@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.
Files changed (105) hide show
  1. package/dist/studio.js +2231 -715
  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} +110 -85
  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} +38 -26
  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,5 @@
1
+ /// <reference lib="dom" />
2
+ /// <reference lib="dom.iterable" />
1
3
  /**
2
4
  * Stylebook panel — renders the Stylebook mode canvas (element catalog with per-file style
3
5
  * defaults). Extracted from studio.js Phase 4e.
@@ -16,64 +18,65 @@ import {
16
18
  canvasPanels,
17
19
  elToPath,
18
20
  projectState,
19
- } from "../store.js";
20
- import { activeTab } from "../workspace/workspace.js";
21
- import { view } from "../view.js";
21
+ } from "../store";
22
+ import { activeTab } from "../workspace/workspace";
23
+ import { view } from "../view";
22
24
  import { defineElement, setSkipServerFunctions } from "@jxsuite/runtime";
23
- import { componentRegistry } from "../files/components.js";
24
- import { getEffectiveStyle, getEffectiveMedia } from "../site-context.js";
25
- import { parseMediaEntries, activeBreakpointsForWidth } from "../utils/canvas-media.js";
26
- import { mediaDisplayName } from "./shared.js";
27
- import { panToCanvasEl } from "../canvas/canvas-utils.js";
25
+ import { componentRegistry } from "../files/components";
26
+ import { getEffectiveStyle, getEffectiveMedia } from "../site-context";
27
+ import { parseMediaEntries, activeBreakpointsForWidth } from "../utils/canvas-media";
28
+ import { mediaDisplayName } from "./shared";
29
+ import { panToCanvasEl } from "../canvas/canvas-utils";
28
30
  import stylebookMeta from "../../data/stylebook-meta.json";
29
31
 
30
- export { stylebookMeta };
32
+ export interface StylebookEntry {
33
+ tag: string;
34
+ text?: string;
35
+ attributes?: Record<string, string>;
36
+ style?: string;
37
+ children?: StylebookEntry[];
38
+ }
31
39
 
32
- /**
33
- * @typedef {{
34
- * tag: string;
35
- * text?: string;
36
- * attributes?: Record<string, string>;
37
- * style?: string;
38
- * children?: StylebookEntry[];
39
- * }} StylebookEntry
40
- */
40
+ interface StylebookPanel {
41
+ mediaName: string | null;
42
+ element: HTMLElement | null;
43
+ canvas: HTMLElement;
44
+ overlay: HTMLElement;
45
+ overlayClk: HTMLElement;
46
+ viewport: HTMLElement | null;
47
+ scrollContainer: HTMLElement | null;
48
+ dropLine: HTMLElement | null;
49
+ _width: number | null;
50
+ _lastHoverTag?: string | null;
51
+ }
41
52
 
42
- /**
43
- * @typedef {{
44
- * mediaName: string | null;
45
- * element: HTMLElement | null;
46
- * canvas: HTMLElement;
47
- * overlay: HTMLElement;
48
- * overlayClk: HTMLElement;
49
- * viewport: HTMLElement | null;
50
- * scrollContainer: HTMLElement | null;
51
- * dropLine: HTMLElement | null;
52
- * _width: number | null;
53
- * _lastHoverTag?: string | null;
54
- * }} StylebookPanel
55
- */
53
+ interface StylebookCtx {
54
+ canvasPanelTemplate: (
55
+ mediaName: string | null,
56
+ label: string | null,
57
+ fullWidth: boolean,
58
+ width?: number | null,
59
+ ) => { tpl: import("lit-html").TemplateResult; panel: any };
60
+ applyTransform: () => void;
61
+ observeCenterUntilStable: () => void;
62
+ renderZoomIndicator: () => void;
63
+ updateActivePanelHeaders: () => void;
64
+ overlayBoxDescriptor: (
65
+ el: Element,
66
+ type: string,
67
+ panel: any,
68
+ ) => { cls: string; top: string; left: string; width: string; height: string };
69
+ effectiveZoom: () => number;
70
+ }
56
71
 
57
- /**
58
- * @typedef {{
59
- * canvasPanelTemplate: Function;
60
- * applyTransform: Function;
61
- * observeCenterUntilStable: Function;
62
- * renderZoomIndicator: Function;
63
- * updateActivePanelHeaders: Function;
64
- * overlayBoxDescriptor: Function;
65
- * effectiveZoom: Function;
66
- * }} StylebookCtx
67
- */
72
+ interface ComponentEntry {
73
+ source?: string;
74
+ tagName: string;
75
+ path?: string;
76
+ props?: Array<{ name: string; default?: string }>;
77
+ }
68
78
 
69
- /**
70
- * @typedef {{
71
- * source?: string;
72
- * tagName: string;
73
- * path?: string;
74
- * props?: Array<{ name: string; default?: string }>;
75
- * }} ComponentEntry
76
- */
79
+ export { stylebookMeta };
77
80
 
78
81
  /**
79
82
  * Resolve a nested tag path in a style object. e.g., "table th" → style["table"]["th"]
@@ -82,23 +85,22 @@ export { stylebookMeta };
82
85
  * @param {string} tagPath
83
86
  * @returns {Record<string, unknown> | null}
84
87
  */
85
- function _resolveNestedStyle(style, tagPath) {
88
+ function _resolveNestedStyle(style: Record<string, unknown>, tagPath: string) {
86
89
  const parts = tagPath.split(" ");
87
90
  let obj = style;
88
91
  for (const part of parts) {
89
92
  if (!obj || typeof obj !== "object") return null;
90
- obj = /** @type {Record<string, unknown>} */ (obj[part]);
93
+ obj = obj[part] as Record<string, unknown>;
91
94
  }
92
- return obj && typeof obj === "object" ? /** @type {Record<string, unknown>} */ (obj) : null;
95
+ return obj && typeof obj === "object" ? (obj as Record<string, unknown>) : null;
93
96
  }
94
97
 
95
- /** @type {StylebookCtx | null} */
96
- let _ctx = null;
98
+ let _ctx: StylebookCtx | null = null;
97
99
 
98
100
  /** Lookup: tag → entry from stylebookMeta (built once) */
99
101
  const _entryByTag = new Map();
100
102
  for (const section of stylebookMeta.$sections) {
101
- for (const entry of /** @type {StylebookEntry[]} */ (section.elements)) {
103
+ for (const entry of section.elements as StylebookEntry[]) {
102
104
  _entryByTag.set(entry.tag, entry);
103
105
  }
104
106
  }
@@ -108,7 +110,7 @@ for (const section of stylebookMeta.$sections) {
108
110
  *
109
111
  * @param {StylebookCtx} ctx
110
112
  */
111
- export function renderStylebookMode(ctx) {
113
+ export function renderStylebookMode(ctx: StylebookCtx) {
112
114
  _ctx = ctx;
113
115
 
114
116
  // Stylebook mode — element catalog only
@@ -123,8 +125,8 @@ export function renderStylebookMode(ctx) {
123
125
  );
124
126
  const hasMedia = sizeBreakpoints.length > 0;
125
127
 
126
- const onFilterInput = (/** @type {Event} */ e) => {
127
- updateUi("stylebookFilter", /** @type {HTMLInputElement} */ (e.target).value);
128
+ const onFilterInput = (e: Event) => {
129
+ updateUi("stylebookFilter", (e.target as HTMLInputElement).value);
128
130
  };
129
131
 
130
132
  const onCustomizedToggle = () => {
@@ -145,7 +147,10 @@ export function renderStylebookMode(ctx) {
145
147
  @input=${onFilterInput}
146
148
  />
147
149
  <button
148
- class=${classMap({ "tb-toggle": true, active: tab?.session.ui.stylebookCustomizedOnly })}
150
+ class=${classMap({
151
+ "tb-toggle": true,
152
+ active: !!tab?.session.ui.stylebookCustomizedOnly,
153
+ })}
149
154
  @click=${onCustomizedToggle}
150
155
  >
151
156
  Customized
@@ -154,7 +159,7 @@ export function renderStylebookMode(ctx) {
154
159
  </div>
155
160
  `;
156
161
 
157
- /** @type {HTMLElement} */ (canvasWrap).style.overflow = "hidden";
162
+ (canvasWrap as HTMLElement).style.overflow = "hidden";
158
163
 
159
164
  /** @type {{ name: string; displayName: string; width: number; activeSet: Set<string> }[]} */
160
165
  const allPanelDefs = [];
@@ -175,10 +180,7 @@ export function renderStylebookMode(ctx) {
175
180
  }
176
181
  }
177
182
 
178
- const renderIntoPanel = (
179
- /** @type {StylebookPanel} */ panel,
180
- /** @type {Set<string>} */ activeBreakpoints,
181
- ) => {
183
+ const renderIntoPanel = (panel: StylebookPanel, activeBreakpoints: Set<string>) => {
182
184
  panel.canvas.classList.add("sb-canvas");
183
185
  renderStylebookElementsIntoCanvas(
184
186
  panel.canvas,
@@ -188,7 +190,7 @@ export function renderStylebookMode(ctx) {
188
190
  activeBreakpoints,
189
191
  );
190
192
  for (const child of panel.canvas.querySelectorAll("*")) {
191
- /** @type {HTMLElement} */ (child).style.pointerEvents = "none";
193
+ (child as HTMLElement).style.pointerEvents = "none";
192
194
  }
193
195
  registerStylebookPanelEvents(panel);
194
196
  };
@@ -211,7 +213,7 @@ export function renderStylebookMode(ctx) {
211
213
  !hasBaseWidth,
212
214
  hasBaseWidth ? baseWidth : undefined,
213
215
  );
214
- panelEntries = [{ tpl: entry.tpl, panel: entry.panel, activeSet: new Set() }];
216
+ panelEntries = [{ tpl: entry.tpl, panel: entry.panel, activeSet: new Set<string>() }];
215
217
  } else {
216
218
  panelEntries = allPanelDefs.map((def) => {
217
219
  const label = `${def.displayName} (${def.width}px)`;
@@ -227,7 +229,7 @@ export function renderStylebookMode(ctx) {
227
229
  class="panzoom-wrap"
228
230
  style="transform-origin:0 0;padding-top:40px"
229
231
  ${ref((el) => {
230
- if (el) view.panzoomWrap = /** @type {HTMLDivElement} */ (el);
232
+ if (el) view.panzoomWrap = el as HTMLDivElement;
231
233
  })}
232
234
  >
233
235
  ${panelEntries.map((e) => e.tpl)}
@@ -266,15 +268,15 @@ export function refreshStylebookStyles() {
266
268
 
267
269
  const { sizeBreakpoints } = parseMediaEntries(getEffectiveMedia(tab.doc.document?.$media));
268
270
  const activeBreakpoints = panel.mediaName
269
- ? activeBreakpointsForWidth(sizeBreakpoints, /** @type {number} */ (panel._width))
270
- : new Set();
271
+ ? activeBreakpointsForWidth(sizeBreakpoints, panel._width as number)
272
+ : new Set<string>();
271
273
 
272
274
  // Re-apply styles to each element in the canvas
273
275
  const allEls = canvas.querySelectorAll("*");
274
276
  for (const el of allEls) {
275
277
  const tag = view.stylebookElToTag.get(el);
276
278
  if (!tag) continue;
277
- const htmlEl = /** @type {HTMLElement} */ (el);
279
+ const htmlEl = el as HTMLElement;
278
280
  // Determine if it's a compound selector (e.g. "ul li") or simple tag
279
281
  const parts = tag.split(" ");
280
282
  const leafTag = parts[parts.length - 1];
@@ -289,27 +291,21 @@ export function refreshStylebookStyles() {
289
291
  for (const [prop, val] of Object.entries(tagStyle)) {
290
292
  if (typeof val === "string" || typeof val === "number") {
291
293
  try {
292
- /** @type {Record<string, string | number>} */ (
293
- /** @type {unknown} */ (htmlEl.style)
294
- )[prop] = val;
294
+ (htmlEl.style as unknown as Record<string, string | number>)[prop] = val;
295
295
  } catch {}
296
296
  }
297
297
  }
298
298
  // Media overrides nested in tag style
299
299
  if (activeBreakpoints.size > 0) {
300
300
  for (const [key, val] of Object.entries(tagStyle)) {
301
- if (!key.startsWith("@") || typeof val !== "object") continue;
301
+ if (!key.startsWith("@") || typeof val !== "object" || val === null) continue;
302
302
  const mediaName = key.slice(1);
303
303
  if (mediaName === "--") continue;
304
304
  if (activeBreakpoints.has(mediaName)) {
305
- for (const [prop, v] of Object.entries(
306
- /** @type {Record<string, unknown>} */ (val),
307
- )) {
305
+ for (const [prop, v] of Object.entries(val as Record<string, unknown>)) {
308
306
  if (typeof v === "string" || typeof v === "number") {
309
307
  try {
310
- /** @type {Record<string, string | number>} */ (
311
- /** @type {unknown} */ (htmlEl.style)
312
- )[prop] = v;
308
+ (htmlEl.style as unknown as Record<string, string | number>)[prop] = v;
313
309
  } catch {}
314
310
  }
315
311
  }
@@ -320,7 +316,7 @@ export function refreshStylebookStyles() {
320
316
  // Top-level @media keys
321
317
  if (activeBreakpoints.size > 0) {
322
318
  for (const [key, val] of Object.entries(rootStyle)) {
323
- if (!key.startsWith("@") || typeof val !== "object") continue;
319
+ if (!key.startsWith("@") || typeof val !== "object" || val === null) continue;
324
320
  const mediaName = key.slice(1);
325
321
  if (mediaName === "--") continue;
326
322
  if (activeBreakpoints.has(mediaName)) {
@@ -332,9 +328,7 @@ export function refreshStylebookStyles() {
332
328
  for (const [prop, v] of Object.entries(mediaTagStyle)) {
333
329
  if (typeof v === "string" || typeof v === "number") {
334
330
  try {
335
- /** @type {Record<string, string | number>} */ (
336
- /** @type {unknown} */ (htmlEl.style)
337
- )[prop] = v;
331
+ (htmlEl.style as unknown as Record<string, string | number>)[prop] = v;
338
332
  } catch {}
339
333
  }
340
334
  }
@@ -354,7 +348,7 @@ export function refreshStylebookStyles() {
354
348
  * @param {string} tag
355
349
  * @param {string | null} [media]
356
350
  */
357
- export function selectStylebookTag(tag, media, { panCanvas = false } = {}) {
351
+ export function selectStylebookTag(tag: string, media?: string | null, { panCanvas = false } = {}) {
358
352
  updateSession({
359
353
  selection: [],
360
354
  ui: {
@@ -380,7 +374,7 @@ export function renderStylebookOverlays() {
380
374
  const selectedTag = activeTab.value?.session.ui.stylebookSelection;
381
375
 
382
376
  for (const panel of canvasPanels) {
383
- const hoverTag = /** @type {StylebookPanel} */ (panel)._lastHoverTag;
377
+ const hoverTag = (panel as StylebookPanel)._lastHoverTag;
384
378
  /**
385
379
  * @type {{
386
380
  * cls: string;
@@ -436,7 +430,12 @@ export function renderStylebookOverlays() {
436
430
  * @param {Set<string> | null} activeBreakpoints
437
431
  * @param {string | null} [parentTag]
438
432
  */
439
- export function buildStylebookElement(entry, rootStyle, activeBreakpoints, parentTag = null) {
433
+ export function buildStylebookElement(
434
+ entry: StylebookEntry,
435
+ rootStyle: Record<string, unknown>,
436
+ activeBreakpoints: Set<string> | null,
437
+ parentTag: string | null = null,
438
+ ) {
440
439
  const el = document.createElement(entry.tag);
441
440
  if (entry.text) el.textContent = entry.text;
442
441
  if (entry.attributes) {
@@ -455,24 +454,21 @@ export function buildStylebookElement(entry, rootStyle, activeBreakpoints, paren
455
454
  for (const [prop, val] of Object.entries(tagStyle)) {
456
455
  if (typeof val === "string" || typeof val === "number") {
457
456
  try {
458
- /** @type {Record<string, string | number>} */ (/** @type {unknown} */ (el.style))[prop] =
459
- val;
457
+ (el.style as unknown as Record<string, string | number>)[prop] = val;
460
458
  } catch {}
461
459
  }
462
460
  }
463
461
  if (activeBreakpoints) {
464
462
  // Check media overrides nested inside the tag style (selector wraps media)
465
463
  for (const [key, val] of Object.entries(tagStyle)) {
466
- if (!key.startsWith("@") || typeof val !== "object") continue;
464
+ if (!key.startsWith("@") || typeof val !== "object" || val === null) continue;
467
465
  const mediaName = key.slice(1);
468
466
  if (mediaName === "--") continue;
469
467
  if (activeBreakpoints.has(mediaName)) {
470
- for (const [prop, v] of Object.entries(/** @type {Record<string, unknown>} */ (val))) {
468
+ for (const [prop, v] of Object.entries(val as Record<string, unknown>)) {
471
469
  if (typeof v === "string" || typeof v === "number") {
472
470
  try {
473
- /** @type {Record<string, string | number>} */ (/** @type {unknown} */ (el.style))[
474
- prop
475
- ] = v;
471
+ (el.style as unknown as Record<string, string | number>)[prop] = v;
476
472
  } catch {}
477
473
  }
478
474
  }
@@ -484,21 +480,16 @@ export function buildStylebookElement(entry, rootStyle, activeBreakpoints, paren
484
480
  if (activeBreakpoints) {
485
481
  const tagPath = compoundTag || entry.tag;
486
482
  for (const [key, val] of Object.entries(rootStyle)) {
487
- if (!key.startsWith("@") || typeof val !== "object") continue;
483
+ if (!key.startsWith("@") || typeof val !== "object" || val === null) continue;
488
484
  const mediaName = key.slice(1);
489
485
  if (mediaName === "--") continue;
490
486
  if (activeBreakpoints.has(mediaName)) {
491
- const mediaTagStyle = _resolveNestedStyle(
492
- /** @type {Record<string, unknown>} */ (val),
493
- tagPath,
494
- );
487
+ const mediaTagStyle = _resolveNestedStyle(val as Record<string, unknown>, tagPath);
495
488
  if (mediaTagStyle && typeof mediaTagStyle === "object") {
496
489
  for (const [prop, v] of Object.entries(mediaTagStyle)) {
497
490
  if (typeof v === "string" || typeof v === "number") {
498
491
  try {
499
- /** @type {Record<string, string | number>} */ (/** @type {unknown} */ (el.style))[
500
- prop
501
- ] = v;
492
+ (el.style as unknown as Record<string, string | number>)[prop] = v;
502
493
  } catch {}
503
494
  }
504
495
  }
@@ -520,7 +511,7 @@ export function buildStylebookElement(entry, rootStyle, activeBreakpoints, paren
520
511
  * @param {ComponentEntry} comp
521
512
  * @returns {Promise<HTMLElement>}
522
513
  */
523
- export async function renderComponentPreview(comp) {
514
+ export async function renderComponentPreview(comp: ComponentEntry) {
524
515
  setSkipServerFunctions(true);
525
516
  try {
526
517
  if (comp.source === "npm") {
@@ -543,14 +534,14 @@ export async function renderComponentPreview(comp) {
543
534
  }
544
535
  }
545
536
  return el;
546
- } catch (/** @type {unknown} */ e) {
537
+ } catch (e) {
547
538
  console.warn("Component preview failed:", comp.tagName, e);
548
539
  return _componentFallback(comp.tagName);
549
540
  }
550
541
  }
551
542
 
552
543
  /** @param {string} tagName */
553
- function _componentFallback(tagName) {
544
+ function _componentFallback(tagName: string) {
554
545
  const fallback = document.createElement("div");
555
546
  fallback.style.cssText =
556
547
  "padding:12px;border:1px dashed var(--border);border-radius:4px;color:var(--fg-dim)";
@@ -562,12 +553,12 @@ function _componentFallback(tagName) {
562
553
  * @param {Record<string, unknown>} rootStyle
563
554
  * @param {string} tag
564
555
  */
565
- function hasTagStyle(rootStyle, tag) {
556
+ function hasTagStyle(rootStyle: Record<string, unknown>, tag: string) {
566
557
  const s = _resolveNestedStyle(rootStyle, tag);
567
558
  if (s && typeof s === "object" && Object.keys(s).length > 0) return true;
568
559
  for (const [key, val] of Object.entries(rootStyle)) {
569
- if (!key.startsWith("@") || typeof val !== "object") continue;
570
- const ms = _resolveNestedStyle(/** @type {Record<string, unknown>} */ (val), tag);
560
+ if (!key.startsWith("@") || typeof val !== "object" || val === null) continue;
561
+ const ms = _resolveNestedStyle(val as Record<string, unknown>, tag);
571
562
  if (ms && typeof ms === "object" && Object.keys(ms).length > 0) return true;
572
563
  }
573
564
  return false;
@@ -581,11 +572,11 @@ function hasTagStyle(rootStyle, tag) {
581
572
  * @param {Set<string> | null} activeBreakpoints
582
573
  */
583
574
  export function renderStylebookElementsIntoCanvas(
584
- canvasEl,
585
- rootStyle,
586
- filter,
587
- customizedOnly,
588
- activeBreakpoints,
575
+ canvasEl: HTMLElement,
576
+ rootStyle: Record<string, unknown>,
577
+ filter: string,
578
+ customizedOnly: boolean | undefined,
579
+ activeBreakpoints: Set<string> | null,
589
580
  ) {
590
581
  for (const [k, v] of Object.entries(rootStyle)) {
591
582
  if (k.startsWith("--") && (typeof v === "string" || typeof v === "number")) {
@@ -593,23 +584,22 @@ export function renderStylebookElementsIntoCanvas(
593
584
  }
594
585
  }
595
586
 
596
- /** @type {import("lit-html").TemplateResult[]} */
597
- const sectionTemplates = [];
587
+ const sectionTemplates: import("lit-html").TemplateResult[] = [];
598
588
 
599
589
  for (const section of stylebookMeta.$sections) {
600
- let entries = /** @type {StylebookEntry[]} */ (section.elements);
590
+ let entries = section.elements as StylebookEntry[];
601
591
  if (filter) {
602
592
  entries = entries.filter(
603
- (/** @type {StylebookEntry} */ e) =>
593
+ (e: StylebookEntry) =>
604
594
  e.tag.includes(filter) || section.label.toLowerCase().includes(filter),
605
595
  );
606
596
  }
607
597
  if (customizedOnly) {
608
- entries = entries.filter((/** @type {StylebookEntry} */ e) => hasTagStyle(rootStyle, e.tag));
598
+ entries = entries.filter((e: StylebookEntry) => hasTagStyle(rootStyle, e.tag));
609
599
  }
610
600
  if (entries.length === 0) continue;
611
601
 
612
- const cardTemplates = entries.map((/** @type {StylebookEntry} */ entry) => {
602
+ const cardTemplates = entries.map((entry: StylebookEntry) => {
613
603
  const el = buildStylebookElement(entry, rootStyle, activeBreakpoints);
614
604
  return html`
615
605
  <div
@@ -654,15 +644,12 @@ export function renderStylebookElementsIntoCanvas(
654
644
  if (componentRegistry.length > 0) {
655
645
  let comps = componentRegistry;
656
646
  if (filter)
657
- comps = comps.filter((/** @type {ComponentEntry} */ c) =>
658
- c.tagName.toLowerCase().includes(filter),
659
- );
647
+ comps = comps.filter((c: ComponentEntry) => c.tagName.toLowerCase().includes(filter));
660
648
  if (customizedOnly)
661
- comps = comps.filter((/** @type {ComponentEntry} */ c) => hasTagStyle(rootStyle, c.tagName));
649
+ comps = comps.filter((c: ComponentEntry) => hasTagStyle(rootStyle, c.tagName));
662
650
  if (comps.length > 0) {
663
- const compCards = comps.map((/** @type {ComponentEntry} */ comp) => {
664
- /** @type {HTMLDivElement | null} */
665
- let previewEl = null;
651
+ const compCards = comps.map((comp: ComponentEntry) => {
652
+ let previewEl: HTMLDivElement | null = null;
666
653
  const cardTpl = html`
667
654
  <div
668
655
  class="element-card"
@@ -676,7 +663,7 @@ export function renderStylebookElementsIntoCanvas(
676
663
  <div
677
664
  class="element-card-preview"
678
665
  ${ref((c) => {
679
- if (c) previewEl = /** @type {HTMLDivElement} */ (c);
666
+ if (c) previewEl = c as HTMLDivElement;
680
667
  })}
681
668
  ></div>
682
669
  <div class="element-card-label">&lt;${comp.tagName}&gt;</div>
@@ -718,20 +705,20 @@ export function renderStylebookElementsIntoCanvas(
718
705
  *
719
706
  * @param {StylebookPanel} panel
720
707
  */
721
- function registerStylebookPanelEvents(panel) {
708
+ function registerStylebookPanelEvents(panel: StylebookPanel) {
722
709
  const { canvas, overlayClk } = panel;
723
710
 
724
- overlayClk.addEventListener("click", (/** @type {MouseEvent} */ e) => {
711
+ overlayClk.addEventListener("click", (e: MouseEvent) => {
725
712
  const els = canvas.querySelectorAll("*");
726
- for (const el of els) /** @type {HTMLElement} */ (el).style.pointerEvents = "auto";
713
+ for (const el of els) (el as HTMLElement).style.pointerEvents = "auto";
727
714
  overlayClk.style.display = "none";
728
715
  const elements = document.elementsFromPoint(e.clientX, e.clientY);
729
716
  overlayClk.style.display = "";
730
- for (const el of els) /** @type {HTMLElement} */ (el).style.pointerEvents = "none";
717
+ for (const el of els) (el as HTMLElement).style.pointerEvents = "none";
731
718
 
732
719
  for (const el of elements) {
733
720
  if (!canvas.contains(el) || el === canvas) continue;
734
- let cur = /** @type {Element | null} */ (el);
721
+ let cur = el as Element | null;
735
722
  while (cur && cur !== canvas) {
736
723
  const tag = view.stylebookElToTag.get(cur);
737
724
  if (tag) {
@@ -747,18 +734,18 @@ function registerStylebookPanelEvents(panel) {
747
734
  renderStylebookOverlays();
748
735
  });
749
736
 
750
- overlayClk.addEventListener("mousemove", (/** @type {MouseEvent} */ e) => {
737
+ overlayClk.addEventListener("mousemove", (e: MouseEvent) => {
751
738
  const els = canvas.querySelectorAll("*");
752
- for (const el of els) /** @type {HTMLElement} */ (el).style.pointerEvents = "auto";
739
+ for (const el of els) (el as HTMLElement).style.pointerEvents = "auto";
753
740
  overlayClk.style.display = "none";
754
741
  const elements = document.elementsFromPoint(e.clientX, e.clientY);
755
742
  overlayClk.style.display = "";
756
- for (const el of els) /** @type {HTMLElement} */ (el).style.pointerEvents = "none";
743
+ for (const el of els) (el as HTMLElement).style.pointerEvents = "none";
757
744
 
758
745
  let hoverTag = null;
759
746
  for (const el of elements) {
760
747
  if (!canvas.contains(el) || el === canvas) continue;
761
- let cur = /** @type {Element | null} */ (el);
748
+ let cur = el as Element | null;
762
749
  while (cur && cur !== canvas) {
763
750
  const tag = view.stylebookElToTag.get(cur);
764
751
  if (tag) {
@@ -778,9 +765,9 @@ function registerStylebookPanelEvents(panel) {
778
765
  }
779
766
 
780
767
  /** Find a stylebook element by tag in the canvas */
781
- function findStylebookEl(/** @type {HTMLElement} */ canvasEl, /** @type {string} */ tag) {
768
+ function findStylebookEl(canvasEl: HTMLElement, tag: string) {
782
769
  for (const child of canvasEl.querySelectorAll("*")) {
783
- if (view.stylebookElToTag.get(child) === tag) return /** @type {HTMLElement} */ (child);
770
+ if (view.stylebookElToTag.get(child) === tag) return child as HTMLElement;
784
771
  }
785
772
  return null;
786
773
  }
@@ -1,3 +1,4 @@
1
+ /// <reference lib="dom" />
1
2
  /**
2
3
  * Tab strip — renders open tabs above the canvas area.
3
4
  *
@@ -8,24 +9,21 @@
8
9
  import { html, render as litRender, nothing } from "lit-html";
9
10
  import { classMap } from "lit-html/directives/class-map.js";
10
11
  import { repeat } from "lit-html/directives/repeat.js";
11
- import { effect, effectScope } from "../reactivity.js";
12
- import { workspace, activateTab, closeTab } from "../workspace/workspace.js";
13
- import { showConfirmDialog } from "../ui/layers.js";
12
+ import { effect, effectScope } from "../reactivity";
13
+ import { workspace, activateTab, closeTab } from "../workspace/workspace";
14
+ import type { Tab } from "../tabs/tab";
15
+ import { showConfirmDialog } from "../ui/layers";
14
16
 
15
- /** @typedef {import("../tabs/tab.js").Tab} Tab */
17
+ let _host: HTMLElement | null = null;
16
18
 
17
- /** @type {HTMLElement | null} */
18
- let _host = null;
19
-
20
- /** @type {import("@vue/reactivity").EffectScope | null} */
21
- let _scope = null;
19
+ let _scope: import("@vue/reactivity").EffectScope | null = null;
22
20
 
23
21
  /**
24
22
  * Mount the tab strip into the given host element.
25
23
  *
26
24
  * @param {HTMLElement} host
27
25
  */
28
- export function mount(host) {
26
+ export function mount(host: HTMLElement) {
29
27
  _host = host;
30
28
  _scope = effectScope();
31
29
  _scope.run(() => {
@@ -71,7 +69,7 @@ function render() {
71
69
  <div
72
70
  class=${classMap({ "tab-strip-tab": true, active: isActive })}
73
71
  @click=${() => activateTab(id)}
74
- @auxclick=${(/** @type {MouseEvent} */ e) => {
72
+ @auxclick=${(e: MouseEvent) => {
75
73
  if (e.button === 1) {
76
74
  e.preventDefault();
77
75
  requestClose(id);
@@ -83,7 +81,7 @@ function render() {
83
81
  ${isDirty ? html`<span class="tab-strip-dirty">●</span>` : nothing}
84
82
  <button
85
83
  class="tab-strip-close"
86
- @click=${(/** @type {Event} */ e) => {
84
+ @click=${(e: Event) => {
87
85
  e.stopPropagation();
88
86
  requestClose(id);
89
87
  }}
@@ -106,7 +104,7 @@ function render() {
106
104
  * @param {Tab} tab
107
105
  * @returns {string}
108
106
  */
109
- function tabLabel(tab) {
107
+ function tabLabel(tab: Tab) {
110
108
  const path = tab.documentPath;
111
109
  if (!path) return "Untitled";
112
110
  const parts = path.split("/");
@@ -118,7 +116,7 @@ function tabLabel(tab) {
118
116
  *
119
117
  * @param {string} id
120
118
  */
121
- async function requestClose(id) {
119
+ async function requestClose(id: string) {
122
120
  const tab = workspace.tabs.get(id);
123
121
  if (!tab) return;
124
122
  if (tab.doc.dirty) {