@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
@@ -20,17 +20,82 @@ import { renderFieldRow } from "../ui/field-row.js";
20
20
  import { renderMediaPicker } from "../ui/media-picker.js";
21
21
  import { fetchPluginSchema, pluginSchemaCache } from "../services/code-services.js";
22
22
 
23
+ /**
24
+ * @typedef {{
25
+ * document: JxMutableNode;
26
+ * ui?: Record<string, unknown>;
27
+ * mode?: string;
28
+ * selection?: (string | number)[] | null;
29
+ * canvas?: Record<string, unknown>;
30
+ * _collapsedSignalCats?: Set<string>;
31
+ * documentPath?: string | null;
32
+ * }} SignalsPanelState
33
+ *
34
+ * @typedef {{ renderLeftPanel(): void; renderCanvas(): void; updateSession(patch: object): void }} SignalsPanelCtx
35
+ *
36
+ * @typedef {Record<string, unknown> & {
37
+ * $prototype?: string;
38
+ * $compute?: string;
39
+ * $deps?: string[];
40
+ * $handler?: unknown;
41
+ * $src?: string;
42
+ * $export?: string;
43
+ * type?: string;
44
+ * default?: unknown;
45
+ * description?: string;
46
+ * attribute?: string;
47
+ * reflects?: boolean;
48
+ * deprecated?: string | boolean;
49
+ * format?: string;
50
+ * url?: string;
51
+ * method?: string;
52
+ * timing?: string;
53
+ * key?: string;
54
+ * database?: string;
55
+ * store?: string;
56
+ * version?: number;
57
+ * name?: string;
58
+ * body?: string;
59
+ * parameters?: Record<string, unknown>[];
60
+ * emits?: Record<string, unknown>[];
61
+ * fields?: unknown;
62
+ * }} SignalDef
63
+ *
64
+ * @typedef {{ name: string; type?: { text?: string }; description?: string; optional?: boolean }} CemParameter
65
+ *
66
+ * @typedef {{ name: string; type?: { text?: string }; description?: string }} CemEvent
67
+ *
68
+ * @typedef {{
69
+ * type?: string;
70
+ * properties?: Record<string, SchemaProperty>;
71
+ * required?: string[];
72
+ * description?: string;
73
+ * }} JsonSchema
74
+ *
75
+ * @typedef {{
76
+ * type?: string;
77
+ * enum?: string[];
78
+ * default?: unknown;
79
+ * format?: string;
80
+ * minimum?: number;
81
+ * maximum?: number;
82
+ * description?: string;
83
+ * examples?: string[];
84
+ * name?: string;
85
+ * }} SchemaProperty
86
+ */
87
+
23
88
  // ─── Module-local state ─────────────────────────────────────────────────────
24
89
 
25
90
  /** Expanded signal editor state (persists across renders). */
26
- /** @type {any} */
91
+ /** @type {string | null} */
27
92
  let expandedSignal = null;
28
93
 
29
94
  /** Track which functions have the advanced param editor open. */
30
95
  const advancedParamOpen = new Set();
31
96
 
32
97
  /** Default templates for creating new signal definitions. */
33
- const DEF_TEMPLATES = /** @type {Record<string, any>} */ ({
98
+ const DEF_TEMPLATES = /** @type {Record<string, SignalDef>} */ ({
34
99
  state: { type: "string", default: "" },
35
100
  computed: { $compute: "", $deps: [] },
36
101
  request: { $prototype: "Request", url: "", method: "GET", timing: "client" },
@@ -67,34 +132,36 @@ const STUDIO_RESERVED_KEYS = new Set([
67
132
  /**
68
133
  * Classify a state entry into a category string.
69
134
  *
70
- * @param {any} def
135
+ * @param {SignalDef | unknown} def
71
136
  */
72
137
  export function defCategory(def) {
73
138
  if (!def) return "state";
74
- if (def.$handler || def.$prototype === "Function") return "function";
75
- if (def.$compute) return "computed";
76
- if (def.$prototype) return "data";
139
+ const d = /** @type {SignalDef} */ (def);
140
+ if (d.$handler || d.$prototype === "Function") return "function";
141
+ if (d.$compute) return "computed";
142
+ if (d.$prototype) return "data";
77
143
  return "state";
78
144
  }
79
145
 
80
146
  /**
81
147
  * Badge label for a def category.
82
148
  *
83
- * @param {any} def
149
+ * @param {SignalDef | unknown} def
84
150
  */
85
151
  export function defBadgeLabel(def) {
86
152
  if (!def) return "S";
87
- if (def.$handler || def.$prototype === "Function") return "F";
88
- if (def.$compute) return "C";
89
- if (def.$prototype) return def.$prototype.charAt(0);
153
+ const d = /** @type {SignalDef} */ (def);
154
+ if (d.$handler || d.$prototype === "Function") return "F";
155
+ if (d.$compute) return "C";
156
+ if (d.$prototype) return d.$prototype.charAt(0);
90
157
  return "S";
91
158
  }
92
159
 
93
160
  /**
94
161
  * Hint text for a signal row.
95
162
  *
96
- * @param {any} name
97
- * @param {any} def
163
+ * @param {string} name
164
+ * @param {SignalDef | null | undefined} def
98
165
  */
99
166
  export function defHint(name, def) {
100
167
  if (!def) return "";
@@ -120,7 +187,7 @@ export function defHint(name, def) {
120
187
  /**
121
188
  * Whether the current document defines a custom element (hyphenated tagName).
122
189
  *
123
- * @param {any} S
190
+ * @param {SignalsPanelState} S
124
191
  */
125
192
  export function isCustomElementDoc(S) {
126
193
  return (S.document.tagName || "").includes("-");
@@ -129,14 +196,16 @@ export function isCustomElementDoc(S) {
129
196
  /**
130
197
  * Recursively collect CSS `part` attributes from the document tree.
131
198
  *
132
- * @param {any} node
133
- * @param {any[]} [parts]
199
+ * @param {JxMutableNode | null | undefined} node
200
+ * @param {{ name: string; tag: string }[]} [parts]
134
201
  */
135
202
  export function collectCssParts(node, parts = []) {
136
203
  if (node?.attributes?.part)
137
204
  parts.push({ name: node.attributes.part, tag: node.tagName || "div" });
138
205
  if (Array.isArray(node?.children))
139
- node.children.forEach((/** @type {any} */ c) => collectCssParts(c, parts));
206
+ node.children.forEach((c) => {
207
+ if (typeof c !== "string") collectCssParts(c, parts);
208
+ });
140
209
  return parts;
141
210
  }
142
211
 
@@ -144,13 +213,14 @@ export function collectCssParts(node, parts = []) {
144
213
  * Resolve a $ref value to a display string using signal defaults. Used by the canvas to show real
145
214
  * values instead of raw refs.
146
215
  *
147
- * @param {any} value
148
- * @param {any} defs
216
+ * @param {unknown} value
217
+ * @param {Record<string, SignalDef> | null | undefined} defs
149
218
  */
150
219
  export function resolveDefaultForCanvas(value, defs) {
151
- if (!value || typeof value !== "object" || !value.$ref) return value;
152
- const ref = value.$ref;
153
- /** @type {any} */
220
+ if (!value || typeof value !== "object" || !(/** @type {Record<string, unknown>} */ (value).$ref))
221
+ return value;
222
+ const ref = /** @type {string} */ (/** @type {Record<string, unknown>} */ (value).$ref);
223
+ /** @type {string | undefined} */
154
224
  let defName;
155
225
  if (ref.startsWith("#/state/")) defName = ref.slice(8);
156
226
  else if (ref.startsWith("$")) defName = ref;
@@ -187,11 +257,11 @@ export function resolveDefaultForCanvas(value, defs) {
187
257
 
188
258
  /** Simple field row for signal editors — vertical stacked layout. */
189
259
  export function signalFieldRow(
190
- /** @type {any} */ label,
191
- /** @type {any} */ value,
192
- /** @type {any} */ onChange,
260
+ /** @type {string} */ label,
261
+ /** @type {string} */ value,
262
+ /** @type {(value: string) => void} */ onChange,
193
263
  ) {
194
- /** @type {any} */
264
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
195
265
  let debounce;
196
266
  return renderFieldRow({
197
267
  prop: label,
@@ -201,9 +271,12 @@ export function signalFieldRow(
201
271
  <sp-textfield
202
272
  size="s"
203
273
  value=${value}
204
- @input=${(/** @type {any} */ e) => {
274
+ @input=${(/** @type {Event} */ e) => {
205
275
  clearTimeout(debounce);
206
- debounce = setTimeout(() => onChange(e.target.value), 400);
276
+ debounce = setTimeout(
277
+ () => onChange(/** @type {HTMLInputElement} */ (e.target).value),
278
+ 400,
279
+ );
207
280
  }}
208
281
  ></sp-textfield>
209
282
  `,
@@ -211,22 +284,22 @@ export function signalFieldRow(
211
284
  }
212
285
 
213
286
  /** Normalize a parameter entry to a CEM object. */
214
- export function normParam(/** @type {any} */ p) {
215
- return typeof p === "string" ? { name: p } : p;
287
+ export function normParam(/** @type {string | Record<string, unknown>} */ p) {
288
+ return typeof p === "string" ? { name: p } : /** @type {CemParameter} */ (p);
216
289
  }
217
290
 
218
291
  // ─── Left panel: Signals ─────────────────────────────────────────────────────
219
292
 
220
293
  /**
221
- * @param {any} S
222
- * @param {any} ctx
294
+ * @param {SignalsPanelState} S
295
+ * @param {SignalsPanelCtx} ctx
223
296
  */
224
297
  export function renderSignalsTemplate(S, ctx) {
225
298
  const defs = S.document.state || {};
226
299
  const entries = Object.entries(defs);
227
300
 
228
301
  // Group by category
229
- const groups = /** @type {Record<string, any[]>} */ ({
302
+ const groups = /** @type {Record<string, [string, SignalDef][]>} */ ({
230
303
  state: [],
231
304
  computed: [],
232
305
  data: [],
@@ -259,7 +332,7 @@ export function renderSignalsTemplate(S, ctx) {
259
332
  }}
260
333
  >
261
334
  ${items.map(([name, def]) => {
262
- /** @type {any} */
335
+ /** @type {boolean} */
263
336
  const isExpanded = expandedSignal === name;
264
337
  return html`
265
338
  <div
@@ -276,7 +349,7 @@ export function renderSignalsTemplate(S, ctx) {
276
349
  quiet
277
350
  size="xs"
278
351
  class="signal-del"
279
- @click=${(/** @type {any} */ e) => {
352
+ @click=${(/** @type {Event} */ e) => {
280
353
  e.stopPropagation();
281
354
  transactDoc(activeTab.value, (t) => mutateRemoveDef(t, name));
282
355
  }}
@@ -304,8 +377,8 @@ export function renderSignalsTemplate(S, ctx) {
304
377
  size="s"
305
378
  label="+ Add…"
306
379
  placeholder="+ Add…"
307
- @change=${(/** @type {any} */ e) => {
308
- const type = e.target.value;
380
+ @change=${(/** @type {Event} */ e) => {
381
+ const type = /** @type {HTMLInputElement} */ (e.target).value;
309
382
  if (!type) return;
310
383
  const template = DEF_TEMPLATES[type];
311
384
  if (!template) return;
@@ -316,7 +389,13 @@ export function renderSignalsTemplate(S, ctx) {
316
389
  while (S.document.state && S.document.state[n]) {
317
390
  n = nameBase + i++;
318
391
  }
319
- transactDoc(activeTab.value, (t) => mutateAddDef(t, n, structuredClone(template)));
392
+ transactDoc(activeTab.value, (t) =>
393
+ mutateAddDef(
394
+ t,
395
+ n,
396
+ /** @type {Record<string, JsonValue>} */ (structuredClone(template)),
397
+ ),
398
+ );
320
399
  expandedSignal = n;
321
400
  ctx.renderLeftPanel();
322
401
  }}
@@ -343,10 +422,10 @@ export function renderSignalsTemplate(S, ctx) {
343
422
 
344
423
  /** Render inline editor fields for a specific signal/def type. */
345
424
  function renderSignalEditorTemplate(
346
- /** @type {any} */ S,
347
- /** @type {any} */ name,
348
- /** @type {any} */ def,
349
- /** @type {any} */ ctx,
425
+ /** @type {SignalsPanelState} */ S,
426
+ /** @type {string} */ name,
427
+ /** @type {SignalDef} */ def,
428
+ /** @type {SignalsPanelCtx} */ ctx,
350
429
  ) {
351
430
  if (typeof def !== "object" || def === null) {
352
431
  def = { default: def };
@@ -355,10 +434,10 @@ function renderSignalEditorTemplate(
355
434
 
356
435
  // Helper for picker rows
357
436
  const pickerRow = (
358
- /** @type {any} */ label,
359
- /** @type {any} */ options,
360
- /** @type {any} */ currentVal,
361
- /** @type {any} */ onChange,
437
+ /** @type {string} */ label,
438
+ /** @type {string[]} */ options,
439
+ /** @type {string} */ currentVal,
440
+ /** @type {(value: string) => void} */ onChange,
362
441
  ) => {
363
442
  return renderFieldRow({
364
443
  prop: label,
@@ -368,10 +447,11 @@ function renderSignalEditorTemplate(
368
447
  <sp-picker
369
448
  size="s"
370
449
  value=${currentVal}
371
- @change=${(/** @type {any} */ e) => onChange(e.target.value)}
450
+ @change=${(/** @type {Event} */ e) =>
451
+ onChange(/** @type {HTMLInputElement} */ (e.target).value)}
372
452
  >
373
453
  ${options.map(
374
- (/** @type {any} */ opt) => html`<sp-menu-item value=${opt}>${opt}</sp-menu-item>`,
454
+ (/** @type {string} */ opt) => html`<sp-menu-item value=${opt}>${opt}</sp-menu-item>`,
375
455
  )}
376
456
  </sp-picker>
377
457
  `,
@@ -380,12 +460,12 @@ function renderSignalEditorTemplate(
380
460
 
381
461
  // Helper for textarea rows
382
462
  const textareaRow = (
383
- /** @type {any} */ label,
384
- /** @type {any} */ value,
385
- /** @type {any} */ onChange,
386
- /** @type {any} */ opts = {},
463
+ /** @type {string} */ label,
464
+ /** @type {string} */ value,
465
+ /** @type {(value: string) => void} */ onChange,
466
+ /** @type {{ minHeight?: string; mono?: boolean }} */ opts = {},
387
467
  ) => {
388
- /** @type {any} */
468
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
389
469
  let debounce;
390
470
  return renderFieldRow({
391
471
  prop: label,
@@ -402,9 +482,12 @@ function renderSignalEditorTemplate(
402
482
  }),
403
483
  })}
404
484
  .value=${value}
405
- @input=${(/** @type {any} */ e) => {
485
+ @input=${(/** @type {Event} */ e) => {
406
486
  clearTimeout(debounce);
407
- debounce = setTimeout(() => onChange(e.target.value), 500);
487
+ debounce = setTimeout(
488
+ () => onChange(/** @type {HTMLInputElement} */ (e.target).value),
489
+ 500,
490
+ );
408
491
  }}
409
492
  ></textarea>
410
493
  `,
@@ -412,14 +495,14 @@ function renderSignalEditorTemplate(
412
495
  };
413
496
 
414
497
  // Name field (common to all)
415
- const nameField = signalFieldRow("Name", name, (/** @type {any} */ v) => {
498
+ const nameField = signalFieldRow("Name", name, (/** @type {string} */ v) => {
416
499
  if (v && v !== name && !(S.document.state && S.document.state[v])) {
417
500
  expandedSignal = v;
418
501
  transactDoc(activeTab.value, (t) => mutateRenameDef(t, name, v));
419
502
  }
420
503
  });
421
504
 
422
- /** @type {any} */
505
+ /** @type {import("lit-html").TemplateResult | typeof nothing} */
423
506
  let fields = nothing;
424
507
 
425
508
  if (cat === "state") {
@@ -432,7 +515,7 @@ function renderSignalEditorTemplate(
432
515
 
433
516
  const cemFields = isCustomElementDoc(S)
434
517
  ? html`
435
- ${signalFieldRow("Attribute", def.attribute || "", (/** @type {any} */ v) =>
518
+ ${signalFieldRow("Attribute", def.attribute || "", (/** @type {string} */ v) =>
436
519
  transactDoc(activeTab.value, (t) =>
437
520
  mutateUpdateDef(t, name, { attribute: v || undefined }),
438
521
  ),
@@ -445,9 +528,11 @@ function renderSignalEditorTemplate(
445
528
  <sp-checkbox
446
529
  class="field-check"
447
530
  ?checked=${!!def.reflects}
448
- @change=${(/** @type {any} */ e) =>
531
+ @change=${(/** @type {Event} */ e) =>
449
532
  transactDoc(activeTab.value, (t) =>
450
- mutateUpdateDef(t, name, { reflects: e.target.checked || undefined }),
533
+ mutateUpdateDef(t, name, {
534
+ reflects: /** @type {HTMLInputElement} */ (e.target).checked || undefined,
535
+ }),
451
536
  )}
452
537
  ></sp-checkbox>
453
538
  `,
@@ -455,7 +540,7 @@ function renderSignalEditorTemplate(
455
540
  ${signalFieldRow(
456
541
  "Deprecated",
457
542
  typeof def.deprecated === "string" ? def.deprecated : "",
458
- (/** @type {any} */ v) =>
543
+ (/** @type {string} */ v) =>
459
544
  transactDoc(activeTab.value, (t) =>
460
545
  mutateUpdateDef(t, name, { deprecated: v || undefined }),
461
546
  ),
@@ -468,7 +553,7 @@ function renderSignalEditorTemplate(
468
553
  "Type",
469
554
  ["string", "integer", "number", "boolean", "array", "object"],
470
555
  def.type || "string",
471
- (/** @type {any} */ v) =>
556
+ (/** @type {string} */ v) =>
472
557
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { type: v })),
473
558
  )}
474
559
  ${def.type === "string" || !def.type
@@ -476,7 +561,7 @@ function renderSignalEditorTemplate(
476
561
  "Format",
477
562
  ["", "image", "date", "color"],
478
563
  def.format || "",
479
- (/** @type {any} */ v) =>
564
+ (/** @type {string} */ v) =>
480
565
  transactDoc(activeTab.value, (t) =>
481
566
  mutateUpdateDef(t, name, { format: v || undefined }),
482
567
  ),
@@ -487,13 +572,14 @@ function renderSignalEditorTemplate(
487
572
  prop: "Default",
488
573
  label: "Default",
489
574
  hasValue: false,
490
- widget: renderMediaPicker("default", defaultVal, (/** @type {any} */ v) => {
575
+ widget: renderMediaPicker("default", defaultVal, (/** @type {string} */ v) => {
491
576
  transactDoc(activeTab.value, (t) =>
492
577
  mutateUpdateDef(t, name, { default: v || undefined }),
493
578
  );
494
579
  }),
495
580
  })
496
- : signalFieldRow("Default", defaultVal, (/** @type {any} */ v) => {
581
+ : signalFieldRow("Default", defaultVal, (/** @type {string} */ v) => {
582
+ /** @type {unknown} */
497
583
  let parsed = v;
498
584
  if (def.type === "integer") parsed = parseInt(v, 10) || 0;
499
585
  else if (def.type === "number") parsed = parseFloat(v) || 0;
@@ -505,9 +591,11 @@ function renderSignalEditorTemplate(
505
591
  parsed = v;
506
592
  }
507
593
  }
508
- transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { default: parsed }));
594
+ transactDoc(activeTab.value, (t) =>
595
+ mutateUpdateDef(t, name, { default: /** @type {JsonValue} */ (parsed) }),
596
+ );
509
597
  })}
510
- ${signalFieldRow("Description", def.description || "", (/** @type {any} */ v) =>
598
+ ${signalFieldRow("Description", def.description || "", (/** @type {string} */ v) =>
511
599
  transactDoc(activeTab.value, (t) =>
512
600
  mutateUpdateDef(t, name, { description: v || undefined }),
513
601
  ),
@@ -515,7 +603,7 @@ function renderSignalEditorTemplate(
515
603
  ${cemFields}
516
604
  `;
517
605
  } else if (cat === "computed") {
518
- /** @type {any} */
606
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
519
607
  let debounce;
520
608
  fields = html`
521
609
  ${renderFieldRow({
@@ -527,10 +615,10 @@ function renderSignalEditorTemplate(
527
615
  class="field-input"
528
616
  style="min-height:40px"
529
617
  .value=${def.$compute || ""}
530
- @input=${(/** @type {any} */ e) => {
618
+ @input=${(/** @type {Event} */ e) => {
531
619
  clearTimeout(debounce);
532
620
  debounce = setTimeout(() => {
533
- const expr = e.target.value;
621
+ const expr = /** @type {HTMLInputElement} */ (e.target).value;
534
622
  const depMatches = expr.match(/\$[a-zA-Z_]\w*/g) || [];
535
623
  const deps = [...new Set(depMatches)].map((d) => `#/state/${d}`);
536
624
  transactDoc(activeTab.value, (t) =>
@@ -549,7 +637,7 @@ function renderSignalEditorTemplate(
549
637
  widget: html`
550
638
  <span class="signal-hint" style="flex:1;max-width:none"
551
639
  >${def.$deps
552
- .map((/** @type {any} */ d) => d.replace("#/state/", ""))
640
+ .map((/** @type {string} */ d) => d.replace("#/state/", ""))
553
641
  .join(", ")}</span
554
642
  >
555
643
  `,
@@ -567,29 +655,33 @@ function renderSignalEditorTemplate(
567
655
 
568
656
  /** Data source fields for signal editor */
569
657
  function renderDataSourceFields(
570
- /** @type {any} */ S,
571
- /** @type {any} */ name,
572
- /** @type {any} */ def,
573
- /** @type {any} */ textareaRow,
574
- /** @type {any} */ pickerRow,
575
- /** @type {any} */ ctx,
658
+ /** @type {SignalsPanelState} */ S,
659
+ /** @type {string} */ name,
660
+ /** @type {SignalDef} */ def,
661
+ /** @type {Function} */ textareaRow,
662
+ /** @type {Function} */ pickerRow,
663
+ /** @type {SignalsPanelCtx} */ ctx,
576
664
  ) {
577
665
  const proto = def.$prototype;
578
666
 
579
667
  if (proto === "Request") {
580
668
  return html`
581
- ${signalFieldRow("URL", def.url || "", (/** @type {any} */ v) =>
669
+ ${signalFieldRow("URL", def.url || "", (/** @type {string} */ v) =>
582
670
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { url: v })),
583
671
  )}
584
672
  ${pickerRow(
585
673
  "Method",
586
674
  ["GET", "POST", "PUT", "DELETE", "PATCH"],
587
675
  def.method || "GET",
588
- (/** @type {any} */ v) =>
676
+ (/** @type {string} */ v) =>
589
677
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { method: v })),
590
678
  )}
591
- ${pickerRow("Timing", ["client", "server"], def.timing || "client", (/** @type {any} */ v) =>
592
- transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { timing: v })),
679
+ ${pickerRow(
680
+ "Timing",
681
+ ["client", "server"],
682
+ def.timing || "client",
683
+ (/** @type {string} */ v) =>
684
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { timing: v })),
593
685
  )}
594
686
  `;
595
687
  }
@@ -601,10 +693,10 @@ function renderDataSourceFields(
601
693
  : String(def.default)
602
694
  : "";
603
695
  return html`
604
- ${signalFieldRow("Key", def.key || "", (/** @type {any} */ v) =>
696
+ ${signalFieldRow("Key", def.key || "", (/** @type {string} */ v) =>
605
697
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { key: v })),
606
698
  )}
607
- ${textareaRow("Default", defaultStr, (/** @type {any} */ v) => {
699
+ ${textareaRow("Default", defaultStr, (/** @type {string} */ v) => {
608
700
  try {
609
701
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { default: JSON.parse(v) }));
610
702
  } catch {
@@ -615,13 +707,13 @@ function renderDataSourceFields(
615
707
  }
616
708
  if (proto === "IndexedDB") {
617
709
  return html`
618
- ${signalFieldRow("Database", def.database || "", (/** @type {any} */ v) =>
710
+ ${signalFieldRow("Database", def.database || "", (/** @type {string} */ v) =>
619
711
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { database: v })),
620
712
  )}
621
- ${signalFieldRow("Store", def.store || "", (/** @type {any} */ v) =>
713
+ ${signalFieldRow("Store", def.store || "", (/** @type {string} */ v) =>
622
714
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { store: v })),
623
715
  )}
624
- ${signalFieldRow("Version", String(def.version || 1), (/** @type {any} */ v) =>
716
+ ${signalFieldRow("Version", String(def.version || 1), (/** @type {string} */ v) =>
625
717
  transactDoc(activeTab.value, (t) =>
626
718
  mutateUpdateDef(t, name, { version: parseInt(v, 10) || 1 }),
627
719
  ),
@@ -630,10 +722,10 @@ function renderDataSourceFields(
630
722
  }
631
723
  if (proto === "Cookie") {
632
724
  return html`
633
- ${signalFieldRow("Cookie", def.name || "", (/** @type {any} */ v) =>
725
+ ${signalFieldRow("Cookie", def.name || "", (/** @type {string} */ v) =>
634
726
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { name: v })),
635
727
  )}
636
- ${signalFieldRow("Default", def.default || "", (/** @type {any} */ v) =>
728
+ ${signalFieldRow("Default", String(def.default || ""), (/** @type {string} */ v) =>
637
729
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { default: v })),
638
730
  )}
639
731
  `;
@@ -647,7 +739,7 @@ function renderDataSourceFields(
647
739
  : proto === "FormData"
648
740
  ? JSON.stringify(def.fields || {}, null, 2)
649
741
  : "";
650
- return textareaRow(fieldLabel, defaultStr, (/** @type {any} */ v) => {
742
+ return textareaRow(fieldLabel, defaultStr, (/** @type {string} */ v) => {
651
743
  try {
652
744
  transactDoc(activeTab.value, (t) =>
653
745
  mutateUpdateDef(t, name, { [fieldName]: JSON.parse(v) }),
@@ -661,16 +753,16 @@ function renderDataSourceFields(
661
753
 
662
754
  /** Function fields for signal editor */
663
755
  function renderFunctionFields(
664
- /** @type {any} */ S,
665
- /** @type {any} */ name,
666
- /** @type {any} */ def,
667
- /** @type {any} */ textareaRow,
668
- /** @type {any} */ ctx,
756
+ /** @type {SignalsPanelState} */ S,
757
+ /** @type {string} */ name,
758
+ /** @type {SignalDef} */ def,
759
+ /** @type {Function} */ textareaRow,
760
+ /** @type {SignalsPanelCtx} */ ctx,
669
761
  ) {
670
762
  const descriptionField = signalFieldRow(
671
763
  "Description",
672
764
  def.description || "",
673
- (/** @type {any} */ v) =>
765
+ (/** @type {string} */ v) =>
674
766
  transactDoc(activeTab.value, (t) =>
675
767
  mutateUpdateDef(t, name, { description: v || undefined }),
676
768
  ),
@@ -678,10 +770,10 @@ function renderFunctionFields(
678
770
 
679
771
  const bodyField = def.$src
680
772
  ? html`
681
- ${signalFieldRow("Source", def.$src || "", (/** @type {any} */ v) =>
773
+ ${signalFieldRow("Source", def.$src || "", (/** @type {string} */ v) =>
682
774
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { $src: v || undefined })),
683
775
  )}
684
- ${signalFieldRow("Export", def.$export || "", (/** @type {any} */ v) =>
776
+ ${signalFieldRow("Export", def.$export || "", (/** @type {string} */ v) =>
685
777
  transactDoc(activeTab.value, (t) =>
686
778
  mutateUpdateDef(t, name, { $export: v || undefined }),
687
779
  ),
@@ -706,8 +798,8 @@ function renderFunctionFields(
706
798
  class="field-input"
707
799
  style="min-height:60px;font-family:monospace;font-size:11px"
708
800
  .value=${def.body || ""}
709
- @input=${(/** @type {any} */ e) => {
710
- const v = e.target.value;
801
+ @input=${(/** @type {Event} */ e) => {
802
+ const v = /** @type {HTMLInputElement} */ (e.target).value;
711
803
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { body: v }));
712
804
  }}
713
805
  ></textarea>
@@ -723,10 +815,10 @@ function renderFunctionFields(
723
815
 
724
816
  /** Render CEM parameter editor with basic/advanced toggle. */
725
817
  function renderParameterEditorTemplate(
726
- /** @type {any} */ S,
727
- /** @type {any} */ name,
728
- /** @type {any} */ def,
729
- /** @type {any} */ ctx,
818
+ /** @type {SignalsPanelState} */ S,
819
+ /** @type {string} */ name,
820
+ /** @type {SignalDef} */ def,
821
+ /** @type {SignalsPanelCtx} */ ctx,
730
822
  ) {
731
823
  const params = (def.parameters || []).map(normParam);
732
824
  const isAdvanced = advancedParamOpen.has(name);
@@ -740,7 +832,7 @@ function renderParameterEditorTemplate(
740
832
  widget: html`
741
833
  <div style="display:flex;flex-wrap:wrap;gap:4px;align-items:center">
742
834
  ${params.map(
743
- (/** @type {any} */ p, /** @type {any} */ i) => html`
835
+ (/** @type {CemParameter} */ p, /** @type {number} */ i) => html`
744
836
  <span
745
837
  style="display:inline-flex;align-items:center;gap:2px;padding:1px 6px;border-radius:3px;background:var(--bg-hover);font-size:11px;font-family:monospace"
746
838
  >
@@ -751,9 +843,11 @@ function renderParameterEditorTemplate(
751
843
  transactDoc(activeTab.value, (t) =>
752
844
  mutateUpdateDef(t, name, {
753
845
  parameters: params.filter(
754
- (/** @type {any} */ _, /** @type {any} */ j) => j !== i,
846
+ (/** @type {unknown} */ _, /** @type {number} */ j) => j !== i,
755
847
  ).length
756
- ? params.filter((/** @type {any} */ _, /** @type {any} */ j) => j !== i)
848
+ ? params.filter(
849
+ (/** @type {unknown} */ _, /** @type {number} */ j) => j !== i,
850
+ )
757
851
  : undefined,
758
852
  }),
759
853
  );
@@ -767,11 +861,14 @@ function renderParameterEditorTemplate(
767
861
  class="field-input"
768
862
  style="width:60px;flex:0 0 auto;font-size:11px"
769
863
  placeholder="+"
770
- @keydown=${(/** @type {any} */ e) => {
771
- if (e.key === "Enter" && e.target.value.trim()) {
864
+ @keydown=${(/** @type {KeyboardEvent} */ e) => {
865
+ if (e.key === "Enter" && /** @type {HTMLInputElement} */ (e.target).value.trim()) {
772
866
  transactDoc(activeTab.value, (t) =>
773
867
  mutateUpdateDef(t, name, {
774
- parameters: [...params, { name: e.target.value.trim() }],
868
+ parameters: [
869
+ ...params,
870
+ { name: /** @type {HTMLInputElement} */ (e.target).value.trim() },
871
+ ],
775
872
  }),
776
873
  );
777
874
  }
@@ -798,16 +895,16 @@ function renderParameterEditorTemplate(
798
895
  widget: html`
799
896
  <div style="display:flex;flex-direction:column;gap:4px">
800
897
  ${params.map(
801
- (/** @type {any} */ p, /** @type {any} */ i) => html`
898
+ (/** @type {CemParameter} */ p, /** @type {number} */ i) => html`
802
899
  <div style="display:flex;gap:4px;align-items:center">
803
900
  <input
804
901
  class="field-input"
805
902
  .value=${p.name || ""}
806
903
  placeholder="name"
807
904
  style="flex:1"
808
- @change=${(/** @type {any} */ e) => {
905
+ @change=${(/** @type {Event} */ e) => {
809
906
  const next = [...params];
810
- next[i] = { ...next[i], name: e.target.value };
907
+ next[i] = { ...next[i], name: /** @type {HTMLInputElement} */ (e.target).value };
811
908
  transactDoc(activeTab.value, (t) =>
812
909
  mutateUpdateDef(t, name, { parameters: next }),
813
910
  );
@@ -818,11 +915,13 @@ function renderParameterEditorTemplate(
818
915
  .value=${p.type?.text || ""}
819
916
  placeholder="type"
820
917
  style="flex:1"
821
- @change=${(/** @type {any} */ e) => {
918
+ @change=${(/** @type {Event} */ e) => {
822
919
  const next = [...params];
823
920
  next[i] = {
824
921
  ...next[i],
825
- type: e.target.value ? { text: e.target.value } : undefined,
922
+ type: /** @type {HTMLInputElement} */ (e.target).value
923
+ ? { text: /** @type {HTMLInputElement} */ (e.target).value }
924
+ : undefined,
826
925
  };
827
926
  transactDoc(activeTab.value, (t) =>
828
927
  mutateUpdateDef(t, name, { parameters: next }),
@@ -834,9 +933,12 @@ function renderParameterEditorTemplate(
834
933
  .value=${p.description || ""}
835
934
  placeholder="desc"
836
935
  style="flex:2"
837
- @change=${(/** @type {any} */ e) => {
936
+ @change=${(/** @type {Event} */ e) => {
838
937
  const next = [...params];
839
- next[i] = { ...next[i], description: e.target.value || undefined };
938
+ next[i] = {
939
+ ...next[i],
940
+ description: /** @type {HTMLInputElement} */ (e.target).value || undefined,
941
+ };
840
942
  transactDoc(activeTab.value, (t) =>
841
943
  mutateUpdateDef(t, name, { parameters: next }),
842
944
  );
@@ -846,9 +948,12 @@ function renderParameterEditorTemplate(
846
948
  type="checkbox"
847
949
  title="optional"
848
950
  .checked=${!!p.optional}
849
- @change=${(/** @type {any} */ e) => {
951
+ @change=${(/** @type {Event} */ e) => {
850
952
  const next = [...params];
851
- next[i] = { ...next[i], optional: e.target.checked || undefined };
953
+ next[i] = {
954
+ ...next[i],
955
+ optional: /** @type {HTMLInputElement} */ (e.target).checked || undefined,
956
+ };
852
957
  transactDoc(activeTab.value, (t) =>
853
958
  mutateUpdateDef(t, name, { parameters: next }),
854
959
  );
@@ -858,7 +963,7 @@ function renderParameterEditorTemplate(
858
963
  style="cursor:pointer;opacity:0.5"
859
964
  @click=${() => {
860
965
  const next = params.filter(
861
- (/** @type {any} */ _, /** @type {any} */ j) => j !== i,
966
+ (/** @type {unknown} */ _, /** @type {number} */ j) => j !== i,
862
967
  );
863
968
  transactDoc(activeTab.value, (t) =>
864
969
  mutateUpdateDef(t, name, { parameters: next.length ? next : undefined }),
@@ -893,11 +998,11 @@ function renderParameterEditorTemplate(
893
998
 
894
999
  /** Render CEM emits editor for function state entries. */
895
1000
  function renderEmitsEditorTemplate(
896
- /** @type {any} */ S,
897
- /** @type {any} */ name,
898
- /** @type {any} */ def,
1001
+ /** @type {SignalsPanelState} */ S,
1002
+ /** @type {string} */ name,
1003
+ /** @type {SignalDef} */ def,
899
1004
  ) {
900
- const emits = def.emits || [];
1005
+ const emits = /** @type {CemEvent[]} */ (def.emits || []);
901
1006
  if (emits.length === 0 && !isCustomElementDoc(S)) return nothing;
902
1007
 
903
1008
  return html`
@@ -907,16 +1012,16 @@ function renderEmitsEditorTemplate(
907
1012
  Emits
908
1013
  </div>
909
1014
  ${emits.map(
910
- (/** @type {any} */ ev, /** @type {any} */ i) => html`
1015
+ (/** @type {CemEvent} */ ev, /** @type {number} */ i) => html`
911
1016
  <div style="display:flex;gap:4px;align-items:center;margin-bottom:4px">
912
1017
  <input
913
1018
  class="field-input"
914
1019
  .value=${ev.name || ""}
915
1020
  placeholder="event name"
916
1021
  style="flex:1"
917
- @change=${(/** @type {any} */ e) => {
1022
+ @change=${(/** @type {Event} */ e) => {
918
1023
  const next = [...emits];
919
- next[i] = { ...next[i], name: e.target.value };
1024
+ next[i] = { ...next[i], name: /** @type {HTMLInputElement} */ (e.target).value };
920
1025
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { emits: next }));
921
1026
  }}
922
1027
  />
@@ -925,9 +1030,14 @@ function renderEmitsEditorTemplate(
925
1030
  .value=${ev.type?.text || ""}
926
1031
  placeholder="type"
927
1032
  style="flex:1"
928
- @change=${(/** @type {any} */ e) => {
1033
+ @change=${(/** @type {Event} */ e) => {
929
1034
  const next = [...emits];
930
- next[i] = { ...next[i], type: e.target.value ? { text: e.target.value } : undefined };
1035
+ next[i] = {
1036
+ ...next[i],
1037
+ type: /** @type {HTMLInputElement} */ (e.target).value
1038
+ ? { text: /** @type {HTMLInputElement} */ (e.target).value }
1039
+ : undefined,
1040
+ };
931
1041
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { emits: next }));
932
1042
  }}
933
1043
  />
@@ -936,9 +1046,12 @@ function renderEmitsEditorTemplate(
936
1046
  .value=${ev.description || ""}
937
1047
  placeholder="description"
938
1048
  style="flex:2"
939
- @change=${(/** @type {any} */ e) => {
1049
+ @change=${(/** @type {Event} */ e) => {
940
1050
  const next = [...emits];
941
- next[i] = { ...next[i], description: e.target.value || undefined };
1051
+ next[i] = {
1052
+ ...next[i],
1053
+ description: /** @type {HTMLInputElement} */ (e.target).value || undefined,
1054
+ };
942
1055
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { emits: next }));
943
1056
  }}
944
1057
  />
@@ -947,9 +1060,10 @@ function renderEmitsEditorTemplate(
947
1060
  @click=${() => {
948
1061
  transactDoc(activeTab.value, (t) =>
949
1062
  mutateUpdateDef(t, name, {
950
- emits: emits.filter((/** @type {any} */ _, /** @type {any} */ j) => j !== i)
951
- .length
952
- ? emits.filter((/** @type {any} */ _, /** @type {any} */ j) => j !== i)
1063
+ emits: emits.filter(
1064
+ (/** @type {unknown} */ _, /** @type {number} */ j) => j !== i,
1065
+ ).length
1066
+ ? emits.filter((/** @type {unknown} */ _, /** @type {number} */ j) => j !== i)
953
1067
  : undefined,
954
1068
  }),
955
1069
  );
@@ -978,10 +1092,10 @@ function renderEmitsEditorTemplate(
978
1092
  * appropriate form controls.
979
1093
  */
980
1094
  export function renderSchemaFieldsTemplate(
981
- /** @type {any} */ schema,
982
- /** @type {any} */ def,
983
- /** @type {any} */ name,
984
- /** @type {any} */ _S,
1095
+ /** @type {JsonSchema | null | undefined} */ schema,
1096
+ /** @type {SignalDef} */ def,
1097
+ /** @type {string} */ name,
1098
+ /** @type {SignalsPanelState} */ _S,
985
1099
  ) {
986
1100
  if (!schema?.properties) return nothing;
987
1101
 
@@ -1003,29 +1117,33 @@ export function renderSchemaFieldsTemplate(
1003
1117
  : ps.default !== undefined
1004
1118
  ? String(ps.default)
1005
1119
  : "__none__"}
1006
- @change=${(/** @type {any} */ e) =>
1120
+ @change=${(/** @type {Event} */ e) =>
1007
1121
  transactDoc(activeTab.value, (t) =>
1008
1122
  mutateUpdateDef(t, name, {
1009
- [prop]: e.target.value === "__none__" ? undefined : e.target.value,
1123
+ [prop]: /** @type {HTMLInputElement} */ (e.target).value === "__none__"
1124
+ ? undefined
1125
+ : /** @type {HTMLInputElement} */ (e.target).value,
1010
1126
  }),
1011
1127
  )}
1012
1128
  >
1013
1129
  ${!required.has(prop) ? html`<sp-menu-item value="__none__">—</sp-menu-item>` : nothing}
1014
1130
  ${ps.enum.map(
1015
- (/** @type {any} */ val) => html`<sp-menu-item value=${val}>${val}</sp-menu-item>`,
1131
+ (/** @type {string} */ val) => html`<sp-menu-item value=${val}>${val}</sp-menu-item>`,
1016
1132
  )}
1017
1133
  </sp-picker>
1018
1134
  `;
1019
1135
  } else if (ps.type === "boolean") {
1020
1136
  control = html`<sp-checkbox
1021
1137
  ?checked=${currentValue ?? ps.default ?? false}
1022
- @change=${(/** @type {any} */ e) =>
1138
+ @change=${(/** @type {Event} */ e) =>
1023
1139
  transactDoc(activeTab.value, (t) =>
1024
- mutateUpdateDef(t, name, { [prop]: e.target.checked }),
1140
+ mutateUpdateDef(t, name, {
1141
+ [prop]: /** @type {HTMLInputElement} */ (e.target).checked,
1142
+ }),
1025
1143
  )}
1026
1144
  ></sp-checkbox>`;
1027
1145
  } else if (ps.type === "integer" || ps.type === "number") {
1028
- /** @type {any} */
1146
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
1029
1147
  let debounce;
1030
1148
  control = html`<sp-number-field
1031
1149
  size="s"
@@ -1034,11 +1152,13 @@ export function renderSchemaFieldsTemplate(
1034
1152
  step=${ps.type === "integer" ? "1" : nothing}
1035
1153
  .value=${currentValue !== undefined ? currentValue : nothing}
1036
1154
  placeholder=${ps.default !== undefined ? String(ps.default) : nothing}
1037
- @change=${(/** @type {any} */ e) => {
1155
+ @change=${(/** @type {Event} */ e) => {
1038
1156
  clearTimeout(debounce);
1039
1157
  debounce = setTimeout(() => {
1040
1158
  const parsed =
1041
- ps.type === "integer" ? parseInt(e.target.value, 10) : parseFloat(e.target.value);
1159
+ ps.type === "integer"
1160
+ ? parseInt(/** @type {HTMLInputElement} */ (e.target).value, 10)
1161
+ : parseFloat(/** @type {HTMLInputElement} */ (e.target).value);
1042
1162
  transactDoc(activeTab.value, (t) =>
1043
1163
  mutateUpdateDef(t, name, { [prop]: isNaN(parsed) ? undefined : parsed }),
1044
1164
  );
@@ -1048,15 +1168,18 @@ export function renderSchemaFieldsTemplate(
1048
1168
  } else if (ps.format === "json-schema") {
1049
1169
  const hasValue =
1050
1170
  currentValue && typeof currentValue === "object" && Object.keys(currentValue).length > 0;
1051
- const isRef = currentValue && typeof currentValue === "object" && currentValue.$ref;
1052
- /** @type {any} */
1171
+ const cv = /** @type {Record<string, unknown>} */ (currentValue);
1172
+ const isRef = hasValue && cv.$ref;
1173
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
1053
1174
  let debounce;
1054
1175
  control = html`
1055
1176
  <div class="schema-param-editor">
1056
- ${hasValue && !isRef && currentValue.properties
1177
+ ${hasValue && !isRef && cv.properties
1057
1178
  ? html`
1058
1179
  <div style="display:flex;flex-wrap:wrap;gap:3px;margin-bottom:4px">
1059
- ${Object.entries(currentValue.properties).map(
1180
+ ${Object.entries(
1181
+ /** @type {Record<string, Record<string, unknown>>} */ (cv.properties),
1182
+ ).map(
1060
1183
  ([k, v]) => html`
1061
1184
  <span
1062
1185
  style="background:var(--bg-alt);padding:1px 6px;border-radius:3px;font-size:10px;color:var(--fg-dim)"
@@ -1077,12 +1200,14 @@ export function renderSchemaFieldsTemplate(
1077
1200
  })}
1078
1201
  .value=${currentValue !== undefined ? JSON.stringify(currentValue, null, 2) : ""}
1079
1202
  placeholder=${ps.description ?? "JSON Schema defining the data shape\u2026"}
1080
- @input=${(/** @type {any} */ e) => {
1203
+ @input=${(/** @type {Event} */ e) => {
1081
1204
  clearTimeout(debounce);
1082
1205
  debounce = setTimeout(() => {
1083
1206
  try {
1084
1207
  transactDoc(activeTab.value, (t) =>
1085
- mutateUpdateDef(t, name, { [prop]: JSON.parse(e.target.value) }),
1208
+ mutateUpdateDef(t, name, {
1209
+ [prop]: JSON.parse(/** @type {HTMLInputElement} */ (e.target).value),
1210
+ }),
1086
1211
  );
1087
1212
  } catch {}
1088
1213
  }, 500);
@@ -1091,7 +1216,7 @@ export function renderSchemaFieldsTemplate(
1091
1216
  </div>
1092
1217
  `;
1093
1218
  } else if (ps.type === "array" || ps.type === "object") {
1094
- /** @type {any} */
1219
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
1095
1220
  let debounce;
1096
1221
  control = html`<sp-textfield
1097
1222
  multiline
@@ -1099,19 +1224,21 @@ export function renderSchemaFieldsTemplate(
1099
1224
  style="min-height:40px"
1100
1225
  .value=${currentValue !== undefined ? JSON.stringify(currentValue, null, 2) : ""}
1101
1226
  placeholder=${ps.default !== undefined ? JSON.stringify(ps.default) : nothing}
1102
- @input=${(/** @type {any} */ e) => {
1227
+ @input=${(/** @type {Event} */ e) => {
1103
1228
  clearTimeout(debounce);
1104
1229
  debounce = setTimeout(() => {
1105
1230
  try {
1106
1231
  transactDoc(activeTab.value, (t) =>
1107
- mutateUpdateDef(t, name, { [prop]: JSON.parse(e.target.value) }),
1232
+ mutateUpdateDef(t, name, {
1233
+ [prop]: JSON.parse(/** @type {HTMLInputElement} */ (e.target).value),
1234
+ }),
1108
1235
  );
1109
1236
  } catch {}
1110
1237
  }, 500);
1111
1238
  }}
1112
1239
  ></sp-textfield>`;
1113
1240
  } else {
1114
- /** @type {any} */
1241
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
1115
1242
  let debounce;
1116
1243
  const ph = ps.default !== undefined ? String(ps.default) : (ps.examples?.[0] ?? "");
1117
1244
  control = html`<sp-textfield
@@ -1119,12 +1246,14 @@ export function renderSchemaFieldsTemplate(
1119
1246
  .value=${currentValue ?? ""}
1120
1247
  placeholder=${ph || nothing}
1121
1248
  title=${ps.description || nothing}
1122
- @input=${(/** @type {any} */ e) => {
1249
+ @input=${(/** @type {Event} */ e) => {
1123
1250
  clearTimeout(debounce);
1124
1251
  debounce = setTimeout(
1125
1252
  () =>
1126
1253
  transactDoc(activeTab.value, (t) =>
1127
- mutateUpdateDef(t, name, { [prop]: e.target.value || undefined }),
1254
+ mutateUpdateDef(t, name, {
1255
+ [prop]: /** @type {HTMLInputElement} */ (e.target).value || undefined,
1256
+ }),
1128
1257
  ),
1129
1258
  400,
1130
1259
  );
@@ -1133,7 +1262,7 @@ export function renderSchemaFieldsTemplate(
1133
1262
  }
1134
1263
 
1135
1264
  return renderFieldRow({
1136
- prop: ps.name,
1265
+ prop: ps.name || prop,
1137
1266
  label: labelText,
1138
1267
  hasValue: false,
1139
1268
  widget: control,
@@ -1146,13 +1275,13 @@ export function renderSchemaFieldsTemplate(
1146
1275
  * schema-driven config fields.
1147
1276
  */
1148
1277
  export function renderExternalPrototypeEditorTemplate(
1149
- /** @type {any} */ S,
1150
- /** @type {any} */ name,
1151
- /** @type {any} */ def,
1152
- /** @type {any} */ ctx,
1278
+ /** @type {SignalsPanelState} */ S,
1279
+ /** @type {string} */ name,
1280
+ /** @type {SignalDef} */ def,
1281
+ /** @type {SignalsPanelCtx} */ ctx,
1153
1282
  ) {
1154
1283
  // Schema-driven config fields (async with cache)
1155
- /** @type {any} */
1284
+ /** @type {import("lit-html").TemplateResult | typeof nothing} */
1156
1285
  let schemaContent = nothing;
1157
1286
  if (def.$src && def.$prototype) {
1158
1287
  const cacheKey = `${def.$src}::${def.$prototype}`;
@@ -1173,23 +1302,23 @@ export function renderExternalPrototypeEditorTemplate(
1173
1302
  >
1174
1303
  Loading schema…
1175
1304
  </div>`;
1176
- fetchPluginSchema(def, S).then((schema) => {
1305
+ fetchPluginSchema(def, /** @type {{ documentPath?: string }} */ (S)).then((schema) => {
1177
1306
  if (schema) ctx.renderLeftPanel();
1178
1307
  });
1179
1308
  }
1180
1309
  }
1181
1310
 
1182
1311
  return html`
1183
- ${signalFieldRow("Source", def.$src || "", (/** @type {any} */ v) => {
1312
+ ${signalFieldRow("Source", def.$src || "", (/** @type {string} */ v) => {
1184
1313
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { $src: v || undefined }));
1185
1314
  pluginSchemaCache.delete(`${v}::${def.$prototype}`);
1186
1315
  })}
1187
- ${signalFieldRow("Prototype", def.$prototype || "", (/** @type {any} */ v) => {
1316
+ ${signalFieldRow("Prototype", def.$prototype || "", (/** @type {string} */ v) => {
1188
1317
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { $prototype: v || undefined }));
1189
1318
  pluginSchemaCache.delete(`${def.$src}::${v}`);
1190
1319
  })}
1191
1320
  ${def.$export
1192
- ? signalFieldRow("Export", def.$export || "", (/** @type {any} */ v) =>
1321
+ ? signalFieldRow("Export", def.$export || "", (/** @type {string} */ v) =>
1193
1322
  transactDoc(activeTab.value, (t) =>
1194
1323
  mutateUpdateDef(t, name, { $export: v || undefined }),
1195
1324
  ),