@jxsuite/studio 0.32.0 → 0.34.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 (94) hide show
  1. package/dist/iframe-entry.js +6230 -0
  2. package/dist/iframe-entry.js.map +35 -0
  3. package/dist/studio.js +22791 -13709
  4. package/dist/studio.js.map +181 -74
  5. package/package.json +12 -7
  6. package/src/browse/browse.ts +11 -4
  7. package/src/canvas/canvas-helpers.ts +2 -56
  8. package/src/canvas/canvas-live-render.ts +102 -435
  9. package/src/canvas/canvas-origin.ts +66 -0
  10. package/src/canvas/canvas-patcher.ts +63 -403
  11. package/src/canvas/canvas-render.ts +70 -212
  12. package/src/canvas/canvas-utils.ts +37 -65
  13. package/src/canvas/iframe-channel.ts +154 -0
  14. package/src/canvas/iframe-drop.ts +484 -0
  15. package/src/canvas/iframe-entry.ts +600 -0
  16. package/src/canvas/iframe-host.ts +1373 -0
  17. package/src/canvas/iframe-inline-edit.ts +367 -0
  18. package/src/canvas/iframe-insert.ts +164 -0
  19. package/src/canvas/iframe-interaction.ts +176 -0
  20. package/src/canvas/iframe-keys.ts +85 -0
  21. package/src/canvas/iframe-overlay.ts +218 -0
  22. package/src/canvas/iframe-patch.ts +363 -0
  23. package/src/canvas/iframe-protocol.ts +361 -0
  24. package/src/canvas/iframe-render.ts +458 -0
  25. package/src/canvas/iframe-slash.ts +114 -0
  26. package/src/canvas/iframe-subtree.ts +113 -0
  27. package/src/canvas/path-mapping.ts +86 -0
  28. package/src/canvas/serialize-scope.ts +65 -0
  29. package/src/editor/canvas-context-menu.ts +40 -0
  30. package/src/editor/canvas-slash-bridge.ts +21 -0
  31. package/src/editor/context-menu.ts +2 -1
  32. package/src/editor/inline-edit-apply.ts +183 -0
  33. package/src/editor/inline-edit.ts +99 -21
  34. package/src/editor/inline-link.ts +89 -0
  35. package/src/editor/insert-zone-action.ts +35 -0
  36. package/src/editor/merge-tags.ts +26 -2
  37. package/src/editor/repeater-scope.ts +144 -0
  38. package/src/editor/shortcuts.ts +14 -28
  39. package/src/editor/slash-menu.ts +73 -42
  40. package/src/files/files.ts +2 -1
  41. package/src/page-params.ts +383 -0
  42. package/src/panels/ai-panel.ts +386 -328
  43. package/src/panels/block-action-bar.ts +296 -138
  44. package/src/panels/canvas-dnd-bridge.ts +397 -0
  45. package/src/panels/component-preview.ts +56 -0
  46. package/src/panels/dnd.ts +41 -17
  47. package/src/panels/drag-ghost.ts +62 -0
  48. package/src/panels/editors.ts +1 -1
  49. package/src/panels/overlays.ts +10 -125
  50. package/src/panels/properties-panel.ts +210 -0
  51. package/src/panels/right-panel.ts +0 -2
  52. package/src/panels/signals-panel.ts +136 -22
  53. package/src/panels/stylebook-doc.ts +373 -0
  54. package/src/panels/stylebook-panel.ts +46 -689
  55. package/src/panels/tab-bar.ts +159 -13
  56. package/src/panels/toolbar.ts +3 -2
  57. package/src/platforms/devserver.ts +18 -47
  58. package/src/services/ai-settings.ts +107 -0
  59. package/src/services/ai-system-prompt.ts +617 -0
  60. package/src/services/ai-tools.ts +854 -0
  61. package/src/services/context-manager.ts +200 -0
  62. package/src/services/document-assistant.ts +183 -0
  63. package/src/services/jx-validate.ts +65 -0
  64. package/src/services/monaco-setup.ts +12 -0
  65. package/src/services/render-critic.ts +75 -0
  66. package/src/services/token-lint.ts +140 -0
  67. package/src/services/tool-executor.ts +156 -0
  68. package/src/settings/css-vars-editor.ts +2 -2
  69. package/src/state.ts +29 -0
  70. package/src/store.ts +4 -62
  71. package/src/studio.ts +90 -40
  72. package/src/tabs/doc-op-apply.ts +89 -0
  73. package/src/tabs/patch-ops.ts +6 -2
  74. package/src/tabs/tab.ts +23 -4
  75. package/src/tabs/transact.ts +33 -69
  76. package/src/types.ts +16 -24
  77. package/src/ui/jx-theme.ts +63 -0
  78. package/src/ui/media-picker.ts +6 -4
  79. package/src/ui/spectrum.ts +5 -0
  80. package/src/utils/canvas-media.ts +0 -137
  81. package/src/utils/edit-display.ts +23 -3
  82. package/src/utils/geometry.ts +43 -0
  83. package/src/utils/link-target.ts +93 -0
  84. package/src/utils/strip-events.ts +54 -0
  85. package/src/view.ts +0 -23
  86. package/src/workspace/workspace.ts +14 -1
  87. package/src/canvas/canvas-subtree-render.ts +0 -113
  88. package/src/editor/component-inline-edit.ts +0 -349
  89. package/src/editor/content-inline-edit.ts +0 -207
  90. package/src/editor/insertion-helper.ts +0 -308
  91. package/src/panels/canvas-dnd.ts +0 -329
  92. package/src/panels/panel-events.ts +0 -306
  93. package/src/panels/preview-render.ts +0 -132
  94. package/src/panels/pseudo-preview.ts +0 -75
@@ -1,207 +0,0 @@
1
- /// <reference lib="dom" />
2
- import { canvasPanels, childIndex, getNodeAtPath, parentElementPath, renderOnly } from "../store";
3
- import { activeTab } from "../workspace/workspace";
4
- import { mutateInsertNode, mutateUpdateProperty, transactDoc } from "../tabs/transact";
5
- import { view } from "../view";
6
- import { isEditableBlock, startEditing } from "./inline-edit";
7
- import { restoreTemplateExpressions } from "../utils/edit-display";
8
- import { renderBlockActionBar } from "../panels/block-action-bar";
9
- import { defaultDef } from "../panels/shared";
10
- import { findCanvasElement, getActivePanel } from "../canvas/canvas-helpers";
11
-
12
- import type { JxContentResult, SlashCommand } from "./inline-edit";
13
- import type { JxPath } from "../state";
14
- import type { JxMutableNode } from "@jxsuite/schema/types";
15
-
16
- /**
17
- * Enter rich-text inline editing on a canvas element (edit/content mode).
18
- *
19
- * @param {HTMLElement} el
20
- * @param {JxPath} path
21
- */
22
- export function enterInlineEdit(el: HTMLElement, path: JxPath) {
23
- // Restore raw template expressions before editing.
24
- // PrepareForEditMode renders ${expr} as ❪ expr ❫ for display;
25
- // Revert so the user edits the real syntax and commits it back intact.
26
- restoreTemplateExpressions(el);
27
-
28
- // Hide overlays while editing
29
- for (const p of canvasPanels) {
30
- p.overlay.style.display = "none";
31
- p.overlayClk.style.pointerEvents = "none";
32
- }
33
-
34
- startEditing(el, path, {
35
- onCommit(
36
- commitPath: JxPath,
37
- children: (JxMutableNode | string)[] | null,
38
- textContent: string | null,
39
- ) {
40
- const node = getNodeAtPath(activeTab.value!.doc.document, commitPath);
41
- if (children) {
42
- if (node && JSON.stringify(node.children) === JSON.stringify(children)) {
43
- return;
44
- }
45
- transactDoc(activeTab.value, (t) => {
46
- mutateUpdateProperty(t, commitPath, "textContent");
47
- mutateUpdateProperty(t, commitPath, "children", children);
48
- });
49
- } else if (textContent != null) {
50
- if (node && node.textContent === textContent && !node.children) {
51
- return;
52
- }
53
- transactDoc(activeTab.value, (t) => {
54
- mutateUpdateProperty(t, commitPath, "children");
55
- mutateUpdateProperty(t, commitPath, "textContent", textContent);
56
- });
57
- }
58
- },
59
-
60
- onEnd() {
61
- if (view.inlineEditCleanup) {
62
- view.inlineEditCleanup();
63
- view.inlineEditCleanup = null;
64
- }
65
- for (const p of canvasPanels) {
66
- p.overlay.style.display = "";
67
- p.overlayClk.style.pointerEvents = "";
68
- }
69
- renderOnly("overlays");
70
- },
71
-
72
- onInsert(afterPath: JxPath, cmd: SlashCommand, commitData: JxContentResult | undefined) {
73
- const isEmpty =
74
- !commitData ||
75
- (commitData.textContent != null && commitData.textContent.trim() === "") ||
76
- (commitData.children &&
77
- (commitData.children.length === 0 ||
78
- (commitData.children.length === 1 &&
79
- typeof commitData.children[0] === "string" &&
80
- commitData.children[0].trim() === "") ||
81
- (commitData.children.length === 1 &&
82
- typeof commitData.children[0] === "object" &&
83
- commitData.children[0]?.tagName === "br")));
84
-
85
- // If the element is empty, swap its tagName instead of inserting after
86
- if (isEmpty) {
87
- transactDoc(activeTab.value, (t) => {
88
- mutateUpdateProperty(t, afterPath, "tagName", cmd.tag);
89
- mutateUpdateProperty(t, afterPath, "children");
90
- const def = defaultDef(cmd.tag);
91
- if (def.textContent && def.textContent !== "Paragraph text") {
92
- mutateUpdateProperty(t, afterPath, "textContent", def.textContent);
93
- } else {
94
- mutateUpdateProperty(t, afterPath, "textContent");
95
- }
96
- t.session.selection = afterPath;
97
- });
98
-
99
- requestAnimationFrame(() => {
100
- const activePanel = getActivePanel();
101
- if (activePanel) {
102
- const nextEl = findCanvasElement(afterPath, activePanel.canvas);
103
- if (nextEl && isEditableBlock(nextEl)) {
104
- enterInlineEdit(nextEl, afterPath);
105
- }
106
- }
107
- });
108
- return;
109
- }
110
-
111
- const elementDef = defaultDef(cmd.tag);
112
- const parentPath = parentElementPath(afterPath) as JxPath;
113
- const idx = childIndex(afterPath) as number;
114
- const newPath = [...parentPath, "children", idx + 1];
115
-
116
- // Apply pending commit from inline edit first (batched to avoid double render)
117
- transactDoc(activeTab.value, (t) => {
118
- if (commitData) {
119
- if (commitData.children) {
120
- mutateUpdateProperty(t, afterPath, "textContent");
121
- mutateUpdateProperty(t, afterPath, "children", commitData.children);
122
- } else if (commitData.textContent != null) {
123
- mutateUpdateProperty(t, afterPath, "children");
124
- mutateUpdateProperty(t, afterPath, "textContent", commitData.textContent);
125
- }
126
- }
127
- mutateInsertNode(t, parentPath, idx + 1, structuredClone(elementDef));
128
- t.session.selection = newPath;
129
- });
130
-
131
- // If the inserted element is editable, enter editing
132
- requestAnimationFrame(() => {
133
- const activePanel = getActivePanel();
134
- if (activePanel) {
135
- const newEl = findCanvasElement(newPath, activePanel.canvas);
136
- if (newEl && isEditableBlock(newEl)) {
137
- enterInlineEdit(newEl, newPath);
138
- }
139
- }
140
- });
141
- },
142
-
143
- onSplit(splitPath: JxPath, before: JxContentResult, after: JxContentResult) {
144
- const tag = "p";
145
-
146
- // Insert new element after with "after" content
147
- const parentPath = parentElementPath(splitPath) as JxPath;
148
- const idx = childIndex(splitPath) as number;
149
- const newNode: JxMutableNode = { tagName: tag };
150
- if (after.textContent != null) {
151
- newNode.textContent = after.textContent;
152
- } else if (after.children) {
153
- newNode.children = after.children;
154
- } else {
155
- newNode.textContent = "";
156
- }
157
-
158
- const newPath = [...parentPath, "children", idx + 1];
159
-
160
- transactDoc(activeTab.value, (t) => {
161
- if (before.textContent != null) {
162
- mutateUpdateProperty(t, splitPath, "children");
163
- mutateUpdateProperty(t, splitPath, "textContent", before.textContent);
164
- } else if (before.children) {
165
- mutateUpdateProperty(t, splitPath, "textContent");
166
- mutateUpdateProperty(t, splitPath, "children", before.children);
167
- }
168
- mutateInsertNode(t, parentPath, idx + 1, newNode);
169
- t.session.selection = newPath;
170
- });
171
-
172
- // Re-enter editing on the new element after render
173
- requestAnimationFrame(() => {
174
- const activePanel = getActivePanel();
175
- if (activePanel) {
176
- const newEl = findCanvasElement(newPath, activePanel.canvas);
177
- if (newEl && isEditableBlock(newEl)) {
178
- enterInlineEdit(newEl, newPath);
179
- // Place cursor at start of new element
180
- const sel = window.getSelection();
181
- const range = document.createRange();
182
- range.selectNodeContents(newEl);
183
- range.collapse(true);
184
- sel?.removeAllRanges();
185
- sel?.addRange(range);
186
- }
187
- }
188
- });
189
- },
190
- });
191
-
192
- // Show the block action bar (with inline formatting buttons) on the viewport
193
- requestAnimationFrame(() => renderBlockActionBar());
194
-
195
- // Re-render action bar when selection changes inside contenteditable
196
- const selectionHandler = () => renderBlockActionBar();
197
- document.addEventListener("selectionchange", selectionHandler);
198
- el.addEventListener("mouseup", selectionHandler);
199
- el.addEventListener("keyup", selectionHandler);
200
-
201
- const inlineEditCleanup = () => {
202
- document.removeEventListener("selectionchange", selectionHandler);
203
- el.removeEventListener("mouseup", selectionHandler);
204
- el.removeEventListener("keyup", selectionHandler);
205
- };
206
- view.inlineEditCleanup = inlineEditCleanup;
207
- }
@@ -1,308 +0,0 @@
1
- /// <reference lib="dom" />
2
- /**
3
- * Insertion-helper.js — Single floating "+" button for element insertion on the canvas.
4
- *
5
- * Uses CSS Anchor Positioning to attach to sibling boundaries and empty containers. Uses Native
6
- * Observables (Chrome 135+) for declarative event handling.
7
- */
8
-
9
- import { showSlashMenu } from "./slash-menu";
10
- import { activeTab } from "../workspace/workspace";
11
- import { mutateInsertNode, transactDoc } from "../tabs/transact";
12
- import type { CanvasPanel } from "../types";
13
- import type { JxPath } from "../state";
14
- import type { JxMutableNode } from "@jxsuite/schema/types";
15
-
16
- interface ObservableSubscription {
17
- subscribe: ((observer: { next: (e: Event) => void }) => void) &
18
- ((observer: { next: (e: MouseEvent) => void }) => void);
19
- }
20
-
21
- interface ObservableElement {
22
- on: (event: string, options?: Record<string, unknown>) => ObservableSubscription;
23
- }
24
-
25
- interface InsertionHelperContext {
26
- getCanvasMode: () => string; // Returns the active canvas mode.
27
- withPanelPointerEvents: (fn: () => unknown) => unknown; // Executes fn with pointer-events
28
- effectiveZoom: () => number; // Returns the current zoom scale factor.
29
- defaultDef: (tag: string) => JxMutableNode; // Creates a default element definition for a tag.
30
- parentElementPath: (path: JxPath) => JxPath | null; // Returns the parent element path,
31
- childIndex: (path: JxPath) => string | number; // Returns the child index within the
32
- getNodeAtPath: (doc: JxMutableNode, path: JxPath) => JxMutableNode | null; // Retrieves
33
- elToPath: WeakMap<Element, JxPath>; // Maps rendered DOM elements to their document
34
- panel: CanvasPanel; // The active canvas panel.
35
- }
36
-
37
- // ─── Types ────────────────────────────────────────────────────────────────────
38
-
39
- // ─── State ───────────────────────────────────────────────────────────────────
40
-
41
- let _ctx: InsertionHelperContext | null = null;
42
-
43
- let _helper: HTMLElement | null = null;
44
-
45
- let _currentAnchor: HTMLElement | null = null;
46
-
47
- let _insertionPoint: {
48
- edge: string;
49
- path: JxPath;
50
- parentPath: JxPath;
51
- idx: number;
52
- } | null = null;
53
-
54
- let _abort: AbortController | null = null;
55
-
56
- let _hideTimer: ReturnType<typeof setTimeout> | null = null;
57
-
58
- // Edge detection threshold in pixels
59
- const EDGE_THRESHOLD = 14;
60
-
61
- // Delay before hiding to allow cursor to reach the button
62
- const HIDE_DELAY = 300;
63
-
64
- // ─── Public API ──────────────────────────────────────────────────────────────
65
-
66
- /**
67
- * Mount the insertion helper system.
68
- *
69
- * @param {InsertionHelperContext} ctx
70
- */
71
- export function mount(ctx: InsertionHelperContext) {
72
- _ctx = ctx;
73
- const { panel } = ctx;
74
-
75
- _helper = document.createElement("button");
76
- _helper.className = "insertion-helper";
77
- _helper.textContent = "+";
78
- _helper.addEventListener("click", onHelperClick);
79
- _helper.addEventListener("mouseenter", () => {
80
- cancelHide();
81
- });
82
- _helper.addEventListener("mouseleave", () => {
83
- scheduleHide();
84
- });
85
- panel.viewport.append(_helper);
86
-
87
- _abort = new AbortController();
88
-
89
- // Listen on viewport — overlayClk gets pointer-events:none during editing/selection
90
- const viewport = panel.viewport as HTMLElement & ObservableElement;
91
- if (typeof viewport.on === "function") {
92
- viewport.on("mousemove", { signal: _abort.signal }).subscribe({ next: onMouseMove });
93
- viewport.on("mouseleave", { signal: _abort.signal }).subscribe({ next: hide });
94
- } else {
95
- panel.viewport.addEventListener("mousemove", onMouseMove, {
96
- signal: _abort.signal,
97
- });
98
- panel.viewport.addEventListener("mouseleave", hide, {
99
- signal: _abort.signal,
100
- });
101
- }
102
- }
103
-
104
- export function unmount() {
105
- _abort?.abort();
106
- _abort = null;
107
- cancelHide();
108
- if (_helper?.parentElement) {
109
- _helper.remove();
110
- }
111
- clearAnchor();
112
- _helper = null;
113
- _ctx = null;
114
- _insertionPoint = null;
115
- }
116
-
117
- // ─── Detection ───────────────────────────────────────────────────────────────
118
-
119
- /** @param {MouseEvent} e */
120
- function onMouseMove(e: MouseEvent) {
121
- if (!_ctx || !_helper) {
122
- return;
123
- }
124
-
125
- const { getCanvasMode } = _ctx;
126
- const mode = getCanvasMode();
127
- if (mode !== "design" && mode !== "edit") {
128
- hide();
129
- return;
130
- }
131
-
132
- const { panel, withPanelPointerEvents, elToPath } = _ctx;
133
- const el = withPanelPointerEvents(() =>
134
- document.elementFromPoint(e.clientX, e.clientY),
135
- ) as HTMLElement | null;
136
-
137
- if (!el || !panel.canvas.contains(el)) {
138
- hide();
139
- return;
140
- }
141
-
142
- const path = elToPath.get(el);
143
- if (!path) {
144
- hide();
145
- return;
146
- }
147
-
148
- // Empty container: show centered "+"
149
- if (el.classList.contains("empty-container-placeholder")) {
150
- showAt(el, "center", path, path, 0);
151
- return;
152
- }
153
-
154
- // Root element — can't insert siblings above/below root
155
- if (path.length === 0) {
156
- hide();
157
- return;
158
- }
159
-
160
- // Determine layout direction of parent container
161
- const parent = el.parentElement;
162
- if (!parent) {
163
- hide();
164
- return;
165
- }
166
-
167
- const parentStyle = getComputedStyle(parent);
168
- const { display } = parentStyle;
169
- const isFlex = display === "flex" || display === "inline-flex";
170
- const isGrid = display === "grid" || display === "inline-grid";
171
- const isRow =
172
- (isFlex && parentStyle.flexDirection.startsWith("row")) ||
173
- (isGrid && parentStyle.gridAutoFlow?.startsWith("column"));
174
-
175
- // Calculate relative position within element
176
- const rect = el.getBoundingClientRect();
177
- const parentPath = _ctx.parentElementPath(path);
178
- if (!parentPath) {
179
- hide();
180
- return;
181
- }
182
- const childIdx = _ctx.childIndex(path) as number;
183
-
184
- if (isRow) {
185
- const relX = e.clientX - rect.left;
186
- if (relX < EDGE_THRESHOLD) {
187
- showAt(el, "left", path, parentPath, childIdx);
188
- } else if (rect.width - relX < EDGE_THRESHOLD) {
189
- showAt(el, "right", path, parentPath, childIdx + 1);
190
- } else {
191
- hide();
192
- }
193
- } else {
194
- const relY = e.clientY - rect.top;
195
- if (relY < EDGE_THRESHOLD) {
196
- showAt(el, "top", path, parentPath, childIdx);
197
- } else if (rect.height - relY < EDGE_THRESHOLD) {
198
- showAt(el, "bottom", path, parentPath, childIdx + 1);
199
- } else {
200
- hide();
201
- }
202
- }
203
- }
204
-
205
- // ─── Show / Hide ─────────────────────────────────────────────────────────────
206
-
207
- /**
208
- * @param {HTMLElement} el
209
- * @param {string} edge
210
- * @param {JxPath} path
211
- * @param {JxPath} parentPath
212
- * @param {number} idx
213
- */
214
- function showAt(el: HTMLElement, edge: string, path: JxPath, parentPath: JxPath, idx: number) {
215
- if (!_helper) {
216
- return;
217
- }
218
-
219
- // Set CSS anchor on target element
220
- if (_currentAnchor !== el) {
221
- clearAnchor();
222
- (el.style as CSSStyleDeclaration & Record<string, string>).anchorName = "--jx-insert";
223
- _currentAnchor = el;
224
- }
225
-
226
- _helper.dataset.edge = edge;
227
- _helper.classList.add("visible");
228
- _insertionPoint = { edge, idx, parentPath, path };
229
- cancelHide();
230
- }
231
-
232
- function scheduleHide() {
233
- cancelHide();
234
- _hideTimer = setTimeout(hideNow, HIDE_DELAY);
235
- }
236
-
237
- function cancelHide() {
238
- if (_hideTimer !== null) {
239
- clearTimeout(_hideTimer);
240
- _hideTimer = null;
241
- }
242
- }
243
-
244
- function hide() {
245
- scheduleHide();
246
- }
247
-
248
- function hideNow() {
249
- _hideTimer = null;
250
- if (!_helper) {
251
- return;
252
- }
253
- _helper.classList.remove("visible");
254
- clearAnchor();
255
- _insertionPoint = null;
256
- }
257
-
258
- function clearAnchor() {
259
- if (_currentAnchor) {
260
- (_currentAnchor.style as CSSStyleDeclaration & Record<string, string>).anchorName = "";
261
- _currentAnchor = null;
262
- }
263
- }
264
-
265
- // ─── Insertion ───────────────────────────────────────────────────────────────
266
-
267
- function onHelperClick(e: MouseEvent) {
268
- e.stopPropagation();
269
- e.preventDefault();
270
-
271
- if (!_ctx || !_helper || !_insertionPoint) {
272
- return;
273
- }
274
-
275
- const captured = _insertionPoint;
276
- showSlashMenu(_helper, "", {
277
- onSelect: (cmd) => onSlashSelect(cmd, captured),
278
- showFilter: true,
279
- });
280
- }
281
-
282
- /**
283
- * @param {{ label: string; tag: string; description?: string }} cmd
284
- * @param {{ edge: string; path: JxPath; parentPath: JxPath; idx: number }} point
285
- */
286
- function onSlashSelect(
287
- cmd: { label: string; tag: string; description?: string },
288
- point: { edge: string; path: JxPath; parentPath: JxPath; idx: number },
289
- ) {
290
- if (!_ctx) {
291
- return;
292
- }
293
-
294
- const { defaultDef } = _ctx;
295
- const { parentPath, idx, edge } = point;
296
-
297
- const newDef = defaultDef(cmd.tag);
298
- const insertPath = edge === "center" ? point.path : parentPath;
299
- const insertIdx = edge === "center" ? 0 : idx;
300
- const newPath = [...insertPath, "children", insertIdx];
301
-
302
- transactDoc(activeTab.value, (t) => {
303
- mutateInsertNode(t, insertPath, insertIdx, newDef);
304
- t.session.selection = newPath;
305
- });
306
-
307
- hide();
308
- }