@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 render — extracted from studio.js (Phase 4o). Multi-mode canvas rendering orchestrator:
3
4
  * dispatches to manage/settings/source/edit/design/preview rendering paths.
@@ -7,12 +8,12 @@ import { html, render as litRender, nothing } from "lit-html";
7
8
  import { ref } from "lit-html/directives/ref.js";
8
9
  import * as monaco from "monaco-editor/esm/vs/editor/editor.api.js";
9
10
 
10
- import { canvasWrap, canvasPanels, updateCanvas } from "../store.js";
11
- import { activeTab } from "../workspace/workspace.js";
12
- import { view } from "../view.js";
13
- import { loadMarkdown } from "../files/file-ops.js";
14
- import { renderWelcome } from "../panels/welcome-screen.js";
15
- import { projectState } from "../state.js";
11
+ import { canvasWrap, canvasPanels, updateCanvas } from "../store";
12
+ import { activeTab } from "../workspace/workspace";
13
+ import { view } from "../view";
14
+ import { loadMarkdown } from "../files/file-ops";
15
+ import { renderWelcome } from "../panels/welcome-screen";
16
+ import { projectState } from "../state";
16
17
  import {
17
18
  canvasPanelTemplate,
18
19
  applyTransform,
@@ -20,61 +21,47 @@ import {
20
21
  renderZoomIndicator,
21
22
  resetZoomIndicator,
22
23
  updateActivePanelHeaders,
23
- } from "./canvas-utils.js";
24
- import { effectiveZoom, overlayBoxDescriptor } from "./canvas-helpers.js";
24
+ } from "./canvas-utils";
25
+ import { effectiveZoom, overlayBoxDescriptor } from "./canvas-helpers";
25
26
  import {
26
27
  parseMediaEntries,
27
28
  activeBreakpointsForWidth,
28
29
  collectMediaOverrides,
29
30
  applyOverridesToCanvas,
30
- } from "../utils/canvas-media.js";
31
- import { getEffectiveMedia } from "../site-context.js";
32
- import { renderCanvasLive } from "./canvas-live-render.js";
33
- import { renderCanvasNode } from "../panels/preview-render.js";
34
- import { registerPanelDnD } from "../panels/canvas-dnd.js";
35
- import { registerPanelEvents } from "../panels/panel-events.js";
36
- import { computeDocumentDiff } from "./canvas-diff.js";
37
- import { updateForcedPseudoPreview } from "../panels/pseudo-preview.js";
38
- import { findCanvasElement } from "./canvas-helpers.js";
39
- import { enterComponentInlineEdit } from "../editor/component-inline-edit.js";
40
- import { renderStylebookMode, refreshStylebookStyles } from "../panels/stylebook-panel.js";
41
- import { dismissLinkPopover, dismissBlockActionBar } from "../panels/block-action-bar.js";
42
- import { dismissContextMenu } from "../editor/context-menu.js";
43
- import { dismissSlashMenu } from "../editor/slash-menu.js";
44
- import { renderFunctionEditor } from "../panels/editors.js";
45
- import { mediaDisplayName } from "../panels/shared.js";
46
- import { statusMessage } from "../panels/statusbar.js";
47
- import * as overlaysPanel from "../panels/overlays.js";
48
-
49
- /** @typedef {import("../panels/canvas-dnd.js").CanvasPanel} CanvasPanel */
50
-
51
- /**
52
- * @typedef {{
53
- * filePath: string;
54
- * originalContent: string;
55
- * currentContent: string;
56
- * isMarkdown: boolean;
57
- * fileStatus: string;
58
- * originalDoc?: JxMutableNode | null;
59
- * currentDoc?: JxMutableNode | null;
60
- * original?: JxMutableNode | null;
61
- * }} GitDiffState
62
- */
63
-
64
- /**
65
- * @typedef {{
66
- * getCanvasMode: () => string;
67
- * setCanvasMode: (mode: string) => void;
68
- * openFileFromTree: (path: string) => void;
69
- * exportFile: () => void;
70
- * closeFunctionEditor: () => void;
71
- * gitDiffState: GitDiffState | null;
72
- * setGitDiffState: (state: GitDiffState | null) => void;
73
- * }} CanvasRenderCtx
74
- */
31
+ } from "../utils/canvas-media";
32
+ import { getEffectiveMedia } from "../site-context";
33
+ import { renderCanvasLive } from "./canvas-live-render";
34
+ import { renderCanvasNode } from "../panels/preview-render";
35
+ import { registerPanelDnD } from "../panels/canvas-dnd";
36
+ import { registerPanelEvents } from "../panels/panel-events";
37
+ import { computeDocumentDiff } from "./canvas-diff";
38
+ import { updateForcedPseudoPreview } from "../panels/pseudo-preview";
39
+ import { findCanvasElement } from "./canvas-helpers";
40
+ import { enterComponentInlineEdit } from "../editor/component-inline-edit";
41
+ import { renderStylebookMode, refreshStylebookStyles } from "../panels/stylebook-panel";
42
+ import { dismissLinkPopover, dismissBlockActionBar } from "../panels/block-action-bar";
43
+ import { dismissContextMenu } from "../editor/context-menu";
44
+ import { dismissSlashMenu } from "../editor/slash-menu";
45
+ import { renderFunctionEditor } from "../panels/editors";
46
+ import { mediaDisplayName } from "../panels/shared";
47
+ import { statusMessage } from "../panels/statusbar";
48
+ import * as overlaysPanel from "../panels/overlays";
49
+
50
+ import type { CanvasPanel } from "../panels/canvas-dnd";
51
+ import type { GitDiffState, InlineEditDef } from "../types";
52
+ import type { JxMutableNode } from "@jxsuite/schema/types";
53
+
54
+ interface CanvasRenderCtx {
55
+ getCanvasMode: () => string;
56
+ setCanvasMode: (mode: string) => void;
57
+ openFileFromTree: (path: string) => void;
58
+ exportFile: () => void;
59
+ closeFunctionEditor: () => void;
60
+ gitDiffState: GitDiffState | null;
61
+ setGitDiffState: (state: GitDiffState | null) => void;
62
+ }
75
63
 
76
- /** @type {CanvasRenderCtx | null} */
77
- let _ctx = null;
64
+ let _ctx: CanvasRenderCtx | null = null;
78
65
 
79
66
  let _prevStylebookFilter = "";
80
67
  let _prevStylebookCustomizedOnly = false;
@@ -84,7 +71,7 @@ let _prevStylebookCustomizedOnly = false;
84
71
  *
85
72
  * @param {CanvasRenderCtx} ctx
86
73
  */
87
- export function initCanvasRender(ctx) {
74
+ export function initCanvasRender(ctx: CanvasRenderCtx) {
88
75
  _ctx = ctx;
89
76
  }
90
77
 
@@ -98,7 +85,7 @@ export function renderCanvas() {
98
85
  }
99
86
  return;
100
87
  }
101
- const ctx = /** @type {CanvasRenderCtx} */ (_ctx);
88
+ const ctx = _ctx as CanvasRenderCtx;
102
89
  const S = { document: tab.doc.document, ui: tab.session.ui, mode: tab.doc.mode };
103
90
  const canvasMode = ctx.getCanvasMode();
104
91
 
@@ -227,8 +214,7 @@ export function renderCanvas() {
227
214
  if (canvasMode === "source") {
228
215
  canvasWrap.style.padding = "0";
229
216
  canvasWrap.style.display = "block";
230
- /** @type {HTMLDivElement | null} */
231
- let editorContainer = null;
217
+ let editorContainer: HTMLDivElement | null = null;
232
218
  litRender(
233
219
  html`<div class="source-wrap">
234
220
  <div class="source-toolbar">
@@ -240,7 +226,7 @@ export function renderCanvas() {
240
226
  <div
241
227
  class="source-editor"
242
228
  ${ref((el) => {
243
- if (el) editorContainer = /** @type {HTMLDivElement} */ (el);
229
+ if (el) editorContainer = el as HTMLDivElement;
244
230
  })}
245
231
  ></div>
246
232
  </div>`,
@@ -253,25 +239,21 @@ export function renderCanvas() {
253
239
  lang === "json" ? JSON.stringify(S.document, null, 2) : S.document?.toString?.() || "";
254
240
  const modelUri = monaco.Uri.parse("file:///" + filePath);
255
241
  const model = monaco.editor.createModel(content, lang, modelUri);
256
- view.monacoEditor = monaco.editor.create(
257
- /** @type {HTMLElement} */ (/** @type {unknown} */ (editorContainer)),
258
- {
259
- model,
260
- theme: "vs-dark",
261
- automaticLayout: true,
262
- minimap: { enabled: false },
263
- fontSize: 12,
264
- fontFamily: "'SF Mono', 'Fira Code', 'Consolas', monospace",
265
- lineNumbers: "on",
266
- scrollBeyondLastLine: false,
267
- wordWrap: "on",
268
- tabSize: 2,
269
- },
270
- );
242
+ view.monacoEditor = monaco.editor.create(editorContainer as unknown as HTMLElement, {
243
+ model,
244
+ theme: "vs-dark",
245
+ automaticLayout: true,
246
+ minimap: { enabled: false },
247
+ fontSize: 12,
248
+ fontFamily: "'SF Mono', 'Fira Code', 'Consolas', monospace",
249
+ lineNumbers: "on",
250
+ scrollBeyondLastLine: false,
251
+ wordWrap: "on",
252
+ tabSize: 2,
253
+ });
271
254
 
272
255
  // Debounced sync back to state
273
- /** @type {ReturnType<typeof setTimeout> | undefined} */
274
- let debounce;
256
+ let debounce: ReturnType<typeof setTimeout> | undefined;
275
257
  view.monacoEditor.onDidChangeModelContent(() => {
276
258
  const editor = view.monacoEditor;
277
259
  if (!editor) return;
@@ -282,6 +264,7 @@ export function renderCanvas() {
282
264
  clearTimeout(debounce);
283
265
  debounce = setTimeout(() => {
284
266
  const tab = activeTab.value;
267
+ if (!tab) return;
285
268
  if (lang === "json") {
286
269
  try {
287
270
  tab.doc.document = JSON.parse(editor.getValue());
@@ -332,7 +315,7 @@ export function renderCanvas() {
332
315
  class="panzoom-wrap"
333
316
  style="transform-origin:0 0"
334
317
  ${ref((el) => {
335
- if (el) view.panzoomWrap = /** @type {HTMLDivElement} */ (el);
318
+ if (el) view.panzoomWrap = el as HTMLDivElement;
336
319
  })}
337
320
  >
338
321
  ${origTpl} ${currTpl}
@@ -341,11 +324,11 @@ export function renderCanvas() {
341
324
  canvasWrap,
342
325
  );
343
326
 
344
- canvasPanels.push(/** @type {CanvasPanel} */ (origPanel));
345
- canvasPanels.push(/** @type {CanvasPanel} */ (currPanel));
327
+ canvasPanels.push(origPanel as unknown as CanvasPanel);
328
+ canvasPanels.push(currPanel as unknown as CanvasPanel);
346
329
 
347
330
  /** @param {string} content */
348
- const parseContent = (content) => {
331
+ const parseContent = (content: string) => {
349
332
  if (gitDiffState.isMarkdown) {
350
333
  return loadMarkdown(content).then((r) => r.document);
351
334
  }
@@ -364,15 +347,15 @@ export function renderCanvas() {
364
347
  parseContent(gitDiffState.currentContent || ""),
365
348
  ]).then(([originalDoc, currentDoc]) => {
366
349
  renderCanvasIntoPanel(
367
- /** @type {CanvasPanel} */ (origPanel),
368
- new Set(),
350
+ origPanel as unknown as CanvasPanel,
351
+ new Set<string>(),
369
352
  featureToggles,
370
353
  originalDoc,
371
354
  gitDiffState,
372
355
  );
373
356
  renderCanvasIntoPanel(
374
- /** @type {CanvasPanel} */ (currPanel),
375
- new Set(),
357
+ currPanel as unknown as CanvasPanel,
358
+ new Set<string>(),
376
359
  featureToggles,
377
360
  currentDoc,
378
361
  gitDiffState,
@@ -400,16 +383,16 @@ export function renderCanvas() {
400
383
  const editTpl = html`
401
384
  <div
402
385
  class="content-edit-canvas"
403
- ${ref((el) => {
404
- panel.scrollContainer = /** @type {HTMLElement | null} */ (el || null);
386
+ ${ref((el: Element | undefined) => {
387
+ panel.scrollContainer = (el as HTMLElement) || null;
405
388
  })}
406
389
  >
407
390
  <div class="content-edit-column" style="max-width:${baseWidth}px">${panelTpl}</div>
408
391
  </div>
409
392
  `;
410
393
  litRender(editTpl, canvasWrap);
411
- canvasPanels.push(/** @type {CanvasPanel} */ (panel));
412
- renderCanvasIntoPanel(/** @type {CanvasPanel} */ (panel), new Set(), S.ui.featureToggles);
394
+ canvasPanels.push(panel as unknown as CanvasPanel);
395
+ renderCanvasIntoPanel(panel as unknown as CanvasPanel, new Set<string>(), S.ui.featureToggles);
413
396
  return;
414
397
  }
415
398
 
@@ -445,7 +428,7 @@ export function renderCanvas() {
445
428
  class="panzoom-wrap"
446
429
  style="transform-origin:0 0"
447
430
  ${ref((el) => {
448
- if (el) view.panzoomWrap = /** @type {HTMLDivElement} */ (el);
431
+ if (el) view.panzoomWrap = el as HTMLDivElement;
449
432
  })}
450
433
  >
451
434
  ${panelTpl}
@@ -453,8 +436,8 @@ export function renderCanvas() {
453
436
  `,
454
437
  canvasWrap,
455
438
  );
456
- canvasPanels.push(/** @type {CanvasPanel} */ (panel));
457
- renderCanvasIntoPanel(/** @type {CanvasPanel} */ (panel), new Set(), featureToggles);
439
+ canvasPanels.push(panel as unknown as CanvasPanel);
440
+ renderCanvasIntoPanel(panel as unknown as CanvasPanel, new Set<string>(), featureToggles);
458
441
  applyTransform();
459
442
  if (modeChanged) {
460
443
  observeCenterUntilStable();
@@ -494,7 +477,7 @@ export function renderCanvas() {
494
477
  class="panzoom-wrap"
495
478
  style="transform-origin:0 0"
496
479
  ${ref((el) => {
497
- if (el) view.panzoomWrap = /** @type {HTMLDivElement} */ (el);
480
+ if (el) view.panzoomWrap = el as HTMLDivElement;
498
481
  })}
499
482
  >
500
483
  ${panelEntries.map((e) => e.tpl)}
@@ -503,10 +486,15 @@ export function renderCanvas() {
503
486
  canvasWrap,
504
487
  );
505
488
 
506
- for (const { panel, activeSet } of panelEntries) {
507
- const p = /** @type {CanvasPanel} */ (panel);
489
+ for (let i = 0; i < panelEntries.length; i++) {
490
+ const { panel, activeSet } = panelEntries[i];
491
+ const p = panel as CanvasPanel;
508
492
  canvasPanels.push(p);
509
- renderCanvasIntoPanel(p, activeSet, featureToggles);
493
+ if (i === 0) {
494
+ renderCanvasIntoPanel(p, activeSet, featureToggles);
495
+ } else {
496
+ setTimeout(() => renderCanvasIntoPanel(p, activeSet, featureToggles), 0);
497
+ }
510
498
  }
511
499
 
512
500
  // Highlight active panel header
@@ -535,19 +523,19 @@ export function renderCanvas() {
535
523
  * applies diff highlighting.
536
524
  */
537
525
  function renderCanvasIntoPanel(
538
- panel,
539
- activeBreakpoints,
540
- featureToggles,
541
- docOverride = null,
542
- gitDiffState = null,
526
+ panel: CanvasPanel,
527
+ activeBreakpoints: Set<string>,
528
+ featureToggles: Record<string, boolean>,
529
+ docOverride: JxMutableNode | null = null,
530
+ gitDiffState: GitDiffState | null = null,
543
531
  ) {
544
532
  const gen = view.renderGeneration;
545
533
  const tab = activeTab.value;
546
- const docToRender = /** @type {JxMutableNode} */ (docOverride || tab?.doc.document);
547
- const canvas = /** @type {HTMLElement} */ (panel.canvas);
534
+ const docToRender = docOverride || (tab?.doc.document as JxMutableNode);
535
+ const canvas = panel.canvas as HTMLElement;
548
536
 
549
537
  renderCanvasLive(gen, docToRender, canvas)
550
- .then((/** @type {Record<string, unknown> | null} */ scope) => {
538
+ .then((scope: Record<string, unknown> | null) => {
551
539
  // Skip post-render setup if a newer render has started
552
540
  if (gen !== view.renderGeneration) return;
553
541
  if (scope) {
@@ -578,20 +566,18 @@ function renderCanvasIntoPanel(
578
566
  renderCanvasNode(docToRender, [], canvas, activeBreakpoints, featureToggles);
579
567
  }
580
568
  try {
581
- registerPanelDnD(/** @type {CanvasPanel} */ (panel));
582
- } catch (/** @type {unknown} */ e) {
583
- console.warn("registerPanelDnD failed:", /** @type {Error} */ (e).message);
569
+ registerPanelDnD(panel as unknown as CanvasPanel);
570
+ } catch (e) {
571
+ console.warn("registerPanelDnD failed:", (e as Error).message);
584
572
  }
585
- registerPanelEvents(/** @type {CanvasPanel} */ (panel));
573
+ registerPanelEvents(panel as unknown as CanvasPanel);
586
574
  renderOverlays();
587
575
  updateForcedPseudoPreview();
588
576
 
589
577
  // Process pending inline edit when canvas becomes ready
590
578
  const currentTab = activeTab.value;
591
579
  if (currentTab?.session.ui?.pendingInlineEdit) {
592
- const { path, mediaName: mn } = /** @type {InlineEditDef} */ (
593
- currentTab.session.ui.pendingInlineEdit
594
- );
580
+ const { path, mediaName: mn } = currentTab.session.ui.pendingInlineEdit as InlineEditDef;
595
581
  currentTab.session.ui.pendingInlineEdit = null;
596
582
  const targetPanel = canvasPanels.find((p) => p.mediaName === mn) || canvasPanels[0];
597
583
  if (targetPanel) {
@@ -600,18 +586,18 @@ function renderCanvasIntoPanel(
600
586
  }
601
587
  }
602
588
  })
603
- .catch((/** @type {unknown} */ err) => {
589
+ .catch((err: unknown) => {
604
590
  if (gen !== view.renderGeneration) return;
605
591
  console.warn("renderCanvasLive rejected:", err instanceof Error ? err.message : err);
606
592
  updateCanvas({ status: "ready", scope: null, error: null });
607
593
  canvas.innerHTML = "";
608
594
  renderCanvasNode(docToRender, [], canvas, activeBreakpoints, featureToggles);
609
595
  try {
610
- registerPanelDnD(/** @type {CanvasPanel} */ (panel));
611
- } catch (/** @type {unknown} */ e) {
612
- console.warn("registerPanelDnD failed:", /** @type {Error} */ (e).message);
596
+ registerPanelDnD(panel as unknown as CanvasPanel);
597
+ } catch (e) {
598
+ console.warn("registerPanelDnD failed:", (e as Error).message);
613
599
  }
614
- registerPanelEvents(/** @type {CanvasPanel} */ (panel));
600
+ registerPanelEvents(panel as unknown as CanvasPanel);
615
601
  renderOverlays();
616
602
  updateForcedPseudoPreview();
617
603
  });
@@ -624,11 +610,14 @@ function renderCanvasIntoPanel(
624
610
  * @param {HTMLElement} canvas
625
611
  * @param {Map<string, "added" | "removed" | "modified">} diffMap
626
612
  */
627
- function applyDiffHighlightToCanvas(canvas, diffMap) {
613
+ function applyDiffHighlightToCanvas(
614
+ canvas: HTMLElement,
615
+ diffMap: Map<string, "added" | "removed" | "modified">,
616
+ ) {
628
617
  if (!diffMap || diffMap.size === 0) return;
629
618
 
630
619
  // Walk all elements in canvas and check their data attributes or other markers
631
- const walkCanvas = (/** @type {HTMLElement} */ el, /** @type {string} */ path = "") => {
620
+ const walkCanvas = (el: HTMLElement, /** @type {string} */ path = "") => {
632
621
  const pathKey = path || "/";
633
622
 
634
623
  if (diffMap.has(pathKey)) {
@@ -643,7 +632,7 @@ function applyDiffHighlightToCanvas(canvas, diffMap) {
643
632
  for (const child of el.children) {
644
633
  const childPath =
645
634
  pathKey === "/" ? `children/${childIdx}` : `${pathKey}/children/${childIdx}`;
646
- walkCanvas(/** @type {HTMLElement} */ (child), childPath);
635
+ walkCanvas(child as HTMLElement, childPath);
647
636
  childIdx++;
648
637
  }
649
638
  };
@@ -659,13 +648,13 @@ function applyDiffHighlightToCanvas(canvas, diffMap) {
659
648
  * @param {Element} canvasEl
660
649
  * @param {Set<string>} activeBreakpoints
661
650
  */
662
- function applyCanvasMediaOverrides(canvasEl, activeBreakpoints) {
651
+ function applyCanvasMediaOverrides(canvasEl: Element, activeBreakpoints: Set<string>) {
663
652
  if (!activeBreakpoints.size) return;
664
653
  const tab = activeTab.value;
665
654
  if (!tab) return;
666
655
  const docMedia = getEffectiveMedia(tab.doc.document.$media || {});
667
656
  // Build a set of CSS condition texts that match active breakpoints
668
- const activeConditions = new Set();
657
+ const activeConditions = new Set<string>();
669
658
  for (const name of activeBreakpoints) {
670
659
  if (docMedia[name]) activeConditions.add(docMedia[name]);
671
660
  }
@@ -1,3 +1,4 @@
1
+ /// <reference lib="dom" />
1
2
  /**
2
3
  * Canvas panel utilities — extracted from studio.js (Phase 4l). Panzoom infrastructure: panel DOM
3
4
  * template creation, centering, transform application, zoom indicator, and fit-to-screen.
@@ -9,24 +10,20 @@ import { classMap } from "lit-html/directives/class-map.js";
9
10
  import { styleMap } from "lit-html/directives/style-map.js";
10
11
  import { ifDefined } from "lit-html/directives/if-defined.js";
11
12
 
12
- import { renderOnly, updateUi, canvasWrap, canvasPanels } from "../store.js";
13
- import { activeTab } from "../workspace/workspace.js";
14
- import { view } from "../view.js";
15
- import { getLayerSlot } from "../ui/layers.js";
16
- import { getActivePanel, findCanvasElement } from "./canvas-helpers.js";
13
+ import { renderOnly, updateUi, canvasWrap, canvasPanels } from "../store";
14
+ import { activeTab } from "../workspace/workspace";
15
+ import { view } from "../view";
16
+ import { getLayerSlot } from "../ui/layers";
17
+ import { getActivePanel, findCanvasElement } from "./canvas-helpers";
17
18
 
18
- /**
19
- * @type {{
20
- * getCanvasMode: () => string;
21
- * getZoom: () => number;
22
- * setZoomDirect: (zoom: number) => void;
23
- * renderStylebookOverlays: () => void;
24
- * }}
25
- */
26
- let _ctx;
19
+ let _ctx: {
20
+ getCanvasMode: () => string;
21
+ getZoom: () => number;
22
+ setZoomDirect: (zoom: number) => void;
23
+ renderStylebookOverlays: () => void;
24
+ };
27
25
 
28
- /** @type {HTMLElement | null} */
29
- let _zoomIndicatorEl = null;
26
+ let _zoomIndicatorEl: HTMLElement | null = null;
30
27
 
31
28
  /**
32
29
  * Initialize the canvas utils module.
@@ -38,7 +35,12 @@ let _zoomIndicatorEl = null;
38
35
  * renderStylebookOverlays: () => void;
39
36
  * }} ctx
40
37
  */
41
- export function initCanvasUtils(ctx) {
38
+ export function initCanvasUtils(ctx: {
39
+ getCanvasMode: () => string;
40
+ getZoom: () => number;
41
+ setZoomDirect: (zoom: number) => void;
42
+ renderStylebookOverlays: () => void;
43
+ }) {
42
44
  _ctx = ctx;
43
45
  }
44
46
 
@@ -50,21 +52,23 @@ export function initCanvasUtils(ctx) {
50
52
  * @param {boolean} fullWidth
51
53
  * @param {number | null} width
52
54
  */
53
- export function canvasPanelTemplate(mediaName, label, fullWidth, width = null) {
54
- /**
55
- * @type {{
56
- * mediaName: string;
57
- * element: HTMLElement | null;
58
- * canvas: HTMLElement | null;
59
- * overlay: HTMLElement | null;
60
- * overlayClk: HTMLElement | null;
61
- * viewport: HTMLElement | null;
62
- * scrollContainer: HTMLElement | null;
63
- * dropLine: HTMLElement | null;
64
- * _width: number | null;
65
- * }}
66
- */
67
- const panel = {
55
+ export function canvasPanelTemplate(
56
+ mediaName: string | null,
57
+ label: string | null,
58
+ fullWidth: boolean,
59
+ width: number | null = null,
60
+ ) {
61
+ const panel: {
62
+ mediaName: string;
63
+ element: HTMLElement | null;
64
+ canvas: HTMLElement | null;
65
+ overlay: HTMLElement | null;
66
+ overlayClk: HTMLElement | null;
67
+ viewport: HTMLElement | null;
68
+ scrollContainer: HTMLElement | null;
69
+ dropLine: HTMLElement | null;
70
+ _width: number | null;
71
+ } = {
68
72
  mediaName: mediaName || "",
69
73
  element: null,
70
74
  canvas: null,
@@ -80,7 +84,7 @@ export function canvasPanelTemplate(mediaName, label, fullWidth, width = null) {
80
84
  class=${classMap({ "canvas-panel": true, "full-width": fullWidth })}
81
85
  data-media=${ifDefined(mediaName !== null ? mediaName : undefined)}
82
86
  ${ref((el) => {
83
- if (el) panel.element = /** @type {HTMLElement} */ (el);
87
+ if (el) panel.element = el as HTMLElement;
84
88
  })}
85
89
  >
86
90
  ${label
@@ -99,34 +103,34 @@ export function canvasPanelTemplate(mediaName, label, fullWidth, width = null) {
99
103
  class="canvas-panel-viewport"
100
104
  style=${styleMap({ width: width && !fullWidth ? `${width}px` : "" })}
101
105
  ${ref((el) => {
102
- if (el) panel.viewport = /** @type {HTMLElement} */ (el);
106
+ if (el) panel.viewport = el as HTMLElement;
103
107
  })}
104
108
  >
105
109
  <div
106
110
  class="canvas-panel-canvas"
107
111
  style=${styleMap({ width: width ? `${width}px` : "" })}
108
112
  ${ref((el) => {
109
- if (el) panel.canvas = /** @type {HTMLElement} */ (el);
113
+ if (el) panel.canvas = el as HTMLElement;
110
114
  })}
111
115
  ></div>
112
116
  <div
113
117
  class="canvas-panel-overlay"
114
118
  ${ref((el) => {
115
- if (el) panel.overlay = /** @type {HTMLElement} */ (el);
119
+ if (el) panel.overlay = el as HTMLElement;
116
120
  })}
117
121
  >
118
122
  <div
119
123
  class="canvas-drop-indicator"
120
124
  style="display:none"
121
125
  ${ref((el) => {
122
- if (el) panel.dropLine = /** @type {HTMLElement} */ (el);
126
+ if (el) panel.dropLine = el as HTMLElement;
123
127
  })}
124
128
  ></div>
125
129
  </div>
126
130
  <div
127
131
  class="canvas-panel-click"
128
132
  ${ref((el) => {
129
- if (el) panel.overlayClk = /** @type {HTMLElement} */ (el);
133
+ if (el) panel.overlayClk = el as HTMLElement;
130
134
  })}
131
135
  ></div>
132
136
  </div>
@@ -231,7 +235,7 @@ export function resetZoomIndicator() {
231
235
  * @param {HTMLElement} el
232
236
  * @param {{ scrollContainer?: HTMLElement | null }} [panel]
233
237
  */
234
- function _panToEl(el, panel) {
238
+ function _panToEl(el: HTMLElement, panel?: { scrollContainer?: HTMLElement | null }) {
235
239
  const wrapRect = canvasWrap.getBoundingClientRect();
236
240
  const elRect = el.getBoundingClientRect();
237
241
  const elCenterY = elRect.top + elRect.height / 2 - wrapRect.top;
@@ -248,7 +252,7 @@ function _panToEl(el, panel) {
248
252
  const targetY = startY + offsetY;
249
253
  const start = performance.now();
250
254
  const duration = 250;
251
- function step(/** @type {number} */ now) {
255
+ function step(now: number) {
252
256
  const t = Math.min((now - start) / duration, 1);
253
257
  const ease = t * (2 - t);
254
258
  view.panY = startY + (targetY - startY) * ease;
@@ -264,7 +268,7 @@ function _panToEl(el, panel) {
264
268
  *
265
269
  * @param {(string | number)[]} path
266
270
  */
267
- export function panToElement(path) {
271
+ export function panToElement(path: (string | number)[]) {
268
272
  const panel = getActivePanel();
269
273
  if (!panel?.canvas) return;
270
274
  const el = findCanvasElement(path, panel.canvas);
@@ -277,7 +281,7 @@ export function panToElement(path) {
277
281
  *
278
282
  * @param {HTMLElement} el
279
283
  */
280
- export function panToCanvasEl(el) {
284
+ export function panToCanvasEl(el: HTMLElement) {
281
285
  const panel = getActivePanel();
282
286
  _panToEl(el, panel ?? undefined);
283
287
  }
@@ -293,8 +297,8 @@ export function renderZoomIndicator() {
293
297
  html`
294
298
  <div
295
299
  class="zoom-indicator"
296
- ${ref((el) => {
297
- _zoomIndicatorEl = /** @type {HTMLElement | null} */ (el || null);
300
+ ${ref((el: Element | undefined) => {
301
+ _zoomIndicatorEl = (el as HTMLElement) || null;
298
302
  })}
299
303
  >
300
304
  <span class="zoom-indicator-action" title="Reset to 100%" @click=${resetZoom}>
@@ -6,10 +6,10 @@
6
6
  * @param {string} scope - The scoping selector (e.g. `[data-jx-site]`)
7
7
  * @returns {string} Generated CSS text
8
8
  */
9
- export function buildNestedSiteCSS(styleObj, scope) {
9
+ export function buildNestedSiteCSS(styleObj: Record<string, any>, scope: string) {
10
10
  let css = "";
11
11
 
12
- function emit(/** @type {string} */ parentSel, /** @type {Record<string, any>} */ rules) {
12
+ function emit(parentSel: string, rules: Record<string, any>) {
13
13
  const props = Object.entries(rules)
14
14
  .filter(([, val]) => val === null || typeof val !== "object" || Array.isArray(val))
15
15
  .map(([p, val]) => `${camelToKebab(p)}: ${val}`)
@@ -45,6 +45,6 @@ export function buildNestedSiteCSS(styleObj, scope) {
45
45
  * @param {string} s
46
46
  * @returns {string}
47
47
  */
48
- function camelToKebab(s) {
48
+ function camelToKebab(s: string) {
49
49
  return s.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
50
50
  }