@jxsuite/studio 0.19.0 → 0.21.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 (99) hide show
  1. package/dist/studio.css +98 -98
  2. package/dist/studio.js +13476 -12330
  3. package/dist/studio.js.map +152 -148
  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 +19 -7
  8. package/src/browse/browse-modal.js +16 -13
  9. package/src/browse/browse.js +44 -35
  10. package/src/canvas/canvas-diff.js +24 -16
  11. package/src/canvas/canvas-helpers.js +24 -19
  12. package/src/canvas/canvas-live-render.js +113 -39
  13. package/src/canvas/canvas-render.js +119 -73
  14. package/src/canvas/canvas-utils.js +51 -21
  15. package/src/canvas/nested-site-style.js +50 -0
  16. package/src/editor/component-inline-edit.js +14 -13
  17. package/src/editor/content-inline-edit.js +22 -10
  18. package/src/editor/context-menu.js +28 -14
  19. package/src/editor/convert-targets.js +60 -0
  20. package/src/editor/convert-to-component.js +20 -19
  21. package/src/editor/convert-to-repeater.js +226 -0
  22. package/src/editor/inline-edit.js +69 -28
  23. package/src/editor/insertion-helper.js +16 -12
  24. package/src/editor/shortcuts.js +14 -4
  25. package/src/editor/slash-menu.js +40 -19
  26. package/src/files/components.js +1 -1
  27. package/src/files/file-ops.js +37 -16
  28. package/src/files/files.js +322 -68
  29. package/src/github/github-auth.js +122 -0
  30. package/src/github/github-publish.js +126 -0
  31. package/src/markdown/md-convert.js +172 -107
  32. package/src/new-project/new-project-modal.js +204 -0
  33. package/src/panels/activity-bar.js +27 -25
  34. package/src/panels/ai-panel.js +406 -0
  35. package/src/panels/block-action-bar.js +77 -28
  36. package/src/panels/canvas-dnd.js +8 -2
  37. package/src/panels/data-explorer.js +19 -13
  38. package/src/panels/dnd.js +144 -92
  39. package/src/panels/editors.js +55 -35
  40. package/src/panels/elements-panel.js +33 -19
  41. package/src/panels/events-panel.js +15 -10
  42. package/src/panels/git-panel.js +475 -99
  43. package/src/panels/head-panel.js +121 -81
  44. package/src/panels/imports-panel.js +94 -65
  45. package/src/panels/layers-panel.js +64 -43
  46. package/src/panels/left-panel.js +105 -48
  47. package/src/panels/overlays.js +1 -0
  48. package/src/panels/panel-events.js +45 -30
  49. package/src/panels/preview-render.js +23 -18
  50. package/src/panels/properties-panel.js +378 -257
  51. package/src/panels/pseudo-preview.js +2 -2
  52. package/src/panels/quick-search.js +11 -9
  53. package/src/panels/right-panel.js +42 -16
  54. package/src/panels/shared.js +4 -4
  55. package/src/panels/signals-panel.js +415 -166
  56. package/src/panels/statusbar.js +10 -6
  57. package/src/panels/style-inputs.js +67 -35
  58. package/src/panels/style-panel.js +239 -134
  59. package/src/panels/style-utils.js +34 -16
  60. package/src/panels/stylebook-layers-panel.js +12 -10
  61. package/src/panels/stylebook-panel.js +134 -66
  62. package/src/panels/tab-strip.js +5 -2
  63. package/src/panels/toolbar.js +333 -109
  64. package/src/panels/welcome-screen.js +121 -0
  65. package/src/platform.js +2 -4
  66. package/src/platforms/devserver.js +113 -7
  67. package/src/resize-edges.js +98 -0
  68. package/src/services/cem-export.js +12 -7
  69. package/src/services/code-services.js +30 -12
  70. package/src/settings/content-types-editor.js +8 -8
  71. package/src/settings/css-vars-editor.js +30 -24
  72. package/src/settings/defs-editor.js +12 -7
  73. package/src/settings/general-settings.js +85 -3
  74. package/src/settings/head-editor.js +30 -24
  75. package/src/settings/schema-field-ui.js +58 -39
  76. package/src/settings/settings-modal.js +12 -9
  77. package/src/site-context.js +41 -31
  78. package/src/state.js +70 -29
  79. package/src/store.js +36 -35
  80. package/src/studio.js +120 -90
  81. package/src/tabs/tab.js +67 -43
  82. package/src/tabs/transact.js +23 -13
  83. package/src/ui/button-group.js +24 -24
  84. package/src/ui/color-selector.js +28 -23
  85. package/src/ui/field-row.js +3 -3
  86. package/src/ui/icons.js +2 -2
  87. package/src/ui/layers.js +31 -1
  88. package/src/ui/media-picker.js +7 -7
  89. package/src/ui/spectrum.js +5 -1
  90. package/src/ui/unit-selector.js +18 -16
  91. package/src/ui/value-selector.js +21 -15
  92. package/src/ui/widgets.js +22 -19
  93. package/src/utils/canvas-media.js +5 -4
  94. package/src/utils/edit-display.js +32 -26
  95. package/src/utils/google-fonts.js +11 -11
  96. package/src/utils/inherited-style.js +9 -8
  97. package/src/utils/studio-utils.js +18 -18
  98. package/src/view.js +58 -1
  99. package/src/workspace/workspace.js +29 -7
@@ -8,6 +8,7 @@ import { html, nothing } from "lit-html";
8
8
  import { classMap } from "lit-html/directives/class-map.js";
9
9
  import { ifDefined } from "lit-html/directives/if-defined.js";
10
10
  import { styleMap } from "lit-html/directives/style-map.js";
11
+ import { projectState } from "../state.js";
11
12
  import { activeTab } from "../workspace/workspace.js";
12
13
  import {
13
14
  transactDoc,
@@ -20,17 +21,87 @@ import { renderFieldRow } from "../ui/field-row.js";
20
21
  import { renderMediaPicker } from "../ui/media-picker.js";
21
22
  import { fetchPluginSchema, pluginSchemaCache } from "../services/code-services.js";
22
23
 
24
+ /**
25
+ * @typedef {{
26
+ * document: JxMutableNode;
27
+ * ui?: Record<string, unknown>;
28
+ * mode?: string;
29
+ * selection?: (string | number)[] | null;
30
+ * canvas?: Record<string, unknown>;
31
+ * _collapsedSignalCats?: Set<string>;
32
+ * documentPath?: string | null;
33
+ * }} SignalsPanelState
34
+ *
35
+ * @typedef {{ renderLeftPanel(): void; renderCanvas(): void; updateSession(patch: object): void }} SignalsPanelCtx
36
+ *
37
+ * @typedef {Record<string, unknown> & {
38
+ * $prototype?: string;
39
+ * $compute?: string;
40
+ * $deps?: string[];
41
+ * $handler?: unknown;
42
+ * $src?: string;
43
+ * $export?: string;
44
+ * type?: string;
45
+ * default?: unknown;
46
+ * description?: string;
47
+ * attribute?: string;
48
+ * reflects?: boolean;
49
+ * deprecated?: string | boolean;
50
+ * format?: string;
51
+ * url?: string;
52
+ * method?: string;
53
+ * timing?: string;
54
+ * key?: string;
55
+ * database?: string;
56
+ * store?: string;
57
+ * version?: number;
58
+ * name?: string;
59
+ * body?: string;
60
+ * parameters?: Record<string, unknown>[];
61
+ * emits?: Record<string, unknown>[];
62
+ * fields?: unknown;
63
+ * contentType?: string;
64
+ * id?: unknown;
65
+ * filter?: Record<string, unknown>;
66
+ * sort?: { field?: string; order?: string };
67
+ * limit?: number;
68
+ * }} SignalDef
69
+ *
70
+ * @typedef {{ name: string; type?: { text?: string }; description?: string; optional?: boolean }} CemParameter
71
+ *
72
+ * @typedef {{ name: string; type?: { text?: string }; description?: string }} CemEvent
73
+ *
74
+ * @typedef {{
75
+ * type?: string;
76
+ * properties?: Record<string, SchemaProperty>;
77
+ * required?: string[];
78
+ * description?: string;
79
+ * }} JsonSchema
80
+ *
81
+ * @typedef {{
82
+ * type?: string;
83
+ * enum?: string[];
84
+ * default?: unknown;
85
+ * format?: string;
86
+ * minimum?: number;
87
+ * maximum?: number;
88
+ * description?: string;
89
+ * examples?: string[];
90
+ * name?: string;
91
+ * }} SchemaProperty
92
+ */
93
+
23
94
  // ─── Module-local state ─────────────────────────────────────────────────────
24
95
 
25
96
  /** Expanded signal editor state (persists across renders). */
26
- /** @type {any} */
97
+ /** @type {string | null} */
27
98
  let expandedSignal = null;
28
99
 
29
100
  /** Track which functions have the advanced param editor open. */
30
101
  const advancedParamOpen = new Set();
31
102
 
32
103
  /** Default templates for creating new signal definitions. */
33
- const DEF_TEMPLATES = /** @type {Record<string, any>} */ ({
104
+ const DEF_TEMPLATES = /** @type {Record<string, SignalDef>} */ ({
34
105
  state: { type: "string", default: "" },
35
106
  computed: { $compute: "", $deps: [] },
36
107
  request: { $prototype: "Request", url: "", method: "GET", timing: "client" },
@@ -67,34 +138,36 @@ const STUDIO_RESERVED_KEYS = new Set([
67
138
  /**
68
139
  * Classify a state entry into a category string.
69
140
  *
70
- * @param {any} def
141
+ * @param {SignalDef | unknown} def
71
142
  */
72
143
  export function defCategory(def) {
73
144
  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";
145
+ const d = /** @type {SignalDef} */ (def);
146
+ if (d.$handler || d.$prototype === "Function") return "function";
147
+ if (d.$compute) return "computed";
148
+ if (d.$prototype) return "data";
77
149
  return "state";
78
150
  }
79
151
 
80
152
  /**
81
153
  * Badge label for a def category.
82
154
  *
83
- * @param {any} def
155
+ * @param {SignalDef | unknown} def
84
156
  */
85
157
  export function defBadgeLabel(def) {
86
158
  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);
159
+ const d = /** @type {SignalDef} */ (def);
160
+ if (d.$handler || d.$prototype === "Function") return "F";
161
+ if (d.$compute) return "C";
162
+ if (d.$prototype) return d.$prototype.charAt(0);
90
163
  return "S";
91
164
  }
92
165
 
93
166
  /**
94
167
  * Hint text for a signal row.
95
168
  *
96
- * @param {any} name
97
- * @param {any} def
169
+ * @param {string} name
170
+ * @param {SignalDef | null | undefined} def
98
171
  */
99
172
  export function defHint(name, def) {
100
173
  if (!def) return "";
@@ -120,7 +193,7 @@ export function defHint(name, def) {
120
193
  /**
121
194
  * Whether the current document defines a custom element (hyphenated tagName).
122
195
  *
123
- * @param {any} S
196
+ * @param {SignalsPanelState} S
124
197
  */
125
198
  export function isCustomElementDoc(S) {
126
199
  return (S.document.tagName || "").includes("-");
@@ -129,14 +202,16 @@ export function isCustomElementDoc(S) {
129
202
  /**
130
203
  * Recursively collect CSS `part` attributes from the document tree.
131
204
  *
132
- * @param {any} node
133
- * @param {any[]} [parts]
205
+ * @param {JxMutableNode | null | undefined} node
206
+ * @param {{ name: string; tag: string }[]} [parts]
134
207
  */
135
208
  export function collectCssParts(node, parts = []) {
136
209
  if (node?.attributes?.part)
137
210
  parts.push({ name: node.attributes.part, tag: node.tagName || "div" });
138
211
  if (Array.isArray(node?.children))
139
- node.children.forEach((/** @type {any} */ c) => collectCssParts(c, parts));
212
+ node.children.forEach((c) => {
213
+ if (typeof c !== "string") collectCssParts(c, parts);
214
+ });
140
215
  return parts;
141
216
  }
142
217
 
@@ -144,13 +219,14 @@ export function collectCssParts(node, parts = []) {
144
219
  * Resolve a $ref value to a display string using signal defaults. Used by the canvas to show real
145
220
  * values instead of raw refs.
146
221
  *
147
- * @param {any} value
148
- * @param {any} defs
222
+ * @param {unknown} value
223
+ * @param {Record<string, SignalDef> | null | undefined} defs
149
224
  */
150
225
  export function resolveDefaultForCanvas(value, defs) {
151
- if (!value || typeof value !== "object" || !value.$ref) return value;
152
- const ref = value.$ref;
153
- /** @type {any} */
226
+ if (!value || typeof value !== "object" || !(/** @type {Record<string, unknown>} */ (value).$ref))
227
+ return value;
228
+ const ref = /** @type {string} */ (/** @type {Record<string, unknown>} */ (value).$ref);
229
+ /** @type {string | undefined} */
154
230
  let defName;
155
231
  if (ref.startsWith("#/state/")) defName = ref.slice(8);
156
232
  else if (ref.startsWith("$")) defName = ref;
@@ -187,11 +263,11 @@ export function resolveDefaultForCanvas(value, defs) {
187
263
 
188
264
  /** Simple field row for signal editors — vertical stacked layout. */
189
265
  export function signalFieldRow(
190
- /** @type {any} */ label,
191
- /** @type {any} */ value,
192
- /** @type {any} */ onChange,
266
+ /** @type {string} */ label,
267
+ /** @type {string} */ value,
268
+ /** @type {(value: string) => void} */ onChange,
193
269
  ) {
194
- /** @type {any} */
270
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
195
271
  let debounce;
196
272
  return renderFieldRow({
197
273
  prop: label,
@@ -201,9 +277,12 @@ export function signalFieldRow(
201
277
  <sp-textfield
202
278
  size="s"
203
279
  value=${value}
204
- @input=${(/** @type {any} */ e) => {
280
+ @input=${(/** @type {Event} */ e) => {
205
281
  clearTimeout(debounce);
206
- debounce = setTimeout(() => onChange(e.target.value), 400);
282
+ debounce = setTimeout(
283
+ () => onChange(/** @type {HTMLInputElement} */ (e.target).value),
284
+ 400,
285
+ );
207
286
  }}
208
287
  ></sp-textfield>
209
288
  `,
@@ -211,22 +290,22 @@ export function signalFieldRow(
211
290
  }
212
291
 
213
292
  /** Normalize a parameter entry to a CEM object. */
214
- export function normParam(/** @type {any} */ p) {
215
- return typeof p === "string" ? { name: p } : p;
293
+ export function normParam(/** @type {string | Record<string, unknown>} */ p) {
294
+ return typeof p === "string" ? { name: p } : /** @type {CemParameter} */ (p);
216
295
  }
217
296
 
218
297
  // ─── Left panel: Signals ─────────────────────────────────────────────────────
219
298
 
220
299
  /**
221
- * @param {any} S
222
- * @param {any} ctx
300
+ * @param {SignalsPanelState} S
301
+ * @param {SignalsPanelCtx} ctx
223
302
  */
224
303
  export function renderSignalsTemplate(S, ctx) {
225
304
  const defs = S.document.state || {};
226
305
  const entries = Object.entries(defs);
227
306
 
228
307
  // Group by category
229
- const groups = /** @type {Record<string, any[]>} */ ({
308
+ const groups = /** @type {Record<string, [string, SignalDef][]>} */ ({
230
309
  state: [],
231
310
  computed: [],
232
311
  data: [],
@@ -259,7 +338,7 @@ export function renderSignalsTemplate(S, ctx) {
259
338
  }}
260
339
  >
261
340
  ${items.map(([name, def]) => {
262
- /** @type {any} */
341
+ /** @type {boolean} */
263
342
  const isExpanded = expandedSignal === name;
264
343
  return html`
265
344
  <div
@@ -276,7 +355,7 @@ export function renderSignalsTemplate(S, ctx) {
276
355
  quiet
277
356
  size="xs"
278
357
  class="signal-del"
279
- @click=${(/** @type {any} */ e) => {
358
+ @click=${(/** @type {Event} */ e) => {
280
359
  e.stopPropagation();
281
360
  transactDoc(activeTab.value, (t) => mutateRemoveDef(t, name));
282
361
  }}
@@ -304,8 +383,8 @@ export function renderSignalsTemplate(S, ctx) {
304
383
  size="s"
305
384
  label="+ Add…"
306
385
  placeholder="+ Add…"
307
- @change=${(/** @type {any} */ e) => {
308
- const type = e.target.value;
386
+ @change=${(/** @type {Event} */ e) => {
387
+ const type = /** @type {HTMLInputElement} */ (e.target).value;
309
388
  if (!type) return;
310
389
  const template = DEF_TEMPLATES[type];
311
390
  if (!template) return;
@@ -316,7 +395,13 @@ export function renderSignalsTemplate(S, ctx) {
316
395
  while (S.document.state && S.document.state[n]) {
317
396
  n = nameBase + i++;
318
397
  }
319
- transactDoc(activeTab.value, (t) => mutateAddDef(t, n, structuredClone(template)));
398
+ transactDoc(activeTab.value, (t) =>
399
+ mutateAddDef(
400
+ t,
401
+ n,
402
+ /** @type {Record<string, JsonValue>} */ (structuredClone(template)),
403
+ ),
404
+ );
320
405
  expandedSignal = n;
321
406
  ctx.renderLeftPanel();
322
407
  }}
@@ -343,10 +428,10 @@ export function renderSignalsTemplate(S, ctx) {
343
428
 
344
429
  /** Render inline editor fields for a specific signal/def type. */
345
430
  function renderSignalEditorTemplate(
346
- /** @type {any} */ S,
347
- /** @type {any} */ name,
348
- /** @type {any} */ def,
349
- /** @type {any} */ ctx,
431
+ /** @type {SignalsPanelState} */ S,
432
+ /** @type {string} */ name,
433
+ /** @type {SignalDef} */ def,
434
+ /** @type {SignalsPanelCtx} */ ctx,
350
435
  ) {
351
436
  if (typeof def !== "object" || def === null) {
352
437
  def = { default: def };
@@ -355,10 +440,10 @@ function renderSignalEditorTemplate(
355
440
 
356
441
  // Helper for picker rows
357
442
  const pickerRow = (
358
- /** @type {any} */ label,
359
- /** @type {any} */ options,
360
- /** @type {any} */ currentVal,
361
- /** @type {any} */ onChange,
443
+ /** @type {string} */ label,
444
+ /** @type {string[]} */ options,
445
+ /** @type {string} */ currentVal,
446
+ /** @type {(value: string) => void} */ onChange,
362
447
  ) => {
363
448
  return renderFieldRow({
364
449
  prop: label,
@@ -368,10 +453,11 @@ function renderSignalEditorTemplate(
368
453
  <sp-picker
369
454
  size="s"
370
455
  value=${currentVal}
371
- @change=${(/** @type {any} */ e) => onChange(e.target.value)}
456
+ @change=${(/** @type {Event} */ e) =>
457
+ onChange(/** @type {HTMLInputElement} */ (e.target).value)}
372
458
  >
373
459
  ${options.map(
374
- (/** @type {any} */ opt) => html`<sp-menu-item value=${opt}>${opt}</sp-menu-item>`,
460
+ (/** @type {string} */ opt) => html`<sp-menu-item value=${opt}>${opt}</sp-menu-item>`,
375
461
  )}
376
462
  </sp-picker>
377
463
  `,
@@ -380,12 +466,12 @@ function renderSignalEditorTemplate(
380
466
 
381
467
  // Helper for textarea rows
382
468
  const textareaRow = (
383
- /** @type {any} */ label,
384
- /** @type {any} */ value,
385
- /** @type {any} */ onChange,
386
- /** @type {any} */ opts = {},
469
+ /** @type {string} */ label,
470
+ /** @type {string} */ value,
471
+ /** @type {(value: string) => void} */ onChange,
472
+ /** @type {{ minHeight?: string; mono?: boolean }} */ opts = {},
387
473
  ) => {
388
- /** @type {any} */
474
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
389
475
  let debounce;
390
476
  return renderFieldRow({
391
477
  prop: label,
@@ -402,9 +488,12 @@ function renderSignalEditorTemplate(
402
488
  }),
403
489
  })}
404
490
  .value=${value}
405
- @input=${(/** @type {any} */ e) => {
491
+ @input=${(/** @type {Event} */ e) => {
406
492
  clearTimeout(debounce);
407
- debounce = setTimeout(() => onChange(e.target.value), 500);
493
+ debounce = setTimeout(
494
+ () => onChange(/** @type {HTMLInputElement} */ (e.target).value),
495
+ 500,
496
+ );
408
497
  }}
409
498
  ></textarea>
410
499
  `,
@@ -412,14 +501,14 @@ function renderSignalEditorTemplate(
412
501
  };
413
502
 
414
503
  // Name field (common to all)
415
- const nameField = signalFieldRow("Name", name, (/** @type {any} */ v) => {
504
+ const nameField = signalFieldRow("Name", name, (/** @type {string} */ v) => {
416
505
  if (v && v !== name && !(S.document.state && S.document.state[v])) {
417
506
  expandedSignal = v;
418
507
  transactDoc(activeTab.value, (t) => mutateRenameDef(t, name, v));
419
508
  }
420
509
  });
421
510
 
422
- /** @type {any} */
511
+ /** @type {import("lit-html").TemplateResult | typeof nothing} */
423
512
  let fields = nothing;
424
513
 
425
514
  if (cat === "state") {
@@ -432,7 +521,7 @@ function renderSignalEditorTemplate(
432
521
 
433
522
  const cemFields = isCustomElementDoc(S)
434
523
  ? html`
435
- ${signalFieldRow("Attribute", def.attribute || "", (/** @type {any} */ v) =>
524
+ ${signalFieldRow("Attribute", def.attribute || "", (/** @type {string} */ v) =>
436
525
  transactDoc(activeTab.value, (t) =>
437
526
  mutateUpdateDef(t, name, { attribute: v || undefined }),
438
527
  ),
@@ -445,9 +534,11 @@ function renderSignalEditorTemplate(
445
534
  <sp-checkbox
446
535
  class="field-check"
447
536
  ?checked=${!!def.reflects}
448
- @change=${(/** @type {any} */ e) =>
537
+ @change=${(/** @type {Event} */ e) =>
449
538
  transactDoc(activeTab.value, (t) =>
450
- mutateUpdateDef(t, name, { reflects: e.target.checked || undefined }),
539
+ mutateUpdateDef(t, name, {
540
+ reflects: /** @type {HTMLInputElement} */ (e.target).checked || undefined,
541
+ }),
451
542
  )}
452
543
  ></sp-checkbox>
453
544
  `,
@@ -455,7 +546,7 @@ function renderSignalEditorTemplate(
455
546
  ${signalFieldRow(
456
547
  "Deprecated",
457
548
  typeof def.deprecated === "string" ? def.deprecated : "",
458
- (/** @type {any} */ v) =>
549
+ (/** @type {string} */ v) =>
459
550
  transactDoc(activeTab.value, (t) =>
460
551
  mutateUpdateDef(t, name, { deprecated: v || undefined }),
461
552
  ),
@@ -468,7 +559,7 @@ function renderSignalEditorTemplate(
468
559
  "Type",
469
560
  ["string", "integer", "number", "boolean", "array", "object"],
470
561
  def.type || "string",
471
- (/** @type {any} */ v) =>
562
+ (/** @type {string} */ v) =>
472
563
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { type: v })),
473
564
  )}
474
565
  ${def.type === "string" || !def.type
@@ -476,7 +567,7 @@ function renderSignalEditorTemplate(
476
567
  "Format",
477
568
  ["", "image", "date", "color"],
478
569
  def.format || "",
479
- (/** @type {any} */ v) =>
570
+ (/** @type {string} */ v) =>
480
571
  transactDoc(activeTab.value, (t) =>
481
572
  mutateUpdateDef(t, name, { format: v || undefined }),
482
573
  ),
@@ -487,13 +578,14 @@ function renderSignalEditorTemplate(
487
578
  prop: "Default",
488
579
  label: "Default",
489
580
  hasValue: false,
490
- widget: renderMediaPicker("default", defaultVal, (/** @type {any} */ v) => {
581
+ widget: renderMediaPicker("default", defaultVal, (/** @type {string} */ v) => {
491
582
  transactDoc(activeTab.value, (t) =>
492
583
  mutateUpdateDef(t, name, { default: v || undefined }),
493
584
  );
494
585
  }),
495
586
  })
496
- : signalFieldRow("Default", defaultVal, (/** @type {any} */ v) => {
587
+ : signalFieldRow("Default", defaultVal, (/** @type {string} */ v) => {
588
+ /** @type {unknown} */
497
589
  let parsed = v;
498
590
  if (def.type === "integer") parsed = parseInt(v, 10) || 0;
499
591
  else if (def.type === "number") parsed = parseFloat(v) || 0;
@@ -505,9 +597,11 @@ function renderSignalEditorTemplate(
505
597
  parsed = v;
506
598
  }
507
599
  }
508
- transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { default: parsed }));
600
+ transactDoc(activeTab.value, (t) =>
601
+ mutateUpdateDef(t, name, { default: /** @type {JsonValue} */ (parsed) }),
602
+ );
509
603
  })}
510
- ${signalFieldRow("Description", def.description || "", (/** @type {any} */ v) =>
604
+ ${signalFieldRow("Description", def.description || "", (/** @type {string} */ v) =>
511
605
  transactDoc(activeTab.value, (t) =>
512
606
  mutateUpdateDef(t, name, { description: v || undefined }),
513
607
  ),
@@ -515,7 +609,7 @@ function renderSignalEditorTemplate(
515
609
  ${cemFields}
516
610
  `;
517
611
  } else if (cat === "computed") {
518
- /** @type {any} */
612
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
519
613
  let debounce;
520
614
  fields = html`
521
615
  ${renderFieldRow({
@@ -527,10 +621,10 @@ function renderSignalEditorTemplate(
527
621
  class="field-input"
528
622
  style="min-height:40px"
529
623
  .value=${def.$compute || ""}
530
- @input=${(/** @type {any} */ e) => {
624
+ @input=${(/** @type {Event} */ e) => {
531
625
  clearTimeout(debounce);
532
626
  debounce = setTimeout(() => {
533
- const expr = e.target.value;
627
+ const expr = /** @type {HTMLInputElement} */ (e.target).value;
534
628
  const depMatches = expr.match(/\$[a-zA-Z_]\w*/g) || [];
535
629
  const deps = [...new Set(depMatches)].map((d) => `#/state/${d}`);
536
630
  transactDoc(activeTab.value, (t) =>
@@ -549,7 +643,7 @@ function renderSignalEditorTemplate(
549
643
  widget: html`
550
644
  <span class="signal-hint" style="flex:1;max-width:none"
551
645
  >${def.$deps
552
- .map((/** @type {any} */ d) => d.replace("#/state/", ""))
646
+ .map((/** @type {string} */ d) => d.replace("#/state/", ""))
553
647
  .join(", ")}</span
554
648
  >
555
649
  `,
@@ -567,29 +661,33 @@ function renderSignalEditorTemplate(
567
661
 
568
662
  /** Data source fields for signal editor */
569
663
  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,
664
+ /** @type {SignalsPanelState} */ S,
665
+ /** @type {string} */ name,
666
+ /** @type {SignalDef} */ def,
667
+ /** @type {Function} */ textareaRow,
668
+ /** @type {Function} */ pickerRow,
669
+ /** @type {SignalsPanelCtx} */ ctx,
576
670
  ) {
577
671
  const proto = def.$prototype;
578
672
 
579
673
  if (proto === "Request") {
580
674
  return html`
581
- ${signalFieldRow("URL", def.url || "", (/** @type {any} */ v) =>
675
+ ${signalFieldRow("URL", def.url || "", (/** @type {string} */ v) =>
582
676
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { url: v })),
583
677
  )}
584
678
  ${pickerRow(
585
679
  "Method",
586
680
  ["GET", "POST", "PUT", "DELETE", "PATCH"],
587
681
  def.method || "GET",
588
- (/** @type {any} */ v) =>
682
+ (/** @type {string} */ v) =>
589
683
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { method: v })),
590
684
  )}
591
- ${pickerRow("Timing", ["client", "server"], def.timing || "client", (/** @type {any} */ v) =>
592
- transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { timing: v })),
685
+ ${pickerRow(
686
+ "Timing",
687
+ ["client", "server"],
688
+ def.timing || "client",
689
+ (/** @type {string} */ v) =>
690
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { timing: v })),
593
691
  )}
594
692
  `;
595
693
  }
@@ -601,10 +699,10 @@ function renderDataSourceFields(
601
699
  : String(def.default)
602
700
  : "";
603
701
  return html`
604
- ${signalFieldRow("Key", def.key || "", (/** @type {any} */ v) =>
702
+ ${signalFieldRow("Key", def.key || "", (/** @type {string} */ v) =>
605
703
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { key: v })),
606
704
  )}
607
- ${textareaRow("Default", defaultStr, (/** @type {any} */ v) => {
705
+ ${textareaRow("Default", defaultStr, (/** @type {string} */ v) => {
608
706
  try {
609
707
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { default: JSON.parse(v) }));
610
708
  } catch {
@@ -615,13 +713,13 @@ function renderDataSourceFields(
615
713
  }
616
714
  if (proto === "IndexedDB") {
617
715
  return html`
618
- ${signalFieldRow("Database", def.database || "", (/** @type {any} */ v) =>
716
+ ${signalFieldRow("Database", def.database || "", (/** @type {string} */ v) =>
619
717
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { database: v })),
620
718
  )}
621
- ${signalFieldRow("Store", def.store || "", (/** @type {any} */ v) =>
719
+ ${signalFieldRow("Store", def.store || "", (/** @type {string} */ v) =>
622
720
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { store: v })),
623
721
  )}
624
- ${signalFieldRow("Version", String(def.version || 1), (/** @type {any} */ v) =>
722
+ ${signalFieldRow("Version", String(def.version || 1), (/** @type {string} */ v) =>
625
723
  transactDoc(activeTab.value, (t) =>
626
724
  mutateUpdateDef(t, name, { version: parseInt(v, 10) || 1 }),
627
725
  ),
@@ -630,14 +728,17 @@ function renderDataSourceFields(
630
728
  }
631
729
  if (proto === "Cookie") {
632
730
  return html`
633
- ${signalFieldRow("Cookie", def.name || "", (/** @type {any} */ v) =>
731
+ ${signalFieldRow("Cookie", def.name || "", (/** @type {string} */ v) =>
634
732
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { name: v })),
635
733
  )}
636
- ${signalFieldRow("Default", def.default || "", (/** @type {any} */ v) =>
734
+ ${signalFieldRow("Default", String(def.default || ""), (/** @type {string} */ v) =>
637
735
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { default: v })),
638
736
  )}
639
737
  `;
640
738
  }
739
+ if (proto === "ContentEntry" || proto === "ContentCollection") {
740
+ return renderContentPrototypeFields(name, def, proto);
741
+ }
641
742
  if (proto === "Set" || proto === "Map" || proto === "FormData") {
642
743
  const fieldName = proto === "FormData" ? "fields" : "default";
643
744
  const fieldLabel = proto === "FormData" ? "Fields" : "Default";
@@ -647,7 +748,7 @@ function renderDataSourceFields(
647
748
  : proto === "FormData"
648
749
  ? JSON.stringify(def.fields || {}, null, 2)
649
750
  : "";
650
- return textareaRow(fieldLabel, defaultStr, (/** @type {any} */ v) => {
751
+ return textareaRow(fieldLabel, defaultStr, (/** @type {string} */ v) => {
651
752
  try {
652
753
  transactDoc(activeTab.value, (t) =>
653
754
  mutateUpdateDef(t, name, { [fieldName]: JSON.parse(v) }),
@@ -659,18 +760,129 @@ function renderDataSourceFields(
659
760
  return renderExternalPrototypeEditorTemplate(S, name, def, ctx);
660
761
  }
661
762
 
763
+ /** ContentEntry/ContentCollection fields for signal editor */
764
+ function renderContentPrototypeFields(
765
+ /** @type {string} */ name,
766
+ /** @type {SignalDef} */ def,
767
+ /** @type {string} */ proto,
768
+ ) {
769
+ const contentTypes = projectState?.projectConfig?.contentTypes;
770
+ const typeNames = contentTypes ? Object.keys(contentTypes) : [];
771
+ const currentType = def.contentType || "";
772
+ const schema = /** @type {ContentTypeSchema | undefined} */ (
773
+ currentType && contentTypes?.[currentType]?.schema
774
+ ) ||
775
+ undefined;
776
+
777
+ const contentTypeField = typeNames.length
778
+ ? html`
779
+ <div class="signal-field-row">
780
+ <label class="signal-field-label">Content Type</label>
781
+ <sp-picker
782
+ size="s"
783
+ value=${currentType}
784
+ style="width:100%"
785
+ @change=${(/** @type {Event} */ e) => {
786
+ const v = /** @type {HTMLSelectElement} */ (e.target).value;
787
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { contentType: v }));
788
+ }}
789
+ >
790
+ ${typeNames.map((t) => html`<sp-menu-item value=${t}>${t}</sp-menu-item>`)}
791
+ </sp-picker>
792
+ </div>
793
+ `
794
+ : signalFieldRow("Content Type", currentType, (/** @type {string} */ v) =>
795
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { contentType: v })),
796
+ );
797
+
798
+ /** @type {import("lit-html").TemplateResult | symbol} */
799
+ let entryFields = nothing;
800
+ if (proto === "ContentEntry") {
801
+ const idVal = def.id;
802
+ const idStr =
803
+ idVal && typeof idVal === "object" && /** @type {Record<string, unknown>} */ (idVal).$ref
804
+ ? /** @type {string} */ (/** @type {Record<string, unknown>} */ (idVal).$ref)
805
+ : typeof idVal === "string"
806
+ ? idVal
807
+ : "";
808
+ entryFields = signalFieldRow("ID", idStr, (/** @type {string} */ v) => {
809
+ const val = v.startsWith("#/") ? { $ref: v } : v;
810
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { id: val }));
811
+ });
812
+ } else {
813
+ const filterStr = def.filter ? JSON.stringify(def.filter) : "";
814
+ const sortStr = def.sort ? `${def.sort.field || ""} ${def.sort.order || ""}`.trim() : "";
815
+ const limitStr = def.limit != null ? String(def.limit) : "";
816
+ entryFields = html`
817
+ ${signalFieldRow("Filter", filterStr, (/** @type {string} */ v) => {
818
+ try {
819
+ const parsed = v ? JSON.parse(v) : undefined;
820
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { filter: parsed }));
821
+ } catch {}
822
+ })}
823
+ ${signalFieldRow("Sort", sortStr, (/** @type {string} */ v) => {
824
+ if (!v) {
825
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { sort: undefined }));
826
+ return;
827
+ }
828
+ const [field, order] = v.split(" ");
829
+ transactDoc(activeTab.value, (t) =>
830
+ mutateUpdateDef(t, name, { sort: { field, order: order || "asc" } }),
831
+ );
832
+ })}
833
+ ${signalFieldRow("Limit", limitStr, (/** @type {string} */ v) => {
834
+ const num = v ? parseInt(v, 10) : undefined;
835
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { limit: num }));
836
+ })}
837
+ `;
838
+ }
839
+
840
+ /** @type {import("lit-html").TemplateResult | symbol} */
841
+ let schemaFields = nothing;
842
+ if (schema?.properties) {
843
+ const fields = Object.entries(schema.properties);
844
+ const required = new Set(schema.required || []);
845
+ schemaFields = html`
846
+ <div style="margin-top:8px;padding:6px 0;border-top:1px solid var(--border-dim)">
847
+ <div style="font-size:11px;color:var(--fg-dim);margin-bottom:4px;font-weight:500">
848
+ Fields (${currentType})
849
+ </div>
850
+ ${fields.map(
851
+ ([field, fieldDef]) => html`
852
+ <div style="font-size:11px;padding:2px 0;display:flex;gap:4px;align-items:center">
853
+ <code style="color:var(--fg-default)">${field}</code>
854
+ <span style="color:var(--fg-dim)">${/** @type {any} */ (fieldDef).type || ""}</span>
855
+ ${required.has(field)
856
+ ? html`<span style="color:var(--color-accent,#f36f32);font-size:9px">req</span>`
857
+ : nothing}
858
+ </div>
859
+ `,
860
+ )}
861
+ </div>
862
+ `;
863
+ }
864
+
865
+ return html`
866
+ ${contentTypeField}
867
+ ${signalFieldRow("Prototype", proto, (/** @type {string} */ v) =>
868
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { $prototype: v })),
869
+ )}
870
+ ${entryFields} ${schemaFields}
871
+ `;
872
+ }
873
+
662
874
  /** Function fields for signal editor */
663
875
  function renderFunctionFields(
664
- /** @type {any} */ S,
665
- /** @type {any} */ name,
666
- /** @type {any} */ def,
667
- /** @type {any} */ textareaRow,
668
- /** @type {any} */ ctx,
876
+ /** @type {SignalsPanelState} */ S,
877
+ /** @type {string} */ name,
878
+ /** @type {SignalDef} */ def,
879
+ /** @type {Function} */ textareaRow,
880
+ /** @type {SignalsPanelCtx} */ ctx,
669
881
  ) {
670
882
  const descriptionField = signalFieldRow(
671
883
  "Description",
672
884
  def.description || "",
673
- (/** @type {any} */ v) =>
885
+ (/** @type {string} */ v) =>
674
886
  transactDoc(activeTab.value, (t) =>
675
887
  mutateUpdateDef(t, name, { description: v || undefined }),
676
888
  ),
@@ -678,10 +890,10 @@ function renderFunctionFields(
678
890
 
679
891
  const bodyField = def.$src
680
892
  ? html`
681
- ${signalFieldRow("Source", def.$src || "", (/** @type {any} */ v) =>
893
+ ${signalFieldRow("Source", def.$src || "", (/** @type {string} */ v) =>
682
894
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { $src: v || undefined })),
683
895
  )}
684
- ${signalFieldRow("Export", def.$export || "", (/** @type {any} */ v) =>
896
+ ${signalFieldRow("Export", def.$export || "", (/** @type {string} */ v) =>
685
897
  transactDoc(activeTab.value, (t) =>
686
898
  mutateUpdateDef(t, name, { $export: v || undefined }),
687
899
  ),
@@ -706,8 +918,8 @@ function renderFunctionFields(
706
918
  class="field-input"
707
919
  style="min-height:60px;font-family:monospace;font-size:11px"
708
920
  .value=${def.body || ""}
709
- @input=${(/** @type {any} */ e) => {
710
- const v = e.target.value;
921
+ @input=${(/** @type {Event} */ e) => {
922
+ const v = /** @type {HTMLInputElement} */ (e.target).value;
711
923
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { body: v }));
712
924
  }}
713
925
  ></textarea>
@@ -723,10 +935,10 @@ function renderFunctionFields(
723
935
 
724
936
  /** Render CEM parameter editor with basic/advanced toggle. */
725
937
  function renderParameterEditorTemplate(
726
- /** @type {any} */ S,
727
- /** @type {any} */ name,
728
- /** @type {any} */ def,
729
- /** @type {any} */ ctx,
938
+ /** @type {SignalsPanelState} */ S,
939
+ /** @type {string} */ name,
940
+ /** @type {SignalDef} */ def,
941
+ /** @type {SignalsPanelCtx} */ ctx,
730
942
  ) {
731
943
  const params = (def.parameters || []).map(normParam);
732
944
  const isAdvanced = advancedParamOpen.has(name);
@@ -740,7 +952,7 @@ function renderParameterEditorTemplate(
740
952
  widget: html`
741
953
  <div style="display:flex;flex-wrap:wrap;gap:4px;align-items:center">
742
954
  ${params.map(
743
- (/** @type {any} */ p, /** @type {any} */ i) => html`
955
+ (/** @type {CemParameter} */ p, /** @type {number} */ i) => html`
744
956
  <span
745
957
  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
958
  >
@@ -751,9 +963,11 @@ function renderParameterEditorTemplate(
751
963
  transactDoc(activeTab.value, (t) =>
752
964
  mutateUpdateDef(t, name, {
753
965
  parameters: params.filter(
754
- (/** @type {any} */ _, /** @type {any} */ j) => j !== i,
966
+ (/** @type {unknown} */ _, /** @type {number} */ j) => j !== i,
755
967
  ).length
756
- ? params.filter((/** @type {any} */ _, /** @type {any} */ j) => j !== i)
968
+ ? params.filter(
969
+ (/** @type {unknown} */ _, /** @type {number} */ j) => j !== i,
970
+ )
757
971
  : undefined,
758
972
  }),
759
973
  );
@@ -767,11 +981,14 @@ function renderParameterEditorTemplate(
767
981
  class="field-input"
768
982
  style="width:60px;flex:0 0 auto;font-size:11px"
769
983
  placeholder="+"
770
- @keydown=${(/** @type {any} */ e) => {
771
- if (e.key === "Enter" && e.target.value.trim()) {
984
+ @keydown=${(/** @type {KeyboardEvent} */ e) => {
985
+ if (e.key === "Enter" && /** @type {HTMLInputElement} */ (e.target).value.trim()) {
772
986
  transactDoc(activeTab.value, (t) =>
773
987
  mutateUpdateDef(t, name, {
774
- parameters: [...params, { name: e.target.value.trim() }],
988
+ parameters: [
989
+ ...params,
990
+ { name: /** @type {HTMLInputElement} */ (e.target).value.trim() },
991
+ ],
775
992
  }),
776
993
  );
777
994
  }
@@ -798,16 +1015,16 @@ function renderParameterEditorTemplate(
798
1015
  widget: html`
799
1016
  <div style="display:flex;flex-direction:column;gap:4px">
800
1017
  ${params.map(
801
- (/** @type {any} */ p, /** @type {any} */ i) => html`
1018
+ (/** @type {CemParameter} */ p, /** @type {number} */ i) => html`
802
1019
  <div style="display:flex;gap:4px;align-items:center">
803
1020
  <input
804
1021
  class="field-input"
805
1022
  .value=${p.name || ""}
806
1023
  placeholder="name"
807
1024
  style="flex:1"
808
- @change=${(/** @type {any} */ e) => {
1025
+ @change=${(/** @type {Event} */ e) => {
809
1026
  const next = [...params];
810
- next[i] = { ...next[i], name: e.target.value };
1027
+ next[i] = { ...next[i], name: /** @type {HTMLInputElement} */ (e.target).value };
811
1028
  transactDoc(activeTab.value, (t) =>
812
1029
  mutateUpdateDef(t, name, { parameters: next }),
813
1030
  );
@@ -818,11 +1035,13 @@ function renderParameterEditorTemplate(
818
1035
  .value=${p.type?.text || ""}
819
1036
  placeholder="type"
820
1037
  style="flex:1"
821
- @change=${(/** @type {any} */ e) => {
1038
+ @change=${(/** @type {Event} */ e) => {
822
1039
  const next = [...params];
823
1040
  next[i] = {
824
1041
  ...next[i],
825
- type: e.target.value ? { text: e.target.value } : undefined,
1042
+ type: /** @type {HTMLInputElement} */ (e.target).value
1043
+ ? { text: /** @type {HTMLInputElement} */ (e.target).value }
1044
+ : undefined,
826
1045
  };
827
1046
  transactDoc(activeTab.value, (t) =>
828
1047
  mutateUpdateDef(t, name, { parameters: next }),
@@ -834,9 +1053,12 @@ function renderParameterEditorTemplate(
834
1053
  .value=${p.description || ""}
835
1054
  placeholder="desc"
836
1055
  style="flex:2"
837
- @change=${(/** @type {any} */ e) => {
1056
+ @change=${(/** @type {Event} */ e) => {
838
1057
  const next = [...params];
839
- next[i] = { ...next[i], description: e.target.value || undefined };
1058
+ next[i] = {
1059
+ ...next[i],
1060
+ description: /** @type {HTMLInputElement} */ (e.target).value || undefined,
1061
+ };
840
1062
  transactDoc(activeTab.value, (t) =>
841
1063
  mutateUpdateDef(t, name, { parameters: next }),
842
1064
  );
@@ -846,9 +1068,12 @@ function renderParameterEditorTemplate(
846
1068
  type="checkbox"
847
1069
  title="optional"
848
1070
  .checked=${!!p.optional}
849
- @change=${(/** @type {any} */ e) => {
1071
+ @change=${(/** @type {Event} */ e) => {
850
1072
  const next = [...params];
851
- next[i] = { ...next[i], optional: e.target.checked || undefined };
1073
+ next[i] = {
1074
+ ...next[i],
1075
+ optional: /** @type {HTMLInputElement} */ (e.target).checked || undefined,
1076
+ };
852
1077
  transactDoc(activeTab.value, (t) =>
853
1078
  mutateUpdateDef(t, name, { parameters: next }),
854
1079
  );
@@ -858,7 +1083,7 @@ function renderParameterEditorTemplate(
858
1083
  style="cursor:pointer;opacity:0.5"
859
1084
  @click=${() => {
860
1085
  const next = params.filter(
861
- (/** @type {any} */ _, /** @type {any} */ j) => j !== i,
1086
+ (/** @type {unknown} */ _, /** @type {number} */ j) => j !== i,
862
1087
  );
863
1088
  transactDoc(activeTab.value, (t) =>
864
1089
  mutateUpdateDef(t, name, { parameters: next.length ? next : undefined }),
@@ -893,11 +1118,11 @@ function renderParameterEditorTemplate(
893
1118
 
894
1119
  /** Render CEM emits editor for function state entries. */
895
1120
  function renderEmitsEditorTemplate(
896
- /** @type {any} */ S,
897
- /** @type {any} */ name,
898
- /** @type {any} */ def,
1121
+ /** @type {SignalsPanelState} */ S,
1122
+ /** @type {string} */ name,
1123
+ /** @type {SignalDef} */ def,
899
1124
  ) {
900
- const emits = def.emits || [];
1125
+ const emits = /** @type {CemEvent[]} */ (def.emits || []);
901
1126
  if (emits.length === 0 && !isCustomElementDoc(S)) return nothing;
902
1127
 
903
1128
  return html`
@@ -907,16 +1132,16 @@ function renderEmitsEditorTemplate(
907
1132
  Emits
908
1133
  </div>
909
1134
  ${emits.map(
910
- (/** @type {any} */ ev, /** @type {any} */ i) => html`
1135
+ (/** @type {CemEvent} */ ev, /** @type {number} */ i) => html`
911
1136
  <div style="display:flex;gap:4px;align-items:center;margin-bottom:4px">
912
1137
  <input
913
1138
  class="field-input"
914
1139
  .value=${ev.name || ""}
915
1140
  placeholder="event name"
916
1141
  style="flex:1"
917
- @change=${(/** @type {any} */ e) => {
1142
+ @change=${(/** @type {Event} */ e) => {
918
1143
  const next = [...emits];
919
- next[i] = { ...next[i], name: e.target.value };
1144
+ next[i] = { ...next[i], name: /** @type {HTMLInputElement} */ (e.target).value };
920
1145
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { emits: next }));
921
1146
  }}
922
1147
  />
@@ -925,9 +1150,14 @@ function renderEmitsEditorTemplate(
925
1150
  .value=${ev.type?.text || ""}
926
1151
  placeholder="type"
927
1152
  style="flex:1"
928
- @change=${(/** @type {any} */ e) => {
1153
+ @change=${(/** @type {Event} */ e) => {
929
1154
  const next = [...emits];
930
- next[i] = { ...next[i], type: e.target.value ? { text: e.target.value } : undefined };
1155
+ next[i] = {
1156
+ ...next[i],
1157
+ type: /** @type {HTMLInputElement} */ (e.target).value
1158
+ ? { text: /** @type {HTMLInputElement} */ (e.target).value }
1159
+ : undefined,
1160
+ };
931
1161
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { emits: next }));
932
1162
  }}
933
1163
  />
@@ -936,9 +1166,12 @@ function renderEmitsEditorTemplate(
936
1166
  .value=${ev.description || ""}
937
1167
  placeholder="description"
938
1168
  style="flex:2"
939
- @change=${(/** @type {any} */ e) => {
1169
+ @change=${(/** @type {Event} */ e) => {
940
1170
  const next = [...emits];
941
- next[i] = { ...next[i], description: e.target.value || undefined };
1171
+ next[i] = {
1172
+ ...next[i],
1173
+ description: /** @type {HTMLInputElement} */ (e.target).value || undefined,
1174
+ };
942
1175
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { emits: next }));
943
1176
  }}
944
1177
  />
@@ -947,9 +1180,10 @@ function renderEmitsEditorTemplate(
947
1180
  @click=${() => {
948
1181
  transactDoc(activeTab.value, (t) =>
949
1182
  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)
1183
+ emits: emits.filter(
1184
+ (/** @type {unknown} */ _, /** @type {number} */ j) => j !== i,
1185
+ ).length
1186
+ ? emits.filter((/** @type {unknown} */ _, /** @type {number} */ j) => j !== i)
953
1187
  : undefined,
954
1188
  }),
955
1189
  );
@@ -978,10 +1212,10 @@ function renderEmitsEditorTemplate(
978
1212
  * appropriate form controls.
979
1213
  */
980
1214
  export function renderSchemaFieldsTemplate(
981
- /** @type {any} */ schema,
982
- /** @type {any} */ def,
983
- /** @type {any} */ name,
984
- /** @type {any} */ _S,
1215
+ /** @type {JsonSchema | null | undefined} */ schema,
1216
+ /** @type {SignalDef} */ def,
1217
+ /** @type {string} */ name,
1218
+ /** @type {SignalsPanelState} */ _S,
985
1219
  ) {
986
1220
  if (!schema?.properties) return nothing;
987
1221
 
@@ -1003,29 +1237,33 @@ export function renderSchemaFieldsTemplate(
1003
1237
  : ps.default !== undefined
1004
1238
  ? String(ps.default)
1005
1239
  : "__none__"}
1006
- @change=${(/** @type {any} */ e) =>
1240
+ @change=${(/** @type {Event} */ e) =>
1007
1241
  transactDoc(activeTab.value, (t) =>
1008
1242
  mutateUpdateDef(t, name, {
1009
- [prop]: e.target.value === "__none__" ? undefined : e.target.value,
1243
+ [prop]: /** @type {HTMLInputElement} */ (e.target).value === "__none__"
1244
+ ? undefined
1245
+ : /** @type {HTMLInputElement} */ (e.target).value,
1010
1246
  }),
1011
1247
  )}
1012
1248
  >
1013
1249
  ${!required.has(prop) ? html`<sp-menu-item value="__none__">—</sp-menu-item>` : nothing}
1014
1250
  ${ps.enum.map(
1015
- (/** @type {any} */ val) => html`<sp-menu-item value=${val}>${val}</sp-menu-item>`,
1251
+ (/** @type {string} */ val) => html`<sp-menu-item value=${val}>${val}</sp-menu-item>`,
1016
1252
  )}
1017
1253
  </sp-picker>
1018
1254
  `;
1019
1255
  } else if (ps.type === "boolean") {
1020
1256
  control = html`<sp-checkbox
1021
1257
  ?checked=${currentValue ?? ps.default ?? false}
1022
- @change=${(/** @type {any} */ e) =>
1258
+ @change=${(/** @type {Event} */ e) =>
1023
1259
  transactDoc(activeTab.value, (t) =>
1024
- mutateUpdateDef(t, name, { [prop]: e.target.checked }),
1260
+ mutateUpdateDef(t, name, {
1261
+ [prop]: /** @type {HTMLInputElement} */ (e.target).checked,
1262
+ }),
1025
1263
  )}
1026
1264
  ></sp-checkbox>`;
1027
1265
  } else if (ps.type === "integer" || ps.type === "number") {
1028
- /** @type {any} */
1266
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
1029
1267
  let debounce;
1030
1268
  control = html`<sp-number-field
1031
1269
  size="s"
@@ -1034,11 +1272,13 @@ export function renderSchemaFieldsTemplate(
1034
1272
  step=${ps.type === "integer" ? "1" : nothing}
1035
1273
  .value=${currentValue !== undefined ? currentValue : nothing}
1036
1274
  placeholder=${ps.default !== undefined ? String(ps.default) : nothing}
1037
- @change=${(/** @type {any} */ e) => {
1275
+ @change=${(/** @type {Event} */ e) => {
1038
1276
  clearTimeout(debounce);
1039
1277
  debounce = setTimeout(() => {
1040
1278
  const parsed =
1041
- ps.type === "integer" ? parseInt(e.target.value, 10) : parseFloat(e.target.value);
1279
+ ps.type === "integer"
1280
+ ? parseInt(/** @type {HTMLInputElement} */ (e.target).value, 10)
1281
+ : parseFloat(/** @type {HTMLInputElement} */ (e.target).value);
1042
1282
  transactDoc(activeTab.value, (t) =>
1043
1283
  mutateUpdateDef(t, name, { [prop]: isNaN(parsed) ? undefined : parsed }),
1044
1284
  );
@@ -1048,15 +1288,18 @@ export function renderSchemaFieldsTemplate(
1048
1288
  } else if (ps.format === "json-schema") {
1049
1289
  const hasValue =
1050
1290
  currentValue && typeof currentValue === "object" && Object.keys(currentValue).length > 0;
1051
- const isRef = currentValue && typeof currentValue === "object" && currentValue.$ref;
1052
- /** @type {any} */
1291
+ const cv = /** @type {Record<string, unknown>} */ (currentValue);
1292
+ const isRef = hasValue && cv.$ref;
1293
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
1053
1294
  let debounce;
1054
1295
  control = html`
1055
1296
  <div class="schema-param-editor">
1056
- ${hasValue && !isRef && currentValue.properties
1297
+ ${hasValue && !isRef && cv.properties
1057
1298
  ? html`
1058
1299
  <div style="display:flex;flex-wrap:wrap;gap:3px;margin-bottom:4px">
1059
- ${Object.entries(currentValue.properties).map(
1300
+ ${Object.entries(
1301
+ /** @type {Record<string, Record<string, unknown>>} */ (cv.properties),
1302
+ ).map(
1060
1303
  ([k, v]) => html`
1061
1304
  <span
1062
1305
  style="background:var(--bg-alt);padding:1px 6px;border-radius:3px;font-size:10px;color:var(--fg-dim)"
@@ -1077,12 +1320,14 @@ export function renderSchemaFieldsTemplate(
1077
1320
  })}
1078
1321
  .value=${currentValue !== undefined ? JSON.stringify(currentValue, null, 2) : ""}
1079
1322
  placeholder=${ps.description ?? "JSON Schema defining the data shape\u2026"}
1080
- @input=${(/** @type {any} */ e) => {
1323
+ @input=${(/** @type {Event} */ e) => {
1081
1324
  clearTimeout(debounce);
1082
1325
  debounce = setTimeout(() => {
1083
1326
  try {
1084
1327
  transactDoc(activeTab.value, (t) =>
1085
- mutateUpdateDef(t, name, { [prop]: JSON.parse(e.target.value) }),
1328
+ mutateUpdateDef(t, name, {
1329
+ [prop]: JSON.parse(/** @type {HTMLInputElement} */ (e.target).value),
1330
+ }),
1086
1331
  );
1087
1332
  } catch {}
1088
1333
  }, 500);
@@ -1091,7 +1336,7 @@ export function renderSchemaFieldsTemplate(
1091
1336
  </div>
1092
1337
  `;
1093
1338
  } else if (ps.type === "array" || ps.type === "object") {
1094
- /** @type {any} */
1339
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
1095
1340
  let debounce;
1096
1341
  control = html`<sp-textfield
1097
1342
  multiline
@@ -1099,19 +1344,21 @@ export function renderSchemaFieldsTemplate(
1099
1344
  style="min-height:40px"
1100
1345
  .value=${currentValue !== undefined ? JSON.stringify(currentValue, null, 2) : ""}
1101
1346
  placeholder=${ps.default !== undefined ? JSON.stringify(ps.default) : nothing}
1102
- @input=${(/** @type {any} */ e) => {
1347
+ @input=${(/** @type {Event} */ e) => {
1103
1348
  clearTimeout(debounce);
1104
1349
  debounce = setTimeout(() => {
1105
1350
  try {
1106
1351
  transactDoc(activeTab.value, (t) =>
1107
- mutateUpdateDef(t, name, { [prop]: JSON.parse(e.target.value) }),
1352
+ mutateUpdateDef(t, name, {
1353
+ [prop]: JSON.parse(/** @type {HTMLInputElement} */ (e.target).value),
1354
+ }),
1108
1355
  );
1109
1356
  } catch {}
1110
1357
  }, 500);
1111
1358
  }}
1112
1359
  ></sp-textfield>`;
1113
1360
  } else {
1114
- /** @type {any} */
1361
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
1115
1362
  let debounce;
1116
1363
  const ph = ps.default !== undefined ? String(ps.default) : (ps.examples?.[0] ?? "");
1117
1364
  control = html`<sp-textfield
@@ -1119,12 +1366,14 @@ export function renderSchemaFieldsTemplate(
1119
1366
  .value=${currentValue ?? ""}
1120
1367
  placeholder=${ph || nothing}
1121
1368
  title=${ps.description || nothing}
1122
- @input=${(/** @type {any} */ e) => {
1369
+ @input=${(/** @type {Event} */ e) => {
1123
1370
  clearTimeout(debounce);
1124
1371
  debounce = setTimeout(
1125
1372
  () =>
1126
1373
  transactDoc(activeTab.value, (t) =>
1127
- mutateUpdateDef(t, name, { [prop]: e.target.value || undefined }),
1374
+ mutateUpdateDef(t, name, {
1375
+ [prop]: /** @type {HTMLInputElement} */ (e.target).value || undefined,
1376
+ }),
1128
1377
  ),
1129
1378
  400,
1130
1379
  );
@@ -1133,7 +1382,7 @@ export function renderSchemaFieldsTemplate(
1133
1382
  }
1134
1383
 
1135
1384
  return renderFieldRow({
1136
- prop: ps.name,
1385
+ prop: ps.name || prop,
1137
1386
  label: labelText,
1138
1387
  hasValue: false,
1139
1388
  widget: control,
@@ -1146,13 +1395,13 @@ export function renderSchemaFieldsTemplate(
1146
1395
  * schema-driven config fields.
1147
1396
  */
1148
1397
  export function renderExternalPrototypeEditorTemplate(
1149
- /** @type {any} */ S,
1150
- /** @type {any} */ name,
1151
- /** @type {any} */ def,
1152
- /** @type {any} */ ctx,
1398
+ /** @type {SignalsPanelState} */ S,
1399
+ /** @type {string} */ name,
1400
+ /** @type {SignalDef} */ def,
1401
+ /** @type {SignalsPanelCtx} */ ctx,
1153
1402
  ) {
1154
1403
  // Schema-driven config fields (async with cache)
1155
- /** @type {any} */
1404
+ /** @type {import("lit-html").TemplateResult | typeof nothing} */
1156
1405
  let schemaContent = nothing;
1157
1406
  if (def.$src && def.$prototype) {
1158
1407
  const cacheKey = `${def.$src}::${def.$prototype}`;
@@ -1173,23 +1422,23 @@ export function renderExternalPrototypeEditorTemplate(
1173
1422
  >
1174
1423
  Loading schema…
1175
1424
  </div>`;
1176
- fetchPluginSchema(def, S).then((schema) => {
1425
+ fetchPluginSchema(def, /** @type {{ documentPath?: string }} */ (S)).then((schema) => {
1177
1426
  if (schema) ctx.renderLeftPanel();
1178
1427
  });
1179
1428
  }
1180
1429
  }
1181
1430
 
1182
1431
  return html`
1183
- ${signalFieldRow("Source", def.$src || "", (/** @type {any} */ v) => {
1432
+ ${signalFieldRow("Source", def.$src || "", (/** @type {string} */ v) => {
1184
1433
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { $src: v || undefined }));
1185
1434
  pluginSchemaCache.delete(`${v}::${def.$prototype}`);
1186
1435
  })}
1187
- ${signalFieldRow("Prototype", def.$prototype || "", (/** @type {any} */ v) => {
1436
+ ${signalFieldRow("Prototype", def.$prototype || "", (/** @type {string} */ v) => {
1188
1437
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { $prototype: v || undefined }));
1189
1438
  pluginSchemaCache.delete(`${def.$src}::${v}`);
1190
1439
  })}
1191
1440
  ${def.$export
1192
- ? signalFieldRow("Export", def.$export || "", (/** @type {any} */ v) =>
1441
+ ? signalFieldRow("Export", def.$export || "", (/** @type {string} */ v) =>
1193
1442
  transactDoc(activeTab.value, (t) =>
1194
1443
  mutateUpdateDef(t, name, { $export: v || undefined }),
1195
1444
  ),