@jxsuite/studio 0.19.0 → 0.20.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 (93) hide show
  1. package/dist/studio.css +98 -98
  2. package/dist/studio.js +12577 -12183
  3. package/dist/studio.js.map +146 -144
  4. package/dist/workers/editor.worker.js +79 -79
  5. package/dist/workers/json.worker.js +109 -109
  6. package/dist/workers/ts.worker.js +82 -82
  7. package/package.json +17 -6
  8. package/src/browse/browse.js +25 -19
  9. package/src/canvas/canvas-diff.js +24 -16
  10. package/src/canvas/canvas-helpers.js +24 -19
  11. package/src/canvas/canvas-live-render.js +94 -39
  12. package/src/canvas/canvas-render.js +119 -73
  13. package/src/canvas/canvas-utils.js +38 -15
  14. package/src/canvas/nested-site-style.js +50 -0
  15. package/src/editor/component-inline-edit.js +14 -13
  16. package/src/editor/content-inline-edit.js +22 -10
  17. package/src/editor/context-menu.js +1 -1
  18. package/src/editor/convert-to-component.js +12 -10
  19. package/src/editor/inline-edit.js +66 -28
  20. package/src/editor/insertion-helper.js +16 -12
  21. package/src/editor/shortcuts.js +4 -2
  22. package/src/editor/slash-menu.js +6 -4
  23. package/src/files/components.js +1 -1
  24. package/src/files/file-ops.js +37 -16
  25. package/src/files/files.js +78 -52
  26. package/src/github/github-auth.js +122 -0
  27. package/src/github/github-publish.js +115 -0
  28. package/src/markdown/md-convert.js +172 -107
  29. package/src/new-project/new-project-modal.js +204 -0
  30. package/src/panels/activity-bar.js +22 -20
  31. package/src/panels/ai-panel.js +399 -0
  32. package/src/panels/block-action-bar.js +5 -5
  33. package/src/panels/canvas-dnd.js +8 -2
  34. package/src/panels/data-explorer.js +19 -13
  35. package/src/panels/dnd.js +144 -92
  36. package/src/panels/editors.js +55 -35
  37. package/src/panels/elements-panel.js +33 -19
  38. package/src/panels/events-panel.js +15 -10
  39. package/src/panels/git-panel.js +469 -98
  40. package/src/panels/head-panel.js +121 -81
  41. package/src/panels/imports-panel.js +88 -64
  42. package/src/panels/layers-panel.js +64 -43
  43. package/src/panels/left-panel.js +85 -47
  44. package/src/panels/overlays.js +1 -0
  45. package/src/panels/panel-events.js +45 -30
  46. package/src/panels/preview-render.js +23 -18
  47. package/src/panels/properties-panel.js +365 -249
  48. package/src/panels/pseudo-preview.js +2 -2
  49. package/src/panels/quick-search.js +7 -5
  50. package/src/panels/right-panel.js +26 -2
  51. package/src/panels/shared.js +4 -4
  52. package/src/panels/signals-panel.js +295 -166
  53. package/src/panels/statusbar.js +4 -4
  54. package/src/panels/style-inputs.js +67 -35
  55. package/src/panels/style-panel.js +233 -132
  56. package/src/panels/style-utils.js +34 -16
  57. package/src/panels/stylebook-layers-panel.js +12 -10
  58. package/src/panels/stylebook-panel.js +134 -66
  59. package/src/panels/toolbar.js +333 -109
  60. package/src/panels/welcome-screen.js +121 -0
  61. package/src/platform.js +2 -4
  62. package/src/platforms/devserver.js +113 -7
  63. package/src/resize-edges.js +98 -0
  64. package/src/services/cem-export.js +12 -7
  65. package/src/services/code-services.js +30 -12
  66. package/src/settings/content-types-editor.js +7 -7
  67. package/src/settings/css-vars-editor.js +30 -24
  68. package/src/settings/defs-editor.js +12 -7
  69. package/src/settings/general-settings.js +85 -3
  70. package/src/settings/head-editor.js +30 -24
  71. package/src/settings/schema-field-ui.js +58 -39
  72. package/src/site-context.js +41 -31
  73. package/src/state.js +70 -29
  74. package/src/store.js +21 -26
  75. package/src/studio.js +103 -81
  76. package/src/tabs/tab.js +67 -43
  77. package/src/tabs/transact.js +23 -13
  78. package/src/ui/button-group.js +24 -24
  79. package/src/ui/color-selector.js +28 -23
  80. package/src/ui/field-row.js +3 -3
  81. package/src/ui/icons.js +2 -2
  82. package/src/ui/media-picker.js +7 -7
  83. package/src/ui/spectrum.js +5 -1
  84. package/src/ui/unit-selector.js +18 -16
  85. package/src/ui/value-selector.js +21 -15
  86. package/src/ui/widgets.js +22 -19
  87. package/src/utils/canvas-media.js +5 -4
  88. package/src/utils/edit-display.js +31 -20
  89. package/src/utils/google-fonts.js +11 -11
  90. package/src/utils/inherited-style.js +9 -8
  91. package/src/utils/studio-utils.js +7 -7
  92. package/src/view.js +58 -1
  93. package/src/workspace/workspace.js +10 -7
@@ -42,6 +42,16 @@ import {
42
42
  } from "./style-utils.js";
43
43
  import { widgetForType } from "./style-inputs.js";
44
44
 
45
+ /**
46
+ * @typedef {{ name: string; entry: Record<string, unknown> }} CssLonghand
47
+ *
48
+ * @typedef {Record<string, unknown>} CssPropertyEntry
49
+ *
50
+ * @typedef {import("../tabs/tab.js").Tab} Tab
51
+ *
52
+ * @typedef {import("../state.js").JxPath} JxPath
53
+ */
54
+
45
55
  /**
46
56
  * Check if a selector is a stylebook tag path (e.g., "table" or "table th"). Tag paths don't start
47
57
  * with selector prefixes (`:`, `.`, `&`, `[`, `@`).
@@ -56,31 +66,32 @@ function isTagPath(selector) {
56
66
  /**
57
67
  * Resolve a style object by traversing a nested tag path. e.g., "table th" → style["table"]["th"]
58
68
  *
59
- * @param {Record<string, any>} style
69
+ * @param {Record<string, unknown>} style
60
70
  * @param {string} tagPath
61
- * @returns {Record<string, any>}
71
+ * @returns {Record<string, unknown>}
62
72
  */
63
73
  function resolveNestedTagStyle(style, tagPath) {
64
74
  const parts = tagPath.split(" ");
75
+ /** @type {unknown} */
65
76
  let obj = style;
66
77
  for (const part of parts) {
67
78
  if (!obj || typeof obj !== "object") return {};
68
- obj = obj[part];
79
+ obj = /** @type {Record<string, unknown>} */ (obj)[part];
69
80
  }
70
- return obj && typeof obj === "object" ? obj : {};
81
+ return obj && typeof obj === "object" ? /** @type {Record<string, unknown>} */ (obj) : {};
71
82
  }
72
83
 
73
84
  // ─── Row renderers ──────────────────────────────────────────────────────────
74
85
 
75
86
  function renderStyleRow(
76
- /** @type {any} */ entry,
77
- /** @type {any} */ prop,
78
- /** @type {any} */ value,
79
- /** @type {any} */ onCommit,
80
- /** @type {any} */ onDelete,
81
- /** @type {any} */ isWarning,
82
- /** @type {any} */ gridMode,
83
- /** @type {any} */ inheritedValue,
87
+ /** @type {CssPropertyEntry} */ entry,
88
+ /** @type {string} */ prop,
89
+ /** @type {string} */ value,
90
+ /** @type {(v: string | undefined) => void} */ onCommit,
91
+ /** @type {() => void} */ onDelete,
92
+ /** @type {boolean} */ isWarning,
93
+ /** @type {boolean} */ gridMode,
94
+ /** @type {string | undefined} */ inheritedValue,
84
95
  ) {
85
96
  const type = inferInputType(entry);
86
97
  const hasVal = value !== undefined && value !== "";
@@ -91,23 +102,30 @@ function renderStyleRow(
91
102
  hasValue: hasVal,
92
103
  onClear: onDelete,
93
104
  widget: widgetForType(type, entry, prop, value, onCommit, { placeholder }),
94
- span: gridMode && entry.$span === 2 ? 2 : undefined,
105
+ span: gridMode && /** @type {Record<string, unknown>} */ (entry).$span === 2 ? 2 : undefined,
95
106
  warning: isWarning,
96
107
  });
97
108
  }
98
109
 
99
110
  /**
100
- * @param {any} shortProp @param {any} entry @param {any} style @param {any} mutateFn
101
- * @param {any} _deleteFn @param {Record<string, any>} inherited
111
+ * @param {string} shortProp
112
+ * @param {CssPropertyEntry} entry
113
+ * @param {Record<string, unknown>} style
114
+ * @param {(t: Tab, prop: string, val: string | Record<string, unknown> | undefined) => void} mutateFn
115
+ * @param {() => void} _deleteFn
116
+ * @param {Record<string, string | number>} inherited
102
117
  */
103
118
  function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inherited = {}) {
104
119
  const tab = activeTab.value;
105
- const longhands = getLonghands(shortProp);
120
+ const longhands = /** @type {CssLonghand[]} */ (getLonghands(shortProp));
106
121
  const shortVal = style[shortProp];
107
- const hasLonghands = longhands.some((/** @type {any} */ l) => style[l.name] !== undefined);
122
+ const hasLonghands = longhands.some(
123
+ (/** @type {CssLonghand} */ l) => style[l.name] !== undefined,
124
+ );
108
125
  const isExpanded = tab.session.ui.styleShorthands[shortProp] ?? hasLonghands;
109
126
  const hasAnyVal =
110
- shortVal !== undefined || longhands.some((/** @type {any} */ l) => style[l.name] !== undefined);
127
+ shortVal !== undefined ||
128
+ longhands.some((/** @type {CssLonghand} */ l) => style[l.name] !== undefined);
111
129
 
112
130
  return html`
113
131
  <div class="style-row" data-prop=${shortProp}>
@@ -116,7 +134,7 @@ function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inheri
116
134
  ? html`<span
117
135
  class="set-dot"
118
136
  title="Clear ${shortProp}"
119
- @click=${(/** @type {any} */ e) => {
137
+ @click=${(/** @type {Event} */ e) => {
120
138
  e.stopPropagation();
121
139
  transactDoc(activeTab.value, (t) => {
122
140
  if (shortVal !== undefined) mutateFn(t, shortProp, undefined);
@@ -134,25 +152,27 @@ function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inheri
134
152
  size="s"
135
153
  .value=${live(shortVal || "")}
136
154
  placeholder=${!shortVal && hasLonghands
137
- ? longhands.map((/** @type {any} */ l) => style[l.name] || "0").join(" ")
155
+ ? longhands.map((/** @type {CssLonghand} */ l) => style[l.name] || "0").join(" ")
138
156
  : !shortVal && inherited[shortProp]
139
157
  ? inherited[shortProp]
140
- : !shortVal && longhands.some((/** @type {any} */ l) => inherited[l.name])
141
- ? longhands.map((/** @type {any} */ l) => inherited[l.name] || "0").join(" ")
158
+ : !shortVal && longhands.some((/** @type {CssLonghand} */ l) => inherited[l.name])
159
+ ? longhands
160
+ .map((/** @type {CssLonghand} */ l) => inherited[l.name] || "0")
161
+ .join(" ")
142
162
  : ""}
143
- @input=${debouncedStyleCommit(`short:${shortProp}`, 400, (/** @type {any} */ e) => {
163
+ @input=${debouncedStyleCommit(`short:${shortProp}`, 400, (/** @type {Event} */ e) => {
144
164
  transactDoc(activeTab.value, (t) => {
145
165
  for (const l of longhands) {
146
166
  if (style[l.name] !== undefined) mutateFn(t, l.name, undefined);
147
167
  }
148
- mutateFn(t, shortProp, e.target.value || undefined);
168
+ mutateFn(t, shortProp, /** @type {HTMLInputElement} */ (e.target).value || undefined);
149
169
  });
150
170
  })}
151
171
  ></sp-textfield>
152
172
  <sp-action-button
153
173
  size="xs"
154
174
  quiet
155
- @click=${(/** @type {any} */ e) => {
175
+ @click=${(/** @type {Event} */ e) => {
156
176
  e.stopPropagation();
157
177
  activeTab.value.session.ui.styleShorthands = {
158
178
  ...activeTab.value.session.ui.styleShorthands,
@@ -168,16 +188,17 @@ function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inheri
168
188
  </div>
169
189
  ${isExpanded
170
190
  ? (() => {
171
- const isBorderSide = entry.$shorthandType === "border-side";
191
+ const isBorderSide = /** @type {Record<string, unknown>} */ (entry).$shorthandType ===
192
+ "border-side";
172
193
  const expanded = shortVal
173
194
  ? isBorderSide
174
- ? expandBorderSide(shortVal)
175
- : expandShorthand(shortVal, longhands.length)
195
+ ? expandBorderSide(/** @type {string} */ (shortVal))
196
+ : expandShorthand(/** @type {string} */ (shortVal), longhands.length)
176
197
  : null;
177
198
  const compress = isBorderSide ? compressBorderSide : compressShorthand;
178
199
  const emptyVal = isBorderSide ? "" : "0";
179
200
  return longhands.map(
180
- (/** @type {any} */ { name, entry: lEntry }, /** @type {any} */ idx) => {
201
+ (/** @type {CssLonghand} */ { name, entry: lEntry }, /** @type {number} */ idx) => {
181
202
  const lVal = style[name] ?? (expanded ? expanded[idx] : "");
182
203
  return html`
183
204
  <div class="style-row style-row--child" data-prop=${name}>
@@ -186,10 +207,10 @@ function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inheri
186
207
  ? html`<span
187
208
  class="set-dot"
188
209
  title="Clear ${name}"
189
- @click=${(/** @type {any} */ e) => {
210
+ @click=${(/** @type {Event} */ e) => {
190
211
  e.stopPropagation();
191
212
  const vals = longhands.map(
192
- (/** @type {any} */ l, /** @type {any} */ i) =>
213
+ (/** @type {CssLonghand} */ l, /** @type {number} */ i) =>
193
214
  i === idx
194
215
  ? emptyVal
195
216
  : (style[l.name] ?? (expanded ? expanded[i] : emptyVal)),
@@ -198,7 +219,7 @@ function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inheri
198
219
  for (const l of longhands) {
199
220
  if (style[l.name] !== undefined) mutateFn(t, l.name, undefined);
200
221
  }
201
- mutateFn(t, shortProp, compress(vals));
222
+ mutateFn(t, shortProp, compress(/** @type {string[]} */ (vals)));
202
223
  });
203
224
  }}
204
225
  ></span>`
@@ -212,17 +233,18 @@ function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inheri
212
233
  lEntry,
213
234
  name,
214
235
  lVal,
215
- (/** @type {any} */ newVal) => {
216
- const vals = longhands.map((/** @type {any} */ l, /** @type {any} */ i) =>
217
- i === idx
218
- ? newVal || emptyVal
219
- : (style[l.name] ?? (expanded ? expanded[i] : emptyVal)),
236
+ (/** @type {string} */ newVal) => {
237
+ const vals = longhands.map(
238
+ (/** @type {CssLonghand} */ l, /** @type {number} */ i) =>
239
+ i === idx
240
+ ? newVal || emptyVal
241
+ : (style[l.name] ?? (expanded ? expanded[i] : emptyVal)),
220
242
  );
221
243
  transactDoc(activeTab.value, (t) => {
222
244
  for (const l of longhands) {
223
245
  if (style[l.name] !== undefined) mutateFn(t, l.name, undefined);
224
246
  }
225
- mutateFn(t, shortProp, compress(vals));
247
+ mutateFn(t, shortProp, compress(/** @type {string[]} */ (vals)));
226
248
  });
227
249
  },
228
250
  { placeholder: !lVal && inherited[name] ? String(inherited[name]) : "" },
@@ -239,14 +261,14 @@ function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inheri
239
261
  // ─── Main template ──────────────────────────────────────────────────────────
240
262
 
241
263
  /**
242
- * @param {any} node
243
- * @param {any} activeMediaTab
244
- * @param {any} activeSelector
245
- * @param {Record<string, any>} [effectiveStyle]
264
+ * @param {JxMutableNode} node
265
+ * @param {string | null} activeMediaTab
266
+ * @param {string | null} activeSelector
267
+ * @param {Record<string, unknown>} [effectiveStyle]
246
268
  */
247
269
  function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveStyle) {
248
270
  const tab = activeTab.value;
249
- const sel = /** @type {import("../state.js").JxPath} */ (tab.session.selection);
271
+ const sel = /** @type {JxPath} */ (tab.session.selection);
250
272
  const style = effectiveStyle || node.style || {};
251
273
  const { sizeBreakpoints } = parseMediaEntries(getEffectiveMedia(tab.doc.document.$media));
252
274
  const mediaNames = sizeBreakpoints.map((bp) => bp.name);
@@ -259,8 +281,8 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
259
281
  <sp-tabs
260
282
  size="s"
261
283
  selected=${mediaTab || "base"}
262
- @change=${(/** @type {any} */ e) => {
263
- const val = e.target.selected;
284
+ @change=${(/** @type {Event} */ e) => {
285
+ const val = /** @type {HTMLElement & { selected: string }} */ (e.target).selected;
264
286
  const newMedia = val === "base" ? null : val;
265
287
  if (newMedia !== tab.session.ui.activeMedia) {
266
288
  tab.session.ui.activeMedia = newMedia;
@@ -276,7 +298,9 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
276
298
  : nothing;
277
299
 
278
300
  // ── Selector dropdown ──────────────────────────────────────────────────────
279
- const contextStyle = mediaTab ? style[`@${mediaTab}`] || {} : style;
301
+ const contextStyle = mediaTab
302
+ ? /** @type {Record<string, unknown>} */ (style[`@${mediaTab}`]) || {}
303
+ : style;
280
304
  const existingSelectors = Object.keys(contextStyle).filter(isNestedSelector);
281
305
  const existingSet = new Set(existingSelectors);
282
306
  const commonSet = new Set(COMMON_SELECTORS);
@@ -292,14 +316,15 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
292
316
  class="selector-select"
293
317
  quiet
294
318
  .value=${live(_selectorVal)}
295
- @change=${(/** @type {any} */ e) => {
296
- const val = e.target.value;
319
+ @change=${(/** @type {Event} */ e) => {
320
+ const val = /** @type {HTMLElement & { value: string }} */ (e.target).value;
297
321
  if (val === "__add_custom__") {
298
322
  requestAnimationFrame(() => {
299
- e.target.value = activeSelector || "__base__";
323
+ /** @type {HTMLElement & { value: string }} */ (e.target).value =
324
+ activeSelector || "__base__";
300
325
  });
301
- const picker = e.target;
302
- const bar = picker.closest(".style-toolbar");
326
+ const picker = /** @type {HTMLElement} */ (e.target);
327
+ const bar = /** @type {HTMLElement} */ (picker.closest(".style-toolbar"));
303
328
  picker.style.display = "none";
304
329
  const inp = document.createElement("input");
305
330
  inp.type = "text";
@@ -308,7 +333,7 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
308
333
  bar.appendChild(inp);
309
334
  inp.focus();
310
335
  let done = false;
311
- const finish = (/** @type {any} */ accept) => {
336
+ const finish = (/** @type {boolean} */ accept) => {
312
337
  if (done) return;
313
338
  done = true;
314
339
  const v = inp.value.trim();
@@ -363,8 +388,8 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
363
388
  class="style-filter-input"
364
389
  placeholder="Filter properties…"
365
390
  .value=${live(tab.session.ui.styleFilter || "")}
366
- @input=${(/** @type {any} */ e) => {
367
- activeTab.value.session.ui.styleFilter = e.target.value;
391
+ @input=${(/** @type {Event} */ e) => {
392
+ activeTab.value.session.ui.styleFilter = /** @type {HTMLInputElement} */ (e.target).value;
368
393
  }}
369
394
  ></sp-textfield>
370
395
  <sp-action-button
@@ -382,61 +407,119 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
382
407
  `;
383
408
 
384
409
  // ── Determine the active style object ──────────────────────────────────────
385
- /** @type {Record<string, any>} */
410
+ /** @type {Record<string, unknown>} */
386
411
  let activeStyle;
387
- /** @type {(prop: any, val: any) => void} */
412
+ /** @type {(prop: string, val: string | Record<string, unknown> | undefined) => void} */
388
413
  let commitStyle;
389
- /** @type {(t: any, prop: any, val: any) => void} */
414
+ /** @type {(t: Tab, prop: string, val: string | Record<string, unknown> | undefined) => void} */
390
415
  let commitMutate;
391
416
  if (activeSelector && isTagPath(activeSelector) && mediaTab && mediaNames.length > 0) {
392
- const mediaObj = style[`@${mediaTab}`] || {};
417
+ const mediaObj = /** @type {Record<string, unknown>} */ (style[`@${mediaTab}`]) || {};
393
418
  activeStyle = resolveNestedTagStyle(mediaObj, activeSelector);
394
419
  const stylePath = activeSelector.split(" ");
395
- commitMutate = (/** @type {any} */ t, /** @type {any} */ prop, /** @type {any} */ val) =>
396
- mutateUpdateMediaNestedStylePath(t, sel, mediaTab, stylePath, prop, val);
397
- commitStyle = (/** @type {any} */ prop, /** @type {any} */ val) =>
398
- transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
420
+ commitMutate = (
421
+ /** @type {Tab} */ t,
422
+ /** @type {string} */ prop,
423
+ /** @type {string | Record<string, unknown> | undefined} */ val,
424
+ ) =>
425
+ mutateUpdateMediaNestedStylePath(
426
+ t,
427
+ sel,
428
+ mediaTab,
429
+ stylePath,
430
+ prop,
431
+ /** @type {string | undefined} */ (val),
432
+ );
433
+ commitStyle = (
434
+ /** @type {string} */ prop,
435
+ /** @type {string | Record<string, unknown> | undefined} */ val,
436
+ ) => transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
399
437
  } else if (activeSelector && isTagPath(activeSelector)) {
400
438
  activeStyle = resolveNestedTagStyle(style, activeSelector);
401
439
  const stylePath = activeSelector.split(" ");
402
- commitMutate = (/** @type {any} */ t, /** @type {any} */ prop, /** @type {any} */ val) =>
403
- mutateUpdateNestedStylePath(t, sel, stylePath, prop, val);
404
- commitStyle = (/** @type {any} */ prop, /** @type {any} */ val) =>
405
- transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
440
+ commitMutate = (
441
+ /** @type {Tab} */ t,
442
+ /** @type {string} */ prop,
443
+ /** @type {string | Record<string, unknown> | undefined} */ val,
444
+ ) =>
445
+ mutateUpdateNestedStylePath(t, sel, stylePath, prop, /** @type {string | undefined} */ (val));
446
+ commitStyle = (
447
+ /** @type {string} */ prop,
448
+ /** @type {string | Record<string, unknown> | undefined} */ val,
449
+ ) => transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
406
450
  } else if (activeSelector && mediaTab && mediaNames.length > 0) {
407
- activeStyle = (style[`@${mediaTab}`] || {})[activeSelector] || {};
408
- commitMutate = (/** @type {any} */ t, /** @type {any} */ prop, /** @type {any} */ val) =>
409
- mutateUpdateMediaNestedStyle(t, sel, mediaTab, activeSelector, prop, val);
410
- commitStyle = (/** @type {any} */ prop, /** @type {any} */ val) =>
411
- transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
451
+ activeStyle = /** @type {Record<string, unknown>} */ (
452
+ /** @type {Record<string, unknown>} */ (style[`@${mediaTab}`] || {})[activeSelector]
453
+ ) || {};
454
+ commitMutate = (
455
+ /** @type {Tab} */ t,
456
+ /** @type {string} */ prop,
457
+ /** @type {string | Record<string, unknown> | undefined} */ val,
458
+ ) =>
459
+ mutateUpdateMediaNestedStyle(
460
+ t,
461
+ sel,
462
+ mediaTab,
463
+ activeSelector,
464
+ prop,
465
+ /** @type {string | undefined} */ (val),
466
+ );
467
+ commitStyle = (
468
+ /** @type {string} */ prop,
469
+ /** @type {string | Record<string, unknown> | undefined} */ val,
470
+ ) => transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
412
471
  } else if (activeSelector) {
413
- activeStyle = style[activeSelector] || {};
414
- commitMutate = (/** @type {any} */ t, /** @type {any} */ prop, /** @type {any} */ val) =>
415
- mutateUpdateNestedStyle(t, sel, activeSelector, prop, val);
416
- commitStyle = (/** @type {any} */ prop, /** @type {any} */ val) =>
417
- transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
472
+ activeStyle = /** @type {Record<string, unknown>} */ (style[activeSelector]) || {};
473
+ commitMutate = (
474
+ /** @type {Tab} */ t,
475
+ /** @type {string} */ prop,
476
+ /** @type {string | Record<string, unknown> | undefined} */ val,
477
+ ) =>
478
+ mutateUpdateNestedStyle(
479
+ t,
480
+ sel,
481
+ activeSelector,
482
+ prop,
483
+ /** @type {string | undefined} */ (val),
484
+ );
485
+ commitStyle = (
486
+ /** @type {string} */ prop,
487
+ /** @type {string | Record<string, unknown> | undefined} */ val,
488
+ ) => transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
418
489
  } else if (mediaTab !== null && mediaNames.length > 0) {
419
490
  activeStyle = {};
420
- for (const [p, v] of Object.entries(style[`@${mediaTab}`] || {})) {
491
+ for (const [p, v] of Object.entries(
492
+ /** @type {Record<string, unknown>} */ (style[`@${mediaTab}`]) || {},
493
+ )) {
421
494
  if (typeof v !== "object") activeStyle[p] = v;
422
495
  }
423
- commitMutate = (/** @type {any} */ t, /** @type {any} */ prop, /** @type {any} */ val) =>
424
- mutateUpdateMediaStyle(t, sel, mediaTab, prop, val);
425
- commitStyle = (/** @type {any} */ prop, /** @type {any} */ val) =>
426
- transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
496
+ commitMutate = (
497
+ /** @type {Tab} */ t,
498
+ /** @type {string} */ prop,
499
+ /** @type {string | Record<string, unknown> | undefined} */ val,
500
+ ) => mutateUpdateMediaStyle(t, sel, mediaTab, prop, /** @type {string | undefined} */ (val));
501
+ commitStyle = (
502
+ /** @type {string} */ prop,
503
+ /** @type {string | Record<string, unknown> | undefined} */ val,
504
+ ) => transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
427
505
  } else {
428
506
  activeStyle = {};
429
507
  for (const [p, v] of Object.entries(style)) {
430
508
  if (typeof v !== "object") activeStyle[p] = v;
431
509
  }
432
- commitMutate = (/** @type {any} */ t, /** @type {any} */ prop, /** @type {any} */ val) =>
433
- mutateUpdateStyle(t, sel, prop, val);
434
- commitStyle = (/** @type {any} */ prop, /** @type {any} */ val) =>
435
- transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
510
+ commitMutate = (
511
+ /** @type {Tab} */ t,
512
+ /** @type {string} */ prop,
513
+ /** @type {string | Record<string, unknown> | undefined} */ val,
514
+ ) => mutateUpdateStyle(t, sel, prop, /** @type {string | undefined} */ (val));
515
+ commitStyle = (
516
+ /** @type {string} */ prop,
517
+ /** @type {string | Record<string, unknown> | undefined} */ val,
518
+ ) => transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
436
519
  }
437
520
 
438
521
  // ── Compute inherited style from higher breakpoints ──────────────────────
439
- /** @type {Record<string, any>} */
522
+ /** @type {Record<string, string | number>} */
440
523
  const inheritedStyle = computeInheritedStyle(style, mediaNames, mediaTab, activeSelector);
441
524
 
442
525
  // Auto-open sections that have properties
@@ -446,23 +529,32 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
446
529
  }
447
530
 
448
531
  // Partition properties into sections
449
- const sectionProps = /** @type {Record<string, any[]>} */ ({});
532
+ const sectionProps =
533
+ /** @type {Record<string, { prop: string; entry: CssPropertyEntry }[]>} */ ({});
450
534
  for (const sec of cssMeta.$sections) sectionProps[sec.key] = [];
451
535
 
452
- for (const [prop, entry] of /** @type {[string, any][]} */ (Object.entries(cssMeta.$defs))) {
453
- if (typeof entry.$shorthand === "string") continue;
454
- const sec = entry.$section || "other";
536
+ for (const [prop, entry] of /** @type {[string, CssPropertyEntry][]} */ (
537
+ Object.entries(cssMeta.$defs)
538
+ )) {
539
+ if (typeof (/** @type {Record<string, unknown>} */ (entry).$shorthand) === "string") continue;
540
+ const sec = /** @type {string} */ (/** @type {Record<string, unknown>} */ (entry).$section) ||
541
+ "other";
455
542
  sectionProps[sec].push({ prop, entry });
456
543
  }
457
544
  for (const sec of cssMeta.$sections) {
458
545
  sectionProps[sec.key].sort(
459
- (/** @type {any} */ a, /** @type {any} */ b) => a.entry.$order - b.entry.$order,
546
+ (
547
+ /** @type {{ prop: string; entry: CssPropertyEntry }} */ a,
548
+ /** @type {{ prop: string; entry: CssPropertyEntry }} */ b,
549
+ ) =>
550
+ /** @type {number} */ (/** @type {Record<string, unknown>} */ (a.entry).$order) -
551
+ /** @type {number} */ (/** @type {Record<string, unknown>} */ (b.entry).$order),
460
552
  );
461
553
  }
462
554
 
463
555
  const otherProps = [];
464
556
  for (const prop of Object.keys(activeStyle)) {
465
- if (!(/** @type {Record<string, any>} */ (cssMeta.$defs)[prop])) {
557
+ if (!(/** @type {Record<string, unknown>} */ (cssMeta.$defs)[prop])) {
466
558
  const val = activeStyle[prop];
467
559
  if (val !== null && typeof val === "object") continue;
468
560
  otherProps.push(prop);
@@ -488,15 +580,17 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
488
580
  .map((sec) => {
489
581
  const entries = sectionProps[sec.key];
490
582
 
491
- const sectionActiveProps = entries.filter((/** @type {any} */ { prop, entry }) => {
492
- if (activeStyle[prop] !== undefined) return true;
493
- if (inferInputType(entry) === "shorthand") {
494
- return getLonghands(prop).some(
495
- (/** @type {any} */ l) => activeStyle[l.name] !== undefined,
496
- );
497
- }
498
- return false;
499
- });
583
+ const sectionActiveProps = entries.filter(
584
+ (/** @type {{ prop: string; entry: CssPropertyEntry }} */ { prop, entry }) => {
585
+ if (activeStyle[prop] !== undefined) return true;
586
+ if (inferInputType(entry) === "shorthand") {
587
+ return /** @type {CssLonghand[]} */ (getLonghands(prop)).some(
588
+ (/** @type {CssLonghand} */ l) => activeStyle[l.name] !== undefined,
589
+ );
590
+ }
591
+ return false;
592
+ },
593
+ );
500
594
 
501
595
  const rows = [];
502
596
  for (const { prop, entry } of entries) {
@@ -512,17 +606,19 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
512
606
  }
513
607
  if (filterActive) {
514
608
  if (type === "shorthand") {
515
- const longhands = getLonghands(prop);
609
+ const longhands = /** @type {CssLonghand[]} */ (getLonghands(prop));
516
610
  const hasAnySet =
517
- hasVal || longhands.some((/** @type {any} */ l) => activeStyle[l.name] !== undefined);
611
+ hasVal ||
612
+ longhands.some((/** @type {CssLonghand} */ l) => activeStyle[l.name] !== undefined);
518
613
  if (!hasAnySet) continue;
519
614
  } else if (!hasVal) continue;
520
615
  }
521
616
 
522
617
  if (type === "shorthand") {
523
- const longhands = getLonghands(prop);
618
+ const longhands = /** @type {CssLonghand[]} */ (getLonghands(prop));
524
619
  const hasAny =
525
- hasVal || longhands.some((/** @type {any} */ l) => activeStyle[l.name] !== undefined);
620
+ hasVal ||
621
+ longhands.some((/** @type {CssLonghand} */ l) => activeStyle[l.name] !== undefined);
526
622
  if (!hasAny && !condMet) continue;
527
623
  rows.push(
528
624
  renderShorthandRow(prop, entry, activeStyle, commitMutate, () => {}, inheritedStyle),
@@ -534,12 +630,13 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
534
630
  renderStyleRow(
535
631
  entry,
536
632
  prop,
537
- val ?? "",
538
- (/** @type {any} */ newVal) => commitStyle(prop, newVal || undefined),
633
+ /** @type {string} */ (val) ?? "",
634
+ (/** @type {string | undefined} */ newVal) =>
635
+ commitStyle(prop, newVal || undefined),
539
636
  () => commitStyle(prop, undefined),
540
637
  isWarning,
541
638
  sec.$layout === "grid",
542
- inheritedStyle[prop],
639
+ /** @type {string | undefined} */ (inheritedStyle[prop]),
543
640
  ),
544
641
  );
545
642
  }
@@ -553,10 +650,10 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
553
650
  <sp-accordion-item
554
651
  label=${sec.label}
555
652
  .open=${isOpen}
556
- @sp-accordion-item-toggle=${(/** @type {any} */ e) => {
653
+ @sp-accordion-item-toggle=${(/** @type {Event} */ e) => {
557
654
  activeTab.value.session.ui.styleSections = {
558
655
  ...activeTab.value.session.ui.styleSections,
559
- [sec.key]: e.target.open,
656
+ [sec.key]: /** @type {HTMLElement & { open: boolean }} */ (e.target).open,
560
657
  };
561
658
  }}
562
659
  >
@@ -567,14 +664,14 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
567
664
  <span
568
665
  class="set-dot set-dot--section"
569
666
  title="Clear all ${sec.label.toLowerCase()} properties"
570
- @click=${(/** @type {any} */ e) => {
667
+ @click=${(/** @type {Event} */ e) => {
571
668
  e.stopPropagation();
572
669
  e.preventDefault();
573
670
  transactDoc(activeTab.value, (t) => {
574
671
  for (const { prop, entry } of sectionActiveProps) {
575
672
  if (activeStyle[prop] !== undefined) commitMutate(t, prop, undefined);
576
673
  if (inferInputType(entry) === "shorthand") {
577
- for (const l of getLonghands(prop)) {
674
+ for (const l of /** @type {CssLonghand[]} */ (getLonghands(prop))) {
578
675
  if (activeStyle[l.name] !== undefined)
579
676
  commitMutate(t, l.name, undefined);
580
677
  }
@@ -598,10 +695,10 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
598
695
  <sp-accordion-item
599
696
  label="Custom"
600
697
  .open=${customIsOpen}
601
- @sp-accordion-item-toggle=${(/** @type {any} */ e) => {
698
+ @sp-accordion-item-toggle=${(/** @type {Event} */ e) => {
602
699
  activeTab.value.session.ui.styleSections = {
603
700
  ...activeTab.value.session.ui.styleSections,
604
- other: e.target.open,
701
+ other: /** @type {HTMLElement & { open: boolean }} */ (e.target).open,
605
702
  };
606
703
  }}
607
704
  >
@@ -613,8 +710,8 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
613
710
  size="s"
614
711
  class="kv-key"
615
712
  .value=${live(prop)}
616
- @change=${(/** @type {any} */ e) => {
617
- const newProp = e.target.value.trim();
713
+ @change=${(/** @type {Event} */ e) => {
714
+ const newProp = /** @type {HTMLInputElement} */ (e.target).value.trim();
618
715
  if (newProp && newProp !== prop) {
619
716
  transactDoc(activeTab.value, (t) => {
620
717
  commitMutate(t, prop, undefined);
@@ -628,8 +725,8 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
628
725
  class="kv-val"
629
726
  .value=${live(String(activeStyle[prop]))}
630
727
  placeholder=${ifDefined(cssInitialMap.get(prop))}
631
- @input=${debouncedStyleCommit(`custom:${prop}`, 400, (/** @type {any} */ e) => {
632
- commitStyle(prop, e.target.value);
728
+ @input=${debouncedStyleCommit(`custom:${prop}`, 400, (/** @type {Event} */ e) => {
729
+ commitStyle(prop, /** @type {HTMLInputElement} */ (e.target).value);
633
730
  })}
634
731
  ></sp-textfield>
635
732
  <sp-action-button size="xs" quiet @click=${() => commitStyle(prop, undefined)}>
@@ -643,14 +740,14 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
643
740
  size="s"
644
741
  placeholder="Property name…"
645
742
  style="flex:1"
646
- @keydown=${(/** @type {any} */ e) => {
743
+ @keydown=${(/** @type {KeyboardEvent} */ e) => {
647
744
  if (e.key === "Enter") {
648
745
  e.preventDefault();
649
- const prop = e.target.value.trim();
746
+ const prop = /** @type {HTMLInputElement} */ (e.target).value.trim();
650
747
  if (prop) {
651
748
  const initial = cssInitialMap.get(prop) || "";
652
749
  commitStyle(prop, initial || "");
653
- e.target.value = "";
750
+ /** @type {HTMLInputElement} */ (e.target).value = "";
654
751
  }
655
752
  }
656
753
  }}
@@ -668,10 +765,10 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
668
765
  <sp-accordion-item
669
766
  label="Relative Styling"
670
767
  .open=${nestedIsOpen}
671
- @sp-accordion-item-toggle=${(/** @type {any} */ e) => {
768
+ @sp-accordion-item-toggle=${(/** @type {Event} */ e) => {
672
769
  activeTab.value.session.ui.styleSections = {
673
770
  ...activeTab.value.session.ui.styleSections,
674
- nested: e.target.open,
771
+ nested: /** @type {HTMLElement & { open: boolean }} */ (e.target).open,
675
772
  };
676
773
  }}
677
774
  >
@@ -755,17 +852,20 @@ export function renderStylePanelTemplate(ctx) {
755
852
 
756
853
  /** Single property input row (generic field row helper) */
757
854
  export function _fieldRow(
758
- /** @type {any} */ label,
759
- /** @type {any} */ type,
760
- /** @type {any} */ value,
761
- /** @type {any} */ onChange,
762
- /** @type {any} */ _datalistId,
855
+ /** @type {string} */ label,
856
+ /** @type {string} */ type,
857
+ /** @type {string} */ value,
858
+ /** @type {(v: string | boolean) => void} */ onChange,
859
+ /** @type {string | undefined} */ _datalistId,
763
860
  ) {
764
- /** @type {any} */
861
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
765
862
  let debounceTimer;
766
- const onInput = (/** @type {any} */ e) => {
863
+ const onInput = (/** @type {Event} */ e) => {
767
864
  clearTimeout(debounceTimer);
768
- debounceTimer = setTimeout(() => onChange(e.target.value), 400);
865
+ debounceTimer = setTimeout(
866
+ () => onChange(/** @type {HTMLInputElement} */ (e.target).value),
867
+ 400,
868
+ );
769
869
  };
770
870
  const inputTpl =
771
871
  type === "textarea"
@@ -778,7 +878,8 @@ export function _fieldRow(
778
878
  : type === "checkbox"
779
879
  ? html`<sp-checkbox
780
880
  ?checked=${!!value}
781
- @change=${(/** @type {any} */ e) => onChange(e.target.checked)}
881
+ @change=${(/** @type {Event} */ e) =>
882
+ onChange(/** @type {HTMLInputElement} */ (e.target).checked)}
782
883
  ></sp-checkbox>`
783
884
  : html`<sp-textfield size="s" value=${value ?? ""} @input=${onInput}></sp-textfield>`;
784
885
  return html`