@jxsuite/studio 0.23.1 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/studio.js +2231 -715
  2. package/dist/studio.js.map +121 -104
  3. package/package.json +8 -7
  4. package/src/browse/{browse-modal.js → browse-modal.ts} +9 -10
  5. package/src/browse/{browse.js → browse.ts} +110 -85
  6. package/src/canvas/{canvas-diff.js → canvas-diff.ts} +42 -35
  7. package/src/canvas/{canvas-helpers.js → canvas-helpers.ts} +25 -19
  8. package/src/canvas/{canvas-live-render.js → canvas-live-render.ts} +53 -63
  9. package/src/canvas/{canvas-render.js → canvas-render.ts} +110 -121
  10. package/src/canvas/{canvas-utils.js → canvas-utils.ts} +48 -44
  11. package/src/canvas/{nested-site-style.js → nested-site-style.ts} +3 -3
  12. package/src/editor/{component-inline-edit.js → component-inline-edit.ts} +45 -33
  13. package/src/editor/{content-inline-edit.js → content-inline-edit.ts} +27 -45
  14. package/src/editor/{context-menu.js → context-menu.ts} +38 -36
  15. package/src/editor/{convert-targets.js → convert-targets.ts} +15 -12
  16. package/src/editor/{convert-to-component.js → convert-to-component.ts} +25 -26
  17. package/src/editor/{convert-to-repeater.js → convert-to-repeater.ts} +60 -40
  18. package/src/editor/{inline-edit.js → inline-edit.ts} +91 -55
  19. package/src/editor/{inline-format.js → inline-format.ts} +46 -51
  20. package/src/editor/{insertion-helper.js → insertion-helper.ts} +47 -63
  21. package/src/editor/{shortcuts.js → shortcuts.ts} +45 -31
  22. package/src/editor/{slash-menu.js → slash-menu.ts} +34 -35
  23. package/src/files/{components.js → components.ts} +12 -15
  24. package/src/files/{file-ops.js → file-ops.ts} +36 -29
  25. package/src/files/{files.js → files.ts} +102 -84
  26. package/src/github/{github-auth.js → github-auth.ts} +3 -3
  27. package/src/github/{github-publish.js → github-publish.ts} +20 -18
  28. package/src/markdown/{md-allowlist.js → md-allowlist.ts} +5 -2
  29. package/src/markdown/{md-convert.js → md-convert.ts} +146 -169
  30. package/src/new-project/{new-project-modal.js → new-project-modal.ts} +16 -18
  31. package/src/panels/{activity-bar.js → activity-bar.ts} +26 -34
  32. package/src/panels/{ai-panel.js → ai-panel.ts} +30 -30
  33. package/src/panels/{block-action-bar.js → block-action-bar.ts} +64 -73
  34. package/src/panels/{canvas-dnd.js → canvas-dnd.ts} +44 -49
  35. package/src/panels/{data-explorer.js → data-explorer.ts} +21 -15
  36. package/src/panels/dnd.ts +417 -0
  37. package/src/panels/{editors.js → editors.ts} +43 -50
  38. package/src/panels/{elements-panel.js → elements-panel.ts} +30 -27
  39. package/src/panels/{events-panel.js → events-panel.ts} +33 -25
  40. package/src/panels/{git-panel.js → git-panel.ts} +97 -83
  41. package/src/panels/{head-panel.js → head-panel.ts} +117 -139
  42. package/src/panels/{imports-panel.js → imports-panel.ts} +69 -89
  43. package/src/panels/{layers-panel.js → layers-panel.ts} +61 -57
  44. package/src/panels/{left-panel.js → left-panel.ts} +56 -63
  45. package/src/panels/{overlays.js → overlays.ts} +28 -36
  46. package/src/panels/{panel-events.js → panel-events.ts} +53 -51
  47. package/src/panels/{preview-render.js → preview-render.ts} +20 -15
  48. package/src/panels/{properties-panel.js → properties-panel.ts} +212 -246
  49. package/src/panels/{pseudo-preview.js → pseudo-preview.ts} +9 -8
  50. package/src/panels/{quick-search.js → quick-search.ts} +16 -17
  51. package/src/panels/{right-panel.js → right-panel.ts} +41 -59
  52. package/src/panels/{shared.js → shared.ts} +7 -5
  53. package/src/panels/{signals-panel.js → signals-panel.ts} +322 -362
  54. package/src/panels/{statusbar.js → statusbar.ts} +14 -14
  55. package/src/panels/{style-inputs.js → style-inputs.ts} +66 -48
  56. package/src/panels/{style-panel.js → style-panel.ts} +235 -290
  57. package/src/panels/{style-utils.js → style-utils.ts} +32 -31
  58. package/src/panels/{stylebook-layers-panel.js → stylebook-layers-panel.ts} +22 -14
  59. package/src/panels/{stylebook-panel.js → stylebook-panel.ts} +127 -140
  60. package/src/panels/{tab-strip.js → tab-strip.ts} +12 -14
  61. package/src/panels/{toolbar.js → toolbar.ts} +68 -70
  62. package/src/panels/{welcome-screen.js → welcome-screen.ts} +14 -16
  63. package/src/{platform.js → platform.ts} +5 -3
  64. package/src/platforms/{devserver.js → devserver.ts} +53 -44
  65. package/src/{recent-projects.js → recent-projects.ts} +17 -12
  66. package/src/{resize-edges.js → resize-edges.ts} +18 -13
  67. package/src/services/{cem-export.js → cem-export.ts} +14 -7
  68. package/src/services/{code-services.js → code-services.ts} +29 -20
  69. package/src/services/{monaco-setup.js → monaco-setup.ts} +2 -3
  70. package/src/settings/{content-types-editor.js → content-types-editor.ts} +88 -67
  71. package/src/settings/{css-vars-editor.js → css-vars-editor.ts} +78 -51
  72. package/src/settings/{defs-editor.js → defs-editor.ts} +70 -56
  73. package/src/settings/{general-settings.js → general-settings.ts} +29 -28
  74. package/src/settings/{head-editor.js → head-editor.ts} +37 -35
  75. package/src/settings/{schema-field-ui.js → schema-field-ui.ts} +86 -76
  76. package/src/settings/{settings-modal.js → settings-modal.ts} +14 -16
  77. package/src/{site-context.js → site-context.ts} +35 -32
  78. package/src/{state.js → state.ts} +95 -78
  79. package/src/{store.js → store.ts} +60 -63
  80. package/src/{studio.js → studio.ts} +117 -119
  81. package/src/tabs/tab.ts +196 -0
  82. package/src/tabs/{transact.js → transact.ts} +107 -60
  83. package/src/types.ts +185 -0
  84. package/src/ui/{button-group.js → button-group.ts} +21 -21
  85. package/src/ui/{color-selector.js → color-selector.ts} +44 -37
  86. package/src/ui/{expression-editor.js → expression-editor.ts} +65 -40
  87. package/src/ui/{field-row.js → field-row.ts} +19 -2
  88. package/src/ui/{icons.js → icons.ts} +2 -2
  89. package/src/ui/{layers.js → layers.ts} +33 -24
  90. package/src/ui/{media-picker.js → media-picker.ts} +13 -9
  91. package/src/ui/{panel-resize.js → panel-resize.ts} +9 -4
  92. package/src/ui/{spectrum.js → spectrum.ts} +4 -4
  93. package/src/ui/{unit-selector.js → unit-selector.ts} +16 -19
  94. package/src/ui/{value-selector.js → value-selector.ts} +28 -28
  95. package/src/ui/{widgets.js → widgets.ts} +62 -15
  96. package/src/utils/{canvas-media.js → canvas-media.ts} +29 -14
  97. package/src/utils/{edit-display.js → edit-display.ts} +23 -20
  98. package/src/utils/{google-fonts.js → google-fonts.ts} +14 -13
  99. package/src/utils/{inherited-style.js → inherited-style.ts} +15 -10
  100. package/src/utils/{studio-utils.js → studio-utils.ts} +38 -26
  101. package/src/{view.js → view.ts} +53 -55
  102. package/src/workspace/{workspace.js → workspace.ts} +44 -33
  103. package/src/panels/dnd.js +0 -426
  104. package/src/tabs/tab.js +0 -192
  105. /package/src/{reactivity.js → reactivity.ts} +0 -0
@@ -1,3 +1,4 @@
1
+ /// <reference lib="dom" />
1
2
  /**
2
3
  * Color-selector.js — Color input widget with swatch, text field, and popover picker.
3
4
  *
@@ -13,10 +14,15 @@
13
14
  import { LitElement, html, nothing } from "lit";
14
15
  import { html as litHtml } from "lit-html";
15
16
  import { live } from "lit-html/directives/live.js";
16
- import { debouncedStyleCommit } from "../store.js";
17
- import { activeTab } from "../workspace/workspace.js";
18
- import { getEffectiveStyle } from "../site-context.js";
19
- import { kebabToLabel } from "../utils/studio-utils.js";
17
+ import { debouncedStyleCommit } from "../store";
18
+ import { activeTab } from "../workspace/workspace";
19
+ import { getEffectiveStyle } from "../site-context";
20
+ import { kebabToLabel } from "../utils/studio-utils";
21
+
22
+ interface ColorVar {
23
+ name: string;
24
+ value: string;
25
+ }
20
26
 
21
27
  // ─── Helpers ─────────────────────────────────────────────────────────────────
22
28
 
@@ -37,12 +43,12 @@ function getColorVars() {
37
43
  * Convert a color variable name to a title-case label. Strips the "--color-" prefix and converts
38
44
  * kebab to title case. e.g. "--color-primary-blue" → "Primary Blue"
39
45
  */
40
- function varToLabel(/** @type {string} */ name) {
46
+ function varToLabel(name: string) {
41
47
  return kebabToLabel(name.replace(/^--color-?/, ""));
42
48
  }
43
49
 
44
50
  /** Resolve a color value for display — if it's a var() reference, look up the actual color. */
45
- function resolveColorForDisplay(/** @type {string | number | undefined} */ val) {
51
+ function resolveColorForDisplay(val: string | number | undefined) {
46
52
  if (!val) return "transparent";
47
53
  const s = String(val);
48
54
  const m = s.match(/^var\((--[^)]+)\)$/);
@@ -55,13 +61,13 @@ function resolveColorForDisplay(/** @type {string | number | undefined} */ val)
55
61
  return s;
56
62
  }
57
63
 
58
- function safeColor(/** @type {string | number | undefined} */ val) {
64
+ function safeColor(val: string | number | undefined) {
59
65
  if (!val) return "transparent";
60
66
  return resolveColorForDisplay(val);
61
67
  }
62
68
 
63
69
  /** Normalize a color string to include # prefix for hex values. */
64
- function normalizeHex(/** @type {string} */ c) {
70
+ function normalizeHex(c: string) {
65
71
  if (!c) return c;
66
72
  if (c.startsWith("var(") || c.startsWith("rgb") || c.startsWith("hsl")) return c;
67
73
  return c.replace(/^#?/, "#");
@@ -73,7 +79,10 @@ function normalizeHex(/** @type {string} */ c) {
73
79
  * @param {string | number | undefined} value
74
80
  * @param {{ name: string; value: string }[]} colorVars
75
81
  */
76
- function matchesColorVar(value, colorVars) {
82
+ function matchesColorVar(
83
+ value: string | number | undefined,
84
+ colorVars: { name: string; value: string }[],
85
+ ) {
77
86
  if (!value || typeof value !== "string") return null;
78
87
  const m = value.match(/^var\((--[^)]+)\)$/);
79
88
  if (!m) return null;
@@ -82,8 +91,6 @@ function matchesColorVar(value, colorVars) {
82
91
 
83
92
  // ─── JxColorPopover LitElement ──────────────────────────────────────────────
84
93
 
85
- /** @typedef {{ name: string; value: string }} ColorVar */
86
-
87
94
  export class JxColorPopover extends LitElement {
88
95
  static properties = {
89
96
  color: { type: String },
@@ -91,11 +98,15 @@ export class JxColorPopover extends LitElement {
91
98
  colorVars: { attribute: false },
92
99
  };
93
100
 
101
+ declare color: string;
102
+ declare displayColor: string;
103
+ declare colorVars: ColorVar[];
104
+
94
105
  constructor() {
95
106
  super();
96
- /** @type {string} */ this.color = "";
97
- /** @type {string} */ this.displayColor = "#000000";
98
- /** @type {ColorVar[]} */ this.colorVars = [];
107
+ this.color = "";
108
+ this.displayColor = "#000000";
109
+ this.colorVars = [];
99
110
  }
100
111
 
101
112
  /** No shadow DOM — render directly into light DOM for Spectrum theming */
@@ -104,7 +115,7 @@ export class JxColorPopover extends LitElement {
104
115
  }
105
116
 
106
117
  /** @param {Map<string, unknown>} changed */
107
- willUpdate(changed) {
118
+ willUpdate(changed: Map<string, unknown>) {
108
119
  if (changed.has("color")) {
109
120
  const raw = resolveColorForDisplay(this.color);
110
121
  if (!raw || raw === "transparent") {
@@ -117,33 +128,29 @@ export class JxColorPopover extends LitElement {
117
128
  }
118
129
  }
119
130
 
120
- _handleArea(/** @type {Event} */ e) {
121
- const color = normalizeHex(
122
- String(/** @type {HTMLElement & { color: string }} */ (e.target).color),
123
- );
131
+ _handleArea(e: Event) {
132
+ const color = normalizeHex(String((e.target as HTMLElement & { color: string }).color));
124
133
  this.displayColor = color;
125
134
  this.color = color;
126
135
  this.dispatchEvent(new CustomEvent("color-change", { detail: color, bubbles: true }));
127
136
  }
128
137
 
129
- _handleSlider(/** @type {Event} */ e) {
130
- const color = normalizeHex(
131
- String(/** @type {HTMLElement & { color: string }} */ (e.target).color),
132
- );
138
+ _handleSlider(e: Event) {
139
+ const color = normalizeHex(String((e.target as HTMLElement & { color: string }).color));
133
140
  this.displayColor = color;
134
141
  this.color = color;
135
142
  this.dispatchEvent(new CustomEvent("color-change", { detail: color, bubbles: true }));
136
143
  }
137
144
 
138
- _handleText(/** @type {Event} */ e) {
139
- const val = /** @type {HTMLInputElement} */ (e.target).value.trim();
145
+ _handleText(e: Event) {
146
+ const val = (e.target as HTMLInputElement).value.trim();
140
147
  if (!val) return;
141
148
  this.displayColor = val;
142
149
  this.color = val;
143
150
  this.dispatchEvent(new CustomEvent("color-change", { detail: val, bubbles: true }));
144
151
  }
145
152
 
146
- _handleSwatch(/** @type {Event} */ e, /** @type {string} */ varName) {
153
+ _handleSwatch(e: Event, varName: string) {
147
154
  e.stopPropagation();
148
155
  const varRef = `var(${varName})`;
149
156
  this.color = varRef;
@@ -181,7 +188,7 @@ export class JxColorPopover extends LitElement {
181
188
  color=${cv.value}
182
189
  .value=${cv.name}
183
190
  title=${cv.name}
184
- @click=${(/** @type {Event} */ e) => this._handleSwatch(e, cv.name)}
191
+ @click=${(e: Event) => this._handleSwatch(e, cv.name)}
185
192
  ></sp-swatch>
186
193
  `,
187
194
  )}
@@ -208,9 +215,9 @@ export class JxColorPopover extends LitElement {
208
215
  * @returns {import("lit-html").TemplateResult}
209
216
  */
210
217
  export function renderColorSelector(
211
- /** @type {string} */ prop,
212
- /** @type {string | number | undefined} */ value,
213
- /** @type {(color: string) => void} */ onChange,
218
+ /** @type {string} */ prop: string,
219
+ /** @type {string | number | undefined} */ value: string | number | undefined,
220
+ /** @type {(color: string) => void} */ onChange: (color: string) => void,
214
221
  ) {
215
222
  const colorVars = getColorVars();
216
223
  const matchedVar = matchesColorVar(value, colorVars);
@@ -233,7 +240,7 @@ export function renderColorSelector(
233
240
  <jx-color-popover
234
241
  .color=${value || ""}
235
242
  .colorVars=${colorVars}
236
- @color-change=${(/** @type {CustomEvent} */ e) => onChange(e.detail)}
243
+ @color-change=${(e: CustomEvent) => onChange(e.detail)}
237
244
  ></jx-color-popover>
238
245
  </sp-popover>
239
246
  </sp-overlay>
@@ -242,9 +249,9 @@ export function renderColorSelector(
242
249
  size="s"
243
250
  style="flex:1; min-width:0"
244
251
  .value=${`var(${matchedVar.name})`}
245
- @change=${(/** @type {Event} */ e) => {
252
+ @change=${(e: Event) => {
246
253
  e.stopPropagation();
247
- onChange(/** @type {HTMLInputElement} */ (e.target).value);
254
+ onChange((e.target as HTMLInputElement).value);
248
255
  }}
249
256
  >
250
257
  ${colorVars.map(
@@ -279,9 +286,9 @@ export function renderColorSelector(
279
286
  size="s"
280
287
  style="flex:1; min-width:0"
281
288
  .value=${live(value || "")}
282
- @click=${(/** @type {Event} */ e) => e.stopPropagation()}
283
- @input=${debouncedStyleCommit(`color:${prop}`, 400, (/** @type {Event} */ e) => {
284
- onChange(/** @type {HTMLInputElement} */ (e.target).value.trim());
289
+ @click=${(e: Event) => e.stopPropagation()}
290
+ @input=${debouncedStyleCommit(`color:${prop}`, 400, (e: Event) => {
291
+ onChange((e.target as HTMLInputElement).value.trim());
285
292
  })}
286
293
  ></sp-textfield>
287
294
  <sp-overlay trigger="${triggerId}@click" placement="bottom-start" type="auto">
@@ -289,7 +296,7 @@ export function renderColorSelector(
289
296
  <jx-color-popover
290
297
  .color=${value || ""}
291
298
  .colorVars=${colorVars}
292
- @color-change=${(/** @type {CustomEvent} */ e) => onChange(e.detail)}
299
+ @color-change=${(e: CustomEvent) => onChange(e.detail)}
293
300
  ></jx-color-popover>
294
301
  </sp-popover>
295
302
  </sp-overlay>
@@ -1,6 +1,7 @@
1
+ /// <reference lib="dom" />
1
2
  import { html, nothing } from "lit-html";
2
3
  import { live } from "lit-html/directives/live.js";
3
- import { renderFieldRow } from "./field-row.js";
4
+ import { renderFieldRow } from "./field-row";
4
5
 
5
6
  // ─── Operator Categories ────────────────────────────────────────────────────
6
7
 
@@ -44,7 +45,7 @@ const OPERATOR_GROUPS = [
44
45
  * valueIsNode: boolean;
45
46
  * }}
46
47
  */
47
- function operatorInfo(op) {
48
+ function operatorInfo(op: string) {
48
49
  if (UNARY_OPS.has(op))
49
50
  return {
50
51
  needsValue: false,
@@ -124,7 +125,7 @@ function operatorInfo(op) {
124
125
  * @param {any} operand
125
126
  * @returns {"ref" | "expression" | "literal"}
126
127
  */
127
- function operandMode(operand) {
128
+ function operandMode(operand: any) {
128
129
  if (operand && typeof operand === "object") {
129
130
  if ("$ref" in operand) return "ref";
130
131
  if ("operator" in operand) return "expression";
@@ -136,7 +137,7 @@ function operandMode(operand) {
136
137
  * @param {string} mode
137
138
  * @returns {any}
138
139
  */
139
- function defaultForMode(mode) {
140
+ function defaultForMode(mode: string) {
140
141
  if (mode === "ref") return { $ref: "" };
141
142
  if (mode === "expression") return { operator: "!", target: null };
142
143
  return null;
@@ -148,7 +149,7 @@ function defaultForMode(mode) {
148
149
  * @param {any} val
149
150
  * @returns {"string" | "number" | "boolean" | "null"}
150
151
  */
151
- function literalType(val) {
152
+ function literalType(val: any) {
152
153
  if (val === null || val === undefined) return "null";
153
154
  if (typeof val === "boolean") return "boolean";
154
155
  if (typeof val === "number") return "number";
@@ -159,7 +160,7 @@ function literalType(val) {
159
160
  * @param {string} type
160
161
  * @returns {any}
161
162
  */
162
- function defaultForLiteralType(type) {
163
+ function defaultForLiteralType(type: string) {
163
164
  if (type === "number") return 0;
164
165
  if (type === "boolean") return false;
165
166
  if (type === "null") return null;
@@ -172,7 +173,7 @@ function defaultForLiteralType(type) {
172
173
  * @param {any} node
173
174
  * @returns {string}
174
175
  */
175
- export function expressionHint(node) {
176
+ export function expressionHint(node: any) {
176
177
  if (!node || !node.operator) return "$expression";
177
178
  const op = node.operator;
178
179
  const targetLabel = node.target?.$ref
@@ -197,7 +198,11 @@ export function expressionHint(node) {
197
198
  * @param {{ stateDefs: string[]; allowEventRef: boolean }} opts
198
199
  * @returns {import("lit-html").TemplateResult}
199
200
  */
200
- function renderRefPicker(refVal, onRefChange, opts) {
201
+ function renderRefPicker(
202
+ refVal: string,
203
+ onRefChange: (newRef: string) => void,
204
+ opts: { stateDefs: string[]; allowEventRef: boolean },
205
+ ) {
201
206
  const stateRefs = (opts.stateDefs || []).map((k) => `#/state/${k}`);
202
207
  const eventRefs = opts.allowEventRef ? ["event#/detail", "event#/target/value"] : [];
203
208
  const allRefs = [...stateRefs, ...eventRefs];
@@ -209,8 +214,8 @@ function renderRefPicker(refVal, onRefChange, opts) {
209
214
  style="flex:1"
210
215
  placeholder="Select…"
211
216
  .value=${live(isCustom ? "__custom__" : refVal || "")}
212
- @change=${(/** @type {Event} */ e) => {
213
- const val = /** @type {HTMLInputElement} */ (e.target).value;
217
+ @change=${(e: Event) => {
218
+ const val = (e.target as HTMLInputElement).value;
214
219
  if (val === "__custom__") return;
215
220
  onRefChange(val);
216
221
  }}
@@ -237,7 +242,7 @@ function renderRefPicker(refVal, onRefChange, opts) {
237
242
  * @param {(newVal: any) => void} onChange
238
243
  * @returns {import("lit-html").TemplateResult}
239
244
  */
240
- function renderLiteralEditor(operand, onChange) {
245
+ function renderLiteralEditor(operand: any, onChange: (newVal: any) => void) {
241
246
  const type = literalType(operand);
242
247
  return html`
243
248
  <div style="display:flex;gap:4px;align-items:center;flex:1">
@@ -245,8 +250,8 @@ function renderLiteralEditor(operand, onChange) {
245
250
  size="s"
246
251
  style="min-width:56px"
247
252
  .value=${live(type)}
248
- @change=${(/** @type {Event} */ e) => {
249
- const newType = /** @type {HTMLInputElement} */ (e.target).value;
253
+ @change=${(e: Event) => {
254
+ const newType = (e.target as HTMLInputElement).value;
250
255
  onChange(defaultForLiteralType(newType));
251
256
  }}
252
257
  >
@@ -260,23 +265,20 @@ function renderLiteralEditor(operand, onChange) {
260
265
  size="s"
261
266
  style="flex:1"
262
267
  .value=${live(String(operand ?? ""))}
263
- @input=${(/** @type {Event} */ e) =>
264
- onChange(/** @type {HTMLInputElement} */ (e.target).value)}
268
+ @input=${(e: Event) => onChange((e.target as HTMLInputElement).value)}
265
269
  ></sp-textfield>`
266
270
  : type === "number"
267
271
  ? html`<sp-number-field
268
272
  size="s"
269
273
  style="flex:1"
270
274
  .value=${live(Number(operand ?? 0))}
271
- @change=${(/** @type {Event} */ e) =>
272
- onChange(Number(/** @type {HTMLInputElement} */ (e.target).value))}
275
+ @change=${(e: Event) => onChange(Number((e.target as HTMLInputElement).value))}
273
276
  ></sp-number-field>`
274
277
  : type === "boolean"
275
278
  ? html`<sp-checkbox
276
279
  size="s"
277
280
  ?checked=${!!operand}
278
- @change=${(/** @type {Event} */ e) =>
279
- onChange(/** @type {HTMLInputElement} */ (e.target).checked)}
281
+ @change=${(e: Event) => onChange((e.target as HTMLInputElement).checked)}
280
282
  >true</sp-checkbox
281
283
  >`
282
284
  : html`<span style="font-size:12px;color:var(--spectrum-global-color-gray-600)"
@@ -299,9 +301,13 @@ function renderLiteralEditor(operand, onChange) {
299
301
  * }} opts
300
302
  * @returns {import("lit-html").TemplateResult}
301
303
  */
302
- function renderOperandEditor(operand, onChange, opts) {
304
+ function renderOperandEditor(
305
+ operand: unknown,
306
+ onChange: (newOperand: unknown) => void,
307
+ opts: { stateDefs: string[]; allowEventRef: boolean; depth: number; mustBeRef?: boolean },
308
+ ): import("lit-html").TemplateResult {
303
309
  if (opts.mustBeRef) {
304
- const refVal = operand?.$ref ?? "";
310
+ const refVal = ((operand as Record<string, unknown> | null)?.$ref as string) ?? "";
305
311
  return html`
306
312
  <div style="flex:1">${renderRefPicker(refVal, (r) => onChange({ $ref: r }), opts)}</div>
307
313
  `;
@@ -314,8 +320,8 @@ function renderOperandEditor(operand, onChange, opts) {
314
320
  size="s"
315
321
  style="min-width:60px"
316
322
  .value=${live(mode)}
317
- @change=${(/** @type {Event} */ e) => {
318
- const newMode = /** @type {HTMLInputElement} */ (e.target).value;
323
+ @change=${(e: Event) => {
324
+ const newMode = (e.target as HTMLInputElement).value;
319
325
  onChange(defaultForMode(newMode));
320
326
  }}
321
327
  >
@@ -326,7 +332,11 @@ function renderOperandEditor(operand, onChange, opts) {
326
332
  ${mode === "literal"
327
333
  ? renderLiteralEditor(operand, onChange)
328
334
  : mode === "ref"
329
- ? renderRefPicker(operand?.$ref ?? "", (r) => onChange({ $ref: r }), opts)
335
+ ? renderRefPicker(
336
+ ((operand as Record<string, unknown> | null)?.$ref as string) ?? "",
337
+ (r) => onChange({ $ref: r }),
338
+ opts,
339
+ )
330
340
  : renderExpressionEditor(operand, onChange, { ...opts, depth: opts.depth + 1 })}
331
341
  </div>
332
342
  `;
@@ -340,7 +350,11 @@ function renderOperandEditor(operand, onChange, opts) {
340
350
  * @param {{ stateDefs: string[]; allowEventRef: boolean; depth: number }} opts
341
351
  * @returns {import("lit-html").TemplateResult}
342
352
  */
343
- function renderSpliceArgsEditor(args, onChange, opts) {
353
+ function renderSpliceArgsEditor(
354
+ args: unknown[],
355
+ onChange: (newArgs: unknown[]) => void,
356
+ opts: { stateDefs: string[]; allowEventRef: boolean; depth: number },
357
+ ): import("lit-html").TemplateResult {
344
358
  const safeArgs = Array.isArray(args) ? args : [];
345
359
  const labels = ["start", "del", "item"];
346
360
 
@@ -408,10 +422,17 @@ const _operatorMenuCache = OPERATOR_GROUPS.map(
408
422
  * }} opts
409
423
  * @returns {import("lit-html").TemplateResult}
410
424
  */
411
- export function renderExpressionEditor(node, onChange, opts) {
425
+ export function renderExpressionEditor(
426
+ node: unknown,
427
+ onChange: (node: unknown) => void,
428
+ opts: { stateDefs: string[]; allowEventRef: boolean; depth?: number },
429
+ ): import("lit-html").TemplateResult {
412
430
  const depth = opts.depth ?? 0;
413
- const safeNode = node && typeof node === "object" ? node : { operator: "=", target: null };
414
- const op = safeNode.operator || "=";
431
+ const safeNode: Record<string, unknown> =
432
+ node && typeof node === "object"
433
+ ? (node as Record<string, unknown>)
434
+ : { operator: "=", target: null };
435
+ const op = (safeNode.operator as string) || "=";
415
436
  const info = operatorInfo(op);
416
437
 
417
438
  const nestStyle =
@@ -429,19 +450,17 @@ export function renderExpressionEditor(node, onChange, opts) {
429
450
  <sp-picker
430
451
  size="s"
431
452
  .value=${live(op)}
432
- @change=${(/** @type {Event} */ e) => {
433
- const newOp = /** @type {HTMLInputElement} */ (e.target).value;
453
+ @change=${(e: Event) => {
454
+ const newOp = (e.target as HTMLInputElement).value;
434
455
  const newInfo = operatorInfo(newOp);
435
- /** @type {any} */
436
- const updated = { operator: newOp, target: safeNode.target };
456
+ const updated: any = { operator: newOp, target: safeNode.target };
437
457
  if (newInfo.targetMustBeRef && operandMode(safeNode.target) !== "ref") {
438
458
  updated.target = { $ref: "" };
439
459
  }
440
460
  if (newInfo.needsValue) {
441
461
  if (newInfo.valueIsNode) {
442
- updated.value = safeNode.value?.operator
443
- ? safeNode.value
444
- : { operator: "!", target: null };
462
+ const val = safeNode.value as Record<string, unknown> | null;
463
+ updated.value = val?.operator ? safeNode.value : { operator: "!", target: null };
445
464
  } else if (newInfo.spliceArray) {
446
465
  updated.value = Array.isArray(safeNode.value) ? safeNode.value : [null];
447
466
  } else {
@@ -488,7 +507,9 @@ export function renderExpressionEditor(node, onChange, opts) {
488
507
  widget: nothing,
489
508
  })}
490
509
  ${renderExpressionEditor(
491
- safeNode.value?.operator ? safeNode.value : { operator: "!", target: null },
510
+ (safeNode.value as Record<string, unknown> | null)?.operator
511
+ ? safeNode.value
512
+ : { operator: "!", target: null },
492
513
  (v) => onChange({ ...safeNode, value: v }),
493
514
  { ...opts, depth: depth + 1 },
494
515
  )}
@@ -504,10 +525,14 @@ export function renderExpressionEditor(node, onChange, opts) {
504
525
  hasValue: false,
505
526
  widget: nothing,
506
527
  })}
507
- ${renderSpliceArgsEditor(safeNode.value, (v) => onChange({ ...safeNode, value: v }), {
508
- ...opts,
509
- depth,
510
- })}
528
+ ${renderSpliceArgsEditor(
529
+ safeNode.value as unknown[],
530
+ (v) => onChange({ ...safeNode, value: v }),
531
+ {
532
+ ...opts,
533
+ depth,
534
+ },
535
+ )}
511
536
  </div>
512
537
  `
513
538
  : nothing}
@@ -1,3 +1,4 @@
1
+ /// <reference lib="dom" />
1
2
  /**
2
3
  * Field-row.js — Universal field row layout for all Studio panels.
3
4
  *
@@ -22,7 +23,23 @@ import { classMap } from "lit-html/directives/class-map.js";
22
23
  * }} opts
23
24
  * @returns {import("lit-html").TemplateResult}
24
25
  */
25
- export function renderFieldRow({ prop, label, hasValue, onClear, widget, span, warning }) {
26
+ export function renderFieldRow({
27
+ prop,
28
+ label,
29
+ hasValue,
30
+ onClear,
31
+ widget,
32
+ span,
33
+ warning,
34
+ }: {
35
+ prop: string;
36
+ label: string;
37
+ hasValue: boolean;
38
+ onClear?: () => void;
39
+ widget: unknown;
40
+ span?: number;
41
+ warning?: boolean;
42
+ }) {
26
43
  return html`
27
44
  <div
28
45
  class=${classMap({ "style-row": true, "style-row--warning": !!warning })}
@@ -34,7 +51,7 @@ export function renderFieldRow({ prop, label, hasValue, onClear, widget, span, w
34
51
  ? html`<span
35
52
  class="set-dot"
36
53
  title="Clear ${prop}"
37
- @click=${(/** @type {Event} */ e) => {
54
+ @click=${(e: Event) => {
38
55
  e.stopPropagation();
39
56
  onClear();
40
57
  }}
@@ -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 {import("lit").TemplateResult} */ d) =>
8
+ const _R = (d: import("lit").TemplateResult) =>
9
9
  html`<svg
10
10
  xmlns="http://www.w3.org/2000/svg"
11
11
  width="16"
@@ -18,7 +18,7 @@ const _R = (/** @type {import("lit").TemplateResult} */ d) =>
18
18
  </svg>`;
19
19
 
20
20
  // Helper for custom stroke icons
21
- const _S = (/** @type {import("lit").TemplateResult} */ d) =>
21
+ const _S = (d: import("lit").TemplateResult) =>
22
22
  html`<svg
23
23
  xmlns="http://www.w3.org/2000/svg"
24
24
  width="16"
@@ -1,16 +1,14 @@
1
+ /// <reference lib="dom" />
1
2
  import { render as litRender, nothing, html } from "lit-html";
2
3
 
3
- /** @type {HTMLElement} */
4
- let _popoverLayer;
5
- /** @type {HTMLElement} */
6
- let _modalLayer;
7
- /** @type {HTMLElement} */
8
- let _dialogLayer;
4
+ let _popoverLayer: HTMLElement;
5
+ let _modalLayer: HTMLElement;
6
+ let _dialogLayer: HTMLElement;
9
7
 
10
8
  export function initLayers() {
11
- _popoverLayer = /** @type {HTMLElement} */ (document.getElementById("layer-popover"));
12
- _modalLayer = /** @type {HTMLElement} */ (document.getElementById("layer-modal"));
13
- _dialogLayer = /** @type {HTMLElement} */ (document.getElementById("layer-dialog"));
9
+ _popoverLayer = document.getElementById("layer-popover") as HTMLElement;
10
+ _modalLayer = document.getElementById("layer-modal") as HTMLElement;
11
+ _dialogLayer = document.getElementById("layer-dialog") as HTMLElement;
14
12
  }
15
13
 
16
14
  /**
@@ -20,13 +18,15 @@ export function initLayers() {
20
18
  * @param {(done: (value: T) => void) => import("lit-html").TemplateResult} templateFn
21
19
  * @returns {Promise<T>}
22
20
  */
23
- export function showDialog(templateFn) {
21
+ export function showDialog<T>(
22
+ templateFn: (done: (value: T) => void) => import("lit-html").TemplateResult,
23
+ ): Promise<T> {
24
24
  return new Promise((resolve) => {
25
25
  const slot = document.createElement("div");
26
26
  slot.style.pointerEvents = "auto";
27
27
  _dialogLayer.appendChild(slot);
28
28
  let resolved = false;
29
- const done = (/** @type {T} */ value) => {
29
+ const done = (value: T) => {
30
30
  if (resolved) return;
31
31
  resolved = true;
32
32
  litRender(nothing, slot);
@@ -45,7 +45,11 @@ export function showDialog(templateFn) {
45
45
  * @param {{ confirmLabel?: string; cancelLabel?: string; destructive?: boolean }} [opts]
46
46
  * @returns {Promise<boolean>}
47
47
  */
48
- export function showConfirmDialog(headline, message, opts = {}) {
48
+ export function showConfirmDialog(
49
+ headline: string,
50
+ message: string | import("lit-html").TemplateResult,
51
+ opts: { confirmLabel?: string; cancelLabel?: string; destructive?: boolean } = {},
52
+ ) {
49
53
  const { confirmLabel = "Confirm", cancelLabel = "Cancel", destructive = false } = opts;
50
54
  return showDialog(
51
55
  (done) => html`
@@ -72,7 +76,7 @@ export function showConfirmDialog(headline, message, opts = {}) {
72
76
  *
73
77
  * @param {import("lit-html").TemplateResult} template
74
78
  */
75
- export function openModal(template) {
79
+ export function openModal(template: import("lit-html").TemplateResult) {
76
80
  const slot = document.createElement("div");
77
81
  slot.style.pointerEvents = "auto";
78
82
  _modalLayer.appendChild(slot);
@@ -80,7 +84,7 @@ export function openModal(template) {
80
84
  return {
81
85
  host: slot,
82
86
  /** @param {import("lit-html").TemplateResult} tpl */
83
- update(tpl) {
87
+ update(tpl: import("lit-html").TemplateResult) {
84
88
  litRender(tpl, slot);
85
89
  },
86
90
  close() {
@@ -100,7 +104,14 @@ export function openModal(template) {
100
104
  * layer?: "popover" | "modal" | "dialog";
101
105
  * }} [opts]
102
106
  */
103
- export function renderPopover(template, opts = {}) {
107
+ export function renderPopover(
108
+ template: import("lit-html").TemplateResult,
109
+ opts: {
110
+ dismissOnOutsideClick?: boolean;
111
+ onDismiss?: () => void;
112
+ layer?: "popover" | "modal" | "dialog";
113
+ } = {},
114
+ ) {
104
115
  const slot = document.createElement("div");
105
116
  slot.style.pointerEvents = "auto";
106
117
  const target =
@@ -108,11 +119,10 @@ export function renderPopover(template, opts = {}) {
108
119
  target.appendChild(slot);
109
120
  litRender(template, slot);
110
121
 
111
- /** @type {((e: MouseEvent) => void) | null} */
112
- let outsideClickHandler = null;
122
+ let outsideClickHandler: ((e: MouseEvent) => void) | null = null;
113
123
  if (opts.dismissOnOutsideClick !== false) {
114
- outsideClickHandler = (/** @type {MouseEvent} */ e) => {
115
- if (!slot.contains(/** @type {Node} */ (e.target))) {
124
+ outsideClickHandler = (e: MouseEvent) => {
125
+ if (!slot.contains(e.target as Node)) {
116
126
  handle.dismiss();
117
127
  opts.onDismiss?.();
118
128
  }
@@ -125,7 +135,7 @@ export function renderPopover(template, opts = {}) {
125
135
  const handle = {
126
136
  host: slot,
127
137
  /** @param {import("lit-html").TemplateResult} tpl */
128
- update(tpl) {
138
+ update(tpl: import("lit-html").TemplateResult) {
129
139
  litRender(tpl, slot);
130
140
  },
131
141
  dismiss() {
@@ -139,8 +149,7 @@ export function renderPopover(template, opts = {}) {
139
149
  return handle;
140
150
  }
141
151
 
142
- /** @type {Map<string, HTMLElement>} */
143
- const _namedSlots = new Map();
152
+ const _namedSlots: Map<string, HTMLElement> = new Map();
144
153
 
145
154
  /**
146
155
  * Get or create a named slot in a layer. Useful for persistent popovers like zoom indicator.
@@ -149,7 +158,7 @@ const _namedSlots = new Map();
149
158
  * @param {string} id
150
159
  * @returns {HTMLElement}
151
160
  */
152
- export function getLayerSlot(layer, id) {
161
+ export function getLayerSlot(layer: "popover" | "modal" | "dialog", id: string) {
153
162
  const key = `${layer}:${id}`;
154
163
  let slot = _namedSlots.get(key);
155
164
  if (slot && slot.parentElement) return slot;
@@ -170,7 +179,7 @@ export function getLayerSlot(layer, id) {
170
179
  * @param {"popover" | "modal" | "dialog"} layer
171
180
  * @param {string} id
172
181
  */
173
- export function clearLayerSlot(layer, id) {
182
+ export function clearLayerSlot(layer: "popover" | "modal" | "dialog", id: string) {
174
183
  const key = `${layer}:${id}`;
175
184
  const slot = _namedSlots.get(key);
176
185
  if (slot) {