@jxsuite/studio 0.28.2 → 0.28.4
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.js +86144 -83971
- package/dist/studio.js.map +229 -219
- package/package.json +5 -5
- package/src/browse/browse-modal.ts +9 -3
- package/src/browse/browse.ts +139 -82
- package/src/canvas/canvas-diff.ts +35 -18
- package/src/canvas/canvas-helpers.ts +55 -21
- package/src/canvas/canvas-live-render.ts +140 -89
- package/src/canvas/canvas-render.ts +127 -82
- package/src/canvas/canvas-utils.ts +68 -42
- package/src/canvas/nested-site-style.ts +15 -7
- package/src/editor/component-inline-edit.ts +90 -51
- package/src/editor/content-inline-edit.ts +75 -72
- package/src/editor/context-menu.ts +125 -71
- package/src/editor/convert-targets.ts +17 -13
- package/src/editor/convert-to-component.ts +51 -28
- package/src/editor/convert-to-repeater.ts +44 -19
- package/src/editor/inline-edit.ts +107 -52
- package/src/editor/inline-format.ts +128 -60
- package/src/editor/insertion-helper.ts +26 -14
- package/src/editor/shortcuts.ts +89 -43
- package/src/editor/slash-menu.ts +41 -26
- package/src/files/components.ts +9 -7
- package/src/files/file-ops.ts +53 -33
- package/src/files/files.ts +246 -143
- package/src/format/constraints.ts +25 -15
- package/src/format/format-host.ts +33 -10
- package/src/github/github-auth.ts +24 -14
- package/src/github/github-publish.ts +20 -14
- package/src/new-project/new-project-modal.ts +27 -18
- package/src/panels/activity-bar.ts +27 -26
- package/src/panels/ai-panel.ts +100 -44
- package/src/panels/block-action-bar.ts +84 -52
- package/src/panels/canvas-dnd.ts +47 -28
- package/src/panels/data-explorer.ts +24 -11
- package/src/panels/dnd.ts +148 -100
- package/src/panels/editors.ts +54 -37
- package/src/panels/elements-panel.ts +23 -13
- package/src/panels/events-panel.ts +48 -38
- package/src/panels/git-panel.ts +95 -60
- package/src/panels/head-panel.ts +138 -84
- package/src/panels/imports-panel.ts +66 -29
- package/src/panels/layers-panel.ts +80 -36
- package/src/panels/left-panel.ts +86 -53
- package/src/panels/overlays.ts +36 -19
- package/src/panels/panel-events.ts +48 -27
- package/src/panels/panel-scheduler.ts +41 -23
- package/src/panels/preview-render.ts +26 -26
- package/src/panels/properties-panel.ts +155 -118
- package/src/panels/pseudo-preview.ts +31 -15
- package/src/panels/quick-search.ts +28 -13
- package/src/panels/right-panel.ts +35 -24
- package/src/panels/shared.ts +46 -29
- package/src/panels/signals-panel.ts +303 -186
- package/src/panels/statusbar.ts +27 -12
- package/src/panels/style-inputs.ts +30 -24
- package/src/panels/style-panel.ts +130 -90
- package/src/panels/style-utils.ts +63 -23
- package/src/panels/stylebook-layers-panel.ts +27 -30
- package/src/panels/stylebook-panel.ts +140 -72
- package/src/panels/tab-strip.ts +21 -10
- package/src/panels/toolbar.ts +52 -40
- package/src/panels/welcome-screen.ts +1 -1
- package/src/platform.ts +2 -1
- package/src/platforms/devserver.ts +221 -105
- package/src/recent-projects.ts +15 -7
- package/src/resize-edges.ts +7 -5
- package/src/services/cem-export.ts +73 -57
- package/src/services/code-services.ts +52 -25
- package/src/services/monaco-setup.ts +5 -5
- package/src/settings/content-types-editor.ts +133 -62
- package/src/settings/css-vars-editor.ts +31 -14
- package/src/settings/defs-editor.ts +125 -63
- package/src/settings/general-settings.ts +11 -6
- package/src/settings/head-editor.ts +30 -15
- package/src/settings/schema-field-ui.ts +75 -35
- package/src/settings/settings-modal.ts +35 -17
- package/src/site-context.ts +80 -47
- package/src/state.ts +98 -65
- package/src/store.ts +42 -23
- package/src/studio.ts +165 -144
- package/src/tabs/tab.ts +51 -45
- package/src/tabs/transact.ts +230 -109
- package/src/types.ts +60 -51
- package/src/ui/button-group.ts +3 -2
- package/src/ui/color-selector.ts +33 -15
- package/src/ui/expression-editor.ts +139 -86
- package/src/ui/field-input.ts +35 -18
- package/src/ui/field-row.ts +1 -1
- package/src/ui/icons.ts +5 -6
- package/src/ui/layers.ts +33 -26
- package/src/ui/media-picker.ts +13 -7
- package/src/ui/panel-resize.ts +39 -20
- package/src/ui/spectrum.ts +4 -2
- package/src/ui/unit-selector.ts +20 -11
- package/src/ui/value-selector.ts +13 -7
- package/src/ui/widgets.ts +34 -18
- package/src/utils/canvas-media.ts +66 -33
- package/src/utils/edit-display.ts +43 -44
- package/src/utils/google-fonts.ts +10 -6
- package/src/utils/inherited-style.ts +22 -8
- package/src/utils/studio-utils.ts +88 -45
- package/src/view.ts +9 -5
- package/src/workspace/workspace.ts +49 -29
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { html, render as litRender, nothing } from "lit-html";
|
|
8
|
+
import { errorMessage } from "@jxsuite/schema/parse";
|
|
8
9
|
import { ref } from "lit-html/directives/ref.js";
|
|
9
10
|
import * as monaco from "monaco-editor/esm/vs/editor/editor.api.js";
|
|
10
11
|
|
|
11
|
-
import {
|
|
12
|
+
import { canvasPanels, canvasWrap, updateCanvas } from "../store";
|
|
12
13
|
import { activeTab } from "../workspace/workspace";
|
|
13
14
|
import { view } from "../view";
|
|
14
15
|
import { parseSourceForPath, serializeDocument } from "../files/file-ops";
|
|
@@ -16,19 +17,19 @@ import { formatByName, formatForPath } from "../format/format-host";
|
|
|
16
17
|
import { renderWelcome } from "../panels/welcome-screen";
|
|
17
18
|
import { projectState } from "../state";
|
|
18
19
|
import {
|
|
19
|
-
canvasPanelTemplate,
|
|
20
20
|
applyTransform,
|
|
21
|
+
canvasPanelTemplate,
|
|
21
22
|
observeCenterUntilStable,
|
|
22
23
|
renderZoomIndicator,
|
|
23
24
|
resetZoomIndicator,
|
|
24
25
|
updateActivePanelHeaders,
|
|
25
26
|
} from "./canvas-utils";
|
|
26
|
-
import { effectiveZoom, overlayBoxDescriptor } from "./canvas-helpers";
|
|
27
|
+
import { effectiveZoom, findCanvasElement, overlayBoxDescriptor } from "./canvas-helpers";
|
|
27
28
|
import {
|
|
28
|
-
parseMediaEntries,
|
|
29
29
|
activeBreakpointsForWidth,
|
|
30
|
-
collectMediaOverrides,
|
|
31
30
|
applyOverridesToCanvas,
|
|
31
|
+
collectMediaOverrides,
|
|
32
|
+
parseMediaEntries,
|
|
32
33
|
} from "../utils/canvas-media";
|
|
33
34
|
import { getEffectiveMedia } from "../site-context";
|
|
34
35
|
import { renderCanvasLive } from "./canvas-live-render";
|
|
@@ -37,10 +38,9 @@ import { registerPanelDnD } from "../panels/canvas-dnd";
|
|
|
37
38
|
import { registerPanelEvents } from "../panels/panel-events";
|
|
38
39
|
import { computeDocumentDiff } from "./canvas-diff";
|
|
39
40
|
import { updateForcedPseudoPreview } from "../panels/pseudo-preview";
|
|
40
|
-
import { findCanvasElement } from "./canvas-helpers";
|
|
41
41
|
import { enterComponentInlineEdit } from "../editor/component-inline-edit";
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
42
|
+
import { refreshStylebookStyles, renderStylebookMode } from "../panels/stylebook-panel";
|
|
43
|
+
import { dismissBlockActionBar, dismissLinkPopover } from "../panels/block-action-bar";
|
|
44
44
|
import { dismissContextMenu } from "../editor/context-menu";
|
|
45
45
|
import { dismissSlashMenu } from "../editor/slash-menu";
|
|
46
46
|
import { renderFunctionEditor } from "../panels/editors";
|
|
@@ -51,6 +51,7 @@ import * as overlaysPanel from "../panels/overlays";
|
|
|
51
51
|
import type { CanvasPanel } from "../panels/canvas-dnd";
|
|
52
52
|
import type { GitDiffState, InlineEditDef } from "../types";
|
|
53
53
|
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
54
|
+
import type { Tab } from "../tabs/tab.js";
|
|
54
55
|
|
|
55
56
|
interface CanvasRenderCtx {
|
|
56
57
|
getCanvasMode: () => string;
|
|
@@ -77,9 +78,11 @@ export function initCanvasRender(ctx: CanvasRenderCtx) {
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
/** Monaco language for the source view of a tab's document. */
|
|
80
|
-
function sourceLang(tab:
|
|
81
|
+
function sourceLang(tab: Tab) {
|
|
81
82
|
const format = formatByName(tab.doc.sourceFormat);
|
|
82
|
-
if (format)
|
|
83
|
+
if (format) {
|
|
84
|
+
return format.mediaType?.split("/").pop() ?? "plaintext";
|
|
85
|
+
}
|
|
83
86
|
return (tab.documentPath || "").endsWith(".js") ? "javascript" : "json";
|
|
84
87
|
}
|
|
85
88
|
|
|
@@ -87,9 +90,13 @@ function sourceLang(tab: import("../tabs/tab.js").Tab) {
|
|
|
87
90
|
* The full source text for the source view. Format-class files serialize to their on-disk form
|
|
88
91
|
* (e.g. frontmatter YAML plus the body for markdown), not just the JSON of the body tree.
|
|
89
92
|
*/
|
|
90
|
-
async function sourceContent(tab:
|
|
91
|
-
if (formatByName(tab.doc.sourceFormat))
|
|
92
|
-
|
|
93
|
+
async function sourceContent(tab: Tab, lang: string) {
|
|
94
|
+
if (formatByName(tab.doc.sourceFormat)) {
|
|
95
|
+
return serializeDocument(tab);
|
|
96
|
+
}
|
|
97
|
+
if (lang === "javascript") {
|
|
98
|
+
return tab.doc.document?.toString?.() || "";
|
|
99
|
+
}
|
|
93
100
|
return JSON.stringify(tab.doc.document, null, 2);
|
|
94
101
|
}
|
|
95
102
|
|
|
@@ -106,25 +113,25 @@ export function renderCanvas() {
|
|
|
106
113
|
const ctx = _ctx as CanvasRenderCtx;
|
|
107
114
|
const S = {
|
|
108
115
|
document: tab.doc.document,
|
|
109
|
-
ui: tab.session.ui,
|
|
110
116
|
mode: tab.doc.mode,
|
|
117
|
+
ui: tab.session.ui,
|
|
111
118
|
};
|
|
112
119
|
const canvasMode = ctx.getCanvasMode();
|
|
113
120
|
|
|
114
121
|
// Advance render generation so stale async renders from the previous cycle bail out
|
|
115
|
-
|
|
122
|
+
view.renderGeneration += 1;
|
|
116
123
|
|
|
117
124
|
// Detect whether this is a mode transition or a content-only re-render
|
|
118
125
|
const modeChanged = canvasMode !== view.prevCanvasMode;
|
|
119
126
|
|
|
120
127
|
// Only clear Lit's internal state on mode transitions (structural panel changes).
|
|
121
128
|
// For content re-renders in the same mode, Lit's template diffing preserves
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
// @ts-
|
|
129
|
+
// The panel structure. Bailed async renders can't corrupt the DOM because
|
|
130
|
+
// RenderCanvasLive uses atomic clear (innerHTML = "" right before appendChild).
|
|
131
|
+
// @ts-expect-error -- _$litPart$ is Lit's private render-part marker, not in the DOM types
|
|
125
132
|
if (modeChanged && canvasWrap["_$litPart$"]) {
|
|
126
133
|
canvasWrap.textContent = "";
|
|
127
|
-
// @ts-
|
|
134
|
+
// @ts-expect-error -- _$litPart$ is Lit's private render-part marker, not in the DOM types
|
|
128
135
|
delete canvasWrap["_$litPart$"];
|
|
129
136
|
}
|
|
130
137
|
|
|
@@ -141,13 +148,15 @@ export function renderCanvas() {
|
|
|
141
148
|
}
|
|
142
149
|
|
|
143
150
|
// Source mode: update existing Monaco editor without recreating. Don't replace the buffer while
|
|
144
|
-
//
|
|
145
|
-
//
|
|
151
|
+
// The user is actively typing in it — that would reformat under the cursor (the source view is
|
|
152
|
+
// The editing surface here, mirroring the panel draft-state behaviour).
|
|
146
153
|
if (canvasMode === "source" && view.monacoEditor) {
|
|
147
154
|
const editor = view.monacoEditor;
|
|
148
155
|
sourceContent(tab, sourceLang(tab))
|
|
149
156
|
.then((newVal) => {
|
|
150
|
-
if (view.monacoEditor !== editor)
|
|
157
|
+
if (view.monacoEditor !== editor) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
151
160
|
if (!editor.hasTextFocus() && editor.getValue() !== newVal) {
|
|
152
161
|
editor._ignoreNextChange = true;
|
|
153
162
|
editor.setValue(newVal);
|
|
@@ -162,7 +171,7 @@ export function renderCanvas() {
|
|
|
162
171
|
// Stylebook fast-path: re-apply styles without rebuilding DOM
|
|
163
172
|
if (canvasMode === "stylebook" && !modeChanged) {
|
|
164
173
|
const curFilter = tab.session.ui.stylebookFilter || "";
|
|
165
|
-
const curCustomized =
|
|
174
|
+
const curCustomized = Boolean(tab.session.ui.stylebookCustomizedOnly);
|
|
166
175
|
const filterChanged =
|
|
167
176
|
curFilter !== _prevStylebookFilter || curCustomized !== _prevStylebookCustomizedOnly;
|
|
168
177
|
if (!filterChanged) {
|
|
@@ -175,13 +184,17 @@ export function renderCanvas() {
|
|
|
175
184
|
view.prevCanvasMode = canvasMode;
|
|
176
185
|
|
|
177
186
|
// DnD handlers are registered on inner canvas elements that get replaced on every
|
|
178
|
-
//
|
|
179
|
-
for (const fn of view.canvasDndCleanups)
|
|
187
|
+
// Content render, so always clean them up.
|
|
188
|
+
for (const fn of view.canvasDndCleanups) {
|
|
189
|
+
fn();
|
|
190
|
+
}
|
|
180
191
|
view.canvasDndCleanups = [];
|
|
181
192
|
|
|
182
193
|
// Panel event handlers (click, dblclick, etc.) capture closures over panel references.
|
|
183
194
|
// Always re-register to keep closures fresh across document switches.
|
|
184
|
-
for (const fn of view.canvasEventCleanups)
|
|
195
|
+
for (const fn of view.canvasEventCleanups) {
|
|
196
|
+
fn();
|
|
197
|
+
}
|
|
185
198
|
view.canvasEventCleanups = [];
|
|
186
199
|
|
|
187
200
|
// Panel JS objects are cheap — always clear and repopulate from templates.
|
|
@@ -225,15 +238,15 @@ export function renderCanvas() {
|
|
|
225
238
|
// Stylebook mode: render element catalog with panzoom surface
|
|
226
239
|
if (canvasMode === "stylebook") {
|
|
227
240
|
_prevStylebookFilter = tab.session.ui.stylebookFilter || "";
|
|
228
|
-
_prevStylebookCustomizedOnly =
|
|
241
|
+
_prevStylebookCustomizedOnly = Boolean(tab.session.ui.stylebookCustomizedOnly);
|
|
229
242
|
renderStylebookMode({
|
|
230
|
-
canvasPanelTemplate,
|
|
231
243
|
applyTransform,
|
|
244
|
+
canvasPanelTemplate,
|
|
245
|
+
effectiveZoom,
|
|
232
246
|
observeCenterUntilStable,
|
|
247
|
+
overlayBoxDescriptor,
|
|
233
248
|
renderZoomIndicator,
|
|
234
249
|
updateActivePanelHeaders,
|
|
235
|
-
overlayBoxDescriptor,
|
|
236
|
-
effectiveZoom,
|
|
237
250
|
});
|
|
238
251
|
return;
|
|
239
252
|
}
|
|
@@ -254,7 +267,9 @@ export function renderCanvas() {
|
|
|
254
267
|
<div
|
|
255
268
|
class="source-editor"
|
|
256
269
|
${ref((el) => {
|
|
257
|
-
if (el)
|
|
270
|
+
if (el) {
|
|
271
|
+
editorContainer = el as HTMLDivElement;
|
|
272
|
+
}
|
|
258
273
|
})}
|
|
259
274
|
></div>
|
|
260
275
|
</div>`,
|
|
@@ -263,7 +278,7 @@ export function renderCanvas() {
|
|
|
263
278
|
|
|
264
279
|
const filePath = tab.documentPath || "document.json";
|
|
265
280
|
const lang = sourceLang(tab);
|
|
266
|
-
const modelUri = monaco.Uri.parse(
|
|
281
|
+
const modelUri = monaco.Uri.parse(`file:///${filePath}`);
|
|
267
282
|
const model = monaco.editor.createModel("", lang, modelUri);
|
|
268
283
|
sourceContent(tab, lang)
|
|
269
284
|
.then((content) => {
|
|
@@ -277,53 +292,57 @@ export function renderCanvas() {
|
|
|
277
292
|
// Serialization unavailable — leave the buffer empty rather than crash the render
|
|
278
293
|
});
|
|
279
294
|
view.monacoEditor = monaco.editor.create(editorContainer as unknown as HTMLElement, {
|
|
280
|
-
model,
|
|
281
|
-
theme: "vs-dark",
|
|
282
295
|
automaticLayout: true,
|
|
283
|
-
minimap: { enabled: false },
|
|
284
|
-
fontSize: 12,
|
|
285
296
|
fontFamily: "'SF Mono', 'Fira Code', 'Consolas', monospace",
|
|
297
|
+
fontSize: 12,
|
|
286
298
|
lineNumbers: "on",
|
|
299
|
+
minimap: { enabled: false },
|
|
300
|
+
model,
|
|
287
301
|
scrollBeyondLastLine: false,
|
|
288
|
-
wordWrap: "on",
|
|
289
302
|
tabSize: 2,
|
|
303
|
+
theme: "vs-dark",
|
|
304
|
+
wordWrap: "on",
|
|
290
305
|
});
|
|
291
306
|
|
|
292
307
|
// Debounced sync back to state
|
|
293
308
|
let debounce: ReturnType<typeof setTimeout> | undefined;
|
|
294
309
|
view.monacoEditor.onDidChangeModelContent(() => {
|
|
295
310
|
const editor = view.monacoEditor;
|
|
296
|
-
if (!editor)
|
|
311
|
+
if (!editor) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
297
314
|
if (editor._ignoreNextChange) {
|
|
298
315
|
editor._ignoreNextChange = false;
|
|
299
316
|
return;
|
|
300
317
|
}
|
|
301
318
|
clearTimeout(debounce);
|
|
302
319
|
debounce = setTimeout(async () => {
|
|
303
|
-
const
|
|
304
|
-
if (!
|
|
305
|
-
|
|
320
|
+
const tabNow = activeTab.value;
|
|
321
|
+
if (!tabNow) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (formatByName(tabNow.doc.sourceFormat) && tabNow.documentPath) {
|
|
306
325
|
try {
|
|
307
326
|
// Parse the full source back into body + frontmatter (title, $head, etc.).
|
|
308
327
|
const { document, frontmatter } = await parseSourceForPath(
|
|
309
|
-
|
|
328
|
+
tabNow.documentPath,
|
|
310
329
|
editor.getValue(),
|
|
311
330
|
);
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
331
|
+
tabNow.doc.document = document as JxMutableNode;
|
|
332
|
+
tabNow.doc.content.frontmatter = frontmatter;
|
|
333
|
+
tabNow.doc.dirty = true;
|
|
315
334
|
} catch {
|
|
316
335
|
// Unparseable source — don't update state
|
|
317
336
|
}
|
|
318
337
|
} else if (lang === "json") {
|
|
319
338
|
try {
|
|
320
|
-
|
|
321
|
-
|
|
339
|
+
tabNow.doc.document = JSON.parse(editor.getValue());
|
|
340
|
+
tabNow.doc.dirty = true;
|
|
322
341
|
} catch {
|
|
323
342
|
// Invalid JSON — don't update state
|
|
324
343
|
}
|
|
325
344
|
} else {
|
|
326
|
-
|
|
345
|
+
tabNow.doc.dirty = true;
|
|
327
346
|
}
|
|
328
347
|
}, 600);
|
|
329
348
|
});
|
|
@@ -343,7 +362,7 @@ export function renderCanvas() {
|
|
|
343
362
|
canvasWrap.style.overflow = "hidden";
|
|
344
363
|
}
|
|
345
364
|
|
|
346
|
-
const gitDiffState = ctx
|
|
365
|
+
const { gitDiffState } = ctx;
|
|
347
366
|
const panelWidth = 800;
|
|
348
367
|
|
|
349
368
|
const { tpl: origTpl, panel: origPanel } = canvasPanelTemplate(
|
|
@@ -365,7 +384,9 @@ export function renderCanvas() {
|
|
|
365
384
|
class="panzoom-wrap"
|
|
366
385
|
style="transform-origin:0 0"
|
|
367
386
|
${ref((el) => {
|
|
368
|
-
if (el)
|
|
387
|
+
if (el) {
|
|
388
|
+
view.panzoomWrap = el as HTMLDivElement;
|
|
389
|
+
}
|
|
369
390
|
})}
|
|
370
391
|
>
|
|
371
392
|
${origTpl} ${currTpl}
|
|
@@ -388,14 +409,14 @@ export function renderCanvas() {
|
|
|
388
409
|
return JSON.parse(content);
|
|
389
410
|
} catch {
|
|
390
411
|
return {
|
|
391
|
-
tagName: "div",
|
|
392
412
|
children: [{ tagName: "p", textContent: "Failed to parse" }],
|
|
413
|
+
tagName: "div",
|
|
393
414
|
};
|
|
394
415
|
}
|
|
395
416
|
});
|
|
396
417
|
};
|
|
397
418
|
|
|
398
|
-
const featureToggles = S.ui
|
|
419
|
+
const { featureToggles } = S.ui;
|
|
399
420
|
Promise.all([
|
|
400
421
|
parseContent(gitDiffState.originalContent || ""),
|
|
401
422
|
parseContent(gitDiffState.currentContent || ""),
|
|
@@ -417,7 +438,9 @@ export function renderCanvas() {
|
|
|
417
438
|
});
|
|
418
439
|
|
|
419
440
|
applyTransform();
|
|
420
|
-
if (modeChanged)
|
|
441
|
+
if (modeChanged) {
|
|
442
|
+
observeCenterUntilStable();
|
|
443
|
+
}
|
|
421
444
|
renderZoomIndicator();
|
|
422
445
|
return;
|
|
423
446
|
}
|
|
@@ -462,7 +485,7 @@ export function renderCanvas() {
|
|
|
462
485
|
baseWidth,
|
|
463
486
|
} = parseMediaEntries(getEffectiveMedia(S.document.$media));
|
|
464
487
|
const hasMedia = sizeBreakpoints.length > 0;
|
|
465
|
-
const featureToggles = S.ui
|
|
488
|
+
const { featureToggles } = S.ui;
|
|
466
489
|
|
|
467
490
|
// Create panzoom wrapper (the element that gets transformed)
|
|
468
491
|
if (!hasMedia) {
|
|
@@ -482,7 +505,9 @@ export function renderCanvas() {
|
|
|
482
505
|
class="panzoom-wrap"
|
|
483
506
|
style="transform-origin:0 0"
|
|
484
507
|
${ref((el) => {
|
|
485
|
-
if (el)
|
|
508
|
+
if (el) {
|
|
509
|
+
view.panzoomWrap = el as HTMLDivElement;
|
|
510
|
+
}
|
|
486
511
|
})}
|
|
487
512
|
>
|
|
488
513
|
${panelTpl}
|
|
@@ -501,28 +526,28 @@ export function renderCanvas() {
|
|
|
501
526
|
}
|
|
502
527
|
|
|
503
528
|
// Build all panels: base first, then breakpoints in declared order (ascending for min-width,
|
|
504
|
-
//
|
|
529
|
+
// Descending for max-width — matching the direction of the design's media queries).
|
|
505
530
|
const allPanelDefs = [
|
|
506
531
|
{
|
|
507
|
-
|
|
532
|
+
activeSet: activeBreakpointsForWidth(sizeBreakpoints, baseWidth),
|
|
508
533
|
displayName: mediaDisplayName("--"),
|
|
534
|
+
name: "base",
|
|
509
535
|
width: baseWidth,
|
|
510
|
-
activeSet: activeBreakpointsForWidth(sizeBreakpoints, baseWidth),
|
|
511
536
|
},
|
|
512
537
|
];
|
|
513
538
|
for (const bp of sizeBreakpoints) {
|
|
514
539
|
allPanelDefs.push({
|
|
515
|
-
|
|
540
|
+
activeSet: activeBreakpointsForWidth(sizeBreakpoints, bp.width),
|
|
516
541
|
displayName: mediaDisplayName(bp.name),
|
|
542
|
+
name: bp.name,
|
|
517
543
|
width: bp.width,
|
|
518
|
-
activeSet: activeBreakpointsForWidth(sizeBreakpoints, bp.width),
|
|
519
544
|
});
|
|
520
545
|
}
|
|
521
546
|
|
|
522
547
|
const panelEntries = allPanelDefs.map((def) => {
|
|
523
548
|
const label = `${def.displayName} (${def.width}px)`;
|
|
524
549
|
const { tpl, panel } = canvasPanelTemplate(def.name, label, false, def.width);
|
|
525
|
-
return {
|
|
550
|
+
return { activeSet: def.activeSet, panel, tpl };
|
|
526
551
|
});
|
|
527
552
|
|
|
528
553
|
litRender(
|
|
@@ -531,7 +556,9 @@ export function renderCanvas() {
|
|
|
531
556
|
class="panzoom-wrap"
|
|
532
557
|
style="transform-origin:0 0"
|
|
533
558
|
${ref((el) => {
|
|
534
|
-
if (el)
|
|
559
|
+
if (el) {
|
|
560
|
+
view.panzoomWrap = el as HTMLDivElement;
|
|
561
|
+
}
|
|
535
562
|
})}
|
|
536
563
|
>
|
|
537
564
|
${panelEntries.map((e) => e.tpl)}
|
|
@@ -591,9 +618,11 @@ function renderCanvasIntoPanel(
|
|
|
591
618
|
renderCanvasLive(gen, docToRender, canvas)
|
|
592
619
|
.then((scope: Record<string, unknown> | null) => {
|
|
593
620
|
// Skip post-render setup if a newer render has started
|
|
594
|
-
if (gen !== view.renderGeneration)
|
|
621
|
+
if (gen !== view.renderGeneration) {
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
595
624
|
if (scope) {
|
|
596
|
-
updateCanvas({
|
|
625
|
+
updateCanvas({ error: null, scope, status: "ready" });
|
|
597
626
|
applyCanvasMediaOverrides(canvas, activeBreakpoints);
|
|
598
627
|
statusMessage("Runtime render OK", 1500);
|
|
599
628
|
|
|
@@ -615,14 +644,14 @@ function renderCanvasIntoPanel(
|
|
|
615
644
|
}
|
|
616
645
|
} else {
|
|
617
646
|
// Fallback to structural preview
|
|
618
|
-
updateCanvas({
|
|
647
|
+
updateCanvas({ error: null, scope: null, status: "ready" });
|
|
619
648
|
canvas.innerHTML = "";
|
|
620
649
|
renderCanvasNode(docToRender, [], canvas, activeBreakpoints, featureToggles);
|
|
621
650
|
}
|
|
622
651
|
try {
|
|
623
652
|
registerPanelDnD(panel as unknown as CanvasPanel);
|
|
624
|
-
} catch (
|
|
625
|
-
console.warn("registerPanelDnD failed:", (
|
|
653
|
+
} catch (dndError) {
|
|
654
|
+
console.warn("registerPanelDnD failed:", errorMessage(dndError));
|
|
626
655
|
}
|
|
627
656
|
registerPanelEvents(panel as unknown as CanvasPanel);
|
|
628
657
|
renderOverlays();
|
|
@@ -636,20 +665,24 @@ function renderCanvasIntoPanel(
|
|
|
636
665
|
const targetPanel = canvasPanels.find((p) => p.mediaName === mn) || canvasPanels[0];
|
|
637
666
|
if (targetPanel) {
|
|
638
667
|
const el = findCanvasElement(path, targetPanel.canvas);
|
|
639
|
-
if (el)
|
|
668
|
+
if (el) {
|
|
669
|
+
enterComponentInlineEdit(el, path);
|
|
670
|
+
}
|
|
640
671
|
}
|
|
641
672
|
}
|
|
642
673
|
})
|
|
643
|
-
.catch((
|
|
644
|
-
if (gen !== view.renderGeneration)
|
|
645
|
-
|
|
646
|
-
|
|
674
|
+
.catch((error: unknown) => {
|
|
675
|
+
if (gen !== view.renderGeneration) {
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
console.warn("renderCanvasLive rejected:", error instanceof Error ? error.message : error);
|
|
679
|
+
updateCanvas({ error: null, scope: null, status: "ready" });
|
|
647
680
|
canvas.innerHTML = "";
|
|
648
681
|
renderCanvasNode(docToRender, [], canvas, activeBreakpoints, featureToggles);
|
|
649
682
|
try {
|
|
650
683
|
registerPanelDnD(panel as unknown as CanvasPanel);
|
|
651
|
-
} catch (
|
|
652
|
-
console.warn("registerPanelDnD failed:", (
|
|
684
|
+
} catch (dndError) {
|
|
685
|
+
console.warn("registerPanelDnD failed:", errorMessage(dndError));
|
|
653
686
|
}
|
|
654
687
|
registerPanelEvents(panel as unknown as CanvasPanel);
|
|
655
688
|
renderOverlays();
|
|
@@ -668,7 +701,9 @@ function applyDiffHighlightToCanvas(
|
|
|
668
701
|
canvas: HTMLElement,
|
|
669
702
|
diffMap: Map<string, "added" | "removed" | "modified">,
|
|
670
703
|
) {
|
|
671
|
-
if (!diffMap || diffMap.size === 0)
|
|
704
|
+
if (!diffMap || diffMap.size === 0) {
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
672
707
|
|
|
673
708
|
// Walk all elements in canvas and check their data attributes or other markers
|
|
674
709
|
const walkCanvas = (el: HTMLElement, /** @type {string} */ path = "") => {
|
|
@@ -676,9 +711,13 @@ function applyDiffHighlightToCanvas(
|
|
|
676
711
|
|
|
677
712
|
if (diffMap.has(pathKey)) {
|
|
678
713
|
const status = diffMap.get(pathKey);
|
|
679
|
-
if (status === "added")
|
|
680
|
-
|
|
681
|
-
else if (status === "
|
|
714
|
+
if (status === "added") {
|
|
715
|
+
el.classList.add("element-diff-added");
|
|
716
|
+
} else if (status === "removed") {
|
|
717
|
+
el.classList.add("element-diff-removed");
|
|
718
|
+
} else if (status === "modified") {
|
|
719
|
+
el.classList.add("element-diff-modified");
|
|
720
|
+
}
|
|
682
721
|
}
|
|
683
722
|
|
|
684
723
|
// Check for child elements (heuristic: children array markers)
|
|
@@ -687,7 +726,7 @@ function applyDiffHighlightToCanvas(
|
|
|
687
726
|
const childPath =
|
|
688
727
|
pathKey === "/" ? `children/${childIdx}` : `${pathKey}/children/${childIdx}`;
|
|
689
728
|
walkCanvas(child as HTMLElement, childPath);
|
|
690
|
-
childIdx
|
|
729
|
+
childIdx += 1;
|
|
691
730
|
}
|
|
692
731
|
};
|
|
693
732
|
|
|
@@ -703,14 +742,20 @@ function applyDiffHighlightToCanvas(
|
|
|
703
742
|
* @param {Set<string>} activeBreakpoints
|
|
704
743
|
*/
|
|
705
744
|
function applyCanvasMediaOverrides(canvasEl: Element, activeBreakpoints: Set<string>) {
|
|
706
|
-
if (
|
|
745
|
+
if (activeBreakpoints.size === 0) {
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
707
748
|
const tab = activeTab.value;
|
|
708
|
-
if (!tab)
|
|
749
|
+
if (!tab) {
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
709
752
|
const docMedia = getEffectiveMedia(tab.doc.document.$media || {});
|
|
710
753
|
// Build a set of CSS condition texts that match active breakpoints
|
|
711
754
|
const activeConditions = new Set<string>();
|
|
712
755
|
for (const name of activeBreakpoints) {
|
|
713
|
-
if (docMedia[name])
|
|
756
|
+
if (docMedia[name]) {
|
|
757
|
+
activeConditions.add(docMedia[name]);
|
|
758
|
+
}
|
|
714
759
|
}
|
|
715
760
|
const overrides = collectMediaOverrides(document.styleSheets, activeConditions);
|
|
716
761
|
applyOverridesToCanvas(canvasEl, overrides);
|