@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
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
buildScope,
|
|
14
14
|
defineElement,
|
|
15
15
|
setSkipServerFunctions,
|
|
16
|
+
setSkipContentResolution,
|
|
16
17
|
} from "@jxsuite/runtime";
|
|
17
18
|
import {
|
|
18
19
|
getEffectiveElements,
|
|
@@ -26,8 +27,21 @@ import {
|
|
|
26
27
|
import { componentRegistry, computeRelativePath } from "../files/components.js";
|
|
27
28
|
import { prepareForEditMode } from "../utils/edit-display.js";
|
|
28
29
|
import { getActiveElement } from "../editor/inline-edit.js";
|
|
30
|
+
import { buildNestedSiteCSS } from "./nested-site-style.js";
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
export { buildNestedSiteCSS } from "./nested-site-style.js";
|
|
33
|
+
|
|
34
|
+
/** @param {Event} e */
|
|
35
|
+
function _preventNav(e) {
|
|
36
|
+
if (/** @type {HTMLElement} */ (e.target).closest("a[href]")) {
|
|
37
|
+
e.preventDefault();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Canvas elements that already have the delegated nav guard listener. */
|
|
42
|
+
const _navGuarded = new WeakSet();
|
|
43
|
+
|
|
44
|
+
/** @type {{ getCanvasMode: () => string } | null} */
|
|
31
45
|
let _ctx = null;
|
|
32
46
|
|
|
33
47
|
/** Set of DOM elements that originated from the layout (not page content). */
|
|
@@ -44,9 +58,9 @@ let _failedElementsDocPath = null;
|
|
|
44
58
|
* the layout slot. Returns the path to the container whose children are the page content (first
|
|
45
59
|
* non-$__layout children array).
|
|
46
60
|
*
|
|
47
|
-
* @param {
|
|
48
|
-
* @param {
|
|
49
|
-
* @returns {
|
|
61
|
+
* @param {JxMutableNode} node
|
|
62
|
+
* @param {(string | number)[]} [path]
|
|
63
|
+
* @returns {(string | number)[] | null}
|
|
50
64
|
*/
|
|
51
65
|
function findPageContentPrefix(node, path = []) {
|
|
52
66
|
if (!node || typeof node !== "object") return null;
|
|
@@ -75,14 +89,19 @@ export let activeLayoutPath = /** @type {string | null} */ (null);
|
|
|
75
89
|
* Recursively mark all nodes in a layout doc tree with $__layout: true so we can identify which
|
|
76
90
|
* rendered DOM elements came from the layout vs page content.
|
|
77
91
|
*/
|
|
78
|
-
function markLayoutNodes(/** @type {
|
|
92
|
+
function markLayoutNodes(/** @type {JxMutableNode} */ node) {
|
|
79
93
|
if (!node || typeof node !== "object") return;
|
|
80
94
|
node.$__layout = true;
|
|
81
95
|
if (Array.isArray(node.children)) {
|
|
82
|
-
for (const child of node.children)
|
|
96
|
+
for (const child of node.children) {
|
|
97
|
+
if (typeof child === "string") continue;
|
|
98
|
+
markLayoutNodes(child);
|
|
99
|
+
}
|
|
83
100
|
}
|
|
84
101
|
if (node.$elements) {
|
|
85
|
-
for (const el of node.$elements)
|
|
102
|
+
for (const el of node.$elements) {
|
|
103
|
+
if (typeof el !== "string") markLayoutNodes(el);
|
|
104
|
+
}
|
|
86
105
|
}
|
|
87
106
|
}
|
|
88
107
|
|
|
@@ -103,13 +122,13 @@ export function initCanvasLiveRender(ctx) {
|
|
|
103
122
|
* failure.
|
|
104
123
|
*
|
|
105
124
|
* @param {number} gen - Render generation for staleness detection
|
|
106
|
-
* @param {
|
|
107
|
-
* @param {
|
|
125
|
+
* @param {JxMutableNode} doc
|
|
126
|
+
* @param {HTMLElement} canvasEl
|
|
108
127
|
*/
|
|
109
128
|
export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
110
129
|
const tab = activeTab.value;
|
|
111
130
|
const S = { documentPath: tab?.documentPath, mode: tab?.doc.mode, document: tab?.doc.document };
|
|
112
|
-
const canvasMode = _ctx.getCanvasMode();
|
|
131
|
+
const canvasMode = /** @type {{ getCanvasMode: () => string }} */ (_ctx).getCanvasMode();
|
|
113
132
|
|
|
114
133
|
if (S.documentPath !== _failedElementsDocPath) {
|
|
115
134
|
_failedElements.clear();
|
|
@@ -120,6 +139,7 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
120
139
|
// failed proxy calls and infinite reactive retries (also covers
|
|
121
140
|
// async custom element connectedCallbacks that run after this function returns)
|
|
122
141
|
setSkipServerFunctions(canvasMode !== "preview");
|
|
142
|
+
setSkipContentResolution(canvasMode !== "preview");
|
|
123
143
|
|
|
124
144
|
let renderDoc =
|
|
125
145
|
canvasMode === "preview"
|
|
@@ -137,7 +157,7 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
137
157
|
projectState?.isSiteProject &&
|
|
138
158
|
(S.documentPath.startsWith("pages/") || S.documentPath.startsWith("./pages/"));
|
|
139
159
|
|
|
140
|
-
/** @type {
|
|
160
|
+
/** @type {(string | number)[] | null} Path prefix in merged doc where page children live */
|
|
141
161
|
let pageContentPrefix = null;
|
|
142
162
|
|
|
143
163
|
if (isPage) {
|
|
@@ -162,18 +182,23 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
162
182
|
// so we can remap runtime paths (children,0,...) → (children,map,...)
|
|
163
183
|
const mapParentPaths = new Set();
|
|
164
184
|
if (canvasMode === "design" || canvasMode === "edit") {
|
|
165
|
-
(function findMapParents(
|
|
185
|
+
(function findMapParents(
|
|
186
|
+
/** @type {JxMutableNode} */ node,
|
|
187
|
+
/** @type {(string | number)[]} */ path,
|
|
188
|
+
) {
|
|
166
189
|
if (!node || typeof node !== "object") return;
|
|
167
190
|
if (
|
|
168
191
|
node.children &&
|
|
169
192
|
typeof node.children === "object" &&
|
|
170
|
-
node.children.$prototype === "Array"
|
|
193
|
+
/** @type {{ $prototype?: string }} */ (node.children).$prototype === "Array"
|
|
171
194
|
) {
|
|
172
195
|
mapParentPaths.add(path.join("/"));
|
|
173
196
|
}
|
|
174
197
|
if (Array.isArray(node.children)) {
|
|
175
198
|
for (let i = 0; i < node.children.length; i++) {
|
|
176
|
-
|
|
199
|
+
const child = node.children[i];
|
|
200
|
+
if (typeof child === "string") continue;
|
|
201
|
+
findMapParents(child, [...path, "children", i]);
|
|
177
202
|
}
|
|
178
203
|
}
|
|
179
204
|
if (node.$switch && node.cases) {
|
|
@@ -190,15 +215,19 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
190
215
|
const docBase = S.documentPath ? `${location.origin}/${docPrefix}${S.documentPath}` : undefined;
|
|
191
216
|
|
|
192
217
|
// Register custom elements so the runtime can render them
|
|
193
|
-
let effectiveElements = getEffectiveElements(
|
|
218
|
+
let effectiveElements = getEffectiveElements(
|
|
219
|
+
/** @type {(JxElement | string)[]} */ (renderDoc.$elements),
|
|
220
|
+
);
|
|
194
221
|
|
|
195
222
|
// In content mode (markdown), auto-discover components for directive-based
|
|
196
223
|
// custom elements that have no explicit $elements registration.
|
|
197
224
|
if (S.mode === "content" && componentRegistry.length > 0) {
|
|
198
225
|
const existingRefs = new Set(
|
|
199
|
-
effectiveElements.map((/** @type {
|
|
226
|
+
effectiveElements.map((/** @type {JxElement | string} */ e) =>
|
|
227
|
+
typeof e === "string" ? e : e?.$ref,
|
|
228
|
+
),
|
|
200
229
|
);
|
|
201
|
-
/** @param {
|
|
230
|
+
/** @param {JxMutableNode} node */
|
|
202
231
|
const collectTags = (node) => {
|
|
203
232
|
/** @type {Set<string>} */
|
|
204
233
|
const tags = new Set();
|
|
@@ -206,13 +235,16 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
206
235
|
if (node.tagName) tags.add(node.tagName);
|
|
207
236
|
if (Array.isArray(node.children)) {
|
|
208
237
|
for (const child of node.children) {
|
|
238
|
+
if (typeof child === "string") continue;
|
|
209
239
|
for (const t of collectTags(child)) tags.add(t);
|
|
210
240
|
}
|
|
211
241
|
}
|
|
212
242
|
return tags;
|
|
213
243
|
};
|
|
214
244
|
for (const tag of collectTags(renderDoc)) {
|
|
215
|
-
const comp = componentRegistry.find(
|
|
245
|
+
const comp = componentRegistry.find(
|
|
246
|
+
(/** @type {import("../files/components.js").ComponentEntry} */ c) => c.tagName === tag,
|
|
247
|
+
);
|
|
216
248
|
if (comp && comp.source !== "npm") {
|
|
217
249
|
const relPath = computeRelativePath(S.documentPath, comp.path);
|
|
218
250
|
if (!existingRefs.has(relPath)) {
|
|
@@ -224,28 +256,30 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
224
256
|
}
|
|
225
257
|
|
|
226
258
|
if (effectiveElements.length) {
|
|
227
|
-
renderDoc.$elements =
|
|
259
|
+
renderDoc.$elements = /** @type {(JxMutableNode | string | { $ref: string })[]} */ (
|
|
260
|
+
/** @type {unknown} */ (effectiveElements)
|
|
261
|
+
);
|
|
228
262
|
for (const entry of effectiveElements) {
|
|
229
263
|
if (typeof entry === "string") {
|
|
230
264
|
try {
|
|
231
265
|
const specifier =
|
|
232
266
|
entry.startsWith("/") || entry.startsWith(".") ? entry : `/node_modules/${entry}`;
|
|
233
267
|
await import(specifier);
|
|
234
|
-
} catch (/** @type {
|
|
268
|
+
} catch (/** @type {unknown} */ e) {
|
|
235
269
|
console.warn("Studio: failed to import package", entry, e);
|
|
236
270
|
}
|
|
237
271
|
} else if (entry?.$ref) {
|
|
238
272
|
let href;
|
|
239
273
|
try {
|
|
240
274
|
href = new URL(entry.$ref, docBase).href;
|
|
241
|
-
} catch (/** @type {
|
|
275
|
+
} catch (/** @type {unknown} */ urlErr) {
|
|
242
276
|
console.warn("Studio: invalid element URL", { ref: entry.$ref, docBase }, urlErr);
|
|
243
277
|
continue;
|
|
244
278
|
}
|
|
245
279
|
if (_failedElements.has(href)) continue;
|
|
246
280
|
try {
|
|
247
281
|
await defineElement(href);
|
|
248
|
-
} catch (/** @type {
|
|
282
|
+
} catch (/** @type {unknown} */ e) {
|
|
249
283
|
_failedElements.add(href);
|
|
250
284
|
console.warn("Studio: failed to register element", entry.$ref, e);
|
|
251
285
|
}
|
|
@@ -265,17 +299,22 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
265
299
|
// This ensures project font-family, color, etc. override the
|
|
266
300
|
// content-mode fallback typography rules in the stylesheet.
|
|
267
301
|
// In edit mode, propagate to the .content-edit-canvas wrapper for seamless appearance.
|
|
268
|
-
const viewport = canvasEl.closest(".canvas-panel-viewport");
|
|
269
|
-
const editSurface =
|
|
302
|
+
const viewport = /** @type {HTMLElement | null} */ (canvasEl.closest(".canvas-panel-viewport"));
|
|
303
|
+
const editSurface =
|
|
304
|
+
canvasMode === "edit"
|
|
305
|
+
? /** @type {HTMLElement | null} */ (canvasEl.closest(".content-edit-canvas"))
|
|
306
|
+
: null;
|
|
270
307
|
const siteStyle = projectState?.projectConfig?.style;
|
|
271
308
|
if (viewport) {
|
|
272
309
|
viewport.style.cssText = "";
|
|
273
310
|
if (siteStyle && typeof siteStyle === "object") {
|
|
274
311
|
for (const [k, v] of Object.entries(siteStyle)) {
|
|
312
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v)) continue;
|
|
275
313
|
if (k.startsWith("--")) {
|
|
276
314
|
viewport.style.setProperty(k, String(v));
|
|
277
315
|
} else {
|
|
278
|
-
/** @type {
|
|
316
|
+
/** @type {Record<string, string>} */ (/** @type {unknown} */ (viewport.style))[k] =
|
|
317
|
+
String(v);
|
|
279
318
|
}
|
|
280
319
|
}
|
|
281
320
|
}
|
|
@@ -283,22 +322,42 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
283
322
|
if (editSurface) {
|
|
284
323
|
if (siteStyle && typeof siteStyle === "object") {
|
|
285
324
|
for (const [k, v] of Object.entries(siteStyle)) {
|
|
325
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v)) continue;
|
|
286
326
|
if (k.startsWith("--")) {
|
|
287
|
-
|
|
327
|
+
editSurface.style.setProperty(k, String(v));
|
|
288
328
|
} else {
|
|
289
|
-
/** @type {
|
|
329
|
+
/** @type {Record<string, string>} */ (/** @type {unknown} */ (editSurface.style))[k] =
|
|
330
|
+
String(v);
|
|
290
331
|
}
|
|
291
332
|
}
|
|
292
333
|
}
|
|
293
334
|
}
|
|
294
335
|
if (siteStyle && typeof siteStyle === "object") {
|
|
295
336
|
for (const [k, v] of Object.entries(siteStyle)) {
|
|
337
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v)) continue;
|
|
296
338
|
if (!k.startsWith("--")) {
|
|
297
|
-
/** @type {
|
|
339
|
+
/** @type {Record<string, string>} */ (/** @type {unknown} */ (canvasEl.style))[k] =
|
|
340
|
+
String(v);
|
|
298
341
|
}
|
|
299
342
|
}
|
|
300
343
|
}
|
|
301
344
|
|
|
345
|
+
// Generate a <style> tag for nested selector rules (e.g. table, thead, etc.)
|
|
346
|
+
if (siteStyle && typeof siteStyle === "object") {
|
|
347
|
+
const scopeAttr = `data-jx-site`;
|
|
348
|
+
canvasEl.setAttribute(scopeAttr, "");
|
|
349
|
+
const css = buildNestedSiteCSS(siteStyle, `[${scopeAttr}]`);
|
|
350
|
+
|
|
351
|
+
if (css) {
|
|
352
|
+
const existingStyleEl = document.getElementById("jx-site-style");
|
|
353
|
+
if (existingStyleEl) existingStyleEl.remove();
|
|
354
|
+
const styleEl = document.createElement("style");
|
|
355
|
+
styleEl.id = "jx-site-style";
|
|
356
|
+
styleEl.textContent = css;
|
|
357
|
+
document.head.appendChild(styleEl);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
302
361
|
// Inject site-level $media so runtime can resolve media queries in styles
|
|
303
362
|
renderDoc.$media = getEffectiveMedia(renderDoc.$media);
|
|
304
363
|
|
|
@@ -310,13 +369,14 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
310
369
|
const tag = entry.tagName.toLowerCase();
|
|
311
370
|
const attrs = { ...entry.attributes };
|
|
312
371
|
for (const key of ["href", "src"]) {
|
|
372
|
+
const val = attrs[key];
|
|
313
373
|
if (
|
|
314
|
-
|
|
315
|
-
!
|
|
316
|
-
!
|
|
317
|
-
!
|
|
374
|
+
typeof val === "string" &&
|
|
375
|
+
!val.startsWith("/") &&
|
|
376
|
+
!val.startsWith(".") &&
|
|
377
|
+
!val.startsWith("http")
|
|
318
378
|
) {
|
|
319
|
-
attrs[key] = `/node_modules/${
|
|
379
|
+
attrs[key] = `/node_modules/${val}`;
|
|
320
380
|
}
|
|
321
381
|
}
|
|
322
382
|
const selector = `${tag}${attrs.href ? `[href="${attrs.href}"]` : ""}${attrs.src ? `[src="${attrs.src}"]` : ""}`;
|
|
@@ -333,7 +393,12 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
333
393
|
if (gen !== view.renderGeneration) return null;
|
|
334
394
|
const el = /** @type {HTMLElement} */ (
|
|
335
395
|
runtimeRenderNode(renderDoc, $defs, {
|
|
336
|
-
onNodeCreated(
|
|
396
|
+
onNodeCreated(
|
|
397
|
+
/** @type {HTMLElement | Text} */ el,
|
|
398
|
+
/** @type {(string | number)[]} */ path,
|
|
399
|
+
/** @type {Record<string, unknown>} */ def,
|
|
400
|
+
) {
|
|
401
|
+
if (!(el instanceof HTMLElement)) return;
|
|
337
402
|
// Track layout-originated elements — don't store in elToPath to avoid
|
|
338
403
|
// path collisions with remapped page content paths
|
|
339
404
|
if (layoutWrapped && def?.$__layout) {
|
|
@@ -349,7 +414,9 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
349
414
|
const pfx = pageContentPrefix;
|
|
350
415
|
if (
|
|
351
416
|
path.length >= pfx.length &&
|
|
352
|
-
pfx.every(
|
|
417
|
+
pfx.every(
|
|
418
|
+
(/** @type {string | number} */ seg, /** @type {number} */ i) => path[i] === seg,
|
|
419
|
+
)
|
|
353
420
|
) {
|
|
354
421
|
mappedPath = ["children", ...path.slice(pfx.length)];
|
|
355
422
|
}
|
|
@@ -391,7 +458,7 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
391
458
|
// Disable pointer events on all rendered elements for edit mode
|
|
392
459
|
el.style.pointerEvents = "none";
|
|
393
460
|
for (const child of el.querySelectorAll("*")) {
|
|
394
|
-
/** @type {
|
|
461
|
+
/** @type {HTMLElement} */ (child).style.pointerEvents = "none";
|
|
395
462
|
}
|
|
396
463
|
}
|
|
397
464
|
// Clear and append atomically — ensures the canvas is never left empty if a
|
|
@@ -405,19 +472,26 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
405
472
|
|
|
406
473
|
canvasEl.appendChild(el);
|
|
407
474
|
|
|
475
|
+
// Delegated click handler prevents link navigation in all canvas modes.
|
|
476
|
+
// Attached once per canvasEl (survives reactive re-renders that replace children).
|
|
477
|
+
if (!_navGuarded.has(canvasEl)) {
|
|
478
|
+
canvasEl.addEventListener("click", _preventNav);
|
|
479
|
+
_navGuarded.add(canvasEl);
|
|
480
|
+
}
|
|
481
|
+
|
|
408
482
|
if (canvasMode === "design" || canvasMode === "edit") {
|
|
409
483
|
requestAnimationFrame(() => {
|
|
410
484
|
const editingEl = getActiveElement();
|
|
411
485
|
for (const child of canvasEl.querySelectorAll("*")) {
|
|
412
486
|
if (view.componentInlineEdit && child === view.componentInlineEdit.el) continue;
|
|
413
487
|
if (editingEl && child === editingEl) continue;
|
|
414
|
-
/** @type {
|
|
488
|
+
/** @type {HTMLElement} */ (child).style.pointerEvents = "none";
|
|
415
489
|
}
|
|
416
490
|
});
|
|
417
491
|
}
|
|
418
492
|
return $defs;
|
|
419
|
-
} catch (/** @type {
|
|
420
|
-
console.warn("renderCanvasLive failed:", err.message, err);
|
|
493
|
+
} catch (/** @type {unknown} */ err) {
|
|
494
|
+
console.warn("renderCanvasLive failed:", /** @type {Error} */ (err).message, err);
|
|
421
495
|
return null;
|
|
422
496
|
}
|
|
423
497
|
}
|