import { ElementNode, NodeKey, EditorConfig, SerializedElementNode, LexicalUpdateJSON, LexicalNode, LexicalEditor } from 'lexical'; import { Transformer } from '@lexical/markdown'; type DiffType = 'deleted' | 'inserted'; interface SerializedDiffHighlightNode extends SerializedElementNode { diffType: DiffType; diffId: string; } declare class DiffHighlightNode extends ElementNode { __diffType: DiffType; __diffId: string; static getType(): string; static clone(node: DiffHighlightNode): DiffHighlightNode; constructor(diffType: DiffType, diffId: string, key?: NodeKey); createDOM(config: EditorConfig): HTMLElement; updateDOM(): boolean; isInline(): boolean; canInsertTextBefore(): boolean; canInsertTextAfter(): boolean; canBeEmpty(): boolean; getDiffType(): DiffType; getDiffId(): string; static importJSON(json: SerializedDiffHighlightNode): DiffHighlightNode; exportJSON(): SerializedDiffHighlightNode; updateFromJSON(json: LexicalUpdateJSON): this; setDiffType(diffType: DiffType): this; setDiffId(diffId: string): this; } declare function $createDiffHighlightNode(diffType: DiffType, diffId: string): DiffHighlightNode; declare function $isDiffHighlightNode(node: LexicalNode | null | undefined): node is DiffHighlightNode; /** * Lexical tree operations for block-level diff highlighting. * * All `$`-prefixed functions must be called inside `editor.update()` or `editorState.read()`. */ /** Collect text content of each root-level block node. */ declare function $collectBlockTexts(): string[]; /** Highlight blocks whose text differs from oldBlockTexts. */ declare function $highlightChangedBlocks(oldBlockTexts: string[]): void; /** Insert deleted block text at approximate positions. */ declare function $insertDeletedBlocks(oldBlockTexts: string[], newBlockTexts: string[]): void; /** Remove all diff highlight nodes — unwrap inserted, remove deleted paragraphs. */ declare function $clearDiffHighlights(): void; /** * Lexical selection utilities for AI text operations. * * All `$`-prefixed functions must be called inside `editor.update()` or `editorState.read()`. */ /** * Serializable snapshot of a Lexical range selection. * Captured before async AI operations so the selection can be restored afterwards. */ interface SavedSelection { anchorKey: string; anchorOffset: number; focusKey: string; focusOffset: number; } /** * Capture the current range selection as a serializable object. * Returns `null` if there is no range selection or it is collapsed. * * Must be called inside `editor.update()` or `editorState.read()`. * * @example * ```ts * let saved: SavedSelection | null = null; * editor.getEditorState().read(() => { * saved = $captureSelection(); * }); * ``` */ declare function $captureSelection(): SavedSelection | null; /** * Get the text content of the current range selection. * Returns an empty string if there is no valid range selection. * * Must be called inside `editor.update()` or `editorState.read()`. */ declare function $getSelectedText(): string; /** * Replace the text in a saved selection range with new text. * Handles both single-node and cross-node selections. * * Must be called inside `editor.update()`. * * @param saved - The selection snapshot captured before the async operation. * @param newText - The replacement text. * @returns `true` if the replacement succeeded, `false` if nodes were not found. * * @example * ```ts * editor.update(() => { * $replaceSelection(savedSelection, rewrittenText); * }, { tag: 'ai-rewrite' }); * ``` */ declare function $replaceSelection(saved: SavedSelection, newText: string): boolean; interface AIDiffRequest { /** The new markdown content to diff against the current editor state. */ newMarkdown: string; } interface AIDiffPluginProps { /** Pass a diff request to trigger the diff pipeline. Set to `null` to idle. */ diff: AIDiffRequest | null; /** Markdown transformers used to parse `newMarkdown` into Lexical nodes. */ transformers: Transformer[]; /** * `'auto'` (default): highlights fade and clear on a timer. * `'review'`: highlights persist until the consumer resolves. No timers. */ mode?: 'auto' | 'review'; /** How long highlights stay visible before fading out. Default: 3000ms. Only used in `'auto'` mode. */ highlightDurationMs?: number; /** CSS classes to add to highlights during fade-out. Default: `['diff-fade-out']`. Only used in `'auto'` mode. */ fadeOutClasses?: string[]; /** Editor update tag applied to diff operations. Default: `'ai-diff'`. */ tag?: string; /** Called after diff highlights are applied to the editor. */ onApplied?: () => void; /** Called after highlights are cleared (fade-out complete). Only used in `'auto'` mode. */ onCleared?: () => void; } declare function AIDiffPlugin({ diff, transformers, mode, highlightDurationMs, fadeOutClasses, tag, onApplied, onCleared, }: AIDiffPluginProps): null; /** * Headless React hook for AI-powered text rewriting in Lexical. * * Manages the full lifecycle: capture selection → call your AI → apply result. * Provider-agnostic — you supply the async rewrite function. * * @example * ```tsx * function MyToolbar() { * const [editor] = useLexicalComposerContext(); * * const { isProcessing, rewrite, selectedText } = useAIEdit({ * editor, * onRewrite: async (text, instruction) => { * const res = await fetch('/api/rewrite', { * method: 'POST', * body: JSON.stringify({ text, instruction }), * }); * const data = await res.json(); * return data.result; * }, * }); * * return ( * * ); * } * ``` */ /** Preset for common rewrite operations. */ interface AIEditPreset { /** Unique identifier. */ id: string; /** Display label. */ label: string; /** Instruction passed to the AI provider. */ instruction: string; } /** * Built-in English presets. Override with your own via `useAIEdit({ presets })`. */ declare const DEFAULT_PRESETS: AIEditPreset[]; interface UseAIEditOptions { /** The Lexical editor instance. */ editor: LexicalEditor; /** * Your AI rewrite function. Receives the selected text and an instruction string. * Return the rewritten text. Throw to signal an error. * * This is where you call your own API — the hook doesn't care which AI provider you use. */ onRewrite: (selectedText: string, instruction: string) => Promise; /** * Called when rewrite fails. If not provided, errors are silently logged. */ onError?: (error: unknown) => void; /** * Editor update tag applied when replacing text. Default: `'ai-rewrite'`. * Use this to filter out AI changes in your OnChangePlugin or autosave logic. */ tag?: string; /** Available presets. Default: `DEFAULT_PRESETS`. */ presets?: AIEditPreset[]; } interface UseAIEditReturn { /** Whether an AI operation is currently in progress. */ isProcessing: boolean; /** The currently selected text (empty string if no selection). */ selectedText: string; /** The saved selection snapshot. `null` when nothing is selected. */ savedSelection: SavedSelection | null; /** Available presets (from options or defaults). */ presets: AIEditPreset[]; /** * Trigger a rewrite on the current selection. * * @param instruction - Free-form instruction for the AI, e.g. "Make it shorter" * @returns The rewritten text, or `null` if the operation failed. */ rewrite: (instruction: string) => Promise; /** * Trigger a rewrite using a preset ID. * * @param presetId - One of the preset IDs. * @returns The rewritten text, or `null` if the operation failed. */ rewriteWithPreset: (presetId: string) => Promise; /** * Manually refresh the selected text / saved selection state. * Normally this is tracked automatically via selection change listener. */ refreshSelection: () => void; } declare function useAIEdit({ editor, onRewrite, onError, tag, presets, }: UseAIEditOptions): UseAIEditReturn; export { $captureSelection, $clearDiffHighlights, $collectBlockTexts, $createDiffHighlightNode, $getSelectedText, $highlightChangedBlocks, $insertDeletedBlocks, $isDiffHighlightNode, $replaceSelection, AIDiffPlugin, type AIDiffPluginProps, type AIDiffRequest, type AIEditPreset, DEFAULT_PRESETS, DiffHighlightNode, type DiffType, type SavedSelection, type SerializedDiffHighlightNode, type UseAIEditOptions, type UseAIEditReturn, useAIEdit };