增加pdf中划句或段ai伴读功能

This commit is contained in:
yhy
2026-08-29 23:07:19 +08:00
parent 1475f8b3c5
commit 0ed8f42029
22 changed files with 2045 additions and 15 deletions
+133 -10
View File
@@ -1,10 +1,104 @@
/** Helpers for Zotero ItemPane collapsible sections. */
import { getLocaleID, getString } from "../../utils/locale";
import { getPref, setPref } from "../../utils/prefs";
import { openChatPaneWindow, openLecturePaneWindow } from "./itemPaneWindow";
export type ItemPaneKind = "chat" | "lecture";
const ADAPTIVE_PREF: Record<ItemPaneKind, keyof _ZoteroTypes.Prefs["PluginPrefsMap"]> =
{
chat: "chatPaneAdaptiveHeight",
lecture: "lecturePaneAdaptiveHeight",
};
const FIXED_HEIGHT: Record<ItemPaneKind, number> = {
chat: 360,
lecture: 420,
};
const OPEN_WINDOW_ICON = "chrome://zotero/skin/16/universal/open.svg";
const ADAPTIVE_ICON = "chrome://zotero/skin/16/universal/expand-all.svg";
const ADAPTIVE_ICON_OFF = "chrome://zotero/skin/16/universal/collapse-all.svg";
export function prepareItemPaneBody(body: HTMLElement): void {
body.classList.add("chatpapers-pane-body");
body.style.minHeight = "0";
}
export function isAdaptiveHeight(kind: ItemPaneKind): boolean {
const val = getPref(ADAPTIVE_PREF[kind]);
return val !== false;
}
export function applyAdaptiveHeight(
body: HTMLElement,
kind: ItemPaneKind,
adaptive = isAdaptiveHeight(kind),
): void {
body.classList.toggle("chatpapers-pane-adaptive", adaptive);
body.classList.toggle("chatpapers-pane-fixed", !adaptive);
body.dataset.chatpapersPaneKind = kind;
const section = resolveCollapsibleSection(body);
section?.classList.toggle("chatpapers-adaptive-height", adaptive);
section?.classList.toggle("chatpapers-fixed-height", !adaptive);
updateAdaptiveButtonState(body, adaptive);
}
export function toggleAdaptiveHeight(
body: HTMLElement,
kind: ItemPaneKind,
): boolean {
const next = !isAdaptiveHeight(kind);
setPref(ADAPTIVE_PREF[kind], next);
applyAdaptiveHeight(body, kind, next);
refreshSectionOpenHeight(body);
return next;
}
export function refreshSectionOpenHeight(body: HTMLElement): void {
const section = resolveCollapsibleSection(body);
if (!section?.hasAttribute("open")) return;
if (section.classList.contains("chatpapers-adaptive-height")) {
section.style.setProperty("--open-height", "auto");
body.style.removeProperty("max-height");
body.style.removeProperty("overflow");
return;
}
const kind = (body.dataset.chatpapersPaneKind || "chat") as ItemPaneKind;
const maxH = FIXED_HEIGHT[kind] ?? 360;
body.style.maxHeight = `${maxH}px`;
body.style.overflowY = "auto";
section.style.setProperty("--open-height", `${maxH}px`);
}
export function updateAdaptiveButtonState(
body: HTMLElement,
adaptive: boolean,
): void {
const section = resolveCollapsibleSection(body);
const btn = section?.querySelector(
".adaptive-height.section-custom-button",
) as HTMLElement | null;
if (!btn) return;
btn.setAttribute("aria-pressed", adaptive ? "true" : "false");
btn.classList.toggle("chatpapers-adaptive-active", adaptive);
btn.style.setProperty(
"--custom-button-icon-light",
`url('${adaptive ? ADAPTIVE_ICON : ADAPTIVE_ICON_OFF}')`,
);
btn.style.setProperty(
"--custom-button-icon-dark",
`url('${adaptive ? ADAPTIVE_ICON : ADAPTIVE_ICON_OFF}')`,
);
btn.title = getString(
adaptive ? "item-pane-adaptive-height-on" : "item-pane-adaptive-height-off",
);
}
export function onItemPaneSectionToggle(options: {
body: HTMLElement;
event?: Event;
@@ -13,28 +107,57 @@ export function onItemPaneSectionToggle(options: {
if (!section) return;
const open = section.hasAttribute("open");
const adaptive = section.classList.contains("chatpapers-adaptive-height");
if (open) {
options.body.style.removeProperty("height");
options.body.style.removeProperty("overflow");
options.body.style.minHeight = "0";
const raf = options.body.ownerDocument?.defaultView?.requestAnimationFrame;
const updateHeight = () => {
const h = options.body.scrollHeight;
if (h > 0) {
section.style.setProperty("--open-height", `${h}px`);
}
};
if (raf) raf(updateHeight);
else updateHeight();
if (adaptive) {
options.body.style.removeProperty("overflow");
options.body.style.removeProperty("max-height");
section.style.setProperty("--open-height", "auto");
return;
}
options.body.style.overflowY = "auto";
const kind = (options.body.dataset.chatpapersPaneKind || "chat") as ItemPaneKind;
const maxH = FIXED_HEIGHT[kind] ?? 360;
options.body.style.maxHeight = `${maxH}px`;
section.style.setProperty("--open-height", `${maxH}px`);
return;
}
options.body.style.minHeight = "0";
options.body.style.height = "0";
options.body.style.overflow = "hidden";
options.body.style.removeProperty("max-height");
section.style.setProperty("--open-height", "0px");
}
export function buildItemPaneSectionButtons(kind: ItemPaneKind) {
return [
{
type: "open-in-window",
icon: OPEN_WINDOW_ICON,
l10nID: getLocaleID("item-pane-open-window"),
onClick: ({ item }: { item?: Zotero.Item }) => {
if (!item) return;
if (kind === "chat") openChatPaneWindow(item);
else openLecturePaneWindow(item);
},
},
{
type: "adaptive-height",
icon: ADAPTIVE_ICON,
l10nID: getLocaleID("item-pane-adaptive-height"),
onClick: ({ body }: { body: HTMLElement }) => {
toggleAdaptiveHeight(body, kind);
},
},
];
}
function resolveCollapsibleSection(
body: HTMLElement,
event?: Event,