@jxsuite/studio 0.23.1 → 0.24.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.js +2231 -715
- package/dist/studio.js.map +121 -104
- package/package.json +8 -7
- package/src/browse/{browse-modal.js → browse-modal.ts} +9 -10
- package/src/browse/{browse.js → browse.ts} +110 -85
- package/src/canvas/{canvas-diff.js → canvas-diff.ts} +42 -35
- package/src/canvas/{canvas-helpers.js → canvas-helpers.ts} +25 -19
- package/src/canvas/{canvas-live-render.js → canvas-live-render.ts} +53 -63
- package/src/canvas/{canvas-render.js → canvas-render.ts} +110 -121
- package/src/canvas/{canvas-utils.js → canvas-utils.ts} +48 -44
- package/src/canvas/{nested-site-style.js → nested-site-style.ts} +3 -3
- package/src/editor/{component-inline-edit.js → component-inline-edit.ts} +45 -33
- package/src/editor/{content-inline-edit.js → content-inline-edit.ts} +27 -45
- package/src/editor/{context-menu.js → context-menu.ts} +38 -36
- package/src/editor/{convert-targets.js → convert-targets.ts} +15 -12
- package/src/editor/{convert-to-component.js → convert-to-component.ts} +25 -26
- package/src/editor/{convert-to-repeater.js → convert-to-repeater.ts} +60 -40
- package/src/editor/{inline-edit.js → inline-edit.ts} +91 -55
- package/src/editor/{inline-format.js → inline-format.ts} +46 -51
- package/src/editor/{insertion-helper.js → insertion-helper.ts} +47 -63
- package/src/editor/{shortcuts.js → shortcuts.ts} +45 -31
- package/src/editor/{slash-menu.js → slash-menu.ts} +34 -35
- package/src/files/{components.js → components.ts} +12 -15
- package/src/files/{file-ops.js → file-ops.ts} +36 -29
- package/src/files/{files.js → files.ts} +102 -84
- package/src/github/{github-auth.js → github-auth.ts} +3 -3
- package/src/github/{github-publish.js → github-publish.ts} +20 -18
- package/src/markdown/{md-allowlist.js → md-allowlist.ts} +5 -2
- package/src/markdown/{md-convert.js → md-convert.ts} +146 -169
- package/src/new-project/{new-project-modal.js → new-project-modal.ts} +16 -18
- package/src/panels/{activity-bar.js → activity-bar.ts} +26 -34
- package/src/panels/{ai-panel.js → ai-panel.ts} +30 -30
- package/src/panels/{block-action-bar.js → block-action-bar.ts} +64 -73
- package/src/panels/{canvas-dnd.js → canvas-dnd.ts} +44 -49
- package/src/panels/{data-explorer.js → data-explorer.ts} +21 -15
- package/src/panels/dnd.ts +417 -0
- package/src/panels/{editors.js → editors.ts} +43 -50
- package/src/panels/{elements-panel.js → elements-panel.ts} +30 -27
- package/src/panels/{events-panel.js → events-panel.ts} +33 -25
- package/src/panels/{git-panel.js → git-panel.ts} +97 -83
- package/src/panels/{head-panel.js → head-panel.ts} +117 -139
- package/src/panels/{imports-panel.js → imports-panel.ts} +69 -89
- package/src/panels/{layers-panel.js → layers-panel.ts} +61 -57
- package/src/panels/{left-panel.js → left-panel.ts} +56 -63
- package/src/panels/{overlays.js → overlays.ts} +28 -36
- package/src/panels/{panel-events.js → panel-events.ts} +53 -51
- package/src/panels/{preview-render.js → preview-render.ts} +20 -15
- package/src/panels/{properties-panel.js → properties-panel.ts} +212 -246
- package/src/panels/{pseudo-preview.js → pseudo-preview.ts} +9 -8
- package/src/panels/{quick-search.js → quick-search.ts} +16 -17
- package/src/panels/{right-panel.js → right-panel.ts} +41 -59
- package/src/panels/{shared.js → shared.ts} +7 -5
- package/src/panels/{signals-panel.js → signals-panel.ts} +322 -362
- package/src/panels/{statusbar.js → statusbar.ts} +14 -14
- package/src/panels/{style-inputs.js → style-inputs.ts} +66 -48
- package/src/panels/{style-panel.js → style-panel.ts} +235 -290
- package/src/panels/{style-utils.js → style-utils.ts} +32 -31
- package/src/panels/{stylebook-layers-panel.js → stylebook-layers-panel.ts} +22 -14
- package/src/panels/{stylebook-panel.js → stylebook-panel.ts} +127 -140
- package/src/panels/{tab-strip.js → tab-strip.ts} +12 -14
- package/src/panels/{toolbar.js → toolbar.ts} +68 -70
- package/src/panels/{welcome-screen.js → welcome-screen.ts} +14 -16
- package/src/{platform.js → platform.ts} +5 -3
- package/src/platforms/{devserver.js → devserver.ts} +53 -44
- package/src/{recent-projects.js → recent-projects.ts} +17 -12
- package/src/{resize-edges.js → resize-edges.ts} +18 -13
- package/src/services/{cem-export.js → cem-export.ts} +14 -7
- package/src/services/{code-services.js → code-services.ts} +29 -20
- package/src/services/{monaco-setup.js → monaco-setup.ts} +2 -3
- package/src/settings/{content-types-editor.js → content-types-editor.ts} +88 -67
- package/src/settings/{css-vars-editor.js → css-vars-editor.ts} +78 -51
- package/src/settings/{defs-editor.js → defs-editor.ts} +70 -56
- package/src/settings/{general-settings.js → general-settings.ts} +29 -28
- package/src/settings/{head-editor.js → head-editor.ts} +37 -35
- package/src/settings/{schema-field-ui.js → schema-field-ui.ts} +86 -76
- package/src/settings/{settings-modal.js → settings-modal.ts} +14 -16
- package/src/{site-context.js → site-context.ts} +35 -32
- package/src/{state.js → state.ts} +95 -78
- package/src/{store.js → store.ts} +60 -63
- package/src/{studio.js → studio.ts} +117 -119
- package/src/tabs/tab.ts +196 -0
- package/src/tabs/{transact.js → transact.ts} +107 -60
- package/src/types.ts +185 -0
- package/src/ui/{button-group.js → button-group.ts} +21 -21
- package/src/ui/{color-selector.js → color-selector.ts} +44 -37
- package/src/ui/{expression-editor.js → expression-editor.ts} +65 -40
- package/src/ui/{field-row.js → field-row.ts} +19 -2
- package/src/ui/{icons.js → icons.ts} +2 -2
- package/src/ui/{layers.js → layers.ts} +33 -24
- package/src/ui/{media-picker.js → media-picker.ts} +13 -9
- package/src/ui/{panel-resize.js → panel-resize.ts} +9 -4
- package/src/ui/{spectrum.js → spectrum.ts} +4 -4
- package/src/ui/{unit-selector.js → unit-selector.ts} +16 -19
- package/src/ui/{value-selector.js → value-selector.ts} +28 -28
- package/src/ui/{widgets.js → widgets.ts} +62 -15
- package/src/utils/{canvas-media.js → canvas-media.ts} +29 -14
- package/src/utils/{edit-display.js → edit-display.ts} +23 -20
- package/src/utils/{google-fonts.js → google-fonts.ts} +14 -13
- package/src/utils/{inherited-style.js → inherited-style.ts} +15 -10
- package/src/utils/{studio-utils.js → studio-utils.ts} +38 -26
- package/src/{view.js → view.ts} +53 -55
- package/src/workspace/{workspace.js → workspace.ts} +44 -33
- package/src/panels/dnd.js +0 -426
- package/src/tabs/tab.js +0 -192
- /package/src/{reactivity.js → reactivity.ts} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
/**
|
|
2
3
|
* Canvas Diff — computes visual diff between original and current Jx documents.
|
|
3
4
|
*
|
|
@@ -5,13 +6,14 @@
|
|
|
5
6
|
* "modified" status up the tree for structural changes (children added/removed/reordered).
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
10
|
+
|
|
11
|
+
type DiffStatus = "added" | "removed" | "modified";
|
|
12
|
+
|
|
13
|
+
interface DiffResult {
|
|
14
|
+
byPath: Map<string, DiffStatus>;
|
|
15
|
+
allPaths: Set<string>;
|
|
16
|
+
}
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Deep equality check for two values. Ignores function and ref properties.
|
|
@@ -20,31 +22,27 @@
|
|
|
20
22
|
* @param {unknown} b
|
|
21
23
|
* @returns {boolean}
|
|
22
24
|
*/
|
|
23
|
-
function valuesEqual(a, b) {
|
|
25
|
+
function valuesEqual(a: unknown, b: unknown): boolean {
|
|
24
26
|
if (a === b) return true;
|
|
25
27
|
if (typeof a !== "object" || typeof b !== "object" || !a || !b) return false;
|
|
26
28
|
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
27
29
|
|
|
28
30
|
if (Array.isArray(a)) {
|
|
29
|
-
const bArr =
|
|
31
|
+
const bArr = b as unknown[];
|
|
30
32
|
if (a.length !== bArr.length) return false;
|
|
31
|
-
return a.every((
|
|
33
|
+
return a.every((v: unknown, i: number) => valuesEqual(v, bArr[i]));
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
const aObj =
|
|
35
|
-
const bObj =
|
|
36
|
+
const aObj = a as Record<string, unknown>;
|
|
37
|
+
const bObj = b as Record<string, unknown>;
|
|
36
38
|
|
|
37
|
-
const keysA = Object.keys(aObj).filter(
|
|
38
|
-
|
|
39
|
-
);
|
|
40
|
-
const keysB = Object.keys(bObj).filter(
|
|
41
|
-
(/** @type {string} */ k) => !k.startsWith("on") && k !== "$ref",
|
|
42
|
-
);
|
|
39
|
+
const keysA = Object.keys(aObj).filter((k: string) => !k.startsWith("on") && k !== "$ref");
|
|
40
|
+
const keysB = Object.keys(bObj).filter((k: string) => !k.startsWith("on") && k !== "$ref");
|
|
43
41
|
|
|
44
42
|
if (keysA.length !== keysB.length) return false;
|
|
45
|
-
if (!keysA.every((
|
|
43
|
+
if (!keysA.every((k: string) => keysB.includes(k))) return false;
|
|
46
44
|
|
|
47
|
-
return keysA.every((
|
|
45
|
+
return keysA.every((k: string) => valuesEqual(aObj[k], bObj[k]));
|
|
48
46
|
}
|
|
49
47
|
|
|
50
48
|
/**
|
|
@@ -53,13 +51,11 @@ function valuesEqual(a, b) {
|
|
|
53
51
|
* @param {JxMutableNode | undefined} node
|
|
54
52
|
* @returns {JxMutableNode[]}
|
|
55
53
|
*/
|
|
56
|
-
function elementChildren(node) {
|
|
54
|
+
function elementChildren(node: JxMutableNode | undefined) {
|
|
57
55
|
if (!node?.children || !Array.isArray(node.children)) return [];
|
|
58
|
-
return
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
)
|
|
62
|
-
);
|
|
56
|
+
return node.children.filter(
|
|
57
|
+
(c: JxMutableNode | string) => c != null && typeof c === "object",
|
|
58
|
+
) as JxMutableNode[];
|
|
63
59
|
}
|
|
64
60
|
|
|
65
61
|
/**
|
|
@@ -71,7 +67,13 @@ function elementChildren(node) {
|
|
|
71
67
|
* @param {Map<string, DiffStatus>} diffMap
|
|
72
68
|
* @param {Set<string>} allPaths
|
|
73
69
|
*/
|
|
74
|
-
function markRecursive(
|
|
70
|
+
function markRecursive(
|
|
71
|
+
node: JxMutableNode,
|
|
72
|
+
status: DiffStatus,
|
|
73
|
+
path: string,
|
|
74
|
+
diffMap: Map<string, DiffStatus>,
|
|
75
|
+
allPaths: Set<string>,
|
|
76
|
+
) {
|
|
75
77
|
allPaths.add(path);
|
|
76
78
|
diffMap.set(path, status);
|
|
77
79
|
const children = elementChildren(node);
|
|
@@ -88,11 +90,12 @@ function markRecursive(node, status, path, diffMap, allPaths) {
|
|
|
88
90
|
* @param {JxMutableNode | undefined} currentDoc - Current document (in memory/on disk)
|
|
89
91
|
* @returns {DiffResult}
|
|
90
92
|
*/
|
|
91
|
-
export function computeDocumentDiff(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
93
|
+
export function computeDocumentDiff(
|
|
94
|
+
originalDoc: JxMutableNode | undefined,
|
|
95
|
+
currentDoc: JxMutableNode | undefined,
|
|
96
|
+
): DiffResult {
|
|
97
|
+
const diffMap: Map<string, DiffStatus> = new Map();
|
|
98
|
+
const allPaths: Set<string> = new Set();
|
|
96
99
|
|
|
97
100
|
/**
|
|
98
101
|
* Walk both trees in parallel and mark differences.
|
|
@@ -101,7 +104,11 @@ export function computeDocumentDiff(originalDoc, currentDoc) {
|
|
|
101
104
|
* @param {JxMutableNode | undefined} currNode
|
|
102
105
|
* @param {string} path
|
|
103
106
|
*/
|
|
104
|
-
const walk = (
|
|
107
|
+
const walk = (
|
|
108
|
+
origNode: JxMutableNode | undefined,
|
|
109
|
+
currNode: JxMutableNode | undefined,
|
|
110
|
+
path: string = "",
|
|
111
|
+
) => {
|
|
105
112
|
const pathKey = path || "/";
|
|
106
113
|
allPaths.add(pathKey);
|
|
107
114
|
|
|
@@ -183,10 +190,10 @@ export function computeDocumentDiff(originalDoc, currentDoc) {
|
|
|
183
190
|
*
|
|
184
191
|
* @param {HTMLElement} canvas
|
|
185
192
|
*/
|
|
186
|
-
export function clearDiffHighlight(canvas) {
|
|
193
|
+
export function clearDiffHighlight(canvas: HTMLElement) {
|
|
187
194
|
canvas
|
|
188
195
|
.querySelectorAll(".element-diff-added, .element-diff-removed, .element-diff-modified")
|
|
189
|
-
.forEach((
|
|
196
|
+
.forEach((el: Element) => {
|
|
190
197
|
el.classList.remove("element-diff-added", "element-diff-removed", "element-diff-modified");
|
|
191
198
|
});
|
|
192
199
|
}
|
|
@@ -1,27 +1,30 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
/// <reference lib="dom.iterable" />
|
|
1
3
|
/**
|
|
2
4
|
* Canvas helpers — extracted from studio.js (Phase 4n). Shared query/utility functions used by
|
|
3
5
|
* multiple canvas-related modules: element lookup, zoom, panel resolution, inline bubbling.
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
|
-
import { canvasPanels, elToPath, pathsEqual, getNodeAtPath, parentElementPath } from "../store
|
|
7
|
-
import { activeTab } from "../workspace/workspace
|
|
8
|
-
import { isInlineInContext } from "../editor/inline-edit
|
|
8
|
+
import { canvasPanels, elToPath, pathsEqual, getNodeAtPath, parentElementPath } from "../store";
|
|
9
|
+
import { activeTab } from "../workspace/workspace";
|
|
10
|
+
import { isInlineInContext } from "../editor/inline-edit";
|
|
11
|
+
import type { JxPath } from "../state";
|
|
12
|
+
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
let _ctx = null;
|
|
14
|
+
let _ctx: { getCanvasMode: () => string; getZoom: () => number } | null = null;
|
|
12
15
|
|
|
13
16
|
/**
|
|
14
17
|
* Initialize the canvas helpers module.
|
|
15
18
|
*
|
|
16
19
|
* @param {{ getCanvasMode: () => string; getZoom: () => number }} ctx
|
|
17
20
|
*/
|
|
18
|
-
export function initCanvasHelpers(ctx) {
|
|
21
|
+
export function initCanvasHelpers(ctx: { getCanvasMode: () => string; getZoom: () => number }) {
|
|
19
22
|
_ctx = ctx;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
/** Effective zoom scale — always 1 in edit (content) mode, actual zoom otherwise. */
|
|
23
26
|
export function effectiveZoom() {
|
|
24
|
-
const ctx =
|
|
27
|
+
const ctx = _ctx!;
|
|
25
28
|
return ctx.getCanvasMode() === "edit"
|
|
26
29
|
? 1
|
|
27
30
|
: (ctx.getZoom?.() ?? activeTab.value?.session.ui.zoom ?? 1);
|
|
@@ -46,7 +49,7 @@ export function getActivePanel() {
|
|
|
46
49
|
* @param {JxMutableNode | undefined} doc
|
|
47
50
|
* @param {JxPath} path
|
|
48
51
|
*/
|
|
49
|
-
export function bubbleInlinePath(doc, path) {
|
|
52
|
+
export function bubbleInlinePath(doc: JxMutableNode | undefined, path: JxPath) {
|
|
50
53
|
if (!doc) return path;
|
|
51
54
|
let currentPath = path;
|
|
52
55
|
while (currentPath.length >= 2) {
|
|
@@ -57,7 +60,7 @@ export function bubbleInlinePath(doc, path) {
|
|
|
57
60
|
const childTag = (node.tagName ?? "div").toLowerCase();
|
|
58
61
|
const parentTag = (parentNode.tagName ?? "div").toLowerCase();
|
|
59
62
|
if (!isInlineInContext(childTag, parentTag)) break;
|
|
60
|
-
currentPath =
|
|
63
|
+
currentPath = pPath as JxPath;
|
|
61
64
|
}
|
|
62
65
|
return currentPath;
|
|
63
66
|
}
|
|
@@ -69,9 +72,8 @@ export function bubbleInlinePath(doc, path) {
|
|
|
69
72
|
* @param {HTMLElement} canvasEl
|
|
70
73
|
* @returns {HTMLElement | null}
|
|
71
74
|
*/
|
|
72
|
-
export function findCanvasElement(path, canvasEl) {
|
|
73
|
-
|
|
74
|
-
let el = /** @type {HTMLElement | null} */ (canvasEl.firstElementChild);
|
|
75
|
+
export function findCanvasElement(path: JxPath, canvasEl: HTMLElement) {
|
|
76
|
+
let el: HTMLElement | null | undefined = canvasEl.firstElementChild as HTMLElement | null;
|
|
75
77
|
if (!el) return null;
|
|
76
78
|
if (path.length === 0) return el;
|
|
77
79
|
|
|
@@ -79,11 +81,11 @@ export function findCanvasElement(path, canvasEl) {
|
|
|
79
81
|
if (path[i] !== "children" && path[i] !== "cases") return null;
|
|
80
82
|
const idx = path[i + 1];
|
|
81
83
|
if (idx === undefined) {
|
|
82
|
-
el =
|
|
84
|
+
el = el.children[0] as HTMLElement | undefined;
|
|
83
85
|
} else if (idx === "map") {
|
|
84
|
-
el =
|
|
86
|
+
el = el.children[0]?.children[0] as HTMLElement | undefined;
|
|
85
87
|
} else {
|
|
86
|
-
el =
|
|
88
|
+
el = el.children[idx as number] as HTMLElement | undefined;
|
|
87
89
|
}
|
|
88
90
|
if (!el) break;
|
|
89
91
|
}
|
|
@@ -95,7 +97,7 @@ export function findCanvasElement(path, canvasEl) {
|
|
|
95
97
|
|
|
96
98
|
for (const candidate of canvasEl.querySelectorAll("*")) {
|
|
97
99
|
const p = elToPath.get(candidate);
|
|
98
|
-
if (p && pathsEqual(p, path)) return
|
|
100
|
+
if (p && pathsEqual(p, path)) return candidate as HTMLElement;
|
|
99
101
|
}
|
|
100
102
|
return null;
|
|
101
103
|
}
|
|
@@ -105,10 +107,14 @@ export function findCanvasElement(path, canvasEl) {
|
|
|
105
107
|
*
|
|
106
108
|
* @param {Element} el
|
|
107
109
|
* @param {string} type
|
|
108
|
-
* @param {import("
|
|
110
|
+
* @param {import("../panels/canvas-dnd.js").CanvasPanel} panel
|
|
109
111
|
*/
|
|
110
|
-
export function overlayBoxDescriptor(
|
|
111
|
-
|
|
112
|
+
export function overlayBoxDescriptor(
|
|
113
|
+
el: Element,
|
|
114
|
+
type: string,
|
|
115
|
+
panel: import("../panels/canvas-dnd.js").CanvasPanel,
|
|
116
|
+
) {
|
|
117
|
+
const viewport = panel.viewport as HTMLElement;
|
|
112
118
|
const vpRect = viewport.getBoundingClientRect();
|
|
113
119
|
const elRect = el.getBoundingClientRect();
|
|
114
120
|
const scale = effectiveZoom();
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
/// <reference lib="dom.iterable" />
|
|
1
3
|
/**
|
|
2
4
|
* Canvas live render — extracted from studio.js (Phase 4p). Async runtime rendering pipeline that
|
|
3
5
|
* builds live canvas DOM using @jxsuite/runtime. Handles element registration, scope building, path
|
|
4
6
|
* mapping ($map remapping), site-level style injection, and $head element injection.
|
|
5
7
|
*/
|
|
6
8
|
|
|
7
|
-
import { elToPath, stripEventHandlers, projectState } from "../store
|
|
8
|
-
import { activeTab } from "../workspace/workspace
|
|
9
|
-
import { view } from "../view
|
|
10
|
-
import { toRaw } from "../reactivity
|
|
9
|
+
import { elToPath, stripEventHandlers, projectState } from "../store";
|
|
10
|
+
import { activeTab } from "../workspace/workspace";
|
|
11
|
+
import { view } from "../view";
|
|
12
|
+
import { toRaw } from "../reactivity";
|
|
11
13
|
import {
|
|
12
14
|
renderNode as runtimeRenderNode,
|
|
13
15
|
buildScope,
|
|
@@ -22,17 +24,19 @@ import {
|
|
|
22
24
|
getEffectiveLayoutPath,
|
|
23
25
|
resolveLayoutDoc,
|
|
24
26
|
distributePageIntoLayout,
|
|
25
|
-
} from "../site-context
|
|
26
|
-
import { componentRegistry, computeRelativePath } from "../files/components
|
|
27
|
-
import { prepareForEditMode } from "../utils/edit-display
|
|
28
|
-
import { getActiveElement } from "../editor/inline-edit
|
|
29
|
-
import { buildNestedSiteCSS } from "./nested-site-style
|
|
27
|
+
} from "../site-context";
|
|
28
|
+
import { componentRegistry, computeRelativePath } from "../files/components";
|
|
29
|
+
import { prepareForEditMode } from "../utils/edit-display";
|
|
30
|
+
import { getActiveElement } from "../editor/inline-edit";
|
|
31
|
+
import { buildNestedSiteCSS } from "./nested-site-style";
|
|
30
32
|
|
|
31
|
-
export { buildNestedSiteCSS } from "./nested-site-style
|
|
33
|
+
export { buildNestedSiteCSS } from "./nested-site-style";
|
|
34
|
+
|
|
35
|
+
import type { JxElement, JxMutableNode } from "@jxsuite/schema/types";
|
|
32
36
|
|
|
33
37
|
/** @param {Event} e */
|
|
34
|
-
function _preventNav(e) {
|
|
35
|
-
if (
|
|
38
|
+
function _preventNav(e: Event) {
|
|
39
|
+
if ((e.target as HTMLElement).closest("a[href]")) {
|
|
36
40
|
e.preventDefault();
|
|
37
41
|
}
|
|
38
42
|
}
|
|
@@ -40,8 +44,7 @@ function _preventNav(e) {
|
|
|
40
44
|
/** Canvas elements that already have the delegated nav guard listener. */
|
|
41
45
|
const _navGuarded = new WeakSet();
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
let _ctx = null;
|
|
47
|
+
let _ctx: { getCanvasMode: () => string } | null = null;
|
|
45
48
|
|
|
46
49
|
/** Set of DOM elements that originated from the layout (not page content). */
|
|
47
50
|
export const layoutElements = new WeakSet();
|
|
@@ -49,8 +52,7 @@ export const layoutElements = new WeakSet();
|
|
|
49
52
|
/** Cache of element HREFs that failed to load — prevents infinite retry loops. */
|
|
50
53
|
const _failedElements = new Set();
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
let _failedElementsDocPath = null;
|
|
55
|
+
let _failedElementsDocPath: string | null = null;
|
|
54
56
|
|
|
55
57
|
/**
|
|
56
58
|
* Walk the merged document tree to find the path prefix where page children were distributed into
|
|
@@ -61,7 +63,10 @@ let _failedElementsDocPath = null;
|
|
|
61
63
|
* @param {(string | number)[]} [path]
|
|
62
64
|
* @returns {(string | number)[] | null}
|
|
63
65
|
*/
|
|
64
|
-
function findPageContentPrefix(
|
|
66
|
+
function findPageContentPrefix(
|
|
67
|
+
node: JxMutableNode,
|
|
68
|
+
path: (string | number)[] = [],
|
|
69
|
+
): (string | number)[] | null {
|
|
65
70
|
if (!node || typeof node !== "object") return null;
|
|
66
71
|
if (Array.isArray(node.children)) {
|
|
67
72
|
for (let i = 0; i < node.children.length; i++) {
|
|
@@ -82,13 +87,13 @@ function findPageContentPrefix(node, path = []) {
|
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
/** The path of the currently active layout file, or null. */
|
|
85
|
-
export let activeLayoutPath =
|
|
90
|
+
export let activeLayoutPath = null as string | null;
|
|
86
91
|
|
|
87
92
|
/**
|
|
88
93
|
* Recursively mark all nodes in a layout doc tree with $__layout: true so we can identify which
|
|
89
94
|
* rendered DOM elements came from the layout vs page content.
|
|
90
95
|
*/
|
|
91
|
-
function markLayoutNodes(
|
|
96
|
+
function markLayoutNodes(node: JxMutableNode) {
|
|
92
97
|
if (!node || typeof node !== "object") return;
|
|
93
98
|
node.$__layout = true;
|
|
94
99
|
if (Array.isArray(node.children)) {
|
|
@@ -111,7 +116,7 @@ function markLayoutNodes(/** @type {JxMutableNode} */ node) {
|
|
|
111
116
|
* getCanvasMode: () => string;
|
|
112
117
|
* }} ctx
|
|
113
118
|
*/
|
|
114
|
-
export function initCanvasLiveRender(ctx) {
|
|
119
|
+
export function initCanvasLiveRender(ctx: { getCanvasMode: () => string }) {
|
|
115
120
|
_ctx = ctx;
|
|
116
121
|
}
|
|
117
122
|
|
|
@@ -124,10 +129,10 @@ export function initCanvasLiveRender(ctx) {
|
|
|
124
129
|
* @param {JxMutableNode} doc
|
|
125
130
|
* @param {HTMLElement} canvasEl
|
|
126
131
|
*/
|
|
127
|
-
export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
132
|
+
export async function renderCanvasLive(gen: number, doc: JxMutableNode, canvasEl: HTMLElement) {
|
|
128
133
|
const tab = activeTab.value;
|
|
129
134
|
const S = { documentPath: tab?.documentPath, mode: tab?.doc.mode, document: tab?.doc.document };
|
|
130
|
-
const canvasMode =
|
|
135
|
+
const canvasMode = _ctx!.getCanvasMode();
|
|
131
136
|
|
|
132
137
|
if (S.documentPath !== _failedElementsDocPath) {
|
|
133
138
|
_failedElements.clear();
|
|
@@ -180,15 +185,12 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
180
185
|
// so we can remap runtime paths (children,0,...) → (children,map,...)
|
|
181
186
|
const mapParentPaths = new Set();
|
|
182
187
|
if (canvasMode === "design" || canvasMode === "edit") {
|
|
183
|
-
(function findMapParents(
|
|
184
|
-
/** @type {JxMutableNode} */ node,
|
|
185
|
-
/** @type {(string | number)[]} */ path,
|
|
186
|
-
) {
|
|
188
|
+
(function findMapParents(node: JxMutableNode, path: (string | number)[]) {
|
|
187
189
|
if (!node || typeof node !== "object") return;
|
|
188
190
|
if (
|
|
189
191
|
node.children &&
|
|
190
192
|
typeof node.children === "object" &&
|
|
191
|
-
|
|
193
|
+
(node.children as unknown as { $prototype?: string }).$prototype === "Array"
|
|
192
194
|
) {
|
|
193
195
|
mapParentPaths.add(path.join("/"));
|
|
194
196
|
}
|
|
@@ -213,22 +215,17 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
213
215
|
const docBase = S.documentPath ? `${location.origin}/${docPrefix}${S.documentPath}` : undefined;
|
|
214
216
|
|
|
215
217
|
// Register custom elements so the runtime can render them
|
|
216
|
-
let effectiveElements = getEffectiveElements(
|
|
217
|
-
/** @type {(JxElement | string)[]} */ (renderDoc.$elements),
|
|
218
|
-
);
|
|
218
|
+
let effectiveElements = getEffectiveElements(renderDoc.$elements as (JxElement | string)[]);
|
|
219
219
|
|
|
220
220
|
// In content mode (markdown) or when a layout is applied, auto-discover components
|
|
221
221
|
// for custom elements that have no explicit $elements registration.
|
|
222
222
|
if ((S.mode === "content" || layoutWrapped) && componentRegistry.length > 0) {
|
|
223
223
|
const existingRefs = new Set(
|
|
224
|
-
effectiveElements.map((
|
|
225
|
-
typeof e === "string" ? e : e?.$ref,
|
|
226
|
-
),
|
|
224
|
+
effectiveElements.map((e: JxElement | string) => (typeof e === "string" ? e : e?.$ref)),
|
|
227
225
|
);
|
|
228
226
|
/** @param {JxMutableNode} node */
|
|
229
|
-
const collectTags = (node) => {
|
|
230
|
-
|
|
231
|
-
const tags = new Set();
|
|
227
|
+
const collectTags = (node: JxMutableNode) => {
|
|
228
|
+
const tags: Set<string> = new Set();
|
|
232
229
|
if (!node || typeof node !== "object") return tags;
|
|
233
230
|
if (node.tagName) tags.add(node.tagName);
|
|
234
231
|
if (Array.isArray(node.children)) {
|
|
@@ -241,10 +238,10 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
241
238
|
};
|
|
242
239
|
for (const tag of collectTags(renderDoc)) {
|
|
243
240
|
const comp = componentRegistry.find(
|
|
244
|
-
(
|
|
241
|
+
(c: import("../files/components.js").ComponentEntry) => c.tagName === tag,
|
|
245
242
|
);
|
|
246
243
|
if (comp && comp.source !== "npm") {
|
|
247
|
-
const relPath = computeRelativePath(S.documentPath, comp.path);
|
|
244
|
+
const relPath = computeRelativePath(S.documentPath ?? null, comp.path);
|
|
248
245
|
if (!existingRefs.has(relPath)) {
|
|
249
246
|
effectiveElements.push({ $ref: relPath });
|
|
250
247
|
existingRefs.add(relPath);
|
|
@@ -254,30 +251,28 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
254
251
|
}
|
|
255
252
|
|
|
256
253
|
if (effectiveElements.length) {
|
|
257
|
-
renderDoc.$elements =
|
|
258
|
-
/** @type {unknown} */ (effectiveElements)
|
|
259
|
-
);
|
|
254
|
+
renderDoc.$elements = effectiveElements as (string | JxMutableNode | { $ref: string })[];
|
|
260
255
|
for (const entry of effectiveElements) {
|
|
261
256
|
if (typeof entry === "string") {
|
|
262
257
|
try {
|
|
263
258
|
const specifier =
|
|
264
259
|
entry.startsWith("/") || entry.startsWith(".") ? entry : `/node_modules/${entry}`;
|
|
265
260
|
await import(specifier);
|
|
266
|
-
} catch (
|
|
261
|
+
} catch (e) {
|
|
267
262
|
console.warn("Studio: failed to import package", entry, e);
|
|
268
263
|
}
|
|
269
264
|
} else if (entry?.$ref) {
|
|
270
265
|
let href;
|
|
271
266
|
try {
|
|
272
267
|
href = new URL(entry.$ref, docBase).href;
|
|
273
|
-
} catch (
|
|
268
|
+
} catch (urlErr) {
|
|
274
269
|
console.warn("Studio: invalid element URL", { ref: entry.$ref, docBase }, urlErr);
|
|
275
270
|
continue;
|
|
276
271
|
}
|
|
277
272
|
if (_failedElements.has(href)) continue;
|
|
278
273
|
try {
|
|
279
274
|
await defineElement(href);
|
|
280
|
-
} catch (
|
|
275
|
+
} catch (e) {
|
|
281
276
|
_failedElements.add(href);
|
|
282
277
|
console.warn("Studio: failed to register element", entry.$ref, e);
|
|
283
278
|
}
|
|
@@ -297,10 +292,10 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
297
292
|
// This ensures project font-family, color, etc. override the
|
|
298
293
|
// content-mode fallback typography rules in the stylesheet.
|
|
299
294
|
// In edit mode, propagate to the .content-edit-canvas wrapper for seamless appearance.
|
|
300
|
-
const viewport =
|
|
295
|
+
const viewport = canvasEl.closest(".canvas-panel-viewport") as HTMLElement | null;
|
|
301
296
|
const editSurface =
|
|
302
297
|
canvasMode === "edit"
|
|
303
|
-
?
|
|
298
|
+
? (canvasEl.closest(".content-edit-canvas") as HTMLElement | null)
|
|
304
299
|
: null;
|
|
305
300
|
const siteStyle = projectState?.projectConfig?.style;
|
|
306
301
|
if (viewport) {
|
|
@@ -311,8 +306,7 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
311
306
|
if (k.startsWith("--")) {
|
|
312
307
|
viewport.style.setProperty(k, String(v));
|
|
313
308
|
} else {
|
|
314
|
-
|
|
315
|
-
String(v);
|
|
309
|
+
(viewport.style as unknown as Record<string, string>)[k] = String(v);
|
|
316
310
|
}
|
|
317
311
|
}
|
|
318
312
|
}
|
|
@@ -324,8 +318,7 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
324
318
|
if (k.startsWith("--")) {
|
|
325
319
|
editSurface.style.setProperty(k, String(v));
|
|
326
320
|
} else {
|
|
327
|
-
|
|
328
|
-
String(v);
|
|
321
|
+
(editSurface.style as unknown as Record<string, string>)[k] = String(v);
|
|
329
322
|
}
|
|
330
323
|
}
|
|
331
324
|
}
|
|
@@ -334,8 +327,7 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
334
327
|
for (const [k, v] of Object.entries(siteStyle)) {
|
|
335
328
|
if (v !== null && typeof v === "object" && !Array.isArray(v)) continue;
|
|
336
329
|
if (!k.startsWith("--")) {
|
|
337
|
-
|
|
338
|
-
String(v);
|
|
330
|
+
(canvasEl.style as unknown as Record<string, string>)[k] = String(v);
|
|
339
331
|
}
|
|
340
332
|
}
|
|
341
333
|
}
|
|
@@ -383,7 +375,7 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
383
375
|
const selector = `${tag}${attrs.href ? `[href="${attrs.href}"]` : ""}${attrs.src ? `[src="${attrs.src}"]` : ""}`;
|
|
384
376
|
if (selector !== tag && document.head.querySelector(selector)) continue;
|
|
385
377
|
const el = document.createElement(tag);
|
|
386
|
-
for (const [k, v] of Object.entries(attrs)) el.setAttribute(k,
|
|
378
|
+
for (const [k, v] of Object.entries(attrs)) el.setAttribute(k, String(v));
|
|
387
379
|
if (entry.textContent) el.textContent = entry.textContent;
|
|
388
380
|
document.head.appendChild(el);
|
|
389
381
|
}
|
|
@@ -395,9 +387,9 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
395
387
|
const el = /** @type {HTMLElement} */ (
|
|
396
388
|
runtimeRenderNode(renderDoc, $defs, {
|
|
397
389
|
onNodeCreated(
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
390
|
+
el: HTMLElement | Text,
|
|
391
|
+
path: (string | number)[],
|
|
392
|
+
def: Record<string, unknown>,
|
|
401
393
|
) {
|
|
402
394
|
if (!(el instanceof HTMLElement)) return;
|
|
403
395
|
// Track layout-originated elements — don't store in elToPath to avoid
|
|
@@ -415,9 +407,7 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
415
407
|
const pfx = pageContentPrefix;
|
|
416
408
|
if (
|
|
417
409
|
path.length >= pfx.length &&
|
|
418
|
-
pfx.every(
|
|
419
|
-
(/** @type {string | number} */ seg, /** @type {number} */ i) => path[i] === seg,
|
|
420
|
-
)
|
|
410
|
+
pfx.every((seg: string | number, i: number) => path[i] === seg)
|
|
421
411
|
) {
|
|
422
412
|
mappedPath = ["children", ...path.slice(pfx.length)];
|
|
423
413
|
}
|
|
@@ -459,7 +449,7 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
459
449
|
// Disable pointer events on all rendered elements for edit mode
|
|
460
450
|
el.style.pointerEvents = "none";
|
|
461
451
|
for (const child of el.querySelectorAll("*")) {
|
|
462
|
-
|
|
452
|
+
(child as HTMLElement).style.pointerEvents = "none";
|
|
463
453
|
}
|
|
464
454
|
}
|
|
465
455
|
// Clear and append atomically — ensures the canvas is never left empty if a
|
|
@@ -486,13 +476,13 @@ export async function renderCanvasLive(gen, doc, canvasEl) {
|
|
|
486
476
|
for (const child of canvasEl.querySelectorAll("*")) {
|
|
487
477
|
if (view.componentInlineEdit && child === view.componentInlineEdit.el) continue;
|
|
488
478
|
if (editingEl && child === editingEl) continue;
|
|
489
|
-
|
|
479
|
+
(child as HTMLElement).style.pointerEvents = "none";
|
|
490
480
|
}
|
|
491
481
|
});
|
|
492
482
|
}
|
|
493
483
|
return $defs;
|
|
494
|
-
} catch (
|
|
495
|
-
console.warn("renderCanvasLive failed:",
|
|
484
|
+
} catch (err) {
|
|
485
|
+
console.warn("renderCanvasLive failed:", (err as Error).message, err);
|
|
496
486
|
return null;
|
|
497
487
|
}
|
|
498
488
|
}
|