优化窗格显示
This commit is contained in:
@@ -87,6 +87,16 @@ collapsible-section[open] > :not(.head) .chatpapers-lecture-root {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
collapsible-section[custom][data-pane="chatpapers-chat"] > :not(.head) .chatpapers-header,
|
||||
collapsible-section[custom][data-pane="chatpapers-lecture"] > :not(.head) .chatpapers-header {
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
collapsible-section[custom][data-pane="chatpapers-chat"] > :not(.head) .chatpapers-header .chatpapers-brand,
|
||||
collapsible-section[custom][data-pane="chatpapers-lecture"] > :not(.head) .chatpapers-header .chatpapers-brand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.chatpapers-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -2,8 +2,10 @@ startup-begin = ChatPapers is loading
|
||||
startup-finish = ChatPapers is ready
|
||||
prefs-title = ChatPapers
|
||||
|
||||
item-section-chat-head = ChatPapers
|
||||
item-section-chat-sidenav = Chat with paper
|
||||
item-section-chat-head =
|
||||
.label = ChatPapers
|
||||
item-section-chat-sidenav =
|
||||
.tooltiptext = Chat with paper
|
||||
item-pane-open-window =
|
||||
.tooltiptext = Open in separate window
|
||||
item-pane-adaptive-height =
|
||||
@@ -67,8 +69,10 @@ cite-copied = Bibliography copied
|
||||
cite-exported = Bibliography exported
|
||||
cite-export-cancel = Export cancelled
|
||||
|
||||
item-section-lecture-head = Voice lecture
|
||||
item-section-lecture-sidenav = Voice lecture
|
||||
item-section-lecture-head =
|
||||
.label = Voice lecture
|
||||
item-section-lecture-sidenav =
|
||||
.tooltiptext = Voice lecture
|
||||
|
||||
lecture-loading = Loading voice lecture…
|
||||
lecture-mode-label = Voice lecture · Prepare then listen
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
itemmenu-label = ChatPapers
|
||||
itemmenu-chat = ChatPapers: Chat
|
||||
itemmenu-compare = ChatPapers: Compare papers
|
||||
|
||||
item-section-chat-head =
|
||||
.label = ChatPapers
|
||||
item-section-chat-sidenav =
|
||||
.tooltiptext = Chat with paper
|
||||
item-section-lecture-head =
|
||||
.label = Voice lecture
|
||||
item-section-lecture-sidenav =
|
||||
.tooltiptext = Voice lecture
|
||||
|
||||
@@ -2,8 +2,10 @@ startup-begin = ChatPapers 加载中
|
||||
startup-finish = ChatPapers 已就绪
|
||||
prefs-title = ChatPapers
|
||||
|
||||
item-section-chat-head = ChatPapers
|
||||
item-section-chat-sidenav = 论文对话
|
||||
item-section-chat-head =
|
||||
.label = ChatPapers
|
||||
item-section-chat-sidenav =
|
||||
.tooltiptext = 论文对话
|
||||
item-pane-open-window =
|
||||
.tooltiptext = 在独立窗口中打开
|
||||
item-pane-adaptive-height =
|
||||
@@ -67,8 +69,10 @@ cite-copied = 参考文献已复制到剪贴板
|
||||
cite-exported = 参考文献已导出
|
||||
cite-export-cancel = 已取消导出
|
||||
|
||||
item-section-lecture-head = 语音伴读
|
||||
item-section-lecture-sidenav = 语音伴读
|
||||
item-section-lecture-head =
|
||||
.label = 语音伴读
|
||||
item-section-lecture-sidenav =
|
||||
.tooltiptext = 语音伴读
|
||||
|
||||
lecture-loading = 正在加载语音伴读…
|
||||
lecture-mode-label = 语音伴读 · 先备课再听
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
itemmenu-label = ChatPapers
|
||||
itemmenu-chat = ChatPapers:对话
|
||||
itemmenu-compare = ChatPapers:多篇对照
|
||||
|
||||
item-section-chat-head =
|
||||
.label = ChatPapers
|
||||
item-section-chat-sidenav =
|
||||
.tooltiptext = 论文对话
|
||||
item-section-lecture-head =
|
||||
.label = 语音伴读
|
||||
item-section-lecture-sidenav =
|
||||
.tooltiptext = 语音伴读
|
||||
|
||||
@@ -34,6 +34,9 @@ async function onMainWindowLoad(win: _ZoteroTypes.MainWindow): Promise<void> {
|
||||
win.MozXULElement.insertFTLIfNeeded(
|
||||
`${addon.data.config.addonRef}-mainWindow.ftl`,
|
||||
);
|
||||
win.MozXULElement.insertFTLIfNeeded(
|
||||
`${addon.data.config.addonRef}-addon.ftl`,
|
||||
);
|
||||
registerItemMenus();
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ import {
|
||||
} from "../application/playSelectionExplain";
|
||||
import { getString } from "../../../utils/locale";
|
||||
import { createLucideIcon } from "../../../utils/icons";
|
||||
import { isItemPaneSectionBody } from "../../ui/itemPaneSection";
|
||||
|
||||
export class LecturePaneView {
|
||||
private doc: Document;
|
||||
@@ -119,18 +120,21 @@ export class LecturePaneView {
|
||||
|
||||
const parent = getRegularItem(this.item);
|
||||
const title = String(parent.getField("title") || "ChatPapers");
|
||||
const inItemPane = isItemPaneSectionBody(this.body);
|
||||
|
||||
const header = this.el("div", "chatpapers-header");
|
||||
const brand = this.el("div", "chatpapers-brand");
|
||||
brand.append(
|
||||
createLucideIcon(this.doc, Headphones, {
|
||||
size: 18,
|
||||
className: "chatpapers-brand-icon",
|
||||
}),
|
||||
this.el("div", "chatpapers-brand-text", "ChatPapers"),
|
||||
);
|
||||
if (!inItemPane) {
|
||||
const brand = this.el("div", "chatpapers-brand");
|
||||
brand.append(
|
||||
createLucideIcon(this.doc, Headphones, {
|
||||
size: 18,
|
||||
className: "chatpapers-brand-icon",
|
||||
}),
|
||||
this.el("div", "chatpapers-brand-text", "ChatPapers"),
|
||||
);
|
||||
header.append(brand);
|
||||
}
|
||||
header.append(
|
||||
brand,
|
||||
this.el("div", "chatpapers-title", title),
|
||||
this.el("div", "chatpapers-meta", getString("lecture-mode-label")),
|
||||
);
|
||||
|
||||
@@ -5,7 +5,9 @@ import { ensureChatPapersStyles } from "../../ui/readerPane";
|
||||
import {
|
||||
applyAdaptiveHeight,
|
||||
applyItemPaneSectionButtonIcons,
|
||||
applyItemPaneSectionHeadLabel,
|
||||
buildItemPaneSectionButtons,
|
||||
clearSectionSummary,
|
||||
onItemPaneSectionToggle,
|
||||
prepareItemPaneBody,
|
||||
refreshSectionOpenHeight,
|
||||
@@ -36,6 +38,7 @@ export function registerLecturePane() {
|
||||
if (doc) ensureChatPapersStyles(doc);
|
||||
prepareItemPaneBody(body);
|
||||
applyAdaptiveHeight(body, "lecture");
|
||||
applyItemPaneSectionHeadLabel(body, "lecture");
|
||||
},
|
||||
onDestroy: ({ body }) => {
|
||||
views.get(body)?.destroy();
|
||||
@@ -45,17 +48,19 @@ export function registerLecturePane() {
|
||||
setEnabled(Boolean(item && findPdfAttachment(item)));
|
||||
return true;
|
||||
},
|
||||
onRender: ({ body }) => {
|
||||
onRender: ({ body, setSectionSummary }) => {
|
||||
body.replaceChildren();
|
||||
const doc = body.ownerDocument;
|
||||
if (!doc) return;
|
||||
applyItemPaneSectionHeadLabel(body, "lecture");
|
||||
clearSectionSummary(body, setSectionSummary);
|
||||
applyItemPaneSectionButtonIcons(body);
|
||||
const loading = doc.createElement("div");
|
||||
loading.className = "chatpapers-loading";
|
||||
loading.textContent = getString("lecture-loading");
|
||||
body.append(loading);
|
||||
},
|
||||
onAsyncRender: async ({ body, item }) => {
|
||||
onAsyncRender: async ({ body, item, setSectionSummary }) => {
|
||||
if (!item) return;
|
||||
const doc = body.ownerDocument;
|
||||
if (!doc) return;
|
||||
@@ -72,6 +77,8 @@ export function registerLecturePane() {
|
||||
err.textContent = getString("lecture-mount-error");
|
||||
body.append(err);
|
||||
}
|
||||
clearSectionSummary(body, setSectionSummary);
|
||||
applyItemPaneSectionHeadLabel(body, "lecture");
|
||||
applyAdaptiveHeight(body, "lecture");
|
||||
applyItemPaneSectionButtonIcons(body);
|
||||
const section = body.closest("collapsible-section");
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
isAbortError,
|
||||
type AbortHandle,
|
||||
} from "../../utils/abort";
|
||||
import { isItemPaneSectionBody } from "./itemPaneSection";
|
||||
|
||||
export class ChatView {
|
||||
private body: HTMLElement;
|
||||
@@ -60,15 +61,19 @@ export class ChatView {
|
||||
const preset = cfg.provider ? getProviderPreset(cfg.provider) : undefined;
|
||||
const providerLabel = preset?.label || cfg.provider || "未配置";
|
||||
|
||||
const inItemPane = isItemPaneSectionBody(this.body);
|
||||
const header = this.el("div", "chatpapers-header");
|
||||
const brand = this.el("div", "chatpapers-brand");
|
||||
brand.append(
|
||||
createLucideIcon(this.doc, MessageSquareText, {
|
||||
size: 18,
|
||||
className: "chatpapers-brand-icon",
|
||||
}),
|
||||
this.el("div", "chatpapers-brand-text", "ChatPapers"),
|
||||
);
|
||||
if (!inItemPane) {
|
||||
const brand = this.el("div", "chatpapers-brand");
|
||||
brand.append(
|
||||
createLucideIcon(this.doc, MessageSquareText, {
|
||||
size: 18,
|
||||
className: "chatpapers-brand-icon",
|
||||
}),
|
||||
this.el("div", "chatpapers-brand-text", "ChatPapers"),
|
||||
);
|
||||
header.append(brand);
|
||||
}
|
||||
const titleRow = this.el("div", "chatpapers-title-row");
|
||||
titleRow.append(
|
||||
this.el(
|
||||
@@ -82,7 +87,7 @@ export class ChatView {
|
||||
createLucideIcon(this.doc, Cpu, { size: 13, className: "chatpapers-meta-icon" }),
|
||||
this.el("span", undefined, `${providerLabel} · ${cfg.model || "无模型"}`),
|
||||
);
|
||||
header.append(brand, titleRow, meta);
|
||||
header.append(titleRow, meta);
|
||||
|
||||
this.messagesEl = this.el("div", "chatpapers-messages");
|
||||
this.emptyEl = this.el("div", "chatpapers-empty");
|
||||
|
||||
@@ -226,6 +226,51 @@ export function buildItemPaneSectionButtons(kind: ItemPaneKind) {
|
||||
];
|
||||
}
|
||||
|
||||
export function isItemPaneSectionBody(body: HTMLElement): boolean {
|
||||
return Boolean(body.closest("collapsible-section"));
|
||||
}
|
||||
|
||||
export type SectionSummaryUpdater = (summary: string) => void;
|
||||
|
||||
export function clearSectionSummary(
|
||||
body: HTMLElement,
|
||||
updater?: SectionSummaryUpdater,
|
||||
): void {
|
||||
updater?.("");
|
||||
const section = body.closest("collapsible-section");
|
||||
if (section) section.setAttribute("summary", "");
|
||||
}
|
||||
|
||||
const SECTION_HEAD_MESSAGE: Record<ItemPaneKind, FluentMessageId> = {
|
||||
chat: "item-section-chat-head",
|
||||
lecture: "item-section-lecture-head",
|
||||
};
|
||||
|
||||
/** Ensure native section head shows plugin name (not just the icon). */
|
||||
export function applyItemPaneSectionHeadLabel(
|
||||
body: HTMLElement,
|
||||
kind: ItemPaneKind,
|
||||
): void {
|
||||
const section = body.closest("collapsible-section") as HTMLElement | null;
|
||||
if (!section) return;
|
||||
|
||||
const messageId = SECTION_HEAD_MESSAGE[kind];
|
||||
const l10nId = getLocaleID(messageId);
|
||||
section.dataset.l10nId = l10nId;
|
||||
|
||||
const label = getString(messageId, "label");
|
||||
if (label && label !== l10nId) {
|
||||
section.setAttribute("label", label);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
body.ownerDocument?.l10n?.setAttributes?.(section, l10nId);
|
||||
} catch {
|
||||
// Fluent may not be ready yet; label stays empty until next render hook.
|
||||
}
|
||||
}
|
||||
|
||||
function resolveCollapsibleSection(
|
||||
body: HTMLElement,
|
||||
event?: Event,
|
||||
|
||||
@@ -4,7 +4,9 @@ import { ChatView } from "./chatView";
|
||||
import {
|
||||
applyAdaptiveHeight,
|
||||
applyItemPaneSectionButtonIcons,
|
||||
applyItemPaneSectionHeadLabel,
|
||||
buildItemPaneSectionButtons,
|
||||
clearSectionSummary,
|
||||
onItemPaneSectionToggle,
|
||||
prepareItemPaneBody,
|
||||
refreshSectionOpenHeight,
|
||||
@@ -34,6 +36,7 @@ export function registerChatPane() {
|
||||
if (doc) ensureStyles(doc);
|
||||
prepareItemPaneBody(body);
|
||||
applyAdaptiveHeight(body, "chat");
|
||||
applyItemPaneSectionHeadLabel(body, "chat");
|
||||
},
|
||||
onDestroy: ({ body }) => {
|
||||
views.get(body)?.destroy();
|
||||
@@ -45,17 +48,19 @@ export function registerChatPane() {
|
||||
void tabType;
|
||||
return true;
|
||||
},
|
||||
onRender: ({ body }) => {
|
||||
onRender: ({ body, setSectionSummary }) => {
|
||||
body.replaceChildren();
|
||||
const doc = body.ownerDocument;
|
||||
if (!doc) return;
|
||||
applyItemPaneSectionHeadLabel(body, "chat");
|
||||
clearSectionSummary(body, setSectionSummary);
|
||||
applyItemPaneSectionButtonIcons(body);
|
||||
const loading = doc.createElement("div");
|
||||
loading.className = "chatpapers-loading";
|
||||
loading.textContent = getString("chat-loading");
|
||||
body.append(loading);
|
||||
},
|
||||
onAsyncRender: async ({ body, item }) => {
|
||||
onAsyncRender: async ({ body, item, setSectionSummary }) => {
|
||||
if (!item) return;
|
||||
const doc = body.ownerDocument;
|
||||
if (!doc) return;
|
||||
@@ -63,6 +68,8 @@ export function registerChatPane() {
|
||||
const view = new ChatView(doc, body, item);
|
||||
views.set(body, view);
|
||||
await view.mount();
|
||||
applyItemPaneSectionHeadLabel(body, "chat");
|
||||
clearSectionSummary(body, setSectionSummary);
|
||||
applyAdaptiveHeight(body, "chat");
|
||||
applyItemPaneSectionButtonIcons(body);
|
||||
const section = body.closest("collapsible-section");
|
||||
|
||||
Reference in New Issue
Block a user