@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,329 +0,0 @@
1
- /// <reference lib="dom" />
2
- /// <reference lib="dom.iterable" />
3
- /**
4
- * Canvas DnD — extracted from studio.js (Phase 4m). Registers canvas elements as drag-and-drop
5
- * targets using @atlaskit/pragmatic-drag-and-drop.
6
- */
7
-
8
- import {
9
- dropTargetForElements,
10
- monitorForElements,
11
- } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
12
-
13
- import { VOID_ELEMENTS, canvasPanels, elToPath, getNodeAtPath, isAncestor } from "../store";
14
- import { activeTab } from "../workspace/workspace";
15
- import { view } from "../view";
16
- import { applyDropInstruction } from "../panels/dnd";
17
- import { effectiveZoom } from "../canvas/canvas-helpers";
18
-
19
- import type { CanvasPanel } from "../types";
20
-
21
- import type { JxPath } from "../state";
22
-
23
- export type { CanvasPanel } from "../types";
24
-
25
- interface DropInstruction {
26
- type: "reorder-above" | "reorder-below" | "make-child";
27
- }
28
-
29
- interface DropResult {
30
- instruction: DropInstruction;
31
- referenceEl: HTMLElement;
32
- targetPath: JxPath;
33
- }
34
-
35
- let _activeDropEl: HTMLElement | null = null;
36
-
37
- /**
38
- * Register all canvas elements in a panel as DnD drop targets.
39
- *
40
- * @param {CanvasPanel} panel
41
- */
42
- export function registerPanelDnD(panel: CanvasPanel) {
43
- const { canvas, dropLine } = panel;
44
- const allEls = canvas.querySelectorAll("*");
45
-
46
- // Drop-target callbacks fire on EVERY target in the stack (innermost → outermost),
47
- // And every canvas element is a drop target — so the indicator and the drop are
48
- // Driven from the monitor using only the innermost target.
49
- /** Innermost drop target if it belongs to this panel's canvas, else null */
50
- const innermostCanvasTarget = (location: {
51
- current: {
52
- dropTargets: {
53
- data: Record<string | symbol, unknown>;
54
- element: Element;
55
- }[];
56
- };
57
- }) => {
58
- const [target] = location.current.dropTargets;
59
- if (!target) {
60
- return null;
61
- }
62
- const tEl = target.element as HTMLElement;
63
- const tPath = target.data.path;
64
- if (!canvas.contains(tEl) || !Array.isArray(tPath)) {
65
- return null;
66
- }
67
- return { el: tEl, isLeaf: Boolean(target.data._isVoid), path: tPath as JxPath };
68
- };
69
-
70
- const monitorCleanup = monitorForElements({
71
- onDrag({ location }) {
72
- view.lastDragInput = location.current.input;
73
- const target = innermostCanvasTarget(location);
74
- if (target) {
75
- if (_activeDropEl && _activeDropEl !== target.el) {
76
- _activeDropEl.classList.remove("canvas-drop-target");
77
- }
78
- _activeDropEl = target.el;
79
- showCanvasDropIndicator(target.el, target.path, target.isLeaf, panel);
80
- } else if (location.current.dropTargets.length > 0) {
81
- // Pointer is over a non-canvas target (e.g. a layer row) — hide this panel's
82
- // Indicator. When over dead space (no targets at all) keep the last indicator
83
- // Visible so it persists for the whole drag.
84
- if (_activeDropEl && canvas.contains(_activeDropEl)) {
85
- _activeDropEl.classList.remove("canvas-drop-target");
86
- _activeDropEl = null;
87
- }
88
- dropLine.style.display = "none";
89
- }
90
- },
91
- onDragStart({ location }) {
92
- view.lastDragInput = location.current.input;
93
- for (const el of canvas.querySelectorAll("*")) {
94
- (el as HTMLElement).style.pointerEvents = "auto";
95
- }
96
- for (const p of canvasPanels) {
97
- p.overlayClk.style.pointerEvents = "none";
98
- }
99
- },
100
- onDrop({ source, location }) {
101
- const target = innermostCanvasTarget(location);
102
- if (target) {
103
- const { instruction, targetPath } = getCanvasDropResult(
104
- target.el,
105
- target.path,
106
- target.isLeaf,
107
- );
108
- applyDropInstruction(instruction, source.data, targetPath);
109
- }
110
- _activeDropEl?.classList.remove("canvas-drop-target");
111
- _activeDropEl = null;
112
- for (const p of canvasPanels) {
113
- if (p.dropLine) {
114
- p.dropLine.style.display = "none";
115
- }
116
- }
117
- view.lastDragInput = null;
118
- for (const el of canvas.querySelectorAll("*")) {
119
- (el as HTMLElement).style.pointerEvents = "none";
120
- }
121
- for (const p of canvasPanels) {
122
- p.overlayClk.style.pointerEvents = "";
123
- }
124
- },
125
- });
126
- view.canvasDndCleanups.push(monitorCleanup);
127
-
128
- for (const el of allEls) {
129
- if (!elToPath.get(el)) {
130
- continue;
131
- }
132
- registerElementDropTarget(el);
133
- }
134
- }
135
-
136
- /**
137
- * Register one canvas element as a drop target. Path and leaf-ness are read live from elToPath and
138
- * the current document at drag time, so surgical patches that remap sibling paths never leave stale
139
- * closures behind.
140
- *
141
- * @param {Element} el
142
- */
143
- function registerElementDropTarget(el: Element) {
144
- const cleanup = dropTargetForElements({
145
- canDrop({ source }) {
146
- const elPath = elToPath.get(el);
147
- if (!elPath) {
148
- return false;
149
- }
150
- const srcPath = source.data.path as JxPath | undefined;
151
- if (srcPath && isAncestor(srcPath, elPath)) {
152
- return false;
153
- }
154
- return true;
155
- },
156
- element: /** @type {HTMLElement} */ el,
157
- getData() {
158
- const elPath = elToPath.get(el) ?? [];
159
- const document = activeTab.value?.doc.document;
160
- const node = document ? getNodeAtPath(document, elPath) : undefined;
161
- const tag = (node?.tagName || "div").toLowerCase();
162
- const hasElementChildren =
163
- Array.isArray(node?.children) &&
164
- node.children.some((c: unknown) => c != null && typeof c === "object");
165
- return { _isVoid: VOID_ELEMENTS.has(tag) || !hasElementChildren, path: elPath };
166
- },
167
- });
168
- view.canvasDndCleanups.push(cleanup);
169
- }
170
-
171
- /**
172
- * Register drop targets for a freshly patched-in subtree (root plus descendants). Cleanups join
173
- * view.canvasDndCleanups and are released by the next full render like all canvas DnD handlers.
174
- *
175
- * @param {HTMLElement} rootEl
176
- */
177
- export function registerSubtreeDnD(rootEl: HTMLElement) {
178
- if (elToPath.get(rootEl)) {
179
- registerElementDropTarget(rootEl);
180
- }
181
- for (const el of rootEl.querySelectorAll("*")) {
182
- if (elToPath.get(el)) {
183
- registerElementDropTarget(el);
184
- }
185
- }
186
- }
187
-
188
- /**
189
- * @param {HTMLElement} el
190
- * @param {JxPath} elPath
191
- * @param {boolean} isLeaf
192
- * @returns {DropResult}
193
- */
194
- function getCanvasDropResult(el: HTMLElement, elPath: JxPath, isLeaf: boolean): DropResult {
195
- if (!view.lastDragInput) {
196
- return {
197
- instruction: { type: "make-child" },
198
- referenceEl: el,
199
- targetPath: elPath,
200
- };
201
- }
202
- const y = view.lastDragInput.clientY;
203
-
204
- if (elPath.length === 0) {
205
- const children = [...el.children] as HTMLElement[];
206
- if (children.length === 0) {
207
- return {
208
- instruction: { type: "make-child" },
209
- referenceEl: el,
210
- targetPath: elPath,
211
- };
212
- }
213
- return nearestChildEdge(children, y, elPath);
214
- }
215
-
216
- const rect = el.getBoundingClientRect();
217
- const relY = (y - rect.top) / rect.height;
218
-
219
- if (isLeaf) {
220
- const instruction =
221
- relY < 0.5 ? { type: "reorder-above" as const } : { type: "reorder-below" as const };
222
- return { instruction, referenceEl: el, targetPath: elPath };
223
- }
224
-
225
- if (relY < 0.25) {
226
- return {
227
- instruction: { type: "reorder-above" },
228
- referenceEl: el,
229
- targetPath: elPath,
230
- };
231
- }
232
- if (relY > 0.75) {
233
- return {
234
- instruction: { type: "reorder-below" },
235
- referenceEl: el,
236
- targetPath: elPath,
237
- };
238
- }
239
- return {
240
- instruction: { type: "make-child" },
241
- referenceEl: el,
242
- targetPath: elPath,
243
- };
244
- }
245
-
246
- /**
247
- * Find the nearest child edge to the cursor and return the appropriate instruction along with the
248
- * reference child element and its path.
249
- *
250
- * @param {HTMLElement[]} children
251
- * @param {number} cursorY
252
- * @param {JxPath} parentPath
253
- * @returns {DropResult}
254
- */
255
- function nearestChildEdge(children: HTMLElement[], cursorY: number, parentPath: JxPath) {
256
- let closestDist = Infinity;
257
- let instruction = { type: "reorder-below" } as DropInstruction;
258
- let closestIdx = children.length - 1;
259
-
260
- for (let i = 0; i < children.length; i++) {
261
- const rect = children[i]!.getBoundingClientRect();
262
- const topDist = Math.abs(cursorY - rect.top);
263
- const bottomDist = Math.abs(cursorY - rect.bottom);
264
-
265
- if (topDist < closestDist) {
266
- closestDist = topDist;
267
- instruction = { type: "reorder-above" };
268
- closestIdx = i;
269
- }
270
- if (bottomDist < closestDist) {
271
- closestDist = bottomDist;
272
- instruction = { type: "reorder-below" };
273
- closestIdx = i;
274
- }
275
- }
276
-
277
- const childPath = [...parentPath, "children", closestIdx];
278
- return {
279
- instruction,
280
- referenceEl: children[closestIdx]!,
281
- targetPath: childPath,
282
- };
283
- }
284
-
285
- /**
286
- * @param {HTMLElement} el
287
- * @param {JxPath} elPath
288
- * @param {boolean} isLeaf
289
- * @param {CanvasPanel} panel
290
- */
291
- function showCanvasDropIndicator(
292
- el: HTMLElement,
293
- elPath: JxPath,
294
- isLeaf: boolean,
295
- panel: CanvasPanel,
296
- ) {
297
- const { instruction, referenceEl } = getCanvasDropResult(el, elPath, isLeaf);
298
- const { dropLine, viewport } = panel;
299
-
300
- const scale = effectiveZoom();
301
- const wrapRect = viewport.getBoundingClientRect();
302
- const refRect = referenceEl.getBoundingClientRect();
303
- const left = (refRect.left - wrapRect.left + viewport.scrollLeft) / scale;
304
- const width = refRect.width / scale;
305
-
306
- if (instruction.type === "make-child") {
307
- dropLine.style.display = "block";
308
- dropLine.style.top = `${(refRect.top - wrapRect.top + viewport.scrollTop) / scale}px`;
309
- dropLine.style.left = `${left}px`;
310
- dropLine.style.width = `${width}px`;
311
- dropLine.style.height = `${refRect.height / scale}px`;
312
- dropLine.className = "canvas-drop-indicator inside";
313
- el.classList.add("canvas-drop-target");
314
- return;
315
- }
316
-
317
- el.classList.remove("canvas-drop-target");
318
- const top =
319
- instruction.type === "reorder-above"
320
- ? (refRect.top - wrapRect.top + viewport.scrollTop) / scale
321
- : (refRect.bottom - wrapRect.top + viewport.scrollTop) / scale;
322
-
323
- dropLine.style.display = "block";
324
- dropLine.style.top = `${top}px`;
325
- dropLine.style.left = `${left}px`;
326
- dropLine.style.width = `${width}px`;
327
- dropLine.style.height = "";
328
- dropLine.className = "canvas-drop-indicator line";
329
- }
@@ -1,306 +0,0 @@
1
- /// <reference lib="dom" />
2
- /**
3
- * Panel events — extracted from studio.js (Phase 4m). Unified event handler system for canvas
4
- * panels: click-to-select, double-click inline edit, context menu, hover tracking, insertion
5
- * helper.
6
- */
7
-
8
- import {
9
- childIndex,
10
- elToPath,
11
- getNodeAtPath,
12
- parentElementPath,
13
- pathsEqual,
14
- renderOnly,
15
- updateUi,
16
- } from "../store";
17
- import { activeTab } from "../workspace/workspace";
18
- import { view } from "../view";
19
- import { isEditableBlock, isEditing, stopEditing } from "../editor/inline-edit";
20
- import { showContextMenu } from "../editor/context-menu";
21
- import * as insertionHelper from "../editor/insertion-helper";
22
- import { defaultDef } from "../panels/shared";
23
- import {
24
- bubbleInlinePath,
25
- effectiveZoom,
26
- findCanvasElement,
27
- panelMediaToActiveMedia,
28
- } from "../canvas/canvas-helpers";
29
- import { activeLayoutPath, layoutElements } from "../canvas/canvas-live-render";
30
- import type { CanvasPanel } from "./canvas-dnd";
31
- import type { JxPath } from "../state";
32
-
33
- interface PanelEventsCtx {
34
- getCanvasMode: () => string;
35
- enterInlineEdit: (el: HTMLElement, path: JxPath) => void;
36
- navigateToComponent: (path: string) => void;
37
- }
38
-
39
- let _ctx: PanelEventsCtx | null = null;
40
-
41
- /**
42
- * Initialize the panel events module.
43
- *
44
- * @param {PanelEventsCtx} ctx
45
- */
46
- export function initPanelEvents(ctx: PanelEventsCtx) {
47
- _ctx = ctx;
48
- }
49
-
50
- /** @param {import("../canvas/canvas-render.js").CanvasPanel} panel */
51
- export function registerPanelEvents(panel: CanvasPanel) {
52
- const ctx = _ctx as PanelEventsCtx;
53
- const canvas = panel.canvas as HTMLElement;
54
- const overlayClk = panel.overlayClk as HTMLElement;
55
- const { mediaName } = panel;
56
- const ac = new AbortController();
57
- const opts = { signal: ac.signal };
58
- view.canvasEventCleanups.push(() => ac.abort());
59
-
60
- /** @param {() => unknown} fn */
61
- function withPanelPointerEvents(fn: () => unknown) {
62
- const els = canvas.querySelectorAll("*") as NodeListOf<HTMLElement>;
63
- for (const el of els) {
64
- el.style.pointerEvents = "auto";
65
- }
66
- overlayClk.style.display = "none";
67
- const result = fn();
68
- overlayClk.style.display = "";
69
- for (const el of els) {
70
- el.style.pointerEvents = "none";
71
- }
72
- return result;
73
- }
74
-
75
- overlayClk.addEventListener(
76
- "click",
77
- (e: MouseEvent) => {
78
- const barInner = view.blockActionBarEl?.firstElementChild;
79
- if (barInner) {
80
- const r = barInner.getBoundingClientRect();
81
- if (
82
- e.clientX >= r.left &&
83
- e.clientX <= r.right &&
84
- e.clientY >= r.top &&
85
- e.clientY <= r.bottom
86
- ) {
87
- return;
88
- }
89
- }
90
- if (isEditing()) {
91
- stopEditing();
92
- }
93
-
94
- const tab = activeTab.value;
95
- const canvasMode = ctx.getCanvasMode();
96
-
97
- const elements = withPanelPointerEvents(() =>
98
- document.elementsFromPoint(e.clientX, e.clientY),
99
- ) as Element[];
100
-
101
- if (!tab) {
102
- return;
103
- }
104
-
105
- for (const el of elements) {
106
- if (canvas.contains(el) && el !== canvas) {
107
- // Layout element clicked — show layout info instead of selecting in page doc
108
- if (layoutElements.has(el)) {
109
- view.layoutSelection = { el, layoutPath: activeLayoutPath };
110
- tab.session.selection = null;
111
- renderOnly("rightPanel");
112
- return;
113
- }
114
- view.layoutSelection = null;
115
-
116
- const originalPath = elToPath.get(el);
117
- if (originalPath) {
118
- const path = bubbleInlinePath(tab.doc.document, originalPath);
119
- const newMedia = panelMediaToActiveMedia(mediaName);
120
-
121
- const resolvedEl = (
122
- path === originalPath
123
- ? el
124
- : (findCanvasElement(path, canvas) as HTMLElement | null) || el
125
- ) as HTMLElement;
126
-
127
- if (
128
- pathsEqual(path, tab.session.selection) &&
129
- isEditableBlock(resolvedEl) &&
130
- (canvasMode === "edit" || tab.doc.mode === "content")
131
- ) {
132
- tab.session.ui.activeMedia = newMedia;
133
- ctx.enterInlineEdit(resolvedEl, path);
134
- return;
135
- }
136
-
137
- if (canvasMode === "design" && tab.doc.mode !== "content") {
138
- updateUi("pendingInlineEdit", { mediaName, path });
139
- tab.session.ui.activeMedia = newMedia;
140
- tab.session.selection = path;
141
- return;
142
- }
143
-
144
- tab.session.ui.activeMedia = newMedia;
145
- tab.session.selection = path;
146
- return;
147
- }
148
- }
149
- }
150
- tab.session.selection = null;
151
- },
152
- opts,
153
- );
154
-
155
- overlayClk.addEventListener(
156
- "dblclick",
157
- (e: MouseEvent) => {
158
- const barInner = view.blockActionBarEl?.firstElementChild;
159
- if (barInner) {
160
- const r = barInner.getBoundingClientRect();
161
- if (
162
- e.clientX >= r.left &&
163
- e.clientX <= r.right &&
164
- e.clientY >= r.top &&
165
- e.clientY <= r.bottom
166
- ) {
167
- return;
168
- }
169
- }
170
- const canvasMode = ctx.getCanvasMode();
171
- if (canvasMode !== "edit" && canvasMode !== "design") {
172
- return;
173
- }
174
-
175
- const tab = activeTab.value;
176
- const elements = withPanelPointerEvents(() =>
177
- document.elementsFromPoint(e.clientX, e.clientY),
178
- ) as Element[];
179
-
180
- if (!tab) {
181
- return;
182
- }
183
-
184
- for (const el of elements) {
185
- if (canvas.contains(el) && el !== canvas) {
186
- const originalPath = elToPath.get(el);
187
- if (originalPath) {
188
- const path = bubbleInlinePath(tab.doc.document, originalPath);
189
- const resolvedEl = (
190
- path === originalPath
191
- ? el
192
- : (findCanvasElement(path, canvas) as HTMLElement | null) || el
193
- ) as HTMLElement;
194
- if (isEditableBlock(resolvedEl)) {
195
- const newMedia = panelMediaToActiveMedia(mediaName);
196
- tab.session.ui.activeMedia = newMedia;
197
- tab.session.selection = path;
198
- ctx.enterInlineEdit(resolvedEl, path);
199
- return;
200
- }
201
- }
202
- }
203
- }
204
- },
205
- opts,
206
- );
207
-
208
- overlayClk.addEventListener(
209
- "contextmenu",
210
- (e: MouseEvent) => {
211
- const barInner = view.blockActionBarEl?.firstElementChild;
212
- if (barInner) {
213
- const r = barInner.getBoundingClientRect();
214
- if (
215
- e.clientX >= r.left &&
216
- e.clientX <= r.right &&
217
- e.clientY >= r.top &&
218
- e.clientY <= r.bottom
219
- ) {
220
- return;
221
- }
222
- }
223
- const tab = activeTab.value;
224
- const elements = withPanelPointerEvents(() =>
225
- document.elementsFromPoint(e.clientX, e.clientY),
226
- ) as Element[];
227
- for (const el of elements) {
228
- if (canvas.contains(el) && el !== canvas) {
229
- let path = elToPath.get(el);
230
- if (path) {
231
- path = bubbleInlinePath(tab?.doc.document, path);
232
- showContextMenu(e, path, {
233
- onEditComponent: ctx.navigateToComponent,
234
- });
235
- return;
236
- }
237
- }
238
- }
239
- e.preventDefault();
240
- },
241
- opts,
242
- );
243
-
244
- overlayClk.addEventListener(
245
- "mousemove",
246
- (e: MouseEvent) => {
247
- const barInner = view.blockActionBarEl?.firstElementChild;
248
- if (barInner) {
249
- const r = barInner.getBoundingClientRect();
250
- if (
251
- e.clientX >= r.left &&
252
- e.clientX <= r.right &&
253
- e.clientY >= r.top &&
254
- e.clientY <= r.bottom
255
- ) {
256
- return;
257
- }
258
- }
259
- const tab = activeTab.value;
260
- if (!tab) {
261
- return;
262
- }
263
- const el = withPanelPointerEvents(() =>
264
- document.elementFromPoint(e.clientX, e.clientY),
265
- ) as Element | null;
266
- if (el && canvas.contains(el) && el !== canvas) {
267
- let path = elToPath.get(el);
268
- if (path) {
269
- path = bubbleInlinePath(tab.doc.document, path);
270
- if (!pathsEqual(path, tab.session.hover)) {
271
- tab.session.hover = path;
272
- renderOnly("overlays");
273
- }
274
- }
275
- } else if (tab.session.hover) {
276
- tab.session.hover = null;
277
- renderOnly("overlays");
278
- }
279
- },
280
- opts,
281
- );
282
-
283
- overlayClk.addEventListener(
284
- "mouseleave",
285
- () => {
286
- if (activeTab.value?.session.hover) {
287
- activeTab.value.session.hover = null;
288
- renderOnly("overlays");
289
- }
290
- },
291
- opts,
292
- );
293
-
294
- insertionHelper.mount({
295
- childIndex: (path: JxPath) => childIndex(path) ?? 0,
296
- defaultDef,
297
- effectiveZoom,
298
- elToPath,
299
- getCanvasMode: ctx.getCanvasMode,
300
- getNodeAtPath,
301
- panel: panel as unknown as Parameters<typeof insertionHelper.mount>[0]["panel"],
302
- parentElementPath,
303
- withPanelPointerEvents,
304
- });
305
- view.canvasEventCleanups.push(() => insertionHelper.unmount());
306
- }