@powerduck/schema-designer 0.1.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 (102) hide show
  1. package/README.md +217 -0
  2. package/dist/InlineSchemaEditor-BssSx3SI.d.ts +6 -0
  3. package/dist/InlineSchemaEditor-Bua-lDhE.d.cts +6 -0
  4. package/dist/chunk-2XUQB6BQ.js +1896 -0
  5. package/dist/chunk-4H3LXOL4.cjs +1900 -0
  6. package/dist/chunk-7LG4MVMD.cjs +2 -0
  7. package/dist/chunk-BXFABOMJ.js +1475 -0
  8. package/dist/chunk-FTALJIVZ.js +1 -0
  9. package/dist/chunk-GTZH37JS.cjs +1120 -0
  10. package/dist/chunk-GWHDTTQK.js +1437 -0
  11. package/dist/chunk-GXXD6ITE.cjs +1477 -0
  12. package/dist/chunk-HI5FBHCO.js +1057 -0
  13. package/dist/chunk-KX3LFFOV.js +88 -0
  14. package/dist/chunk-MADPN2PJ.js +185 -0
  15. package/dist/chunk-NOUORNCI.cjs +1452 -0
  16. package/dist/chunk-O6PTRJQ5.js +1195 -0
  17. package/dist/chunk-OBHJC3I2.cjs +2 -0
  18. package/dist/chunk-OOSNM45O.cjs +264 -0
  19. package/dist/chunk-P5B3HSPY.js +244 -0
  20. package/dist/chunk-PBE36BVA.js +1 -0
  21. package/dist/chunk-PVEYKCM6.js +1 -0
  22. package/dist/chunk-QZADGAP3.cjs +1218 -0
  23. package/dist/chunk-S7GNTGEE.js +1 -0
  24. package/dist/chunk-SMBDRWV2.cjs +2 -0
  25. package/dist/chunk-THYDOHOP.cjs +2 -0
  26. package/dist/chunk-WRHWOMZQ.cjs +190 -0
  27. package/dist/chunk-YNKP5JJA.cjs +32 -0
  28. package/dist/chunk-YWDKCX6N.cjs +91 -0
  29. package/dist/chunk-ZHNYNFGL.js +28 -0
  30. package/dist/compat/inline.cjs +137 -0
  31. package/dist/compat/inline.css +275 -0
  32. package/dist/compat/inline.d.cts +6 -0
  33. package/dist/compat/inline.d.ts +6 -0
  34. package/dist/compat/inline.js +4 -0
  35. package/dist/compat/parameters.cjs +90 -0
  36. package/dist/compat/parameters.css +997 -0
  37. package/dist/compat/parameters.d.cts +70 -0
  38. package/dist/compat/parameters.d.ts +70 -0
  39. package/dist/compat/parameters.js +9 -0
  40. package/dist/compat/tree.cjs +142 -0
  41. package/dist/compat/tree.css +1069 -0
  42. package/dist/compat/tree.d.cts +4 -0
  43. package/dist/compat/tree.d.ts +4 -0
  44. package/dist/compat/tree.js +5 -0
  45. package/dist/compat/variable-editor.cjs +82 -0
  46. package/dist/compat/variable-editor.d.cts +68 -0
  47. package/dist/compat/variable-editor.d.ts +68 -0
  48. package/dist/compat/variable-editor.js +1 -0
  49. package/dist/core/index.cjs +197 -0
  50. package/dist/core/index.d.cts +21 -0
  51. package/dist/core/index.d.ts +21 -0
  52. package/dist/core/index.js +4 -0
  53. package/dist/index.cjs +35 -0
  54. package/dist/index.css +1791 -0
  55. package/dist/index.d.cts +9 -0
  56. package/dist/index.d.ts +9 -0
  57. package/dist/index.js +10 -0
  58. package/dist/parameterValue-2pL9n-3q.d.ts +8 -0
  59. package/dist/parameterValue-Cky9Lnd3.d.cts +8 -0
  60. package/dist/react/index.cjs +35 -0
  61. package/dist/react/index.css +1791 -0
  62. package/dist/react/index.d.cts +9 -0
  63. package/dist/react/index.d.ts +9 -0
  64. package/dist/react/index.js +10 -0
  65. package/dist/react/inlineSchemaEditor/index.cjs +97 -0
  66. package/dist/react/inlineSchemaEditor/index.css +275 -0
  67. package/dist/react/inlineSchemaEditor/index.d.cts +6 -0
  68. package/dist/react/inlineSchemaEditor/index.d.ts +6 -0
  69. package/dist/react/inlineSchemaEditor/index.js +4 -0
  70. package/dist/react/parametersTable/index.cjs +22 -0
  71. package/dist/react/parametersTable/index.css +997 -0
  72. package/dist/react/parametersTable/index.d.cts +7 -0
  73. package/dist/react/parametersTable/index.d.ts +7 -0
  74. package/dist/react/parametersTable/index.js +9 -0
  75. package/dist/react/parametersTable/libs/generateValue.cjs +558 -0
  76. package/dist/react/parametersTable/libs/generateValue.d.cts +17 -0
  77. package/dist/react/parametersTable/libs/generateValue.d.ts +17 -0
  78. package/dist/react/parametersTable/libs/generateValue.js +551 -0
  79. package/dist/react/schemaTreeEditor/index.cjs +18 -0
  80. package/dist/react/schemaTreeEditor/index.css +1069 -0
  81. package/dist/react/schemaTreeEditor/index.d.cts +7 -0
  82. package/dist/react/schemaTreeEditor/index.d.ts +7 -0
  83. package/dist/react/schemaTreeEditor/index.js +5 -0
  84. package/dist/react/variableTextEditor/index.cjs +11 -0
  85. package/dist/react/variableTextEditor/index.css +306 -0
  86. package/dist/react/variableTextEditor/index.d.cts +6 -0
  87. package/dist/react/variableTextEditor/index.d.ts +6 -0
  88. package/dist/react/variableTextEditor/index.js +2 -0
  89. package/dist/schema-CKAPsYeW.d.cts +41 -0
  90. package/dist/schema-o7OJRaJ8.d.ts +41 -0
  91. package/dist/styles.css +1791 -0
  92. package/dist/tree-D06Xa6_N.d.ts +95 -0
  93. package/dist/tree-Dq1Rk-CF.d.cts +95 -0
  94. package/dist/types-B27Dw9do.d.cts +163 -0
  95. package/dist/types-B27Dw9do.d.ts +163 -0
  96. package/dist/types-Bk9rzr6l.d.cts +48 -0
  97. package/dist/types-D2jEaM9Z.d.cts +145 -0
  98. package/dist/types-D2jEaM9Z.d.ts +145 -0
  99. package/dist/types-xAihO_KW.d.ts +48 -0
  100. package/dist/variableTextEditor.types-ClzvARvK.d.cts +49 -0
  101. package/dist/variableTextEditor.types-ClzvARvK.d.ts +49 -0
  102. package/package.json +130 -0
@@ -0,0 +1,1475 @@
1
+ import { normalizeMaximumLength, sanitizePlainText, findVariableTokens, tokenAtPosition, escapeCssAttrValue, truncateInsertion, createVariableCompletionSource, createTokenDecorations } from './chunk-P5B3HSPY.js';
2
+ import { memo, useRef, useState, useLayoutEffect, useMemo, useCallback, useEffect } from 'react';
3
+ import { Box } from '@chakra-ui/react';
4
+ import { completionKeymap, acceptCompletion, autocompletion } from '@codemirror/autocomplete';
5
+ import { history, historyKeymap, defaultKeymap } from '@codemirror/commands';
6
+ import { Annotation, Compartment, EditorState } from '@codemirror/state';
7
+ import { EditorView, drawSelection, placeholder, keymap, ViewPlugin, Decoration } from '@codemirror/view';
8
+ import { jsxs, jsx } from 'react/jsx-runtime';
9
+
10
+ // src/react/variableTextEditor/VariableTextEditor.module.css
11
+ var VariableTextEditor_default = {
12
+ root: "VariableTextEditor_root",
13
+ variableToken: "VariableTextEditor_variableToken",
14
+ variableToken_unknown: "VariableTextEditor_variableToken_unknown",
15
+ variableToken_environment: "VariableTextEditor_variableToken_environment",
16
+ variableToken_secret: "VariableTextEditor_variableToken_secret",
17
+ variableToken_request: "VariableTextEditor_variableToken_request",
18
+ variableToken_dynamic: "VariableTextEditor_variableToken_dynamic",
19
+ hoverPopover: "VariableTextEditor_hoverPopover",
20
+ hoverPopoverArrow: "VariableTextEditor_hoverPopoverArrow",
21
+ popoverHeader: "VariableTextEditor_popoverHeader",
22
+ popoverType: "VariableTextEditor_popoverType",
23
+ popoverValue: "VariableTextEditor_popoverValue",
24
+ popoverDesc: "VariableTextEditor_popoverDesc"
25
+ };
26
+ var EMPTY_VARIABLES = Object.freeze([]);
27
+ var TOKEN_SELECTOR = `.${VariableTextEditor_default.variableToken}`;
28
+ var HOVER_OPEN_DELAY = 260;
29
+ var HOVER_CLOSE_DELAY = 50;
30
+ var POPOVER_GAP = 14;
31
+ var VIEWPORT_MARGIN = 12;
32
+ var POPOVER_MAX_WIDTH = 360;
33
+ var POPOVER_FALLBACK_HEIGHT = 200;
34
+ var MAX_PENDING_CONTROLLED_VALUES = 16;
35
+ var ExternalValueUpdate = Annotation.define();
36
+ var VariablesSnapshotUpdate = Annotation.define();
37
+ function variablesEqual(a, b) {
38
+ if (a === b) return true;
39
+ if (a.length !== b.length) return false;
40
+ for (let i = 0; i < a.length; i += 1) {
41
+ const left = a[i];
42
+ const right = b[i];
43
+ if (left.name !== right.name || left.value !== right.value || left.description !== right.description || left.type !== right.type || left.color !== right.color || left.backgroundColor !== right.backgroundColor) {
44
+ return false;
45
+ }
46
+ }
47
+ return true;
48
+ }
49
+ function mergeRanges(ranges) {
50
+ if (ranges.length <= 1) return ranges.slice();
51
+ const sorted = ranges.filter((range) => range.to > range.from).slice().sort((a, b) => {
52
+ if (a.from !== b.from) return a.from - b.from;
53
+ return a.to - b.to;
54
+ });
55
+ if (sorted.length <= 1) return sorted;
56
+ const result = [];
57
+ let current = {
58
+ from: sorted[0].from,
59
+ to: sorted[0].to
60
+ };
61
+ for (let i = 1; i < sorted.length; i += 1) {
62
+ const next = sorted[i];
63
+ if (next.from <= current.to) {
64
+ current.to = Math.max(current.to, next.to);
65
+ continue;
66
+ }
67
+ result.push(current);
68
+ current = {
69
+ from: next.from,
70
+ to: next.to
71
+ };
72
+ }
73
+ result.push(current);
74
+ return result;
75
+ }
76
+ function createInputInvariantFilter(maxLengthRef, allowLineBreaksRef, isComposingRef) {
77
+ return EditorState.transactionFilter.of((transaction) => {
78
+ if (!transaction.docChanged) {
79
+ return transaction;
80
+ }
81
+ if (isComposingRef.current) {
82
+ return transaction;
83
+ }
84
+ const newDoc = transaction.newDoc;
85
+ const invalidRanges = [];
86
+ if (!allowLineBreaksRef.current) {
87
+ const text = newDoc.toString();
88
+ const newlineRegex = /[\r\n\u2028\u2029]+/g;
89
+ let match;
90
+ while ((match = newlineRegex.exec(text)) !== null) {
91
+ const from = match.index;
92
+ const to = from + match[0].length;
93
+ invalidRanges.push({ from, to });
94
+ }
95
+ }
96
+ const limit = maxLengthRef.current;
97
+ if (Number.isFinite(limit) && newDoc.length > limit) {
98
+ invalidRanges.push({
99
+ from: Math.max(0, Math.floor(limit)),
100
+ to: newDoc.length
101
+ });
102
+ }
103
+ if (invalidRanges.length === 0) {
104
+ return transaction;
105
+ }
106
+ const merged = mergeRanges(invalidRanges);
107
+ return [
108
+ transaction,
109
+ {
110
+ changes: merged.map(({ from, to }) => ({
111
+ from,
112
+ to,
113
+ insert: ""
114
+ })),
115
+ sequential: true
116
+ }
117
+ ];
118
+ });
119
+ }
120
+ var VariableDecorationPlugin = class {
121
+ constructor(view, getSnapshot, tokenIndexRef, classes) {
122
+ this.getSnapshot = getSnapshot;
123
+ this.tokenIndexRef = tokenIndexRef;
124
+ this.classes = classes;
125
+ this.rebuild(view.state);
126
+ }
127
+ getSnapshot;
128
+ tokenIndexRef;
129
+ classes;
130
+ decorations = Decoration.none;
131
+ rebuild(state) {
132
+ const snapshot = this.getSnapshot();
133
+ const doc = state.doc.toString();
134
+ const tokens = findVariableTokens(doc, snapshot.variables);
135
+ this.tokenIndexRef.current = {
136
+ doc,
137
+ variablesVersion: snapshot.version,
138
+ tokens
139
+ };
140
+ this.decorations = createTokenDecorations(
141
+ state,
142
+ snapshot.variables,
143
+ this.classes,
144
+ tokens
145
+ );
146
+ }
147
+ update(update) {
148
+ if (update.docChanged || update.transactions.some(
149
+ (transaction) => transaction.annotation(VariablesSnapshotUpdate) !== void 0
150
+ ))
151
+ this.rebuild(update.state);
152
+ }
153
+ };
154
+ function createVariableDecorationExtension(snapshotRef, tokenIndexRef) {
155
+ const getSnapshot = () => snapshotRef.current;
156
+ const plugin = ViewPlugin.fromClass(
157
+ class extends VariableDecorationPlugin {
158
+ constructor(view) {
159
+ super(view, getSnapshot, tokenIndexRef, VariableTextEditor_default);
160
+ }
161
+ },
162
+ {
163
+ decorations: (plugin2) => plugin2.decorations
164
+ }
165
+ );
166
+ return plugin;
167
+ }
168
+ function createVariableCompletionExtension(variables) {
169
+ return autocompletion({
170
+ override: [createVariableCompletionSource(variables)],
171
+ activateOnTyping: true,
172
+ defaultKeymap: false
173
+ });
174
+ }
175
+ var VariableTextEditor = memo(
176
+ (props) => {
177
+ const {
178
+ value,
179
+ variables: variablesProp,
180
+ disabled = false,
181
+ readOnly = false,
182
+ placeholder: placeholder$1 = "",
183
+ ariaLabel = "Variable text editor",
184
+ className,
185
+ autoFocus = false,
186
+ submitOnEnter = false,
187
+ minHeight = 32,
188
+ expansionMode = "inline",
189
+ safePadding = 8,
190
+ maxFocusedHeight = 320,
191
+ maxLength,
192
+ allowLineBreaks = false,
193
+ onChange,
194
+ onSubmit,
195
+ onFocus,
196
+ onBlur
197
+ } = props;
198
+ const variables = variablesProp ?? EMPTY_VARIABLES;
199
+ const safeMaxLength = normalizeMaximumLength(maxLength);
200
+ const normalizedValue = sanitizePlainText(
201
+ value,
202
+ safeMaxLength,
203
+ allowLineBreaks
204
+ );
205
+ const safeMinHeight = Number.isFinite(minHeight) ? Math.max(32, Math.round(minHeight)) : 32;
206
+ const safeMaxHeight = Number.isFinite(maxFocusedHeight) ? Math.max(safeMinHeight, Math.round(maxFocusedHeight)) : Math.max(safeMinHeight, 320);
207
+ const hostRef = useRef(null);
208
+ const tooltipRef = useRef(null);
209
+ const viewRef = useRef(null);
210
+ const destroyedRef = useRef(false);
211
+ const isComposingRef = useRef(false);
212
+ const pendingCompositionSyncRef = useRef(false);
213
+ const initialValueRef = useRef(normalizedValue);
214
+ const editorValueRef = useRef(normalizedValue);
215
+ const lastAcknowledgedPropValueRef = useRef(normalizedValue);
216
+ const pendingEmittedValuesRef = useRef([]);
217
+ const [hover, setHover] = useState(null);
218
+ const hoverRef = useRef(null);
219
+ const [focused, setFocused] = useState(false);
220
+ const focusedRef = useRef(false);
221
+ const [focusedHeight, setFocusedHeight] = useState(safeMinHeight);
222
+ const focusedHeightRef = useRef(safeMinHeight);
223
+ const [wrapped, setWrapped] = useState(false);
224
+ const wrappedRef = useRef(false);
225
+ const anchorRef = useRef(null);
226
+ const [overlayStyle, setOverlayStyle] = useState();
227
+ useLayoutEffect(() => {
228
+ if (!focused || expansionMode !== "overlay") {
229
+ setOverlayStyle(void 0);
230
+ return;
231
+ }
232
+ let frame = 0;
233
+ const position = () => {
234
+ frame = 0;
235
+ const anchor = anchorRef.current;
236
+ if (!anchor) return;
237
+ const rect = anchor.getBoundingClientRect();
238
+ if (rect.bottom <= 0 || rect.top >= window.innerHeight) {
239
+ viewRef.current?.contentDOM.blur();
240
+ return;
241
+ }
242
+ const height = Math.min(focusedHeight, window.innerHeight - 16);
243
+ const top = Math.max(
244
+ 8,
245
+ Math.min(rect.top, window.innerHeight - height - 8)
246
+ );
247
+ setOverlayStyle({
248
+ position: "fixed",
249
+ top,
250
+ left: Math.max(8, rect.left),
251
+ width: Math.min(
252
+ rect.width,
253
+ window.innerWidth - Math.max(8, rect.left) - 8
254
+ ),
255
+ maxHeight: Math.max(safeMinHeight, window.innerHeight - top - 8),
256
+ zIndex: 1100
257
+ });
258
+ };
259
+ const schedule = () => {
260
+ if (!frame) frame = requestAnimationFrame(position);
261
+ };
262
+ position();
263
+ window.addEventListener("scroll", schedule, true);
264
+ window.addEventListener("resize", schedule);
265
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(schedule);
266
+ if (anchorRef.current) observer?.observe(anchorRef.current);
267
+ return () => {
268
+ cancelAnimationFrame(frame);
269
+ observer?.disconnect();
270
+ window.removeEventListener("scroll", schedule, true);
271
+ window.removeEventListener("resize", schedule);
272
+ };
273
+ }, [focused, expansionMode, focusedHeight, safeMinHeight]);
274
+ const [popoverPosition, setPopoverPosition] = useState(null);
275
+ const activeTokenKeyRef = useRef(null);
276
+ const lastPointerTokenKeyRef = useRef(null);
277
+ const openTimerRef = useRef(null);
278
+ const closeTimerRef = useRef(null);
279
+ const frameRef = useRef(null);
280
+ const pointerRafRef = useRef(null);
281
+ const repositionRafRef = useRef(null);
282
+ const resizeObserverRef = useRef(null);
283
+ const tooltipResizeObserverRef = useRef(null);
284
+ const pointerInEditorRef = useRef(false);
285
+ const pointerInPopoverRef = useRef(false);
286
+ const markPopoverPointerDownUntilRef = useRef(0);
287
+ const variablesSnapshotRef = useRef({
288
+ variables,
289
+ version: 1
290
+ });
291
+ const disabledRef = useRef(disabled);
292
+ const readOnlyRef = useRef(readOnly);
293
+ const submitOnEnterRef = useRef(submitOnEnter);
294
+ const allowLineBreaksRef = useRef(allowLineBreaks);
295
+ const maxLengthRef = useRef(safeMaxLength);
296
+ const onChangeRef = useRef(onChange);
297
+ const onSubmitRef = useRef(onSubmit);
298
+ const onFocusRef = useRef(onFocus);
299
+ const onBlurRef = useRef(onBlur);
300
+ const editableCompartmentRef = useRef(new Compartment());
301
+ const variableDecorationCompartmentRef = useRef(new Compartment());
302
+ const variableCompletionCompartmentRef = useRef(new Compartment());
303
+ const placeholderCompartmentRef = useRef(new Compartment());
304
+ const ariaCompartmentRef = useRef(new Compartment());
305
+ const wrappingCompartmentRef = useRef(new Compartment());
306
+ const tokenIndexRef = useRef({
307
+ doc: normalizedValue,
308
+ variablesVersion: variablesSnapshotRef.current.version,
309
+ tokens: []
310
+ });
311
+ const pendingPointerRef = useRef(null);
312
+ const rootStyle = useMemo(
313
+ () => ({
314
+ "--editor-min-height": `${safeMinHeight}px`,
315
+ "--editor-max-focused-height": `${safeMaxHeight}px`,
316
+ "--editor-padding-block": `${Number.isFinite(safePadding) ? Math.max(0, safePadding) : 8}px`,
317
+ "--editor-focused-height": `${focusedHeight}px`
318
+ }),
319
+ [focusedHeight, safeMaxHeight, safeMinHeight, safePadding]
320
+ );
321
+ const sameRect = useCallback(
322
+ (a, b, eps = 0.01) => {
323
+ return Math.abs(a.left - b.left) < eps && Math.abs(a.top - b.top) < eps && Math.abs(a.width - b.width) < eps && Math.abs(a.height - b.height) < eps;
324
+ },
325
+ []
326
+ );
327
+ useEffect(() => {
328
+ focusedHeightRef.current = focusedHeight;
329
+ }, [focusedHeight]);
330
+ useEffect(() => {
331
+ wrappedRef.current = wrapped;
332
+ }, [wrapped]);
333
+ const cancelOpen = useCallback(() => {
334
+ if (openTimerRef.current !== null) {
335
+ window.clearTimeout(openTimerRef.current);
336
+ openTimerRef.current = null;
337
+ }
338
+ }, []);
339
+ const cancelClose = useCallback(() => {
340
+ if (closeTimerRef.current !== null) {
341
+ window.clearTimeout(closeTimerRef.current);
342
+ closeTimerRef.current = null;
343
+ }
344
+ }, []);
345
+ const cancelFrame = useCallback(() => {
346
+ if (frameRef.current !== null) {
347
+ window.cancelAnimationFrame(frameRef.current);
348
+ frameRef.current = null;
349
+ }
350
+ }, []);
351
+ const cancelRepositionFrame = useCallback(() => {
352
+ if (repositionRafRef.current !== null) {
353
+ window.cancelAnimationFrame(repositionRafRef.current);
354
+ repositionRafRef.current = null;
355
+ }
356
+ }, []);
357
+ const commitHover = useCallback(
358
+ (next) => {
359
+ if (destroyedRef.current) {
360
+ return;
361
+ }
362
+ const current = hoverRef.current;
363
+ if (!current && !next) {
364
+ return;
365
+ }
366
+ if (current && next && current.tokenKey === next.tokenKey && sameRect(current.rect, next.rect)) {
367
+ return;
368
+ }
369
+ hoverRef.current = next;
370
+ activeTokenKeyRef.current = next?.tokenKey ?? null;
371
+ setHover(next);
372
+ if (!next) {
373
+ setPopoverPosition(null);
374
+ }
375
+ },
376
+ [sameRect]
377
+ );
378
+ const closeHover = useCallback(() => {
379
+ cancelOpen();
380
+ cancelClose();
381
+ cancelRepositionFrame();
382
+ commitHover(null);
383
+ }, [cancelClose, cancelOpen, cancelRepositionFrame, commitHover]);
384
+ const markPopoverPointerDown = useCallback(() => {
385
+ markPopoverPointerDownUntilRef.current = Date.now() + 150;
386
+ }, []);
387
+ const requestFinalClose = useCallback(() => {
388
+ cancelClose();
389
+ closeTimerRef.current = window.setTimeout(() => {
390
+ closeTimerRef.current = null;
391
+ if (destroyedRef.current) {
392
+ return;
393
+ }
394
+ if (Date.now() < markPopoverPointerDownUntilRef.current) {
395
+ return;
396
+ }
397
+ const shouldClose = !pointerInEditorRef.current && !pointerInPopoverRef.current && !focusedRef.current;
398
+ if (!shouldClose) {
399
+ return;
400
+ }
401
+ closeHover();
402
+ setWrapped(false);
403
+ setFocusedHeight(safeMinHeight);
404
+ }, HOVER_CLOSE_DELAY);
405
+ }, [cancelClose, closeHover, safeMinHeight]);
406
+ const scheduleClose = useCallback(() => {
407
+ cancelClose();
408
+ closeTimerRef.current = window.setTimeout(() => {
409
+ closeTimerRef.current = null;
410
+ if (destroyedRef.current) {
411
+ return;
412
+ }
413
+ if (Date.now() < markPopoverPointerDownUntilRef.current) {
414
+ return;
415
+ }
416
+ if (tooltipRef.current?.matches(":hover, :focus-within")) {
417
+ return;
418
+ }
419
+ commitHover(null);
420
+ }, HOVER_CLOSE_DELAY);
421
+ }, [cancelClose, commitHover]);
422
+ const getTokensCurrent = useCallback(
423
+ (view) => {
424
+ const doc = view.state.doc.toString();
425
+ const snapshot = variablesSnapshotRef.current;
426
+ const cached = tokenIndexRef.current;
427
+ if (cached.doc === doc && cached.variablesVersion === snapshot.version) {
428
+ return cached.tokens;
429
+ }
430
+ const tokens = findVariableTokens(doc, snapshot.variables);
431
+ tokenIndexRef.current = {
432
+ doc,
433
+ variablesVersion: snapshot.version,
434
+ tokens
435
+ };
436
+ return tokens;
437
+ },
438
+ []
439
+ );
440
+ const getTokenAtPointer = useCallback(
441
+ (snapshot) => {
442
+ const view = viewRef.current;
443
+ if (destroyedRef.current || !view) {
444
+ return null;
445
+ }
446
+ const tokens = getTokensCurrent(view);
447
+ if (!tokens.length) {
448
+ return null;
449
+ }
450
+ const pos = view.posAtCoords({
451
+ x: snapshot.clientX,
452
+ y: snapshot.clientY
453
+ });
454
+ if (pos == null) {
455
+ return null;
456
+ }
457
+ return tokenAtPosition(tokens, pos) ?? null;
458
+ },
459
+ [getTokensCurrent]
460
+ );
461
+ const fullLineTokenRectFromView = useCallback(
462
+ (view, from, to) => {
463
+ if (view.state.doc.length === 0) return null;
464
+ const host = view.dom;
465
+ const selector = `${TOKEN_SELECTOR}[data-variable-from="${escapeCssAttrValue(String(from))}"][data-variable-to="${escapeCssAttrValue(String(to))}"]`;
466
+ const element = host.querySelector(selector);
467
+ if (element) {
468
+ const tokenRect = element.getBoundingClientRect();
469
+ const lineElement = element.closest(".cm-line");
470
+ const lineRect = lineElement?.getBoundingClientRect();
471
+ const editorRect = view.dom.getBoundingClientRect();
472
+ const top2 = Math.max(editorRect.top, lineRect?.top ?? tokenRect.top);
473
+ const bottom2 = Math.min(
474
+ editorRect.bottom,
475
+ lineRect?.bottom ?? tokenRect.bottom
476
+ );
477
+ const height2 = Math.max(tokenRect.height, bottom2 - top2);
478
+ const width2 = tokenRect.width;
479
+ if (width2 <= 0 || height2 <= 0) {
480
+ return null;
481
+ }
482
+ return new DOMRect(tokenRect.left, top2, width2, height2);
483
+ }
484
+ const startCoords = view.coordsAtPos(from);
485
+ const endCoords = view.coordsAtPos(to);
486
+ if (!startCoords || !endCoords) {
487
+ return null;
488
+ }
489
+ const left = startCoords.left;
490
+ const right = endCoords.right;
491
+ const top = Math.min(startCoords.top, endCoords.top);
492
+ const bottom = Math.max(startCoords.bottom, endCoords.bottom);
493
+ const width = right - left;
494
+ const height = bottom - top;
495
+ if (width <= 0 || height <= 0) {
496
+ return null;
497
+ }
498
+ return new DOMRect(left, top, width, height);
499
+ },
500
+ []
501
+ );
502
+ const buildHoverFromToken = useCallback(
503
+ (view, token) => {
504
+ if (destroyedRef.current) {
505
+ return null;
506
+ }
507
+ const rect = fullLineTokenRectFromView(view, token.from, token.to);
508
+ if (!rect || rect.width <= 0 || rect.height <= 0) {
509
+ return null;
510
+ }
511
+ const tokenKey = `${token.from}:${token.to}:${token.name}`;
512
+ return {
513
+ tokenKey,
514
+ rect,
515
+ from: token.from,
516
+ to: token.to,
517
+ name: token.name,
518
+ variable: token.variable
519
+ };
520
+ },
521
+ [fullLineTokenRectFromView]
522
+ );
523
+ const showToken = useCallback(
524
+ (token) => {
525
+ if (destroyedRef.current) {
526
+ return;
527
+ }
528
+ const view = viewRef.current;
529
+ if (!view || disabledRef.current) {
530
+ return;
531
+ }
532
+ const nextHover = buildHoverFromToken(view, token);
533
+ if (!nextHover) {
534
+ closeHover();
535
+ return;
536
+ }
537
+ if (activeTokenKeyRef.current === nextHover.tokenKey && hoverRef.current && hoverRef.current.tokenKey === nextHover.tokenKey && sameRect(hoverRef.current.rect, nextHover.rect)) {
538
+ return;
539
+ }
540
+ commitHover(nextHover);
541
+ },
542
+ [buildHoverFromToken, closeHover, commitHover, sameRect]
543
+ );
544
+ const scheduleOpen = useCallback(
545
+ (snapshot, token) => {
546
+ cancelClose();
547
+ cancelOpen();
548
+ openTimerRef.current = window.setTimeout(() => {
549
+ openTimerRef.current = null;
550
+ if (destroyedRef.current) {
551
+ return;
552
+ }
553
+ const view = viewRef.current;
554
+ if (!view || disabledRef.current) {
555
+ return;
556
+ }
557
+ const tokens = getTokensCurrent(view);
558
+ const latest = tokens.find(
559
+ (candidate) => candidate.from === token.from && candidate.to === token.to && candidate.token === token.token
560
+ );
561
+ if (!latest) {
562
+ return;
563
+ }
564
+ const latestToken = getTokenAtPointer(snapshot);
565
+ if (!latestToken || latestToken.from !== latest.from || latestToken.to !== latest.to) {
566
+ return;
567
+ }
568
+ showToken(latest);
569
+ }, HOVER_OPEN_DELAY);
570
+ },
571
+ [cancelClose, cancelOpen, getTokenAtPointer, getTokensCurrent, showToken]
572
+ );
573
+ const flushPointer = useCallback(() => {
574
+ pointerRafRef.current = null;
575
+ const snapshot = pendingPointerRef.current;
576
+ pendingPointerRef.current = null;
577
+ if (!snapshot || destroyedRef.current) {
578
+ return;
579
+ }
580
+ pointerInEditorRef.current = true;
581
+ const token = getTokenAtPointer(snapshot);
582
+ if (token) {
583
+ const tokenKey = `${token.from}:${token.to}:${token.name}`;
584
+ if (lastPointerTokenKeyRef.current !== tokenKey) {
585
+ lastPointerTokenKeyRef.current = tokenKey;
586
+ scheduleOpen(snapshot, token);
587
+ } else if (!hoverRef.current) {
588
+ scheduleOpen(snapshot, token);
589
+ }
590
+ return;
591
+ }
592
+ lastPointerTokenKeyRef.current = null;
593
+ if (hoverRef.current) {
594
+ requestFinalClose();
595
+ } else {
596
+ cancelOpen();
597
+ }
598
+ }, [cancelOpen, getTokenAtPointer, requestFinalClose, scheduleOpen]);
599
+ const schedulePointerFlush = useCallback(
600
+ (event) => {
601
+ pendingPointerRef.current = {
602
+ clientX: event.clientX,
603
+ clientY: event.clientY
604
+ };
605
+ if (pointerRafRef.current !== null) {
606
+ return;
607
+ }
608
+ pointerRafRef.current = window.requestAnimationFrame(flushPointer);
609
+ },
610
+ [flushPointer]
611
+ );
612
+ const handlePointerMove = useCallback(
613
+ (event) => {
614
+ schedulePointerFlush(event);
615
+ },
616
+ [schedulePointerFlush]
617
+ );
618
+ const handleHostPointerEnter = useCallback(() => {
619
+ pointerInEditorRef.current = true;
620
+ }, []);
621
+ const handleHostPointerLeave = useCallback(() => {
622
+ pointerInEditorRef.current = false;
623
+ cancelOpen();
624
+ requestFinalClose();
625
+ }, [cancelOpen, requestFinalClose]);
626
+ const handleTooltipPointerEnter = useCallback(() => {
627
+ pointerInPopoverRef.current = true;
628
+ cancelClose();
629
+ }, [cancelClose]);
630
+ const handleTooltipPointerLeave = useCallback(() => {
631
+ pointerInPopoverRef.current = false;
632
+ requestFinalClose();
633
+ }, [requestFinalClose]);
634
+ const measureFocusedHeight = useCallback(() => {
635
+ if (destroyedRef.current || !focusedRef.current) {
636
+ return;
637
+ }
638
+ const expectedView = viewRef.current;
639
+ if (!expectedView) {
640
+ return;
641
+ }
642
+ if (frameRef.current !== null) {
643
+ return;
644
+ }
645
+ frameRef.current = window.requestAnimationFrame(() => {
646
+ frameRef.current = null;
647
+ if (destroyedRef.current) {
648
+ return;
649
+ }
650
+ if (viewRef.current !== expectedView) {
651
+ return;
652
+ }
653
+ if (!focusedRef.current) {
654
+ return;
655
+ }
656
+ const content = expectedView.contentDOM;
657
+ const measuredRaw = Math.max(
658
+ safeMinHeight,
659
+ Math.ceil(content.scrollHeight)
660
+ );
661
+ const stableHeight = measuredRaw <= safeMinHeight + 2 ? safeMinHeight : measuredRaw;
662
+ const nextHeight = Math.min(stableHeight, safeMaxHeight);
663
+ const nextWrapped = stableHeight > safeMinHeight;
664
+ const currentHeight = focusedHeightRef.current;
665
+ const currentWrapped = wrappedRef.current;
666
+ if (nextHeight === currentHeight && nextWrapped === currentWrapped) {
667
+ return;
668
+ }
669
+ focusedHeightRef.current = nextHeight;
670
+ wrappedRef.current = nextWrapped;
671
+ setFocusedHeight(nextHeight);
672
+ setWrapped(nextWrapped);
673
+ });
674
+ }, [safeMaxHeight, safeMinHeight]);
675
+ const calculatePopoverPosition = useCallback(
676
+ (rect, width, height) => {
677
+ const safeWidth = Math.min(
678
+ POPOVER_MAX_WIDTH,
679
+ Math.max(1, window.innerWidth - VIEWPORT_MARGIN * 2)
680
+ );
681
+ const actualWidth = Math.min(
682
+ safeWidth,
683
+ Math.max(1, width || safeWidth)
684
+ );
685
+ const actualHeight = Math.max(1, height || POPOVER_FALLBACK_HEIGHT);
686
+ const tokenCenter = rect.left + rect.width / 2;
687
+ const left = Math.max(
688
+ VIEWPORT_MARGIN,
689
+ Math.min(
690
+ tokenCenter - actualWidth / 2,
691
+ window.innerWidth - actualWidth - VIEWPORT_MARGIN
692
+ )
693
+ );
694
+ const canPlaceBelow = rect.bottom + POPOVER_GAP + actualHeight <= window.innerHeight - VIEWPORT_MARGIN;
695
+ const canPlaceAbove = rect.top - POPOVER_GAP - actualHeight >= VIEWPORT_MARGIN;
696
+ const showAbove = !canPlaceBelow && canPlaceAbove;
697
+ const top = showAbove ? Math.max(VIEWPORT_MARGIN, rect.top - actualHeight - POPOVER_GAP) : Math.min(
698
+ Math.max(VIEWPORT_MARGIN, rect.bottom + POPOVER_GAP),
699
+ Math.max(
700
+ VIEWPORT_MARGIN,
701
+ window.innerHeight - actualHeight - VIEWPORT_MARGIN
702
+ )
703
+ );
704
+ return {
705
+ left,
706
+ top,
707
+ placement: showAbove ? "top" : "bottom",
708
+ arrowLeft: Math.max(
709
+ 12,
710
+ Math.min(tokenCenter - left, actualWidth - 12)
711
+ )
712
+ };
713
+ },
714
+ []
715
+ );
716
+ const repositionPopover = useCallback(() => {
717
+ if (destroyedRef.current) {
718
+ return;
719
+ }
720
+ const currentHover = hoverRef.current;
721
+ if (!currentHover) {
722
+ return;
723
+ }
724
+ const tooltip = tooltipRef.current;
725
+ const rect = currentHover.rect;
726
+ const tooltipRect = tooltip?.getBoundingClientRect();
727
+ const width = tooltipRect?.width || Math.min(POPOVER_MAX_WIDTH, window.innerWidth - VIEWPORT_MARGIN * 2);
728
+ const height = tooltipRect?.height || POPOVER_FALLBACK_HEIGHT;
729
+ const next = calculatePopoverPosition(rect, width, height);
730
+ setPopoverPosition((previous) => {
731
+ if (previous && previous.left === next.left && previous.top === next.top && previous.placement === next.placement && previous.arrowLeft === next.arrowLeft) {
732
+ return previous;
733
+ }
734
+ return next;
735
+ });
736
+ }, [calculatePopoverPosition]);
737
+ const schedulePopoverReposition = useCallback(() => {
738
+ if (repositionRafRef.current !== null) {
739
+ return;
740
+ }
741
+ repositionRafRef.current = window.requestAnimationFrame(() => {
742
+ repositionRafRef.current = null;
743
+ repositionPopover();
744
+ });
745
+ }, [repositionPopover]);
746
+ const applyExternalValue = useCallback(
747
+ (view, nextValue) => {
748
+ if (destroyedRef.current || viewRef.current !== view) {
749
+ return false;
750
+ }
751
+ const currentDoc = view.state.doc.toString();
752
+ if (currentDoc === nextValue) {
753
+ editorValueRef.current = currentDoc;
754
+ return false;
755
+ }
756
+ closeHover();
757
+ view.dispatch({
758
+ changes: {
759
+ from: 0,
760
+ to: currentDoc.length,
761
+ insert: nextValue
762
+ },
763
+ annotations: ExternalValueUpdate.of(true)
764
+ });
765
+ editorValueRef.current = nextValue;
766
+ return true;
767
+ },
768
+ [closeHover]
769
+ );
770
+ useLayoutEffect(() => {
771
+ disabledRef.current = disabled;
772
+ readOnlyRef.current = readOnly;
773
+ submitOnEnterRef.current = submitOnEnter;
774
+ allowLineBreaksRef.current = allowLineBreaks;
775
+ maxLengthRef.current = safeMaxLength;
776
+ onChangeRef.current = onChange;
777
+ onSubmitRef.current = onSubmit;
778
+ onFocusRef.current = onFocus;
779
+ onBlurRef.current = onBlur;
780
+ }, [
781
+ disabled,
782
+ readOnly,
783
+ submitOnEnter,
784
+ allowLineBreaks,
785
+ safeMaxLength,
786
+ onChange,
787
+ onSubmit,
788
+ onFocus,
789
+ onBlur
790
+ ]);
791
+ const measurementRef = useRef(measureFocusedHeight);
792
+ const minimumHeightRef = useRef(safeMinHeight);
793
+ useLayoutEffect(() => {
794
+ measurementRef.current = measureFocusedHeight;
795
+ minimumHeightRef.current = safeMinHeight;
796
+ measureFocusedHeight();
797
+ }, [measureFocusedHeight, safeMinHeight]);
798
+ useLayoutEffect(() => {
799
+ const host = hostRef.current;
800
+ if (!host || viewRef.current) {
801
+ return;
802
+ }
803
+ destroyedRef.current = false;
804
+ const editable = !disabledRef.current && !readOnlyRef.current;
805
+ const view = new EditorView({
806
+ parent: host,
807
+ state: EditorState.create({
808
+ doc: initialValueRef.current,
809
+ extensions: [
810
+ history(),
811
+ drawSelection({
812
+ cursorBlinkRate: 1200
813
+ }),
814
+ EditorState.allowMultipleSelections.of(false),
815
+ editableCompartmentRef.current.of([
816
+ EditorState.readOnly.of(!editable),
817
+ EditorView.editable.of(editable)
818
+ ]),
819
+ /**
820
+ * P0:
821
+ *
822
+ * All document invariants are enforced at the transaction level.
823
+ */
824
+ createInputInvariantFilter(
825
+ maxLengthRef,
826
+ allowLineBreaksRef,
827
+ isComposingRef
828
+ ),
829
+ wrappingCompartmentRef.current.of([]),
830
+ EditorView.updateListener.of((update) => {
831
+ if (update.docChanged) {
832
+ closeHover();
833
+ const newLength = update.state.doc.length;
834
+ if (newLength === 0) {
835
+ hoverRef.current = null;
836
+ activeTokenKeyRef.current = null;
837
+ lastPointerTokenKeyRef.current = null;
838
+ setHover(null);
839
+ setPopoverPosition(null);
840
+ cancelRepositionFrame();
841
+ }
842
+ }
843
+ if (!update.docChanged) {
844
+ return;
845
+ }
846
+ const isExternal = update.transactions.some(
847
+ (transaction) => transaction.annotation(ExternalValueUpdate) === true
848
+ );
849
+ const rawValue = update.state.doc.toString();
850
+ editorValueRef.current = rawValue;
851
+ if (isComposingRef.current) {
852
+ pendingCompositionSyncRef.current = true;
853
+ return;
854
+ }
855
+ if (isExternal) {
856
+ return;
857
+ }
858
+ const nextValue = sanitizePlainText(
859
+ rawValue,
860
+ maxLengthRef.current,
861
+ allowLineBreaksRef.current
862
+ );
863
+ if (nextValue !== rawValue) {
864
+ return;
865
+ }
866
+ if (focusedRef.current) {
867
+ measurementRef.current();
868
+ }
869
+ pendingEmittedValuesRef.current.push(nextValue);
870
+ if (pendingEmittedValuesRef.current.length > MAX_PENDING_CONTROLLED_VALUES) {
871
+ pendingEmittedValuesRef.current = pendingEmittedValuesRef.current.slice(
872
+ -MAX_PENDING_CONTROLLED_VALUES
873
+ );
874
+ }
875
+ onChangeRef.current?.(nextValue);
876
+ }),
877
+ variableDecorationCompartmentRef.current.of(
878
+ createVariableDecorationExtension(
879
+ variablesSnapshotRef,
880
+ tokenIndexRef
881
+ )
882
+ ),
883
+ variableCompletionCompartmentRef.current.of(
884
+ createVariableCompletionExtension(
885
+ variablesSnapshotRef.current.variables
886
+ )
887
+ ),
888
+ placeholderCompartmentRef.current.of(
889
+ placeholder$1 ? placeholder(placeholder$1) : []
890
+ ),
891
+ ariaCompartmentRef.current.of(
892
+ EditorView.contentAttributes.of({
893
+ "aria-label": ariaLabel,
894
+ "aria-disabled": disabledRef.current ? "true" : "false",
895
+ "aria-readonly": readOnlyRef.current ? "true" : "false",
896
+ spellcheck: "false",
897
+ autocapitalize: "off",
898
+ autocomplete: "off"
899
+ })
900
+ ),
901
+ keymap.of([
902
+ ...completionKeymap,
903
+ ...historyKeymap,
904
+ ...defaultKeymap
905
+ ]),
906
+ EditorView.domEventHandlers({
907
+ compositionstart() {
908
+ isComposingRef.current = true;
909
+ return false;
910
+ },
911
+ compositionend() {
912
+ isComposingRef.current = false;
913
+ queueMicrotask(() => {
914
+ if (destroyedRef.current || viewRef.current !== view) {
915
+ return;
916
+ }
917
+ if (!pendingCompositionSyncRef.current) {
918
+ return;
919
+ }
920
+ pendingCompositionSyncRef.current = false;
921
+ const rawValue = view.state.doc.toString();
922
+ const normalized = sanitizePlainText(
923
+ rawValue,
924
+ maxLengthRef.current,
925
+ allowLineBreaksRef.current
926
+ );
927
+ if (normalized !== rawValue) {
928
+ view.dispatch({
929
+ changes: {
930
+ from: 0,
931
+ to: rawValue.length,
932
+ insert: normalized
933
+ },
934
+ annotations: ExternalValueUpdate.of(true)
935
+ });
936
+ editorValueRef.current = normalized;
937
+ pendingEmittedValuesRef.current.push(normalized);
938
+ onChangeRef.current?.(normalized);
939
+ return;
940
+ }
941
+ editorValueRef.current = rawValue;
942
+ pendingEmittedValuesRef.current.push(rawValue);
943
+ onChangeRef.current?.(rawValue);
944
+ });
945
+ return false;
946
+ },
947
+ keydown(event, currentView) {
948
+ if (event.isComposing || isComposingRef.current || disabledRef.current || readOnlyRef.current)
949
+ return false;
950
+ if (event.key !== "Enter") {
951
+ return false;
952
+ }
953
+ if (allowLineBreaksRef.current) {
954
+ return false;
955
+ }
956
+ if (acceptCompletion(currentView)) {
957
+ event.preventDefault();
958
+ event.stopPropagation();
959
+ return true;
960
+ }
961
+ event.preventDefault();
962
+ event.stopPropagation();
963
+ if (submitOnEnterRef.current) {
964
+ onSubmitRef.current?.(currentView.state.doc.toString());
965
+ }
966
+ return true;
967
+ },
968
+ beforeinput(event, currentView) {
969
+ if (disabledRef.current || readOnlyRef.current) {
970
+ event.preventDefault();
971
+ return true;
972
+ }
973
+ if (isComposingRef.current) {
974
+ return false;
975
+ }
976
+ if (!event.inputType.startsWith("insert")) {
977
+ return false;
978
+ }
979
+ const text = event.data;
980
+ if (!allowLineBreaksRef.current) {
981
+ if (event.inputType === "insertLineBreak" || event.inputType === "insertParagraph" || event.inputType === "insertNewline") {
982
+ event.preventDefault();
983
+ return true;
984
+ }
985
+ if (text != null && /[\r\n\u2028\u2029]/.test(text)) {
986
+ event.preventDefault();
987
+ return true;
988
+ }
989
+ }
990
+ if (!text) {
991
+ return false;
992
+ }
993
+ const normalizedInsert = allowLineBreaksRef.current ? text : text.replace(/[\r\n\u2028\u2029]+/g, " ");
994
+ const selection = currentView.state.selection.main;
995
+ const insert = truncateInsertion(
996
+ normalizedInsert,
997
+ currentView.state.doc.length,
998
+ selection.to - selection.from,
999
+ maxLengthRef.current
1000
+ );
1001
+ if (insert === text) {
1002
+ return false;
1003
+ }
1004
+ event.preventDefault();
1005
+ if (insert) {
1006
+ currentView.dispatch(
1007
+ currentView.state.replaceSelection(insert)
1008
+ );
1009
+ }
1010
+ return true;
1011
+ },
1012
+ paste(event, currentView) {
1013
+ if (disabledRef.current || readOnlyRef.current) {
1014
+ event.preventDefault();
1015
+ return true;
1016
+ }
1017
+ const text = event.clipboardData?.getData("text/plain");
1018
+ if (text === void 0) {
1019
+ return false;
1020
+ }
1021
+ event.preventDefault();
1022
+ const normalizedInsert = allowLineBreaksRef.current ? text : text.replace(/[\r\n\u2028\u2029]+/g, " ");
1023
+ const selection = currentView.state.selection.main;
1024
+ const insert = truncateInsertion(
1025
+ normalizedInsert,
1026
+ currentView.state.doc.length,
1027
+ selection.to - selection.from,
1028
+ maxLengthRef.current
1029
+ );
1030
+ if (insert) {
1031
+ currentView.dispatch(
1032
+ currentView.state.replaceSelection(insert)
1033
+ );
1034
+ }
1035
+ return true;
1036
+ },
1037
+ drop(event, currentView) {
1038
+ if (disabledRef.current || readOnlyRef.current) {
1039
+ event.preventDefault();
1040
+ return true;
1041
+ }
1042
+ const text = event.dataTransfer?.getData("text/plain");
1043
+ if (text === void 0) {
1044
+ return false;
1045
+ }
1046
+ event.preventDefault();
1047
+ const normalizedInsert = allowLineBreaksRef.current ? text : text.replace(/[\r\n\u2028\u2029]+/g, " ");
1048
+ const position = currentView.posAtCoords({
1049
+ x: event.clientX,
1050
+ y: event.clientY
1051
+ });
1052
+ const selection = currentView.state.selection.main;
1053
+ const hasSelection = !selection.empty && position != null;
1054
+ let from;
1055
+ let to;
1056
+ let replacedLength;
1057
+ if (hasSelection) {
1058
+ from = selection.from;
1059
+ to = selection.to;
1060
+ replacedLength = to - from;
1061
+ } else {
1062
+ from = position ?? selection.from;
1063
+ to = from;
1064
+ replacedLength = 0;
1065
+ }
1066
+ const insert = truncateInsertion(
1067
+ normalizedInsert,
1068
+ currentView.state.doc.length,
1069
+ replacedLength,
1070
+ maxLengthRef.current
1071
+ );
1072
+ if (insert) {
1073
+ currentView.dispatch({
1074
+ changes: {
1075
+ from,
1076
+ to,
1077
+ insert
1078
+ },
1079
+ selection: {
1080
+ anchor: from + insert.length
1081
+ },
1082
+ scrollIntoView: true,
1083
+ userEvent: "input.drop"
1084
+ });
1085
+ }
1086
+ return true;
1087
+ },
1088
+ focus() {
1089
+ if (destroyedRef.current || viewRef.current !== view) {
1090
+ return false;
1091
+ }
1092
+ focusedRef.current = true;
1093
+ setFocused(true);
1094
+ view.dispatch({
1095
+ effects: wrappingCompartmentRef.current.reconfigure([
1096
+ EditorView.lineWrapping
1097
+ ])
1098
+ });
1099
+ queueMicrotask(() => {
1100
+ if (destroyedRef.current || viewRef.current !== view) {
1101
+ return;
1102
+ }
1103
+ if (!view.hasFocus) {
1104
+ return;
1105
+ }
1106
+ measurementRef.current();
1107
+ });
1108
+ onFocusRef.current?.();
1109
+ return false;
1110
+ },
1111
+ blur() {
1112
+ if (destroyedRef.current || viewRef.current !== view) {
1113
+ return false;
1114
+ }
1115
+ focusedRef.current = false;
1116
+ setFocused(false);
1117
+ setWrapped(false);
1118
+ focusedHeightRef.current = minimumHeightRef.current;
1119
+ setFocusedHeight(minimumHeightRef.current);
1120
+ scheduleClose();
1121
+ view.dispatch({
1122
+ effects: wrappingCompartmentRef.current.reconfigure([])
1123
+ });
1124
+ onBlurRef.current?.();
1125
+ return false;
1126
+ }
1127
+ })
1128
+ ]
1129
+ })
1130
+ });
1131
+ viewRef.current = view;
1132
+ editorValueRef.current = view.state.doc.toString();
1133
+ if (typeof ResizeObserver !== "undefined") {
1134
+ resizeObserverRef.current = new ResizeObserver(() => {
1135
+ if (destroyedRef.current) {
1136
+ return;
1137
+ }
1138
+ const currentView = viewRef.current;
1139
+ if (!currentView || currentView !== view) {
1140
+ return;
1141
+ }
1142
+ if (focusedRef.current) {
1143
+ measurementRef.current();
1144
+ }
1145
+ });
1146
+ resizeObserverRef.current.observe(host);
1147
+ }
1148
+ if (autoFocus && editable) {
1149
+ queueMicrotask(() => {
1150
+ if (destroyedRef.current || viewRef.current !== view) {
1151
+ return;
1152
+ }
1153
+ if (!view.hasFocus) {
1154
+ view.focus();
1155
+ }
1156
+ });
1157
+ }
1158
+ return () => {
1159
+ destroyedRef.current = true;
1160
+ focusedRef.current = false;
1161
+ isComposingRef.current = false;
1162
+ pendingCompositionSyncRef.current = false;
1163
+ cancelOpen();
1164
+ cancelClose();
1165
+ cancelFrame();
1166
+ cancelRepositionFrame();
1167
+ resizeObserverRef.current?.disconnect();
1168
+ resizeObserverRef.current = null;
1169
+ tooltipResizeObserverRef.current?.disconnect();
1170
+ tooltipResizeObserverRef.current = null;
1171
+ if (pointerRafRef.current !== null) {
1172
+ window.cancelAnimationFrame(pointerRafRef.current);
1173
+ pointerRafRef.current = null;
1174
+ }
1175
+ pendingPointerRef.current = null;
1176
+ hoverRef.current = null;
1177
+ activeTokenKeyRef.current = null;
1178
+ lastPointerTokenKeyRef.current = null;
1179
+ tokenIndexRef.current = {
1180
+ doc: "",
1181
+ variablesVersion: variablesSnapshotRef.current.version,
1182
+ tokens: []
1183
+ };
1184
+ setHover(null);
1185
+ setPopoverPosition(null);
1186
+ if (viewRef.current === view) {
1187
+ viewRef.current = null;
1188
+ }
1189
+ try {
1190
+ view.destroy();
1191
+ } catch {
1192
+ }
1193
+ };
1194
+ }, []);
1195
+ useEffect(() => {
1196
+ const view = viewRef.current;
1197
+ if (!view || destroyedRef.current) {
1198
+ return;
1199
+ }
1200
+ if (isComposingRef.current) {
1201
+ return;
1202
+ }
1203
+ const nextValue = normalizedValue;
1204
+ const currentDoc = view.state.doc.toString();
1205
+ if (currentDoc === nextValue) {
1206
+ editorValueRef.current = currentDoc;
1207
+ lastAcknowledgedPropValueRef.current = nextValue;
1208
+ pendingEmittedValuesRef.current = [];
1209
+ return;
1210
+ }
1211
+ if (nextValue === lastAcknowledgedPropValueRef.current) {
1212
+ return;
1213
+ }
1214
+ const pendingIndex = pendingEmittedValuesRef.current.indexOf(nextValue);
1215
+ if (pendingIndex !== -1) {
1216
+ lastAcknowledgedPropValueRef.current = nextValue;
1217
+ pendingEmittedValuesRef.current = pendingEmittedValuesRef.current.slice(
1218
+ pendingIndex + 1
1219
+ );
1220
+ return;
1221
+ }
1222
+ pendingEmittedValuesRef.current = [];
1223
+ lastAcknowledgedPropValueRef.current = nextValue;
1224
+ applyExternalValue(view, nextValue);
1225
+ }, [applyExternalValue, normalizedValue]);
1226
+ useEffect(() => {
1227
+ const view = viewRef.current;
1228
+ if (!view || destroyedRef.current) {
1229
+ return;
1230
+ }
1231
+ const editableNow = !disabled && !readOnly;
1232
+ if (!editableNow) {
1233
+ closeHover();
1234
+ if (view.hasFocus) {
1235
+ try {
1236
+ view.contentDOM.blur();
1237
+ } catch {
1238
+ }
1239
+ }
1240
+ focusedRef.current = false;
1241
+ setFocused(false);
1242
+ setWrapped(false);
1243
+ focusedHeightRef.current = safeMinHeight;
1244
+ setFocusedHeight(safeMinHeight);
1245
+ view.dispatch({
1246
+ effects: wrappingCompartmentRef.current.reconfigure([])
1247
+ });
1248
+ }
1249
+ view.dispatch({
1250
+ effects: [
1251
+ editableCompartmentRef.current.reconfigure([
1252
+ EditorState.readOnly.of(!editableNow),
1253
+ EditorView.editable.of(editableNow)
1254
+ ]),
1255
+ ariaCompartmentRef.current.reconfigure(
1256
+ EditorView.contentAttributes.of({
1257
+ "aria-label": ariaLabel,
1258
+ "aria-disabled": disabled ? "true" : "false",
1259
+ "aria-readonly": readOnly ? "true" : "false",
1260
+ spellcheck: "false",
1261
+ autocapitalize: "off",
1262
+ autocomplete: "off"
1263
+ })
1264
+ )
1265
+ ]
1266
+ });
1267
+ }, [ariaLabel, closeHover, disabled, readOnly, safeMinHeight]);
1268
+ useEffect(() => {
1269
+ const view = viewRef.current;
1270
+ if (!view || destroyedRef.current) {
1271
+ return;
1272
+ }
1273
+ const currentSnapshot = variablesSnapshotRef.current;
1274
+ if (variablesEqual(currentSnapshot.variables, variables)) {
1275
+ return;
1276
+ }
1277
+ closeHover();
1278
+ activeTokenKeyRef.current = null;
1279
+ lastPointerTokenKeyRef.current = null;
1280
+ const nextVersion = currentSnapshot.version + 1;
1281
+ variablesSnapshotRef.current = {
1282
+ variables,
1283
+ version: nextVersion
1284
+ };
1285
+ tokenIndexRef.current = {
1286
+ doc: view.state.doc.toString(),
1287
+ variablesVersion: nextVersion,
1288
+ tokens: []
1289
+ };
1290
+ view.dispatch({
1291
+ annotations: VariablesSnapshotUpdate.of(nextVersion)
1292
+ });
1293
+ view.dispatch({
1294
+ effects: variableCompletionCompartmentRef.current.reconfigure(
1295
+ createVariableCompletionExtension(variables)
1296
+ )
1297
+ });
1298
+ }, [closeHover, variables]);
1299
+ useEffect(() => {
1300
+ const view = viewRef.current;
1301
+ if (!view || destroyedRef.current) {
1302
+ return;
1303
+ }
1304
+ view.dispatch({
1305
+ effects: placeholderCompartmentRef.current.reconfigure(
1306
+ placeholder$1 ? placeholder(placeholder$1) : []
1307
+ )
1308
+ });
1309
+ }, [placeholder$1]);
1310
+ useLayoutEffect(() => {
1311
+ if (!hover) {
1312
+ tooltipResizeObserverRef.current?.disconnect();
1313
+ tooltipResizeObserverRef.current = null;
1314
+ setPopoverPosition(null);
1315
+ return;
1316
+ }
1317
+ setPopoverPosition(null);
1318
+ repositionPopover();
1319
+ if (typeof ResizeObserver !== "undefined" && tooltipRef.current) {
1320
+ tooltipResizeObserverRef.current?.disconnect();
1321
+ const observer = new ResizeObserver(() => {
1322
+ schedulePopoverReposition();
1323
+ });
1324
+ observer.observe(tooltipRef.current);
1325
+ tooltipResizeObserverRef.current = observer;
1326
+ }
1327
+ return () => {
1328
+ tooltipResizeObserverRef.current?.disconnect();
1329
+ tooltipResizeObserverRef.current = null;
1330
+ };
1331
+ }, [hover?.tokenKey, repositionPopover, schedulePopoverReposition]);
1332
+ useEffect(() => {
1333
+ if (!hover?.tokenKey) {
1334
+ return;
1335
+ }
1336
+ const reposition = () => {
1337
+ if (destroyedRef.current || repositionRafRef.current !== null) {
1338
+ return;
1339
+ }
1340
+ const view = viewRef.current;
1341
+ const latestHover = hoverRef.current;
1342
+ if (!view || !latestHover || view.state.doc.length === 0) {
1343
+ closeHover();
1344
+ return;
1345
+ }
1346
+ repositionRafRef.current = window.requestAnimationFrame(() => {
1347
+ repositionRafRef.current = null;
1348
+ if (destroyedRef.current) {
1349
+ return;
1350
+ }
1351
+ const view2 = viewRef.current;
1352
+ const latestHover2 = hoverRef.current;
1353
+ if (!view2 || !latestHover2) {
1354
+ closeHover();
1355
+ return;
1356
+ }
1357
+ const tokens = getTokensCurrent(view2);
1358
+ const latest = tokens.find(
1359
+ (token) => `${token.from}:${token.to}:${token.name}` === latestHover2.tokenKey
1360
+ );
1361
+ if (!latest) {
1362
+ closeHover();
1363
+ return;
1364
+ }
1365
+ const rect = fullLineTokenRectFromView(view2, latest.from, latest.to);
1366
+ if (!rect) {
1367
+ closeHover();
1368
+ return;
1369
+ }
1370
+ if (!sameRect(latestHover2.rect, rect)) {
1371
+ commitHover({
1372
+ tokenKey: latestHover2.tokenKey,
1373
+ rect,
1374
+ from: latest.from,
1375
+ to: latest.to,
1376
+ name: latest.name,
1377
+ variable: latest.variable
1378
+ });
1379
+ }
1380
+ repositionPopover();
1381
+ });
1382
+ };
1383
+ window.addEventListener("resize", reposition, { passive: true });
1384
+ window.addEventListener("scroll", reposition, {
1385
+ passive: true,
1386
+ capture: true
1387
+ });
1388
+ return () => {
1389
+ window.removeEventListener("resize", reposition);
1390
+ window.removeEventListener("scroll", reposition, true);
1391
+ cancelRepositionFrame();
1392
+ };
1393
+ }, [
1394
+ cancelRepositionFrame,
1395
+ closeHover,
1396
+ commitHover,
1397
+ fullLineTokenRectFromView,
1398
+ getTokensCurrent,
1399
+ hover?.tokenKey,
1400
+ repositionPopover,
1401
+ sameRect
1402
+ ]);
1403
+ const popoverStyle = useMemo(() => {
1404
+ if (!popoverPosition) {
1405
+ return {
1406
+ position: "fixed",
1407
+ zIndex: 999999,
1408
+ pointerEvents: "auto",
1409
+ left: 0,
1410
+ top: 0,
1411
+ visibility: "hidden"
1412
+ };
1413
+ }
1414
+ return {
1415
+ position: "fixed",
1416
+ zIndex: 999999,
1417
+ pointerEvents: "auto",
1418
+ left: popoverPosition.left,
1419
+ top: popoverPosition.top,
1420
+ visibility: "visible",
1421
+ "--popover-arrow-left": `${popoverPosition.arrowLeft}px`
1422
+ };
1423
+ }, [popoverPosition]);
1424
+ const popoverNode = hover ? /* @__PURE__ */ jsxs(
1425
+ Box,
1426
+ {
1427
+ ref: tooltipRef,
1428
+ className: VariableTextEditor_default.hoverPopover,
1429
+ style: popoverStyle,
1430
+ "data-placement": popoverPosition?.placement ?? "bottom",
1431
+ onPointerEnter: handleTooltipPointerEnter,
1432
+ onPointerLeave: handleTooltipPointerLeave,
1433
+ onPointerDown: markPopoverPointerDown,
1434
+ children: [
1435
+ /* @__PURE__ */ jsx(Box, { className: VariableTextEditor_default.hoverPopoverArrow }),
1436
+ /* @__PURE__ */ jsx(Box, { className: VariableTextEditor_default.popoverHeader, children: hover.variable?.name ?? hover.name }),
1437
+ /* @__PURE__ */ jsx(Box, { className: VariableTextEditor_default.popoverType, children: hover.variable?.type ?? "Undefined variable" }),
1438
+ hover.variable?.value !== void 0 ? /* @__PURE__ */ jsx("div", { className: VariableTextEditor_default.popoverValue, children: hover.variable.value }) : null,
1439
+ hover.variable?.description ? /* @__PURE__ */ jsx(Box, { className: VariableTextEditor_default.popoverDesc, children: hover.variable.description }) : !hover.variable ? /* @__PURE__ */ jsx(Box, { className: VariableTextEditor_default.popoverDesc, children: "This variable is not defined in the current environment." }) : null
1440
+ ]
1441
+ }
1442
+ ) : null;
1443
+ return /* @__PURE__ */ jsx(
1444
+ "div",
1445
+ {
1446
+ ref: anchorRef,
1447
+ style: {
1448
+ minWidth: 0,
1449
+ width: "100%",
1450
+ height: expansionMode === "overlay" ? safeMinHeight : void 0
1451
+ },
1452
+ children: /* @__PURE__ */ jsx(
1453
+ Box,
1454
+ {
1455
+ ref: hostRef,
1456
+ className: [VariableTextEditor_default.root, className ?? ""].filter(Boolean).join(" "),
1457
+ style: { ...rootStyle, ...overlayStyle },
1458
+ "data-focused": focused ? "" : void 0,
1459
+ "data-wrapped": focused && wrapped ? "" : void 0,
1460
+ "data-multiline": allowLineBreaks ? "" : void 0,
1461
+ "data-disabled": disabled ? "" : void 0,
1462
+ "data-readonly": readOnly ? "" : void 0,
1463
+ onPointerEnter: handleHostPointerEnter,
1464
+ onPointerMove: handlePointerMove,
1465
+ onPointerLeave: handleHostPointerLeave,
1466
+ children: popoverNode
1467
+ }
1468
+ )
1469
+ }
1470
+ );
1471
+ }
1472
+ );
1473
+ VariableTextEditor.displayName = "VariableTextEditor";
1474
+
1475
+ export { VariableTextEditor };