@jxsuite/studio 0.19.0 → 0.21.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 +13476 -12330
- package/dist/studio.js.map +152 -148
- 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 +19 -7
- package/src/browse/browse-modal.js +16 -13
- package/src/browse/browse.js +44 -35
- package/src/canvas/canvas-diff.js +24 -16
- package/src/canvas/canvas-helpers.js +24 -19
- package/src/canvas/canvas-live-render.js +113 -39
- package/src/canvas/canvas-render.js +119 -73
- package/src/canvas/canvas-utils.js +51 -21
- 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 +28 -14
- package/src/editor/convert-targets.js +60 -0
- package/src/editor/convert-to-component.js +20 -19
- package/src/editor/convert-to-repeater.js +226 -0
- package/src/editor/inline-edit.js +69 -28
- package/src/editor/insertion-helper.js +16 -12
- package/src/editor/shortcuts.js +14 -4
- package/src/editor/slash-menu.js +40 -19
- package/src/files/components.js +1 -1
- package/src/files/file-ops.js +37 -16
- package/src/files/files.js +322 -68
- package/src/github/github-auth.js +122 -0
- package/src/github/github-publish.js +126 -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 +27 -25
- package/src/panels/ai-panel.js +406 -0
- package/src/panels/block-action-bar.js +77 -28
- 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 +475 -99
- package/src/panels/head-panel.js +121 -81
- package/src/panels/imports-panel.js +94 -65
- package/src/panels/layers-panel.js +64 -43
- package/src/panels/left-panel.js +105 -48
- 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 +378 -257
- package/src/panels/pseudo-preview.js +2 -2
- package/src/panels/quick-search.js +11 -9
- package/src/panels/right-panel.js +42 -16
- package/src/panels/shared.js +4 -4
- package/src/panels/signals-panel.js +415 -166
- package/src/panels/statusbar.js +10 -6
- package/src/panels/style-inputs.js +67 -35
- package/src/panels/style-panel.js +239 -134
- 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/tab-strip.js +5 -2
- 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 +8 -8
- 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/settings/settings-modal.js +12 -9
- package/src/site-context.js +41 -31
- package/src/state.js +70 -29
- package/src/store.js +36 -35
- package/src/studio.js +120 -90
- 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/layers.js +31 -1
- 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 +32 -26
- package/src/utils/google-fonts.js +11 -11
- package/src/utils/inherited-style.js +9 -8
- package/src/utils/studio-utils.js +18 -18
- package/src/view.js +58 -1
- package/src/workspace/workspace.js +29 -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,9 @@ import { selectStylebookTag, stylebookMeta } from "./stylebook-panel.js";
|
|
|
40
40
|
* registerElementsDnD: () => void;
|
|
41
41
|
* registerComponentsDnD: () => void;
|
|
42
42
|
* setupTreeKeyboard: (tree: HTMLElement) => void;
|
|
43
|
-
*
|
|
43
|
+
* registerFileTreeDnD: (ctx: { renderLeftPanel: () => void }) => void;
|
|
44
|
+
* setGitDiffState: (state: import("../canvas/canvas-render.js").GitDiffState | null) => void;
|
|
45
|
+
* cloneRepository?: () => void;
|
|
44
46
|
* }} LeftPanelCtx
|
|
45
47
|
*/
|
|
46
48
|
|
|
@@ -52,6 +54,16 @@ let _scope = null;
|
|
|
52
54
|
|
|
53
55
|
let _rendering = false;
|
|
54
56
|
let _scheduled = false;
|
|
57
|
+
let _hasFocus = false;
|
|
58
|
+
|
|
59
|
+
function _onFocusIn() {
|
|
60
|
+
_hasFocus = true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function _onFocusOut() {
|
|
64
|
+
_hasFocus = false;
|
|
65
|
+
render();
|
|
66
|
+
}
|
|
55
67
|
|
|
56
68
|
/**
|
|
57
69
|
* Mount the left panel orchestrator.
|
|
@@ -60,20 +72,25 @@ let _scheduled = false;
|
|
|
60
72
|
*/
|
|
61
73
|
export function mount(ctx) {
|
|
62
74
|
_ctx = ctx;
|
|
75
|
+
leftPanel.addEventListener("focusin", _onFocusIn);
|
|
76
|
+
leftPanel.addEventListener("focusout", _onFocusOut);
|
|
63
77
|
_scope = effectScope();
|
|
64
78
|
_scope.run(() => {
|
|
65
79
|
effect(() => {
|
|
66
80
|
const tab = activeTab.value;
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
if (tab) {
|
|
82
|
+
// Track properties the left panel reads
|
|
83
|
+
void tab.doc.document;
|
|
84
|
+
void tab.doc.mode;
|
|
85
|
+
void tab.session.selection;
|
|
86
|
+
void tab.session.ui.settingsTab;
|
|
87
|
+
void tab.session.ui.gitStatus;
|
|
88
|
+
void tab.session.ui.gitLoading;
|
|
89
|
+
void tab.session.ui.gitError;
|
|
90
|
+
}
|
|
91
|
+
if (!_hasFocus) {
|
|
92
|
+
render();
|
|
93
|
+
}
|
|
77
94
|
});
|
|
78
95
|
});
|
|
79
96
|
}
|
|
@@ -82,6 +99,9 @@ export function unmount() {
|
|
|
82
99
|
_scope?.stop();
|
|
83
100
|
_scope = null;
|
|
84
101
|
_ctx = null;
|
|
102
|
+
leftPanel.removeEventListener("focusin", _onFocusIn);
|
|
103
|
+
leftPanel.removeEventListener("focusout", _onFocusOut);
|
|
104
|
+
_hasFocus = false;
|
|
85
105
|
}
|
|
86
106
|
|
|
87
107
|
export function render() {
|
|
@@ -122,26 +142,78 @@ function _flush() {
|
|
|
122
142
|
|
|
123
143
|
function _render() {
|
|
124
144
|
const ctx = /** @type {LeftPanelCtx} */ (_ctx);
|
|
145
|
+
const tab = view.leftTab;
|
|
146
|
+
|
|
147
|
+
// ── Project-level panels: render based on projectState, independent of active tab ──
|
|
148
|
+
|
|
149
|
+
if (tab === "files") {
|
|
150
|
+
litRender(html`<div class="panel-body">${ctx.renderFilesTemplate()}</div>`, leftPanel);
|
|
151
|
+
const tree = /** @type {HTMLElement | null} */ (leftPanel.querySelector(".file-tree"));
|
|
152
|
+
if (tree) ctx.setupTreeKeyboard(tree);
|
|
153
|
+
ctx.registerFileTreeDnD({ renderLeftPanel: render });
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (tab === "git") {
|
|
158
|
+
const aTab = activeTab.value;
|
|
159
|
+
const S = aTab ? { ui: aTab.session.ui } : { ui: {} };
|
|
160
|
+
litRender(html`<div class="panel-body">${ctx.renderGitPanel(S, ctx)}</div>`, leftPanel);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (tab === "blocks") {
|
|
165
|
+
const content = renderElementsTemplate(
|
|
166
|
+
/** @type {Parameters<typeof renderElementsTemplate>[0]} */ ({
|
|
167
|
+
webdata: ctx.webdata,
|
|
168
|
+
defaultDef: ctx.defaultDef,
|
|
169
|
+
rerender: render,
|
|
170
|
+
}),
|
|
171
|
+
);
|
|
172
|
+
litRender(html`<div class="panel-body">${content}</div>`, leftPanel);
|
|
173
|
+
ctx.registerElementsDnD();
|
|
174
|
+
ctx.registerComponentsDnD();
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ── Document-level panels: require an active tab ──
|
|
179
|
+
|
|
125
180
|
const aTab = activeTab.value;
|
|
126
|
-
if (!aTab)
|
|
127
|
-
|
|
181
|
+
if (!aTab) {
|
|
182
|
+
litRender(html`<div class="panel-body"></div>`, leftPanel);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const S = /**
|
|
187
|
+
* @type {{
|
|
188
|
+
* ui: unknown;
|
|
189
|
+
* document: JxMutableNode;
|
|
190
|
+
* mode: string;
|
|
191
|
+
* selection: JxPath | null;
|
|
192
|
+
* canvas: { scope?: object } | null;
|
|
193
|
+
* content?: { frontmatter?: Record<string, unknown> };
|
|
194
|
+
* documentPath?: string;
|
|
195
|
+
* }}
|
|
196
|
+
*/ ({
|
|
128
197
|
ui: aTab.session.ui,
|
|
129
198
|
document: aTab.doc.document,
|
|
130
199
|
mode: aTab.doc.mode,
|
|
131
200
|
selection: aTab.session.selection,
|
|
132
201
|
canvas: aTab.session.canvas,
|
|
202
|
+
content: aTab.doc.content,
|
|
203
|
+
documentPath: aTab.documentPath,
|
|
133
204
|
});
|
|
134
|
-
const tab = view.leftTab;
|
|
135
205
|
|
|
136
206
|
/** @type {TemplateResult | typeof nothing} */
|
|
137
207
|
let content;
|
|
138
208
|
if (tab === "layers")
|
|
139
209
|
content =
|
|
140
210
|
ctx.getCanvasMode() === "stylebook"
|
|
141
|
-
? renderStylebookLayersTemplate(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
211
|
+
? renderStylebookLayersTemplate(
|
|
212
|
+
/** @type {Parameters<typeof renderStylebookLayersTemplate>[0]} */ ({
|
|
213
|
+
selectStylebookTag,
|
|
214
|
+
stylebookMeta,
|
|
215
|
+
}),
|
|
216
|
+
)
|
|
145
217
|
: renderLayersTemplate({
|
|
146
218
|
navigateToComponent: ctx.navigateToComponent,
|
|
147
219
|
rerender: render,
|
|
@@ -155,13 +227,6 @@ function _render() {
|
|
|
155
227
|
transact(activeTab.value, fn);
|
|
156
228
|
},
|
|
157
229
|
});
|
|
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
230
|
else if (tab === "state")
|
|
166
231
|
content = ctx.renderSignalsTemplate(S, {
|
|
167
232
|
renderLeftPanel: render,
|
|
@@ -184,33 +249,25 @@ function _render() {
|
|
|
184
249
|
applyMutation: isContent
|
|
185
250
|
? (/** @type {(doc: object) => void} */ fn) => {
|
|
186
251
|
const tab = activeTab.value;
|
|
187
|
-
const fm = /** @type {Record<string,
|
|
188
|
-
const
|
|
252
|
+
const fm = /** @type {Record<string, unknown>} */ (tab.doc.content?.frontmatter ?? {});
|
|
253
|
+
const fmHead = /** @type {unknown[] | undefined} */ (fm.$head);
|
|
254
|
+
const tmp = { title: fm.title, $head: fmHead ? [...fmHead] : undefined };
|
|
189
255
|
fn(tmp);
|
|
190
256
|
if (tmp.title !== fm.title)
|
|
191
|
-
mutateUpdateFrontmatter(tab, "title", /** @type {
|
|
257
|
+
mutateUpdateFrontmatter(tab, "title", /** @type {JsonValue} */ (tmp.title));
|
|
192
258
|
const newHead = tmp.$head && tmp.$head.length > 0 ? tmp.$head : undefined;
|
|
193
|
-
mutateUpdateFrontmatter(tab, "$head", newHead);
|
|
259
|
+
mutateUpdateFrontmatter(tab, "$head", /** @type {JsonValue} */ (newHead));
|
|
260
|
+
render();
|
|
194
261
|
}
|
|
195
262
|
: (/** @type {(doc: object) => void} */ fn) => {
|
|
196
263
|
transact(activeTab.value, fn);
|
|
197
264
|
},
|
|
198
265
|
renderLeftPanel: render,
|
|
199
266
|
});
|
|
200
|
-
} else
|
|
201
|
-
else content = nothing;
|
|
267
|
+
} else content = nothing;
|
|
202
268
|
|
|
203
269
|
litRender(html`<div class="panel-body">${content}</div>`, leftPanel);
|
|
204
270
|
|
|
205
271
|
// Post-render side effects
|
|
206
272
|
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
273
|
}
|
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";
|