import { TemplateResult, CSSResultArray, PropertyValues } from 'lit'; import { EditorView } from 'prosemirror-view'; import NileElement from '../internal/nile-element'; import type { CommandAttrs } from './engine/commands'; import type { MathRenderer } from './engine/plugins/math-view'; import type { MentionsConfig, SnippetConfig, MergeFieldConfig, TemplateConfig } from './types'; import './nile-wysiwyg-toolbar/nile-wysiwyg-toolbar'; import './nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item'; import './ui/link-editor'; import './ui/bubble-menu'; import './ui/bubble-item'; import './ui/image-panel'; import './ui/table-menu'; import './ui/suggest-list'; import './ui/find-replace'; import './ui/source-view'; import './ui/embed-panel'; import './ui/bookmark-panel'; import './ui/code-block-menu'; import './ui/shortcut-help'; import './ui/image-menu'; import './ui/embed-menu'; import './ui/special-chars'; import './ui/emoji-picker'; import './ui/insert-menu'; import './ui/math-panel'; import './ui/markdown-panel'; /** * WYSIWYG rich text editor powered by ProseMirror. * * Requires evergreen browsers: selection inside shadow DOM relies on * `Selection.getComposedRanges()` (Safari 17+, recent Firefox) or Chrome's * `shadowRoot.getSelection()`. * * @tag nile-wysiwyg-editor * * @event nile-change - Debounced content change. detail: { value, json, content } * @event nile-input - Every document change. detail: { value, json, content } * @event nile-focus - Editor gained focus. * @event nile-blur - Editor lost focus. * @event nile-selection-change - detail: ToolbarState snapshot. * @event nile-image-upload-request - Cancelable. detail: { file, insert(src, alt) }. * Call preventDefault() and use insert() to fully own the upload. * @event nile-image-upload-error - detail: { file, error } when uploadHandler rejects. * @event nile-mention-query - detail: { trigger, query, provide(items) } for async sources. * @event nile-mention-select - detail: { trigger, key, label } when a mention is inserted. * @event nile-word-count - detail: { words, characters } on every document change. * @event nile-autosave - detail: { value, json, content } on the autosave interval. * * @csspart base - Outer container. * @csspart toolbar - The toolbar element. * @csspart surface - The scrollable editing surface. */ export declare class NileWysiwygEditor extends NileElement { static formAssociated: boolean; /** Document content as HTML. Not reflected to the attribute. */ value: string; placeholder: string; disabled: boolean; readonly: boolean; /** Toolbar config string; `"none"` hides the toolbar. */ toolbar: string; /** * Toolbar built from authored `` light-DOM * children. When present it takes precedence over the `toolbar` string. */ private authoredToolbar?; /** * Bubble menu built from authored `` light-DOM * children. When present it replaces the built-in bold/italic/underline/ * strike/link set. */ private authoredBubble?; /** Watches authored toolbar/bubble children so edits re-render them. */ private toolbarObserver?; /** Shows a balloon toolbar over text selections. */ bubbleMenu: boolean; /** Sanitizes incoming HTML (value, paste, insertHTML). Leave on. */ sanitize: boolean; /** Constrains content to a single paragraph. */ singleLine: boolean; /** Form field name. */ name: string; required: boolean; /** * Async image upload handler. When set, pasted/dropped/picked image files * are uploaded through it (an "Uploading…" placeholder shows meanwhile) * and the returned src is inserted. */ uploadHandler?: (file: File) => Promise<{ src: string; alt?: string; }>; /** * Fallback when no uploadHandler is set and no nile-image-upload-request * listener takes over: embed image files as base64 data URLs. */ allowBase64Images: boolean; /** * Shows an "Upload from computer" button in the Insert Image dialog. Off by * default (URL-only, like the RTE's default); files can always be added via * drag & drop or paste regardless. */ uploadFromComputer: boolean; /** Max upload size (bytes) shown/enforced by the image dialog. Default 2 MB. */ imageMaxFileSize: number; /** * Mention config: trigger character → items, same shape and JSON attribute * format as nile-rich-text-editor. For async sources, configure the * trigger with an empty array and listen to nile-mention-query. */ mentions: MentionsConfig; /** Disables the "/" command menu. */ noSlashCommands: boolean; /** Disables the ":" emoji picker. */ noEmoji: boolean; /** Maximum character count; further input is rejected. 0 = unlimited. */ maxLength: number; /** Always show the word/character counter footer (implied by maxlength). */ showCounter: boolean; /** Keeps the toolbar pinned to the viewport while scrolling long content. */ stickyToolbar: boolean; /** Reusable snippets surfaced in the "/" menu (JSON attribute). */ snippets: SnippetConfig[]; /** Merge fields offered in the merge-field picker (JSON attribute). */ mergeFields: MergeFieldConfig[]; /** Content templates offered in the template picker (JSON attribute). */ templates: TemplateConfig[]; /** * Renders a math node's LaTeX to HTML (KaTeX/MathJax/MathML). When unset, * the raw LaTeX is shown. Same trust level as uploadHandler — output is * inserted as HTML. */ mathRenderer?: MathRenderer; /** Emit nile-autosave every N ms while the document has changed. 0 = off. */ autosaveInterval: number; /** When on, every paste drops formatting and inserts plain text. */ pasteAsPlainText: boolean; /** Disables Markdown detection on plain-text pastes. */ noMarkdownPaste: boolean; /** Disables typography autocorrect (smart quotes, dashes, (c) → ©). */ noTextTransform: boolean; /** Expands the editor to fill the viewport. Esc exits. */ fullscreen: boolean; /** Outlines block boundaries (paragraphs, headings, lists…) while editing. */ showBlocks: boolean; private toolbarState?; private hasFocus; private sourceMode; private counter; /** Which list the shared insert-menu is showing, if open. */ private insertMenuMode; private baseEl; private toolbarEl?; private surfaceEl; private linkEditorEl; private bubbleMenuEl; private imagePanelEl; private tableMenuEl; private suggestListEl; private findBarEl; private sourceViewEl?; private embedPanelEl; private bookmarkPanelEl; private codeMenuEl; private shortcutHelpEl; private imageMenuEl; private embedMenuEl; private specialCharsEl; private emojiPickerEl; private insertMenuEl; private mathPanelEl; private markdownPanelEl; private view?; private mentionMatch; private slashMatch; private emojiMatch; private activeSuggestSource; private suggestDismissed; private mentionQueryToken; private autosaveTimer?; private lastAutosaveValue; private internals?; private defaultValue; private syncingValue; private changeTimer?; private pendingChange; /** Body overflow value before fullscreen locked it; null = not locked. */ private previousBodyOverflow; static get styles(): CSSResultArray; constructor(); connectedCallback(): void; disconnectedCallback(): void; protected firstUpdated(): void; /** * Reads `` children into a structured toolbar and * stores it on {@link authoredToolbar}. Absent items clear the override * (falling back to the `toolbar` string). No-ops when nothing changed to * avoid needless re-renders. */ private syncAuthoredToolbar; /** * Reads `` children into a structured bubble-menu * item list stored on {@link authoredBubble}. Absent items clear the override * (falling back to the built-in default). No-ops when nothing changed. */ private syncAuthoredBubble; protected updated(changed: PropertyValues): void; private restartAutosave; private buildState; /** * ToolbarState snapshot augmented with host-only flags the pure * computeToolbarState can't know about (e.g. the paste-as-plain-text toggle). */ private snapshotToolbar; private mountView; /** * True while a drag-selection is in progress with the format painter armed. * Suppresses the mid-drag auto-apply in {@link handleTransaction} so the * copied formatting lands on the final, complete selection (on mouse-up). */ private painterDragActive; private onPainterPointerDown; private onPainterPointerUp; private handleTransaction; private buildChangeDetail; private scheduleChange; private flushChange; private syncFormValue; private updateValidity; formResetCallback(): void; formDisabledCallback(disabled: boolean): void; checkValidity(): boolean; reportValidity(): boolean; /** Positions an overlay element near a doc position (default: selection). */ private positionOverlay; /** Positions a floating panel directly below a toolbar/anchor element. */ private positionOverlayBelowAnchor; /** Finds a toolbar button by its aria-label, to anchor a panel below it. */ private toolbarButton; private updateOverlays; private openSpecialChars; private closeSpecialChars; private insertSpecialChar; private openInsertMenu; private closeInsertMenu; private handleInsertSelect; /** Inserts a merge-field placeholder at the selection. */ insertMergeField(id: string, label?: string): void; /** Changes the case of the selected text. */ changeCase(mode: 'upper' | 'lower' | 'title' | 'sentence'): void; private openMathPanel; private closeMathPanel; private openMarkdownPanel; private closeMarkdownPanel; private applyMath; /** Inserts a math formula (LaTeX) at the selection. */ insertMath(latex: string, display?: 'inline' | 'block'): void; private openEmojiPicker; private closeEmojiPicker; private openEmbedPanel; private closeEmbedPanel; private openBookmarkPanel; private closeBookmarkPanel; private openImagePanel; private closeImagePanel; private insertImageAt; /** * Routes an image file through the upload pipeline: a cancelable * `nile-image-upload-request` first, then `uploadHandler` (with an * uploading placeholder), then the base64 fallback. */ insertImageFile(file: File, pos?: number | null, meta?: { alt?: string | null; caption?: string | null; }): Promise; private dismissKeyFor; private handleSuggestKey; private closeSuggestList; private updateSuggestList; private handleSuggestSelect; /** Opens the find & replace bar (also bound to Mod-f inside the editor). */ openFindBar(): void; closeFindBar(): void; find(query: string): number; findNext(): void; findPrev(): void; replace(replacement: string): void; replaceAll(replacement: string): void; private scrollToActiveMatch; /** Toggles raw-HTML source editing. Content re-sanitizes on the way back. */ toggleSourceView(): void; private applySourceView; /** Word and character counts for the current document. */ getWordCount(): { words: number; characters: number; }; /** * Arms the format painter (copies the formatting at the selection) or, if * already armed, disarms it. Once armed, the next non-empty selection the * user makes receives the copied formatting. */ toggleFormatPainter(): void; /** The live, styled content element used as the source for exports. */ private get contentRoot(); /** Sanitizes `this.name` (or a fallback) into a safe download basename. */ private exportFilename; private downloadBlob; /** * The document as HTML with every style folded into inline `style` * attributes — the form email clients (which strip `