@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,8 +1,10 @@
1
+ /// <reference lib="dom" />
1
2
  /** Properties panel — inspector for element attributes, component props, media, and frontmatter. */
2
3
 
3
4
  import { html, nothing } from "lit-html";
4
5
  import { live } from "lit-html/directives/live.js";
5
- import { getNodeAtPath, debouncedStyleCommit, renderOnly, projectState } from "../store.js";
6
+ import { getNodeAtPath, debouncedStyleCommit, renderOnly, projectState } from "../store";
7
+ import type { JsonValue, DirEntry } from "../types";
6
8
  import {
7
9
  transactDoc,
8
10
  mutateUpdateProperty,
@@ -12,39 +14,58 @@ import {
12
14
  mutateAddSwitchCase,
13
15
  mutateRemoveSwitchCase,
14
16
  mutateRenameSwitchCase,
15
- } from "../tabs/transact.js";
16
- import { activeTab } from "../workspace/workspace.js";
17
- import { view } from "../view.js";
18
- import { componentRegistry } from "../files/components.js";
19
- import { widgetForType } from "./style-inputs.js";
20
- import { renderFieldRow } from "../ui/field-row.js";
17
+ } from "../tabs/transact";
18
+ import { activeTab } from "../workspace/workspace";
19
+ import { view } from "../view";
20
+ import { componentRegistry } from "../files/components";
21
+ import { widgetForType } from "./style-inputs";
22
+ import { renderFieldRow } from "../ui/field-row";
21
23
  import {
22
24
  attrLabel,
23
25
  inferInputType,
24
26
  friendlyNameToVar,
25
27
  camelToLabel,
26
28
  parseCemType,
27
- } from "../utils/studio-utils.js";
28
- import { isCustomElementDoc, collectCssParts } from "./signals-panel.js";
29
- import { mediaDisplayName } from "./shared.js";
30
- import { getCssInitialMap } from "./style-utils.js";
31
- import { renderMediaPicker } from "../ui/media-picker.js";
32
- import { renderColorSelector } from "../ui/color-selector.js";
33
- import { getEffectiveLayoutPath, invalidateLayoutCache } from "../site-context.js";
34
- import { getPlatform } from "../platform.js";
29
+ } from "../utils/studio-utils";
30
+ import { isCustomElementDoc, collectCssParts } from "./signals-panel";
31
+ import { mediaDisplayName } from "./shared";
32
+ import { getCssInitialMap } from "./style-utils";
33
+ import { renderMediaPicker } from "../ui/media-picker";
34
+ import { renderColorSelector } from "../ui/color-selector";
35
+ import { getEffectiveLayoutPath, invalidateLayoutCache } from "../site-context";
36
+ import { getPlatform } from "../platform";
35
37
  import htmlMeta from "../../data/html-meta.json";
36
38
 
39
+ import type { JxPrototypeDef, JxMutableNode } from "@jxsuite/schema/types";
40
+ import type { JxPath } from "../state";
41
+
42
+ interface SignalOption {
43
+ value: string;
44
+ label: string;
45
+ }
46
+
47
+ interface HtmlMetaEntry {
48
+ $section: string;
49
+ $order: number;
50
+ $elements?: string[];
51
+ $label?: string;
52
+ $input?: string;
53
+ $shorthand?: boolean;
54
+ type?: string;
55
+ [key: string]: unknown;
56
+ }
57
+
37
58
  /**
38
59
  * Convert a human-friendly name like "Tablet" to a $media key "--tablet"
39
60
  *
40
61
  * @param {string} name
41
62
  */
42
- function friendlyNameToMedia(name) {
63
+ function friendlyNameToMedia(name: string) {
43
64
  return friendlyNameToVar(name, "--");
44
65
  }
45
66
 
46
67
  /** Check if a selection path is inside a $map template (contains [..., "children", "map", ...]). */
47
- function isInsideMapTemplate(/** @type {JxPath | null} */ path) {
68
+ function isInsideMapTemplate(path: JxPath | null) {
48
69
  if (!path) return false;
49
70
  for (let i = 0; i < path.length - 1; i++) {
50
71
  if (path[i] === "children" && path[i + 1] === "map") return true;
@@ -56,42 +77,30 @@ function isInsideMapTemplate(/** @type {JxPath | null} */ path) {
56
77
  * Field row with binding toggle — allows switching between static value and signal binding.
57
78
  * rawValue can be a string/bool (static) or { $ref: "..." } (bound).
58
79
  */
59
- /** @typedef {{ value: string; label: string }} SignalOption */
60
-
61
- /**
62
- * @typedef {{
63
- * $section: string;
64
- * $order: number;
65
- * $elements?: string[];
66
- * $label?: string;
67
- * $input?: string;
68
- * $shorthand?: boolean;
69
- * type?: string;
70
- * [key: string]: unknown;
71
- * }} HtmlMetaEntry
72
- */
73
-
74
80
  function bindableFieldRow(
75
- /** @type {string} */ label,
76
- /** @type {string} */ type,
77
- /** @type {string | number | boolean | { $ref: string } | null | undefined} */ rawValue,
78
- /** @type {(v: JsonValue) => void} */ onChange,
79
- /** @type {((d: import("./signals-panel.js").SignalDef) => boolean) | null} */ filterFn = null,
80
- /** @type {SignalOption[] | null} */ extraSignals = null,
81
+ label: string,
82
+ type: string,
83
+ rawValue: string | number | boolean | { $ref: string } | null | undefined,
84
+ onChange: (v: JsonValue) => void,
85
+ filterFn: ((d: import("./signals-panel.js").SignalDef) => boolean) | null = null,
86
+ extraSignals: SignalOption[] | null = null,
81
87
  ) {
82
88
  const tab = activeTab.value;
83
- const defs = tab.doc.document.state || {};
89
+ const defs = tab!.doc.document.state || {};
84
90
  const isBound = typeof rawValue === "object" && rawValue !== null && rawValue.$ref;
85
91
 
86
92
  const signalDefs = Object.entries(defs).filter(([, d]) =>
87
- filterFn ? filterFn(d) : !d.$handler && d.$prototype !== "Function",
93
+ filterFn
94
+ ? filterFn(d as import("./signals-panel.js").SignalDef)
95
+ : !(d as Record<string, unknown>)?.$handler &&
96
+ (d as JxPrototypeDef)?.$prototype !== "Function",
88
97
  );
89
98
 
90
99
  /** @type {ReturnType<typeof setTimeout> | undefined} */
91
- let debounce;
92
- const onInput = (/** @type {Event} */ e) => {
100
+ let debounce: ReturnType<typeof setTimeout> | undefined;
101
+ const onInput = (e: Event) => {
93
102
  clearTimeout(debounce);
94
- debounce = setTimeout(() => onChange(/** @type {HTMLInputElement} */ (e.target).value), 400);
103
+ debounce = setTimeout(() => onChange((e.target as HTMLInputElement).value), 400);
95
104
  };
96
105
 
97
106
  const staticVal = isBound ? "" : (rawValue ?? "");
@@ -106,8 +115,7 @@ function bindableFieldRow(
106
115
  : type === "checkbox"
107
116
  ? html`<sp-checkbox
108
117
  ?checked=${!!staticVal}
109
- @change=${(/** @type {Event} */ e) =>
110
- onChange(/** @type {HTMLInputElement} */ (e.target).checked)}
118
+ @change=${(e: Event) => onChange((e.target as HTMLInputElement).checked)}
111
119
  ></sp-checkbox>`
112
120
  : html`<sp-textfield size="s" .value=${live(staticVal)} @input=${onInput}></sp-textfield>`;
113
121
 
@@ -117,9 +125,9 @@ function bindableFieldRow(
117
125
  quiet
118
126
  placeholder="— select signal —"
119
127
  value=${isBound && rawValue.$ref ? rawValue.$ref : nothing}
120
- @change=${(/** @type {Event} */ e) => {
121
- if (/** @type {HTMLInputElement} */ (e.target).value)
122
- onChange({ $ref: /** @type {HTMLInputElement} */ (e.target).value });
128
+ @change=${(e: Event) => {
129
+ if ((e.target as HTMLInputElement).value)
130
+ onChange({ $ref: (e.target as HTMLInputElement).value });
123
131
  else onChange(undefined);
124
132
  }}
125
133
  >
@@ -130,7 +138,7 @@ function bindableFieldRow(
130
138
  ? html`
131
139
  <sp-menu-divider></sp-menu-divider>
132
140
  ${extraSignals.map(
133
- (/** @type {SignalOption} */ sig) =>
141
+ (sig: SignalOption) =>
134
142
  html`<sp-menu-item value=${sig.value}>${sig.label}</sp-menu-item>`,
135
143
  )}
136
144
  `
@@ -174,14 +182,14 @@ function bindableFieldRow(
174
182
 
175
183
  /** Key-value pair row for styles / attributes */
176
184
  function kvRow(
177
- /** @type {string} */ key,
178
- /** @type {string} */ value,
179
- /** @type {(newKey: string, newVal: string) => void} */ onChange,
180
- /** @type {() => void} */ onDelete,
185
+ key: string,
186
+ value: string,
187
+ onChange: (newKey: string, newVal: string) => void,
188
+ onDelete: () => void,
181
189
  /** @type {string | null} */ datalistId = null,
182
190
  ) {
183
191
  /** @type {ReturnType<typeof setTimeout> | undefined} */
184
- let debounceTimer;
192
+ let debounceTimer: ReturnType<typeof setTimeout> | undefined;
185
193
  let currentKey = key;
186
194
  let currentVal = value;
187
195
  const commit = () => {
@@ -195,19 +203,19 @@ function kvRow(
195
203
  size="s"
196
204
  class="kv-key"
197
205
  .value=${live(key)}
198
- @input=${(/** @type {Event} */ e) => {
199
- currentKey = /** @type {HTMLInputElement} */ (e.target).value;
206
+ @input=${(e: Event) => {
207
+ currentKey = (e.target as HTMLInputElement).value;
200
208
  commit();
201
209
  }}
202
210
  @change=${datalistId === "css-props"
203
- ? (/** @type {Event} */ e) => {
204
- const el = /** @type {HTMLInputElement} */ (e.target)
211
+ ? (e: Event) => {
212
+ const el = (e.target as HTMLInputElement)
205
213
  .closest(".kv-row")
206
214
  ?.querySelector(".kv-val");
207
215
  if (el)
208
216
  el.setAttribute(
209
217
  "placeholder",
210
- getCssInitialMap().get(/** @type {HTMLInputElement} */ (e.target).value) || "",
218
+ getCssInitialMap().get((e.target as HTMLInputElement).value) || "",
211
219
  );
212
220
  }
213
221
  : nothing}
@@ -217,8 +225,8 @@ function kvRow(
217
225
  class="kv-val"
218
226
  .value=${live(value)}
219
227
  placeholder=${placeholder}
220
- @input=${(/** @type {Event} */ e) => {
221
- currentVal = /** @type {HTMLInputElement} */ (e.target).value;
228
+ @input=${(e: Event) => {
229
+ currentVal = (e.target as HTMLInputElement).value;
222
230
  commit();
223
231
  }}
224
232
  ></sp-textfield>
@@ -233,23 +241,23 @@ function kvRow(
233
241
 
234
242
  /** Repeater fields template */
235
243
  function renderRepeaterFieldsTemplate(
236
- /** @type {JxMutableNode} */ node,
237
- /** @type {JxPath} */ path,
238
- /** @type {SignalOption[] | null} */ _mapSignals,
244
+ node: JxMutableNode,
245
+ path: JxPath,
246
+ _mapSignals: SignalOption[] | null,
239
247
  ) {
240
248
  return html`
241
- ${bindableFieldRow("Items", "text", node.items, (/** @type {JsonValue} */ v) =>
249
+ ${bindableFieldRow("Items", "text", node.items, (v: JsonValue) =>
242
250
  transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, path, "items", v)),
243
251
  )}
244
252
  ${node.filter
245
- ? bindableFieldRow("Filter", "text", node.filter, (/** @type {JsonValue} */ v) =>
253
+ ? bindableFieldRow("Filter", "text", node.filter, (v: JsonValue) =>
246
254
  transactDoc(activeTab.value, (t) =>
247
255
  mutateUpdateProperty(t, path, "filter", v || undefined),
248
256
  ),
249
257
  )
250
258
  : nothing}
251
259
  ${node.sort
252
- ? bindableFieldRow("Sort", "text", node.sort, (/** @type {JsonValue} */ v) =>
260
+ ? bindableFieldRow("Sort", "text", node.sort, (v: JsonValue) =>
253
261
  transactDoc(activeTab.value, (t) =>
254
262
  mutateUpdateProperty(t, path, "sort", v || undefined),
255
263
  ),
@@ -283,7 +291,7 @@ function renderRepeaterFieldsTemplate(
283
291
  size="s"
284
292
  style="margin-top:8px;width:100%"
285
293
  @click=${() => {
286
- activeTab.value.session.selection = [...path, "map"];
294
+ activeTab.value!.session.selection = [...path, "map"];
287
295
  }}
288
296
  >Edit template →</sp-action-button
289
297
  >
@@ -294,9 +302,9 @@ function renderRepeaterFieldsTemplate(
294
302
 
295
303
  /** Switch fields template */
296
304
  function renderSwitchFieldsTemplate(
297
- /** @type {JxMutableNode} */ node,
298
- /** @type {JxPath} */ path,
299
- /** @type {SignalOption[] | null} */ mapSignals,
305
+ node: JxMutableNode,
306
+ path: JxPath,
307
+ mapSignals: SignalOption[] | null,
300
308
  ) {
301
309
  const caseNames = Object.keys(node.cases || {});
302
310
  return html`
@@ -304,7 +312,7 @@ function renderSwitchFieldsTemplate(
304
312
  "Expression",
305
313
  "text",
306
314
  node.$switch,
307
- (/** @type {JsonValue} */ v) =>
315
+ (v: JsonValue) =>
308
316
  transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, path, "$switch", v)),
309
317
  null,
310
318
  mapSignals,
@@ -316,27 +324,22 @@ function renderSwitchFieldsTemplate(
316
324
  </div>
317
325
  ${caseNames.map((caseName) => {
318
326
  /** @type {ReturnType<typeof setTimeout> | undefined} */
319
- let debounce;
327
+ let debounce: ReturnType<typeof setTimeout> | undefined;
320
328
  return html`
321
329
  <div class="field-row" style="display:flex;align-items:center;gap:4px;margin-bottom:3px">
322
330
  <input
323
331
  class="field-input"
324
332
  .value=${live(caseName)}
325
333
  style="flex:1"
326
- @input=${(/** @type {Event} */ e) => {
334
+ @input=${(e: Event) => {
327
335
  clearTimeout(debounce);
328
336
  debounce = setTimeout(() => {
329
337
  if (
330
- /** @type {HTMLInputElement} */ (e.target).value &&
331
- /** @type {HTMLInputElement} */ (e.target).value !== caseName
338
+ (e.target as HTMLInputElement).value &&
339
+ (e.target as HTMLInputElement).value !== caseName
332
340
  )
333
341
  transactDoc(activeTab.value, (t) =>
334
- mutateRenameSwitchCase(
335
- t,
336
- path,
337
- caseName,
338
- /** @type {HTMLInputElement} */ (e.target).value,
339
- ),
342
+ mutateRenameSwitchCase(t, path, caseName, (e.target as HTMLInputElement).value),
340
343
  );
341
344
  }, 500);
342
345
  }}
@@ -345,17 +348,17 @@ function renderSwitchFieldsTemplate(
345
348
  class="bind-toggle"
346
349
  title="Edit case"
347
350
  style="cursor:pointer"
348
- @click=${(/** @type {Event} */ e) => {
351
+ @click=${(e: Event) => {
349
352
  e.stopPropagation();
350
- activeTab.value.session.selection = [...path, "cases", caseName];
353
+ activeTab.value!.session.selection = [...path, "cases", caseName];
351
354
  }}
352
355
  >→</span
353
356
  >
354
357
  <span
355
358
  style="cursor:pointer;color:var(--danger);font-size:11px"
356
- @click=${(/** @type {Event} */ e) => {
359
+ @click=${(e: Event) => {
357
360
  e.stopPropagation();
358
- transactDoc(activeTab.value, (t) => mutateRemoveSwitchCase(t, path, caseName));
361
+ transactDoc(activeTab.value!, (t) => mutateRemoveSwitchCase(t, path, caseName));
359
362
  }}
360
363
  >✕</span
361
364
  >
@@ -376,10 +379,10 @@ function renderSwitchFieldsTemplate(
376
379
 
377
380
  /** Component props fields template */
378
381
  function renderComponentPropsFieldsTemplate(
379
- /** @type {JxMutableNode} */ node,
380
- /** @type {JxPath} */ path,
381
- /** @type {SignalOption[] | null} */ mapSignals,
382
- /** @type {(path: string) => void} */ navigateToComponent,
382
+ node: JxMutableNode,
383
+ path: JxPath,
384
+ mapSignals: SignalOption[] | null,
385
+ navigateToComponent: (path: string) => void,
383
386
  ) {
384
387
  const tab = activeTab.value;
385
388
  const comp = componentRegistry.find((c) => c.tagName === node.tagName);
@@ -387,21 +390,17 @@ function renderComponentPropsFieldsTemplate(
387
390
  const isNpm = comp.source === "npm";
388
391
  const currentVals = isNpm ? node.attributes || {} : node.$props || {};
389
392
  const updateFn = isNpm
390
- ? (/** @type {string} */ name, /** @type {JsonValue} */ v) =>
393
+ ? (name: string, v: JsonValue) =>
391
394
  transactDoc(activeTab.value, (t) =>
392
- mutateUpdateAttribute(
393
- t,
394
- path,
395
- name,
396
- v === "" ? undefined : /** @type {string | undefined} */ (v),
397
- ),
395
+ mutateUpdateAttribute(t, path, name, v === "" ? undefined : (v as string | undefined)),
398
396
  )
399
- : (/** @type {string} */ name, /** @type {JsonValue} */ v) =>
397
+ : (name: string, v: JsonValue) =>
400
398
  transactDoc(activeTab.value, (t) => mutateUpdateProp(t, path, name, v));
401
399
 
402
- const defs = tab.doc.document.state || {};
400
+ const defs = tab!.doc.document.state || {};
403
401
  const signalDefs = Object.entries(defs).filter(
404
- ([, d]) => !d.$handler && d.$prototype !== "Function",
402
+ ([, d]) =>
403
+ !(d as Record<string, unknown>)?.$handler && (d as JxPrototypeDef)?.$prototype !== "Function",
405
404
  );
406
405
  const extraSignals = mapSignals;
407
406
 
@@ -414,9 +413,10 @@ function renderComponentPropsFieldsTemplate(
414
413
  const isBound = typeof rawValue === "object" && rawValue !== null && rawValue.$ref;
415
414
  const hasVal = rawValue !== undefined && rawValue !== null;
416
415
  const parsed = parseCemType(prop.type);
417
- const onChange = (/** @type {JsonValue} */ v) => updateFn(prop.name, v);
416
+ const onChange = (v: JsonValue) => updateFn(prop.name, v);
417
+ const staticVal = isBound ? "" : String(rawValue ?? "");
418
418
 
419
- const clearProp = (/** @type {Event} */ e) => {
419
+ const clearProp = (e: Event) => {
420
420
  e.stopPropagation();
421
421
  updateFn(prop.name, undefined);
422
422
  };
@@ -446,9 +446,9 @@ function renderComponentPropsFieldsTemplate(
446
446
  quiet
447
447
  placeholder="— select signal —"
448
448
  value=${isBound && rawValue.$ref ? rawValue.$ref : nothing}
449
- @change=${(/** @type {Event} */ e) => {
450
- if (/** @type {HTMLInputElement} */ (e.target).value)
451
- onChange({ $ref: /** @type {HTMLInputElement} */ (e.target).value });
449
+ @change=${(e: Event) => {
450
+ if ((e.target as HTMLInputElement).value)
451
+ onChange({ $ref: (e.target as HTMLInputElement).value });
452
452
  else onChange(undefined);
453
453
  }}
454
454
  >
@@ -460,7 +460,7 @@ function renderComponentPropsFieldsTemplate(
460
460
  ? html`
461
461
  <sp-menu-divider></sp-menu-divider>
462
462
  ${extraSignals.map(
463
- (/** @type {SignalOption} */ sig) =>
463
+ (sig: SignalOption) =>
464
464
  html`<sp-menu-item value=${sig.value}>${sig.label}</sp-menu-item>`,
465
465
  )}
466
466
  `
@@ -469,9 +469,7 @@ function renderComponentPropsFieldsTemplate(
469
469
  `;
470
470
 
471
471
  /** @type {ReturnType<typeof setTimeout> | undefined} */
472
- let debounce;
473
- const staticVal = isBound ? "" : (rawValue ?? "");
474
- /** @type {import("lit-html").TemplateResult | undefined} */
472
+ let debounce: ReturnType<typeof setTimeout> | undefined;
475
473
  let widgetTpl;
476
474
  if (prop.format === "image") {
477
475
  widgetTpl = renderMediaPicker(prop.name, staticVal, onChange);
@@ -482,55 +480,43 @@ function renderComponentPropsFieldsTemplate(
482
480
  size="s"
483
481
  placeholder="YYYY-MM-DD"
484
482
  .value=${live(staticVal)}
485
- @input=${(/** @type {Event} */ e) => {
483
+ @input=${(e: Event) => {
486
484
  clearTimeout(debounce);
487
- debounce = setTimeout(
488
- () => onChange(/** @type {HTMLInputElement} */ (e.target).value),
489
- 400,
490
- );
485
+ debounce = setTimeout(() => onChange((e.target as HTMLInputElement).value), 400);
491
486
  }}
492
487
  ></sp-textfield>`;
493
488
  } else if (parsed.kind === "boolean") {
494
489
  widgetTpl = html`<sp-checkbox
495
490
  size="s"
496
491
  .checked=${live(!!staticVal)}
497
- @change=${(/** @type {Event} */ e) =>
498
- onChange(/** @type {HTMLInputElement} */ (e.target).checked || undefined)}
492
+ @change=${(e: Event) => onChange((e.target as HTMLInputElement).checked || undefined)}
499
493
  ></sp-checkbox>`;
500
494
  } else if (parsed.kind === "number") {
501
495
  widgetTpl = html`<sp-number-field
502
496
  size="s"
503
497
  .value=${live(staticVal)}
504
- @input=${(/** @type {Event} */ e) => {
498
+ @input=${(e: Event) => {
505
499
  clearTimeout(debounce);
506
- debounce = setTimeout(
507
- () => onChange(/** @type {HTMLInputElement} */ (e.target).value),
508
- 400,
509
- );
500
+ debounce = setTimeout(() => onChange((e.target as HTMLInputElement).value), 400);
510
501
  }}
511
502
  ></sp-number-field>`;
512
503
  } else if (parsed.kind === "combobox") {
513
- const options = /** @type {string[]} */ (
514
- /** @type {{ options?: string[] }} */ (parsed).options
515
- );
504
+ const options = /** @type {{ options?: string[] }} */ (parsed).options as string[];
516
505
  widgetTpl = html`<jx-value-selector
517
506
  .value=${String(staticVal)}
518
507
  size="s"
519
508
  placeholder="—"
520
509
  .options=${options.map((o) => ({ value: o, label: camelToLabel(o) }))}
521
- @change=${(/** @type {Event & { detail?: { value?: string } }} */ e) =>
522
- onChange(e.detail?.value ?? /** @type {HTMLInputElement} */ (e.target).value)}
510
+ @change=${(e: Event & { detail?: { value?: string } }) =>
511
+ onChange(e.detail?.value ?? (e.target as HTMLInputElement).value)}
523
512
  ></jx-value-selector>`;
524
513
  } else {
525
514
  widgetTpl = html`<sp-textfield
526
515
  size="s"
527
516
  .value=${live(staticVal)}
528
- @input=${(/** @type {Event} */ e) => {
517
+ @input=${(e: Event) => {
529
518
  clearTimeout(debounce);
530
- debounce = setTimeout(
531
- () => onChange(/** @type {HTMLInputElement} */ (e.target).value),
532
- 400,
533
- );
519
+ debounce = setTimeout(() => onChange((e.target as HTMLInputElement).value), 400);
534
520
  }}
535
521
  ></sp-textfield>`;
536
522
  }
@@ -568,10 +554,10 @@ function renderComponentPropsFieldsTemplate(
568
554
 
569
555
  /** Custom attrs fields template */
570
556
  function renderCustomAttrsFieldsTemplate(
571
- /** @type {JxMutableNode} */ node,
572
- /** @type {JxPath} */ path,
573
- /** @type {Record<string, unknown>} */ attrs,
574
- /** @type {Set<string>} */ knownAttrNames,
557
+ node: JxMutableNode,
558
+ path: JxPath,
559
+ attrs: Record<string, unknown>,
560
+ knownAttrNames: Set<string>,
575
561
  ) {
576
562
  const customAttrs = Object.entries(attrs).filter(([k]) => !knownAttrNames.has(k));
577
563
  return html`
@@ -579,7 +565,7 @@ function renderCustomAttrsFieldsTemplate(
579
565
  kvRow(
580
566
  attr,
581
567
  String(val),
582
- (/** @type {string} */ newAttr, /** @type {string} */ newVal) => {
568
+ (newAttr: string, newVal: string) => {
583
569
  if (newAttr !== attr) {
584
570
  transactDoc(activeTab.value, (t) => {
585
571
  mutateUpdateAttribute(t, path, attr, undefined);
@@ -604,10 +590,10 @@ function renderCustomAttrsFieldsTemplate(
604
590
  // ─── Media breakpoints ──────────────────────────────────────────────────────
605
591
 
606
592
  /** Media breakpoint fields template */
607
- function renderMediaFieldsTemplate(/** @type {JxMutableNode} */ node) {
593
+ function renderMediaFieldsTemplate(node: JxMutableNode) {
608
594
  const media = node.$media || {};
609
595
  /** @type {ReturnType<typeof setTimeout> | undefined} */
610
- let baseDebounce;
596
+ let baseDebounce: ReturnType<typeof setTimeout> | undefined;
611
597
  const breakpoints = Object.entries(media).filter(([k]) => k !== "--");
612
598
 
613
599
  return html`
@@ -618,10 +604,10 @@ function renderMediaFieldsTemplate(/** @type {JxMutableNode} */ node) {
618
604
  style="width:70px;flex:none"
619
605
  placeholder="320px"
620
606
  .value=${live(media["--"] || "")}
621
- @input=${(/** @type {Event} */ e) => {
607
+ @input=${(e: Event) => {
622
608
  clearTimeout(baseDebounce);
623
609
  baseDebounce = setTimeout(() => {
624
- const val = /** @type {HTMLInputElement} */ (e.target).value.trim();
610
+ const val = (e.target as HTMLInputElement).value.trim();
625
611
  transactDoc(activeTab.value, (t) => mutateUpdateMedia(t, "--", val || undefined));
626
612
  }, 400);
627
613
  }}
@@ -642,7 +628,7 @@ function renderMediaFieldsTemplate(/** @type {JxMutableNode} */ node) {
642
628
  <span
643
629
  class="kv-add"
644
630
  style=${view.showAddBreakpointForm ? "display:none" : ""}
645
- @click=${(/** @type {Event} */ _e) => {
631
+ @click=${(_e: Event) => {
646
632
  view.showAddBreakpointForm = true;
647
633
  renderOnly("rightPanel");
648
634
  }}
@@ -656,9 +642,9 @@ function renderMediaFieldsTemplate(/** @type {JxMutableNode} */ node) {
656
642
  class="field-input"
657
643
  placeholder="Name (e.g. Tablet)"
658
644
  style="flex:1"
659
- @input=${(/** @type {Event} */ e) => {
645
+ @input=${(e: Event) => {
660
646
  view.addBreakpointPreview =
661
- friendlyNameToMedia(/** @type {HTMLInputElement} */ (e.target).value) || "";
647
+ friendlyNameToMedia((e.target as HTMLInputElement).value) || "";
662
648
  renderOnly("rightPanel");
663
649
  }}
664
650
  />
@@ -674,13 +660,11 @@ function renderMediaFieldsTemplate(/** @type {JxMutableNode} */ node) {
674
660
  <button
675
661
  class="kv-add"
676
662
  style="padding:2px 10px;cursor:pointer"
677
- @click=${(/** @type {Event} */ e) => {
678
- const wrap = /** @type {HTMLElement} */ (e.target).closest(
679
- "div",
680
- )?.parentElement;
663
+ @click=${(e: Event) => {
664
+ const wrap = (e.target as HTMLElement).closest("div")?.parentElement;
681
665
  const nameVal = wrap?.querySelector("input")?.value;
682
- const queryVal = /** @type {HTMLInputElement | null} */ (
683
- wrap?.querySelector(".add-bp-query")
666
+ const queryVal = (
667
+ wrap?.querySelector(".add-bp-query") as HTMLInputElement | null
684
668
  )?.value?.trim();
685
669
  const key = friendlyNameToMedia(nameVal || "");
686
670
  if (key && queryVal) {
@@ -712,9 +696,9 @@ function renderMediaFieldsTemplate(/** @type {JxMutableNode} */ node) {
712
696
  }
713
697
 
714
698
  /** Single media breakpoint row template */
715
- function mediaBreakpointRowTemplate(/** @type {string} */ name, /** @type {string} */ query) {
699
+ function mediaBreakpointRowTemplate(name: string, query: string) {
716
700
  /** @type {ReturnType<typeof setTimeout> | undefined} */
717
- let debounceTimer;
701
+ let debounceTimer: ReturnType<typeof setTimeout> | undefined;
718
702
  let currentRawLabel = name;
719
703
  return html`
720
704
  <div style="margin-bottom:6px;padding:4px 0;border-bottom:1px solid var(--border)">
@@ -723,21 +707,17 @@ function mediaBreakpointRowTemplate(/** @type {string} */ name, /** @type {strin
723
707
  class="field-input"
724
708
  .value=${live(mediaDisplayName(name))}
725
709
  style="flex:1;font-weight:600;font-size:12px"
726
- @input=${(/** @type {Event} */ e) => {
727
- const newKey = friendlyNameToMedia(/** @type {HTMLInputElement} */ (e.target).value);
710
+ @input=${(e: Event) => {
711
+ const newKey = friendlyNameToMedia((e.target as HTMLInputElement).value);
728
712
  currentRawLabel = newKey || "";
729
- const rawEl = /** @type {HTMLElement} */ (e.target).parentElement?.querySelector(
730
- ".bp-raw-label",
731
- );
713
+ const rawEl = (e.target as HTMLElement).parentElement?.querySelector(".bp-raw-label");
732
714
  if (rawEl) rawEl.textContent = currentRawLabel;
733
715
  clearTimeout(debounceTimer);
734
716
  debounceTimer = setTimeout(() => {
735
717
  if (newKey && newKey !== name) {
736
- const queryEl = /** @type {HTMLInputElement | null} */ (
737
- /** @type {HTMLElement} */ (e.target)
738
- .closest("div[style]")
739
- ?.parentElement?.querySelector(".bp-query-input")
740
- );
718
+ const queryEl = (e.target as HTMLElement)
719
+ .closest("div[style]")
720
+ ?.parentElement?.querySelector(".bp-query-input") as HTMLInputElement | null;
741
721
  transactDoc(activeTab.value, (t) => {
742
722
  mutateUpdateMedia(t, name, undefined);
743
723
  mutateUpdateMedia(t, newKey, queryEl?.value || query);
@@ -762,12 +742,12 @@ function mediaBreakpointRowTemplate(/** @type {string} */ name, /** @type {strin
762
742
  class="field-input bp-query-input"
763
743
  .value=${live(query)}
764
744
  style="flex:1"
765
- @input=${(/** @type {Event} */ e) => {
745
+ @input=${(e: Event) => {
766
746
  clearTimeout(debounceTimer);
767
747
  debounceTimer = setTimeout(
768
748
  () =>
769
749
  transactDoc(activeTab.value, (t) =>
770
- mutateUpdateMedia(t, name, /** @type {HTMLInputElement} */ (e.target).value),
750
+ mutateUpdateMedia(t, name, (e.target as HTMLInputElement).value),
771
751
  ),
772
752
  400,
773
753
  );
@@ -781,18 +761,15 @@ function mediaBreakpointRowTemplate(/** @type {string} */ name, /** @type {strin
781
761
  // ─── Layout picker ──────────────────────────────────────────────────────────
782
762
 
783
763
  /** @type {{ name: string; path: string }[] | null} */
784
- let layoutEntries = null;
764
+ let layoutEntries: { name: string; path: string }[] | null = null;
785
765
 
786
766
  async function loadLayoutEntries() {
787
767
  try {
788
768
  const platform = getPlatform();
789
769
  const listing = await platform.listDirectory("layouts");
790
770
  layoutEntries = listing
791
- .filter(
792
- (/** @type {{ type: string; name: string }} */ f) =>
793
- f.type === "file" && f.name.endsWith(".json"),
794
- )
795
- .map((/** @type {{ type: string; name: string }} */ f) => ({
771
+ .filter((f: DirEntry) => f.type === "file" && f.name.endsWith(".json"))
772
+ .map((f: DirEntry) => ({
796
773
  name: f.name.replace(/\.json$/, ""),
797
774
  path: `./layouts/${f.name}`,
798
775
  }));
@@ -806,14 +783,14 @@ export function invalidateLayoutPickerCache() {
806
783
  layoutEntries = null;
807
784
  }
808
785
 
809
- function isPageDocument(/** @type {string | undefined | null} */ documentPath) {
786
+ function isPageDocument(documentPath: string | undefined | null) {
810
787
  if (!documentPath || !projectState?.isSiteProject) return false;
811
788
  return documentPath.startsWith("pages/") || documentPath.startsWith("./pages/");
812
789
  }
813
790
 
814
- function renderPageSection(/** @type {JxMutableNode} */ node) {
791
+ function renderPageSection(node: JxMutableNode) {
815
792
  const tab = activeTab.value;
816
- if (!isPageDocument(tab.documentPath)) return nothing;
793
+ if (!isPageDocument(tab!.documentPath)) return nothing;
817
794
 
818
795
  if (layoutEntries === null) {
819
796
  loadLayoutEntries();
@@ -835,7 +812,7 @@ function renderPageSection(/** @type {JxMutableNode} */ node) {
835
812
  ? html`<span
836
813
  class="set-dot"
837
814
  title="Reset to default"
838
- @click=${(/** @type {Event} */ e) => {
815
+ @click=${(e: Event) => {
839
816
  e.stopPropagation();
840
817
  transactDoc(activeTab.value, (t) =>
841
818
  mutateUpdateProperty(t, [], "$layout", undefined),
@@ -848,8 +825,8 @@ function renderPageSection(/** @type {JxMutableNode} */ node) {
848
825
  <sp-picker
849
826
  size="s"
850
827
  value=${displayValue}
851
- @change=${(/** @type {Event} */ e) => {
852
- const val = /** @type {HTMLInputElement} */ (e.target).value;
828
+ @change=${(e: Event) => {
829
+ const val = (e.target as HTMLInputElement).value;
853
830
  if (val === "__default__") {
854
831
  transactDoc(activeTab.value, (t) =>
855
832
  mutateUpdateProperty(t, [], "$layout", undefined),
@@ -869,8 +846,8 @@ function renderPageSection(/** @type {JxMutableNode} */ node) {
869
846
  >
870
847
  <sp-menu-item value="__none__">None</sp-menu-item>
871
848
  <sp-menu-divider></sp-menu-divider>
872
- ${layoutEntries.map(
873
- (/** @type {{ name: string; path: string }} */ l) =>
849
+ ${layoutEntries!.map(
850
+ (l: { name: string; path: string }) =>
874
851
  html`<sp-menu-item value=${l.path}>${l.name}</sp-menu-item>`,
875
852
  )}
876
853
  </sp-picker>
@@ -887,12 +864,8 @@ function renderPageSection(/** @type {JxMutableNode} */ node) {
887
864
 
888
865
  // ─── Layout selection panel ─────────────────────────────────────────────────
889
866
 
890
- function renderLayoutSelectionPanel(
891
- /** @type {{ navigateToComponent: (path: string) => void }} */ ctx,
892
- ) {
893
- const { el, layoutPath } = /** @type {{ el: HTMLElement; layoutPath: string }} */ (
894
- view.layoutSelection
895
- );
867
+ function renderLayoutSelectionPanel(ctx: { navigateToComponent: (path: string) => void }) {
868
+ const { el, layoutPath } = view.layoutSelection as { el: HTMLElement; layoutPath: string };
896
869
  const tagName = el?.tagName?.toLowerCase() || "element";
897
870
  const className = el?.className || "";
898
871
  const displayPath = layoutPath || "layout";
@@ -939,7 +912,9 @@ function renderLayoutSelectionPanel(
939
912
  *
940
913
  * @param {{ navigateToComponent: (path: string) => void }} ctx
941
914
  */
942
- export function renderPropertiesPanelTemplate(ctx) {
915
+ export function renderPropertiesPanelTemplate(ctx: {
916
+ navigateToComponent: (path: string) => void;
917
+ }) {
943
918
  const tab = activeTab.value;
944
919
  if (!tab) return html`<div class="empty-state">No document loaded</div>`;
945
920
 
@@ -959,7 +934,7 @@ export function renderPropertiesPanelTemplate(ctx) {
959
934
  const isMapParent =
960
935
  node.children &&
961
936
  typeof node.children === "object" &&
962
- /** @type {{ $prototype?: string }} */ (node.children).$prototype === "Array";
937
+ (node.children as unknown as { $prototype?: string }).$prototype === "Array";
963
938
  const isSwitchNode = !!node.$switch;
964
939
  const isCustomInstance = (node.tagName || "").includes("-");
965
940
  const isRoot = path.length === 0;
@@ -973,11 +948,7 @@ export function renderPropertiesPanelTemplate(ctx) {
973
948
  ]
974
949
  : null;
975
950
 
976
- function renderAttrRow(
977
- /** @type {string} */ attr,
978
- /** @type {HtmlMetaEntry} */ entry,
979
- /** @type {unknown} */ value,
980
- ) {
951
+ function renderAttrRow(attr: string, entry: HtmlMetaEntry, value: unknown) {
981
952
  const type = inferInputType(entry);
982
953
  const hasVal = value !== undefined && value !== "";
983
954
 
@@ -992,13 +963,13 @@ export function renderPropertiesPanelTemplate(ctx) {
992
963
  <sp-checkbox
993
964
  size="s"
994
965
  .checked=${live(!!value)}
995
- @change=${(/** @type {Event} */ e) =>
966
+ @change=${(e: Event) =>
996
967
  transactDoc(activeTab.value, (t) =>
997
968
  mutateUpdateAttribute(
998
969
  t,
999
970
  path,
1000
971
  attr,
1001
- /** @type {HTMLInputElement} */ (e.target).checked ? "" : undefined,
972
+ (e.target as HTMLInputElement).checked ? "" : undefined,
1002
973
  ),
1003
974
  )}
1004
975
  >
@@ -1013,23 +984,21 @@ export function renderPropertiesPanelTemplate(ctx) {
1013
984
  hasValue: hasVal,
1014
985
  onClear: () =>
1015
986
  transactDoc(activeTab.value, (t) => mutateUpdateAttribute(t, path, attr, undefined)),
1016
- widget: widgetForType(type, entry, attr, String(value || ""), (/** @type {string} */ v) =>
1017
- transactDoc(activeTab.value, (t) => mutateUpdateAttribute(t, path, attr, v || undefined)),
987
+ widget: widgetForType(type, entry, attr, String(value || ""), (v: string) =>
988
+ transactDoc(activeTab.value!, (t) => mutateUpdateAttribute(t, path, attr, v || undefined)),
1018
989
  ),
1019
990
  });
1020
991
  }
1021
992
 
1022
993
  // ── Collect applicable attributes from html-meta ──
1023
- const applicableAttrs = /** @type {Record<string, HtmlMetaEntry>} */ ({});
1024
- for (const [attr, entry] of /** @type {[string, HtmlMetaEntry][]} */ (
1025
- Object.entries(htmlMeta.$defs)
1026
- )) {
994
+ const applicableAttrs = {} as Record<string, HtmlMetaEntry>;
995
+ for (const [attr, entry] of Object.entries(htmlMeta.$defs) as [string, HtmlMetaEntry][]) {
1027
996
  if (!entry.$elements || entry.$elements.includes(tagName)) {
1028
997
  applicableAttrs[attr] = entry;
1029
998
  }
1030
999
  }
1031
1000
 
1032
- const attrSections = /** @type {Record<string, { name: string; entry: HtmlMetaEntry }[]>} */ ({});
1001
+ const attrSections: Record<string, { name: string; entry: HtmlMetaEntry }[]> = {};
1033
1002
  for (const sec of htmlMeta.$sections) attrSections[sec.key] = [];
1034
1003
  for (const [attr, entry] of Object.entries(applicableAttrs)) {
1035
1004
  const secKey = entry.$section;
@@ -1037,10 +1006,8 @@ export function renderPropertiesPanelTemplate(ctx) {
1037
1006
  }
1038
1007
  for (const sec of htmlMeta.$sections) {
1039
1008
  attrSections[sec.key].sort(
1040
- (
1041
- /** @type {{ name: string; entry: HtmlMetaEntry }} */ a,
1042
- /** @type {{ name: string; entry: HtmlMetaEntry }} */ b,
1043
- ) => a.entry.$order - b.entry.$order,
1009
+ (a: { name: string; entry: HtmlMetaEntry }, b: { name: string; entry: HtmlMetaEntry }) =>
1010
+ a.entry.$order - b.entry.$order,
1044
1011
  );
1045
1012
  }
1046
1013
 
@@ -1058,16 +1025,16 @@ export function renderPropertiesPanelTemplate(ctx) {
1058
1025
  }
1059
1026
  if (customAttrs.length > 0) autoOpen.add("__custom");
1060
1027
 
1061
- function isSectionOpen(/** @type {string} */ key) {
1062
- if (tab.session.ui.inspectorSections[key] !== undefined)
1063
- return tab.session.ui.inspectorSections[key];
1028
+ function isSectionOpen(key: string) {
1029
+ if (tab!.session.ui.inspectorSections[key] !== undefined)
1030
+ return tab!.session.ui.inspectorSections[key];
1064
1031
  return autoOpen.has(key);
1065
1032
  }
1066
1033
 
1067
- function toggleSection(/** @type {string} */ key) {
1034
+ function toggleSection(key: string) {
1068
1035
  const current = isSectionOpen(key);
1069
- activeTab.value.session.ui.inspectorSections = {
1070
- ...activeTab.value.session.ui.inspectorSections,
1036
+ activeTab.value!.session.ui.inspectorSections = {
1037
+ ...activeTab.value!.session.ui.inspectorSections,
1071
1038
  [key]: !current,
1072
1039
  };
1073
1040
  }
@@ -1090,13 +1057,13 @@ export function renderPropertiesPanelTemplate(ctx) {
1090
1057
  .value=${live(tagName)}
1091
1058
  autocomplete="off"
1092
1059
  list="tag-names"
1093
- @input=${debouncedStyleCommit("prop:tagName", 400, (/** @type {Event} */ e) => {
1060
+ @input=${debouncedStyleCommit("prop:tagName", 400, (e: Event) => {
1094
1061
  transactDoc(activeTab.value, (t) =>
1095
1062
  mutateUpdateProperty(
1096
1063
  t,
1097
1064
  path,
1098
1065
  "tagName",
1099
- /** @type {HTMLInputElement} */ (e.target).value || undefined,
1066
+ (e.target as HTMLInputElement).value || undefined,
1100
1067
  ),
1101
1068
  );
1102
1069
  })}
@@ -1108,7 +1075,7 @@ export function renderPropertiesPanelTemplate(ctx) {
1108
1075
  ? html`<span
1109
1076
  class="set-dot"
1110
1077
  title="Clear $id"
1111
- @click=${(/** @type {Event} */ e) => {
1078
+ @click=${(e: Event) => {
1112
1079
  e.stopPropagation();
1113
1080
  transactDoc(activeTab.value, (t) =>
1114
1081
  mutateUpdateProperty(t, path, "$id", undefined),
@@ -1121,13 +1088,13 @@ export function renderPropertiesPanelTemplate(ctx) {
1121
1088
  <sp-textfield
1122
1089
  size="s"
1123
1090
  .value=${live(node.$id || "")}
1124
- @input=${debouncedStyleCommit("prop:$id", 400, (/** @type {Event} */ e) => {
1091
+ @input=${debouncedStyleCommit("prop:$id", 400, (e: Event) => {
1125
1092
  transactDoc(activeTab.value, (t) =>
1126
1093
  mutateUpdateProperty(
1127
1094
  t,
1128
1095
  path,
1129
1096
  "$id",
1130
- /** @type {HTMLInputElement} */ (e.target).value || undefined,
1097
+ (e.target as HTMLInputElement).value || undefined,
1131
1098
  ),
1132
1099
  );
1133
1100
  })}
@@ -1139,7 +1106,7 @@ export function renderPropertiesPanelTemplate(ctx) {
1139
1106
  ? html`<span
1140
1107
  class="set-dot"
1141
1108
  title="Clear class"
1142
- @click=${(/** @type {Event} */ e) => {
1109
+ @click=${(e: Event) => {
1143
1110
  e.stopPropagation();
1144
1111
  transactDoc(activeTab.value, (t) =>
1145
1112
  mutateUpdateProperty(t, path, "className", undefined),
@@ -1152,13 +1119,13 @@ export function renderPropertiesPanelTemplate(ctx) {
1152
1119
  <sp-textfield
1153
1120
  size="s"
1154
1121
  .value=${live(node.className || "")}
1155
- @input=${debouncedStyleCommit("prop:className", 400, (/** @type {Event} */ e) => {
1122
+ @input=${debouncedStyleCommit("prop:className", 400, (e: Event) => {
1156
1123
  transactDoc(activeTab.value, (t) =>
1157
1124
  mutateUpdateProperty(
1158
1125
  t,
1159
1126
  path,
1160
1127
  "className",
1161
- /** @type {HTMLInputElement} */ (e.target).value || undefined,
1128
+ (e.target as HTMLInputElement).value || undefined,
1162
1129
  ),
1163
1130
  );
1164
1131
  })}
@@ -1172,7 +1139,7 @@ export function renderPropertiesPanelTemplate(ctx) {
1172
1139
  ? html`<span
1173
1140
  class="set-dot"
1174
1141
  title="Clear text"
1175
- @click=${(/** @type {Event} */ e) => {
1142
+ @click=${(e: Event) => {
1176
1143
  e.stopPropagation();
1177
1144
  transactDoc(activeTab.value, (t) =>
1178
1145
  mutateUpdateProperty(t, path, "textContent", undefined),
@@ -1190,20 +1157,16 @@ export function renderPropertiesPanelTemplate(ctx) {
1190
1157
  ? node.textContent
1191
1158
  : (node.textContent ?? ""),
1192
1159
  )}
1193
- @input=${debouncedStyleCommit(
1194
- "prop:textContent",
1195
- 400,
1196
- (/** @type {Event} */ e) => {
1197
- transactDoc(activeTab.value, (t) =>
1198
- mutateUpdateProperty(
1199
- t,
1200
- path,
1201
- "textContent",
1202
- /** @type {HTMLInputElement} */ (e.target).value || undefined,
1203
- ),
1204
- );
1205
- },
1206
- )}
1160
+ @input=${debouncedStyleCommit("prop:textContent", 400, (e: Event) => {
1161
+ transactDoc(activeTab.value, (t) =>
1162
+ mutateUpdateProperty(
1163
+ t,
1164
+ path,
1165
+ "textContent",
1166
+ (e.target as HTMLInputElement).value || undefined,
1167
+ ),
1168
+ );
1169
+ })}
1207
1170
  ></sp-textfield>
1208
1171
  </div>
1209
1172
  `
@@ -1214,7 +1177,7 @@ export function renderPropertiesPanelTemplate(ctx) {
1214
1177
  ? html`<span
1215
1178
  class="set-dot"
1216
1179
  title="Clear hidden"
1217
- @click=${(/** @type {Event} */ e) => {
1180
+ @click=${(e: Event) => {
1218
1181
  e.stopPropagation();
1219
1182
  transactDoc(activeTab.value, (t) =>
1220
1183
  mutateUpdateProperty(t, path, "hidden", undefined),
@@ -1227,13 +1190,13 @@ export function renderPropertiesPanelTemplate(ctx) {
1227
1190
  <sp-checkbox
1228
1191
  size="s"
1229
1192
  .checked=${live(!!node.hidden)}
1230
- @change=${(/** @type {Event} */ e) =>
1193
+ @change=${(e: Event) =>
1231
1194
  transactDoc(activeTab.value, (t) =>
1232
1195
  mutateUpdateProperty(
1233
1196
  t,
1234
1197
  path,
1235
1198
  "hidden",
1236
- /** @type {HTMLInputElement} */ (e.target).checked || undefined,
1199
+ (e.target as HTMLInputElement).checked || undefined,
1237
1200
  ),
1238
1201
  )}
1239
1202
  >
@@ -1274,7 +1237,9 @@ export function renderPropertiesPanelTemplate(ctx) {
1274
1237
  isCustomElementDoc({ document: tab.doc.document }) && isRoot
1275
1238
  ? (() => {
1276
1239
  const state = tab.doc.document.state || {};
1277
- const entries = Object.entries(state).filter(([, d]) => d.attribute);
1240
+ const entries = Object.entries(state).filter(
1241
+ ([, d]) => (d as Record<string, unknown>).attribute,
1242
+ );
1278
1243
  return html`
1279
1244
  <sp-accordion-item label="Observed Attributes" ?open=${isSectionOpen("__observed")}>
1280
1245
  <div class="style-section-body">
@@ -1282,30 +1247,31 @@ export function renderPropertiesPanelTemplate(ctx) {
1282
1247
  ? html`<div class="empty-state">
1283
1248
  No attributes declared. Set "attribute" on a state entry.
1284
1249
  </div>`
1285
- : entries.map(
1286
- ([key, d]) => html`
1250
+ : entries.map(([key, d]) => {
1251
+ const def = d as Record<string, unknown>;
1252
+ return html`
1287
1253
  <div
1288
1254
  style="display:flex;gap:6px;align-items:center;padding:2px 0;font-size:11px"
1289
1255
  >
1290
1256
  <code style="font-family:monospace;color:var(--accent)"
1291
- >${d.attribute}</code
1257
+ >${def.attribute}</code
1292
1258
  >
1293
1259
  <span style="color:var(--fg-dim)"> → </span>
1294
1260
  <span>${key}</span>
1295
- ${d.type
1261
+ ${def.type
1296
1262
  ? html`<span style="margin-left:auto;color:var(--fg-dim);font-size:10px"
1297
- >${d.type}</span
1263
+ >${def.type}</span
1298
1264
  >`
1299
1265
  : nothing}
1300
- ${d.reflects
1266
+ ${def.reflects
1301
1267
  ? html`<span
1302
1268
  style="font-size:9px;background:var(--bg-hover);padding:1px 4px;border-radius:3px"
1303
1269
  >reflects</span
1304
1270
  >`
1305
1271
  : nothing}
1306
1272
  </div>
1307
- `,
1308
- )}
1273
+ `;
1274
+ })}
1309
1275
  </div>
1310
1276
  </sp-accordion-item>
1311
1277
  `;
@@ -1327,7 +1293,7 @@ export function renderPropertiesPanelTemplate(ctx) {
1327
1293
  .map((sec) => {
1328
1294
  const sectionAttrs = attrSections[sec.key];
1329
1295
  const hasAnySet = sectionAttrs.some(
1330
- (/** @type {{ name: string; entry: HtmlMetaEntry }} */ a) => attrs[a.name] !== undefined,
1296
+ (a: { name: string; entry: HtmlMetaEntry }) => attrs[a.name] !== undefined,
1331
1297
  );
1332
1298
  return html`
1333
1299
  <sp-accordion-item
@@ -1339,7 +1305,7 @@ export function renderPropertiesPanelTemplate(ctx) {
1339
1305
  ? html`<span slot="heading" class="set-dot set-dot--section"></span>`
1340
1306
  : nothing}
1341
1307
  <div class="style-section-body">
1342
- ${sectionAttrs.map((/** @type {{ name: string; entry: HtmlMetaEntry }} */ a) =>
1308
+ ${sectionAttrs.map((a: { name: string; entry: HtmlMetaEntry }) =>
1343
1309
  renderAttrRow(a.name, a.entry, attrs[a.name]),
1344
1310
  )}
1345
1311
  </div>