@jxsuite/studio 0.23.2 → 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.
Files changed (105) hide show
  1. package/dist/studio.js +2230 -714
  2. package/dist/studio.js.map +121 -104
  3. package/package.json +8 -7
  4. package/src/browse/{browse-modal.js → browse-modal.ts} +9 -10
  5. package/src/browse/{browse.js → browse.ts} +109 -84
  6. package/src/canvas/{canvas-diff.js → canvas-diff.ts} +42 -35
  7. package/src/canvas/{canvas-helpers.js → canvas-helpers.ts} +25 -19
  8. package/src/canvas/{canvas-live-render.js → canvas-live-render.ts} +53 -63
  9. package/src/canvas/{canvas-render.js → canvas-render.ts} +110 -121
  10. package/src/canvas/{canvas-utils.js → canvas-utils.ts} +48 -44
  11. package/src/canvas/{nested-site-style.js → nested-site-style.ts} +3 -3
  12. package/src/editor/{component-inline-edit.js → component-inline-edit.ts} +45 -33
  13. package/src/editor/{content-inline-edit.js → content-inline-edit.ts} +27 -45
  14. package/src/editor/{context-menu.js → context-menu.ts} +38 -36
  15. package/src/editor/{convert-targets.js → convert-targets.ts} +15 -12
  16. package/src/editor/{convert-to-component.js → convert-to-component.ts} +25 -26
  17. package/src/editor/{convert-to-repeater.js → convert-to-repeater.ts} +60 -40
  18. package/src/editor/{inline-edit.js → inline-edit.ts} +91 -55
  19. package/src/editor/{inline-format.js → inline-format.ts} +46 -51
  20. package/src/editor/{insertion-helper.js → insertion-helper.ts} +47 -63
  21. package/src/editor/{shortcuts.js → shortcuts.ts} +45 -31
  22. package/src/editor/{slash-menu.js → slash-menu.ts} +34 -35
  23. package/src/files/{components.js → components.ts} +12 -15
  24. package/src/files/{file-ops.js → file-ops.ts} +36 -29
  25. package/src/files/{files.js → files.ts} +102 -84
  26. package/src/github/{github-auth.js → github-auth.ts} +3 -3
  27. package/src/github/{github-publish.js → github-publish.ts} +20 -18
  28. package/src/markdown/{md-allowlist.js → md-allowlist.ts} +5 -2
  29. package/src/markdown/{md-convert.js → md-convert.ts} +146 -169
  30. package/src/new-project/{new-project-modal.js → new-project-modal.ts} +16 -18
  31. package/src/panels/{activity-bar.js → activity-bar.ts} +26 -34
  32. package/src/panels/{ai-panel.js → ai-panel.ts} +30 -30
  33. package/src/panels/{block-action-bar.js → block-action-bar.ts} +64 -73
  34. package/src/panels/{canvas-dnd.js → canvas-dnd.ts} +44 -49
  35. package/src/panels/{data-explorer.js → data-explorer.ts} +21 -15
  36. package/src/panels/dnd.ts +417 -0
  37. package/src/panels/{editors.js → editors.ts} +43 -50
  38. package/src/panels/{elements-panel.js → elements-panel.ts} +30 -27
  39. package/src/panels/{events-panel.js → events-panel.ts} +33 -25
  40. package/src/panels/{git-panel.js → git-panel.ts} +97 -83
  41. package/src/panels/{head-panel.js → head-panel.ts} +117 -139
  42. package/src/panels/{imports-panel.js → imports-panel.ts} +69 -89
  43. package/src/panels/{layers-panel.js → layers-panel.ts} +61 -57
  44. package/src/panels/{left-panel.js → left-panel.ts} +56 -63
  45. package/src/panels/{overlays.js → overlays.ts} +28 -36
  46. package/src/panels/{panel-events.js → panel-events.ts} +53 -51
  47. package/src/panels/{preview-render.js → preview-render.ts} +20 -15
  48. package/src/panels/{properties-panel.js → properties-panel.ts} +212 -246
  49. package/src/panels/{pseudo-preview.js → pseudo-preview.ts} +9 -8
  50. package/src/panels/{quick-search.js → quick-search.ts} +16 -17
  51. package/src/panels/{right-panel.js → right-panel.ts} +41 -59
  52. package/src/panels/{shared.js → shared.ts} +7 -5
  53. package/src/panels/{signals-panel.js → signals-panel.ts} +322 -362
  54. package/src/panels/{statusbar.js → statusbar.ts} +14 -14
  55. package/src/panels/{style-inputs.js → style-inputs.ts} +66 -48
  56. package/src/panels/{style-panel.js → style-panel.ts} +235 -290
  57. package/src/panels/{style-utils.js → style-utils.ts} +32 -31
  58. package/src/panels/{stylebook-layers-panel.js → stylebook-layers-panel.ts} +22 -14
  59. package/src/panels/{stylebook-panel.js → stylebook-panel.ts} +127 -140
  60. package/src/panels/{tab-strip.js → tab-strip.ts} +12 -14
  61. package/src/panels/{toolbar.js → toolbar.ts} +68 -70
  62. package/src/panels/{welcome-screen.js → welcome-screen.ts} +14 -16
  63. package/src/{platform.js → platform.ts} +5 -3
  64. package/src/platforms/{devserver.js → devserver.ts} +53 -44
  65. package/src/{recent-projects.js → recent-projects.ts} +17 -12
  66. package/src/{resize-edges.js → resize-edges.ts} +18 -13
  67. package/src/services/{cem-export.js → cem-export.ts} +14 -7
  68. package/src/services/{code-services.js → code-services.ts} +29 -20
  69. package/src/services/{monaco-setup.js → monaco-setup.ts} +2 -3
  70. package/src/settings/{content-types-editor.js → content-types-editor.ts} +88 -67
  71. package/src/settings/{css-vars-editor.js → css-vars-editor.ts} +78 -51
  72. package/src/settings/{defs-editor.js → defs-editor.ts} +70 -56
  73. package/src/settings/{general-settings.js → general-settings.ts} +29 -28
  74. package/src/settings/{head-editor.js → head-editor.ts} +37 -35
  75. package/src/settings/{schema-field-ui.js → schema-field-ui.ts} +86 -76
  76. package/src/settings/{settings-modal.js → settings-modal.ts} +14 -16
  77. package/src/{site-context.js → site-context.ts} +35 -32
  78. package/src/{state.js → state.ts} +95 -78
  79. package/src/{store.js → store.ts} +60 -63
  80. package/src/{studio.js → studio.ts} +117 -119
  81. package/src/tabs/tab.ts +196 -0
  82. package/src/tabs/{transact.js → transact.ts} +107 -60
  83. package/src/types.ts +185 -0
  84. package/src/ui/{button-group.js → button-group.ts} +21 -21
  85. package/src/ui/{color-selector.js → color-selector.ts} +44 -37
  86. package/src/ui/{expression-editor.js → expression-editor.ts} +65 -40
  87. package/src/ui/{field-row.js → field-row.ts} +19 -2
  88. package/src/ui/{icons.js → icons.ts} +2 -2
  89. package/src/ui/{layers.js → layers.ts} +33 -24
  90. package/src/ui/{media-picker.js → media-picker.ts} +13 -9
  91. package/src/ui/{panel-resize.js → panel-resize.ts} +9 -4
  92. package/src/ui/{spectrum.js → spectrum.ts} +4 -4
  93. package/src/ui/{unit-selector.js → unit-selector.ts} +16 -19
  94. package/src/ui/{value-selector.js → value-selector.ts} +28 -28
  95. package/src/ui/{widgets.js → widgets.ts} +62 -15
  96. package/src/utils/{canvas-media.js → canvas-media.ts} +29 -14
  97. package/src/utils/{edit-display.js → edit-display.ts} +23 -20
  98. package/src/utils/{google-fonts.js → google-fonts.ts} +14 -13
  99. package/src/utils/{inherited-style.js → inherited-style.ts} +15 -10
  100. package/src/utils/{studio-utils.js → studio-utils.ts} +35 -23
  101. package/src/{view.js → view.ts} +53 -55
  102. package/src/workspace/{workspace.js → workspace.ts} +44 -33
  103. package/src/panels/dnd.js +0 -426
  104. package/src/tabs/tab.js +0 -192
  105. /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
- * @typedef {JxMutableNode} JxNode
10
- *
11
- * @typedef {"added" | "removed" | "modified"} DiffStatus
12
- *
13
- * @typedef {{ byPath: Map<string, DiffStatus>; allPaths: Set<string> }} DiffResult
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 = /** @type {unknown[]} */ (b);
31
+ const bArr = b as unknown[];
30
32
  if (a.length !== bArr.length) return false;
31
- return a.every((/** @type {unknown} */ v, /** @type {number} */ i) => valuesEqual(v, bArr[i]));
33
+ return a.every((v: unknown, i: number) => valuesEqual(v, bArr[i]));
32
34
  }
33
35
 
34
- const aObj = /** @type {Record<string, unknown>} */ (a);
35
- const bObj = /** @type {Record<string, unknown>} */ (b);
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
- (/** @type {string} */ k) => !k.startsWith("on") && k !== "$ref",
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((/** @type {string} */ k) => keysB.includes(k))) return false;
43
+ if (!keysA.every((k: string) => keysB.includes(k))) return false;
46
44
 
47
- return keysA.every((/** @type {string} */ k) => valuesEqual(aObj[k], bObj[k]));
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 /** @type {JxMutableNode[]} */ (
59
- node.children.filter(
60
- (/** @type {JxMutableNode | string} */ c) => c != null && typeof c === "object",
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(node, status, path, diffMap, allPaths) {
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(originalDoc, currentDoc) {
92
- /** @type {Map<string, DiffStatus>} */
93
- const diffMap = new Map();
94
- /** @type {Set<string>} */
95
- const allPaths = new Set();
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 = (origNode, currNode, path = "") => {
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((/** @type {Element} */ el) => {
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.js";
7
- import { activeTab } from "../workspace/workspace.js";
8
- import { isInlineInContext } from "../editor/inline-edit.js";
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
- /** @type {{ getCanvasMode: () => string; getZoom: () => number } | null} */
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 = /** @type {{ getCanvasMode: () => string; getZoom: () => number }} */ (_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 = /** @type {JxPath} */ (pPath);
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
- /** @type {HTMLElement | null | undefined} */
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 = /** @type {HTMLElement | undefined} */ (el.children[0]);
84
+ el = el.children[0] as HTMLElement | undefined;
83
85
  } else if (idx === "map") {
84
- el = /** @type {HTMLElement | undefined} */ (el.children[0]?.children[0]);
86
+ el = el.children[0]?.children[0] as HTMLElement | undefined;
85
87
  } else {
86
- el = /** @type {HTMLElement | undefined} */ (el.children[/** @type {number} */ (idx)]);
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 /** @type {HTMLElement} */ (candidate);
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("./canvas-render.js").CanvasPanel} panel
110
+ * @param {import("../panels/canvas-dnd.js").CanvasPanel} panel
109
111
  */
110
- export function overlayBoxDescriptor(el, type, panel) {
111
- const viewport = /** @type {HTMLElement} */ (panel.viewport);
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.js";
8
- import { activeTab } from "../workspace/workspace.js";
9
- import { view } from "../view.js";
10
- import { toRaw } from "../reactivity.js";
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.js";
26
- import { componentRegistry, computeRelativePath } from "../files/components.js";
27
- import { prepareForEditMode } from "../utils/edit-display.js";
28
- import { getActiveElement } from "../editor/inline-edit.js";
29
- import { buildNestedSiteCSS } from "./nested-site-style.js";
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.js";
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 (/** @type {HTMLElement} */ (e.target).closest("a[href]")) {
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
- /** @type {{ getCanvasMode: () => string } | null} */
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
- /** @type {string | null} */
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(node, path = []) {
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 = /** @type {string | null} */ (null);
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(/** @type {JxMutableNode} */ node) {
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 = /** @type {{ getCanvasMode: () => string }} */ (_ctx).getCanvasMode();
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
- /** @type {{ $prototype?: string }} */ (node.children).$prototype === "Array"
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((/** @type {JxElement | string} */ e) =>
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
- /** @type {Set<string>} */
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
- (/** @type {import("../files/components.js").ComponentEntry} */ c) => c.tagName === tag,
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 = /** @type {(JxMutableNode | string | { $ref: string })[]} */ (
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 (/** @type {unknown} */ e) {
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 (/** @type {unknown} */ urlErr) {
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 (/** @type {unknown} */ e) {
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 = /** @type {HTMLElement | null} */ (canvasEl.closest(".canvas-panel-viewport"));
295
+ const viewport = canvasEl.closest(".canvas-panel-viewport") as HTMLElement | null;
301
296
  const editSurface =
302
297
  canvasMode === "edit"
303
- ? /** @type {HTMLElement | null} */ (canvasEl.closest(".content-edit-canvas"))
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
- /** @type {Record<string, string>} */ (/** @type {unknown} */ (viewport.style))[k] =
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
- /** @type {Record<string, string>} */ (/** @type {unknown} */ (editSurface.style))[k] =
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
- /** @type {Record<string, string>} */ (/** @type {unknown} */ (canvasEl.style))[k] =
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, /** @type {string} */ (v));
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
- /** @type {HTMLElement | Text} */ el,
399
- /** @type {(string | number)[]} */ path,
400
- /** @type {Record<string, unknown>} */ def,
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
- /** @type {HTMLElement} */ (child).style.pointerEvents = "none";
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
- /** @type {HTMLElement} */ (child).style.pointerEvents = "none";
479
+ (child as HTMLElement).style.pointerEvents = "none";
490
480
  }
491
481
  });
492
482
  }
493
483
  return $defs;
494
- } catch (/** @type {unknown} */ err) {
495
- console.warn("renderCanvasLive failed:", /** @type {Error} */ (err).message, err);
484
+ } catch (err) {
485
+ console.warn("renderCanvasLive failed:", (err as Error).message, err);
496
486
  return null;
497
487
  }
498
488
  }