@jxsuite/studio 0.28.2 → 0.28.4

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 (104) hide show
  1. package/dist/studio.js +86144 -83971
  2. package/dist/studio.js.map +229 -219
  3. package/package.json +5 -5
  4. package/src/browse/browse-modal.ts +9 -3
  5. package/src/browse/browse.ts +139 -82
  6. package/src/canvas/canvas-diff.ts +35 -18
  7. package/src/canvas/canvas-helpers.ts +55 -21
  8. package/src/canvas/canvas-live-render.ts +140 -89
  9. package/src/canvas/canvas-render.ts +127 -82
  10. package/src/canvas/canvas-utils.ts +68 -42
  11. package/src/canvas/nested-site-style.ts +15 -7
  12. package/src/editor/component-inline-edit.ts +90 -51
  13. package/src/editor/content-inline-edit.ts +75 -72
  14. package/src/editor/context-menu.ts +125 -71
  15. package/src/editor/convert-targets.ts +17 -13
  16. package/src/editor/convert-to-component.ts +51 -28
  17. package/src/editor/convert-to-repeater.ts +44 -19
  18. package/src/editor/inline-edit.ts +107 -52
  19. package/src/editor/inline-format.ts +128 -60
  20. package/src/editor/insertion-helper.ts +26 -14
  21. package/src/editor/shortcuts.ts +89 -43
  22. package/src/editor/slash-menu.ts +41 -26
  23. package/src/files/components.ts +9 -7
  24. package/src/files/file-ops.ts +53 -33
  25. package/src/files/files.ts +246 -143
  26. package/src/format/constraints.ts +25 -15
  27. package/src/format/format-host.ts +33 -10
  28. package/src/github/github-auth.ts +24 -14
  29. package/src/github/github-publish.ts +20 -14
  30. package/src/new-project/new-project-modal.ts +27 -18
  31. package/src/panels/activity-bar.ts +27 -26
  32. package/src/panels/ai-panel.ts +100 -44
  33. package/src/panels/block-action-bar.ts +84 -52
  34. package/src/panels/canvas-dnd.ts +47 -28
  35. package/src/panels/data-explorer.ts +24 -11
  36. package/src/panels/dnd.ts +148 -100
  37. package/src/panels/editors.ts +54 -37
  38. package/src/panels/elements-panel.ts +23 -13
  39. package/src/panels/events-panel.ts +48 -38
  40. package/src/panels/git-panel.ts +95 -60
  41. package/src/panels/head-panel.ts +138 -84
  42. package/src/panels/imports-panel.ts +66 -29
  43. package/src/panels/layers-panel.ts +80 -36
  44. package/src/panels/left-panel.ts +86 -53
  45. package/src/panels/overlays.ts +36 -19
  46. package/src/panels/panel-events.ts +48 -27
  47. package/src/panels/panel-scheduler.ts +41 -23
  48. package/src/panels/preview-render.ts +26 -26
  49. package/src/panels/properties-panel.ts +155 -118
  50. package/src/panels/pseudo-preview.ts +31 -15
  51. package/src/panels/quick-search.ts +28 -13
  52. package/src/panels/right-panel.ts +35 -24
  53. package/src/panels/shared.ts +46 -29
  54. package/src/panels/signals-panel.ts +303 -186
  55. package/src/panels/statusbar.ts +27 -12
  56. package/src/panels/style-inputs.ts +30 -24
  57. package/src/panels/style-panel.ts +130 -90
  58. package/src/panels/style-utils.ts +63 -23
  59. package/src/panels/stylebook-layers-panel.ts +27 -30
  60. package/src/panels/stylebook-panel.ts +140 -72
  61. package/src/panels/tab-strip.ts +21 -10
  62. package/src/panels/toolbar.ts +52 -40
  63. package/src/panels/welcome-screen.ts +1 -1
  64. package/src/platform.ts +2 -1
  65. package/src/platforms/devserver.ts +221 -105
  66. package/src/recent-projects.ts +15 -7
  67. package/src/resize-edges.ts +7 -5
  68. package/src/services/cem-export.ts +73 -57
  69. package/src/services/code-services.ts +52 -25
  70. package/src/services/monaco-setup.ts +5 -5
  71. package/src/settings/content-types-editor.ts +133 -62
  72. package/src/settings/css-vars-editor.ts +31 -14
  73. package/src/settings/defs-editor.ts +125 -63
  74. package/src/settings/general-settings.ts +11 -6
  75. package/src/settings/head-editor.ts +30 -15
  76. package/src/settings/schema-field-ui.ts +75 -35
  77. package/src/settings/settings-modal.ts +35 -17
  78. package/src/site-context.ts +80 -47
  79. package/src/state.ts +98 -65
  80. package/src/store.ts +42 -23
  81. package/src/studio.ts +165 -144
  82. package/src/tabs/tab.ts +51 -45
  83. package/src/tabs/transact.ts +230 -109
  84. package/src/types.ts +60 -51
  85. package/src/ui/button-group.ts +3 -2
  86. package/src/ui/color-selector.ts +33 -15
  87. package/src/ui/expression-editor.ts +139 -86
  88. package/src/ui/field-input.ts +35 -18
  89. package/src/ui/field-row.ts +1 -1
  90. package/src/ui/icons.ts +5 -6
  91. package/src/ui/layers.ts +33 -26
  92. package/src/ui/media-picker.ts +13 -7
  93. package/src/ui/panel-resize.ts +39 -20
  94. package/src/ui/spectrum.ts +4 -2
  95. package/src/ui/unit-selector.ts +20 -11
  96. package/src/ui/value-selector.ts +13 -7
  97. package/src/ui/widgets.ts +34 -18
  98. package/src/utils/canvas-media.ts +66 -33
  99. package/src/utils/edit-display.ts +43 -44
  100. package/src/utils/google-fonts.ts +10 -6
  101. package/src/utils/inherited-style.ts +22 -8
  102. package/src/utils/studio-utils.ts +88 -45
  103. package/src/view.ts +9 -5
  104. package/src/workspace/workspace.ts +49 -29
@@ -32,25 +32,40 @@ const FORMAT_TAGS = new Set([
32
32
  * @returns {boolean}
33
33
  */
34
34
  export function isTagActiveInSelection(tag: string, editableRoot: HTMLElement | null) {
35
- if (!editableRoot) return false;
36
- if (editableRoot.contentEditable === "plaintext-only") return false;
35
+ if (!editableRoot) {
36
+ return false;
37
+ }
38
+ if (editableRoot.contentEditable === "plaintext-only") {
39
+ return false;
40
+ }
37
41
  const sel = window.getSelection();
38
- if (!sel || !sel.rangeCount) return false;
42
+ if (!sel || !sel.rangeCount) {
43
+ return false;
44
+ }
39
45
 
40
46
  const anchor = sel.anchorNode;
41
47
  const focus = sel.focusNode;
42
- if (!anchor || !focus) return false;
43
- if (!editableRoot.contains(anchor) || !editableRoot.contains(focus)) return false;
48
+ if (!anchor || !focus) {
49
+ return false;
50
+ }
51
+ if (!editableRoot.contains(anchor) || !editableRoot.contains(focus)) {
52
+ return false;
53
+ }
44
54
 
45
55
  /**
46
56
  * @param {Node | null} node
47
57
  * @returns {boolean}
48
58
  */
49
59
  const hasTag = (node: Node | null) => {
50
- while (node && node !== editableRoot) {
51
- if (node.nodeType === Node.ELEMENT_NODE && (node as Element).tagName.toLowerCase() === tag)
60
+ let current = node;
61
+ while (current && current !== editableRoot) {
62
+ if (
63
+ current.nodeType === Node.ELEMENT_NODE &&
64
+ (current as Element).tagName.toLowerCase() === tag
65
+ ) {
52
66
  return true;
53
- node = node.parentNode;
67
+ }
68
+ current = current.parentNode;
54
69
  }
55
70
  return false;
56
71
  };
@@ -66,12 +81,20 @@ export function isTagActiveInSelection(tag: string, editableRoot: HTMLElement |
66
81
  * @param {HTMLElement | null} editableRoot
67
82
  */
68
83
  export function toggleInlineFormat(tag: string, editableRoot: HTMLElement | null) {
69
- if (!editableRoot) return;
84
+ if (!editableRoot) {
85
+ return;
86
+ }
70
87
  const sel = window.getSelection();
71
- if (!sel || !sel.rangeCount) return;
88
+ if (!sel || !sel.rangeCount) {
89
+ return;
90
+ }
72
91
  const range = sel.getRangeAt(0);
73
- if (range.collapsed) return;
74
- if (!editableRoot.contains(range.commonAncestorContainer)) return;
92
+ if (range.collapsed) {
93
+ return;
94
+ }
95
+ if (!editableRoot.contains(range.commonAncestorContainer)) {
96
+ return;
97
+ }
75
98
 
76
99
  // Expand selection to fully include any partially-selected ${...} expressions
77
100
  expandRangeToTemplateExpressions(range);
@@ -145,12 +168,14 @@ function unwrapTagInRange(
145
168
  */
146
169
  function unwrapElement(el: Element) {
147
170
  const parent = el.parentNode;
148
- if (!parent) return;
171
+ if (!parent) {
172
+ return;
173
+ }
149
174
  const frag = document.createDocumentFragment();
150
175
  while (el.firstChild) {
151
- frag.appendChild(el.firstChild);
176
+ frag.append(el.firstChild);
152
177
  }
153
- parent.replaceChild(frag, el);
178
+ el.replaceWith(frag);
154
179
  }
155
180
 
156
181
  /**
@@ -171,7 +196,9 @@ function wrapRangeInTag(tag: string, range: Range, _editableRoot: HTMLElement) {
171
196
 
172
197
  // If nothing left after trimming, re-insert everything and bail
173
198
  if (!contents.hasChildNodes()) {
174
- if (leadingWS) range.insertNode(document.createTextNode(leadingWS));
199
+ if (leadingWS) {
200
+ range.insertNode(document.createTextNode(leadingWS));
201
+ }
175
202
  if (trailingWS) {
176
203
  const t = document.createTextNode(trailingWS);
177
204
  range.collapse(false);
@@ -181,13 +208,17 @@ function wrapRangeInTag(tag: string, range: Range, _editableRoot: HTMLElement) {
181
208
  }
182
209
 
183
210
  const wrapper = document.createElement(tag);
184
- wrapper.appendChild(contents);
211
+ wrapper.append(contents);
185
212
 
186
213
  // Build the insertion fragment: [leadingWS] [wrapper] [trailingWS]
187
214
  const frag = document.createDocumentFragment();
188
- if (leadingWS) frag.appendChild(document.createTextNode(leadingWS));
189
- frag.appendChild(wrapper);
190
- if (trailingWS) frag.appendChild(document.createTextNode(trailingWS));
215
+ if (leadingWS) {
216
+ frag.append(document.createTextNode(leadingWS));
217
+ }
218
+ frag.append(wrapper);
219
+ if (trailingWS) {
220
+ frag.append(document.createTextNode(trailingWS));
221
+ }
191
222
 
192
223
  range.insertNode(frag);
193
224
 
@@ -210,14 +241,18 @@ function wrapRangeInTag(tag: string, range: Range, _editableRoot: HTMLElement) {
210
241
  */
211
242
  function trimLeadingWhitespace(frag: DocumentFragment) {
212
243
  const first = frag.firstChild;
213
- if (!first || first.nodeType !== Node.TEXT_NODE) return null;
244
+ if (!first || first.nodeType !== Node.TEXT_NODE) {
245
+ return null;
246
+ }
214
247
  const text = first.textContent ?? "";
215
248
  const match = text.match(/^(\s+)/);
216
- if (!match) return null;
217
- const ws = match[1];
249
+ if (!match) {
250
+ return null;
251
+ }
252
+ const [, ws] = match;
218
253
  if (text.length === ws.length) {
219
254
  // Entire node is whitespace — remove it
220
- frag.removeChild(first);
255
+ first.remove();
221
256
  } else {
222
257
  first.textContent = text.slice(ws.length);
223
258
  }
@@ -233,13 +268,17 @@ function trimLeadingWhitespace(frag: DocumentFragment) {
233
268
  */
234
269
  function trimTrailingWhitespace(frag: DocumentFragment) {
235
270
  const last = frag.lastChild;
236
- if (!last || last.nodeType !== Node.TEXT_NODE) return null;
271
+ if (!last || last.nodeType !== Node.TEXT_NODE) {
272
+ return null;
273
+ }
237
274
  const text = last.textContent ?? "";
238
275
  const match = text.match(/(\s+)$/);
239
- if (!match) return null;
240
- const ws = match[1];
276
+ if (!match) {
277
+ return null;
278
+ }
279
+ const [, ws] = match;
241
280
  if (text.length === ws.length) {
242
- frag.removeChild(last);
281
+ last.remove();
243
282
  } else {
244
283
  last.textContent = text.slice(0, -ws.length);
245
284
  }
@@ -256,30 +295,42 @@ function trimTrailingWhitespace(frag: DocumentFragment) {
256
295
  * @param {HTMLElement | null} root
257
296
  */
258
297
  export function normalizeInlineContent(root: HTMLElement | null) {
259
- if (!root) return;
298
+ if (!root) {
299
+ return;
300
+ }
260
301
  let changed = true;
261
302
  let iterations = 0;
262
303
  while (changed && iterations < 10) {
263
304
  changed = false;
264
- iterations++;
305
+ iterations += 1;
265
306
 
266
307
  // 1. Merge adjacent text nodes
267
308
  root.normalize();
268
309
 
269
310
  // 2. Merge adjacent same-tag siblings
270
- if (mergeAdjacentSiblings(root)) changed = true;
311
+ if (mergeAdjacentSiblings(root)) {
312
+ changed = true;
313
+ }
271
314
 
272
315
  // 3. Collapse redundant nesting (strong > strong → strong)
273
- if (collapseRedundantNesting(root)) changed = true;
316
+ if (collapseRedundantNesting(root)) {
317
+ changed = true;
318
+ }
274
319
 
275
320
  // 4. Remove empty inline elements
276
- if (removeEmptyInlines(root)) changed = true;
321
+ if (removeEmptyInlines(root)) {
322
+ changed = true;
323
+ }
277
324
 
278
325
  // 5. Lift edge whitespace out of inline wrappers
279
- if (liftEdgeWhitespace(root)) changed = true;
326
+ if (liftEdgeWhitespace(root)) {
327
+ changed = true;
328
+ }
280
329
 
281
330
  // 6. Unwrap bare <span> elements (no class, style, or attributes)
282
- if (unwrapBareSpans(root)) changed = true;
331
+ if (unwrapBareSpans(root)) {
332
+ changed = true;
333
+ }
283
334
  }
284
335
  }
285
336
 
@@ -315,7 +366,7 @@ function mergeAdjacentSiblings(root: HTMLElement) {
315
366
  const [el, next] = toMerge[i];
316
367
  // Move all children from next into el
317
368
  while (next.firstChild) {
318
- el.appendChild(next.firstChild);
369
+ el.append(next.firstChild);
319
370
  }
320
371
  next.remove();
321
372
  changed = true;
@@ -337,7 +388,9 @@ function collapseRedundantNesting(root: HTMLElement) {
337
388
 
338
389
  while (walker.nextNode()) {
339
390
  const el = walker.currentNode as Element;
340
- if (!FORMAT_TAGS.has(el.tagName.toLowerCase())) continue;
391
+ if (!FORMAT_TAGS.has(el.tagName.toLowerCase())) {
392
+ continue;
393
+ }
341
394
  // Check if only child is an element with the same tag
342
395
  if (
343
396
  el.childNodes.length === 1 &&
@@ -353,9 +406,9 @@ function collapseRedundantNesting(root: HTMLElement) {
353
406
  const inner = el.firstChild as Element;
354
407
  // Replace outer with inner's children
355
408
  while (inner.firstChild) {
356
- el.insertBefore(inner.firstChild, inner);
409
+ inner.before(inner.firstChild);
357
410
  }
358
- el.removeChild(inner);
411
+ inner.remove();
359
412
  changed = true;
360
413
  }
361
414
  return changed;
@@ -374,7 +427,9 @@ function removeEmptyInlines(root: HTMLElement) {
374
427
 
375
428
  while (walker.nextNode()) {
376
429
  const el = walker.currentNode as Element;
377
- if (!FORMAT_TAGS.has(el.tagName.toLowerCase())) continue;
430
+ if (!FORMAT_TAGS.has(el.tagName.toLowerCase())) {
431
+ continue;
432
+ }
378
433
  if (el.childNodes.length === 0 && !el.textContent) {
379
434
  toRemove.push(el);
380
435
  }
@@ -402,15 +457,19 @@ function liftEdgeWhitespace(root: HTMLElement) {
402
457
 
403
458
  while (walker.nextNode()) {
404
459
  const el = walker.currentNode as Element;
405
- if (!FORMAT_TAGS.has(el.tagName.toLowerCase())) continue;
406
- if (el === root) continue;
460
+ if (!FORMAT_TAGS.has(el.tagName.toLowerCase())) {
461
+ continue;
462
+ }
463
+ if (el === root) {
464
+ continue;
465
+ }
407
466
 
408
467
  const first = el.firstChild;
409
468
  if (first && first.nodeType === Node.TEXT_NODE) {
410
469
  const text = first.textContent ?? "";
411
470
  const m = text.match(/^(\s+)/);
412
471
  if (m && text.length > m[1].length) {
413
- ops.push({ type: "lift-leading", el, ws: m[1] });
472
+ ops.push({ el, type: "lift-leading", ws: m[1] });
414
473
  }
415
474
  }
416
475
 
@@ -419,7 +478,7 @@ function liftEdgeWhitespace(root: HTMLElement) {
419
478
  const text = last.textContent ?? "";
420
479
  const m = text.match(/(\s+)$/);
421
480
  if (m && text.length > m[1].length) {
422
- ops.push({ type: "lift-trailing", el, ws: m[1] });
481
+ ops.push({ el, type: "lift-trailing", ws: m[1] });
423
482
  }
424
483
  }
425
484
  }
@@ -454,10 +513,16 @@ function unwrapBareSpans(root: HTMLElement) {
454
513
 
455
514
  while (walker.nextNode()) {
456
515
  const el = walker.currentNode as Element;
457
- if (el.tagName.toLowerCase() !== "span") continue;
458
- if (el === root) continue;
516
+ if (el.tagName.toLowerCase() !== "span") {
517
+ continue;
518
+ }
519
+ if (el === root) {
520
+ continue;
521
+ }
459
522
  // Keep spans with class, style, or any attributes
460
- if (el.attributes.length > 0) continue;
523
+ if (el.attributes.length > 0) {
524
+ continue;
525
+ }
461
526
  toUnwrap.push(el);
462
527
  }
463
528
 
@@ -498,8 +563,8 @@ function attributesMatch(a: Element, b: Element) {
498
563
  * @param {Range} range
499
564
  */
500
565
  export function expandRangeToTemplateExpressions(range: Range) {
501
- expandBoundary(range, true); // start
502
- expandBoundary(range, false); // end
566
+ expandBoundary(range, true); // Start
567
+ expandBoundary(range, false); // End
503
568
  }
504
569
 
505
570
  /**
@@ -512,24 +577,24 @@ export function expandRangeToTemplateExpressions(range: Range) {
512
577
  function expandBoundary(range: Range, isStart: boolean) {
513
578
  const node = isStart ? range.startContainer : range.endContainer;
514
579
  const offset = isStart ? range.startOffset : range.endOffset;
515
- if (node.nodeType !== Node.TEXT_NODE) return;
580
+ if (node.nodeType !== Node.TEXT_NODE) {
581
+ return;
582
+ }
516
583
 
517
584
  const text = node.textContent ?? "";
518
585
 
519
586
  // Find all ${...} expression spans in this text node (supporting nested braces)
520
587
  const exprs = findTemplateExpressions(text);
521
588
  for (const expr of exprs) {
522
- // expr = { start, end } — character indices of "$" and the closing "}" + 1
589
+ // Expr = { start, end } — character indices of "$" and the closing "}" + 1
523
590
  if (isStart) {
524
591
  // If the range starts inside this expression, move it to include the whole expr
525
592
  if (offset > expr.start && offset < expr.end) {
526
593
  range.setStart(node, expr.start);
527
594
  }
528
- } else {
595
+ } else if (offset > expr.start && offset < expr.end) {
529
596
  // If the range ends inside this expression, expand to include the whole expr
530
- if (offset > expr.start && offset < expr.end) {
531
- range.setEnd(node, expr.end);
532
- }
597
+ range.setEnd(node, expr.end);
533
598
  }
534
599
  }
535
600
  }
@@ -551,17 +616,20 @@ export function findTemplateExpressions(text: string) {
551
616
  let depth = 1;
552
617
  let j = i + 2;
553
618
  while (j < text.length && depth > 0) {
554
- if (text[j] === "{") depth++;
555
- else if (text[j] === "}") depth--;
556
- j++;
619
+ if (text[j] === "{") {
620
+ depth += 1;
621
+ } else if (text[j] === "}") {
622
+ depth -= 1;
623
+ }
624
+ j += 1;
557
625
  }
558
626
  if (depth === 0) {
559
- results.push({ start, end: j });
627
+ results.push({ end: j, start });
560
628
  i = j;
561
629
  continue;
562
630
  }
563
631
  }
564
- i++;
632
+ i += 1;
565
633
  }
566
634
  return results;
567
635
  }
@@ -8,14 +8,14 @@
8
8
 
9
9
  import { showSlashMenu } from "./slash-menu";
10
10
  import { activeTab } from "../workspace/workspace";
11
- import { transactDoc, mutateInsertNode } from "../tabs/transact";
11
+ import { mutateInsertNode, transactDoc } from "../tabs/transact";
12
12
  import type { CanvasPanel } from "../types";
13
13
  import type { JxPath } from "../state";
14
14
  import type { JxMutableNode } from "@jxsuite/schema/types";
15
15
 
16
16
  interface ObservableSubscription {
17
- subscribe(observer: { next: (e: Event) => void }): void;
18
- subscribe(observer: { next: (e: MouseEvent) => void }): void;
17
+ subscribe: ((observer: { next: (e: Event) => void }) => void) &
18
+ ((observer: { next: (e: MouseEvent) => void }) => void);
19
19
  }
20
20
 
21
21
  interface ObservableElement {
@@ -26,7 +26,7 @@ interface InsertionHelperContext {
26
26
  getCanvasMode: () => string; // Returns the active canvas mode.
27
27
  withPanelPointerEvents: (fn: () => unknown) => unknown; // Executes fn with pointer-events
28
28
  effectiveZoom: () => number; // Returns the current zoom scale factor.
29
- defaultDef: (tag: string) => object; // Creates a default element definition for a tag.
29
+ defaultDef: (tag: string) => JxMutableNode; // Creates a default element definition for a tag.
30
30
  parentElementPath: (path: JxPath) => JxPath | null; // Returns the parent element path,
31
31
  childIndex: (path: JxPath) => string | number; // Returns the child index within the
32
32
  getNodeAtPath: (doc: JxMutableNode, path: JxPath) => JxMutableNode | null; // Retrieves
@@ -82,7 +82,7 @@ export function mount(ctx: InsertionHelperContext) {
82
82
  _helper.addEventListener("mouseleave", () => {
83
83
  scheduleHide();
84
84
  });
85
- panel.viewport.appendChild(_helper);
85
+ panel.viewport.append(_helper);
86
86
 
87
87
  _abort = new AbortController();
88
88
 
@@ -105,7 +105,9 @@ export function unmount() {
105
105
  _abort?.abort();
106
106
  _abort = null;
107
107
  cancelHide();
108
- if (_helper?.parentElement) _helper.remove();
108
+ if (_helper?.parentElement) {
109
+ _helper.remove();
110
+ }
109
111
  clearAnchor();
110
112
  _helper = null;
111
113
  _ctx = null;
@@ -116,7 +118,9 @@ export function unmount() {
116
118
 
117
119
  /** @param {MouseEvent} e */
118
120
  function onMouseMove(e: MouseEvent) {
119
- if (!_ctx || !_helper) return;
121
+ if (!_ctx || !_helper) {
122
+ return;
123
+ }
120
124
 
121
125
  const { getCanvasMode } = _ctx;
122
126
  const mode = getCanvasMode();
@@ -161,7 +165,7 @@ function onMouseMove(e: MouseEvent) {
161
165
  }
162
166
 
163
167
  const parentStyle = getComputedStyle(parent);
164
- const display = parentStyle.display;
168
+ const { display } = parentStyle;
165
169
  const isFlex = display === "flex" || display === "inline-flex";
166
170
  const isGrid = display === "grid" || display === "inline-grid";
167
171
  const isRow =
@@ -208,7 +212,9 @@ function onMouseMove(e: MouseEvent) {
208
212
  * @param {number} idx
209
213
  */
210
214
  function showAt(el: HTMLElement, edge: string, path: JxPath, parentPath: JxPath, idx: number) {
211
- if (!_helper) return;
215
+ if (!_helper) {
216
+ return;
217
+ }
212
218
 
213
219
  // Set CSS anchor on target element
214
220
  if (_currentAnchor !== el) {
@@ -219,7 +225,7 @@ function showAt(el: HTMLElement, edge: string, path: JxPath, parentPath: JxPath,
219
225
 
220
226
  _helper.dataset.edge = edge;
221
227
  _helper.classList.add("visible");
222
- _insertionPoint = { edge, path, parentPath, idx };
228
+ _insertionPoint = { edge, idx, parentPath, path };
223
229
  cancelHide();
224
230
  }
225
231
 
@@ -241,7 +247,9 @@ function hide() {
241
247
 
242
248
  function hideNow() {
243
249
  _hideTimer = null;
244
- if (!_helper) return;
250
+ if (!_helper) {
251
+ return;
252
+ }
245
253
  _helper.classList.remove("visible");
246
254
  clearAnchor();
247
255
  _insertionPoint = null;
@@ -260,12 +268,14 @@ function onHelperClick(e: MouseEvent) {
260
268
  e.stopPropagation();
261
269
  e.preventDefault();
262
270
 
263
- if (!_ctx || !_helper || !_insertionPoint) return;
271
+ if (!_ctx || !_helper || !_insertionPoint) {
272
+ return;
273
+ }
264
274
 
265
275
  const captured = _insertionPoint;
266
276
  showSlashMenu(_helper, "", {
267
- showFilter: true,
268
277
  onSelect: (cmd) => onSlashSelect(cmd, captured),
278
+ showFilter: true,
269
279
  });
270
280
  }
271
281
 
@@ -277,7 +287,9 @@ function onSlashSelect(
277
287
  cmd: { label: string; tag: string; description?: string },
278
288
  point: { edge: string; path: JxPath; parentPath: JxPath; idx: number },
279
289
  ) {
280
- if (!_ctx) return;
290
+ if (!_ctx) {
291
+ return;
292
+ }
281
293
 
282
294
  const { defaultDef } = _ctx;
283
295
  const { parentPath, idx, edge } = point;