@kopexa/tiptap 17.3.0 → 17.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 (59) hide show
  1. package/dist/{chunk-NSYSECKW.mjs → chunk-2UDCL55K.mjs} +3 -1
  2. package/dist/chunk-4HO7BWDC.mjs +89 -0
  3. package/dist/chunk-552JLRNB.mjs +35 -0
  4. package/dist/chunk-5SMDMQDF.mjs +34 -0
  5. package/dist/{chunk-XKWTI3MA.mjs → chunk-DQK6PA4U.mjs} +11 -2
  6. package/dist/chunk-H7MS2UMO.mjs +168 -0
  7. package/dist/chunk-JVSH5T4B.mjs +72 -0
  8. package/dist/chunk-JZBRWJHC.mjs +170 -0
  9. package/dist/chunk-Q5FK7SFY.mjs +75 -0
  10. package/dist/chunk-QIELBKP3.mjs +104 -0
  11. package/dist/{chunk-XNDXYI2N.mjs → chunk-XGAABDMW.mjs} +2 -1
  12. package/dist/extensions/variable/extract-variables.d.mts +16 -0
  13. package/dist/extensions/variable/extract-variables.d.ts +16 -0
  14. package/dist/extensions/variable/extract-variables.js +58 -0
  15. package/dist/extensions/variable/extract-variables.mjs +7 -0
  16. package/dist/extensions/variable/index.d.mts +38 -0
  17. package/dist/extensions/variable/index.d.ts +38 -0
  18. package/dist/extensions/variable/index.js +190 -0
  19. package/dist/extensions/variable/index.mjs +11 -0
  20. package/dist/extensions/variable/messages.d.mts +69 -0
  21. package/dist/extensions/variable/messages.d.ts +69 -0
  22. package/dist/extensions/variable/messages.js +98 -0
  23. package/dist/extensions/variable/messages.mjs +7 -0
  24. package/dist/extensions/variable/variable-context.d.mts +56 -0
  25. package/dist/extensions/variable/variable-context.d.ts +56 -0
  26. package/dist/extensions/variable/variable-context.js +70 -0
  27. package/dist/extensions/variable/variable-context.mjs +12 -0
  28. package/dist/extensions/variable/variable-filler-dialog.d.mts +43 -0
  29. package/dist/extensions/variable/variable-filler-dialog.d.ts +43 -0
  30. package/dist/extensions/variable/variable-filler-dialog.js +207 -0
  31. package/dist/extensions/variable/variable-filler-dialog.mjs +9 -0
  32. package/dist/extensions/variable/variable-suggestion.d.mts +31 -0
  33. package/dist/extensions/variable/variable-suggestion.d.ts +31 -0
  34. package/dist/extensions/variable/variable-suggestion.js +615 -0
  35. package/dist/extensions/variable/variable-suggestion.mjs +14 -0
  36. package/dist/extensions/variable/variable-view.d.mts +13 -0
  37. package/dist/extensions/variable/variable-view.d.ts +13 -0
  38. package/dist/extensions/variable/variable-view.js +110 -0
  39. package/dist/extensions/variable/variable-view.mjs +11 -0
  40. package/dist/hooks/use-create-editor.d.mts +8 -2
  41. package/dist/hooks/use-create-editor.d.ts +8 -2
  42. package/dist/hooks/use-create-editor.js +163 -7
  43. package/dist/hooks/use-create-editor.mjs +4 -1
  44. package/dist/index.d.mts +5 -0
  45. package/dist/index.d.ts +5 -0
  46. package/dist/index.js +1866 -1260
  47. package/dist/index.mjs +35 -9
  48. package/dist/presets/basic/editor-header.mjs +3 -3
  49. package/dist/presets/basic/index.d.mts +12 -1
  50. package/dist/presets/basic/index.d.ts +12 -1
  51. package/dist/presets/basic/index.js +4245 -3858
  52. package/dist/presets/basic/index.mjs +12 -8
  53. package/dist/ui/bubble-menu/index.js +3 -1
  54. package/dist/ui/bubble-menu/index.mjs +1 -1
  55. package/dist/ui/link-bubble/index.js +2 -1
  56. package/dist/ui/link-bubble/index.mjs +1 -1
  57. package/package.json +24 -24
  58. package/dist/chunk-LWU4F64F.mjs +0 -110
  59. package/dist/{chunk-FDPXD6VC.mjs → chunk-RFWNKE7D.mjs} +3 -3
@@ -0,0 +1,31 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { Editor } from '@tiptap/react';
3
+
4
+ interface VariableDefinition {
5
+ /** The variable key/name */
6
+ name: string;
7
+ /** Display label for the variable */
8
+ label: string;
9
+ /** Optional description */
10
+ description?: string;
11
+ /** Category for grouping */
12
+ category?: string;
13
+ /** Default/fallback value */
14
+ fallback?: string;
15
+ }
16
+ interface VariableSuggestionProps {
17
+ editor?: Editor | null;
18
+ /**
19
+ * List of available variables to suggest
20
+ */
21
+ variables: VariableDefinition[];
22
+ }
23
+ /**
24
+ * Variable Suggestion Menu
25
+ *
26
+ * A suggestion popup triggered by typing `{{` that shows available
27
+ * template variables for insertion.
28
+ */
29
+ declare function VariableSuggestion({ editor, variables, }: VariableSuggestionProps): react_jsx_runtime.JSX.Element;
30
+
31
+ export { type VariableDefinition, VariableSuggestion, type VariableSuggestionProps };
@@ -0,0 +1,31 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { Editor } from '@tiptap/react';
3
+
4
+ interface VariableDefinition {
5
+ /** The variable key/name */
6
+ name: string;
7
+ /** Display label for the variable */
8
+ label: string;
9
+ /** Optional description */
10
+ description?: string;
11
+ /** Category for grouping */
12
+ category?: string;
13
+ /** Default/fallback value */
14
+ fallback?: string;
15
+ }
16
+ interface VariableSuggestionProps {
17
+ editor?: Editor | null;
18
+ /**
19
+ * List of available variables to suggest
20
+ */
21
+ variables: VariableDefinition[];
22
+ }
23
+ /**
24
+ * Variable Suggestion Menu
25
+ *
26
+ * A suggestion popup triggered by typing `{{` that shows available
27
+ * template variables for insertion.
28
+ */
29
+ declare function VariableSuggestion({ editor, variables, }: VariableSuggestionProps): react_jsx_runtime.JSX.Element;
30
+
31
+ export { type VariableDefinition, VariableSuggestion, type VariableSuggestionProps };
@@ -0,0 +1,615 @@
1
+ "use client";
2
+ "use strict";
3
+ "use client";
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
30
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
+
32
+ // src/extensions/variable/variable-suggestion.tsx
33
+ var variable_suggestion_exports = {};
34
+ __export(variable_suggestion_exports, {
35
+ VariableSuggestion: () => VariableSuggestion
36
+ });
37
+ module.exports = __toCommonJS(variable_suggestion_exports);
38
+ var import_editor_utils = require("@kopexa/editor-utils");
39
+ var import_theme = require("@kopexa/theme");
40
+ var import_state2 = require("@tiptap/pm/state");
41
+ var React4 = __toESM(require("react"));
42
+
43
+ // src/ui/suggestion-menu/suggestion-menu.tsx
44
+ var import_react4 = require("@floating-ui/react");
45
+ var import_state = require("@tiptap/pm/state");
46
+ var import_suggestion = require("@tiptap/suggestion");
47
+ var React3 = __toESM(require("react"));
48
+
49
+ // src/hooks/use-floating-element.ts
50
+ var import_react = require("@floating-ui/react");
51
+ var React = __toESM(require("react"));
52
+ function useFloatingElement(show, referencePos, zIndex, options) {
53
+ const { dismissOptions, ...floatingOptions } = options || {};
54
+ const { refs, update, context, floatingStyles } = (0, import_react.useFloating)({
55
+ open: show,
56
+ ...floatingOptions
57
+ });
58
+ const { isMounted, styles } = (0, import_react.useTransitionStyles)(context);
59
+ const dismiss = (0, import_react.useDismiss)(context, dismissOptions);
60
+ const { getReferenceProps, getFloatingProps } = (0, import_react.useInteractions)([dismiss]);
61
+ React.useEffect(() => {
62
+ update();
63
+ }, [referencePos, update]);
64
+ React.useEffect(() => {
65
+ if (referencePos === null) {
66
+ return;
67
+ }
68
+ refs.setReference({
69
+ getBoundingClientRect: () => referencePos
70
+ });
71
+ }, [referencePos, refs]);
72
+ return React.useMemo(
73
+ () => ({
74
+ isMounted,
75
+ ref: refs.setFloating,
76
+ style: {
77
+ ...styles,
78
+ ...floatingStyles,
79
+ zIndex
80
+ },
81
+ update,
82
+ getFloatingProps,
83
+ getReferenceProps
84
+ }),
85
+ [
86
+ floatingStyles,
87
+ isMounted,
88
+ refs.setFloating,
89
+ styles,
90
+ update,
91
+ zIndex,
92
+ getFloatingProps,
93
+ getReferenceProps
94
+ ]
95
+ );
96
+ }
97
+
98
+ // src/hooks/use-menu-navigation.ts
99
+ var React2 = __toESM(require("react"));
100
+ function useMenuNavigation({
101
+ editor,
102
+ containerRef,
103
+ query,
104
+ items,
105
+ onSelect,
106
+ onClose,
107
+ orientation = "vertical",
108
+ autoSelectFirstItem = true
109
+ }) {
110
+ const [selectedIndex, setSelectedIndex] = React2.useState(
111
+ autoSelectFirstItem ? 0 : -1
112
+ );
113
+ React2.useEffect(() => {
114
+ const handleKeyboardNavigation = (event) => {
115
+ if (!items.length) return false;
116
+ const moveNext = () => setSelectedIndex((currentIndex) => {
117
+ if (currentIndex === -1) return 0;
118
+ return (currentIndex + 1) % items.length;
119
+ });
120
+ const movePrev = () => setSelectedIndex((currentIndex) => {
121
+ if (currentIndex === -1) return items.length - 1;
122
+ return (currentIndex - 1 + items.length) % items.length;
123
+ });
124
+ switch (event.key) {
125
+ case "ArrowUp": {
126
+ if (orientation === "horizontal") return false;
127
+ event.preventDefault();
128
+ movePrev();
129
+ return true;
130
+ }
131
+ case "ArrowDown": {
132
+ if (orientation === "horizontal") return false;
133
+ event.preventDefault();
134
+ moveNext();
135
+ return true;
136
+ }
137
+ case "ArrowLeft": {
138
+ if (orientation === "vertical") return false;
139
+ event.preventDefault();
140
+ movePrev();
141
+ return true;
142
+ }
143
+ case "ArrowRight": {
144
+ if (orientation === "vertical") return false;
145
+ event.preventDefault();
146
+ moveNext();
147
+ return true;
148
+ }
149
+ case "Tab": {
150
+ event.preventDefault();
151
+ if (event.shiftKey) {
152
+ movePrev();
153
+ } else {
154
+ moveNext();
155
+ }
156
+ return true;
157
+ }
158
+ case "Home": {
159
+ event.preventDefault();
160
+ setSelectedIndex(0);
161
+ return true;
162
+ }
163
+ case "End": {
164
+ event.preventDefault();
165
+ setSelectedIndex(items.length - 1);
166
+ return true;
167
+ }
168
+ case "Enter": {
169
+ if (event.isComposing) return false;
170
+ event.preventDefault();
171
+ if (selectedIndex !== -1 && items[selectedIndex]) {
172
+ onSelect == null ? void 0 : onSelect(items[selectedIndex]);
173
+ }
174
+ return true;
175
+ }
176
+ case "Escape": {
177
+ event.preventDefault();
178
+ onClose == null ? void 0 : onClose();
179
+ return true;
180
+ }
181
+ default:
182
+ return false;
183
+ }
184
+ };
185
+ let targetElement = null;
186
+ if (editor) {
187
+ targetElement = editor.view.dom;
188
+ } else if (containerRef == null ? void 0 : containerRef.current) {
189
+ targetElement = containerRef.current;
190
+ }
191
+ if (targetElement) {
192
+ targetElement.addEventListener("keydown", handleKeyboardNavigation, true);
193
+ return () => {
194
+ targetElement == null ? void 0 : targetElement.removeEventListener(
195
+ "keydown",
196
+ handleKeyboardNavigation,
197
+ true
198
+ );
199
+ };
200
+ }
201
+ return void 0;
202
+ }, [
203
+ editor,
204
+ containerRef,
205
+ items,
206
+ selectedIndex,
207
+ onSelect,
208
+ onClose,
209
+ orientation
210
+ ]);
211
+ React2.useEffect(() => {
212
+ if (query) {
213
+ setSelectedIndex(autoSelectFirstItem ? 0 : -1);
214
+ }
215
+ }, [query, autoSelectFirstItem]);
216
+ return {
217
+ selectedIndex: items.length ? selectedIndex : void 0,
218
+ setSelectedIndex
219
+ };
220
+ }
221
+
222
+ // src/hooks/use-tiptap-editor.ts
223
+ var import_react2 = require("@tiptap/react");
224
+ var import_react3 = require("react");
225
+ function useTiptapEditor(providedEditor) {
226
+ const { editor: coreEditor } = (0, import_react2.useCurrentEditor)();
227
+ const mainEditor = (0, import_react3.useMemo)(
228
+ () => providedEditor || coreEditor,
229
+ [providedEditor, coreEditor]
230
+ );
231
+ const editorState = (0, import_react2.useEditorState)({
232
+ editor: mainEditor,
233
+ selector(context) {
234
+ if (!context.editor) {
235
+ return {
236
+ editor: null,
237
+ editorState: void 0,
238
+ canCommand: void 0
239
+ };
240
+ }
241
+ return {
242
+ editor: context.editor,
243
+ editorState: context.editor.state,
244
+ canCommand: context.editor.can
245
+ };
246
+ }
247
+ });
248
+ return editorState || { editor: null };
249
+ }
250
+
251
+ // src/ui/suggestion-menu/suggestion-menu-utils.ts
252
+ function calculateStartPosition(cursorPosition, previousNode, triggerChar) {
253
+ if (!(previousNode == null ? void 0 : previousNode.text) || !triggerChar) {
254
+ return cursorPosition;
255
+ }
256
+ const commandText = previousNode.text;
257
+ const triggerCharIndex = commandText.lastIndexOf(triggerChar);
258
+ if (triggerCharIndex === -1) {
259
+ return cursorPosition;
260
+ }
261
+ const textLength = commandText.substring(triggerCharIndex).length;
262
+ return cursorPosition - textLength;
263
+ }
264
+
265
+ // src/ui/suggestion-menu/suggestion-menu.tsx
266
+ var import_jsx_runtime = require("react/jsx-runtime");
267
+ var SuggestionMenu = ({
268
+ editor: providedEditor,
269
+ floatingOptions,
270
+ selector = "tiptap-suggestion-menu",
271
+ children,
272
+ maxHeight = 384,
273
+ pluginKey = import_suggestion.SuggestionPluginKey,
274
+ ...internalSuggestionProps
275
+ }) => {
276
+ const { editor } = useTiptapEditor(providedEditor);
277
+ const [show, setShow] = React3.useState(false);
278
+ const [internalClientRect, setInternalClientRect] = React3.useState(null);
279
+ const [internalCommand, setInternalCommand] = React3.useState(null);
280
+ const [internalItems, setInternalItems] = React3.useState(
281
+ []
282
+ );
283
+ const [internalQuery, setInternalQuery] = React3.useState("");
284
+ const [, setInternalRange] = React3.useState(null);
285
+ const { ref, style, getFloatingProps, isMounted } = useFloatingElement(
286
+ show,
287
+ internalClientRect,
288
+ 1e3,
289
+ {
290
+ placement: "bottom-start",
291
+ middleware: [
292
+ (0, import_react4.offset)(10),
293
+ (0, import_react4.flip)({
294
+ mainAxis: true,
295
+ crossAxis: false
296
+ }),
297
+ (0, import_react4.shift)(),
298
+ (0, import_react4.size)({
299
+ apply({ availableHeight, elements }) {
300
+ if (elements.floating) {
301
+ const maxHeightValue = maxHeight ? Math.min(maxHeight, availableHeight) : availableHeight;
302
+ elements.floating.style.setProperty(
303
+ "--suggestion-menu-max-height",
304
+ `${maxHeightValue}px`
305
+ );
306
+ }
307
+ }
308
+ })
309
+ ],
310
+ onOpenChange(open) {
311
+ if (!open) {
312
+ setShow(false);
313
+ }
314
+ },
315
+ ...floatingOptions
316
+ }
317
+ );
318
+ const internalSuggestionPropsRef = React3.useRef(internalSuggestionProps);
319
+ React3.useEffect(() => {
320
+ internalSuggestionPropsRef.current = internalSuggestionProps;
321
+ }, [internalSuggestionProps]);
322
+ const closePopup = React3.useCallback(() => {
323
+ setShow(false);
324
+ }, []);
325
+ React3.useEffect(() => {
326
+ if (!editor || editor.isDestroyed) {
327
+ return;
328
+ }
329
+ const existingPlugin = editor.state.plugins.find(
330
+ (plugin) => plugin.spec.key === pluginKey
331
+ );
332
+ if (existingPlugin) {
333
+ editor.unregisterPlugin(pluginKey);
334
+ }
335
+ const suggestion = (0, import_suggestion.Suggestion)({
336
+ pluginKey: pluginKey instanceof import_state.PluginKey ? pluginKey : new import_state.PluginKey(pluginKey),
337
+ editor,
338
+ command({ editor: editor2, range, props }) {
339
+ var _a, _b;
340
+ if (!range) {
341
+ return;
342
+ }
343
+ const { view, state } = editor2;
344
+ const { selection } = state;
345
+ const isMention = editor2.extensionManager.extensions.some(
346
+ (extension) => {
347
+ var _a2, _b2;
348
+ const name = extension.name;
349
+ return name === "mention" && ((_b2 = (_a2 = extension.options) == null ? void 0 : _a2.suggestion) == null ? void 0 : _b2.char) === internalSuggestionPropsRef.current.char;
350
+ }
351
+ );
352
+ if (!isMention) {
353
+ const cursorPosition = selection.$from.pos;
354
+ const previousNode = (_a = selection.$head) == null ? void 0 : _a.nodeBefore;
355
+ const startPosition = previousNode ? calculateStartPosition(
356
+ cursorPosition,
357
+ previousNode,
358
+ internalSuggestionPropsRef.current.char
359
+ ) : selection.$from.start();
360
+ const transaction = state.tr.deleteRange(
361
+ startPosition,
362
+ cursorPosition
363
+ );
364
+ view.dispatch(transaction);
365
+ }
366
+ const nodeAfter = view.state.selection.$to.nodeAfter;
367
+ const overrideSpace = (_b = nodeAfter == null ? void 0 : nodeAfter.text) == null ? void 0 : _b.startsWith(" ");
368
+ const rangeToUse = { ...range };
369
+ if (overrideSpace) {
370
+ rangeToUse.to += 1;
371
+ }
372
+ props.onSelect({ editor: editor2, range: rangeToUse, context: props.context });
373
+ },
374
+ render: () => {
375
+ return {
376
+ onStart: (props) => {
377
+ var _a, _b;
378
+ setInternalCommand(() => props.command);
379
+ setInternalItems(props.items);
380
+ setInternalQuery(props.query);
381
+ setInternalRange(props.range);
382
+ setInternalClientRect((_b = (_a = props.clientRect) == null ? void 0 : _a.call(props)) != null ? _b : null);
383
+ setShow(true);
384
+ },
385
+ onUpdate: (props) => {
386
+ var _a, _b;
387
+ setInternalCommand(() => props.command);
388
+ setInternalItems(props.items);
389
+ setInternalQuery(props.query);
390
+ setInternalRange(props.range);
391
+ setInternalClientRect((_b = (_a = props.clientRect) == null ? void 0 : _a.call(props)) != null ? _b : null);
392
+ },
393
+ onKeyDown: (props) => {
394
+ if (props.event.key === "Escape") {
395
+ closePopup();
396
+ return true;
397
+ }
398
+ return false;
399
+ },
400
+ onExit: () => {
401
+ setInternalCommand(null);
402
+ setInternalItems([]);
403
+ setInternalQuery("");
404
+ setInternalRange(null);
405
+ setInternalClientRect(null);
406
+ setShow(false);
407
+ }
408
+ };
409
+ },
410
+ ...internalSuggestionPropsRef.current
411
+ });
412
+ editor.registerPlugin(suggestion);
413
+ return () => {
414
+ if (!editor.isDestroyed) {
415
+ editor.unregisterPlugin(pluginKey);
416
+ }
417
+ };
418
+ }, [editor, pluginKey, closePopup]);
419
+ const onSelect = React3.useCallback(
420
+ (item) => {
421
+ closePopup();
422
+ if (internalCommand) {
423
+ internalCommand(item);
424
+ }
425
+ },
426
+ [closePopup, internalCommand]
427
+ );
428
+ const { selectedIndex } = useMenuNavigation({
429
+ editor,
430
+ query: internalQuery,
431
+ items: internalItems,
432
+ onSelect
433
+ });
434
+ if (!isMounted || !show || !editor) {
435
+ return null;
436
+ }
437
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.FloatingPortal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
438
+ "div",
439
+ {
440
+ ref,
441
+ style,
442
+ ...getFloatingProps(),
443
+ "data-selector": selector,
444
+ className: "tiptap-suggestion-menu",
445
+ role: "listbox",
446
+ "aria-label": "Suggestions",
447
+ onPointerDown: (e) => e.preventDefault(),
448
+ children: children({
449
+ items: internalItems,
450
+ selectedIndex,
451
+ onSelect
452
+ })
453
+ }
454
+ ) });
455
+ };
456
+
457
+ // src/extensions/variable/variable-suggestion.tsx
458
+ var import_jsx_runtime2 = require("react/jsx-runtime");
459
+ var VariableSuggestionPluginKey = new import_state2.PluginKey("variableSuggestion");
460
+ function VariableSuggestion({
461
+ editor,
462
+ variables
463
+ }) {
464
+ const getItems = React4.useCallback(
465
+ ({ query }) => {
466
+ const normalizedQuery = query.toLowerCase().trim();
467
+ const items = [];
468
+ const matchingVariables = variables.filter((variable) => {
469
+ var _a, _b;
470
+ if (!normalizedQuery) return true;
471
+ return variable.name.toLowerCase().includes(normalizedQuery) || variable.label.toLowerCase().includes(normalizedQuery) || ((_a = variable.category) == null ? void 0 : _a.toLowerCase().includes(normalizedQuery)) || ((_b = variable.description) == null ? void 0 : _b.toLowerCase().includes(normalizedQuery));
472
+ });
473
+ for (const variable of matchingVariables) {
474
+ items.push({
475
+ title: variable.label,
476
+ subtext: variable.name,
477
+ group: variable.category,
478
+ keywords: [variable.name, variable.label, variable.category].filter(
479
+ Boolean
480
+ ),
481
+ onSelect: ({ editor: editor2 }) => {
482
+ const attrs = {
483
+ name: variable.name,
484
+ fallback: variable.fallback,
485
+ category: variable.category
486
+ };
487
+ editor2.chain().focus().insertVariable(attrs).run();
488
+ }
489
+ });
490
+ }
491
+ if (normalizedQuery && !variables.some((v) => v.name.toLowerCase() === normalizedQuery)) {
492
+ items.push({
493
+ title: `Neue Variable "${query}"`,
494
+ subtext: query,
495
+ group: "Neu",
496
+ onSelect: ({ editor: editor2 }) => {
497
+ const attrs = {
498
+ name: query
499
+ };
500
+ editor2.chain().focus().insertVariable(attrs).run();
501
+ }
502
+ });
503
+ }
504
+ return items;
505
+ },
506
+ [variables]
507
+ );
508
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
509
+ SuggestionMenu,
510
+ {
511
+ editor,
512
+ pluginKey: VariableSuggestionPluginKey,
513
+ char: "{{",
514
+ items: getItems,
515
+ allowSpaces: false,
516
+ selector: "tiptap-variable-suggestion-menu",
517
+ children: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VariableList, { ...props })
518
+ }
519
+ );
520
+ }
521
+ var VariableItem = ({
522
+ item,
523
+ isSelected,
524
+ onSelect
525
+ }) => {
526
+ const itemRef = React4.useRef(null);
527
+ React4.useEffect(() => {
528
+ const selector = document.querySelector(
529
+ '[data-selector="tiptap-variable-suggestion-menu"]'
530
+ );
531
+ if (!itemRef.current || !isSelected || !selector) return;
532
+ const overflow = (0, import_editor_utils.getElementOverflowPosition)(itemRef.current, selector);
533
+ if (overflow === "top") {
534
+ itemRef.current.scrollIntoView(true);
535
+ } else if (overflow === "bottom") {
536
+ itemRef.current.scrollIntoView(false);
537
+ }
538
+ }, [isSelected]);
539
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
540
+ "button",
541
+ {
542
+ ref: itemRef,
543
+ type: "button",
544
+ "data-active-state": isSelected ? "on" : "off",
545
+ onClick: onSelect,
546
+ className: "w-full flex items-center justify-between gap-3 px-2 py-1.5 text-sm rounded-sm outline-none hover:bg-muted data-[active-state=on]:bg-muted",
547
+ children: [
548
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-medium truncate", children: item.title }),
549
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { className: "text-xs text-muted-foreground font-mono shrink-0", children: item.subtext })
550
+ ]
551
+ }
552
+ );
553
+ };
554
+ var VariableList = ({
555
+ items,
556
+ selectedIndex,
557
+ onSelect
558
+ }) => {
559
+ const styles = (0, import_theme.slashDropdownMenu)();
560
+ const renderedItems = React4.useMemo(() => {
561
+ const rendered = [];
562
+ const groups = {};
563
+ items.forEach((item, index) => {
564
+ const groupLabel = item.group || "";
565
+ if (!groups[groupLabel]) {
566
+ groups[groupLabel] = { items: [], indices: [] };
567
+ }
568
+ groups[groupLabel].items.push(item);
569
+ groups[groupLabel].indices.push(index);
570
+ });
571
+ Object.entries(groups).forEach(([groupLabel, groupData], groupIndex) => {
572
+ const groupItems = groupData.items.map((item, itemIndex) => {
573
+ const originalIndex = groupData.indices[itemIndex];
574
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
575
+ VariableItem,
576
+ {
577
+ item,
578
+ isSelected: originalIndex === selectedIndex,
579
+ onSelect: () => onSelect(item)
580
+ },
581
+ `item-${originalIndex}-${item.title}`
582
+ );
583
+ });
584
+ if (groupLabel) {
585
+ rendered.push(
586
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "py-1", children: [
587
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "px-2 py-1 text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: groupLabel }),
588
+ groupItems
589
+ ] }, `group-${groupIndex}-${groupLabel}`)
590
+ );
591
+ } else {
592
+ rendered.push(...groupItems);
593
+ }
594
+ });
595
+ return rendered;
596
+ }, [items, selectedIndex, onSelect]);
597
+ if (!renderedItems.length) {
598
+ return null;
599
+ }
600
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
601
+ "div",
602
+ {
603
+ className: styles.card(),
604
+ style: {
605
+ maxHeight: "var(--suggestion-menu-max-height)",
606
+ minWidth: "240px"
607
+ },
608
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: styles.body(), children: renderedItems })
609
+ }
610
+ );
611
+ };
612
+ // Annotate the CommonJS export names for ESM import in node:
613
+ 0 && (module.exports = {
614
+ VariableSuggestion
615
+ });
@@ -0,0 +1,14 @@
1
+ "use client";
2
+ "use client";
3
+ import {
4
+ VariableSuggestion
5
+ } from "../../chunk-H7MS2UMO.mjs";
6
+ import "../../chunk-HLLA2HRV.mjs";
7
+ import "../../chunk-L5RDMV3H.mjs";
8
+ import "../../chunk-CNVACBGT.mjs";
9
+ import "../../chunk-P55PLOHR.mjs";
10
+ import "../../chunk-KK4K43WM.mjs";
11
+ import "../../chunk-42HKGCOO.mjs";
12
+ export {
13
+ VariableSuggestion
14
+ };
@@ -0,0 +1,13 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { NodeViewProps } from '@tiptap/react';
3
+
4
+ /**
5
+ * Variable NodeView Component
6
+ *
7
+ * Renders a template variable as an inline chip.
8
+ * Shows resolved value if available, otherwise shows variable name.
9
+ * In read-only mode with a resolved value, renders as plain text.
10
+ */
11
+ declare function VariableNodeView({ node, editor }: NodeViewProps): react_jsx_runtime.JSX.Element;
12
+
13
+ export { VariableNodeView, VariableNodeView as default };