import * as React from 'react'; import { d as DocxEditor, E as EditorMode, k as HistoryEntry, n as HistoryStorageAdapter, l as HistoryEntryMeta, f as DocxEditorOptions } from './editor-BpyLLFVy.cjs'; import { f as CommentsStorageAdapter, b as CommentMentionUser, e as CommentsHostViewState, d as CommentsHostCommand, C as CommentThread, P as PageSetup, W as WatermarkConfig } from './watermark-DNRzAyyP.cjs'; export { i as PAGE_SIZE_PRESETS } from './watermark-DNRzAyyP.cjs'; import { Extension } from '@tiptap/core'; export { BlockDiffEntry, BlockDiffResult, diffHistoryBlocks } from './diff.cjs'; import '@tiptap/pm/model'; /** * HeaderActions — the compact icon-button cluster at the right end of the * header row (Open .docx / Save .docx / Export PDF / History + any * host-supplied `titleBarActions`). Split out from `TitleBar` so the * filename input can sit directly next to the File/Insert/Format/View menu, * with these actions docked to the far right of the same row instead. * * The History sidebar itself is NOT mounted here — it lives in * `ReactDocxEditor.tsx`'s body row (a flex sibling of the editor's main * column, docked like the AI agent sidebar) so opening it can shrink the * editor's width instead of overlaying it. This component only owns the * toggle button; `onToggleHistory`'s presence is what shows it. * * Host chrome (`actions`) can be either: * - a bare `React.ReactNode` — rendered once, right after the mode pill * (the historical behavior, unchanged); or * - a `HeaderActionItem[]` — each node dropped into a named slot between * the built-in buttons (`placement`, default `'after-mode'`), so an app * can interleave its own buttons with Open/Save/Export/History and the * mode pill instead of only appending to the end. * Built-in buttons can be hidden individually via `hideDefaultActions` (so an * app that saves somewhere other than a local download doesn't need CSS to * hide the file-download "Save", etc.). */ /** Where a `HeaderActionItem` is inserted among the built-in header buttons. */ type HeaderActionPlacement = 'start' | 'before-open' | 'after-open' | 'after-save' | 'after-export-pdf' | 'after-history' | 'before-mode' | 'after-mode' | 'end'; interface HeaderActionItem { /** Stable key for the node. */ id: string; /** Rendered content (a button, a badge, a `<>`-fragment of several, …). */ node: React.ReactNode; /** Slot to render into. Default `'after-mode'` — same spot as the legacy bare-node `actions`. */ placement?: HeaderActionPlacement; } /** Per-button visibility for the built-in header actions (`true` hides it). */ interface HeaderDefaultActionToggles { open?: boolean; save?: boolean; exportPdf?: boolean; } interface HeaderActionsProps { editor: DocxEditor; /** Current filename (used as the download name and updated on file open). */ name: string; onNameChange: (name: string) => void; /** Host chrome: a bare node (rendered at `'after-mode'`) or positioned `HeaderActionItem[]`. */ actions?: React.ReactNode | HeaderActionItem[]; /** Hide individual built-in buttons (Open / Save / Export PDF). */ hideDefaultActions?: HeaderDefaultActionToggles; /** Locks the Editing/Suggesting/Viewing pill to this mode (see `ReactDocxEditorProps.mode`). Omit to let the user switch freely. */ mode?: EditorMode; /** * Override what "Export PDF" does. Defaults to `editor.printToPdf()` * (the browser's print dialog — works today, no server needed). Pass * this to swap in a server-rendered pipeline instead — e.g. sending * `editor.getExportSnapshot()` to a server that calls `renderHtmlToPdf()` * (`@shashimadushan/docx-editor-editor/headless`) — without changing this button. */ onExportPdf?: (editor: DocxEditor) => void; /** * Shows the "History" button and wires it to toggle the History sidebar. * Omit (or pass `undefined`) to hide the button entirely — matches * `ReactDocxEditorProps.history: false`, which also disables checkpoint * recording on save. */ onToggleHistory?: () => void; } declare function HeaderActions({ editor, name, onNameChange, actions, hideDefaultActions, onExportPdf, onToggleHistory, mode, }: HeaderActionsProps): React.ReactElement; /** In-memory adapter (for testing or server-side). */ declare class MemoryHistoryAdapter implements HistoryStorageAdapter { private entries; append(entry: Omit): Promise; list(): Promise; get(id: string): Promise; prune(opts?: { maxEntries?: number; maxAgeMs?: number; }): Promise; remove(id: string): Promise; } /** * localStorage adapter (browser only) — one JSON array under a single key. * Simplest possible default; be mindful of localStorage's ~5-10MB quota if * documents are large, since every entry stores a full snapshot * (`IndexedDBHistoryAdapter` scales further). */ declare class LocalStorageHistoryAdapter implements HistoryStorageAdapter { private key; constructor(key: string); private readAll; private writeAll; append(entry: Omit): Promise; list(): Promise; get(id: string): Promise; prune(opts?: { maxEntries?: number; maxAgeMs?: number; }): Promise; clear(): Promise; remove(id: string): Promise; } /** * IndexedDB adapter (browser only) — one object store keyed by entry id. * Recommended over `LocalStorageHistoryAdapter` for real use: full-document * snapshots accumulate past localStorage's quota quickly, and IndexedDB * reads are local-disk-cheap. Note: `list()` still reads every record's * bytes (no separate meta-only index) — acceptable for a first version. */ declare class IndexedDBHistoryAdapter implements HistoryStorageAdapter { private dbName; private storeName; private dbPromise; constructor(key: string, opts?: { dbName?: string; }); private openDB; private getAllEntries; append(entry: Omit): Promise; list(): Promise; get(id: string): Promise; prune(opts?: { maxEntries?: number; maxAgeMs?: number; }): Promise; remove(id: string): Promise; } /** * HTTP API adapter — save/list/get via fetch to a REST endpoint. This is * the adapter `examples/demo` wires up against `app/api/history/route.ts` * (JSON-file-backed) — any app with a real database implements the same * contract: `POST {baseUrl}` (append), `GET {baseUrl}` (list, no content), * `GET {baseUrl}/:id` (get, with content). */ declare class HttpHistoryAdapter implements HistoryStorageAdapter { private opts; constructor(opts: { baseUrl: string; /** Scopes requests to one document — sent as a `documentId` query param. */ documentId?: string; headers?: Record; }); private withDocId; append(entry: Omit): Promise; list(): Promise; get(id: string): Promise; prune(opts?: { maxEntries?: number; maxAgeMs?: number; }): Promise; remove(id: string): Promise; } /** * The subset of `HistoryManager` that the drawer UI (`react/history/`) * actually needs — `HistoryDrawer`/`HistoryEntryRow` take this instead of * the full class so `useDocumentHistory`'s returned `get`/`restore` * functions satisfy it directly, no manager instance or casting required. */ interface HistoryManagerLike { get(id: string): Promise; restore(id: string): Promise; remove(id: string): Promise; } interface HistoryManagerOptions { /** Unique key for this document's history (used by the default localStorage adapter). */ key: string; /** Storage adapter. Defaults to LocalStorageHistoryAdapter. */ storage?: HistoryStorageAdapter; /** Prune to at most this many entries after every append. Unset = unbounded. */ maxEntries?: number; /** Called with the full entry list whenever it changes (append, restore). */ onEntriesChange?: (entries: HistoryEntryMeta[]) => void; } declare class HistoryManager { private storage; private maxEntries?; private onEntriesChange?; private editor; private unsubscribeCheckpoint; constructor(opts: HistoryManagerOptions); /** * Subscribe to `editor.onCheckpoint()` — every checkpoint (manual save, * agent turn) is snapshotted (`editor.getJSON()`) and appended. Returns a * detach function. */ attach(editor: DocxEditor): () => void; private recordFromCheckpoint; /** `append()`, then prune (if configured) and notify — every write path (checkpoint, restore) does exactly this. */ private appendAndSync; private notifyChange; list(): Promise; get(id: string): Promise; /** * Restore `id` as the current document, then append a new `restore` entry * describing the revert. Append-only: never truncates or removes existing * entries, so a revert is itself part of the timeline (Google-Docs style). */ restore(id: string): Promise; /** Permanently delete one entry. Does not touch the live document. */ remove(id: string): Promise; } declare function useDocumentHistory(editor: DocxEditor | null, options: HistoryManagerOptions): { entries: HistoryEntryMeta[]; refresh: () => Promise; get: (id: string) => Promise; restore: (id: string) => Promise; remove: (id: string) => Promise; }; /** * MobileMoreSheet — the full-height sheet opened by `MobileHeader`'s "⋯" * button, holding File/View/Watermark/Header-Footer actions that don't * already have a dedicated `MobileToolbar` tab (Home/Insert cover the * everyday formatting + insert actions; this sheet is the mobile * equivalent of desktop's File/View menus). * * Presented as grouped iOS-Settings-style cards — each action is a row * with a leading icon + label (destructive actions tinted red), View is a * segmented Pageless/Paged toggle, and Watermark presets are quick chips. * This is a mobile-native pattern rather than a re-skin of the desktop * text-only dropdown, so it does NOT reuse `MenuBar`'s row components; the * rows are rendered from a local data model (`Row`/`Section`) below. */ /** * Icons a host can name for a custom menu row. A name rather than a React * node because the React Native package delivers these rows across a WebView * RPC boundary, where only JSON survives — so the icon has to be something * both sides can spell. Web callers not crossing that boundary can pass a * `React.ReactNode` instead (see `MobileMenuItem.icon`). */ type MobileMenuIconName = 'save' | 'cloud' | 'open' | 'new' | 'pdf' | 'history' | 'template' | 'trash' | 'settings' | 'header' | 'footer'; /** One host-provided row in the "⋯" menu. */ interface MobileMenuItem { id: string; label: string; icon?: MobileMenuIconName | React.ReactNode; /** Tint the row red (destructive action). */ danger?: boolean; disabled?: boolean; /** Show a trailing chevron — the row opens something further. */ chevron?: boolean; /** * Whether tapping closes the sheet. Defaults to `true`; pass `false` for a * row whose own UI (a spinner, a toggled checkmark) should stay visible. */ closeOnPress?: boolean; } /** A group of host-provided rows, rendered as one iOS-settings-style card. */ interface MobileMenuSection { id: string; /** Card heading. Omit to render the rows with no section label. */ title?: string; /** * Where the section lands: * - `'file'` — appended into the built-in **File** card, for actions * that belong beside Save/Open/Export (e.g. "Save to Drive"). * - `'top'` — its own card above File, for app-level actions. * - `'bottom'` — its own card after everything (the default). */ placement?: 'file' | 'top' | 'bottom'; items: MobileMenuItem[]; } /** * TemplatePicker — a Google-Docs-style "Insert > Template" control. * * Deliberately generic: it knows nothing about "legal" or Sri Lanka. It just * renders a searchable, category-grouped list of `{ id, title, category, * description }` items and calls `onSelect(id)` when the user picks one. * Wire it up to any template source (e.g. `@shashimadushan/docx-editor-legal-templates`) * from the host app — see `Toolbar`'s `templates` / `onInsertTemplate` props. * * This lets users manually browse and insert a template into the document * at any time, independent of the AI agent (which can also insert templates * via the `insert_legal_template` tool in `@shashimadushan/docx-editor-agent/legal`) — * both paths end up calling the same insertion logic. */ interface TemplatePickerItem { id: string; title: string; /** Group label shown as a section header (e.g. "Notarial", "Agreements"). */ category: string; description?: string; } interface TemplatePickerProps { templates: TemplatePickerItem[]; onSelect: (id: string) => void; /** Tooltip/aria-label for the trigger button and the popover's `aria-label`. */ title?: string; /** * Visible text on the trigger button, e.g. "Templates" or "Clauses". Was * previously hardcoded to "Templates" regardless of `title` — meaning * every consumer of this component (Templates *and* Clauses dropdowns) * showed the same label. Defaults to `title` so existing callers that * only set `title` still get a sensible label. */ buttonLabel?: string; /** Icon shown on the trigger button. Defaults to `TemplateIcon`. */ icon?: React.ReactNode; /** Placeholder for the search box. */ searchPlaceholder?: string; } declare function TemplatePicker({ templates, onSelect, title, buttonLabel, icon, searchPlaceholder, }: TemplatePickerProps): React.ReactElement; /** * EditorContextMenuLayer — wires up right-click menus, the link dialog, and * the link hover preview automatically for a mounted `DocxEditor`. * * Drop this alongside a mounted `DocxEditor` to get, for free: * - Right-click on the page (no selection) → `PageContextMenu`. * - Right-click on selected text → `SelectionContextMenu` (formatting, * font size, colors, link). * - Right-click on an image → the existing `ImageContextMenu`. * - Right-click inside a table → `TableContextMenu` (row/column/cell * actions, style/border/color pickers, table properties). * - Hovering a link → a small preview card with Open/Copy/Edit/Remove, * Google-Docs style. * - "Add link" / "Edit link" everywhere opens the same `LinkDialogView`. * * `ReactDocxEditor` mounts this automatically; only use it directly if * you're composing your own layout around `DocxEditor`. */ /** * The selected text plus where it lives in the document — enough for a * context chip like "Selected text — page 2, line 5" that tells the user * (or the agent) exactly what's being referenced without re-scanning the * whole document. Computed here via `editor.getCursorContext()`, which * resolves page/line off `selection.from` — correct for a non-empty * selection too (that's its start, not just a bare cursor position). */ interface SelectionContextInfo { text: string; page: number; totalPages: number; line: number; totalLines: number; } interface EditorContextMenuLayerProps { editor: DocxEditor; /** * Adds "Edit with AI" to the selection right-click menu. Called with the * selection's text/page/line context plus the screen position to anchor * the agent bubble at (computed here from the selection's end, same as * the "@" trigger uses). Omit to hide the row entirely — `ReactDocxEditor` * only passes this when an `agent` adapter is configured. */ onEditWithAI?: (info: SelectionContextInfo & { left: number; top: number; }) => void; /** * Adds "Add to agent" to the selection right-click menu. Called with the * selection's text/page/line context; the host app is expected to open * its agent sidebar and attach it as a context chip (see `ReactDocxEditor`'s * `onAddSelectionToAgent` and `AgentPanel`'s `context` prop). */ onAddToAgent?: (info: SelectionContextInfo) => void; } declare function EditorContextMenuLayer({ editor, onEditWithAI, onAddToAgent }: EditorContextMenuLayerProps): React.ReactElement | null; /** * One step of agent activity, surfaced live in the in-page bubble. * * `thinking` chunks arrive incrementally (`done: false`) followed by one * final call with the full text (`done: true`) — mirrors `DocxAgent`'s * `onThinking` callback. `tool_call`/`tool_result` share a `callId` so the * bubble can merge them into a single timeline entry instead of two * separate lines. */ interface AgentMentionActivityEvent { type: 'user' | 'assistant' | 'thinking' | 'tool_call' | 'tool_result'; /** For type='user'/'assistant'/'thinking': the message/reasoning text. */ text?: string; /** For type='thinking': false while still streaming, true on the final call for this round. */ done?: boolean; /** For type='tool_call'/'tool_result': the tool's name. */ toolName?: string; /** For type='tool_call': the arguments passed to the tool. */ args?: any; /** For type='tool_call'/'tool_result': links a call to its result. */ callId?: string; /** For type='tool_result': a short human-readable summary. */ summary?: string; /** For type='tool_result': true if the tool call failed. */ isError?: boolean; } interface AgentMentionAdapter { /** * Send a natural-language instruction to the agent. The agent is expected * to carry it out using its own tools, bound to this same `DocxEditor` * instance (insert/replace/format/etc). Reject/throw on failure so the * bubble can surface an error and let the user retry. * * Call `onActivity` for every step (thinking / assistant message / tool * call / tool result) as it happens so the bubble can show live progress * instead of a generic spinner. Purely optional — omit it and the bubble * just shows a "Working…" placeholder while the promise is pending. */ sendMessage: (prompt: string, onActivity?: (event: AgentMentionActivityEvent) => void) => Promise; } /** * Lets a host component (e.g. `EditorContextMenuLayer`'s "Edit with AI" * context-menu item) open the bubble directly, without going through the * "@" trigger flow — used for editing a text selection in place. * `nonce` must change on every request (e.g. `Date.now()`) so the effect * that watches it fires even if the same selection is requested twice in a * row. */ interface AgentMentionExternalTrigger { nonce: number; left: number; top: number; /** The selected text to show as context and prepend to the next prompt. */ contextText: string; /** Where the selection is, e.g. "Page 2, Line 5" — shown above `contextText` in the chip. */ contextLabel?: string; } interface AgentMentionViewProps { editor: DocxEditor; adapter: AgentMentionAdapter; /** Opens the bubble directly (see `AgentMentionExternalTrigger`) — used for "Edit with AI" on a selection. */ externalTrigger?: AgentMentionExternalTrigger | null; /** * Presents the bubble as a bottom `MobileSheet` instead of a draggable * floating card — see `ReactDocxEditor`'s `layout` prop. Threaded down * the same way `CommentsLayer`/`SelectionFormatBar` receive it. */ mobile?: boolean; } /** * AgentMention — type "@" anywhere in the document to invoke the AI agent * right at the cursor. * * Flow: * 1. Typing "@word" near the cursor opens a small floating menu with a * single "Agent" item (Enter or click to select). * 2. Selecting it deletes the "@word" trigger text (so it doesn't linger * as real content) and opens an input bubble in its place — a small * textarea + send button, Google-Docs-comment-style. * 3. Sending calls `adapter.sendMessage(prompt, onActivity)`. Because the * trigger text was just deleted, the ProseMirror selection is still * sitting exactly where "@" was typed — so if the agent uses * cursor-relative tools (e.g. `insert_at_cursor`), it naturally acts * "at that place" with no extra plumbing needed. * 4. While the agent works, the bubble stays open and shows a live feed * of what it's doing — reasoning ("thinking") streamed into a * collapsed block, and tool calls collapsed into a plain-language * timeline — in the order it actually happened. It never auto-hides; * the user closes it explicitly (✕ button or Escape), so they can * always see what happened, even after the request finishes. * * This component is deliberately decoupled from `@shashimadushan/docx-editor-agent` — it * only needs an object with a `sendMessage(prompt, onActivity?)` method. * Wire it up with the raw `DocxAgent` instance so you can forward its * run() callbacks as activity events: * * const { agent } = useDocxAgent({ adapter: myLLMAdapter }, editor); * * agent!.run(prompt, { * onThinking: (text, done) => onActivity?.({ type: 'thinking', text, done }), * onAssistantMessage: (text) => onActivity?.({ type: 'assistant', text }), * onToolCall: (name, args, callId) => onActivity?.({ type: 'tool_call', toolName: name, args, callId }), * onToolResult: (r) => onActivity?.({ type: 'tool_result', toolName: r.tool, callId: r.callId, summary: r.summary, isError: r.isError }), * }), * }} * ... * /> * * If no `agent` adapter is supplied, `ReactDocxEditor` doesn't mount this * component at all, so typing "@" behaves like a normal character. * * There's a second entry point besides typing "@": the `externalTrigger` * prop lets another component (the right-click "Edit with AI" menu item, in * `EditorContextMenuLayer`) open the bubble directly at a given position * with a text selection as context — see `AgentMentionExternalTrigger`. * * On mobile (`mobile` prop) the bubble presents as a bottom `MobileSheet` * instead of a draggable floating card — see `AgentMentionBubble`. */ declare function AgentMentionView({ editor, adapter, externalTrigger, mobile }: AgentMentionViewProps): React.ReactElement | null; /** * ReactDocxEditor — a React wrapper around the framework-agnostic DocxEditor. * * Renders a Word/Google-Docs-style editor with: * - Title bar (filename + open/save buttons) * - Menu bar (File / Insert / Format / View dropdowns) * - Formatting toolbar (grouped, with font/size pickers, color pickers, table tools) * - Paged paper-like editing surface * - Optional status bar * * Customization props: * - `renderToolbar` / `renderTitleBar` / `renderMenuBar` — replace entire sections * - `toolbarExtraItems` / `toolbarExtraGroups` — append/prepend custom toolbar items * - `titleBarActions` — append custom buttons to the title bar * - `showTitleBar` / `showMenuBar` / `showStatusBar` — toggle sections * - `mode` — lock Editing/Suggesting/Viewing to a value the host controls, * hiding the pill's own dropdown so the user can't switch modes themselves */ interface ReactDocxEditorProps extends Omit { /** Initial HTML content. */ content?: string; /** CSS class applied to the host wrapper. */ className?: string; /** Inline style applied to the host wrapper. */ style?: React.CSSProperties; /** Called whenever the document content changes. */ onUpdate?: (json: any, html: string) => void; /** Called when the editor is created. Receives the DocxEditor instance. */ onReady?: (editor: DocxEditor) => void; /** Render-prop: replace the entire title bar. */ renderTitleBar?: (editor: DocxEditor) => React.ReactNode; /** Render-prop: replace the entire menu bar. */ renderMenuBar?: (editor: DocxEditor) => React.ReactNode; /** Render-prop: replace the entire toolbar. */ renderToolbar?: (editor: DocxEditor) => React.ReactNode; /** Render-prop: replace the entire status bar. */ renderStatusBar?: (editor: DocxEditor) => React.ReactNode; /** * Controls Editing/Suggesting/Viewing from the host app instead of * leaving it to the user. When set, the mode pill in the header renders * as a plain, non-interactive indicator (no dropdown) — the user can't * switch modes from the editor UI, only the host can, by changing this * prop (e.g. render Viewing for a read-only share, or Suggesting for a * reviewer who shouldn't be able to edit directly). Omit to let the user * pick freely from the pill (default). */ mode?: EditorMode; /** Show the title bar (default: true). */ showTitleBar?: boolean; /** Show the indent/tab-stop ruler above the page. Default: false. */ showRuler?: boolean; /** Show the menu bar (default: true). */ showMenuBar?: boolean; /** Show the status bar at the bottom (default: true). */ showStatusBar?: boolean; /** Initial filename shown in the title bar. */ filename?: string; /** Called when the user edits the filename in the title bar. */ onFilenameChange?: (filename: string) => void; /** * Host chrome for the header's right-hand action area. Two shapes: * - a bare `React.ReactNode` — rendered once, right after the * Editing/Suggesting/Viewing pill (unchanged historical behavior); or * - `HeaderActionItem[]` — each `{ id, node, placement }` dropped into a * named slot among the built-in buttons (`placement` slots: * `start`, `before-open`, `after-open`, `after-save`, * `after-export-pdf`, `after-history`, `before-mode`, `after-mode` * (default), `end`), so app buttons can be interleaved with * Open/Save/Export/History and the mode pill rather than only appended. * Desktop (`layout: 'desktop'`) header only. */ titleBarActions?: React.ReactNode | HeaderActionItem[]; /** * Hide individual built-in header buttons — `{ open?, save?, exportPdf? }`, * each `true` to hide. Lets an app that opens/saves through its own flow * (e.g. a document manager, not a local file download) drop the built-in * buttons without CSS overrides. Desktop header only; the History button is * governed by `history` instead. */ hideDefaultHeaderActions?: HeaderDefaultActionToggles; /** * Override what the "Export PDF" button/menu item does. Defaults to * `editor.printToPdf()` (the browser's print dialog — works with no * server or extra dependency). Pass this to swap in a server-rendered * pipeline instead: send `editor.getExportSnapshot()` to a server that * calls `renderHtmlToPdf()` (`@shashimadushan/docx-editor-editor/headless`, via * Puppeteer/Playwright) for higher-fidelity, no-user-interaction export. */ onExportPdf?: (editor: DocxEditor) => void; /** * Edit-history sidebar config, shown via a "History" button right after * "Export PDF" — a docked, collapsible panel (like the AI agent sidebar) * that shrinks the editor's width when open rather than overlaying it. * `false` hides the button and disables checkpoint recording entirely. * Omitted (default) shows the button backed by `LocalStorageHistoryAdapter`. * See `HistoryManagerOptions` (`./history.ts`) for `storage`/`key`/`maxEntries`. */ history?: false | HistoryManagerOptions; /** Extra items appended to the end of the default toolbar. Desktop (`layout: 'desktop'`) only — see `mobileToolbarExtraItems` for `layout: 'mobile'`. */ toolbarExtraItems?: React.ReactNode; /** Extra groups prepended to the beginning of the default toolbar. Desktop only — see `mobileToolbarExtraItems`. */ toolbarExtraGroups?: React.ReactNode; /** * `MobileToolbar`'s (`layout: 'mobile'`) equivalent of `toolbarExtraItems` * — appended after the built-in Insert-tab group. Data-only (`id`/`label`, * no `ReactNode`) rather than the desktop slot's arbitrary-content shape, * since `layout: 'mobile'`'s primary consumer * (`@shashimadushan/docx-editor-react-native`) needs to describe these * from a JSON config channel, not live React elements — see that * package's `toolbarItems` prop. */ mobileToolbarExtraItems?: Array<{ id: string; label: string; }>; /** Fired when a `mobileToolbarExtraItems` entry is tapped, with that item's `id`. */ onMobileToolbarExtraItemPress?: (id: string) => void; /** * Host-provided rows added to the mobile "⋯" menu sheet (`layout: 'mobile'` * only) — the extension point for app-level actions the editor knows * nothing about, e.g. "Save to Drive". Each section chooses whether it * merges into the built-in File card or renders as its own card; see * `MobileMenuSection`. Data-only for the same reason as * `mobileToolbarExtraItems`: `@shashimadushan/docx-editor-react-native` * describes these over a JSON config channel, not as live React elements. */ mobileMenuSections?: MobileMenuSection[]; /** Fired when a `mobileMenuSections` item is tapped, with that item's `id`. */ onMobileMenuItemPress?: (id: string) => void; /** * Templates the user can manually browse and insert via a "Templates" * dropdown right after the toolbar's History (Undo/Redo) group (e.g. pass * `builtinLegalTemplates` from `@shashimadushan/docx-editor-legal-templates`, mapped to * `{ id, title, category, description }`). Omit to hide the dropdown. */ templates?: TemplatePickerItem[]; /** * Called with the chosen template's `id` when the user picks one from the * Templates dropdown. Typical implementation: look up the template, call * `renderLegalTemplate(template, {})` from `@shashimadushan/docx-editor-legal-templates` * (empty field values are fine — unfilled fields render as `[Field Label]` * placeholders the user then edits by hand), and insert the result with * `editor.editor.chain().focus().insertContent(rendered).run()`. */ onInsertTemplate?: (templateId: string) => void; /** * Reusable boilerplate clauses the user can manually browse and insert via * a "Clauses" dropdown right after Templates (e.g. pass * `builtinLegalClauses` from `@shashimadushan/docx-editor-legal-templates`, * mapped to `{ id, title, category: c.applicableTo[0], description }`). * Omit to hide the dropdown. */ clauses?: TemplatePickerItem[]; /** * Called with the chosen clause's `id` when the user picks one from the * Clauses dropdown. Typical implementation: look up the clause (e.g. via * `getLegalClause(id)`) and insert its `body` with * `editor.editor.chain().focus().insertContent(clause.body).run()`. */ onInsertClause?: (clauseId: string) => void; /** * Enable the built-in right-click context menus (page menu / selection * formatting menu / image menu) plus the link "Insert link" dialog and * hover preview card. Default: true. */ enableContextMenu?: boolean; /** * Enable the "@" agent mention: typing "@" in the document opens a small * menu with an "Agent" item; selecting it opens an input bubble that * sends the typed instruction to `agent.sendMessage(prompt)`. Pass a * `{ sendMessage }` adapter backed by `@shashimadushan/docx-editor-agent`'s * `useDocxAgent()` hook (`{ sendMessage: run }`), or any object with a * matching method. Omit this prop to disable the feature entirely — * typing "@" then behaves like a normal character. */ agent?: AgentMentionAdapter; /** * Adds "Add to agent" to the right-click menu shown on a text selection. * Called with the selected text plus where it is in the document (page/ * line) — the typical implementation opens the host app's agent sidebar * (`AgentPanel`) and attaches it as a context chip (e.g. via `AgentPanel`'s * `context` prop, which shows an editable "Page 2, Line 5" chip above the * input instead of dumping the quoted text into it). Omit to hide the * menu item. * * "Edit with AI" (also on the selection right-click menu) needs no * separate prop — it reuses the `agent` adapter above to open the same * in-document bubble the "@" mention uses, seeded with the selection as * context, and is only shown when `agent` is configured. */ onAddSelectionToAgent?: (info: SelectionContextInfo) => void; /** * Text-anchored comment threads: select text to leave a comment (with * "@" mention autocomplete), browse every thread in a draggable panel * toggled from a bubble pinned to the page margin. Thread content is * persisted through `storage` (defaults to an in-memory adapter, so * comments are lost on reload unless you supply one — e.g. * `LocalStorageCommentsAdapter` or a custom `CommentsStorageAdapter` * backed by IndexedDB/a real API). Pass `false` to disable the feature * entirely. */ comments?: false | { storage?: CommentsStorageAdapter; mentionableUsers?: CommentMentionUser[]; currentAuthor?: string; /** * `'host'` suppresses the built-in draggable/sheet `CommentsPanel` — * the toggle bubble and "Add comment" selection trigger keep rendering * (their position depends on document layout only this component * knows), but every open/thread-list/expanded/pending-composer change * is reported via `onHostOpen` instead, so a host app (e.g. * `@shashimadushan/docx-editor-react-native`'s `CommentsSheet`) can * render its own native equivalent. Actions come back via * `hostCommand`. Default `'panel'` — today's behavior, unchanged. */ presentation?: 'panel' | 'host'; onHostOpen?: (state: CommentsHostViewState) => void; hostCommand?: CommentsHostCommand; }; /** * Switch the entire header/toolbar chrome to a touch-friendly mobile * layout: `MobileHeader` (folded filename + a "⋯" sheet with File/View * actions) replaces the `TitleBar`/`HeaderActions` row and the desktop * `MenuBar` is skipped entirely, and `MobileToolbar` (tabbed Home/Insert/ * Table/Image, 44px touch targets) replaces `Toolbar`. A floating * `SelectionFormatBar` also appears above any non-empty text selection, * the touch equivalent of "select text, right-click to format." * * Sets `data-de-layout` on the host div, which is the *only* CSS gate for * every mobile style partial (`styles/mobile/*.css`, all scoped under * `.docx-editor-react[data-de-layout="mobile"]`) — so omitting this prop * (default `'desktop'`) reproduces today's desktop rendering exactly, * with zero behavior change for existing consumers. */ layout?: 'desktop' | 'mobile'; } declare function ReactDocxEditor(props: ReactDocxEditorProps): React.ReactElement; /** * TitleBar — the document icon + filename, left-most in the header row. * * Exported so consumers can use it in custom layouts or replace it entirely * via the `renderTitleBar` prop on . Open/Save/Export live * in `HeaderActions` (the right side of the same header row) — kept as a * separate component so the filename input can sit directly next to the * File/Insert/Format/View menu instead of being pushed apart from it. */ interface TitleBarProps { editor: DocxEditor; /** Current filename (controlled — see `ReactDocxEditor`'s internal state). */ name: string; onNameChange: (name: string) => void; } declare function TitleBar({ name, onNameChange }: TitleBarProps): React.ReactElement; /** * ModeSwitcher — the Editing / Suggesting / Viewing pill at the right end of * the toolbar (matches Google Docs' mode dropdown). Drives `editor.setMode`. * * Uncontrolled by default: the user can freely pick a mode from the * dropdown. Pass `mode` to control it from the host app instead — the pill * then renders as a plain, non-interactive indicator (no dropdown, no * click handler) showing whatever mode the host set, and the only way to * change it is for the host to change the `mode` prop. `ReactDocxEditor`'s * own `mode` prop threads through to this automatically. */ interface ModeSwitcherProps { editor: DocxEditor; /** * Locks the mode to this value and hides the dropdown — the user can no * longer switch modes from the editor UI. Omit to let the user pick * freely (default); the internal selection still calls `editor.setMode`. */ mode?: EditorMode; } declare function ModeSwitcher({ editor, mode: controlledMode }: ModeSwitcherProps): React.ReactElement; /** * SuggestionLayer — a small Accept/Reject card for a tracked * insertion/deletion span, Google-Docs style. It shows for whichever * suggestion is currently relevant: * * - **Hover**: moving the mouse over any suggestion span previews it * immediately, and moving off it (without landing on the card itself) * reverts to whatever the caret is doing. * - **Caret**: it opens the moment the caret sits inside a suggestion — * while actively typing it, right after clicking into it (even if the * click drags slightly and produces a non-empty selection, e.g. a * double-click that selects the suggested word), or when arrowing * through it — and closes once the caret moves off it. * * It never inserts anything into the document itself; the card is a * portal-rendered overlay, so accepting/rejecting is the only thing that * touches doc content. * * Positioned in the margin, not on top of the page: like Google Docs' * comment cards, it renders just outside the right edge of the paper * (`editor.getPage()`), vertically aligned with the suggestion, instead of * overlapping the document text. Falls back to clamping inside the viewport * if there isn't room (narrow window, sidebar squeezing the page). * * Closing is driven by hover/selection state, not a generic "click outside * to close" listener: a real click on inline text inside a contentEditable * region can report `e.target` as the outer ProseMirror container rather * than the clicked inline element, which made a generic "was the click * inside the anchor?" check (as `FloatingPanel` uses for ordinary toolbar * dropdowns, anchored to stable React-rendered buttons) unreliable for a * ProseMirror-anchored overlay like this one — it would close the card on * the very click that was supposed to (re)open it. * * Mounted unconditionally (like the context-menu/agent-mention layers): * suggestions left over from a previous Suggesting session are still * reachable after switching back to Editing mode, since they aren't * resolved until accepted or rejected. */ interface SuggestionLayerProps { editor: DocxEditor; } declare function SuggestionLayer({ editor }: SuggestionLayerProps): React.ReactElement | null; /** * FindReplace — Ctrl/Cmd+F find bar with replace, Word/Google-Docs style. * * Navigation selects the match with the editor's own text selection (so the * browser highlights it natively) and scrolls it into view; it doesn't * decorate every match on screen (v1 — see IMPROVEMENT_PLAN §4). Replace * goes through TipTap commands, so it plays nicely with suggesting mode the * same way every other agent/human edit does. */ interface FindReplaceProps { editor: DocxEditor; open: boolean; onClose: () => void; } declare function FindReplace({ editor, open, onClose }: FindReplaceProps): React.ReactElement | null; /** * Ruler — horizontal indent/tab-stop ruler above the page, Word-style. * * Shows the current paragraph's left indent, first-line indent, and tab * stops (all `paragraphLayout` attrs — see `extensions/builtins/ * paragraph-layout.ts`). Drag the indent markers to change indent; click * empty ruler space to add a left tab stop; click an existing tab stop to * remove it. Scale is derived from the live page width/margins so 1 inch on * the ruler always matches 1 inch on the page. */ interface RulerProps { editor: DocxEditor; } declare function Ruler({ editor }: RulerProps): React.ReactElement; /** * CommentsLayer — orchestrates the whole Comments feature: a toggle bubble * pinned to the page margin, a small "Add comment" button that appears * near a non-empty text selection, and the draggable panel listing every * thread. Independent of "Suggesting" mode track changes (`SuggestionLayer`) * — comments are anchored via the separate `comment` mark * (`extensions/builtins/comments.ts`). * * Thread content (author, messages, mentions, resolved state) is owned by * this component's React state, hydrated from and persisted through a * pluggable `CommentsStorageAdapter` — the document's `comment` mark only * carries an anchor id, never thread content, so resolving/replying never * touches document/undo history. * * A comment's highlight is only written to the document once its first * message is actually submitted (not the moment the trigger is clicked) — * this keeps the mark and the persisted thread record atomic, so there's * never an orphaned highlight for a thread that was opened and abandoned. */ interface CommentsLayerProps { editor: DocxEditor; /** Persistence backend. Defaults to an in-memory adapter (comments are lost on reload). */ storage?: CommentsStorageAdapter; /** People selectable via "@" in the comment composer. Omit to disable @mention autocomplete. */ mentionableUsers?: CommentMentionUser[]; /** Display name attached to comments/replies made from now on. Default: 'You'. */ currentAuthor?: string; /** See `CommentsToggleButtonProps.mobile` — passed straight from `ReactDocxEditor`'s `layout` prop. */ mobile?: boolean; /** * Bumped to open the "new comment" composer for whatever text is * currently selected, without going through `CommentAddTrigger` — used * by `SelectionFormatBar`'s mobile-only "Comment" button (see * `ReactDocxEditor`'s `handleAddComment`). On mobile, `CommentAddTrigger` * is not rendered at all (§2.3 "one selection affordance"), so this is * the only way to start a thread from a selection there. Undefined or * an unchanged value is a no-op. */ commentRequest?: number; /** * `'host'` suppresses `CommentsPanel` entirely — `CommentsLayer` keeps * anchoring/mutating threads exactly as before (the toggle bubble and * "Add comment" selection trigger still render, since their position * depends on document layout only this component knows), but reports * every state change via `onHostOpen` instead of rendering the panel, and * takes actions back via `hostCommand` instead of `CommentsPanel`'s own * composer/reply/resolve/delete buttons. Default `'panel'` (today's * behavior, unchanged). */ presentation?: 'panel' | 'host'; /** Required (and only called) when `presentation: 'host'`. See `CommentsHostViewState`. */ onHostOpen?: (state: CommentsHostViewState) => void; /** Action from a host-rendered sheet — see `CommentsHostCommand`. */ hostCommand?: CommentsHostCommand; } declare function CommentsLayer({ editor, storage, mentionableUsers, currentAuthor, mobile, commentRequest, presentation, onHostOpen, hostCommand, }: CommentsLayerProps): React.ReactElement | null; /** * Built-in `CommentsStorageAdapter` implementations (see `../comments-types`). * * `MemoryCommentsAdapter` is the default used by `CommentsLayer` when no * `storage` is passed, so the Comments feature works out of the box. * `LocalStorageCommentsAdapter` persists across reloads for simple/demo * use without a backend. A real app (or the demo's IndexedDB adapter) can * supply its own `CommentsStorageAdapter` instead. */ /** In-memory adapter — comments are lost on reload. Default when no `storage` is given. */ declare class MemoryCommentsAdapter implements CommentsStorageAdapter { private threads; list(): Promise; save(thread: CommentThread): Promise; remove(id: string): Promise; } /** localStorage adapter (browser only) — persists across reloads under a single JSON blob. */ declare class LocalStorageCommentsAdapter implements CommentsStorageAdapter { private key; constructor(key: string); private read; private write; list(): Promise; save(thread: CommentThread): Promise; remove(id: string): Promise; } interface MenuDropdownProps { label: string; open: boolean; onToggle: () => void; onClose: () => void; children: React.ReactNode; /** Disable the trigger entirely (e.g. Insert/Format while Viewing). */ disabled?: boolean; } declare function MenuDropdown({ label, open, onToggle, onClose, children, disabled, }: MenuDropdownProps): React.ReactElement; interface MenuDropdownItemProps { label: string; shortcut?: string; active?: boolean; disabled?: boolean; onClick: () => void; } declare function MenuDropdownItem({ label, shortcut, active, disabled, onClick, }: MenuDropdownItemProps): React.ReactElement; declare function MenuDropdownSeparator(): React.ReactElement; declare function MenuDropdownLabel({ children }: { children: React.ReactNode; }): React.ReactElement; /** * MenuBar — the File / Insert / Format / View menu bar above the toolbar. */ interface MenuBarProps { editor: DocxEditor; /** * Override what "Export PDF" does. Defaults to `editor.printToPdf()` * (the browser's print dialog). See `TitleBarProps.onExportPdf` for the * full rationale — same override, so a host app can wire both the * title-bar button and this menu item to the same server-rendered path. */ onExportPdf?: (editor: DocxEditor) => void; } declare function MenuBar({ editor, onExportPdf }: MenuBarProps): React.ReactElement; /** * Toolbar — the formatting toolbar with grouped buttons. * * Exported so consumers can: * 1. Use the default directly in custom layouts * 2. Append custom items via the `extraItems` prop * 3. Replace specific groups via render-props */ interface ToolbarProps { editor: DocxEditor; /** Extra items to append to the end of the toolbar (after Clear formatting). */ extraItems?: React.ReactNode; /** Extra groups to prepend to the beginning of the toolbar (before dropdowns). */ extraGroups?: React.ReactNode; /** * Templates the user can manually browse and insert, shown as a * "Templates" dropdown right after the History (Undo/Redo) group. Pass template * summaries from any source (e.g. `builtinLegalTemplates` from * `@shashimadushan/docx-editor-legal-templates`) plus `onInsertTemplate` to handle the * selection. Omit both to hide the group entirely (default). */ templates?: TemplatePickerItem[]; /** Called with the chosen template's `id` when the user picks one from the Templates dropdown. */ onInsertTemplate?: (templateId: string) => void; /** * Reusable boilerplate clauses the user can manually browse and insert, * shown as a "Clauses" dropdown right after Templates. Pass clause * summaries from any source (e.g. `builtinLegalClauses` from * `@shashimadushan/docx-editor-legal-templates`, mapped to * `{ id, title, category: applicableTo[0], description }`) plus * `onInsertClause` to handle the selection. Same insertion mechanism the * AI agent's `insert_legal_clause` tool uses — this is the manual, * agent-free path to the same clause library. Omit both to hide the * group entirely (default). */ clauses?: TemplatePickerItem[]; /** Called with the chosen clause's `id` when the user picks one from the Clauses dropdown. */ onInsertClause?: (clauseId: string) => void; } declare function Toolbar({ editor, extraItems, extraGroups, templates, onInsertTemplate, clauses, onInsertClause }: ToolbarProps): React.ReactElement; /** * StatusBar — the bottom bar showing word count, char count, and page info. * * Page count is calculated based on the number of pageBreak nodes in the * document (each pageBreak starts a new page, so page count = pageBreaks + 1). * * Exported so consumers can use it in custom layouts. */ interface StatusBarProps { editor: DocxEditor; } declare function StatusBar({ editor }: StatusBarProps): React.ReactElement; /** A vertical separator group in the toolbar. */ interface ToolbarGroupProps { children: React.ReactNode; /** Category name (e.g. "Font", "Align", "Insert") exposed as a hover * tooltip / accessible label on the group, so the tools are still * discoverable by category — without printing a visible caption under * every group, which doesn't match a compact, single-row toolbar. */ label?: string; } declare function ToolbarGroup({ children, label }: ToolbarGroupProps): React.ReactElement; /** A small icon button in the toolbar. */ interface ToolbarIconButtonProps { title: string; active?: boolean; disabled?: boolean; onClick: () => void; children: React.ReactNode; style?: React.CSSProperties; className?: string; } declare function ToolbarIconButton({ title, active, disabled, onClick, children, style, className, }: ToolbarIconButtonProps): React.ReactElement; /** A button with text + optional icon in the toolbar. */ interface ToolbarTextButtonProps { title?: string; active?: boolean; disabled?: boolean; onClick: () => void; children: React.ReactNode; style?: React.CSSProperties; } declare function ToolbarTextButton({ title, active, disabled, onClick, children, style, }: ToolbarTextButtonProps): React.ReactElement; /** * A toolbar dropdown that collapses a set of mutually-exclusive icon * choices (e.g. text alignment, list type) into a single trigger showing * only the currently-active option, matching Google Docs' compact toolbar. */ interface ToolbarIconMenuOption { value: string; label: string; icon: React.ReactNode; active: boolean; onSelect: () => void; } interface ToolbarIconMenuProps { title: string; options: ToolbarIconMenuOption[]; } declare function ToolbarIconMenu({ title, options }: ToolbarIconMenuProps): React.ReactElement; /** Shared color palette for `ColorButton` (text color / highlight pickers). */ declare const PALETTE: string[]; /** Color picker button with a swatch underline. */ interface ColorButtonProps { title: string; currentColor: string; onPick: (color: string) => void; onClear: () => void; mode?: 'text' | 'highlight'; } declare function ColorButton({ title, currentColor, onPick, onClear, mode, }: ColorButtonProps): React.ReactElement; /** * TableSizePicker — a Google-Docs-style "Insert table" control. * * Combines two ways to choose a size: * 1. A grid (default 10×8) that highlights rows/cols as you move over it * and shows a live "C × R" label, then inserts on release. Works with * both a mouse (hover + click) and touch (drag a finger across the * grid, lift to insert) — touch has no `mouseenter`, so we track the * finger with `elementFromPoint` on `touchmove` instead. * 2. A "Custom size" row with two number inputs for dimensions bigger * than the grid (e.g. 12×20), with its own Insert button. */ interface TableSizePickerProps { /** Called with the chosen size when the user picks one. */ onInsert: (rows: number, cols: number) => void; /** Max columns/rows shown in the hover grid. Default 10×8. */ maxCols?: number; maxRows?: number; /** Tooltip for the trigger button. */ title?: string; } declare function TableSizePicker({ onInsert, maxCols, maxRows, title, }: TableSizePickerProps): React.ReactElement; /** * Dropdown — a custom dropdown component with white background, hover states, * keyboard navigation, and a check mark on the selected option. * * Replaces the native