import type { ExactReference } from "@quixos/camino-react"; import { Icon } from "@quixos/camino-react/ui"; import { copyText } from "./canvas/selection.js"; import type { ReaderExtension } from "./extensions.js"; import { VocabularyLabel, VocabularySettings } from "./vocabulary.js"; /** Register reader-wide configuration alongside the extension's contributions. * The host retains this section in shared presentation navigation. */ export const readerExtensionSettings: readonly Pick[] = [ { id: "vocabulary", title: "Vocabulary", Settings: VocabularySettings }, ]; /** Install application extensions here. A sidebar and a context menu share the * same source-aware actions; neither needs to pretend to be an annotator. */ export function createReaderExtensions(vocabulary: { vocabulary: ExactReference | null; name: string; known: ReadonlySet; busy: string | null; ready: boolean; remember(word: string): Promise; report(error: unknown): void; }): ReaderExtension[] { return [ { id: "source", title: "Text", actions: (context) => { const text = context.target ? context.text.slice(context.target.start, context.target.end) : ""; return text ? [{ id: "copy", label: "Copy original text", run: () => copyText(text) }] : []; }, }, { ...readerExtensionSettings[0]!, sidebarVisible: (context) => !!context.target, actions: (context) => { const word = context.target ? context.text.slice(context.target.start, context.target.end).trim() : ""; if (!word || word.length > 100 || !vocabulary.vocabulary) return []; const saved = vocabulary.known.has(word); return [ { id: "vocabulary.save", label: saved ? "Saved to vocabulary" : vocabulary.busy === word ? "Saving…" : "Add to vocabulary", disabled: saved || !!vocabulary.busy || !vocabulary.ready, run: () => vocabulary.remember(word), }, ]; }, Sidebar: ({ context, actions }) => !context.target ? null : (
{actions.map((action) => ( ))}
{vocabulary.vocabulary && ( Saved in )}
), }, ]; }