@spatika/editor 1.5.0

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.
Files changed (142) hide show
  1. package/README.md +43 -0
  2. package/dist/composites/AiCommandDock.d.ts +13 -0
  3. package/dist/composites/AiCommandDock.d.ts.map +1 -0
  4. package/dist/composites/AiCommandDock.js +63 -0
  5. package/dist/composites/AiCommandMenu.d.ts +16 -0
  6. package/dist/composites/AiCommandMenu.d.ts.map +1 -0
  7. package/dist/composites/AiCommandMenu.js +51 -0
  8. package/dist/composites/AiCommandPanel.d.ts +12 -0
  9. package/dist/composites/AiCommandPanel.d.ts.map +1 -0
  10. package/dist/composites/AiCommandPanel.js +12 -0
  11. package/dist/composites/AiOverlay.d.ts +7 -0
  12. package/dist/composites/AiOverlay.d.ts.map +1 -0
  13. package/dist/composites/AiOverlay.js +8 -0
  14. package/dist/composites/EditorBubbleMenu.d.ts +10 -0
  15. package/dist/composites/EditorBubbleMenu.d.ts.map +1 -0
  16. package/dist/composites/EditorBubbleMenu.js +16 -0
  17. package/dist/composites/EditorToolbar.d.ts +20 -0
  18. package/dist/composites/EditorToolbar.d.ts.map +1 -0
  19. package/dist/composites/EditorToolbar.js +45 -0
  20. package/dist/composites/ImageBubbleMenu.d.ts +7 -0
  21. package/dist/composites/ImageBubbleMenu.d.ts.map +1 -0
  22. package/dist/composites/ImageBubbleMenu.js +15 -0
  23. package/dist/composites/MentionMenu.d.ts +11 -0
  24. package/dist/composites/MentionMenu.d.ts.map +1 -0
  25. package/dist/composites/MentionMenu.js +10 -0
  26. package/dist/composites/SlashCommandMenu.d.ts +13 -0
  27. package/dist/composites/SlashCommandMenu.d.ts.map +1 -0
  28. package/dist/composites/SlashCommandMenu.js +10 -0
  29. package/dist/composites/SpatikaEditor.d.ts +14 -0
  30. package/dist/composites/SpatikaEditor.d.ts.map +1 -0
  31. package/dist/composites/SpatikaEditor.js +159 -0
  32. package/dist/composites/TableGridPicker.d.ts +9 -0
  33. package/dist/composites/TableGridPicker.d.ts.map +1 -0
  34. package/dist/composites/TableGridPicker.js +54 -0
  35. package/dist/composites/ToolbarButton.d.ts +7 -0
  36. package/dist/composites/ToolbarButton.d.ts.map +1 -0
  37. package/dist/composites/ToolbarButton.js +7 -0
  38. package/dist/composites/ToolbarFormatPanels.d.ts +9 -0
  39. package/dist/composites/ToolbarFormatPanels.d.ts.map +1 -0
  40. package/dist/composites/ToolbarFormatPanels.js +35 -0
  41. package/dist/composites/ToolbarInsertPanels.d.ts +14 -0
  42. package/dist/composites/ToolbarInsertPanels.d.ts.map +1 -0
  43. package/dist/composites/ToolbarInsertPanels.js +96 -0
  44. package/dist/composites/ToolbarPopover.d.ts +14 -0
  45. package/dist/composites/ToolbarPopover.d.ts.map +1 -0
  46. package/dist/composites/ToolbarPopover.js +48 -0
  47. package/dist/extensions/ai-action.d.ts +24 -0
  48. package/dist/extensions/ai-action.d.ts.map +1 -0
  49. package/dist/extensions/ai-action.js +53 -0
  50. package/dist/extensions/mention.d.ts +17 -0
  51. package/dist/extensions/mention.d.ts.map +1 -0
  52. package/dist/extensions/mention.js +129 -0
  53. package/dist/extensions/resizable-image-view.d.ts +18 -0
  54. package/dist/extensions/resizable-image-view.d.ts.map +1 -0
  55. package/dist/extensions/resizable-image-view.js +70 -0
  56. package/dist/extensions/resizable-image.d.ts +4 -0
  57. package/dist/extensions/resizable-image.d.ts.map +1 -0
  58. package/dist/extensions/resizable-image.js +68 -0
  59. package/dist/extensions/slash-command.d.ts +18 -0
  60. package/dist/extensions/slash-command.d.ts.map +1 -0
  61. package/dist/extensions/slash-command.js +178 -0
  62. package/dist/index.d.ts +24 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +18 -0
  65. package/dist/lib/ai-command-menu.d.ts +9 -0
  66. package/dist/lib/ai-command-menu.d.ts.map +1 -0
  67. package/dist/lib/ai-command-menu.js +43 -0
  68. package/dist/lib/cn.d.ts +4 -0
  69. package/dist/lib/cn.d.ts.map +1 -0
  70. package/dist/lib/cn.js +6 -0
  71. package/dist/lib/editor-format-colors.d.ts +10 -0
  72. package/dist/lib/editor-format-colors.d.ts.map +1 -0
  73. package/dist/lib/editor-format-colors.js +24 -0
  74. package/dist/lib/image-upload.d.ts +14 -0
  75. package/dist/lib/image-upload.d.ts.map +1 -0
  76. package/dist/lib/image-upload.js +29 -0
  77. package/dist/lib/insert-editor-images.d.ts +5 -0
  78. package/dist/lib/insert-editor-images.d.ts.map +1 -0
  79. package/dist/lib/insert-editor-images.js +32 -0
  80. package/dist/lib/types.d.ts +131 -0
  81. package/dist/lib/types.d.ts.map +1 -0
  82. package/dist/lib/types.js +12 -0
  83. package/dist/lib/use-spatika-editor.d.ts +13 -0
  84. package/dist/lib/use-spatika-editor.d.ts.map +1 -0
  85. package/dist/lib/use-spatika-editor.js +171 -0
  86. package/dist/styles.css +1056 -0
  87. package/package.json +101 -0
  88. package/src/composites/AiCommandDock.test.tsx +48 -0
  89. package/src/composites/AiCommandDock.tsx +137 -0
  90. package/src/composites/AiCommandMenu.test.tsx +67 -0
  91. package/src/composites/AiCommandMenu.tsx +120 -0
  92. package/src/composites/AiCommandPanel.test.tsx +42 -0
  93. package/src/composites/AiCommandPanel.tsx +56 -0
  94. package/src/composites/AiOverlay.test.tsx +18 -0
  95. package/src/composites/AiOverlay.tsx +18 -0
  96. package/src/composites/EditorBubbleMenu.test.tsx +49 -0
  97. package/src/composites/EditorBubbleMenu.tsx +73 -0
  98. package/src/composites/EditorToolbar.test.tsx +146 -0
  99. package/src/composites/EditorToolbar.tsx +362 -0
  100. package/src/composites/ImageBubbleMenu.test.tsx +43 -0
  101. package/src/composites/ImageBubbleMenu.tsx +51 -0
  102. package/src/composites/MentionMenu.test.tsx +25 -0
  103. package/src/composites/MentionMenu.tsx +46 -0
  104. package/src/composites/SlashCommandMenu.test.tsx +33 -0
  105. package/src/composites/SlashCommandMenu.tsx +54 -0
  106. package/src/composites/SpatikaEditor.test.tsx +167 -0
  107. package/src/composites/SpatikaEditor.tsx +302 -0
  108. package/src/composites/TableGridPicker.test.tsx +26 -0
  109. package/src/composites/TableGridPicker.tsx +138 -0
  110. package/src/composites/ToolbarButton.test.tsx +21 -0
  111. package/src/composites/ToolbarButton.tsx +24 -0
  112. package/src/composites/ToolbarFormatPanels.test.tsx +46 -0
  113. package/src/composites/ToolbarFormatPanels.tsx +78 -0
  114. package/src/composites/ToolbarInsertPanels.test.tsx +60 -0
  115. package/src/composites/ToolbarInsertPanels.tsx +200 -0
  116. package/src/composites/ToolbarPopover.test.tsx +24 -0
  117. package/src/composites/ToolbarPopover.tsx +95 -0
  118. package/src/extensions/ai-action.test.ts +26 -0
  119. package/src/extensions/ai-action.ts +73 -0
  120. package/src/extensions/mention.test.ts +31 -0
  121. package/src/extensions/mention.ts +158 -0
  122. package/src/extensions/resizable-image-view.test.ts +26 -0
  123. package/src/extensions/resizable-image-view.ts +96 -0
  124. package/src/extensions/resizable-image.test.ts +28 -0
  125. package/src/extensions/resizable-image.ts +75 -0
  126. package/src/extensions/slash-command.test.ts +45 -0
  127. package/src/extensions/slash-command.ts +197 -0
  128. package/src/index.ts +49 -0
  129. package/src/lib/ai-command-menu.test.ts +48 -0
  130. package/src/lib/ai-command-menu.ts +57 -0
  131. package/src/lib/cn.ts +7 -0
  132. package/src/lib/editor-format-colors.test.ts +15 -0
  133. package/src/lib/editor-format-colors.ts +31 -0
  134. package/src/lib/image-upload.test.ts +19 -0
  135. package/src/lib/image-upload.ts +49 -0
  136. package/src/lib/insert-editor-images.test.ts +76 -0
  137. package/src/lib/insert-editor-images.ts +44 -0
  138. package/src/lib/types.ts +149 -0
  139. package/src/lib/use-spatika-editor.test.tsx +56 -0
  140. package/src/lib/use-spatika-editor.ts +191 -0
  141. package/src/styles/editor.css +1056 -0
  142. package/src/test/setup.ts +46 -0
@@ -0,0 +1,159 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from "react";
3
+ import { createPortal } from "react-dom";
4
+ import { EditorContent } from "@tiptap/react";
5
+ import { defaultSlashCommands } from "../extensions/slash-command";
6
+ import { cn } from "../lib/cn";
7
+ import { DEFAULT_TOOLBAR } from "../lib/types";
8
+ import { DEFAULT_AI_ACTIONS, resolveAiCommandActions, resolveAiCommandPlacement, usesAiCommandMenu, } from "../lib/ai-command-menu";
9
+ import { useSpatikaEditor } from "../lib/use-spatika-editor";
10
+ import { AiCommandDock } from "./AiCommandDock";
11
+ import { AiOverlay } from "./AiOverlay";
12
+ import { EditorBubbleMenu } from "./EditorBubbleMenu";
13
+ import { EditorToolbar } from "./EditorToolbar";
14
+ import { ImageBubbleMenu } from "./ImageBubbleMenu";
15
+ import { MentionMenu } from "./MentionMenu";
16
+ import { SlashCommandMenu } from "./SlashCommandMenu";
17
+ const DEFAULT_AI_ACTIONS_LEGACY = DEFAULT_AI_ACTIONS;
18
+ export const SpatikaEditor = forwardRef(function SpatikaEditor({ value, onChange, placeholder, disabled, className, heightClassName = "min-h-[400px]", toolbar, toolbarActions, extensions, starterKit, slashCommands, mentions, onAiSelectionAction, onAiCommand, isAiProcessing, aiActions = DEFAULT_AI_ACTIONS_LEGACY, aiCommandMenu, imageInsert, onInsertImage, resizableImages = true, mobileFullscreen = false, }, ref) {
19
+ const shellRef = useRef(null);
20
+ const [fullscreen, setFullscreen] = useState(false);
21
+ const [slashState, setSlashState] = useState(null);
22
+ const [mentionState, setMentionState] = useState(null);
23
+ const [aiBusyInternal, setAiBusyInternal] = useState(false);
24
+ const { editor, isReady } = useSpatikaEditor({
25
+ value,
26
+ onChange,
27
+ placeholder,
28
+ disabled,
29
+ extensions,
30
+ starterKit,
31
+ slashCommands,
32
+ mentions,
33
+ onAiSelectionAction,
34
+ onAiCommand,
35
+ resizableImages,
36
+ imageInsert,
37
+ onSlashUpdate: setSlashState,
38
+ onMentionUpdate: setMentionState,
39
+ });
40
+ useImperativeHandle(ref, () => ({
41
+ focus: () => editor?.commands.focus(),
42
+ setContent: (html) => editor?.commands.setContent(html),
43
+ getHTML: () => editor?.getHTML() ?? "",
44
+ getJSON: () => editor?.getJSON(),
45
+ expandFullscreen: () => setFullscreen(true),
46
+ }), [editor]);
47
+ const toolbarConfig = { ...DEFAULT_TOOLBAR, ...toolbar };
48
+ const allSlashCommands = useMemo(() => [...defaultSlashCommands(), ...(slashCommands ?? [])], [slashCommands]);
49
+ const filteredSlashItems = useMemo(() => {
50
+ const query = slashState?.query?.toLowerCase() ?? "";
51
+ return allSlashCommands.filter((item) => {
52
+ if (!query)
53
+ return true;
54
+ const haystack = [item.title, item.description, ...(item.keywords ?? [])]
55
+ .filter(Boolean)
56
+ .join(" ")
57
+ .toLowerCase();
58
+ return haystack.includes(query);
59
+ });
60
+ }, [allSlashCommands, slashState?.query]);
61
+ const filteredMentions = useMemo(() => {
62
+ const query = mentionState?.query?.toLowerCase() ?? "";
63
+ const contacts = mentions ?? [];
64
+ return contacts.filter((contact) => contact.label.toLowerCase().includes(query));
65
+ }, [mentions, mentionState?.query]);
66
+ const aiHandler = onAiCommand
67
+ ? onAiCommand
68
+ : onAiSelectionAction
69
+ ? async (context) => onAiSelectionAction(context.actionId, context.selectedText)
70
+ : undefined;
71
+ const commandMenuEnabled = usesAiCommandMenu(aiCommandMenu, Boolean(aiHandler));
72
+ const aiCommandPlacement = resolveAiCommandPlacement(aiCommandMenu);
73
+ const toolbarAiEnabled = commandMenuEnabled && aiCommandPlacement === "toolbar";
74
+ const dockAiEnabled = commandMenuEnabled && aiCommandPlacement === "dock";
75
+ const commandActions = resolveAiCommandActions(aiCommandMenu === false ? undefined : aiCommandMenu, aiActions);
76
+ const bubbleActionsEnabled = Boolean(aiHandler) &&
77
+ (commandMenuEnabled
78
+ ? aiCommandMenu !== false && aiCommandMenu?.bubbleActions
79
+ : true);
80
+ const runAiCommand = useCallback(async (request) => {
81
+ if (!editor || !aiHandler)
82
+ return;
83
+ const { from, to } = editor.state.selection;
84
+ const selectedText = from !== to ? editor.state.doc.textBetween(from, to, " ") : "";
85
+ editor.commands.setAiSelection?.(from !== to ? { from, to } : null);
86
+ editor.commands.setAiProcessing?.(true);
87
+ setAiBusyInternal(true);
88
+ try {
89
+ const result = await aiHandler({
90
+ actionId: request.actionId,
91
+ prompt: request.prompt,
92
+ selectedText,
93
+ documentText: editor.state.doc.textContent,
94
+ html: editor.getHTML(),
95
+ selection: from !== to ? { from, to } : null,
96
+ });
97
+ if (result) {
98
+ if (from !== to) {
99
+ editor.chain().focus().insertContentAt({ from, to }, result).run();
100
+ }
101
+ else {
102
+ editor.chain().focus().insertContentAt(from, result).run();
103
+ }
104
+ }
105
+ }
106
+ finally {
107
+ editor.commands.setAiProcessing?.(false);
108
+ editor.commands.setAiSelection?.(null);
109
+ setAiBusyInternal(false);
110
+ }
111
+ }, [editor, aiHandler]);
112
+ const runLegacyAiAction = useCallback(async (actionId) => {
113
+ if (!editor)
114
+ return;
115
+ const { from, to } = editor.state.selection;
116
+ if (from === to)
117
+ return;
118
+ await runAiCommand({ actionId });
119
+ }, [editor, runAiCommand]);
120
+ useEffect(() => {
121
+ if (!mobileFullscreen || !editor)
122
+ return;
123
+ const onFocus = () => {
124
+ if (window.matchMedia("(max-width: 767px)").matches) {
125
+ setFullscreen(true);
126
+ }
127
+ };
128
+ editor.on("focus", onFocus);
129
+ return () => {
130
+ editor.off("focus", onFocus);
131
+ };
132
+ }, [editor, mobileFullscreen]);
133
+ const shell = (_jsxs("div", { ref: shellRef, "data-slot": "spatika-editor", className: cn("spk-editor-shell", disabled && "spk-editor-shell--disabled", fullscreen && "spk-editor-shell--fullscreen", dockAiEnabled && "spk-editor-shell--with-ai-dock", className), children: [_jsx(EditorToolbar, { editor: editor, config: toolbarConfig, toolbarActions: toolbarActions, aiActions: toolbarAiEnabled ? commandActions : !commandMenuEnabled ? aiActions : undefined, aiCommandMenu: aiCommandMenu === false ? false : aiCommandMenu, useAiCommandMenu: toolbarAiEnabled, onAiCommand: commandMenuEnabled ? runAiCommand : undefined, onAiAction: !commandMenuEnabled ? runLegacyAiAction : undefined, onInsertImage: onInsertImage, imageInsert: imageInsert }), _jsxs("div", { className: "spk-editor-body", children: [_jsx(EditorContent, { editor: editor, className: cn("spk-editor-surface", heightClassName) }), _jsx(EditorBubbleMenu, { editor: editor, aiActions: bubbleActionsEnabled ? (commandMenuEnabled ? commandActions : aiActions) : undefined, onAiAction: runLegacyAiAction }), _jsx(ImageBubbleMenu, { editor: editor }), _jsx(SlashCommandMenu, { editor: editor, items: filteredSlashItems, query: slashState?.query ?? "", activeIndex: slashState?.activeIndex ?? 0, open: Boolean(slashState?.range), onSelect: (item) => {
134
+ if (!editor || !slashState?.range)
135
+ return;
136
+ editor.chain().focus().deleteRange(slashState.range).run();
137
+ item.command(editor);
138
+ setSlashState(null);
139
+ } }), _jsx(MentionMenu, { contacts: filteredMentions, query: mentionState?.query ?? "", activeIndex: mentionState?.activeIndex ?? 0, open: Boolean(mentionState?.range), onSelect: (contact) => {
140
+ if (!editor || !mentionState?.range)
141
+ return;
142
+ editor
143
+ .chain()
144
+ .focus()
145
+ .insertContentAt(mentionState.range, [
146
+ { type: "mention", attrs: { id: contact.id, label: contact.label } },
147
+ { type: "text", text: " " },
148
+ ])
149
+ .run();
150
+ setMentionState(null);
151
+ } }), _jsx(AiOverlay, { visible: Boolean(isAiProcessing || aiBusyInternal) })] }), dockAiEnabled ? (_jsx("div", { className: "spk-editor-ai-dock-footer", children: _jsx(AiCommandDock, { editor: editor, config: aiCommandMenu === false ? undefined : aiCommandMenu, actions: commandActions, onRun: runAiCommand }) })) : null, fullscreen ? (_jsx("div", { className: "spk-editor-fullscreen-bar", children: _jsx("button", { type: "button", className: "spk-editor-fullscreen-close", onClick: () => setFullscreen(false), children: "Done" }) })) : null] }));
152
+ if (fullscreen && typeof document !== "undefined") {
153
+ return createPortal(shell, document.body);
154
+ }
155
+ if (!isReady) {
156
+ return (_jsx("div", { className: cn("spk-editor-shell spk-editor-shell--loading", className), "aria-busy": "true", children: "Loading editor\u2026" }));
157
+ }
158
+ return shell;
159
+ });
@@ -0,0 +1,9 @@
1
+ export declare const TABLE_GRID_SIZE = 10;
2
+ export declare const TABLE_ROWS_MAX = 20;
3
+ export declare const TABLE_COLS_MAX = 12;
4
+ type TableGridPickerProps = {
5
+ onInsert: (rows: number, cols: number, withHeaderRow: boolean) => void;
6
+ };
7
+ export declare function TableGridPicker({ onInsert }: TableGridPickerProps): import("react").JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=TableGridPicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TableGridPicker.d.ts","sourceRoot":"","sources":["../../src/composites/TableGridPicker.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,KAAK,oBAAoB,GAAG;IAC1B,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,KAAK,IAAI,CAAC;CACxE,CAAC;AAeF,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,EAAE,oBAAoB,+BAgHjE"}
@@ -0,0 +1,54 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { Columns3, Rows3 } from "lucide-react";
3
+ import { useState } from "react";
4
+ import { cn } from "../lib/cn";
5
+ export const TABLE_GRID_SIZE = 10;
6
+ export const TABLE_ROWS_MAX = 20;
7
+ export const TABLE_COLS_MAX = 12;
8
+ function clampDimension(value, max) {
9
+ return Math.max(1, Math.min(max, value));
10
+ }
11
+ function pickCell(event) {
12
+ const cell = event.target.closest("[data-table-row]");
13
+ if (!cell)
14
+ return null;
15
+ return {
16
+ rows: clampDimension(Number(cell.dataset.tableRow), TABLE_ROWS_MAX),
17
+ cols: clampDimension(Number(cell.dataset.tableCol), TABLE_COLS_MAX),
18
+ };
19
+ }
20
+ export function TableGridPicker({ onInsert }) {
21
+ const [rows, setRows] = useState(3);
22
+ const [cols, setCols] = useState(3);
23
+ const [withHeaderRow, setWithHeaderRow] = useState(true);
24
+ const [hover, setHover] = useState(null);
25
+ const previewRows = hover?.rows ?? rows;
26
+ const previewCols = hover?.cols ?? cols;
27
+ const handleGridMove = (event) => {
28
+ const picked = pickCell(event);
29
+ if (picked)
30
+ setHover(picked);
31
+ };
32
+ const handleGridClick = (event) => {
33
+ const picked = pickCell(event);
34
+ if (picked) {
35
+ setRows(picked.rows);
36
+ setCols(picked.cols);
37
+ onInsert(picked.rows, picked.cols, withHeaderRow);
38
+ }
39
+ };
40
+ return (_jsxs("div", { className: "spk-editor-table-picker", children: [_jsxs("div", { className: "spk-editor-table-picker-label", "aria-live": "polite", children: [previewCols, " \u00D7 ", previewRows] }), _jsx("div", { className: "spk-editor-table-picker-grid", role: "grid", "aria-label": "Choose table size", onMouseMove: handleGridMove, onMouseLeave: () => setHover(null), onClick: handleGridClick, children: Array.from({ length: TABLE_GRID_SIZE * TABLE_GRID_SIZE }, (_, index) => {
41
+ const rowIndex = Math.floor(index / TABLE_GRID_SIZE);
42
+ const colIndex = index % TABLE_GRID_SIZE;
43
+ const cellRows = rowIndex + 1;
44
+ const cellCols = colIndex + 1;
45
+ const active = cellRows <= previewRows && cellCols <= previewCols;
46
+ return (_jsx("button", { type: "button", role: "gridcell", "data-table-row": cellRows, "data-table-col": cellCols, className: cn("spk-editor-table-picker-cell", active && "spk-editor-table-picker-cell--active"), "aria-label": `${cellCols} by ${cellRows} table`, onMouseDown: (event) => event.preventDefault() }, `${rowIndex}-${colIndex}`));
47
+ }) }), _jsxs("div", { className: "spk-editor-table-picker-size", children: [_jsxs("label", { className: "spk-editor-table-picker-size-field", children: [_jsx(Columns3, { className: "size-4", "aria-hidden": "true" }), _jsx("span", { className: "sr-only", children: "Columns" }), _jsx("input", { type: "number", min: 1, max: TABLE_COLS_MAX, className: "spk-editor-table-picker-size-input", value: previewCols, onChange: (event) => {
48
+ setHover(null);
49
+ setCols(clampDimension(Number.parseInt(event.target.value, 10) || 1, TABLE_COLS_MAX));
50
+ } })] }), _jsx("span", { className: "spk-editor-table-picker-size-sep", "aria-hidden": "true", children: "\u00D7" }), _jsxs("label", { className: "spk-editor-table-picker-size-field", children: [_jsx(Rows3, { className: "size-4", "aria-hidden": "true" }), _jsx("span", { className: "sr-only", children: "Rows" }), _jsx("input", { type: "number", min: 1, max: TABLE_ROWS_MAX, className: "spk-editor-table-picker-size-input", value: previewRows, onChange: (event) => {
51
+ setHover(null);
52
+ setRows(clampDimension(Number.parseInt(event.target.value, 10) || 1, TABLE_ROWS_MAX));
53
+ } })] })] }), _jsxs("label", { className: "spk-editor-toolbar-popover-check", children: [_jsx("input", { type: "checkbox", checked: withHeaderRow, onChange: (event) => setWithHeaderRow(event.target.checked) }), _jsx("span", { children: "Header row" })] }), _jsxs("button", { type: "button", className: "spk-editor-toolbar-popover-btn spk-editor-toolbar-popover-btn--primary spk-editor-table-picker-insert", onClick: () => onInsert(previewRows, previewCols, withHeaderRow), children: ["Insert ", previewCols, " \u00D7 ", previewRows, " table"] })] }));
54
+ }
@@ -0,0 +1,7 @@
1
+ import { type ButtonHTMLAttributes, type ReactNode } from "react";
2
+ /** Compact icon control for the editor toolbar. Avoids a @spatika/react dependency. */
3
+ export declare const ToolbarButton: import("react").ForwardRefExoticComponent<ButtonHTMLAttributes<HTMLButtonElement> & {
4
+ active?: boolean;
5
+ children: ReactNode;
6
+ } & import("react").RefAttributes<HTMLButtonElement>>;
7
+ //# sourceMappingURL=ToolbarButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolbarButton.d.ts","sourceRoot":"","sources":["../../src/composites/ToolbarButton.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,oBAAoB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAG9E,uFAAuF;AACvF,eAAO,MAAM,aAAa;aAGb,OAAO;cACN,SAAS;qDAerB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { cn } from "../lib/cn";
4
+ /** Compact icon control for the editor toolbar. Avoids a @spatika/react dependency. */
5
+ export const ToolbarButton = forwardRef(function ToolbarButton({ className, active, children, ...props }, ref) {
6
+ return (_jsx("button", { ref: ref, type: "button", "data-slot": "editor-toolbar-button", "data-active": active ? "true" : undefined, className: cn("spk-editor-toolbar-btn", active && "spk-editor-toolbar-btn--active", className), ...props, children: children }));
7
+ });
@@ -0,0 +1,9 @@
1
+ import type { Editor } from "@tiptap/core";
2
+ type PanelProps = {
3
+ editor: Editor;
4
+ close: () => void;
5
+ };
6
+ export declare function TextColorPanel({ editor, close }: PanelProps): import("react").JSX.Element;
7
+ export declare function HighlightColorPanel({ editor, close }: PanelProps): import("react").JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=ToolbarFormatPanels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolbarFormatPanels.d.ts","sourceRoot":"","sources":["../../src/composites/ToolbarFormatPanels.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAK3C,KAAK,UAAU,GAAG;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;AAqCF,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,UAAU,+BAe3D;AAED,wBAAgB,mBAAmB,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,UAAU,+BAehE"}
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { EDITOR_HIGHLIGHT_COLORS, EDITOR_TEXT_COLORS } from "../lib/editor-format-colors";
3
+ import { cn } from "../lib/cn";
4
+ function ColorSwatchGrid({ colors, activeValue, onSelect, }) {
5
+ return (_jsx("div", { className: "spk-editor-color-grid", role: "list", children: colors.map((color) => {
6
+ const isActive = color.value === null ? !activeValue : activeValue?.toLowerCase() === color.value.toLowerCase();
7
+ return (_jsx("button", { type: "button", role: "listitem", "aria-label": color.label, "aria-pressed": isActive, className: cn("spk-editor-color-swatch", color.value === null && "spk-editor-color-swatch--default", isActive && "spk-editor-color-swatch--active"), style: color.value ? { "--swatch-color": color.value } : undefined, onClick: () => onSelect(color.value) }, color.id));
8
+ }) }));
9
+ }
10
+ export function TextColorPanel({ editor, close }) {
11
+ const activeColor = editor.getAttributes("textStyle").color ?? null;
12
+ const apply = (value) => {
13
+ if (value === null) {
14
+ editor.chain().focus().unsetColor().run();
15
+ }
16
+ else {
17
+ editor.chain().focus().setColor(value).run();
18
+ }
19
+ close();
20
+ };
21
+ return (_jsx(ColorSwatchGrid, { colors: EDITOR_TEXT_COLORS, activeValue: activeColor, onSelect: apply }));
22
+ }
23
+ export function HighlightColorPanel({ editor, close }) {
24
+ const activeColor = editor.getAttributes("highlight").color ?? null;
25
+ const apply = (value) => {
26
+ if (value === null) {
27
+ editor.chain().focus().unsetHighlight().run();
28
+ }
29
+ else {
30
+ editor.chain().focus().setHighlight({ color: value }).run();
31
+ }
32
+ close();
33
+ };
34
+ return (_jsx(ColorSwatchGrid, { colors: EDITOR_HIGHLIGHT_COLORS, activeValue: activeColor, onSelect: apply }));
35
+ }
@@ -0,0 +1,14 @@
1
+ import type { Editor } from "@tiptap/core";
2
+ import type { ImageInsertConfig } from "../lib/types";
3
+ type PanelProps = {
4
+ editor: Editor;
5
+ close: () => void;
6
+ };
7
+ type ImagePanelProps = PanelProps & {
8
+ config?: ImageInsertConfig;
9
+ };
10
+ export declare function LinkInsertPanel({ editor, close }: PanelProps): import("react").JSX.Element;
11
+ export declare function ImageInsertPanel({ editor, close, config }: ImagePanelProps): import("react").JSX.Element;
12
+ export declare function TableInsertPanel({ editor, close }: PanelProps): import("react").JSX.Element;
13
+ export {};
14
+ //# sourceMappingURL=ToolbarInsertPanels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolbarInsertPanels.d.ts","sourceRoot":"","sources":["../../src/composites/ToolbarInsertPanels.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAKtD,KAAK,UAAU,GAAG;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;AAEF,KAAK,eAAe,GAAG,UAAU,GAAG;IAClC,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B,CAAC;AAEF,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,UAAU,+BAmD5D;AAMD,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,eAAe,+BA4G1E;AAED,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,UAAU,+BAe7D"}
@@ -0,0 +1,96 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ImagePlus, Upload } from "lucide-react";
3
+ import { useRef, useState } from "react";
4
+ import { cn } from "../lib/cn";
5
+ import { insertImageFilesIntoEditor } from "../lib/insert-editor-images";
6
+ import { TableGridPicker } from "./TableGridPicker";
7
+ export function LinkInsertPanel({ editor, close }) {
8
+ const previous = editor.getAttributes("link").href;
9
+ const [url, setUrl] = useState(previous ?? "https://");
10
+ const apply = () => {
11
+ const trimmed = url.trim();
12
+ if (!trimmed) {
13
+ editor.chain().focus().extendMarkRange("link").unsetLink().run();
14
+ }
15
+ else {
16
+ editor.chain().focus().extendMarkRange("link").setLink({ href: trimmed }).run();
17
+ }
18
+ close();
19
+ };
20
+ const remove = () => {
21
+ editor.chain().focus().extendMarkRange("link").unsetLink().run();
22
+ close();
23
+ };
24
+ return (_jsxs("div", { className: "spk-editor-toolbar-popover-form", children: [_jsxs("label", { className: "spk-editor-toolbar-popover-field", children: [_jsx("span", { children: "URL" }), _jsx("input", { type: "url", className: "spk-editor-toolbar-popover-input", value: url, placeholder: "https://", onChange: (event) => setUrl(event.target.value), onKeyDown: (event) => {
25
+ if (event.key === "Enter")
26
+ apply();
27
+ if (event.key === "Escape")
28
+ close();
29
+ }, autoFocus: true })] }), _jsxs("div", { className: "spk-editor-toolbar-popover-actions", children: [previous ? (_jsx("button", { type: "button", className: "spk-editor-toolbar-popover-btn spk-editor-toolbar-popover-btn--ghost", onClick: remove, children: "Remove" })) : null, _jsx("button", { type: "button", className: "spk-editor-toolbar-popover-btn spk-editor-toolbar-popover-btn--ghost", onClick: close, children: "Cancel" }), _jsx("button", { type: "button", className: "spk-editor-toolbar-popover-btn spk-editor-toolbar-popover-btn--primary", onClick: apply, children: "Apply" })] })] }));
30
+ }
31
+ function insertImageSrc(editor, src) {
32
+ editor.chain().focus().setImage({ src }).run();
33
+ }
34
+ export function ImageInsertPanel({ editor, close, config }) {
35
+ const inputRef = useRef(null);
36
+ const [url, setUrl] = useState("");
37
+ const [error, setError] = useState(null);
38
+ const [dragging, setDragging] = useState(false);
39
+ const maxFiles = config?.maxFiles ?? 3;
40
+ const maxSizeBytes = config?.maxSizeBytes ?? 5 * 1024 * 1024;
41
+ const accept = config?.accept ?? "image/*";
42
+ const limitMb = Math.round(maxSizeBytes / (1024 * 1024));
43
+ const handleFiles = async (files) => {
44
+ setError(null);
45
+ const inserted = await insertImageFilesIntoEditor(editor, files, config);
46
+ if (!inserted) {
47
+ setError("Could not insert image.");
48
+ return;
49
+ }
50
+ close();
51
+ };
52
+ const insertFromUrl = () => {
53
+ const trimmed = url.trim();
54
+ if (!trimmed)
55
+ return;
56
+ insertImageSrc(editor, trimmed);
57
+ close();
58
+ };
59
+ return (_jsxs("div", { className: "spk-editor-toolbar-popover-form spk-editor-image-insert", children: [_jsxs("button", { type: "button", className: cn("spk-editor-image-dropzone", dragging && "spk-editor-image-dropzone--active"), onClick: () => inputRef.current?.click(), onDragEnter: (event) => {
60
+ event.preventDefault();
61
+ setDragging(true);
62
+ }, onDragOver: (event) => {
63
+ event.preventDefault();
64
+ setDragging(true);
65
+ }, onDragLeave: () => setDragging(false), onDrop: (event) => {
66
+ event.preventDefault();
67
+ setDragging(false);
68
+ if (event.dataTransfer.files.length) {
69
+ void handleFiles(event.dataTransfer.files);
70
+ }
71
+ }, children: [_jsx("span", { className: "spk-editor-image-dropzone-icon", "aria-hidden": "true", children: _jsx(Upload, { className: "size-5" }) }), _jsx("span", { className: "spk-editor-image-dropzone-title", children: "Click to upload or drag and drop" }), _jsxs("span", { className: "spk-editor-image-dropzone-hint", children: ["Maximum ", maxFiles, " files, ", limitMb, "MB each"] })] }), _jsx("input", { ref: inputRef, type: "file", accept: accept, multiple: maxFiles > 1, className: "sr-only", onChange: (event) => {
72
+ if (event.target.files?.length) {
73
+ void handleFiles(event.target.files);
74
+ }
75
+ } }), error ? _jsx("p", { className: "spk-editor-image-insert-error", children: error }) : null, _jsx("div", { className: "spk-editor-image-insert-divider", children: _jsx("span", { children: "or paste a URL" }) }), _jsxs("label", { className: "spk-editor-toolbar-popover-field", children: [_jsx("span", { children: "Image URL" }), _jsx("input", { type: "url", className: "spk-editor-toolbar-popover-input", value: url, placeholder: "https://example.com/photo.jpg", onChange: (event) => setUrl(event.target.value), onKeyDown: (event) => {
76
+ if (event.key === "Enter")
77
+ insertFromUrl();
78
+ if (event.key === "Escape")
79
+ close();
80
+ } })] }), _jsxs("div", { className: "spk-editor-toolbar-popover-actions", children: [_jsx("button", { type: "button", className: "spk-editor-toolbar-popover-btn spk-editor-toolbar-popover-btn--ghost", onClick: close, children: "Cancel" }), _jsxs("button", { type: "button", className: "spk-editor-toolbar-popover-btn spk-editor-toolbar-popover-btn--primary", onClick: insertFromUrl, disabled: !url.trim(), children: [_jsx(ImagePlus, { className: "size-4", "aria-hidden": "true" }), "Insert"] })] })] }));
81
+ }
82
+ export function TableInsertPanel({ editor, close }) {
83
+ const insert = (nextRows, nextCols, withHeaderRow) => {
84
+ editor
85
+ .chain()
86
+ .focus()
87
+ .insertTable({
88
+ rows: nextRows,
89
+ cols: nextCols,
90
+ withHeaderRow,
91
+ })
92
+ .run();
93
+ close();
94
+ };
95
+ return _jsx(TableGridPicker, { onInsert: insert });
96
+ }
@@ -0,0 +1,14 @@
1
+ import { type ReactNode } from "react";
2
+ type ToolbarPopoverProps = {
3
+ label: string;
4
+ icon: ReactNode;
5
+ title: string;
6
+ className?: string;
7
+ active?: boolean;
8
+ children: (api: {
9
+ close: () => void;
10
+ }) => ReactNode;
11
+ };
12
+ export declare function ToolbarPopover({ label, icon, title, className, active, children, }: ToolbarPopoverProps): import("react").JSX.Element;
13
+ export {};
14
+ //# sourceMappingURL=ToolbarPopover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolbarPopover.d.ts","sourceRoot":"","sources":["../../src/composites/ToolbarPopover.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAK5F,KAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,CAAC,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,IAAI,CAAA;KAAE,KAAK,SAAS,CAAC;CACrD,CAAC;AAEF,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,IAAI,EACJ,KAAK,EACL,SAAS,EACT,MAAc,EACd,QAAQ,GACT,EAAE,mBAAmB,+BAyErB"}
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useId, useLayoutEffect, useRef, useState } from "react";
3
+ import { createPortal } from "react-dom";
4
+ import { cn } from "../lib/cn";
5
+ import { ToolbarButton } from "./ToolbarButton";
6
+ export function ToolbarPopover({ label, icon, title, className, active = false, children, }) {
7
+ const [open, setOpen] = useState(false);
8
+ const [panelStyle, setPanelStyle] = useState(null);
9
+ const rootRef = useRef(null);
10
+ const triggerRef = useRef(null);
11
+ const panelRef = useRef(null);
12
+ const titleId = useId();
13
+ const close = () => setOpen(false);
14
+ useLayoutEffect(() => {
15
+ if (!open || !triggerRef.current)
16
+ return;
17
+ const updatePosition = () => {
18
+ const rect = triggerRef.current?.getBoundingClientRect();
19
+ if (!rect)
20
+ return;
21
+ setPanelStyle({
22
+ top: rect.bottom + 6,
23
+ left: rect.left,
24
+ });
25
+ };
26
+ updatePosition();
27
+ window.addEventListener("resize", updatePosition);
28
+ window.addEventListener("scroll", updatePosition, true);
29
+ return () => {
30
+ window.removeEventListener("resize", updatePosition);
31
+ window.removeEventListener("scroll", updatePosition, true);
32
+ };
33
+ }, [open]);
34
+ useEffect(() => {
35
+ if (!open)
36
+ return;
37
+ const onPointerDown = (event) => {
38
+ const target = event.target;
39
+ if (rootRef.current?.contains(target) || panelRef.current?.contains(target))
40
+ return;
41
+ setOpen(false);
42
+ };
43
+ document.addEventListener("mousedown", onPointerDown);
44
+ return () => document.removeEventListener("mousedown", onPointerDown);
45
+ }, [open]);
46
+ const panel = open && panelStyle ? (_jsxs("div", { ref: panelRef, id: titleId, className: "spk-editor-toolbar-popover-panel spk-editor-toolbar-popover-panel--portal", role: "dialog", "aria-label": title, style: { top: panelStyle.top, left: panelStyle.left }, onMouseDown: (event) => event.stopPropagation(), onPointerDown: (event) => event.stopPropagation(), children: [_jsx("div", { className: "spk-editor-toolbar-popover-title", children: title }), children({ close })] })) : null;
47
+ return (_jsxs("div", { ref: rootRef, className: cn("spk-editor-toolbar-popover", className), children: [_jsx(ToolbarButton, { ref: triggerRef, "aria-label": label, "aria-expanded": open, "aria-haspopup": "dialog", "aria-controls": open ? titleId : undefined, active: open || active, onClick: () => setOpen((value) => !value), children: icon }), typeof document !== "undefined" && panel ? createPortal(panel, document.body) : null] }));
48
+ }
@@ -0,0 +1,24 @@
1
+ import { Extension } from "@tiptap/core";
2
+ import { PluginKey } from "@tiptap/pm/state";
3
+ export declare const AiActionPluginKey: PluginKey<any>;
4
+ export type AiActionStorage = {
5
+ processing: boolean;
6
+ lastSelection: {
7
+ from: number;
8
+ to: number;
9
+ } | null;
10
+ };
11
+ /** Highlights the active selection while an AI transform runs. */
12
+ export declare function createAiActionExtension(): Extension<any, any>;
13
+ declare module "@tiptap/core" {
14
+ interface Commands<ReturnType> {
15
+ aiAction: {
16
+ setAiProcessing: (processing: boolean) => ReturnType;
17
+ setAiSelection: (range: {
18
+ from: number;
19
+ to: number;
20
+ } | null) => ReturnType;
21
+ };
22
+ }
23
+ }
24
+ //# sourceMappingURL=ai-action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-action.d.ts","sourceRoot":"","sources":["../../src/extensions/ai-action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAU,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGrD,eAAO,MAAM,iBAAiB,gBAAmC,CAAC;AAElE,MAAM,MAAM,eAAe,GAAG;IAC5B,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACpD,CAAC;AAEF,kEAAkE;AAClE,wBAAgB,uBAAuB,wBAmDtC;AAED,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,QAAQ,CAAC,UAAU;QAC3B,QAAQ,EAAE;YACR,eAAe,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,UAAU,CAAC;YACrD,cAAc,EAAE,CAAC,KAAK,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,EAAE,EAAE,MAAM,CAAA;aAAE,GAAG,IAAI,KAAK,UAAU,CAAC;SAC5E,CAAC;KACH;CACF"}
@@ -0,0 +1,53 @@
1
+ import { Extension } from "@tiptap/core";
2
+ import { Plugin, PluginKey } from "@tiptap/pm/state";
3
+ import { Decoration, DecorationSet } from "@tiptap/pm/view";
4
+ export const AiActionPluginKey = new PluginKey("spatikaAiAction");
5
+ /** Highlights the active selection while an AI transform runs. */
6
+ export function createAiActionExtension() {
7
+ return Extension.create({
8
+ name: "aiAction",
9
+ addStorage() {
10
+ return {
11
+ processing: false,
12
+ lastSelection: null,
13
+ };
14
+ },
15
+ addCommands() {
16
+ return {
17
+ setAiProcessing: (processing) => ({ tr, dispatch }) => {
18
+ this.storage.processing = processing;
19
+ if (dispatch)
20
+ dispatch(tr);
21
+ return true;
22
+ },
23
+ setAiSelection: (range) => ({ tr, dispatch }) => {
24
+ this.storage.lastSelection = range;
25
+ if (dispatch)
26
+ dispatch(tr);
27
+ return true;
28
+ },
29
+ };
30
+ },
31
+ addProseMirrorPlugins() {
32
+ const storage = this.storage;
33
+ return [
34
+ new Plugin({
35
+ key: AiActionPluginKey,
36
+ props: {
37
+ decorations: (state) => {
38
+ if (!storage.processing || !storage.lastSelection) {
39
+ return DecorationSet.empty;
40
+ }
41
+ const { from, to } = storage.lastSelection;
42
+ if (from === to)
43
+ return DecorationSet.empty;
44
+ return DecorationSet.create(state.doc, [
45
+ Decoration.inline(from, to, { class: "spk-editor-ai-selection" }),
46
+ ]);
47
+ },
48
+ },
49
+ }),
50
+ ];
51
+ },
52
+ });
53
+ }
@@ -0,0 +1,17 @@
1
+ import { Extension, Node } from "@tiptap/core";
2
+ import { PluginKey } from "@tiptap/pm/state";
3
+ import type { MentionContact } from "../lib/types";
4
+ export type MentionStorage = {
5
+ contacts: MentionContact[];
6
+ activeIndex: number;
7
+ query: string;
8
+ range: {
9
+ from: number;
10
+ to: number;
11
+ } | null;
12
+ filteredItems: MentionContact[];
13
+ runCommand: ((item: MentionContact) => void) | null;
14
+ };
15
+ export declare const MentionPluginKey: PluginKey<any>;
16
+ export declare function createMentionExtension(contacts: MentionContact[], onUpdate?: (state: MentionStorage) => void): (Extension<any, any> | Node<any, any>)[];
17
+ //# sourceMappingURL=mention.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mention.d.ts","sourceRoot":"","sources":["../../src/extensions/mention.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmB,IAAI,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3C,aAAa,EAAE,cAAc,EAAE,CAAC;IAChC,UAAU,EAAE,CAAC,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;CACrD,CAAC;AAEF,eAAO,MAAM,gBAAgB,gBAAkC,CAAC;AAEhE,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,cAAc,EAAE,EAC1B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,4CAsC3C"}