@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
@@ -6,8 +6,6 @@ import { getNodeAtPath, parentElementPath, childIndex, pathsEqual, isAncestor }
6
6
  *
7
7
  * @typedef {import("../state.js").JxPath} JxPath
8
8
  *
9
- * @typedef {import("../state.js").JxNode} JxNode
10
- *
11
9
  * @typedef {string | number | boolean | object | null | undefined} JsonValue
12
10
  */
13
11
 
@@ -19,11 +17,12 @@ const HISTORY_LIMIT = 100;
19
17
  * Apply a document mutation transactionally: push to history and mark dirty. The mutationFn
20
18
  * receives the tab and should mutate tab.doc.document in place.
21
19
  *
22
- * @param {Tab} tab
20
+ * @param {Tab | null} tab
23
21
  * @param {(tab: Tab) => void} mutationFn
24
22
  * @param {{ skipHistory?: boolean }} [opts]
25
23
  */
26
24
  export function transactDoc(tab, mutationFn, { skipHistory = false } = {}) {
25
+ if (!tab) return;
27
26
  mutationFn(tab);
28
27
 
29
28
  // Replace the document root reference so effects tracking tab.doc.document re-trigger.
@@ -49,8 +48,8 @@ export function transactDoc(tab, mutationFn, { skipHistory = false } = {}) {
49
48
  /**
50
49
  * Convenience: transact with a mutation fn that receives the document directly.
51
50
  *
52
- * @param {Tab} tab
53
- * @param {(doc: JxNode) => void} fn
51
+ * @param {Tab | null} tab
52
+ * @param {(doc: JxMutableNode) => void} fn
54
53
  * @param {{ skipHistory?: boolean }} [opts]
55
54
  */
56
55
  export function transact(tab, fn, opts) {
@@ -85,7 +84,7 @@ export function redo(tab) {
85
84
  * @param {Tab} tab
86
85
  * @param {JxPath} parentPath
87
86
  * @param {number} index
88
- * @param {JxNode} nodeDef
87
+ * @param {JxMutableNode} nodeDef
89
88
  */
90
89
  export function mutateInsertNode(tab, parentPath, index, nodeDef) {
91
90
  const parent = getNodeAtPath(tab.doc.document, parentPath);
@@ -101,7 +100,10 @@ export function mutateRemoveNode(tab, path) {
101
100
  if (!path || path.length < 2) return;
102
101
  const elemPath = /** @type {JxPath} */ (parentElementPath(path));
103
102
  const idx = /** @type {number} */ (childIndex(path));
104
- getNodeAtPath(tab.doc.document, elemPath).children.splice(idx, 1);
103
+ /** @type {JxMutableNode[]} */ (getNodeAtPath(tab.doc.document, elemPath).children).splice(
104
+ idx,
105
+ 1,
106
+ );
105
107
  if (tab.session.selection && isAncestor(path, tab.session.selection)) {
106
108
  tab.session.selection = null;
107
109
  }
@@ -118,7 +120,11 @@ export function mutateDuplicateNode(tab, path) {
118
120
  const elemPath = /** @type {JxPath} */ (parentElementPath(path));
119
121
  const idx = /** @type {number} */ (childIndex(path));
120
122
  const clone = structuredClone(toRaw(node));
121
- getNodeAtPath(tab.doc.document, elemPath).children.splice(idx + 1, 0, clone);
123
+ /** @type {JxMutableNode[]} */ (getNodeAtPath(tab.doc.document, elemPath).children).splice(
124
+ idx + 1,
125
+ 0,
126
+ clone,
127
+ );
122
128
  tab.session.selection = [...elemPath, "children", idx + 1];
123
129
  }
124
130
 
@@ -134,7 +140,11 @@ export function mutateWrapNode(tab, path, wrapperTag = "div") {
134
140
  const elemPath = /** @type {JxPath} */ (parentElementPath(path));
135
141
  const idx = /** @type {number} */ (childIndex(path));
136
142
  const wrapper = { tagName: wrapperTag, children: [structuredClone(toRaw(node))] };
137
- getNodeAtPath(tab.doc.document, elemPath).children.splice(idx, 1, wrapper);
143
+ /** @type {JxMutableNode[]} */ (getNodeAtPath(tab.doc.document, elemPath).children).splice(
144
+ idx,
145
+ 1,
146
+ wrapper,
147
+ );
138
148
  tab.session.selection = [...elemPath, "children", idx];
139
149
  }
140
150
 
@@ -149,7 +159,7 @@ export function mutateMoveNode(tab, fromPath, toParentPath, toIndex) {
149
159
  const fromParentPath = /** @type {JxPath} */ (parentElementPath(fromPath));
150
160
  const fromIdx = /** @type {number} */ (childIndex(fromPath));
151
161
  const fromParent = getNodeAtPath(doc, fromParentPath);
152
- const [node] = fromParent.children.splice(fromIdx, 1);
162
+ const [node] = /** @type {JxMutableNode[]} */ (fromParent.children).splice(fromIdx, 1);
153
163
  const toParent = getNodeAtPath(doc, toParentPath);
154
164
  if (!toParent.children) toParent.children = [];
155
165
  let adjustedIndex = toIndex;
@@ -351,7 +361,7 @@ export function mutateUpdateMediaNestedStylePath(tab, path, mediaName, stylePath
351
361
  /**
352
362
  * @param {Tab} tab
353
363
  * @param {JxPath} path
354
- * @param {Record<string, any> | undefined} style
364
+ * @param {Record<string, string | undefined> | undefined} style
355
365
  */
356
366
  export function mutateReplaceStyle(tab, path, style) {
357
367
  const node = getNodeAtPath(tab.doc.document, path);
@@ -388,7 +398,7 @@ export function mutateRemoveDef(tab, name) {
388
398
  /**
389
399
  * @param {Tab} tab
390
400
  * @param {string} name
391
- * @param {Record<string, any>} updates
401
+ * @param {Record<string, JsonValue>} updates
392
402
  */
393
403
  export function mutateUpdateDef(tab, name, updates) {
394
404
  const doc = tab.doc.document;
@@ -452,7 +462,7 @@ export function mutateUpdateProp(tab, path, propName, value) {
452
462
  * @param {Tab} tab
453
463
  * @param {JxPath} path
454
464
  * @param {string} caseName
455
- * @param {JxNode} [caseDef]
465
+ * @param {JxMutableNode} [caseDef]
456
466
  */
457
467
  export function mutateAddSwitchCase(tab, path, caseName, caseDef) {
458
468
  const node = getNodeAtPath(tab.doc.document, path);
@@ -13,35 +13,37 @@ import icons from "./icons.js";
13
13
  /**
14
14
  * Render a button group widget with optional overflow menu.
15
15
  *
16
- * @param {any} entry — css-meta entry with $buttonValues, enum, $icons
16
+ * @param {Record<string, unknown>} entry — css-meta entry with $buttonValues, enum, $icons
17
17
  * @param {string} prop — property key (for menu ID namespace)
18
- * @param {any} value — current value
18
+ * @param {string | number | undefined} value — current value
19
19
  * @param {(val: string) => void} onChange — commit callback
20
- * @returns {any}
20
+ * @returns {import("lit-html").TemplateResult}
21
21
  */
22
22
  export function renderButtonGroup(
23
- /** @type {any} */ entry,
24
- /** @type {any} */ prop,
25
- /** @type {any} */ value,
26
- /** @type {any} */ onChange,
23
+ /** @type {Record<string, unknown>} */ entry,
24
+ /** @type {string} */ prop,
25
+ /** @type {string | number | undefined} */ value,
26
+ /** @type {(val: string) => void} */ onChange,
27
27
  ) {
28
- const values = entry.$buttonValues || entry.enum || [];
29
- /** @type {Record<string, any>} */
30
- const iconMap = entry.$icons || {};
28
+ const values = /** @type {string[]} */ (entry.$buttonValues || entry.enum || []);
29
+ /** @type {Record<string, string>} */
30
+ const iconMap = /** @type {Record<string, string>} */ (entry.$icons || {});
31
+ const buttonValues = /** @type {string[] | undefined} */ (entry.$buttonValues);
32
+ const enumValues = /** @type {string[] | undefined} */ (entry.enum);
31
33
  const extra =
32
- entry.$buttonValues && entry.enum && entry.enum.length > entry.$buttonValues.length
33
- ? entry.enum.filter((/** @type {any} */ v) => !entry.$buttonValues.includes(v))
34
+ buttonValues && enumValues && enumValues.length > buttonValues.length
35
+ ? enumValues.filter((/** @type {string} */ v) => !buttonValues.includes(v))
34
36
  : [];
35
37
 
36
38
  const menuId = `style-btngrp-${prop}`;
37
39
  const hasExtra = extra.length > 0;
38
- const extraSelected = hasExtra && extra.includes(value);
40
+ const extraSelected = hasExtra && extra.includes(/** @type {string} */ (value));
39
41
 
40
42
  return html`
41
43
  <div class=${classMap({ "button-group-combo": true, "has-overflow": hasExtra })}>
42
44
  <sp-action-group size="s" compact>
43
45
  ${values.map(
44
- (/** @type {any} */ v) => html`
46
+ (/** @type {string} */ v) => html`
45
47
  <sp-action-button
46
48
  size="s"
47
49
  value=${v}
@@ -49,12 +51,9 @@ export function renderButtonGroup(
49
51
  ?selected=${v === value}
50
52
  @click=${() => onChange(v === value ? "" : v)}
51
53
  >
52
- ${
53
- /** @type {any} */ (iconMap)[v] &&
54
- /** @type {any} */ (icons)[/** @type {any} */ (iconMap)[v]]
55
- ? /** @type {any} */ (icons)[/** @type {any} */ (iconMap)[v]]
56
- : abbreviateValue(v)
57
- }
54
+ ${iconMap[v] && /** @type {Record<string, unknown>} */ (icons)[iconMap[v]]
55
+ ? /** @type {Record<string, unknown>} */ (icons)[iconMap[v]]
56
+ : abbreviateValue(v)}
58
57
  </sp-action-button>
59
58
  `,
60
59
  )}
@@ -69,15 +68,16 @@ export function renderButtonGroup(
69
68
  <sp-overlay trigger="${menuId}@click" placement="bottom-end" type="auto">
70
69
  <sp-popover>
71
70
  <sp-menu
72
- @change=${(/** @type {any} */ e) => {
73
- if (e.target.value) onChange(e.target.value);
71
+ @change=${(/** @type {Event} */ e) => {
72
+ if (/** @type {HTMLInputElement} */ (e.target).value)
73
+ onChange(/** @type {HTMLInputElement} */ (e.target).value);
74
74
  }}
75
75
  >
76
76
  <sp-menu-item value="__none__">—</sp-menu-item>
77
- ${extra.map((/** @type {any} */ v) => {
77
+ ${extra.map((/** @type {string} */ v) => {
78
78
  const label = v.includes("-")
79
79
  ? kebabToLabel(v)
80
- : v.replace(/^./, (/** @type {any} */ c) => c.toUpperCase());
80
+ : v.replace(/^./, (/** @type {string} */ c) => c.toUpperCase());
81
81
  return html`<sp-menu-item value=${v} ?selected=${v === value}
82
82
  >${label}</sp-menu-item
83
83
  >`;
@@ -42,19 +42,20 @@ function varToLabel(/** @type {string} */ name) {
42
42
  }
43
43
 
44
44
  /** Resolve a color value for display — if it's a var() reference, look up the actual color. */
45
- function resolveColorForDisplay(/** @type {any} */ val) {
45
+ function resolveColorForDisplay(/** @type {string | number | undefined} */ val) {
46
46
  if (!val) return "transparent";
47
- const m = val.match(/^var\((--[^)]+)\)$/);
47
+ const s = String(val);
48
+ const m = s.match(/^var\((--[^)]+)\)$/);
48
49
  if (m) {
49
50
  const style = getEffectiveStyle(activeTab.value?.doc.document?.style);
50
51
  const resolved = style?.[m[1]];
51
52
  if (typeof resolved === "string") return resolved;
52
53
  return "transparent";
53
54
  }
54
- return val;
55
+ return s;
55
56
  }
56
57
 
57
- function safeColor(/** @type {any} */ val) {
58
+ function safeColor(/** @type {string | number | undefined} */ val) {
58
59
  if (!val) return "transparent";
59
60
  return resolveColorForDisplay(val);
60
61
  }
@@ -69,7 +70,7 @@ function normalizeHex(/** @type {string} */ c) {
69
70
  /**
70
71
  * Check if a value is a var() reference that matches a defined color variable.
71
72
  *
72
- * @param {any} value
73
+ * @param {string | number | undefined} value
73
74
  * @param {{ name: string; value: string }[]} colorVars
74
75
  */
75
76
  function matchesColorVar(value, colorVars) {
@@ -102,7 +103,7 @@ export class JxColorPopover extends LitElement {
102
103
  return this;
103
104
  }
104
105
 
105
- /** @param {Map<string, any>} changed */
106
+ /** @param {Map<string, unknown>} changed */
106
107
  willUpdate(changed) {
107
108
  if (changed.has("color")) {
108
109
  const raw = resolveColorForDisplay(this.color);
@@ -116,29 +117,33 @@ export class JxColorPopover extends LitElement {
116
117
  }
117
118
  }
118
119
 
119
- _handleArea(/** @type {any} */ e) {
120
- const color = normalizeHex(String(e.target.color));
120
+ _handleArea(/** @type {Event} */ e) {
121
+ const color = normalizeHex(
122
+ String(/** @type {HTMLElement & { color: string }} */ (e.target).color),
123
+ );
121
124
  this.displayColor = color;
122
125
  this.color = color;
123
126
  this.dispatchEvent(new CustomEvent("color-change", { detail: color, bubbles: true }));
124
127
  }
125
128
 
126
- _handleSlider(/** @type {any} */ e) {
127
- const color = normalizeHex(String(e.target.color));
129
+ _handleSlider(/** @type {Event} */ e) {
130
+ const color = normalizeHex(
131
+ String(/** @type {HTMLElement & { color: string }} */ (e.target).color),
132
+ );
128
133
  this.displayColor = color;
129
134
  this.color = color;
130
135
  this.dispatchEvent(new CustomEvent("color-change", { detail: color, bubbles: true }));
131
136
  }
132
137
 
133
- _handleText(/** @type {any} */ e) {
134
- const val = e.target.value.trim();
138
+ _handleText(/** @type {Event} */ e) {
139
+ const val = /** @type {HTMLInputElement} */ (e.target).value.trim();
135
140
  if (!val) return;
136
141
  this.displayColor = val;
137
142
  this.color = val;
138
143
  this.dispatchEvent(new CustomEvent("color-change", { detail: val, bubbles: true }));
139
144
  }
140
145
 
141
- _handleSwatch(/** @type {any} */ e, /** @type {string} */ varName) {
146
+ _handleSwatch(/** @type {Event} */ e, /** @type {string} */ varName) {
142
147
  e.stopPropagation();
143
148
  const varRef = `var(${varName})`;
144
149
  this.color = varRef;
@@ -176,7 +181,7 @@ export class JxColorPopover extends LitElement {
176
181
  color=${cv.value}
177
182
  .value=${cv.name}
178
183
  title=${cv.name}
179
- @click=${(/** @type {any} */ e) => this._handleSwatch(e, cv.name)}
184
+ @click=${(/** @type {Event} */ e) => this._handleSwatch(e, cv.name)}
180
185
  ></sp-swatch>
181
186
  `,
182
187
  )}
@@ -198,14 +203,14 @@ export class JxColorPopover extends LitElement {
198
203
  * title-cased label with swatch (e.g. "Primary Blue").
199
204
  *
200
205
  * @param {string} prop — property key (for debounce namespace)
201
- * @param {any} value — current color value
206
+ * @param {string | number | undefined} value — current color value
202
207
  * @param {(color: string) => void} onChange — commit callback
203
- * @returns {any}
208
+ * @returns {import("lit-html").TemplateResult}
204
209
  */
205
210
  export function renderColorSelector(
206
- /** @type {any} */ prop,
207
- /** @type {any} */ value,
208
- /** @type {any} */ onChange,
211
+ /** @type {string} */ prop,
212
+ /** @type {string | number | undefined} */ value,
213
+ /** @type {(color: string) => void} */ onChange,
209
214
  ) {
210
215
  const colorVars = getColorVars();
211
216
  const matchedVar = matchesColorVar(value, colorVars);
@@ -237,9 +242,9 @@ export function renderColorSelector(
237
242
  size="s"
238
243
  style="flex:1; min-width:0"
239
244
  .value=${`var(${matchedVar.name})`}
240
- @change=${(/** @type {any} */ e) => {
245
+ @change=${(/** @type {Event} */ e) => {
241
246
  e.stopPropagation();
242
- onChange(e.target.value);
247
+ onChange(/** @type {HTMLInputElement} */ (e.target).value);
243
248
  }}
244
249
  >
245
250
  ${colorVars.map(
@@ -275,8 +280,8 @@ export function renderColorSelector(
275
280
  style="flex:1; min-width:0"
276
281
  .value=${live(value || "")}
277
282
  @click=${(/** @type {Event} */ e) => e.stopPropagation()}
278
- @input=${debouncedStyleCommit(`color:${prop}`, 400, (/** @type {any} */ e) => {
279
- onChange(e.target.value.trim());
283
+ @input=${debouncedStyleCommit(`color:${prop}`, 400, (/** @type {Event} */ e) => {
284
+ onChange(/** @type {HTMLInputElement} */ (e.target).value.trim());
280
285
  })}
281
286
  ></sp-textfield>
282
287
  <sp-overlay trigger="${triggerId}@click" placement="bottom-start" type="auto">
@@ -16,11 +16,11 @@ import { classMap } from "lit-html/directives/class-map.js";
16
16
  * label: string;
17
17
  * hasValue: boolean;
18
18
  * onClear?: () => void;
19
- * widget: any;
19
+ * widget: unknown;
20
20
  * span?: number;
21
21
  * warning?: boolean;
22
22
  * }} opts
23
- * @returns {any}
23
+ * @returns {import("lit-html").TemplateResult}
24
24
  */
25
25
  export function renderFieldRow({ prop, label, hasValue, onClear, widget, span, warning }) {
26
26
  return html`
@@ -34,7 +34,7 @@ export function renderFieldRow({ prop, label, hasValue, onClear, widget, span, w
34
34
  ? html`<span
35
35
  class="set-dot"
36
36
  title="Clear ${prop}"
37
- @click=${(/** @type {any} */ e) => {
37
+ @click=${(/** @type {Event} */ e) => {
38
38
  e.stopPropagation();
39
39
  onClear();
40
40
  }}
package/src/ui/icons.js CHANGED
@@ -5,7 +5,7 @@
5
5
  import { html } from "lit";
6
6
 
7
7
  // Helper for custom filled-rect icons (alignment/justify diagrams) where no Spectrum match exists
8
- const _R = (/** @type {any} */ d) =>
8
+ const _R = (/** @type {import("lit").TemplateResult} */ d) =>
9
9
  html`<svg
10
10
  xmlns="http://www.w3.org/2000/svg"
11
11
  width="16"
@@ -18,7 +18,7 @@ const _R = (/** @type {any} */ d) =>
18
18
  </svg>`;
19
19
 
20
20
  // Helper for custom stroke icons
21
- const _S = (/** @type {any} */ d) =>
21
+ const _S = (/** @type {import("lit").TemplateResult} */ d) =>
22
22
  html`<svg
23
23
  xmlns="http://www.w3.org/2000/svg"
24
24
  width="16"
@@ -94,9 +94,9 @@ export function invalidateMediaCache() {
94
94
  * Render the media picker widget for src-type attributes.
95
95
  *
96
96
  * @param {string} prop — attribute name (e.g. "src")
97
- * @param {any} value — current attribute value
98
- * @param {(val: any) => void} onCommit — commit callback
99
- * @returns {any}
97
+ * @param {string} value — current attribute value
98
+ * @param {(val: string) => void} onCommit — commit callback
99
+ * @returns {import("lit-html").TemplateResult}
100
100
  */
101
101
  export function renderMediaPicker(prop, value, onCommit) {
102
102
  // Kick off async load (won't block render)
@@ -127,8 +127,8 @@ export function renderMediaPicker(prop, value, onCommit) {
127
127
  size="s"
128
128
  placeholder="/image.jpg"
129
129
  .value=${live(currentValue)}
130
- @input=${debouncedStyleCommit(`media:${prop}`, 400, (/** @type {any} */ e) =>
131
- onCommit(e.target.value),
130
+ @input=${debouncedStyleCommit(`media:${prop}`, 400, (/** @type {Event} */ e) =>
131
+ onCommit(/** @type {HTMLInputElement} */ (e.target).value),
132
132
  )}
133
133
  @focus=${() => loadMediaCache()}
134
134
  ></sp-textfield>
@@ -140,8 +140,8 @@ export function renderMediaPicker(prop, value, onCommit) {
140
140
  </sp-action-button>
141
141
  <sp-popover slot="click-content" class="media-picker-popover">
142
142
  <sp-menu
143
- @change=${(/** @type {any} */ e) => {
144
- onCommit(e.target.value);
143
+ @change=${(/** @type {Event} */ e) => {
144
+ onCommit(/** @type {HTMLInputElement} */ (e.target).value);
145
145
  }}
146
146
  >
147
147
  ${options.map(
@@ -90,6 +90,7 @@ import { IconSettings } from "@spectrum-web-components/icons-workflow/src/elemen
90
90
  import { IconBack } from "@spectrum-web-components/icons-workflow/src/elements/IconBack.js";
91
91
  import { IconProperties } from "@spectrum-web-components/icons-workflow/src/elements/IconProperties.js";
92
92
  import { IconEvent } from "@spectrum-web-components/icons-workflow/src/elements/IconEvent.js";
93
+ import { IconMore } from "@spectrum-web-components/icons-workflow/src/elements/IconMore.js";
93
94
 
94
95
  // Layout / alignment icons
95
96
  import { IconArrowRight } from "@spectrum-web-components/icons-workflow/src/elements/IconArrowRight.js";
@@ -122,6 +123,7 @@ import { IconBox } from "@spectrum-web-components/icons-workflow/src/elements/Ic
122
123
  import { IconVisibility } from "@spectrum-web-components/icons-workflow/src/elements/IconVisibility.js";
123
124
  import { IconVisibilityOff } from "@spectrum-web-components/icons-workflow/src/elements/IconVisibilityOff.js";
124
125
  import { IconArtboard } from "@spectrum-web-components/icons-workflow/src/elements/IconArtboard.js";
126
+ import { IconChat } from "@spectrum-web-components/icons-workflow/src/elements/IconChat.js";
125
127
  import { IconViewList } from "@spectrum-web-components/icons-workflow/src/elements/IconViewList.js";
126
128
  import { IconRailRightClose } from "@spectrum-web-components/icons-workflow/src/elements/IconRailRightClose.js";
127
129
  import { IconRailRightOpen } from "@spectrum-web-components/icons-workflow/src/elements/IconRailRightOpen.js";
@@ -228,6 +230,7 @@ const components = [
228
230
  ["sp-icon-back", IconBack],
229
231
  ["sp-icon-properties", IconProperties],
230
232
  ["sp-icon-event", IconEvent],
233
+ ["sp-icon-more", IconMore],
231
234
  ["sp-icon-arrow-right", IconArrowRight],
232
235
  ["sp-icon-arrow-left", IconArrowLeft],
233
236
  ["sp-icon-arrow-down", IconArrowDown],
@@ -263,6 +266,7 @@ const components = [
263
266
  ["sp-icon-visibility", IconVisibility],
264
267
  ["sp-icon-visibility-off", IconVisibilityOff],
265
268
  ["sp-icon-artboard", IconArtboard],
269
+ ["sp-icon-chat", IconChat],
266
270
  ["sp-icon-view-list", IconViewList],
267
271
  ["sp-icon-text-bold", IconTextBold],
268
272
  ["sp-icon-text-italic", IconTextItalic],
@@ -279,7 +283,7 @@ const components = [
279
283
  ];
280
284
 
281
285
  for (const [tag, ctor] of /** @type {[string, CustomElementConstructor][]} */ (components)) {
282
- if (!customElements.get(tag)) defineElement(tag, /** @type {any} */ (ctor));
286
+ if (!customElements.get(tag)) defineElement(tag, /** @type {CustomElementConstructor} */ (ctor));
283
287
  }
284
288
 
285
289
  // Register theme fragments (these are also side-effect-only in the original modules)
@@ -15,25 +15,26 @@ export const UNIT_RE = /^(-?[\d.]+)(px|rem|em|%|vw|vh|svw|svh|dvh|ms|s|fr|ch|ex|
15
15
  /**
16
16
  * Render a number + unit selector widget.
17
17
  *
18
- * @param {any} entry — css-meta entry with $units and $keywords arrays
18
+ * @param {Record<string, unknown>} entry — css-meta entry with $units and $keywords arrays
19
19
  * @param {string} prop — property key (for debounce namespace)
20
- * @param {any} value — current value (e.g. "12px", "auto", "")
20
+ * @param {string | number | undefined} value — current value (e.g. "12px", "auto", "")
21
21
  * @param {(val: string) => void} onChange — commit callback
22
- * @returns {any}
22
+ * @param {string} [placeholder]
23
+ * @returns {import("lit-html").TemplateResult}
23
24
  */
24
25
  export function renderUnitSelector(
25
- /** @type {any} */ entry,
26
- /** @type {any} */ prop,
27
- /** @type {any} */ value,
28
- /** @type {any} */ onChange,
26
+ /** @type {Record<string, unknown>} */ entry,
27
+ /** @type {string} */ prop,
28
+ /** @type {string | number | undefined} */ value,
29
+ /** @type {(val: string) => void} */ onChange,
29
30
  /** @type {string} */ placeholder = "",
30
31
  ) {
31
- const units = entry.$units || [];
32
- const keywords = entry.$keywords || [];
32
+ const units = /** @type {string[]} */ (entry.$units || []);
33
+ const keywords = /** @type {string[]} */ (entry.$keywords || []);
33
34
  const strVal = String(value ?? "");
34
35
  const match = strVal.match(UNIT_RE);
35
36
  const isKeyword = !match && strVal !== "" && keywords.includes(strVal);
36
- const isNumericVal = (/** @type {any} */ v) => /^-?\d*\.?\d*$/.test(v);
37
+ const isNumericVal = (/** @type {string} */ v) => /^-?\d*\.?\d*$/.test(v);
37
38
 
38
39
  const currentUnit = isKeyword ? units[0] || "" : match ? match[2] || "" : units[0] || "";
39
40
  let displayValue;
@@ -55,8 +56,8 @@ export function renderUnitSelector(
55
56
  size="s"
56
57
  placeholder=${placeholder || "0"}
57
58
  .value=${live(displayValue)}
58
- @input=${debouncedStyleCommit(`nui:${prop}`, 400, (/** @type {any} */ e) => {
59
- const val = (e.target.value ?? "").trim();
59
+ @input=${debouncedStyleCommit(`nui:${prop}`, 400, (/** @type {Event} */ e) => {
60
+ const val = /** @type {HTMLInputElement} */ (e.target).value.trim();
60
61
  if (val === "") {
61
62
  onChange("");
62
63
  return;
@@ -74,8 +75,8 @@ export function renderUnitSelector(
74
75
  <sp-popover style="min-width: var(--spectrum-component-width-900, 64px)">
75
76
  <sp-menu
76
77
  label="CSS unit"
77
- @change=${(/** @type {any} */ e) => {
78
- const chosen = e.target.value;
78
+ @change=${(/** @type {Event} */ e) => {
79
+ const chosen = /** @type {HTMLInputElement} */ (e.target).value;
79
80
  if (keywords.includes(chosen)) {
80
81
  onChange(chosen);
81
82
  } else if (units.includes(chosen)) {
@@ -86,13 +87,14 @@ export function renderUnitSelector(
86
87
  }}
87
88
  >
88
89
  ${units.map(
89
- (/** @type {any} */ u) => html`<sp-menu-item value=${u}>${u}</sp-menu-item>`,
90
+ (/** @type {string} */ u) =>
91
+ html`<sp-menu-item value=${u}>${u}</sp-menu-item>`,
90
92
  )}
91
93
  ${keywords.length > 0 && units.length > 0
92
94
  ? html`<sp-menu-divider></sp-menu-divider>`
93
95
  : nothing}
94
96
  ${keywords.map(
95
- (/** @type {any} */ kw) =>
97
+ (/** @type {string} */ kw) =>
96
98
  html`<sp-menu-item value=${kw}>${kw}</sp-menu-item>`,
97
99
  )}
98
100
  </sp-menu>
@@ -47,21 +47,27 @@ export class JxValueSelector extends LitElement {
47
47
  get _isPicker() {
48
48
  return (
49
49
  !!this.value &&
50
- this.options.some((/** @type {any} */ o) => !o.divider && o.value === this.value)
50
+ this.options.some(
51
+ (/** @type {ComboOption} */ o) => !("divider" in o) && o.value === this.value,
52
+ )
51
53
  );
52
54
  }
53
55
 
54
56
  /** Get the selected option's style string for the picker button preview */
55
57
  get _selectedStyle() {
56
58
  if (!this._isPicker) return "";
57
- const opt = this.options.find((/** @type {any} */ o) => !o.divider && o.value === this.value);
58
- return /** @type {any} */ (opt)?.style || "";
59
+ const opt = this.options.find(
60
+ (/** @type {ComboOption} */ o) => !("divider" in o) && o.value === this.value,
61
+ );
62
+ return (
63
+ /** @type {{ value: string; label: string; style?: string } | undefined} */ (opt)?.style || ""
64
+ );
59
65
  }
60
66
 
61
67
  /** Render menu items from options array */
62
68
  _renderMenuItems() {
63
- return this.options.map((/** @type {any} */ opt) =>
64
- opt.divider
69
+ return this.options.map((/** @type {ComboOption} */ opt) =>
70
+ "divider" in opt
65
71
  ? html`<sp-menu-divider></sp-menu-divider>`
66
72
  : html`<sp-menu-item value=${opt.value} style=${opt.style || ""}
67
73
  >${opt.label}</sp-menu-item
@@ -70,33 +76,33 @@ export class JxValueSelector extends LitElement {
70
76
  }
71
77
 
72
78
  /** Picker mode: sp-picker @change handler */
73
- _handlePickerChange(/** @type {any} */ e) {
79
+ _handlePickerChange(/** @type {Event} */ e) {
74
80
  e.stopPropagation();
75
- this.value = e.target.value;
81
+ this.value = /** @type {HTMLInputElement} */ (e.target).value;
76
82
  this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
77
83
  }
78
84
 
79
85
  /** Combobox mode: sp-menu @change handler */
80
- _handleMenuChange(/** @type {any} */ e) {
86
+ _handleMenuChange(/** @type {Event} */ e) {
81
87
  e.stopPropagation();
82
- if (!e.target.value) return;
83
- this.value = e.target.value;
88
+ if (!(/** @type {HTMLInputElement} */ (e.target).value)) return;
89
+ this.value = /** @type {HTMLInputElement} */ (e.target).value;
84
90
  this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
85
91
  }
86
92
 
87
93
  /** Combobox mode: textfield @input handler */
88
- _handleInput(/** @type {any} */ e) {
94
+ _handleInput(/** @type {Event} */ e) {
89
95
  e.stopPropagation();
90
- this.value = e.target.value;
96
+ this.value = /** @type {HTMLInputElement} */ (e.target).value;
91
97
  this.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
92
98
  }
93
99
 
94
100
  /** Set popover min-width to match trigger width (replicates sp-picker behavior) */
95
- _setPopoverWidth(/** @type {any} */ e) {
101
+ _setPopoverWidth(/** @type {Event} */ e) {
96
102
  const group = this.querySelector(".jx-combobox-group");
97
103
  const w = group ? /** @type {HTMLElement} */ (group).offsetWidth : 0;
98
- const popover = e.target.querySelector("sp-popover");
99
- if (popover && w) popover.style.minWidth = `${w}px`;
104
+ const popover = /** @type {HTMLElement} */ (e.target).querySelector("sp-popover");
105
+ if (popover && w) /** @type {HTMLElement} */ (popover).style.minWidth = `${w}px`;
100
106
  }
101
107
 
102
108
  render() {