@jxsuite/studio 0.19.0 → 0.20.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.
- package/dist/studio.css +98 -98
- package/dist/studio.js +12577 -12183
- package/dist/studio.js.map +146 -144
- package/dist/workers/editor.worker.js +79 -79
- package/dist/workers/json.worker.js +109 -109
- package/dist/workers/ts.worker.js +82 -82
- package/package.json +17 -6
- package/src/browse/browse.js +25 -19
- package/src/canvas/canvas-diff.js +24 -16
- package/src/canvas/canvas-helpers.js +24 -19
- package/src/canvas/canvas-live-render.js +94 -39
- package/src/canvas/canvas-render.js +119 -73
- package/src/canvas/canvas-utils.js +38 -15
- package/src/canvas/nested-site-style.js +50 -0
- package/src/editor/component-inline-edit.js +14 -13
- package/src/editor/content-inline-edit.js +22 -10
- package/src/editor/context-menu.js +1 -1
- package/src/editor/convert-to-component.js +12 -10
- package/src/editor/inline-edit.js +66 -28
- package/src/editor/insertion-helper.js +16 -12
- package/src/editor/shortcuts.js +4 -2
- package/src/editor/slash-menu.js +6 -4
- package/src/files/components.js +1 -1
- package/src/files/file-ops.js +37 -16
- package/src/files/files.js +78 -52
- package/src/github/github-auth.js +122 -0
- package/src/github/github-publish.js +115 -0
- package/src/markdown/md-convert.js +172 -107
- package/src/new-project/new-project-modal.js +204 -0
- package/src/panels/activity-bar.js +22 -20
- package/src/panels/ai-panel.js +399 -0
- package/src/panels/block-action-bar.js +5 -5
- package/src/panels/canvas-dnd.js +8 -2
- package/src/panels/data-explorer.js +19 -13
- package/src/panels/dnd.js +144 -92
- package/src/panels/editors.js +55 -35
- package/src/panels/elements-panel.js +33 -19
- package/src/panels/events-panel.js +15 -10
- package/src/panels/git-panel.js +469 -98
- package/src/panels/head-panel.js +121 -81
- package/src/panels/imports-panel.js +88 -64
- package/src/panels/layers-panel.js +64 -43
- package/src/panels/left-panel.js +85 -47
- package/src/panels/overlays.js +1 -0
- package/src/panels/panel-events.js +45 -30
- package/src/panels/preview-render.js +23 -18
- package/src/panels/properties-panel.js +365 -249
- package/src/panels/pseudo-preview.js +2 -2
- package/src/panels/quick-search.js +7 -5
- package/src/panels/right-panel.js +26 -2
- package/src/panels/shared.js +4 -4
- package/src/panels/signals-panel.js +295 -166
- package/src/panels/statusbar.js +4 -4
- package/src/panels/style-inputs.js +67 -35
- package/src/panels/style-panel.js +233 -132
- package/src/panels/style-utils.js +34 -16
- package/src/panels/stylebook-layers-panel.js +12 -10
- package/src/panels/stylebook-panel.js +134 -66
- package/src/panels/toolbar.js +333 -109
- package/src/panels/welcome-screen.js +121 -0
- package/src/platform.js +2 -4
- package/src/platforms/devserver.js +113 -7
- package/src/resize-edges.js +98 -0
- package/src/services/cem-export.js +12 -7
- package/src/services/code-services.js +30 -12
- package/src/settings/content-types-editor.js +7 -7
- package/src/settings/css-vars-editor.js +30 -24
- package/src/settings/defs-editor.js +12 -7
- package/src/settings/general-settings.js +85 -3
- package/src/settings/head-editor.js +30 -24
- package/src/settings/schema-field-ui.js +58 -39
- package/src/site-context.js +41 -31
- package/src/state.js +70 -29
- package/src/store.js +21 -26
- package/src/studio.js +103 -81
- package/src/tabs/tab.js +67 -43
- package/src/tabs/transact.js +23 -13
- package/src/ui/button-group.js +24 -24
- package/src/ui/color-selector.js +28 -23
- package/src/ui/field-row.js +3 -3
- package/src/ui/icons.js +2 -2
- package/src/ui/media-picker.js +7 -7
- package/src/ui/spectrum.js +5 -1
- package/src/ui/unit-selector.js +18 -16
- package/src/ui/value-selector.js +21 -15
- package/src/ui/widgets.js +22 -19
- package/src/utils/canvas-media.js +5 -4
- package/src/utils/edit-display.js +31 -20
- package/src/utils/google-fonts.js +11 -11
- package/src/utils/inherited-style.js +9 -8
- package/src/utils/studio-utils.js +7 -7
- package/src/view.js +58 -1
- package/src/workspace/workspace.js +10 -7
package/src/panels/left-panel.js
CHANGED
|
@@ -24,15 +24,15 @@ import { selectStylebookTag, stylebookMeta } from "./stylebook-panel.js";
|
|
|
24
24
|
* @typedef {{
|
|
25
25
|
* getCanvasMode: () => string;
|
|
26
26
|
* setCanvasMode: (mode: string) => void;
|
|
27
|
-
* renderImportsTemplate:
|
|
28
|
-
* renderFilesTemplate:
|
|
29
|
-
* renderSignalsTemplate:
|
|
30
|
-
* renderDataExplorerTemplate:
|
|
31
|
-
* renderHeadTemplate:
|
|
32
|
-
* renderGitPanel:
|
|
27
|
+
* renderImportsTemplate: Function;
|
|
28
|
+
* renderFilesTemplate: Function;
|
|
29
|
+
* renderSignalsTemplate: Function;
|
|
30
|
+
* renderDataExplorerTemplate: Function;
|
|
31
|
+
* renderHeadTemplate: Function;
|
|
32
|
+
* renderGitPanel: Function;
|
|
33
33
|
* renderCanvas: () => void;
|
|
34
|
-
* defCategory: (
|
|
35
|
-
* defBadgeLabel: (
|
|
34
|
+
* defCategory: (def: unknown) => string;
|
|
35
|
+
* defBadgeLabel: (def: unknown) => string;
|
|
36
36
|
* navigateToComponent: (path: string) => void;
|
|
37
37
|
* webdata: object;
|
|
38
38
|
* defaultDef: (tag: string) => object;
|
|
@@ -40,7 +40,8 @@ import { selectStylebookTag, stylebookMeta } from "./stylebook-panel.js";
|
|
|
40
40
|
* registerElementsDnD: () => void;
|
|
41
41
|
* registerComponentsDnD: () => void;
|
|
42
42
|
* setupTreeKeyboard: (tree: HTMLElement) => void;
|
|
43
|
-
* setGitDiffState: (state:
|
|
43
|
+
* setGitDiffState: (state: import("../canvas/canvas-render.js").GitDiffState | null) => void;
|
|
44
|
+
* cloneRepository?: () => void;
|
|
44
45
|
* }} LeftPanelCtx
|
|
45
46
|
*/
|
|
46
47
|
|
|
@@ -64,15 +65,16 @@ export function mount(ctx) {
|
|
|
64
65
|
_scope.run(() => {
|
|
65
66
|
effect(() => {
|
|
66
67
|
const tab = activeTab.value;
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
68
|
+
if (tab) {
|
|
69
|
+
// Track properties the left panel reads
|
|
70
|
+
void tab.doc.document;
|
|
71
|
+
void tab.doc.mode;
|
|
72
|
+
void tab.session.selection;
|
|
73
|
+
void tab.session.ui.settingsTab;
|
|
74
|
+
void tab.session.ui.gitStatus;
|
|
75
|
+
void tab.session.ui.gitLoading;
|
|
76
|
+
void tab.session.ui.gitError;
|
|
77
|
+
}
|
|
76
78
|
render();
|
|
77
79
|
});
|
|
78
80
|
});
|
|
@@ -122,26 +124,77 @@ function _flush() {
|
|
|
122
124
|
|
|
123
125
|
function _render() {
|
|
124
126
|
const ctx = /** @type {LeftPanelCtx} */ (_ctx);
|
|
127
|
+
const tab = view.leftTab;
|
|
128
|
+
|
|
129
|
+
// ── Project-level panels: render based on projectState, independent of active tab ──
|
|
130
|
+
|
|
131
|
+
if (tab === "files") {
|
|
132
|
+
litRender(html`<div class="panel-body">${ctx.renderFilesTemplate()}</div>`, leftPanel);
|
|
133
|
+
const tree = /** @type {HTMLElement | null} */ (leftPanel.querySelector(".file-tree"));
|
|
134
|
+
if (tree) ctx.setupTreeKeyboard(tree);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (tab === "git") {
|
|
139
|
+
const aTab = activeTab.value;
|
|
140
|
+
const S = aTab ? { ui: aTab.session.ui } : { ui: {} };
|
|
141
|
+
litRender(html`<div class="panel-body">${ctx.renderGitPanel(S, ctx)}</div>`, leftPanel);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (tab === "blocks") {
|
|
146
|
+
const content = renderElementsTemplate(
|
|
147
|
+
/** @type {Parameters<typeof renderElementsTemplate>[0]} */ ({
|
|
148
|
+
webdata: ctx.webdata,
|
|
149
|
+
defaultDef: ctx.defaultDef,
|
|
150
|
+
rerender: render,
|
|
151
|
+
}),
|
|
152
|
+
);
|
|
153
|
+
litRender(html`<div class="panel-body">${content}</div>`, leftPanel);
|
|
154
|
+
ctx.registerElementsDnD();
|
|
155
|
+
ctx.registerComponentsDnD();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ── Document-level panels: require an active tab ──
|
|
160
|
+
|
|
125
161
|
const aTab = activeTab.value;
|
|
126
|
-
if (!aTab)
|
|
127
|
-
|
|
162
|
+
if (!aTab) {
|
|
163
|
+
litRender(html`<div class="panel-body"></div>`, leftPanel);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const S = /**
|
|
168
|
+
* @type {{
|
|
169
|
+
* ui: unknown;
|
|
170
|
+
* document: JxMutableNode;
|
|
171
|
+
* mode: string;
|
|
172
|
+
* selection: JxPath | null;
|
|
173
|
+
* canvas: { scope?: object } | null;
|
|
174
|
+
* content?: { frontmatter?: Record<string, unknown> };
|
|
175
|
+
* documentPath?: string;
|
|
176
|
+
* }}
|
|
177
|
+
*/ ({
|
|
128
178
|
ui: aTab.session.ui,
|
|
129
179
|
document: aTab.doc.document,
|
|
130
180
|
mode: aTab.doc.mode,
|
|
131
181
|
selection: aTab.session.selection,
|
|
132
182
|
canvas: aTab.session.canvas,
|
|
183
|
+
content: aTab.doc.content,
|
|
184
|
+
documentPath: aTab.documentPath,
|
|
133
185
|
});
|
|
134
|
-
const tab = view.leftTab;
|
|
135
186
|
|
|
136
187
|
/** @type {TemplateResult | typeof nothing} */
|
|
137
188
|
let content;
|
|
138
189
|
if (tab === "layers")
|
|
139
190
|
content =
|
|
140
191
|
ctx.getCanvasMode() === "stylebook"
|
|
141
|
-
? renderStylebookLayersTemplate(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
192
|
+
? renderStylebookLayersTemplate(
|
|
193
|
+
/** @type {Parameters<typeof renderStylebookLayersTemplate>[0]} */ ({
|
|
194
|
+
selectStylebookTag,
|
|
195
|
+
stylebookMeta,
|
|
196
|
+
}),
|
|
197
|
+
)
|
|
145
198
|
: renderLayersTemplate({
|
|
146
199
|
navigateToComponent: ctx.navigateToComponent,
|
|
147
200
|
rerender: render,
|
|
@@ -155,13 +208,6 @@ function _render() {
|
|
|
155
208
|
transact(activeTab.value, fn);
|
|
156
209
|
},
|
|
157
210
|
});
|
|
158
|
-
else if (tab === "files") content = ctx.renderFilesTemplate();
|
|
159
|
-
else if (tab === "blocks")
|
|
160
|
-
content = renderElementsTemplate({
|
|
161
|
-
webdata: ctx.webdata,
|
|
162
|
-
defaultDef: ctx.defaultDef,
|
|
163
|
-
rerender: render,
|
|
164
|
-
});
|
|
165
211
|
else if (tab === "state")
|
|
166
212
|
content = ctx.renderSignalsTemplate(S, {
|
|
167
213
|
renderLeftPanel: render,
|
|
@@ -184,33 +230,25 @@ function _render() {
|
|
|
184
230
|
applyMutation: isContent
|
|
185
231
|
? (/** @type {(doc: object) => void} */ fn) => {
|
|
186
232
|
const tab = activeTab.value;
|
|
187
|
-
const fm = /** @type {Record<string,
|
|
188
|
-
const
|
|
233
|
+
const fm = /** @type {Record<string, unknown>} */ (tab.doc.content?.frontmatter ?? {});
|
|
234
|
+
const fmHead = /** @type {unknown[] | undefined} */ (fm.$head);
|
|
235
|
+
const tmp = { title: fm.title, $head: fmHead ? [...fmHead] : undefined };
|
|
189
236
|
fn(tmp);
|
|
190
237
|
if (tmp.title !== fm.title)
|
|
191
|
-
mutateUpdateFrontmatter(tab, "title", /** @type {
|
|
238
|
+
mutateUpdateFrontmatter(tab, "title", /** @type {JsonValue} */ (tmp.title));
|
|
192
239
|
const newHead = tmp.$head && tmp.$head.length > 0 ? tmp.$head : undefined;
|
|
193
|
-
mutateUpdateFrontmatter(tab, "$head", newHead);
|
|
240
|
+
mutateUpdateFrontmatter(tab, "$head", /** @type {JsonValue} */ (newHead));
|
|
241
|
+
render();
|
|
194
242
|
}
|
|
195
243
|
: (/** @type {(doc: object) => void} */ fn) => {
|
|
196
244
|
transact(activeTab.value, fn);
|
|
197
245
|
},
|
|
198
246
|
renderLeftPanel: render,
|
|
199
247
|
});
|
|
200
|
-
} else
|
|
201
|
-
else content = nothing;
|
|
248
|
+
} else content = nothing;
|
|
202
249
|
|
|
203
250
|
litRender(html`<div class="panel-body">${content}</div>`, leftPanel);
|
|
204
251
|
|
|
205
252
|
// Post-render side effects
|
|
206
253
|
if (tab === "layers" && ctx.getCanvasMode() !== "stylebook") ctx.registerLayersDnD();
|
|
207
|
-
else if (tab === "imports") {
|
|
208
|
-
/* no post-render DnD needed */
|
|
209
|
-
} else if (tab === "blocks") {
|
|
210
|
-
ctx.registerElementsDnD();
|
|
211
|
-
ctx.registerComponentsDnD();
|
|
212
|
-
} else if (tab === "files") {
|
|
213
|
-
const tree = /** @type {HTMLElement | null} */ (leftPanel.querySelector(".file-tree"));
|
|
214
|
-
if (tree) ctx.setupTreeKeyboard(tree);
|
|
215
|
-
}
|
|
216
254
|
}
|
package/src/panels/overlays.js
CHANGED
|
@@ -118,6 +118,7 @@ function _flush() {
|
|
|
118
118
|
const boxes = [];
|
|
119
119
|
|
|
120
120
|
// Batch layout reads: read viewport geometry once per panel
|
|
121
|
+
if (!p.viewport) continue;
|
|
121
122
|
const vpRect = p.viewport.getBoundingClientRect();
|
|
122
123
|
const scrollTop = p.viewport.scrollTop;
|
|
123
124
|
const scrollLeft = p.viewport.scrollLeft;
|
|
@@ -22,32 +22,39 @@ import { defaultDef } from "../panels/shared.js";
|
|
|
22
22
|
import { bubbleInlinePath, findCanvasElement, effectiveZoom } from "../canvas/canvas-helpers.js";
|
|
23
23
|
import { layoutElements, activeLayoutPath } from "../canvas/canvas-live-render.js";
|
|
24
24
|
|
|
25
|
-
/** @type {
|
|
25
|
+
/** @type {PanelEventsCtx | null} */
|
|
26
26
|
let _ctx = null;
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {{
|
|
30
|
+
* getCanvasMode: () => string;
|
|
31
|
+
* enterInlineEdit: (el: HTMLElement, path: JxPath) => void;
|
|
32
|
+
* navigateToComponent: (path: string) => void;
|
|
33
|
+
* }} PanelEventsCtx
|
|
34
|
+
*/
|
|
35
|
+
|
|
28
36
|
/**
|
|
29
37
|
* Initialize the panel events module.
|
|
30
38
|
*
|
|
31
|
-
* @param {
|
|
32
|
-
* getCanvasMode: () => string;
|
|
33
|
-
* enterInlineEdit: (el: any, path: any) => void;
|
|
34
|
-
* navigateToComponent: (path: any) => void;
|
|
35
|
-
* }} ctx
|
|
39
|
+
* @param {PanelEventsCtx} ctx
|
|
36
40
|
*/
|
|
37
41
|
export function initPanelEvents(ctx) {
|
|
38
42
|
_ctx = ctx;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
/** @param {
|
|
45
|
+
/** @param {import("../canvas/canvas-render.js").CanvasPanel} panel */
|
|
42
46
|
export function registerPanelEvents(panel) {
|
|
43
|
-
const
|
|
47
|
+
const ctx = /** @type {PanelEventsCtx} */ (_ctx);
|
|
48
|
+
const canvas = /** @type {HTMLElement} */ (panel.canvas);
|
|
49
|
+
const overlayClk = /** @type {HTMLElement} */ (panel.overlayClk);
|
|
50
|
+
const { mediaName } = panel;
|
|
44
51
|
const ac = new AbortController();
|
|
45
52
|
const opts = { signal: ac.signal };
|
|
46
53
|
view.canvasEventCleanups.push(() => ac.abort());
|
|
47
54
|
|
|
48
|
-
/** @param {
|
|
55
|
+
/** @param {Function} fn */
|
|
49
56
|
function withPanelPointerEvents(fn) {
|
|
50
|
-
const els = canvas.querySelectorAll("*");
|
|
57
|
+
const els = /** @type {NodeListOf<HTMLElement>} */ (canvas.querySelectorAll("*"));
|
|
51
58
|
for (const el of els) el.style.pointerEvents = "auto";
|
|
52
59
|
overlayClk.style.display = "none";
|
|
53
60
|
const result = fn();
|
|
@@ -58,7 +65,7 @@ export function registerPanelEvents(panel) {
|
|
|
58
65
|
|
|
59
66
|
overlayClk.addEventListener(
|
|
60
67
|
"click",
|
|
61
|
-
(/** @type {
|
|
68
|
+
(/** @type {MouseEvent} */ e) => {
|
|
62
69
|
const barInner = view.blockActionBarEl?.firstElementChild;
|
|
63
70
|
if (barInner) {
|
|
64
71
|
const r = barInner.getBoundingClientRect();
|
|
@@ -75,10 +82,10 @@ export function registerPanelEvents(panel) {
|
|
|
75
82
|
}
|
|
76
83
|
|
|
77
84
|
const tab = activeTab.value;
|
|
78
|
-
const canvasMode =
|
|
85
|
+
const canvasMode = ctx.getCanvasMode();
|
|
79
86
|
|
|
80
|
-
const elements =
|
|
81
|
-
document.elementsFromPoint(e.clientX, e.clientY)
|
|
87
|
+
const elements = /** @type {Element[]} */ (
|
|
88
|
+
withPanelPointerEvents(() => document.elementsFromPoint(e.clientX, e.clientY))
|
|
82
89
|
);
|
|
83
90
|
|
|
84
91
|
if (!tab) return;
|
|
@@ -99,7 +106,9 @@ export function registerPanelEvents(panel) {
|
|
|
99
106
|
let path = bubbleInlinePath(tab.doc.document, originalPath);
|
|
100
107
|
const newMedia = mediaName === "base" ? null : (mediaName ?? null);
|
|
101
108
|
|
|
102
|
-
const resolvedEl =
|
|
109
|
+
const resolvedEl = /** @type {HTMLElement} */ (
|
|
110
|
+
path === originalPath ? el : findCanvasElement(path, canvas) || el
|
|
111
|
+
);
|
|
103
112
|
|
|
104
113
|
if (
|
|
105
114
|
pathsEqual(path, tab.session.selection) &&
|
|
@@ -107,7 +116,7 @@ export function registerPanelEvents(panel) {
|
|
|
107
116
|
(canvasMode === "edit" || tab.doc.mode === "content")
|
|
108
117
|
) {
|
|
109
118
|
tab.session.ui.activeMedia = newMedia;
|
|
110
|
-
|
|
119
|
+
ctx.enterInlineEdit(resolvedEl, path);
|
|
111
120
|
return;
|
|
112
121
|
}
|
|
113
122
|
|
|
@@ -131,7 +140,7 @@ export function registerPanelEvents(panel) {
|
|
|
131
140
|
|
|
132
141
|
overlayClk.addEventListener(
|
|
133
142
|
"dblclick",
|
|
134
|
-
(/** @type {
|
|
143
|
+
(/** @type {MouseEvent} */ e) => {
|
|
135
144
|
const barInner = view.blockActionBarEl?.firstElementChild;
|
|
136
145
|
if (barInner) {
|
|
137
146
|
const r = barInner.getBoundingClientRect();
|
|
@@ -143,12 +152,12 @@ export function registerPanelEvents(panel) {
|
|
|
143
152
|
)
|
|
144
153
|
return;
|
|
145
154
|
}
|
|
146
|
-
const canvasMode =
|
|
155
|
+
const canvasMode = ctx.getCanvasMode();
|
|
147
156
|
if (canvasMode !== "edit" && canvasMode !== "design") return;
|
|
148
157
|
|
|
149
158
|
const tab = activeTab.value;
|
|
150
|
-
const elements =
|
|
151
|
-
document.elementsFromPoint(e.clientX, e.clientY)
|
|
159
|
+
const elements = /** @type {Element[]} */ (
|
|
160
|
+
withPanelPointerEvents(() => document.elementsFromPoint(e.clientX, e.clientY))
|
|
152
161
|
);
|
|
153
162
|
|
|
154
163
|
if (!tab) return;
|
|
@@ -158,12 +167,14 @@ export function registerPanelEvents(panel) {
|
|
|
158
167
|
const originalPath = elToPath.get(el);
|
|
159
168
|
if (originalPath) {
|
|
160
169
|
const path = bubbleInlinePath(tab.doc.document, originalPath);
|
|
161
|
-
const resolvedEl =
|
|
170
|
+
const resolvedEl = /** @type {HTMLElement} */ (
|
|
171
|
+
path === originalPath ? el : findCanvasElement(path, canvas) || el
|
|
172
|
+
);
|
|
162
173
|
if (isEditableBlock(resolvedEl)) {
|
|
163
174
|
const newMedia = mediaName === "base" ? null : (mediaName ?? null);
|
|
164
175
|
tab.session.ui.activeMedia = newMedia;
|
|
165
176
|
tab.session.selection = path;
|
|
166
|
-
|
|
177
|
+
ctx.enterInlineEdit(resolvedEl, path);
|
|
167
178
|
return;
|
|
168
179
|
}
|
|
169
180
|
}
|
|
@@ -175,7 +186,7 @@ export function registerPanelEvents(panel) {
|
|
|
175
186
|
|
|
176
187
|
overlayClk.addEventListener(
|
|
177
188
|
"contextmenu",
|
|
178
|
-
(/** @type {
|
|
189
|
+
(/** @type {MouseEvent} */ e) => {
|
|
179
190
|
const barInner = view.blockActionBarEl?.firstElementChild;
|
|
180
191
|
if (barInner) {
|
|
181
192
|
const r = barInner.getBoundingClientRect();
|
|
@@ -188,15 +199,15 @@ export function registerPanelEvents(panel) {
|
|
|
188
199
|
return;
|
|
189
200
|
}
|
|
190
201
|
const tab = activeTab.value;
|
|
191
|
-
const elements =
|
|
192
|
-
document.elementsFromPoint(e.clientX, e.clientY)
|
|
202
|
+
const elements = /** @type {Element[]} */ (
|
|
203
|
+
withPanelPointerEvents(() => document.elementsFromPoint(e.clientX, e.clientY))
|
|
193
204
|
);
|
|
194
205
|
for (const el of elements) {
|
|
195
206
|
if (canvas.contains(el) && el !== canvas) {
|
|
196
207
|
let path = elToPath.get(el);
|
|
197
208
|
if (path) {
|
|
198
209
|
path = bubbleInlinePath(tab?.doc.document, path);
|
|
199
|
-
showContextMenu(e, path, { onEditComponent:
|
|
210
|
+
showContextMenu(e, path, { onEditComponent: ctx.navigateToComponent });
|
|
200
211
|
return;
|
|
201
212
|
}
|
|
202
213
|
}
|
|
@@ -208,7 +219,7 @@ export function registerPanelEvents(panel) {
|
|
|
208
219
|
|
|
209
220
|
overlayClk.addEventListener(
|
|
210
221
|
"mousemove",
|
|
211
|
-
(/** @type {
|
|
222
|
+
(/** @type {MouseEvent} */ e) => {
|
|
212
223
|
const barInner = view.blockActionBarEl?.firstElementChild;
|
|
213
224
|
if (barInner) {
|
|
214
225
|
const r = barInner.getBoundingClientRect();
|
|
@@ -222,7 +233,9 @@ export function registerPanelEvents(panel) {
|
|
|
222
233
|
}
|
|
223
234
|
const tab = activeTab.value;
|
|
224
235
|
if (!tab) return;
|
|
225
|
-
const el =
|
|
236
|
+
const el = /** @type {Element | null} */ (
|
|
237
|
+
withPanelPointerEvents(() => document.elementFromPoint(e.clientX, e.clientY))
|
|
238
|
+
);
|
|
226
239
|
if (el && canvas.contains(el) && el !== canvas) {
|
|
227
240
|
let path = elToPath.get(el);
|
|
228
241
|
if (path) {
|
|
@@ -252,7 +265,7 @@ export function registerPanelEvents(panel) {
|
|
|
252
265
|
);
|
|
253
266
|
|
|
254
267
|
insertionHelper.mount({
|
|
255
|
-
getCanvasMode:
|
|
268
|
+
getCanvasMode: ctx.getCanvasMode,
|
|
256
269
|
withPanelPointerEvents,
|
|
257
270
|
effectiveZoom: effectiveZoom,
|
|
258
271
|
defaultDef,
|
|
@@ -260,7 +273,9 @@ export function registerPanelEvents(panel) {
|
|
|
260
273
|
childIndex,
|
|
261
274
|
getNodeAtPath,
|
|
262
275
|
elToPath,
|
|
263
|
-
panel
|
|
276
|
+
panel: /** @type {import("../editor/insertion-helper.js").CanvasPanel} */ (
|
|
277
|
+
/** @type {unknown} */ (panel)
|
|
278
|
+
),
|
|
264
279
|
});
|
|
265
280
|
view.canvasEventCleanups.push(() => insertionHelper.unmount());
|
|
266
281
|
}
|
|
@@ -12,11 +12,11 @@ import { resolveDefaultForCanvas } from "../panels/signals-panel.js";
|
|
|
12
12
|
* Recursively render a Jx node to the canvas DOM. Media-aware: applies base styles + active
|
|
13
13
|
* breakpoint/feature overrides.
|
|
14
14
|
*
|
|
15
|
-
* @param {
|
|
16
|
-
* @param {
|
|
17
|
-
* @param {
|
|
18
|
-
* @param {
|
|
19
|
-
* @param {
|
|
15
|
+
* @param {JxMutableNode | string | number | boolean | null | undefined} node
|
|
16
|
+
* @param {JxPath} path
|
|
17
|
+
* @param {HTMLElement} parent
|
|
18
|
+
* @param {Set<string>} activeBreakpoints
|
|
19
|
+
* @param {Record<string, boolean>} featureToggles
|
|
20
20
|
*/
|
|
21
21
|
export function renderCanvasNode(node, path, parent, activeBreakpoints, featureToggles) {
|
|
22
22
|
if (typeof node === "string" || typeof node === "number" || typeof node === "boolean") {
|
|
@@ -32,15 +32,15 @@ export function renderCanvasNode(node, path, parent, activeBreakpoints, featureT
|
|
|
32
32
|
|
|
33
33
|
if (typeof node.textContent === "string") {
|
|
34
34
|
el.textContent = node.textContent;
|
|
35
|
-
} else if (typeof node.textContent === "object" && node.textContent
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
activeTab.value?.doc.document?.state
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
} else if (typeof node.textContent === "object" && node.textContent !== null) {
|
|
36
|
+
const tc = /** @type {Record<string, unknown>} */ (node.textContent);
|
|
37
|
+
if (tc.$ref) {
|
|
38
|
+
const resolved = resolveDefaultForCanvas(tc, activeTab.value?.doc.document?.state);
|
|
39
|
+
el.textContent = /** @type {string | null} */ (resolved);
|
|
40
|
+
el.style.opacity = "0.7";
|
|
41
|
+
el.style.fontStyle = "italic";
|
|
42
|
+
el.title = `Bound: ${tc.$ref}`;
|
|
43
|
+
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
if (node.id) el.id = node.id;
|
|
@@ -51,9 +51,13 @@ export function renderCanvasNode(node, path, parent, activeBreakpoints, featureT
|
|
|
51
51
|
if (node.attributes && typeof node.attributes === "object") {
|
|
52
52
|
for (const [attr, val] of Object.entries(node.attributes)) {
|
|
53
53
|
try {
|
|
54
|
-
if (
|
|
54
|
+
if (
|
|
55
|
+
typeof val === "object" &&
|
|
56
|
+
val !== null &&
|
|
57
|
+
/** @type {Record<string, unknown>} */ (val).$ref
|
|
58
|
+
) {
|
|
55
59
|
const resolved = resolveDefaultForCanvas(val, activeTab.value?.doc.document?.state);
|
|
56
|
-
el.setAttribute(attr, resolved);
|
|
60
|
+
el.setAttribute(attr, /** @type {string} */ (resolved));
|
|
57
61
|
} else {
|
|
58
62
|
el.setAttribute(attr, val);
|
|
59
63
|
}
|
|
@@ -74,9 +78,10 @@ export function renderCanvasNode(node, path, parent, activeBreakpoints, featureT
|
|
|
74
78
|
} else if (
|
|
75
79
|
node.children &&
|
|
76
80
|
typeof node.children === "object" &&
|
|
77
|
-
node.children.$prototype === "Array"
|
|
81
|
+
/** @type {Record<string, unknown>} */ (node.children).$prototype === "Array"
|
|
78
82
|
) {
|
|
79
|
-
const
|
|
83
|
+
const childrenObj = /** @type {Record<string, unknown>} */ (node.children);
|
|
84
|
+
const template = childrenObj.map;
|
|
80
85
|
if (template && typeof template === "object") {
|
|
81
86
|
const wrapper = document.createElement("div");
|
|
82
87
|
wrapper.className = "repeater-perimeter";
|