@relevaince/mentions 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/components/MentionsInput.tsx","../src/hooks/useMentionsEditor.ts","../src/core/mentionExtension.ts","../src/core/suggestionPlugin.ts","../src/core/markdownSerializer.ts","../src/core/markdownParser.ts","../src/hooks/useSuggestion.ts","../src/components/SuggestionList.tsx","../src/utils/ariaHelpers.ts"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* Components */\n/* ------------------------------------------------------------------ */\nexport { MentionsInput } from \"./components/MentionsInput\";\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\nexport type { MentionToken } from \"./types/MentionToken\";\nexport type {\n MentionProvider,\n MentionItem,\n} from \"./types/MentionProvider\";\nexport type { MentionsOutput } from \"./types/MentionsOutput\";\nexport type { MentionsInputProps } from \"./types/MentionsInputProps\";\n\n/* ------------------------------------------------------------------ */\n/* Utilities */\n/* ------------------------------------------------------------------ */\nexport { serializeToMarkdown } from \"./core/markdownSerializer\";\nexport { parseFromMarkdown } from \"./core/markdownParser\";\n","import React, { useCallback } from \"react\";\nimport { EditorContent } from \"@tiptap/react\";\nimport { useMentionsEditor } from \"../hooks/useMentionsEditor\";\nimport { useSuggestion } from \"../hooks/useSuggestion\";\nimport { SuggestionList } from \"./SuggestionList\";\nimport { comboboxAttrs } from \"../utils/ariaHelpers\";\nimport type { MentionsInputProps } from \"../types/MentionsInputProps\";\n\nconst LISTBOX_ID = \"mentions-suggestion-listbox\";\n\n/**\n * `<MentionsInput>` — the single public component.\n *\n * A structured text editor with typed entity tokens.\n * Consumers register `providers` for each trigger character,\n * and receive structured output via `onChange` and `onSubmit`.\n */\nexport function MentionsInput({\n value,\n providers,\n onChange,\n placeholder = \"Type a message...\",\n autoFocus = false,\n disabled = false,\n className,\n onSubmit,\n maxLength,\n renderItem,\n renderChip,\n}: MentionsInputProps) {\n const { uiState, actions, callbacksRef } = useSuggestion(providers);\n\n const { editor } = useMentionsEditor({\n providers,\n value,\n onChange,\n onSubmit,\n placeholder,\n autoFocus,\n editable: !disabled,\n callbacksRef,\n });\n\n const isExpanded = uiState.state !== \"idle\";\n\n const handleHover = useCallback((index: number) => {\n // TODO: expose a direct setActiveIndex action if needed\n }, []);\n\n return (\n <div\n className={className}\n data-mentions-input=\"\"\n data-disabled={disabled ? \"\" : undefined}\n {...comboboxAttrs(isExpanded, LISTBOX_ID)}\n aria-activedescendant={\n isExpanded && uiState.items[uiState.activeIndex]\n ? `mention-option-${uiState.items[uiState.activeIndex].id}`\n : undefined\n }\n >\n <EditorContent editor={editor} />\n\n {isExpanded && (\n <SuggestionList\n items={uiState.items}\n activeIndex={uiState.activeIndex}\n breadcrumbs={uiState.breadcrumbs}\n loading={uiState.loading}\n trigger={uiState.trigger}\n clientRect={uiState.clientRect}\n onSelect={(item) => actions.select(item)}\n onHover={handleHover}\n onGoBack={actions.goBack}\n renderItem={renderItem}\n />\n )}\n </div>\n );\n}\n","import { useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { useEditor } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport { Extension } from \"@tiptap/core\";\nimport { MentionNode } from \"../core/mentionExtension\";\nimport { createSuggestionExtension } from \"../core/suggestionPlugin\";\nimport {\n serializeToMarkdown,\n extractTokens,\n extractPlainText,\n} from \"../core/markdownSerializer\";\nimport { parseFromMarkdown } from \"../core/markdownParser\";\nimport type { MentionProvider } from \"../types/MentionProvider\";\nimport type { MentionsOutput } from \"../types/MentionsOutput\";\nimport type { SuggestionCallbacksRef } from \"../core/suggestionPlugin\";\n\n/* ------------------------------------------------------------------ */\n/* Submit keyboard shortcut extension */\n/* ------------------------------------------------------------------ */\n\nfunction createSubmitExtension(\n onSubmitRef: React.RefObject<((output: MentionsOutput) => void) | undefined>,\n) {\n return Extension.create({\n name: \"submitShortcut\",\n\n addKeyboardShortcuts() {\n return {\n \"Mod-Enter\": () => {\n if (onSubmitRef.current) {\n const json = this.editor.getJSON();\n onSubmitRef.current({\n markdown: serializeToMarkdown(json),\n tokens: extractTokens(json),\n plainText: extractPlainText(json),\n });\n }\n return true;\n },\n };\n },\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* Enter key — submit (only when suggestions are NOT active) */\n/* ------------------------------------------------------------------ */\n\nfunction createEnterExtension(\n onSubmitRef: React.RefObject<((output: MentionsOutput) => void) | undefined>,\n) {\n return Extension.create({\n name: \"enterSubmit\",\n priority: 50,\n\n addKeyboardShortcuts() {\n return {\n Enter: () => {\n if (onSubmitRef.current) {\n const json = this.editor.getJSON();\n onSubmitRef.current({\n markdown: serializeToMarkdown(json),\n tokens: extractTokens(json),\n plainText: extractPlainText(json),\n });\n }\n return true;\n },\n };\n },\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* Hook */\n/* ------------------------------------------------------------------ */\n\ntype UseMentionsEditorOptions = {\n providers: MentionProvider[];\n value?: string;\n onChange?: (output: MentionsOutput) => void;\n onSubmit?: (output: MentionsOutput) => void;\n placeholder?: string;\n autoFocus?: boolean;\n editable?: boolean;\n callbacksRef: SuggestionCallbacksRef;\n};\n\nexport function useMentionsEditor({\n providers,\n value,\n onChange,\n onSubmit,\n placeholder,\n autoFocus = false,\n editable = true,\n callbacksRef,\n}: UseMentionsEditorOptions) {\n // Stable refs for handlers that the PM plugin reads\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n\n const onSubmitRef = useRef(onSubmit);\n onSubmitRef.current = onSubmit;\n\n // Build initial content from markdown value (only once)\n const initialContent = useMemo(() => {\n if (!value) return undefined;\n return parseFromMarkdown(value);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Extract trigger strings for memoisation key\n const triggersKey = providers.map((p) => p.trigger).join(\",\");\n const triggers = useMemo(\n () => providers.map((p) => p.trigger),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [triggersKey],\n );\n\n // Single suggestion extension — uses callbacksRef so always fresh\n const suggestionExtension = useMemo(\n () => createSuggestionExtension(triggers, callbacksRef),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [triggersKey],\n );\n\n // Submit extensions — also use refs, so stable across renders\n const submitExt = useMemo(() => createSubmitExtension(onSubmitRef), []);\n const enterExt = useMemo(() => createEnterExtension(onSubmitRef), []);\n\n const editor = useEditor({\n extensions: [\n StarterKit.configure({\n heading: false,\n blockquote: false,\n codeBlock: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n horizontalRule: false,\n }),\n MentionNode,\n suggestionExtension,\n submitExt,\n enterExt,\n ],\n content: initialContent,\n autofocus: autoFocus ? \"end\" : false,\n editable,\n editorProps: {\n attributes: {\n \"data-placeholder\": placeholder ?? \"\",\n class: \"mentions-editor\",\n },\n },\n onUpdate: ({ editor }) => {\n const json = editor.getJSON();\n onChangeRef.current?.({\n markdown: serializeToMarkdown(json),\n tokens: extractTokens(json),\n plainText: extractPlainText(json),\n });\n },\n });\n\n // Sync editable state\n useEffect(() => {\n if (editor && editor.isEditable !== editable) {\n editor.setEditable(editable);\n }\n }, [editor, editable]);\n\n // Helper to get current output\n const getOutput = useCallback((): MentionsOutput | null => {\n if (!editor) return null;\n const json = editor.getJSON();\n return {\n markdown: serializeToMarkdown(json),\n tokens: extractTokens(json),\n plainText: extractPlainText(json),\n };\n }, [editor]);\n\n return { editor, getOutput };\n}\n","import { mergeAttributes, Node } from \"@tiptap/core\";\nimport { NodeViewRendererProps } from \"@tiptap/core\";\nimport { ReactNodeViewRenderer } from \"@tiptap/react\";\n\nexport interface MentionNodeAttrs {\n id: string;\n label: string;\n entityType: string;\n}\n\n/**\n * Trigger-character prefix map.\n * Used when rendering a mention chip to prepend the correct character.\n */\nconst DEFAULT_PREFIXES: Record<string, string> = {\n workspace: \"@\",\n contract: \"@\",\n file: \"#\",\n web: \":\",\n};\n\n/**\n * Custom Tiptap Node extension for inline mention tokens.\n *\n * Each mention is an atomic inline node with `id`, `label`, and `entityType`\n * attributes. Renders as a `<span>` with `data-mention` and `data-type`\n * attributes for styling.\n */\nexport const MentionNode = Node.create({\n name: \"mention\",\n group: \"inline\",\n inline: true,\n atom: true,\n selectable: true,\n draggable: false,\n\n addAttributes() {\n return {\n id: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"data-id\"),\n renderHTML: (attributes) => ({ \"data-id\": attributes.id }),\n },\n label: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"data-label\"),\n renderHTML: (attributes) => ({ \"data-label\": attributes.label }),\n },\n entityType: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"data-type\"),\n renderHTML: (attributes) => ({ \"data-type\": attributes.entityType }),\n },\n };\n },\n\n parseHTML() {\n return [{ tag: 'span[data-mention]' }];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n const entityType = node.attrs.entityType as string;\n const label = node.attrs.label as string;\n const prefix = DEFAULT_PREFIXES[entityType] ?? \"@\";\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-mention\": \"\",\n class: \"mention-chip\",\n }),\n `${prefix}${label}`,\n ];\n },\n\n renderText({ node }) {\n const entityType = node.attrs.entityType as string;\n const label = node.attrs.label as string;\n const prefix = DEFAULT_PREFIXES[entityType] ?? \"@\";\n return `${prefix}${label}`;\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () =>\n this.editor.commands.command(({ tr, state }) => {\n let isMention = false;\n const { selection } = state;\n const { empty, anchor } = selection;\n\n if (!empty) return false;\n\n state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {\n if (node.type.name === this.name) {\n isMention = true;\n tr.insertText(\"\", pos, pos + node.nodeSize);\n }\n });\n\n return isMention;\n }),\n };\n },\n});\n","import { Extension } from \"@tiptap/core\";\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\";\nimport type { EditorView } from \"@tiptap/pm/view\";\nimport type { MentionItem, MentionProvider } from \"../types/MentionProvider\";\n\n/* ------------------------------------------------------------------ */\n/* Suggestion state types */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionState =\n | \"idle\"\n | \"loading\"\n | \"showing\"\n | \"drilling\"\n | \"inserting\";\n\n/* ------------------------------------------------------------------ */\n/* Callbacks the React layer implements */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionCallbacks = {\n onStart: (props: SuggestionCallbackProps) => void;\n onUpdate: (props: SuggestionCallbackProps) => void;\n onExit: () => void;\n onKeyDown: (props: { event: KeyboardEvent }) => boolean;\n};\n\nexport type SuggestionCallbackProps = {\n query: string;\n trigger: string;\n clientRect: (() => DOMRect | null) | null;\n range: { from: number; to: number };\n command: (attrs: Record<string, unknown>) => void;\n};\n\n/* ------------------------------------------------------------------ */\n/* Trigger detection */\n/* ------------------------------------------------------------------ */\n\ntype TriggerMatch = {\n trigger: string;\n query: string;\n from: number;\n to: number;\n};\n\nfunction detectTrigger(\n text: string,\n cursorPos: number,\n docStartPos: number,\n triggers: string[],\n): TriggerMatch | null {\n const relCursor = cursorPos - docStartPos;\n const before = text.slice(0, relCursor);\n\n for (let i = before.length - 1; i >= 0; i--) {\n const ch = before[i];\n if (ch === \"\\n\") return null;\n\n for (const trigger of triggers) {\n if (before.substring(i, i + trigger.length) === trigger) {\n if (i === 0 || /\\s/.test(before[i - 1])) {\n const query = before.slice(i + trigger.length);\n return { trigger, query, from: docStartPos + i, to: cursorPos };\n }\n }\n }\n }\n\n return null;\n}\n\n/* ------------------------------------------------------------------ */\n/* Plugin key */\n/* ------------------------------------------------------------------ */\n\nconst suggestionPluginKey = new PluginKey(\"mentionSuggestion\");\n\n/* ------------------------------------------------------------------ */\n/* Ref-based callback container (avoids stale closure issues) */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionCallbacksRef = { current: SuggestionCallbacks };\n\n/* ------------------------------------------------------------------ */\n/* Build the single multi-trigger suggestion extension */\n/* ------------------------------------------------------------------ */\n\nexport function createSuggestionExtension(\n triggers: string[],\n callbacksRef: SuggestionCallbacksRef,\n) {\n return Extension.create({\n name: \"mentionSuggestion\",\n\n addProseMirrorPlugins() {\n const editor = this.editor;\n let active = false;\n let lastQuery: string | null = null;\n let lastTrigger: string | null = null;\n\n const getClientRect = (\n view: EditorView,\n from: number,\n ): (() => DOMRect | null) => {\n return () => {\n try {\n const coords = view.coordsAtPos(from);\n return new DOMRect(\n coords.left,\n coords.top,\n 0,\n coords.bottom - coords.top,\n );\n } catch {\n return null;\n }\n };\n };\n\n const makeCommand = (range: { from: number; to: number }) => {\n return (attrs: Record<string, unknown>) => {\n editor\n .chain()\n .focus()\n .insertContentAt(range, [\n {\n type: \"mention\",\n attrs: {\n id: attrs.id,\n label: attrs.label,\n entityType: attrs.entityType,\n },\n },\n { type: \"text\", text: \" \" },\n ])\n .run();\n };\n };\n\n const plugin = new Plugin({\n key: suggestionPluginKey,\n\n props: {\n handleKeyDown(_view, event) {\n if (!active) return false;\n // Always read from the ref — never a stale closure\n return callbacksRef.current.onKeyDown({ event });\n },\n },\n\n view() {\n return {\n update(view, _prevState) {\n const { state } = view;\n const { selection } = state;\n\n if (!selection.empty) {\n if (active) {\n active = false;\n lastQuery = null;\n lastTrigger = null;\n callbacksRef.current.onExit();\n }\n return;\n }\n\n const $pos = selection.$from;\n const textBlock = $pos.parent;\n\n if (!textBlock.isTextblock) {\n if (active) {\n active = false;\n lastQuery = null;\n lastTrigger = null;\n callbacksRef.current.onExit();\n }\n return;\n }\n\n const blockStart = $pos.start();\n const blockText = textBlock.textContent;\n const cursorPos = $pos.pos;\n\n const match = detectTrigger(blockText, cursorPos, blockStart, triggers);\n\n if (match) {\n const range = { from: match.from, to: match.to };\n const props: SuggestionCallbackProps = {\n query: match.query,\n trigger: match.trigger,\n clientRect: getClientRect(view, match.from),\n range,\n command: makeCommand(range),\n };\n\n if (!active) {\n active = true;\n lastQuery = match.query;\n lastTrigger = match.trigger;\n callbacksRef.current.onStart(props);\n } else if (\n match.query !== lastQuery ||\n match.trigger !== lastTrigger\n ) {\n // Only fire onUpdate when something actually changed\n lastQuery = match.query;\n lastTrigger = match.trigger;\n callbacksRef.current.onUpdate(props);\n }\n } else {\n if (active) {\n active = false;\n lastQuery = null;\n lastTrigger = null;\n callbacksRef.current.onExit();\n }\n }\n },\n\n destroy() {\n if (active) {\n callbacksRef.current.onExit();\n }\n },\n };\n },\n });\n\n return [plugin];\n },\n });\n}\n","import type { JSONContent } from \"@tiptap/core\";\nimport type { MentionToken } from \"../types/MentionToken\";\n\n/**\n * Serialize a Tiptap JSON document to a markdown string.\n *\n * Mention nodes are encoded as `@[label](id)` tokens.\n * All other text passes through verbatim.\n */\nexport function serializeToMarkdown(doc: JSONContent): string {\n if (!doc.content) return \"\";\n\n const parts: string[] = [];\n\n for (const block of doc.content) {\n if (block.type === \"paragraph\") {\n parts.push(serializeParagraph(block));\n } else if (block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n\n return parts.join(\"\\n\");\n}\n\nfunction serializeParagraph(node: JSONContent): string {\n if (!node.content) return \"\";\n\n return node.content\n .map((child) => {\n if (child.type === \"mention\") {\n const { id, label } = child.attrs ?? {};\n return `@[${label}](${id})`;\n }\n return child.text ?? \"\";\n })\n .join(\"\");\n}\n\n/**\n * Extract all `MentionToken`s from a Tiptap JSON document, in document order.\n */\nexport function extractTokens(doc: JSONContent): MentionToken[] {\n const tokens: MentionToken[] = [];\n\n function walk(node: JSONContent) {\n if (node.type === \"mention\" && node.attrs) {\n tokens.push({\n id: node.attrs.id as string,\n type: node.attrs.entityType as string,\n label: node.attrs.label as string,\n });\n }\n if (node.content) {\n for (const child of node.content) {\n walk(child);\n }\n }\n }\n\n walk(doc);\n return tokens;\n}\n\n/**\n * Extract plain text from a Tiptap JSON document.\n * Mention nodes are replaced with their label (no trigger prefix in plain text).\n */\nexport function extractPlainText(doc: JSONContent): string {\n if (!doc.content) return \"\";\n\n const parts: string[] = [];\n\n for (const block of doc.content) {\n if (block.type === \"paragraph\") {\n parts.push(extractParagraphText(block));\n }\n }\n\n return parts.join(\"\\n\");\n}\n\nfunction extractParagraphText(node: JSONContent): string {\n if (!node.content) return \"\";\n\n return node.content\n .map((child) => {\n if (child.type === \"mention\") {\n return child.attrs?.label ?? \"\";\n }\n return child.text ?? \"\";\n })\n .join(\"\");\n}\n","import type { JSONContent } from \"@tiptap/core\";\n\n/**\n * Regex matching the `@[label](id)` mention token syntax.\n *\n * Optionally captures an `entityType` when encoded as `@[label](type:id)`.\n * Falls back to `\"unknown\"` when no type prefix is present.\n */\nconst MENTION_RE = /@\\[([^\\]]+)\\]\\((?:([^:)]+):)?([^)]+)\\)/g;\n\n/**\n * Parse a markdown string (with `@[label](id)` or `@[label](type:id)` tokens)\n * into a Tiptap-compatible JSON document.\n */\nexport function parseFromMarkdown(markdown: string): JSONContent {\n const lines = markdown.split(\"\\n\");\n\n const content: JSONContent[] = lines.map((line) => ({\n type: \"paragraph\",\n content: parseLine(line),\n }));\n\n return { type: \"doc\", content };\n}\n\nfunction parseLine(line: string): JSONContent[] {\n const nodes: JSONContent[] = [];\n let lastIndex = 0;\n\n // Reset regex state\n MENTION_RE.lastIndex = 0;\n\n let match: RegExpExecArray | null;\n while ((match = MENTION_RE.exec(line)) !== null) {\n const fullMatch = match[0];\n const label = match[1];\n const entityType = match[2] ?? \"unknown\";\n const id = match[3];\n\n // Text before this mention\n if (match.index > lastIndex) {\n nodes.push({\n type: \"text\",\n text: line.slice(lastIndex, match.index),\n });\n }\n\n // Mention node\n nodes.push({\n type: \"mention\",\n attrs: {\n id,\n label,\n entityType,\n },\n });\n\n lastIndex = match.index + fullMatch.length;\n }\n\n // Trailing text\n if (lastIndex < line.length) {\n nodes.push({\n type: \"text\",\n text: line.slice(lastIndex),\n });\n }\n\n // Empty line — ProseMirror needs at least one node\n if (nodes.length === 0) {\n nodes.push({ type: \"text\", text: \"\" });\n }\n\n return nodes;\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport type { MentionItem, MentionProvider } from \"../types/MentionProvider\";\nimport type {\n SuggestionCallbackProps,\n SuggestionCallbacks,\n SuggestionCallbacksRef,\n SuggestionState,\n} from \"../core/suggestionPlugin\";\n\n/* ------------------------------------------------------------------ */\n/* Public state exposed to the UI layer */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionUIState = {\n state: SuggestionState;\n items: MentionItem[];\n breadcrumbs: MentionItem[];\n activeIndex: number;\n loading: boolean;\n clientRect: (() => DOMRect | null) | null;\n trigger: string | null;\n query: string;\n};\n\nexport type SuggestionActions = {\n navigateUp: () => void;\n navigateDown: () => void;\n select: (item?: MentionItem) => void;\n goBack: () => void;\n close: () => void;\n};\n\nconst IDLE_STATE: SuggestionUIState = {\n state: \"idle\",\n items: [],\n breadcrumbs: [],\n activeIndex: 0,\n loading: false,\n clientRect: null,\n trigger: null,\n query: \"\",\n};\n\n/* ------------------------------------------------------------------ */\n/* Hook */\n/* ------------------------------------------------------------------ */\n\nexport function useSuggestion(providers: MentionProvider[]) {\n const [uiState, setUIState] = useState<SuggestionUIState>(IDLE_STATE);\n\n // --- Refs for values that callbacks need to read (avoids stale closures) ---\n const stateRef = useRef(uiState);\n stateRef.current = uiState;\n\n const providersRef = useRef(providers);\n providersRef.current = providers;\n\n const commandRef = useRef<((attrs: Record<string, unknown>) => void) | null>(\n null,\n );\n const providerRef = useRef<MentionProvider | null>(null);\n\n /* ---------------------------------------------------------------- */\n /* Fetch items from the provider */\n /* ---------------------------------------------------------------- */\n\n const fetchItems = useCallback(\n async (\n provider: MentionProvider,\n query: string,\n parent?: MentionItem,\n ) => {\n setUIState((prev) => ({ ...prev, loading: true, state: \"loading\" }));\n\n try {\n const items =\n parent && provider.getChildren\n ? await provider.getChildren(parent, query)\n : await provider.getRootItems(query);\n\n setUIState((prev) => ({\n ...prev,\n items,\n loading: false,\n state: \"showing\",\n activeIndex: 0,\n }));\n } catch {\n setUIState((prev) => ({\n ...prev,\n items: [],\n loading: false,\n state: \"showing\",\n }));\n }\n },\n [],\n );\n\n /* ---------------------------------------------------------------- */\n /* Suggestion plugin callbacks */\n /* ---------------------------------------------------------------- */\n\n const onStart = useCallback(\n (props: SuggestionCallbackProps) => {\n const provider = providersRef.current.find(\n (p) => p.trigger === props.trigger,\n );\n if (!provider) return;\n\n providerRef.current = provider;\n commandRef.current = props.command;\n\n setUIState({\n state: \"loading\",\n items: [],\n breadcrumbs: [],\n activeIndex: 0,\n loading: true,\n clientRect: props.clientRect,\n trigger: props.trigger,\n query: props.query,\n });\n\n fetchItems(provider, props.query);\n },\n [fetchItems],\n );\n\n const onUpdate = useCallback(\n (props: SuggestionCallbackProps) => {\n const provider = providerRef.current;\n if (!provider) return;\n\n commandRef.current = props.command;\n\n setUIState((prev) => ({\n ...prev,\n clientRect: props.clientRect,\n query: props.query,\n }));\n\n // Only refetch at root level — nested levels manage their own queries\n // Read from ref to avoid stale closure\n if (stateRef.current.breadcrumbs.length === 0) {\n fetchItems(provider, props.query);\n }\n },\n [fetchItems],\n );\n\n const onExit = useCallback(() => {\n providerRef.current = null;\n commandRef.current = null;\n setUIState(IDLE_STATE);\n }, []);\n\n /* ---------------------------------------------------------------- */\n /* Navigation actions */\n /* ---------------------------------------------------------------- */\n\n const navigateUp = useCallback(() => {\n setUIState((prev) => ({\n ...prev,\n activeIndex: Math.max(0, prev.activeIndex - 1),\n }));\n }, []);\n\n const navigateDown = useCallback(() => {\n setUIState((prev) => ({\n ...prev,\n activeIndex: Math.min(prev.items.length - 1, prev.activeIndex + 1),\n }));\n }, []);\n\n const select = useCallback(\n (item?: MentionItem) => {\n const current = stateRef.current;\n const selected = item ?? current.items[current.activeIndex];\n if (!selected) return;\n\n const provider = providerRef.current;\n\n // If the item has children and the provider supports it, drill down\n if (selected.hasChildren && provider?.getChildren) {\n setUIState((prev) => ({\n ...prev,\n state: \"drilling\",\n breadcrumbs: [...prev.breadcrumbs, selected],\n items: [],\n activeIndex: 0,\n query: \"\",\n }));\n\n fetchItems(provider, \"\", selected);\n return;\n }\n\n // Otherwise insert the mention\n if (commandRef.current) {\n commandRef.current({\n id: selected.id,\n label: selected.label,\n entityType: selected.type,\n });\n }\n },\n [fetchItems],\n );\n\n const goBack = useCallback(() => {\n const provider = providerRef.current;\n if (!provider) return;\n\n setUIState((prev) => {\n const newBreadcrumbs = prev.breadcrumbs.slice(0, -1);\n const parent = newBreadcrumbs[newBreadcrumbs.length - 1];\n\n if (parent) {\n fetchItems(provider, \"\", parent);\n } else {\n fetchItems(provider, prev.query);\n }\n\n return {\n ...prev,\n breadcrumbs: newBreadcrumbs,\n items: [],\n activeIndex: 0,\n state: \"loading\" as const,\n };\n });\n }, [fetchItems]);\n\n const close = useCallback(() => {\n setUIState(IDLE_STATE);\n }, []);\n\n /* ---------------------------------------------------------------- */\n /* Keyboard handler */\n /* ---------------------------------------------------------------- */\n\n const onKeyDown = useCallback(\n ({ event }: { event: KeyboardEvent }): boolean => {\n const current = stateRef.current;\n if (current.state === \"idle\") return false;\n\n switch (event.key) {\n case \"ArrowUp\":\n event.preventDefault();\n navigateUp();\n return true;\n case \"ArrowDown\":\n event.preventDefault();\n navigateDown();\n return true;\n case \"Enter\": {\n event.preventDefault();\n const selectedItem = current.items[current.activeIndex];\n if (selectedItem) {\n select(selectedItem);\n }\n return true;\n }\n case \"ArrowRight\": {\n const activeItem = current.items[current.activeIndex];\n if (activeItem?.hasChildren) {\n event.preventDefault();\n select(activeItem);\n return true;\n }\n return false;\n }\n case \"ArrowLeft\":\n if (current.breadcrumbs.length > 0) {\n event.preventDefault();\n goBack();\n return true;\n }\n return false;\n case \"Escape\":\n event.preventDefault();\n close();\n return true;\n default:\n return false;\n }\n },\n [navigateUp, navigateDown, select, goBack, close],\n );\n\n /* ---------------------------------------------------------------- */\n /* Build a ref that always points to the latest callbacks. */\n /* The ProseMirror plugin reads from this ref — no stale closures. */\n /* ---------------------------------------------------------------- */\n\n const callbacksRef: SuggestionCallbacksRef = useRef<SuggestionCallbacks>({\n onStart,\n onUpdate,\n onExit,\n onKeyDown,\n });\n\n // Update on every render so the PM plugin always sees fresh functions\n callbacksRef.current = { onStart, onUpdate, onExit, onKeyDown };\n\n const actions: SuggestionActions = {\n navigateUp,\n navigateDown,\n select,\n goBack,\n close,\n };\n\n return { uiState, actions, callbacksRef };\n}\n","import React, { useEffect, useRef } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { MentionItem } from \"../types/MentionProvider\";\nimport { listboxAttrs, optionAttrs } from \"../utils/ariaHelpers\";\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionListProps = {\n items: MentionItem[];\n activeIndex: number;\n breadcrumbs: MentionItem[];\n loading: boolean;\n trigger: string | null;\n clientRect: (() => DOMRect | null) | null;\n onSelect: (item: MentionItem) => void;\n onHover: (index: number) => void;\n onGoBack: () => void;\n renderItem?: (item: MentionItem, depth: number) => ReactNode;\n};\n\nconst LISTBOX_ID = \"mentions-suggestion-listbox\";\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport function SuggestionList({\n items,\n activeIndex,\n breadcrumbs,\n loading,\n trigger,\n clientRect,\n onSelect,\n onHover,\n onGoBack,\n renderItem,\n}: SuggestionListProps) {\n const listRef = useRef<HTMLDivElement>(null);\n const depth = breadcrumbs.length;\n\n // Scroll the active item into view\n useEffect(() => {\n if (!listRef.current) return;\n const active = listRef.current.querySelector('[aria-selected=\"true\"]');\n active?.scrollIntoView({ block: \"nearest\" });\n }, [activeIndex]);\n\n // Position the popover based on clientRect\n const style = usePopoverPosition(clientRect);\n\n if (items.length === 0 && !loading) return null;\n\n return (\n <div\n data-suggestions=\"\"\n data-trigger={trigger}\n style={style}\n ref={listRef}\n >\n {/* Breadcrumbs for nested navigation */}\n {breadcrumbs.length > 0 && (\n <div data-suggestion-breadcrumb=\"\">\n <button\n type=\"button\"\n data-suggestion-back=\"\"\n onClick={onGoBack}\n aria-label=\"Go back\"\n >\n &larr;\n </button>\n {breadcrumbs.map((crumb, i) => (\n <span key={crumb.id} data-suggestion-breadcrumb-item=\"\">\n {i > 0 && <span data-suggestion-breadcrumb-sep=\"\">/</span>}\n {crumb.label}\n </span>\n ))}\n </div>\n )}\n\n {/* Loading indicator */}\n {loading && (\n <div data-suggestion-loading=\"\">Loading...</div>\n )}\n\n {/* Item list */}\n {!loading && (\n <div {...listboxAttrs(LISTBOX_ID, `${trigger ?? \"\"} suggestions`)}>\n {items.map((item, index) => {\n const isActive = index === activeIndex;\n const itemId = `mention-option-${item.id}`;\n\n return (\n <div\n key={item.id}\n {...optionAttrs(itemId, isActive, index)}\n data-suggestion-item=\"\"\n data-suggestion-item-active={isActive ? \"\" : undefined}\n data-has-children={item.hasChildren ? \"\" : undefined}\n onMouseEnter={() => onHover(index)}\n onClick={() => onSelect(item)}\n >\n {renderItem ? (\n renderItem(item, depth)\n ) : (\n <DefaultSuggestionItem item={item} />\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Default item render */\n/* ------------------------------------------------------------------ */\n\nfunction DefaultSuggestionItem({ item }: { item: MentionItem }) {\n return (\n <>\n {item.icon && (\n <span data-suggestion-item-icon=\"\">{item.icon}</span>\n )}\n <span data-suggestion-item-label=\"\">{item.label}</span>\n {item.description && (\n <span data-suggestion-item-description=\"\">{item.description}</span>\n )}\n {item.hasChildren && (\n <span data-suggestion-item-chevron=\"\" aria-hidden=\"true\">\n &rsaquo;\n </span>\n )}\n </>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Popover positioning */\n/* ------------------------------------------------------------------ */\n\nfunction usePopoverPosition(\n clientRect: (() => DOMRect | null) | null,\n): React.CSSProperties {\n if (!clientRect) {\n return { display: \"none\" };\n }\n\n const rect = clientRect();\n if (!rect) {\n return { display: \"none\" };\n }\n\n return {\n position: \"fixed\",\n left: `${rect.left}px`,\n top: `${rect.bottom + 4}px`,\n zIndex: 50,\n };\n}\n","/**\n * Generates ARIA attributes for the suggestion combobox pattern.\n */\n\nexport function comboboxAttrs(expanded: boolean, listboxId: string) {\n return {\n role: \"combobox\" as const,\n \"aria-haspopup\": \"listbox\" as const,\n \"aria-expanded\": expanded,\n \"aria-owns\": expanded ? listboxId : undefined,\n };\n}\n\nexport function listboxAttrs(id: string, label: string) {\n return {\n id,\n role: \"listbox\" as const,\n \"aria-label\": label,\n };\n}\n\nexport function optionAttrs(\n id: string,\n selected: boolean,\n index: number,\n) {\n return {\n id,\n role: \"option\" as const,\n \"aria-selected\": selected,\n \"aria-posinset\": index + 1,\n };\n}\n"],"mappings":"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","names":["import_react","import_react","import_core","import_core","StarterKit","editor","import_react","import_react","import_jsx_runtime","LISTBOX_ID"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/components/MentionsInput.tsx","../src/hooks/useMentionsEditor.ts","../src/core/mentionExtension.ts","../src/core/suggestionPlugin.ts","../src/core/markdownSerializer.ts","../src/core/markdownParser.ts","../src/hooks/useSuggestion.ts","../src/components/SuggestionList.tsx","../src/utils/ariaHelpers.ts"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* Components */\n/* ------------------------------------------------------------------ */\nexport { MentionsInput } from \"./components/MentionsInput\";\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\nexport type { MentionToken } from \"./types/MentionToken\";\nexport type {\n MentionProvider,\n MentionItem,\n} from \"./types/MentionProvider\";\nexport type { MentionsOutput } from \"./types/MentionsOutput\";\nexport type {\n MentionsInputProps,\n MentionsInputHandle,\n} from \"./types/MentionsInputProps\";\n\n/* ------------------------------------------------------------------ */\n/* Utilities */\n/* ------------------------------------------------------------------ */\nexport { serializeToMarkdown } from \"./core/markdownSerializer\";\nexport { parseFromMarkdown } from \"./core/markdownParser\";\n","import React, { forwardRef, useCallback, useImperativeHandle } from \"react\";\nimport { EditorContent } from \"@tiptap/react\";\nimport { useMentionsEditor } from \"../hooks/useMentionsEditor\";\nimport { useSuggestion } from \"../hooks/useSuggestion\";\nimport { SuggestionList } from \"./SuggestionList\";\nimport { comboboxAttrs } from \"../utils/ariaHelpers\";\nimport type {\n MentionsInputProps,\n MentionsInputHandle,\n} from \"../types/MentionsInputProps\";\n\nconst LISTBOX_ID = \"mentions-suggestion-listbox\";\n\n/**\n * `<MentionsInput>` — the single public component.\n *\n * A structured text editor with typed entity tokens.\n * Consumers register `providers` for each trigger character,\n * and receive structured output via `onChange` and `onSubmit`.\n *\n * Supports an imperative ref handle for programmatic control:\n * ```tsx\n * const ref = useRef<MentionsInputHandle>(null);\n * ref.current.clear();\n * ref.current.setContent(\"@[Marketing](ws_123) summarize\");\n * ref.current.focus();\n * ```\n */\nexport const MentionsInput = forwardRef<MentionsInputHandle, MentionsInputProps>(\n function MentionsInput(\n {\n value,\n providers,\n onChange,\n placeholder = \"Type a message...\",\n autoFocus = false,\n disabled = false,\n className,\n onSubmit,\n clearOnSubmit = true,\n maxLength,\n renderItem,\n renderChip,\n },\n ref,\n ) {\n const { uiState, actions, callbacksRef } = useSuggestion(providers);\n\n const { editor, clear, setContent, focus } = useMentionsEditor({\n providers,\n value,\n onChange,\n onSubmit,\n clearOnSubmit,\n placeholder,\n autoFocus,\n editable: !disabled,\n callbacksRef,\n });\n\n /* ---------------------------------------------------------------- */\n /* Expose imperative handle */\n /* ---------------------------------------------------------------- */\n\n useImperativeHandle(\n ref,\n () => ({ clear, setContent, focus }),\n [clear, setContent, focus],\n );\n\n const isExpanded = uiState.state !== \"idle\";\n\n const handleHover = useCallback((_index: number) => {\n // Direct setActiveIndex can be exposed if needed\n }, []);\n\n return (\n <div\n className={className}\n data-mentions-input=\"\"\n data-disabled={disabled ? \"\" : undefined}\n {...comboboxAttrs(isExpanded, LISTBOX_ID)}\n aria-activedescendant={\n isExpanded && uiState.items[uiState.activeIndex]\n ? `mention-option-${uiState.items[uiState.activeIndex].id}`\n : undefined\n }\n >\n <EditorContent editor={editor} />\n\n {isExpanded && (\n <SuggestionList\n items={uiState.items}\n activeIndex={uiState.activeIndex}\n breadcrumbs={uiState.breadcrumbs}\n loading={uiState.loading}\n trigger={uiState.trigger}\n clientRect={uiState.clientRect}\n onSelect={(item) => actions.select(item)}\n onHover={handleHover}\n onGoBack={actions.goBack}\n renderItem={renderItem}\n />\n )}\n </div>\n );\n },\n);\n","import { useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { useEditor } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport { Extension } from \"@tiptap/core\";\nimport { MentionNode } from \"../core/mentionExtension\";\nimport { createSuggestionExtension } from \"../core/suggestionPlugin\";\nimport {\n serializeToMarkdown,\n extractTokens,\n extractPlainText,\n} from \"../core/markdownSerializer\";\nimport { parseFromMarkdown } from \"../core/markdownParser\";\nimport type { MentionProvider } from \"../types/MentionProvider\";\nimport type { MentionsOutput } from \"../types/MentionsOutput\";\nimport type { SuggestionCallbacksRef } from \"../core/suggestionPlugin\";\n\n/* ------------------------------------------------------------------ */\n/* Helper: build MentionsOutput from the current editor */\n/* ------------------------------------------------------------------ */\n\nfunction buildOutput(editor: { getJSON: () => any }): MentionsOutput {\n const json = editor.getJSON();\n return {\n markdown: serializeToMarkdown(json),\n tokens: extractTokens(json),\n plainText: extractPlainText(json),\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* Submit keyboard shortcut extension (Cmd+Enter) */\n/* ------------------------------------------------------------------ */\n\nfunction createSubmitExtension(\n onSubmitRef: React.RefObject<((output: MentionsOutput) => void) | undefined>,\n clearOnSubmitRef: React.RefObject<boolean>,\n) {\n return Extension.create({\n name: \"submitShortcut\",\n\n addKeyboardShortcuts() {\n return {\n \"Mod-Enter\": () => {\n if (onSubmitRef.current) {\n onSubmitRef.current(buildOutput(this.editor));\n if (clearOnSubmitRef.current) {\n this.editor.commands.clearContent(true);\n }\n }\n return true;\n },\n };\n },\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* Enter key — submit (only when suggestions are NOT active) */\n/* ------------------------------------------------------------------ */\n\nfunction createEnterExtension(\n onSubmitRef: React.RefObject<((output: MentionsOutput) => void) | undefined>,\n clearOnSubmitRef: React.RefObject<boolean>,\n) {\n return Extension.create({\n name: \"enterSubmit\",\n priority: 50,\n\n addKeyboardShortcuts() {\n return {\n Enter: () => {\n if (onSubmitRef.current) {\n onSubmitRef.current(buildOutput(this.editor));\n if (clearOnSubmitRef.current) {\n this.editor.commands.clearContent(true);\n }\n }\n return true;\n },\n };\n },\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* Hook */\n/* ------------------------------------------------------------------ */\n\ntype UseMentionsEditorOptions = {\n providers: MentionProvider[];\n value?: string;\n onChange?: (output: MentionsOutput) => void;\n onSubmit?: (output: MentionsOutput) => void;\n clearOnSubmit?: boolean;\n placeholder?: string;\n autoFocus?: boolean;\n editable?: boolean;\n callbacksRef: SuggestionCallbacksRef;\n};\n\nexport function useMentionsEditor({\n providers,\n value,\n onChange,\n onSubmit,\n clearOnSubmit = true,\n placeholder,\n autoFocus = false,\n editable = true,\n callbacksRef,\n}: UseMentionsEditorOptions) {\n // Stable refs for handlers that the PM plugin reads\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n\n const onSubmitRef = useRef(onSubmit);\n onSubmitRef.current = onSubmit;\n\n const clearOnSubmitRef = useRef(clearOnSubmit);\n clearOnSubmitRef.current = clearOnSubmit;\n\n // Build initial content from markdown value (only once)\n const initialContent = useMemo(() => {\n if (!value) return undefined;\n return parseFromMarkdown(value);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Extract trigger strings for memoisation key\n const triggersKey = providers.map((p) => p.trigger).join(\",\");\n const triggers = useMemo(\n () => providers.map((p) => p.trigger),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [triggersKey],\n );\n\n // Single suggestion extension — uses callbacksRef so always fresh\n const suggestionExtension = useMemo(\n () => createSuggestionExtension(triggers, callbacksRef),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [triggersKey],\n );\n\n // Submit extensions — use refs, stable across renders\n const submitExt = useMemo(\n () => createSubmitExtension(onSubmitRef, clearOnSubmitRef),\n [],\n );\n const enterExt = useMemo(\n () => createEnterExtension(onSubmitRef, clearOnSubmitRef),\n [],\n );\n\n const editor = useEditor({\n extensions: [\n StarterKit.configure({\n heading: false,\n blockquote: false,\n codeBlock: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n horizontalRule: false,\n }),\n Placeholder.configure({\n placeholder: placeholder ?? \"Type a message...\",\n }),\n MentionNode,\n suggestionExtension,\n submitExt,\n enterExt,\n ],\n content: initialContent,\n autofocus: autoFocus ? \"end\" : false,\n editable,\n editorProps: {\n attributes: {\n class: \"mentions-editor\",\n },\n },\n onUpdate: ({ editor }) => {\n onChangeRef.current?.(buildOutput(editor));\n },\n });\n\n // Sync editable state\n useEffect(() => {\n if (editor && editor.isEditable !== editable) {\n editor.setEditable(editable);\n }\n }, [editor, editable]);\n\n /* ---------------------------------------------------------------- */\n /* Imperative methods */\n /* ---------------------------------------------------------------- */\n\n const clear = useCallback(() => {\n editor?.commands.clearContent(true);\n }, [editor]);\n\n const setContent = useCallback(\n (markdown: string) => {\n if (!editor) return;\n const doc = parseFromMarkdown(markdown);\n editor.commands.setContent(doc);\n },\n [editor],\n );\n\n const focus = useCallback(() => {\n editor?.commands.focus(\"end\");\n }, [editor]);\n\n const getOutput = useCallback((): MentionsOutput | null => {\n if (!editor) return null;\n return buildOutput(editor);\n }, [editor]);\n\n return { editor, getOutput, clear, setContent, focus };\n}\n","import { mergeAttributes, Node } from \"@tiptap/core\";\nimport { NodeViewRendererProps } from \"@tiptap/core\";\nimport { ReactNodeViewRenderer } from \"@tiptap/react\";\n\nexport interface MentionNodeAttrs {\n id: string;\n label: string;\n entityType: string;\n}\n\n/**\n * Trigger-character prefix map.\n * Used when rendering a mention chip to prepend the correct character.\n */\nconst DEFAULT_PREFIXES: Record<string, string> = {\n workspace: \"@\",\n contract: \"@\",\n file: \"#\",\n web: \":\",\n};\n\n/**\n * Custom Tiptap Node extension for inline mention tokens.\n *\n * Each mention is an atomic inline node with `id`, `label`, and `entityType`\n * attributes. Renders as a `<span>` with `data-mention` and `data-type`\n * attributes for styling.\n */\nexport const MentionNode = Node.create({\n name: \"mention\",\n group: \"inline\",\n inline: true,\n atom: true,\n selectable: true,\n draggable: false,\n\n addAttributes() {\n return {\n id: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"data-id\"),\n renderHTML: (attributes) => ({ \"data-id\": attributes.id }),\n },\n label: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"data-label\"),\n renderHTML: (attributes) => ({ \"data-label\": attributes.label }),\n },\n entityType: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"data-type\"),\n renderHTML: (attributes) => ({ \"data-type\": attributes.entityType }),\n },\n };\n },\n\n parseHTML() {\n return [{ tag: 'span[data-mention]' }];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n const entityType = node.attrs.entityType as string;\n const label = node.attrs.label as string;\n const prefix = DEFAULT_PREFIXES[entityType] ?? \"@\";\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-mention\": \"\",\n class: \"mention-chip\",\n }),\n `${prefix}${label}`,\n ];\n },\n\n renderText({ node }) {\n const entityType = node.attrs.entityType as string;\n const label = node.attrs.label as string;\n const prefix = DEFAULT_PREFIXES[entityType] ?? \"@\";\n return `${prefix}${label}`;\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () =>\n this.editor.commands.command(({ tr, state }) => {\n let isMention = false;\n const { selection } = state;\n const { empty, anchor } = selection;\n\n if (!empty) return false;\n\n state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {\n if (node.type.name === this.name) {\n isMention = true;\n tr.insertText(\"\", pos, pos + node.nodeSize);\n }\n });\n\n return isMention;\n }),\n };\n },\n});\n","import { Extension } from \"@tiptap/core\";\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\";\nimport type { EditorView } from \"@tiptap/pm/view\";\nimport type { MentionItem, MentionProvider } from \"../types/MentionProvider\";\n\n/* ------------------------------------------------------------------ */\n/* Suggestion state types */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionState =\n | \"idle\"\n | \"loading\"\n | \"showing\"\n | \"drilling\"\n | \"inserting\";\n\n/* ------------------------------------------------------------------ */\n/* Callbacks the React layer implements */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionCallbacks = {\n onStart: (props: SuggestionCallbackProps) => void;\n onUpdate: (props: SuggestionCallbackProps) => void;\n onExit: () => void;\n onKeyDown: (props: { event: KeyboardEvent }) => boolean;\n};\n\nexport type SuggestionCallbackProps = {\n query: string;\n trigger: string;\n clientRect: (() => DOMRect | null) | null;\n range: { from: number; to: number };\n command: (attrs: Record<string, unknown>) => void;\n};\n\n/* ------------------------------------------------------------------ */\n/* Trigger detection */\n/* ------------------------------------------------------------------ */\n\ntype TriggerMatch = {\n trigger: string;\n query: string;\n from: number;\n to: number;\n};\n\nfunction detectTrigger(\n text: string,\n cursorPos: number,\n docStartPos: number,\n triggers: string[],\n): TriggerMatch | null {\n const relCursor = cursorPos - docStartPos;\n const before = text.slice(0, relCursor);\n\n for (let i = before.length - 1; i >= 0; i--) {\n const ch = before[i];\n if (ch === \"\\n\") return null;\n\n for (const trigger of triggers) {\n if (before.substring(i, i + trigger.length) === trigger) {\n if (i === 0 || /\\s/.test(before[i - 1])) {\n const query = before.slice(i + trigger.length);\n return { trigger, query, from: docStartPos + i, to: cursorPos };\n }\n }\n }\n }\n\n return null;\n}\n\n/* ------------------------------------------------------------------ */\n/* Plugin key */\n/* ------------------------------------------------------------------ */\n\nconst suggestionPluginKey = new PluginKey(\"mentionSuggestion\");\n\n/* ------------------------------------------------------------------ */\n/* Ref-based callback container (avoids stale closure issues) */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionCallbacksRef = { current: SuggestionCallbacks };\n\n/* ------------------------------------------------------------------ */\n/* Build the single multi-trigger suggestion extension */\n/* ------------------------------------------------------------------ */\n\nexport function createSuggestionExtension(\n triggers: string[],\n callbacksRef: SuggestionCallbacksRef,\n) {\n return Extension.create({\n name: \"mentionSuggestion\",\n\n addProseMirrorPlugins() {\n const editor = this.editor;\n let active = false;\n let lastQuery: string | null = null;\n let lastTrigger: string | null = null;\n\n const getClientRect = (\n view: EditorView,\n from: number,\n ): (() => DOMRect | null) => {\n return () => {\n try {\n const coords = view.coordsAtPos(from);\n return new DOMRect(\n coords.left,\n coords.top,\n 0,\n coords.bottom - coords.top,\n );\n } catch {\n return null;\n }\n };\n };\n\n const makeCommand = (range: { from: number; to: number }) => {\n return (attrs: Record<string, unknown>) => {\n editor\n .chain()\n .focus()\n .insertContentAt(range, [\n {\n type: \"mention\",\n attrs: {\n id: attrs.id,\n label: attrs.label,\n entityType: attrs.entityType,\n },\n },\n { type: \"text\", text: \" \" },\n ])\n .run();\n };\n };\n\n const plugin = new Plugin({\n key: suggestionPluginKey,\n\n props: {\n handleKeyDown(_view, event) {\n if (!active) return false;\n // Always read from the ref — never a stale closure\n return callbacksRef.current.onKeyDown({ event });\n },\n },\n\n view() {\n return {\n update(view, _prevState) {\n const { state } = view;\n const { selection } = state;\n\n if (!selection.empty) {\n if (active) {\n active = false;\n lastQuery = null;\n lastTrigger = null;\n callbacksRef.current.onExit();\n }\n return;\n }\n\n const $pos = selection.$from;\n const textBlock = $pos.parent;\n\n if (!textBlock.isTextblock) {\n if (active) {\n active = false;\n lastQuery = null;\n lastTrigger = null;\n callbacksRef.current.onExit();\n }\n return;\n }\n\n const blockStart = $pos.start();\n const blockText = textBlock.textContent;\n const cursorPos = $pos.pos;\n\n const match = detectTrigger(blockText, cursorPos, blockStart, triggers);\n\n if (match) {\n const range = { from: match.from, to: match.to };\n const props: SuggestionCallbackProps = {\n query: match.query,\n trigger: match.trigger,\n clientRect: getClientRect(view, match.from),\n range,\n command: makeCommand(range),\n };\n\n if (!active) {\n active = true;\n lastQuery = match.query;\n lastTrigger = match.trigger;\n callbacksRef.current.onStart(props);\n } else if (\n match.query !== lastQuery ||\n match.trigger !== lastTrigger\n ) {\n // Only fire onUpdate when something actually changed\n lastQuery = match.query;\n lastTrigger = match.trigger;\n callbacksRef.current.onUpdate(props);\n }\n } else {\n if (active) {\n active = false;\n lastQuery = null;\n lastTrigger = null;\n callbacksRef.current.onExit();\n }\n }\n },\n\n destroy() {\n if (active) {\n callbacksRef.current.onExit();\n }\n },\n };\n },\n });\n\n return [plugin];\n },\n });\n}\n","import type { JSONContent } from \"@tiptap/core\";\nimport type { MentionToken } from \"../types/MentionToken\";\n\n/**\n * Serialize a Tiptap JSON document to a markdown string.\n *\n * Mention nodes are encoded as `@[label](id)` tokens.\n * All other text passes through verbatim.\n */\nexport function serializeToMarkdown(doc: JSONContent): string {\n if (!doc.content) return \"\";\n\n const parts: string[] = [];\n\n for (const block of doc.content) {\n if (block.type === \"paragraph\") {\n parts.push(serializeParagraph(block));\n } else if (block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n\n return parts.join(\"\\n\");\n}\n\nfunction serializeParagraph(node: JSONContent): string {\n if (!node.content) return \"\";\n\n return node.content\n .map((child) => {\n if (child.type === \"mention\") {\n const { id, label } = child.attrs ?? {};\n return `@[${label}](${id})`;\n }\n return child.text ?? \"\";\n })\n .join(\"\");\n}\n\n/**\n * Extract all `MentionToken`s from a Tiptap JSON document, in document order.\n */\nexport function extractTokens(doc: JSONContent): MentionToken[] {\n const tokens: MentionToken[] = [];\n\n function walk(node: JSONContent) {\n if (node.type === \"mention\" && node.attrs) {\n tokens.push({\n id: node.attrs.id as string,\n type: node.attrs.entityType as string,\n label: node.attrs.label as string,\n });\n }\n if (node.content) {\n for (const child of node.content) {\n walk(child);\n }\n }\n }\n\n walk(doc);\n return tokens;\n}\n\n/**\n * Extract plain text from a Tiptap JSON document.\n * Mention nodes are replaced with their label (no trigger prefix in plain text).\n */\nexport function extractPlainText(doc: JSONContent): string {\n if (!doc.content) return \"\";\n\n const parts: string[] = [];\n\n for (const block of doc.content) {\n if (block.type === \"paragraph\") {\n parts.push(extractParagraphText(block));\n }\n }\n\n return parts.join(\"\\n\");\n}\n\nfunction extractParagraphText(node: JSONContent): string {\n if (!node.content) return \"\";\n\n return node.content\n .map((child) => {\n if (child.type === \"mention\") {\n return child.attrs?.label ?? \"\";\n }\n return child.text ?? \"\";\n })\n .join(\"\");\n}\n","import type { JSONContent } from \"@tiptap/core\";\n\n/**\n * Regex matching the `@[label](id)` mention token syntax.\n *\n * Optionally captures an `entityType` when encoded as `@[label](type:id)`.\n * Falls back to `\"unknown\"` when no type prefix is present.\n */\nconst MENTION_RE = /@\\[([^\\]]+)\\]\\((?:([^:)]+):)?([^)]+)\\)/g;\n\n/**\n * Parse a markdown string (with `@[label](id)` or `@[label](type:id)` tokens)\n * into a Tiptap-compatible JSON document.\n */\nexport function parseFromMarkdown(markdown: string): JSONContent {\n const lines = markdown.split(\"\\n\");\n\n const content: JSONContent[] = lines.map((line) => ({\n type: \"paragraph\",\n content: parseLine(line),\n }));\n\n return { type: \"doc\", content };\n}\n\nfunction parseLine(line: string): JSONContent[] {\n const nodes: JSONContent[] = [];\n let lastIndex = 0;\n\n // Reset regex state\n MENTION_RE.lastIndex = 0;\n\n let match: RegExpExecArray | null;\n while ((match = MENTION_RE.exec(line)) !== null) {\n const fullMatch = match[0];\n const label = match[1];\n const entityType = match[2] ?? \"unknown\";\n const id = match[3];\n\n // Text before this mention\n if (match.index > lastIndex) {\n nodes.push({\n type: \"text\",\n text: line.slice(lastIndex, match.index),\n });\n }\n\n // Mention node\n nodes.push({\n type: \"mention\",\n attrs: {\n id,\n label,\n entityType,\n },\n });\n\n lastIndex = match.index + fullMatch.length;\n }\n\n // Trailing text\n if (lastIndex < line.length) {\n nodes.push({\n type: \"text\",\n text: line.slice(lastIndex),\n });\n }\n\n // Empty line — ProseMirror needs at least one node\n if (nodes.length === 0) {\n nodes.push({ type: \"text\", text: \"\" });\n }\n\n return nodes;\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport type { MentionItem, MentionProvider } from \"../types/MentionProvider\";\nimport type {\n SuggestionCallbackProps,\n SuggestionCallbacks,\n SuggestionCallbacksRef,\n SuggestionState,\n} from \"../core/suggestionPlugin\";\n\n/* ------------------------------------------------------------------ */\n/* Public state exposed to the UI layer */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionUIState = {\n state: SuggestionState;\n items: MentionItem[];\n breadcrumbs: MentionItem[];\n activeIndex: number;\n loading: boolean;\n clientRect: (() => DOMRect | null) | null;\n trigger: string | null;\n query: string;\n};\n\nexport type SuggestionActions = {\n navigateUp: () => void;\n navigateDown: () => void;\n select: (item?: MentionItem) => void;\n goBack: () => void;\n close: () => void;\n};\n\nconst IDLE_STATE: SuggestionUIState = {\n state: \"idle\",\n items: [],\n breadcrumbs: [],\n activeIndex: 0,\n loading: false,\n clientRect: null,\n trigger: null,\n query: \"\",\n};\n\n/* ------------------------------------------------------------------ */\n/* Hook */\n/* ------------------------------------------------------------------ */\n\nexport function useSuggestion(providers: MentionProvider[]) {\n const [uiState, setUIState] = useState<SuggestionUIState>(IDLE_STATE);\n\n // --- Refs for values that callbacks need to read (avoids stale closures) ---\n const stateRef = useRef(uiState);\n stateRef.current = uiState;\n\n const providersRef = useRef(providers);\n providersRef.current = providers;\n\n const commandRef = useRef<((attrs: Record<string, unknown>) => void) | null>(\n null,\n );\n const providerRef = useRef<MentionProvider | null>(null);\n\n /* ---------------------------------------------------------------- */\n /* Fetch items from the provider */\n /* ---------------------------------------------------------------- */\n\n const fetchItems = useCallback(\n async (\n provider: MentionProvider,\n query: string,\n parent?: MentionItem,\n ) => {\n setUIState((prev) => ({ ...prev, loading: true, state: \"loading\" }));\n\n try {\n const items =\n parent && provider.getChildren\n ? await provider.getChildren(parent, query)\n : await provider.getRootItems(query);\n\n setUIState((prev) => ({\n ...prev,\n items,\n loading: false,\n state: \"showing\",\n activeIndex: 0,\n }));\n } catch {\n setUIState((prev) => ({\n ...prev,\n items: [],\n loading: false,\n state: \"showing\",\n }));\n }\n },\n [],\n );\n\n /* ---------------------------------------------------------------- */\n /* Suggestion plugin callbacks */\n /* ---------------------------------------------------------------- */\n\n const onStart = useCallback(\n (props: SuggestionCallbackProps) => {\n const provider = providersRef.current.find(\n (p) => p.trigger === props.trigger,\n );\n if (!provider) return;\n\n providerRef.current = provider;\n commandRef.current = props.command;\n\n setUIState({\n state: \"loading\",\n items: [],\n breadcrumbs: [],\n activeIndex: 0,\n loading: true,\n clientRect: props.clientRect,\n trigger: props.trigger,\n query: props.query,\n });\n\n fetchItems(provider, props.query);\n },\n [fetchItems],\n );\n\n const onUpdate = useCallback(\n (props: SuggestionCallbackProps) => {\n const provider = providerRef.current;\n if (!provider) return;\n\n commandRef.current = props.command;\n\n setUIState((prev) => ({\n ...prev,\n clientRect: props.clientRect,\n query: props.query,\n }));\n\n // Only refetch at root level — nested levels manage their own queries\n // Read from ref to avoid stale closure\n if (stateRef.current.breadcrumbs.length === 0) {\n fetchItems(provider, props.query);\n }\n },\n [fetchItems],\n );\n\n const onExit = useCallback(() => {\n providerRef.current = null;\n commandRef.current = null;\n setUIState(IDLE_STATE);\n }, []);\n\n /* ---------------------------------------------------------------- */\n /* Navigation actions */\n /* ---------------------------------------------------------------- */\n\n const navigateUp = useCallback(() => {\n setUIState((prev) => ({\n ...prev,\n activeIndex: Math.max(0, prev.activeIndex - 1),\n }));\n }, []);\n\n const navigateDown = useCallback(() => {\n setUIState((prev) => ({\n ...prev,\n activeIndex: Math.min(prev.items.length - 1, prev.activeIndex + 1),\n }));\n }, []);\n\n const select = useCallback(\n (item?: MentionItem) => {\n const current = stateRef.current;\n const selected = item ?? current.items[current.activeIndex];\n if (!selected) return;\n\n const provider = providerRef.current;\n\n // If the item has children and the provider supports it, drill down\n if (selected.hasChildren && provider?.getChildren) {\n setUIState((prev) => ({\n ...prev,\n state: \"drilling\",\n breadcrumbs: [...prev.breadcrumbs, selected],\n items: [],\n activeIndex: 0,\n query: \"\",\n }));\n\n fetchItems(provider, \"\", selected);\n return;\n }\n\n // Otherwise insert the mention\n if (commandRef.current) {\n commandRef.current({\n id: selected.id,\n label: selected.label,\n entityType: selected.type,\n });\n }\n },\n [fetchItems],\n );\n\n const goBack = useCallback(() => {\n const provider = providerRef.current;\n if (!provider) return;\n\n setUIState((prev) => {\n const newBreadcrumbs = prev.breadcrumbs.slice(0, -1);\n const parent = newBreadcrumbs[newBreadcrumbs.length - 1];\n\n if (parent) {\n fetchItems(provider, \"\", parent);\n } else {\n fetchItems(provider, prev.query);\n }\n\n return {\n ...prev,\n breadcrumbs: newBreadcrumbs,\n items: [],\n activeIndex: 0,\n state: \"loading\" as const,\n };\n });\n }, [fetchItems]);\n\n const close = useCallback(() => {\n setUIState(IDLE_STATE);\n }, []);\n\n /* ---------------------------------------------------------------- */\n /* Keyboard handler */\n /* ---------------------------------------------------------------- */\n\n const onKeyDown = useCallback(\n ({ event }: { event: KeyboardEvent }): boolean => {\n const current = stateRef.current;\n if (current.state === \"idle\") return false;\n\n switch (event.key) {\n case \"ArrowUp\":\n event.preventDefault();\n navigateUp();\n return true;\n case \"ArrowDown\":\n event.preventDefault();\n navigateDown();\n return true;\n case \"Enter\": {\n event.preventDefault();\n const selectedItem = current.items[current.activeIndex];\n if (selectedItem) {\n select(selectedItem);\n }\n return true;\n }\n case \"ArrowRight\": {\n const activeItem = current.items[current.activeIndex];\n if (activeItem?.hasChildren) {\n event.preventDefault();\n select(activeItem);\n return true;\n }\n return false;\n }\n case \"ArrowLeft\":\n if (current.breadcrumbs.length > 0) {\n event.preventDefault();\n goBack();\n return true;\n }\n return false;\n case \"Escape\":\n event.preventDefault();\n close();\n return true;\n default:\n return false;\n }\n },\n [navigateUp, navigateDown, select, goBack, close],\n );\n\n /* ---------------------------------------------------------------- */\n /* Build a ref that always points to the latest callbacks. */\n /* The ProseMirror plugin reads from this ref — no stale closures. */\n /* ---------------------------------------------------------------- */\n\n const callbacksRef: SuggestionCallbacksRef = useRef<SuggestionCallbacks>({\n onStart,\n onUpdate,\n onExit,\n onKeyDown,\n });\n\n // Update on every render so the PM plugin always sees fresh functions\n callbacksRef.current = { onStart, onUpdate, onExit, onKeyDown };\n\n const actions: SuggestionActions = {\n navigateUp,\n navigateDown,\n select,\n goBack,\n close,\n };\n\n return { uiState, actions, callbacksRef };\n}\n","import React, { useEffect, useRef } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { MentionItem } from \"../types/MentionProvider\";\nimport { listboxAttrs, optionAttrs } from \"../utils/ariaHelpers\";\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionListProps = {\n items: MentionItem[];\n activeIndex: number;\n breadcrumbs: MentionItem[];\n loading: boolean;\n trigger: string | null;\n clientRect: (() => DOMRect | null) | null;\n onSelect: (item: MentionItem) => void;\n onHover: (index: number) => void;\n onGoBack: () => void;\n renderItem?: (item: MentionItem, depth: number) => ReactNode;\n};\n\nconst LISTBOX_ID = \"mentions-suggestion-listbox\";\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport function SuggestionList({\n items,\n activeIndex,\n breadcrumbs,\n loading,\n trigger,\n clientRect,\n onSelect,\n onHover,\n onGoBack,\n renderItem,\n}: SuggestionListProps) {\n const listRef = useRef<HTMLDivElement>(null);\n const depth = breadcrumbs.length;\n\n // Scroll the active item into view\n useEffect(() => {\n if (!listRef.current) return;\n const active = listRef.current.querySelector('[aria-selected=\"true\"]');\n active?.scrollIntoView({ block: \"nearest\" });\n }, [activeIndex]);\n\n // Position the popover based on clientRect\n const style = usePopoverPosition(clientRect);\n\n if (items.length === 0 && !loading) return null;\n\n return (\n <div\n data-suggestions=\"\"\n data-trigger={trigger}\n style={style}\n ref={listRef}\n >\n {/* Breadcrumbs for nested navigation */}\n {breadcrumbs.length > 0 && (\n <div data-suggestion-breadcrumb=\"\">\n <button\n type=\"button\"\n data-suggestion-back=\"\"\n onClick={onGoBack}\n aria-label=\"Go back\"\n >\n &larr;\n </button>\n {breadcrumbs.map((crumb, i) => (\n <span key={crumb.id} data-suggestion-breadcrumb-item=\"\">\n {i > 0 && <span data-suggestion-breadcrumb-sep=\"\">/</span>}\n {crumb.label}\n </span>\n ))}\n </div>\n )}\n\n {/* Loading indicator */}\n {loading && (\n <div data-suggestion-loading=\"\">Loading...</div>\n )}\n\n {/* Item list */}\n {!loading && (\n <div {...listboxAttrs(LISTBOX_ID, `${trigger ?? \"\"} suggestions`)}>\n {items.map((item, index) => {\n const isActive = index === activeIndex;\n const itemId = `mention-option-${item.id}`;\n\n return (\n <div\n key={item.id}\n {...optionAttrs(itemId, isActive, index)}\n data-suggestion-item=\"\"\n data-suggestion-item-active={isActive ? \"\" : undefined}\n data-has-children={item.hasChildren ? \"\" : undefined}\n onMouseEnter={() => onHover(index)}\n onClick={() => onSelect(item)}\n >\n {renderItem ? (\n renderItem(item, depth)\n ) : (\n <DefaultSuggestionItem item={item} />\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Default item render */\n/* ------------------------------------------------------------------ */\n\nfunction DefaultSuggestionItem({ item }: { item: MentionItem }) {\n return (\n <>\n {item.icon && (\n <span data-suggestion-item-icon=\"\">{item.icon}</span>\n )}\n <span data-suggestion-item-label=\"\">{item.label}</span>\n {item.description && (\n <span data-suggestion-item-description=\"\">{item.description}</span>\n )}\n {item.hasChildren && (\n <span data-suggestion-item-chevron=\"\" aria-hidden=\"true\">\n &rsaquo;\n </span>\n )}\n </>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Popover positioning */\n/* ------------------------------------------------------------------ */\n\nfunction usePopoverPosition(\n clientRect: (() => DOMRect | null) | null,\n): React.CSSProperties {\n if (!clientRect) {\n return { display: \"none\" };\n }\n\n const rect = clientRect();\n if (!rect) {\n return { display: \"none\" };\n }\n\n return {\n position: \"fixed\",\n left: `${rect.left}px`,\n top: `${rect.bottom + 4}px`,\n zIndex: 50,\n };\n}\n","/**\n * Generates ARIA attributes for the suggestion combobox pattern.\n */\n\nexport function comboboxAttrs(expanded: boolean, listboxId: string) {\n return {\n role: \"combobox\" as const,\n \"aria-haspopup\": \"listbox\" as const,\n \"aria-expanded\": expanded,\n \"aria-owns\": expanded ? listboxId : undefined,\n };\n}\n\nexport function listboxAttrs(id: string, label: string) {\n return {\n id,\n role: \"listbox\" as const,\n \"aria-label\": label,\n };\n}\n\nexport function optionAttrs(\n id: string,\n selected: boolean,\n index: number,\n) {\n return {\n id,\n role: \"option\" as const,\n \"aria-selected\": selected,\n \"aria-posinset\": index + 1,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAAoE;AACpE,IAAAA,gBAA8B;;;ACD9B,mBAAwD;AACxD,IAAAC,gBAA0B;AAC1B,yBAAuB;AACvB,mCAAwB;AACxB,IAAAC,eAA0B;;;ACJ1B,kBAAsC;AActC,IAAM,mBAA2C;AAAA,EAC/C,WAAW;AAAA,EACX,UAAU;AAAA,EACV,MAAM;AAAA,EACN,KAAK;AACP;AASO,IAAM,cAAc,iBAAK,OAAO;AAAA,EACrC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,WAAW;AAAA,EAEX,gBAAgB;AACd,WAAO;AAAA,MACL,IAAI;AAAA,QACF,SAAS;AAAA,QACT,WAAW,CAAC,YAAY,QAAQ,aAAa,SAAS;AAAA,QACtD,YAAY,CAAC,gBAAgB,EAAE,WAAW,WAAW,GAAG;AAAA,MAC1D;AAAA,MACA,OAAO;AAAA,QACL,SAAS;AAAA,QACT,WAAW,CAAC,YAAY,QAAQ,aAAa,YAAY;AAAA,QACzD,YAAY,CAAC,gBAAgB,EAAE,cAAc,WAAW,MAAM;AAAA,MAChE;AAAA,MACA,YAAY;AAAA,QACV,SAAS;AAAA,QACT,WAAW,CAAC,YAAY,QAAQ,aAAa,WAAW;AAAA,QACxD,YAAY,CAAC,gBAAgB,EAAE,aAAa,WAAW,WAAW;AAAA,MACpE;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YAAY;AACV,WAAO,CAAC,EAAE,KAAK,qBAAqB,CAAC;AAAA,EACvC;AAAA,EAEA,WAAW,EAAE,MAAM,eAAe,GAAG;AACnC,UAAM,aAAa,KAAK,MAAM;AAC9B,UAAM,QAAQ,KAAK,MAAM;AACzB,UAAM,SAAS,iBAAiB,UAAU,KAAK;AAE/C,WAAO;AAAA,MACL;AAAA,UACA,6BAAgB,gBAAgB;AAAA,QAC9B,gBAAgB;AAAA,QAChB,OAAO;AAAA,MACT,CAAC;AAAA,MACD,GAAG,MAAM,GAAG,KAAK;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,WAAW,EAAE,KAAK,GAAG;AACnB,UAAM,aAAa,KAAK,MAAM;AAC9B,UAAM,QAAQ,KAAK,MAAM;AACzB,UAAM,SAAS,iBAAiB,UAAU,KAAK;AAC/C,WAAO,GAAG,MAAM,GAAG,KAAK;AAAA,EAC1B;AAAA,EAEA,uBAAuB;AACrB,WAAO;AAAA,MACL,WAAW,MACT,KAAK,OAAO,SAAS,QAAQ,CAAC,EAAE,IAAI,MAAM,MAAM;AAC9C,YAAI,YAAY;AAChB,cAAM,EAAE,UAAU,IAAI;AACtB,cAAM,EAAE,OAAO,OAAO,IAAI;AAE1B,YAAI,CAAC,MAAO,QAAO;AAEnB,cAAM,IAAI,aAAa,SAAS,GAAG,QAAQ,CAAC,MAAM,QAAQ;AACxD,cAAI,KAAK,KAAK,SAAS,KAAK,MAAM;AAChC,wBAAY;AACZ,eAAG,WAAW,IAAI,KAAK,MAAM,KAAK,QAAQ;AAAA,UAC5C;AAAA,QACF,CAAC;AAED,eAAO;AAAA,MACT,CAAC;AAAA,IACL;AAAA,EACF;AACF,CAAC;;;ACvGD,IAAAC,eAA0B;AAC1B,mBAAkC;AA6ClC,SAAS,cACP,MACA,WACA,aACA,UACqB;AACrB,QAAM,YAAY,YAAY;AAC9B,QAAM,SAAS,KAAK,MAAM,GAAG,SAAS;AAEtC,WAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,UAAM,KAAK,OAAO,CAAC;AACnB,QAAI,OAAO,KAAM,QAAO;AAExB,eAAW,WAAW,UAAU;AAC9B,UAAI,OAAO,UAAU,GAAG,IAAI,QAAQ,MAAM,MAAM,SAAS;AACvD,YAAI,MAAM,KAAK,KAAK,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG;AACvC,gBAAM,QAAQ,OAAO,MAAM,IAAI,QAAQ,MAAM;AAC7C,iBAAO,EAAE,SAAS,OAAO,MAAM,cAAc,GAAG,IAAI,UAAU;AAAA,QAChE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAMA,IAAM,sBAAsB,IAAI,uBAAU,mBAAmB;AAYtD,SAAS,0BACd,UACA,cACA;AACA,SAAO,uBAAU,OAAO;AAAA,IACtB,MAAM;AAAA,IAEN,wBAAwB;AACtB,YAAM,SAAS,KAAK;AACpB,UAAI,SAAS;AACb,UAAI,YAA2B;AAC/B,UAAI,cAA6B;AAEjC,YAAM,gBAAgB,CACpB,MACA,SAC2B;AAC3B,eAAO,MAAM;AACX,cAAI;AACF,kBAAM,SAAS,KAAK,YAAY,IAAI;AACpC,mBAAO,IAAI;AAAA,cACT,OAAO;AAAA,cACP,OAAO;AAAA,cACP;AAAA,cACA,OAAO,SAAS,OAAO;AAAA,YACzB;AAAA,UACF,QAAQ;AACN,mBAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAEA,YAAM,cAAc,CAAC,UAAwC;AAC3D,eAAO,CAAC,UAAmC;AACzC,iBACG,MAAM,EACN,MAAM,EACN,gBAAgB,OAAO;AAAA,YACtB;AAAA,cACE,MAAM;AAAA,cACN,OAAO;AAAA,gBACL,IAAI,MAAM;AAAA,gBACV,OAAO,MAAM;AAAA,gBACb,YAAY,MAAM;AAAA,cACpB;AAAA,YACF;AAAA,YACA,EAAE,MAAM,QAAQ,MAAM,IAAI;AAAA,UAC5B,CAAC,EACA,IAAI;AAAA,QACT;AAAA,MACF;AAEA,YAAM,SAAS,IAAI,oBAAO;AAAA,QACxB,KAAK;AAAA,QAEL,OAAO;AAAA,UACL,cAAc,OAAO,OAAO;AAC1B,gBAAI,CAAC,OAAQ,QAAO;AAEpB,mBAAO,aAAa,QAAQ,UAAU,EAAE,MAAM,CAAC;AAAA,UACjD;AAAA,QACF;AAAA,QAEA,OAAO;AACL,iBAAO;AAAA,YACL,OAAO,MAAM,YAAY;AACvB,oBAAM,EAAE,MAAM,IAAI;AAClB,oBAAM,EAAE,UAAU,IAAI;AAEtB,kBAAI,CAAC,UAAU,OAAO;AACpB,oBAAI,QAAQ;AACV,2BAAS;AACT,8BAAY;AACZ,gCAAc;AACd,+BAAa,QAAQ,OAAO;AAAA,gBAC9B;AACA;AAAA,cACF;AAEA,oBAAM,OAAO,UAAU;AACvB,oBAAM,YAAY,KAAK;AAEvB,kBAAI,CAAC,UAAU,aAAa;AAC1B,oBAAI,QAAQ;AACV,2BAAS;AACT,8BAAY;AACZ,gCAAc;AACd,+BAAa,QAAQ,OAAO;AAAA,gBAC9B;AACA;AAAA,cACF;AAEA,oBAAM,aAAa,KAAK,MAAM;AAC9B,oBAAM,YAAY,UAAU;AAC5B,oBAAM,YAAY,KAAK;AAEvB,oBAAM,QAAQ,cAAc,WAAW,WAAW,YAAY,QAAQ;AAEtE,kBAAI,OAAO;AACT,sBAAM,QAAQ,EAAE,MAAM,MAAM,MAAM,IAAI,MAAM,GAAG;AAC/C,sBAAM,QAAiC;AAAA,kBACrC,OAAO,MAAM;AAAA,kBACb,SAAS,MAAM;AAAA,kBACf,YAAY,cAAc,MAAM,MAAM,IAAI;AAAA,kBAC1C;AAAA,kBACA,SAAS,YAAY,KAAK;AAAA,gBAC5B;AAEA,oBAAI,CAAC,QAAQ;AACX,2BAAS;AACT,8BAAY,MAAM;AAClB,gCAAc,MAAM;AACpB,+BAAa,QAAQ,QAAQ,KAAK;AAAA,gBACpC,WACE,MAAM,UAAU,aAChB,MAAM,YAAY,aAClB;AAEA,8BAAY,MAAM;AAClB,gCAAc,MAAM;AACpB,+BAAa,QAAQ,SAAS,KAAK;AAAA,gBACrC;AAAA,cACF,OAAO;AACL,oBAAI,QAAQ;AACV,2BAAS;AACT,8BAAY;AACZ,gCAAc;AACd,+BAAa,QAAQ,OAAO;AAAA,gBAC9B;AAAA,cACF;AAAA,YACF;AAAA,YAEA,UAAU;AACR,kBAAI,QAAQ;AACV,6BAAa,QAAQ,OAAO;AAAA,cAC9B;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAED,aAAO,CAAC,MAAM;AAAA,IAChB;AAAA,EACF,CAAC;AACH;;;AC/NO,SAAS,oBAAoB,KAA0B;AAC5D,MAAI,CAAC,IAAI,QAAS,QAAO;AAEzB,QAAM,QAAkB,CAAC;AAEzB,aAAW,SAAS,IAAI,SAAS;AAC/B,QAAI,MAAM,SAAS,aAAa;AAC9B,YAAM,KAAK,mBAAmB,KAAK,CAAC;AAAA,IACtC,WAAW,MAAM,SAAS,QAAQ;AAChC,YAAM,KAAK,MAAM,QAAQ,EAAE;AAAA,IAC7B;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,mBAAmB,MAA2B;AACrD,MAAI,CAAC,KAAK,QAAS,QAAO;AAE1B,SAAO,KAAK,QACT,IAAI,CAAC,UAAU;AACd,QAAI,MAAM,SAAS,WAAW;AAC5B,YAAM,EAAE,IAAI,MAAM,IAAI,MAAM,SAAS,CAAC;AACtC,aAAO,KAAK,KAAK,KAAK,EAAE;AAAA,IAC1B;AACA,WAAO,MAAM,QAAQ;AAAA,EACvB,CAAC,EACA,KAAK,EAAE;AACZ;AAKO,SAAS,cAAc,KAAkC;AAC9D,QAAM,SAAyB,CAAC;AAEhC,WAAS,KAAK,MAAmB;AAC/B,QAAI,KAAK,SAAS,aAAa,KAAK,OAAO;AACzC,aAAO,KAAK;AAAA,QACV,IAAI,KAAK,MAAM;AAAA,QACf,MAAM,KAAK,MAAM;AAAA,QACjB,OAAO,KAAK,MAAM;AAAA,MACpB,CAAC;AAAA,IACH;AACA,QAAI,KAAK,SAAS;AAChB,iBAAW,SAAS,KAAK,SAAS;AAChC,aAAK,KAAK;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAEA,OAAK,GAAG;AACR,SAAO;AACT;AAMO,SAAS,iBAAiB,KAA0B;AACzD,MAAI,CAAC,IAAI,QAAS,QAAO;AAEzB,QAAM,QAAkB,CAAC;AAEzB,aAAW,SAAS,IAAI,SAAS;AAC/B,QAAI,MAAM,SAAS,aAAa;AAC9B,YAAM,KAAK,qBAAqB,KAAK,CAAC;AAAA,IACxC;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,qBAAqB,MAA2B;AACvD,MAAI,CAAC,KAAK,QAAS,QAAO;AAE1B,SAAO,KAAK,QACT,IAAI,CAAC,UAAU;AACd,QAAI,MAAM,SAAS,WAAW;AAC5B,aAAO,MAAM,OAAO,SAAS;AAAA,IAC/B;AACA,WAAO,MAAM,QAAQ;AAAA,EACvB,CAAC,EACA,KAAK,EAAE;AACZ;;;ACrFA,IAAM,aAAa;AAMZ,SAAS,kBAAkB,UAA+B;AAC/D,QAAM,QAAQ,SAAS,MAAM,IAAI;AAEjC,QAAM,UAAyB,MAAM,IAAI,CAAC,UAAU;AAAA,IAClD,MAAM;AAAA,IACN,SAAS,UAAU,IAAI;AAAA,EACzB,EAAE;AAEF,SAAO,EAAE,MAAM,OAAO,QAAQ;AAChC;AAEA,SAAS,UAAU,MAA6B;AAC9C,QAAM,QAAuB,CAAC;AAC9B,MAAI,YAAY;AAGhB,aAAW,YAAY;AAEvB,MAAI;AACJ,UAAQ,QAAQ,WAAW,KAAK,IAAI,OAAO,MAAM;AAC/C,UAAM,YAAY,MAAM,CAAC;AACzB,UAAM,QAAQ,MAAM,CAAC;AACrB,UAAM,aAAa,MAAM,CAAC,KAAK;AAC/B,UAAM,KAAK,MAAM,CAAC;AAGlB,QAAI,MAAM,QAAQ,WAAW;AAC3B,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,MAAM,KAAK,MAAM,WAAW,MAAM,KAAK;AAAA,MACzC,CAAC;AAAA,IACH;AAGA,UAAM,KAAK;AAAA,MACT,MAAM;AAAA,MACN,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAED,gBAAY,MAAM,QAAQ,UAAU;AAAA,EACtC;AAGA,MAAI,YAAY,KAAK,QAAQ;AAC3B,UAAM,KAAK;AAAA,MACT,MAAM;AAAA,MACN,MAAM,KAAK,MAAM,SAAS;AAAA,IAC5B,CAAC;AAAA,EACH;AAGA,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,KAAK,EAAE,MAAM,QAAQ,MAAM,GAAG,CAAC;AAAA,EACvC;AAEA,SAAO;AACT;;;AJrDA,SAAS,YAAY,QAAgD;AACnE,QAAM,OAAO,OAAO,QAAQ;AAC5B,SAAO;AAAA,IACL,UAAU,oBAAoB,IAAI;AAAA,IAClC,QAAQ,cAAc,IAAI;AAAA,IAC1B,WAAW,iBAAiB,IAAI;AAAA,EAClC;AACF;AAMA,SAAS,sBACP,aACA,kBACA;AACA,SAAO,uBAAU,OAAO;AAAA,IACtB,MAAM;AAAA,IAEN,uBAAuB;AACrB,aAAO;AAAA,QACL,aAAa,MAAM;AACjB,cAAI,YAAY,SAAS;AACvB,wBAAY,QAAQ,YAAY,KAAK,MAAM,CAAC;AAC5C,gBAAI,iBAAiB,SAAS;AAC5B,mBAAK,OAAO,SAAS,aAAa,IAAI;AAAA,YACxC;AAAA,UACF;AACA,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAMA,SAAS,qBACP,aACA,kBACA;AACA,SAAO,uBAAU,OAAO;AAAA,IACtB,MAAM;AAAA,IACN,UAAU;AAAA,IAEV,uBAAuB;AACrB,aAAO;AAAA,QACL,OAAO,MAAM;AACX,cAAI,YAAY,SAAS;AACvB,wBAAY,QAAQ,YAAY,KAAK,MAAM,CAAC;AAC5C,gBAAI,iBAAiB,SAAS;AAC5B,mBAAK,OAAO,SAAS,aAAa,IAAI;AAAA,YACxC;AAAA,UACF;AACA,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAkBO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AAAA,EACA,YAAY;AAAA,EACZ,WAAW;AAAA,EACX;AACF,GAA6B;AAE3B,QAAM,kBAAc,qBAAO,QAAQ;AACnC,cAAY,UAAU;AAEtB,QAAM,kBAAc,qBAAO,QAAQ;AACnC,cAAY,UAAU;AAEtB,QAAM,uBAAmB,qBAAO,aAAa;AAC7C,mBAAiB,UAAU;AAG3B,QAAM,qBAAiB,sBAAQ,MAAM;AACnC,QAAI,CAAC,MAAO,QAAO;AACnB,WAAO,kBAAkB,KAAK;AAAA,EAEhC,GAAG,CAAC,CAAC;AAGL,QAAM,cAAc,UAAU,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,GAAG;AAC5D,QAAM,eAAW;AAAA,IACf,MAAM,UAAU,IAAI,CAAC,MAAM,EAAE,OAAO;AAAA;AAAA,IAEpC,CAAC,WAAW;AAAA,EACd;AAGA,QAAM,0BAAsB;AAAA,IAC1B,MAAM,0BAA0B,UAAU,YAAY;AAAA;AAAA,IAEtD,CAAC,WAAW;AAAA,EACd;AAGA,QAAM,gBAAY;AAAA,IAChB,MAAM,sBAAsB,aAAa,gBAAgB;AAAA,IACzD,CAAC;AAAA,EACH;AACA,QAAM,eAAW;AAAA,IACf,MAAM,qBAAqB,aAAa,gBAAgB;AAAA,IACxD,CAAC;AAAA,EACH;AAEA,QAAM,aAAS,yBAAU;AAAA,IACvB,YAAY;AAAA,MACV,mBAAAC,QAAW,UAAU;AAAA,QACnB,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,WAAW;AAAA,QACX,YAAY;AAAA,QACZ,aAAa;AAAA,QACb,UAAU;AAAA,QACV,gBAAgB;AAAA,MAClB,CAAC;AAAA,MACD,6BAAAC,QAAY,UAAU;AAAA,QACpB,aAAa,eAAe;AAAA,MAC9B,CAAC;AAAA,MACD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,SAAS;AAAA,IACT,WAAW,YAAY,QAAQ;AAAA,IAC/B;AAAA,IACA,aAAa;AAAA,MACX,YAAY;AAAA,QACV,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,UAAU,CAAC,EAAE,QAAAC,QAAO,MAAM;AACxB,kBAAY,UAAU,YAAYA,OAAM,CAAC;AAAA,IAC3C;AAAA,EACF,CAAC;AAGD,8BAAU,MAAM;AACd,QAAI,UAAU,OAAO,eAAe,UAAU;AAC5C,aAAO,YAAY,QAAQ;AAAA,IAC7B;AAAA,EACF,GAAG,CAAC,QAAQ,QAAQ,CAAC;AAMrB,QAAM,YAAQ,0BAAY,MAAM;AAC9B,YAAQ,SAAS,aAAa,IAAI;AAAA,EACpC,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,iBAAa;AAAA,IACjB,CAAC,aAAqB;AACpB,UAAI,CAAC,OAAQ;AACb,YAAM,MAAM,kBAAkB,QAAQ;AACtC,aAAO,SAAS,WAAW,GAAG;AAAA,IAChC;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,YAAQ,0BAAY,MAAM;AAC9B,YAAQ,SAAS,MAAM,KAAK;AAAA,EAC9B,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,gBAAY,0BAAY,MAA6B;AACzD,QAAI,CAAC,OAAQ,QAAO;AACpB,WAAO,YAAY,MAAM;AAAA,EAC3B,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO,EAAE,QAAQ,WAAW,OAAO,YAAY,MAAM;AACvD;;;AK5NA,IAAAC,gBAA8C;AAgC9C,IAAM,aAAgC;AAAA,EACpC,OAAO;AAAA,EACP,OAAO,CAAC;AAAA,EACR,aAAa,CAAC;AAAA,EACd,aAAa;AAAA,EACb,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,OAAO;AACT;AAMO,SAAS,cAAc,WAA8B;AAC1D,QAAM,CAAC,SAAS,UAAU,QAAI,wBAA4B,UAAU;AAGpE,QAAM,eAAW,sBAAO,OAAO;AAC/B,WAAS,UAAU;AAEnB,QAAM,mBAAe,sBAAO,SAAS;AACrC,eAAa,UAAU;AAEvB,QAAM,iBAAa;AAAA,IACjB;AAAA,EACF;AACA,QAAM,kBAAc,sBAA+B,IAAI;AAMvD,QAAM,iBAAa;AAAA,IACjB,OACE,UACA,OACA,WACG;AACH,iBAAW,CAAC,UAAU,EAAE,GAAG,MAAM,SAAS,MAAM,OAAO,UAAU,EAAE;AAEnE,UAAI;AACF,cAAM,QACJ,UAAU,SAAS,cACf,MAAM,SAAS,YAAY,QAAQ,KAAK,IACxC,MAAM,SAAS,aAAa,KAAK;AAEvC,mBAAW,CAAC,UAAU;AAAA,UACpB,GAAG;AAAA,UACH;AAAA,UACA,SAAS;AAAA,UACT,OAAO;AAAA,UACP,aAAa;AAAA,QACf,EAAE;AAAA,MACJ,QAAQ;AACN,mBAAW,CAAC,UAAU;AAAA,UACpB,GAAG;AAAA,UACH,OAAO,CAAC;AAAA,UACR,SAAS;AAAA,UACT,OAAO;AAAA,QACT,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,CAAC;AAAA,EACH;AAMA,QAAM,cAAU;AAAA,IACd,CAAC,UAAmC;AAClC,YAAM,WAAW,aAAa,QAAQ;AAAA,QACpC,CAAC,MAAM,EAAE,YAAY,MAAM;AAAA,MAC7B;AACA,UAAI,CAAC,SAAU;AAEf,kBAAY,UAAU;AACtB,iBAAW,UAAU,MAAM;AAE3B,iBAAW;AAAA,QACT,OAAO;AAAA,QACP,OAAO,CAAC;AAAA,QACR,aAAa,CAAC;AAAA,QACd,aAAa;AAAA,QACb,SAAS;AAAA,QACT,YAAY,MAAM;AAAA,QAClB,SAAS,MAAM;AAAA,QACf,OAAO,MAAM;AAAA,MACf,CAAC;AAED,iBAAW,UAAU,MAAM,KAAK;AAAA,IAClC;AAAA,IACA,CAAC,UAAU;AAAA,EACb;AAEA,QAAM,eAAW;AAAA,IACf,CAAC,UAAmC;AAClC,YAAM,WAAW,YAAY;AAC7B,UAAI,CAAC,SAAU;AAEf,iBAAW,UAAU,MAAM;AAE3B,iBAAW,CAAC,UAAU;AAAA,QACpB,GAAG;AAAA,QACH,YAAY,MAAM;AAAA,QAClB,OAAO,MAAM;AAAA,MACf,EAAE;AAIF,UAAI,SAAS,QAAQ,YAAY,WAAW,GAAG;AAC7C,mBAAW,UAAU,MAAM,KAAK;AAAA,MAClC;AAAA,IACF;AAAA,IACA,CAAC,UAAU;AAAA,EACb;AAEA,QAAM,aAAS,2BAAY,MAAM;AAC/B,gBAAY,UAAU;AACtB,eAAW,UAAU;AACrB,eAAW,UAAU;AAAA,EACvB,GAAG,CAAC,CAAC;AAML,QAAM,iBAAa,2BAAY,MAAM;AACnC,eAAW,CAAC,UAAU;AAAA,MACpB,GAAG;AAAA,MACH,aAAa,KAAK,IAAI,GAAG,KAAK,cAAc,CAAC;AAAA,IAC/C,EAAE;AAAA,EACJ,GAAG,CAAC,CAAC;AAEL,QAAM,mBAAe,2BAAY,MAAM;AACrC,eAAW,CAAC,UAAU;AAAA,MACpB,GAAG;AAAA,MACH,aAAa,KAAK,IAAI,KAAK,MAAM,SAAS,GAAG,KAAK,cAAc,CAAC;AAAA,IACnE,EAAE;AAAA,EACJ,GAAG,CAAC,CAAC;AAEL,QAAM,aAAS;AAAA,IACb,CAAC,SAAuB;AACtB,YAAM,UAAU,SAAS;AACzB,YAAM,WAAW,QAAQ,QAAQ,MAAM,QAAQ,WAAW;AAC1D,UAAI,CAAC,SAAU;AAEf,YAAM,WAAW,YAAY;AAG7B,UAAI,SAAS,eAAe,UAAU,aAAa;AACjD,mBAAW,CAAC,UAAU;AAAA,UACpB,GAAG;AAAA,UACH,OAAO;AAAA,UACP,aAAa,CAAC,GAAG,KAAK,aAAa,QAAQ;AAAA,UAC3C,OAAO,CAAC;AAAA,UACR,aAAa;AAAA,UACb,OAAO;AAAA,QACT,EAAE;AAEF,mBAAW,UAAU,IAAI,QAAQ;AACjC;AAAA,MACF;AAGA,UAAI,WAAW,SAAS;AACtB,mBAAW,QAAQ;AAAA,UACjB,IAAI,SAAS;AAAA,UACb,OAAO,SAAS;AAAA,UAChB,YAAY,SAAS;AAAA,QACvB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,CAAC,UAAU;AAAA,EACb;AAEA,QAAM,aAAS,2BAAY,MAAM;AAC/B,UAAM,WAAW,YAAY;AAC7B,QAAI,CAAC,SAAU;AAEf,eAAW,CAAC,SAAS;AACnB,YAAM,iBAAiB,KAAK,YAAY,MAAM,GAAG,EAAE;AACnD,YAAM,SAAS,eAAe,eAAe,SAAS,CAAC;AAEvD,UAAI,QAAQ;AACV,mBAAW,UAAU,IAAI,MAAM;AAAA,MACjC,OAAO;AACL,mBAAW,UAAU,KAAK,KAAK;AAAA,MACjC;AAEA,aAAO;AAAA,QACL,GAAG;AAAA,QACH,aAAa;AAAA,QACb,OAAO,CAAC;AAAA,QACR,aAAa;AAAA,QACb,OAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,YAAQ,2BAAY,MAAM;AAC9B,eAAW,UAAU;AAAA,EACvB,GAAG,CAAC,CAAC;AAML,QAAM,gBAAY;AAAA,IAChB,CAAC,EAAE,MAAM,MAAyC;AAChD,YAAM,UAAU,SAAS;AACzB,UAAI,QAAQ,UAAU,OAAQ,QAAO;AAErC,cAAQ,MAAM,KAAK;AAAA,QACjB,KAAK;AACH,gBAAM,eAAe;AACrB,qBAAW;AACX,iBAAO;AAAA,QACT,KAAK;AACH,gBAAM,eAAe;AACrB,uBAAa;AACb,iBAAO;AAAA,QACT,KAAK,SAAS;AACZ,gBAAM,eAAe;AACrB,gBAAM,eAAe,QAAQ,MAAM,QAAQ,WAAW;AACtD,cAAI,cAAc;AAChB,mBAAO,YAAY;AAAA,UACrB;AACA,iBAAO;AAAA,QACT;AAAA,QACA,KAAK,cAAc;AACjB,gBAAM,aAAa,QAAQ,MAAM,QAAQ,WAAW;AACpD,cAAI,YAAY,aAAa;AAC3B,kBAAM,eAAe;AACrB,mBAAO,UAAU;AACjB,mBAAO;AAAA,UACT;AACA,iBAAO;AAAA,QACT;AAAA,QACA,KAAK;AACH,cAAI,QAAQ,YAAY,SAAS,GAAG;AAClC,kBAAM,eAAe;AACrB,mBAAO;AACP,mBAAO;AAAA,UACT;AACA,iBAAO;AAAA,QACT,KAAK;AACH,gBAAM,eAAe;AACrB,gBAAM;AACN,iBAAO;AAAA,QACT;AACE,iBAAO;AAAA,MACX;AAAA,IACF;AAAA,IACA,CAAC,YAAY,cAAc,QAAQ,QAAQ,KAAK;AAAA,EAClD;AAOA,QAAM,mBAAuC,sBAA4B;AAAA,IACvE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAGD,eAAa,UAAU,EAAE,SAAS,UAAU,QAAQ,UAAU;AAE9D,QAAM,UAA6B;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,SAAS,aAAa;AAC1C;;;AC3TA,IAAAC,gBAAyC;;;ACIlC,SAAS,cAAc,UAAmB,WAAmB;AAClE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB,aAAa,WAAW,YAAY;AAAA,EACtC;AACF;AAEO,SAAS,aAAa,IAAY,OAAe;AACtD,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AACF;AAEO,SAAS,YACd,IACA,UACA,OACA;AACA,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,iBAAiB,QAAQ;AAAA,EAC3B;AACF;;;ADiCU;AA3CV,IAAM,aAAa;AAMZ,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAwB;AACtB,QAAM,cAAU,sBAAuB,IAAI;AAC3C,QAAM,QAAQ,YAAY;AAG1B,+BAAU,MAAM;AACd,QAAI,CAAC,QAAQ,QAAS;AACtB,UAAM,SAAS,QAAQ,QAAQ,cAAc,wBAAwB;AACrE,YAAQ,eAAe,EAAE,OAAO,UAAU,CAAC;AAAA,EAC7C,GAAG,CAAC,WAAW,CAAC;AAGhB,QAAM,QAAQ,mBAAmB,UAAU;AAE3C,MAAI,MAAM,WAAW,KAAK,CAAC,QAAS,QAAO;AAE3C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,oBAAiB;AAAA,MACjB,gBAAc;AAAA,MACd;AAAA,MACA,KAAK;AAAA,MAGJ;AAAA,oBAAY,SAAS,KACpB,6CAAC,SAAI,8BAA2B,IAC9B;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,wBAAqB;AAAA,cACrB,SAAS;AAAA,cACT,cAAW;AAAA,cACZ;AAAA;AAAA,UAED;AAAA,UACC,YAAY,IAAI,CAAC,OAAO,MACvB,6CAAC,UAAoB,mCAAgC,IAClD;AAAA,gBAAI,KAAK,4CAAC,UAAK,kCAA+B,IAAG,eAAC;AAAA,YAClD,MAAM;AAAA,eAFE,MAAM,EAGjB,CACD;AAAA,WACH;AAAA,QAID,WACC,4CAAC,SAAI,2BAAwB,IAAG,wBAAU;AAAA,QAI3C,CAAC,WACA,4CAAC,SAAK,GAAG,aAAa,YAAY,GAAG,WAAW,EAAE,cAAc,GAC7D,gBAAM,IAAI,CAAC,MAAM,UAAU;AAC1B,gBAAM,WAAW,UAAU;AAC3B,gBAAM,SAAS,kBAAkB,KAAK,EAAE;AAExC,iBACE;AAAA,YAAC;AAAA;AAAA,cAEE,GAAG,YAAY,QAAQ,UAAU,KAAK;AAAA,cACvC,wBAAqB;AAAA,cACrB,+BAA6B,WAAW,KAAK;AAAA,cAC7C,qBAAmB,KAAK,cAAc,KAAK;AAAA,cAC3C,cAAc,MAAM,QAAQ,KAAK;AAAA,cACjC,SAAS,MAAM,SAAS,IAAI;AAAA,cAE3B,uBACC,WAAW,MAAM,KAAK,IAEtB,4CAAC,yBAAsB,MAAY;AAAA;AAAA,YAXhC,KAAK;AAAA,UAaZ;AAAA,QAEJ,CAAC,GACH;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAMA,SAAS,sBAAsB,EAAE,KAAK,GAA0B;AAC9D,SACE,4EACG;AAAA,SAAK,QACJ,4CAAC,UAAK,6BAA0B,IAAI,eAAK,MAAK;AAAA,IAEhD,4CAAC,UAAK,8BAA2B,IAAI,eAAK,OAAM;AAAA,IAC/C,KAAK,eACJ,4CAAC,UAAK,oCAAiC,IAAI,eAAK,aAAY;AAAA,IAE7D,KAAK,eACJ,4CAAC,UAAK,gCAA6B,IAAG,eAAY,QAAO,oBAEzD;AAAA,KAEJ;AAEJ;AAMA,SAAS,mBACP,YACqB;AACrB,MAAI,CAAC,YAAY;AACf,WAAO,EAAE,SAAS,OAAO;AAAA,EAC3B;AAEA,QAAM,OAAO,WAAW;AACxB,MAAI,CAAC,MAAM;AACT,WAAO,EAAE,SAAS,OAAO;AAAA,EAC3B;AAEA,SAAO;AAAA,IACL,UAAU;AAAA,IACV,MAAM,GAAG,KAAK,IAAI;AAAA,IAClB,KAAK,GAAG,KAAK,SAAS,CAAC;AAAA,IACvB,QAAQ;AAAA,EACV;AACF;;;APtFM,IAAAC,sBAAA;AAlEN,IAAMC,cAAa;AAiBZ,IAAM,oBAAgB;AAAA,EAC3B,SAASC,eACP;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,EACF,GACA,KACA;AACA,UAAM,EAAE,SAAS,SAAS,aAAa,IAAI,cAAc,SAAS;AAElE,UAAM,EAAE,QAAQ,OAAO,YAAY,MAAM,IAAI,kBAAkB;AAAA,MAC7D;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,CAAC;AAAA,MACX;AAAA,IACF,CAAC;AAMD;AAAA,MACE;AAAA,MACA,OAAO,EAAE,OAAO,YAAY,MAAM;AAAA,MAClC,CAAC,OAAO,YAAY,KAAK;AAAA,IAC3B;AAEA,UAAM,aAAa,QAAQ,UAAU;AAErC,UAAM,kBAAc,2BAAY,CAAC,WAAmB;AAAA,IAEpD,GAAG,CAAC,CAAC;AAEL,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,uBAAoB;AAAA,QACpB,iBAAe,WAAW,KAAK;AAAA,QAC9B,GAAG,cAAc,YAAYD,WAAU;AAAA,QACxC,yBACE,cAAc,QAAQ,MAAM,QAAQ,WAAW,IAC3C,kBAAkB,QAAQ,MAAM,QAAQ,WAAW,EAAE,EAAE,KACvD;AAAA,QAGN;AAAA,uDAAC,+BAAc,QAAgB;AAAA,UAE9B,cACC;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,QAAQ;AAAA,cACf,aAAa,QAAQ;AAAA,cACrB,aAAa,QAAQ;AAAA,cACrB,SAAS,QAAQ;AAAA,cACjB,SAAS,QAAQ;AAAA,cACjB,YAAY,QAAQ;AAAA,cACpB,UAAU,CAAC,SAAS,QAAQ,OAAO,IAAI;AAAA,cACvC,SAAS;AAAA,cACT,UAAU,QAAQ;AAAA,cAClB;AAAA;AAAA,UACF;AAAA;AAAA;AAAA,IAEJ;AAAA,EAEJ;AACF;","names":["import_react","import_react","import_core","import_core","StarterKit","Placeholder","editor","import_react","import_react","import_jsx_runtime","LISTBOX_ID","MentionsInput"]}
package/dist/index.mjs CHANGED
@@ -1,11 +1,12 @@
1
1
  // src/components/MentionsInput.tsx
2
- import { useCallback as useCallback3 } from "react";
2
+ import { forwardRef, useCallback as useCallback3, useImperativeHandle } from "react";
3
3
  import { EditorContent } from "@tiptap/react";
4
4
 
5
5
  // src/hooks/useMentionsEditor.ts
6
6
  import { useCallback, useEffect, useMemo, useRef } from "react";
7
7
  import { useEditor } from "@tiptap/react";
8
8
  import StarterKit from "@tiptap/starter-kit";
9
+ import Placeholder from "@tiptap/extension-placeholder";
9
10
  import { Extension as Extension2 } from "@tiptap/core";
10
11
 
11
12
  // src/core/mentionExtension.ts
@@ -331,19 +332,25 @@ function parseLine(line) {
331
332
  }
332
333
 
333
334
  // src/hooks/useMentionsEditor.ts
334
- function createSubmitExtension(onSubmitRef) {
335
+ function buildOutput(editor) {
336
+ const json = editor.getJSON();
337
+ return {
338
+ markdown: serializeToMarkdown(json),
339
+ tokens: extractTokens(json),
340
+ plainText: extractPlainText(json)
341
+ };
342
+ }
343
+ function createSubmitExtension(onSubmitRef, clearOnSubmitRef) {
335
344
  return Extension2.create({
336
345
  name: "submitShortcut",
337
346
  addKeyboardShortcuts() {
338
347
  return {
339
348
  "Mod-Enter": () => {
340
349
  if (onSubmitRef.current) {
341
- const json = this.editor.getJSON();
342
- onSubmitRef.current({
343
- markdown: serializeToMarkdown(json),
344
- tokens: extractTokens(json),
345
- plainText: extractPlainText(json)
346
- });
350
+ onSubmitRef.current(buildOutput(this.editor));
351
+ if (clearOnSubmitRef.current) {
352
+ this.editor.commands.clearContent(true);
353
+ }
347
354
  }
348
355
  return true;
349
356
  }
@@ -351,7 +358,7 @@ function createSubmitExtension(onSubmitRef) {
351
358
  }
352
359
  });
353
360
  }
354
- function createEnterExtension(onSubmitRef) {
361
+ function createEnterExtension(onSubmitRef, clearOnSubmitRef) {
355
362
  return Extension2.create({
356
363
  name: "enterSubmit",
357
364
  priority: 50,
@@ -359,12 +366,10 @@ function createEnterExtension(onSubmitRef) {
359
366
  return {
360
367
  Enter: () => {
361
368
  if (onSubmitRef.current) {
362
- const json = this.editor.getJSON();
363
- onSubmitRef.current({
364
- markdown: serializeToMarkdown(json),
365
- tokens: extractTokens(json),
366
- plainText: extractPlainText(json)
367
- });
369
+ onSubmitRef.current(buildOutput(this.editor));
370
+ if (clearOnSubmitRef.current) {
371
+ this.editor.commands.clearContent(true);
372
+ }
368
373
  }
369
374
  return true;
370
375
  }
@@ -377,6 +382,7 @@ function useMentionsEditor({
377
382
  value,
378
383
  onChange,
379
384
  onSubmit,
385
+ clearOnSubmit = true,
380
386
  placeholder,
381
387
  autoFocus = false,
382
388
  editable = true,
@@ -386,6 +392,8 @@ function useMentionsEditor({
386
392
  onChangeRef.current = onChange;
387
393
  const onSubmitRef = useRef(onSubmit);
388
394
  onSubmitRef.current = onSubmit;
395
+ const clearOnSubmitRef = useRef(clearOnSubmit);
396
+ clearOnSubmitRef.current = clearOnSubmit;
389
397
  const initialContent = useMemo(() => {
390
398
  if (!value) return void 0;
391
399
  return parseFromMarkdown(value);
@@ -401,8 +409,14 @@ function useMentionsEditor({
401
409
  // eslint-disable-next-line react-hooks/exhaustive-deps
402
410
  [triggersKey]
403
411
  );
404
- const submitExt = useMemo(() => createSubmitExtension(onSubmitRef), []);
405
- const enterExt = useMemo(() => createEnterExtension(onSubmitRef), []);
412
+ const submitExt = useMemo(
413
+ () => createSubmitExtension(onSubmitRef, clearOnSubmitRef),
414
+ []
415
+ );
416
+ const enterExt = useMemo(
417
+ () => createEnterExtension(onSubmitRef, clearOnSubmitRef),
418
+ []
419
+ );
406
420
  const editor = useEditor({
407
421
  extensions: [
408
422
  StarterKit.configure({
@@ -414,6 +428,9 @@ function useMentionsEditor({
414
428
  listItem: false,
415
429
  horizontalRule: false
416
430
  }),
431
+ Placeholder.configure({
432
+ placeholder: placeholder ?? "Type a message..."
433
+ }),
417
434
  MentionNode,
418
435
  suggestionExtension,
419
436
  submitExt,
@@ -424,17 +441,11 @@ function useMentionsEditor({
424
441
  editable,
425
442
  editorProps: {
426
443
  attributes: {
427
- "data-placeholder": placeholder ?? "",
428
444
  class: "mentions-editor"
429
445
  }
430
446
  },
431
447
  onUpdate: ({ editor: editor2 }) => {
432
- const json = editor2.getJSON();
433
- onChangeRef.current?.({
434
- markdown: serializeToMarkdown(json),
435
- tokens: extractTokens(json),
436
- plainText: extractPlainText(json)
437
- });
448
+ onChangeRef.current?.(buildOutput(editor2));
438
449
  }
439
450
  });
440
451
  useEffect(() => {
@@ -442,16 +453,25 @@ function useMentionsEditor({
442
453
  editor.setEditable(editable);
443
454
  }
444
455
  }, [editor, editable]);
456
+ const clear = useCallback(() => {
457
+ editor?.commands.clearContent(true);
458
+ }, [editor]);
459
+ const setContent = useCallback(
460
+ (markdown) => {
461
+ if (!editor) return;
462
+ const doc = parseFromMarkdown(markdown);
463
+ editor.commands.setContent(doc);
464
+ },
465
+ [editor]
466
+ );
467
+ const focus = useCallback(() => {
468
+ editor?.commands.focus("end");
469
+ }, [editor]);
445
470
  const getOutput = useCallback(() => {
446
471
  if (!editor) return null;
447
- const json = editor.getJSON();
448
- return {
449
- markdown: serializeToMarkdown(json),
450
- tokens: extractTokens(json),
451
- plainText: extractPlainText(json)
452
- };
472
+ return buildOutput(editor);
453
473
  }, [editor]);
454
- return { editor, getOutput };
474
+ return { editor, getOutput, clear, setContent, focus };
455
475
  }
456
476
 
457
477
  // src/hooks/useSuggestion.ts
@@ -794,62 +814,71 @@ function usePopoverPosition(clientRect) {
794
814
  // src/components/MentionsInput.tsx
795
815
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
796
816
  var LISTBOX_ID2 = "mentions-suggestion-listbox";
797
- function MentionsInput({
798
- value,
799
- providers,
800
- onChange,
801
- placeholder = "Type a message...",
802
- autoFocus = false,
803
- disabled = false,
804
- className,
805
- onSubmit,
806
- maxLength,
807
- renderItem,
808
- renderChip
809
- }) {
810
- const { uiState, actions, callbacksRef } = useSuggestion(providers);
811
- const { editor } = useMentionsEditor({
812
- providers,
817
+ var MentionsInput = forwardRef(
818
+ function MentionsInput2({
813
819
  value,
820
+ providers,
814
821
  onChange,
822
+ placeholder = "Type a message...",
823
+ autoFocus = false,
824
+ disabled = false,
825
+ className,
815
826
  onSubmit,
816
- placeholder,
817
- autoFocus,
818
- editable: !disabled,
819
- callbacksRef
820
- });
821
- const isExpanded = uiState.state !== "idle";
822
- const handleHover = useCallback3((index) => {
823
- }, []);
824
- return /* @__PURE__ */ jsxs2(
825
- "div",
826
- {
827
- className,
828
- "data-mentions-input": "",
829
- "data-disabled": disabled ? "" : void 0,
830
- ...comboboxAttrs(isExpanded, LISTBOX_ID2),
831
- "aria-activedescendant": isExpanded && uiState.items[uiState.activeIndex] ? `mention-option-${uiState.items[uiState.activeIndex].id}` : void 0,
832
- children: [
833
- /* @__PURE__ */ jsx2(EditorContent, { editor }),
834
- isExpanded && /* @__PURE__ */ jsx2(
835
- SuggestionList,
836
- {
837
- items: uiState.items,
838
- activeIndex: uiState.activeIndex,
839
- breadcrumbs: uiState.breadcrumbs,
840
- loading: uiState.loading,
841
- trigger: uiState.trigger,
842
- clientRect: uiState.clientRect,
843
- onSelect: (item) => actions.select(item),
844
- onHover: handleHover,
845
- onGoBack: actions.goBack,
846
- renderItem
847
- }
848
- )
849
- ]
850
- }
851
- );
852
- }
827
+ clearOnSubmit = true,
828
+ maxLength,
829
+ renderItem,
830
+ renderChip
831
+ }, ref) {
832
+ const { uiState, actions, callbacksRef } = useSuggestion(providers);
833
+ const { editor, clear, setContent, focus } = useMentionsEditor({
834
+ providers,
835
+ value,
836
+ onChange,
837
+ onSubmit,
838
+ clearOnSubmit,
839
+ placeholder,
840
+ autoFocus,
841
+ editable: !disabled,
842
+ callbacksRef
843
+ });
844
+ useImperativeHandle(
845
+ ref,
846
+ () => ({ clear, setContent, focus }),
847
+ [clear, setContent, focus]
848
+ );
849
+ const isExpanded = uiState.state !== "idle";
850
+ const handleHover = useCallback3((_index) => {
851
+ }, []);
852
+ return /* @__PURE__ */ jsxs2(
853
+ "div",
854
+ {
855
+ className,
856
+ "data-mentions-input": "",
857
+ "data-disabled": disabled ? "" : void 0,
858
+ ...comboboxAttrs(isExpanded, LISTBOX_ID2),
859
+ "aria-activedescendant": isExpanded && uiState.items[uiState.activeIndex] ? `mention-option-${uiState.items[uiState.activeIndex].id}` : void 0,
860
+ children: [
861
+ /* @__PURE__ */ jsx2(EditorContent, { editor }),
862
+ isExpanded && /* @__PURE__ */ jsx2(
863
+ SuggestionList,
864
+ {
865
+ items: uiState.items,
866
+ activeIndex: uiState.activeIndex,
867
+ breadcrumbs: uiState.breadcrumbs,
868
+ loading: uiState.loading,
869
+ trigger: uiState.trigger,
870
+ clientRect: uiState.clientRect,
871
+ onSelect: (item) => actions.select(item),
872
+ onHover: handleHover,
873
+ onGoBack: actions.goBack,
874
+ renderItem
875
+ }
876
+ )
877
+ ]
878
+ }
879
+ );
880
+ }
881
+ );
853
882
  export {
854
883
  MentionsInput,
855
884
  parseFromMarkdown,