@jxsuite/studio 0.28.4 → 0.29.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 (81) hide show
  1. package/dist/studio.js +6769 -5399
  2. package/dist/studio.js.map +86 -79
  3. package/package.json +5 -4
  4. package/src/browse/browse-modal.ts +2 -2
  5. package/src/browse/browse.ts +20 -19
  6. package/src/canvas/canvas-diff.ts +3 -3
  7. package/src/canvas/canvas-helpers.ts +15 -2
  8. package/src/canvas/canvas-live-render.ts +168 -86
  9. package/src/canvas/canvas-patcher.ts +656 -0
  10. package/src/canvas/canvas-perf.ts +68 -0
  11. package/src/canvas/canvas-render.ts +135 -23
  12. package/src/canvas/canvas-subtree-render.ts +113 -0
  13. package/src/canvas/canvas-utils.ts +4 -0
  14. package/src/editor/component-inline-edit.ts +4 -35
  15. package/src/editor/context-menu.ts +92 -74
  16. package/src/editor/convert-to-component.ts +11 -2
  17. package/src/editor/convert-to-repeater.ts +4 -5
  18. package/src/editor/inline-edit.ts +9 -9
  19. package/src/editor/inline-format.ts +11 -11
  20. package/src/editor/merge-tags.ts +120 -0
  21. package/src/editor/shortcuts.ts +4 -4
  22. package/src/files/components.ts +37 -0
  23. package/src/files/file-ops.ts +28 -7
  24. package/src/files/files.ts +25 -21
  25. package/src/github/github-auth.ts +14 -2
  26. package/src/github/github-publish.ts +12 -2
  27. package/src/panels/activity-bar.ts +1 -1
  28. package/src/panels/ai-panel.ts +67 -39
  29. package/src/panels/block-action-bar.ts +72 -1
  30. package/src/panels/canvas-dnd.ts +55 -26
  31. package/src/panels/data-explorer.ts +5 -3
  32. package/src/panels/dnd.ts +12 -15
  33. package/src/panels/editors.ts +5 -23
  34. package/src/panels/elements-panel.ts +2 -12
  35. package/src/panels/events-panel.ts +1 -1
  36. package/src/panels/git-panel.ts +6 -6
  37. package/src/panels/head-panel.ts +1 -1
  38. package/src/panels/layers-panel.ts +177 -147
  39. package/src/panels/preview-render.ts +15 -0
  40. package/src/panels/properties-panel.ts +16 -27
  41. package/src/panels/quick-search.ts +2 -2
  42. package/src/panels/right-panel.ts +2 -6
  43. package/src/panels/shared.ts +3 -0
  44. package/src/panels/signals-panel.ts +24 -22
  45. package/src/panels/statusbar.ts +15 -6
  46. package/src/panels/style-panel.ts +3 -3
  47. package/src/panels/style-utils.ts +3 -3
  48. package/src/panels/stylebook-panel.ts +4 -4
  49. package/src/panels/tab-bar.ts +198 -0
  50. package/src/panels/tab-strip.ts +2 -2
  51. package/src/panels/toolbar.ts +6 -75
  52. package/src/platforms/devserver.ts +60 -34
  53. package/src/reactivity.ts +2 -0
  54. package/src/services/cem-export.ts +23 -2
  55. package/src/services/code-services.ts +16 -2
  56. package/src/services/monaco-setup.ts +2 -1
  57. package/src/settings/content-types-editor.ts +16 -16
  58. package/src/settings/css-vars-editor.ts +2 -2
  59. package/src/settings/defs-editor.ts +14 -14
  60. package/src/settings/general-settings.ts +6 -6
  61. package/src/settings/head-editor.ts +2 -2
  62. package/src/site-context.ts +1 -1
  63. package/src/state.ts +66 -12
  64. package/src/store.ts +15 -3
  65. package/src/studio.ts +73 -33
  66. package/src/tabs/patch-ops.ts +143 -0
  67. package/src/tabs/tab.ts +15 -1
  68. package/src/tabs/transact.ts +454 -27
  69. package/src/types.ts +41 -0
  70. package/src/ui/color-selector.ts +7 -7
  71. package/src/ui/icons.ts +0 -30
  72. package/src/ui/media-picker.ts +2 -2
  73. package/src/ui/panel-resize.ts +6 -1
  74. package/src/ui/unit-selector.ts +2 -2
  75. package/src/ui/value-selector.ts +3 -3
  76. package/src/utils/canvas-media.ts +6 -6
  77. package/src/utils/edit-display.ts +125 -83
  78. package/src/utils/google-fonts.ts +1 -1
  79. package/src/utils/inherited-style.ts +3 -2
  80. package/src/utils/studio-utils.ts +1 -1
  81. package/src/view.ts +4 -1
@@ -8,6 +8,26 @@
8
8
  * See spec/desktop.md §8 for the full specification.
9
9
  */
10
10
 
11
+ import type { ProjectConfig } from "@jxsuite/schema/types";
12
+ import type { DirEntry } from "../types";
13
+
14
+ /** A directory entry from the server, tolerating extra wire fields. */
15
+ type WireDirEntry = DirEntry & Record<string, unknown>;
16
+
17
+ /** Parse a fetch Response body as JSON, asserting the expected shape at the boundary. */
18
+ async function readJson<T>(res: Response): Promise<T> {
19
+ return (await res.json()) as T;
20
+ }
21
+
22
+ interface ErrorBody {
23
+ error?: string;
24
+ }
25
+
26
+ interface SiteEntry {
27
+ config: unknown;
28
+ path: string;
29
+ }
30
+
11
31
  /**
12
32
  * Create a DevServerPlatform instance.
13
33
  *
@@ -58,7 +78,7 @@ export function createDevServerPlatform() {
58
78
  set projectRoot(v) {
59
79
  _projectRoot = v || "";
60
80
  if (_projectRoot) {
61
- this.activate(_projectRoot);
81
+ void this.activate(_projectRoot);
62
82
  }
63
83
  },
64
84
 
@@ -109,19 +129,16 @@ export function createDevServerPlatform() {
109
129
  }
110
130
 
111
131
  const file = await siteHandle.getFile();
112
- const config = JSON.parse(await file.text());
132
+ const config = JSON.parse(await file.text()) as ProjectConfig;
113
133
 
114
134
  // Resolve server-relative path by matching against known sites
115
135
  const sitesRes = await fetch("/__studio/sites");
116
136
  if (!sitesRes.ok) {
117
137
  throw new Error("Failed to fetch site list from server");
118
138
  }
119
- const sites = await sitesRes.json();
139
+ const sites = await readJson<SiteEntry[]>(sitesRes);
120
140
  const match = sites.find(
121
- /** @param {{ config: unknown; path: string }} s */ (s: {
122
- config: unknown;
123
- path: string;
124
- }) => JSON.stringify(s.config) === JSON.stringify(config),
141
+ (s: SiteEntry) => JSON.stringify(s.config) === JSON.stringify(config),
125
142
  );
126
143
 
127
144
  if (!match) {
@@ -132,7 +149,7 @@ export function createDevServerPlatform() {
132
149
  if (!findRes.ok) {
133
150
  throw new Error("Could not locate project on disk");
134
151
  }
135
- const found = await findRes.json();
152
+ const found = await readJson<{ path?: string }>(findRes);
136
153
  if (!found.path) {
137
154
  throw new Error(`Could not find project directory "${dirHandle.name}"`);
138
155
  }
@@ -147,7 +164,7 @@ export function createDevServerPlatform() {
147
164
  return {
148
165
  config,
149
166
  handle: {
150
- name: config.name || _projectRoot.split("/").pop(),
167
+ name: config.name || _projectRoot.split("/").pop()!,
151
168
  projectConfig: config,
152
169
  root: _projectRoot,
153
170
  },
@@ -164,8 +181,17 @@ export function createDevServerPlatform() {
164
181
  fetch("/__studio/project"),
165
182
  fetch("/__studio/project-info?dir=."),
166
183
  ]);
167
- const meta = projectRes.ok ? await projectRes.json() : { name: "project", root: "." };
168
- const info = infoRes.ok ? await infoRes.json() : { isSiteProject: false };
184
+ const meta = projectRes.ok
185
+ ? await readJson<{ name: string; root: string }>(projectRes)
186
+ : { name: "project", root: "." };
187
+ const info = infoRes.ok
188
+ ? await readJson<{
189
+ isSiteProject: boolean;
190
+ projectConfig?: ProjectConfig | null;
191
+ directories?: string[];
192
+ [key: string]: unknown;
193
+ }>(infoRes)
194
+ : { isSiteProject: false };
169
195
  return { info, meta };
170
196
  } catch {
171
197
  return null;
@@ -196,7 +222,7 @@ export function createDevServerPlatform() {
196
222
  method: "POST",
197
223
  });
198
224
  if (!res.ok) {
199
- const data = await res.json();
225
+ const data = await readJson<ErrorBody>(res);
200
226
  throw new Error(data.error || "Failed to create project");
201
227
  }
202
228
  return await res.json();
@@ -210,7 +236,7 @@ export function createDevServerPlatform() {
210
236
  if (!res.ok) {
211
237
  throw new Error(`Failed to list directory: ${dir}`);
212
238
  }
213
- const entries = await res.json();
239
+ const entries = await readJson<WireDirEntry[]>(res);
214
240
  for (const e of entries) {
215
241
  e.path = stripRoot(e.path);
216
242
  }
@@ -223,7 +249,7 @@ export function createDevServerPlatform() {
223
249
  if (!res.ok) {
224
250
  throw new Error(`Failed to read file: ${path}`);
225
251
  }
226
- const data = await res.json();
252
+ const data = await readJson<{ content: string }>(res);
227
253
  return data.content;
228
254
  },
229
255
 
@@ -392,7 +418,7 @@ export function createDevServerPlatform() {
392
418
  method: "POST",
393
419
  });
394
420
  if (res.ok) {
395
- const body = await res.json();
421
+ const body = await readJson<{ path?: string }>(res);
396
422
  return body.path || null;
397
423
  }
398
424
  } catch {}
@@ -412,7 +438,7 @@ export function createDevServerPlatform() {
412
438
  if (!res.ok) {
413
439
  return [];
414
440
  }
415
- const entries = await res.json();
441
+ const entries = await readJson<WireDirEntry[]>(res);
416
442
  for (const e of entries) {
417
443
  e.path = stripRoot(e.path);
418
444
  }
@@ -427,7 +453,7 @@ export function createDevServerPlatform() {
427
453
  if (!res.ok) {
428
454
  return [];
429
455
  }
430
- const body = await res.json();
456
+ const body = await readJson<{ formats?: unknown[] }>(res);
431
457
  return body.formats ?? [];
432
458
  },
433
459
 
@@ -442,7 +468,7 @@ export function createDevServerPlatform() {
442
468
  headers: { "Content-Type": "application/json" },
443
469
  method: "POST",
444
470
  });
445
- const data = await res.json();
471
+ const data = await readJson<{ error?: string; result?: unknown }>(res);
446
472
  if (!res.ok) {
447
473
  throw new Error(data.error || "Format action failed");
448
474
  }
@@ -468,7 +494,7 @@ export function createDevServerPlatform() {
468
494
  if (!res.ok) {
469
495
  return null;
470
496
  }
471
- const { schema } = await res.json();
497
+ const { schema } = await readJson<{ schema: unknown }>(res);
472
498
  return schema;
473
499
  },
474
500
 
@@ -508,7 +534,7 @@ export function createDevServerPlatform() {
508
534
  method: "POST",
509
535
  });
510
536
  if (!res.ok) {
511
- const body = await res.json();
537
+ const body = await readJson<ErrorBody>(res);
512
538
  throw new Error(body.error);
513
539
  }
514
540
  return await res.json();
@@ -522,7 +548,7 @@ export function createDevServerPlatform() {
522
548
  method: "POST",
523
549
  });
524
550
  if (!res.ok) {
525
- const body = await res.json();
551
+ const body = await readJson<ErrorBody>(res);
526
552
  throw new Error(body.error);
527
553
  }
528
554
  return await res.json();
@@ -536,7 +562,7 @@ export function createDevServerPlatform() {
536
562
  method: "POST",
537
563
  });
538
564
  if (!res.ok) {
539
- const body = await res.json();
565
+ const body = await readJson<ErrorBody>(res);
540
566
  throw new Error(body.error);
541
567
  }
542
568
  return await res.json();
@@ -550,7 +576,7 @@ export function createDevServerPlatform() {
550
576
  method: "POST",
551
577
  });
552
578
  if (!res.ok) {
553
- const body = await res.json();
579
+ const body = await readJson<ErrorBody>(res);
554
580
  throw new Error(body.error);
555
581
  }
556
582
  return await res.json();
@@ -559,7 +585,7 @@ export function createDevServerPlatform() {
559
585
  async gitPull() {
560
586
  const res = await fetch("/__studio/git/pull", { method: "POST" });
561
587
  if (!res.ok) {
562
- const body = await res.json();
588
+ const body = await readJson<ErrorBody>(res);
563
589
  throw new Error(body.error);
564
590
  }
565
591
  return await res.json();
@@ -568,7 +594,7 @@ export function createDevServerPlatform() {
568
594
  async gitFetch() {
569
595
  const res = await fetch("/__studio/git/fetch", { method: "POST" });
570
596
  if (!res.ok) {
571
- const body = await res.json();
597
+ const body = await readJson<ErrorBody>(res);
572
598
  throw new Error(body.error);
573
599
  }
574
600
  return await res.json();
@@ -582,7 +608,7 @@ export function createDevServerPlatform() {
582
608
  method: "POST",
583
609
  });
584
610
  if (!res.ok) {
585
- const body = await res.json();
611
+ const body = await readJson<ErrorBody>(res);
586
612
  throw new Error(body.error);
587
613
  }
588
614
  return await res.json();
@@ -596,7 +622,7 @@ export function createDevServerPlatform() {
596
622
  method: "POST",
597
623
  });
598
624
  if (!res.ok) {
599
- const body = await res.json();
625
+ const body = await readJson<ErrorBody>(res);
600
626
  throw new Error(body.error);
601
627
  }
602
628
  return await res.json();
@@ -621,7 +647,7 @@ export function createDevServerPlatform() {
621
647
  if (!res.ok) {
622
648
  throw new Error(await res.text());
623
649
  }
624
- const data = await res.json();
650
+ const data = await readJson<{ content: string }>(res);
625
651
  return data.content;
626
652
  },
627
653
 
@@ -633,7 +659,7 @@ export function createDevServerPlatform() {
633
659
  method: "POST",
634
660
  });
635
661
  if (!res.ok) {
636
- const body = await res.json();
662
+ const body = await readJson<ErrorBody>(res);
637
663
  throw new Error(body.error);
638
664
  }
639
665
  return await res.json();
@@ -647,7 +673,7 @@ export function createDevServerPlatform() {
647
673
  method: "POST",
648
674
  });
649
675
  if (!res.ok) {
650
- const body = await res.json();
676
+ const body = await readJson<ErrorBody>(res);
651
677
  throw new Error(body.error);
652
678
  }
653
679
  return await res.json();
@@ -656,7 +682,7 @@ export function createDevServerPlatform() {
656
682
  async gitInit() {
657
683
  const res = await fetch("/__studio/git/init", { method: "POST" });
658
684
  if (!res.ok) {
659
- const body = await res.json();
685
+ const body = await readJson<ErrorBody>(res);
660
686
  throw new Error(body.error);
661
687
  }
662
688
  },
@@ -672,7 +698,7 @@ export function createDevServerPlatform() {
672
698
  method: "POST",
673
699
  });
674
700
  if (!res.ok) {
675
- const body = await res.json();
701
+ const body = await readJson<ErrorBody>(res);
676
702
  throw new Error(body.error);
677
703
  }
678
704
  },
@@ -692,7 +718,7 @@ export function createDevServerPlatform() {
692
718
  method: "POST",
693
719
  });
694
720
  if (!res.ok) {
695
- const body = await res.json();
721
+ const body = await readJson<ErrorBody>(res);
696
722
  throw new Error(body.error);
697
723
  }
698
724
  return await res.json();
@@ -706,7 +732,7 @@ export function createDevServerPlatform() {
706
732
  method: "POST",
707
733
  });
708
734
  if (!res.ok) {
709
- const body = await res.json();
735
+ const body = await readJson<ErrorBody>(res);
710
736
  throw new Error(body.error);
711
737
  }
712
738
  return await res.json();
package/src/reactivity.ts CHANGED
@@ -26,3 +26,5 @@ export {
26
26
  isReactive,
27
27
  isRef,
28
28
  } from "@vue/reactivity";
29
+
30
+ export type { EffectScope } from "@vue/reactivity";
@@ -2,11 +2,11 @@
2
2
  import type { CemParameter, JxMutableNode, JxStateObject } from "@jxsuite/schema/types";
3
3
  import { isFunctionDef, isJsonObject } from "@jxsuite/schema/guards";
4
4
 
5
- /** Collect slot elements from the document tree. */
5
+ /** Collect slot elements from the document tree. Whitespace-only names count as unnamed. */
6
6
  export function collectSlots(node?: JxMutableNode | null, slots: string[] = []) {
7
7
  if (node?.tagName === "slot") {
8
8
  const name = node.attributes?.name;
9
- slots.push(typeof name === "string" ? name : "");
9
+ slots.push(typeof name === "string" ? name.trim() : "");
10
10
  }
11
11
  if (Array.isArray(node?.children)) {
12
12
  for (const c of node.children) {
@@ -16,6 +16,27 @@ export function collectSlots(node?: JxMutableNode | null, slots: string[] = [])
16
16
  return slots;
17
17
  }
18
18
 
19
+ /**
20
+ * Validate slot usage in a component definition. Returns a warning message or null when valid. Only
21
+ * static children are walked — slots inside $switch cases are mutually exclusive branches and are
22
+ * not counted (v1 limitation).
23
+ */
24
+ export function validateComponentSlots(doc: JxMutableNode): string | null {
25
+ const names = collectSlots(doc);
26
+ const unnamed = names.filter((n) => n === "").length;
27
+ if (unnamed > 1) {
28
+ return `Component has ${unnamed} unnamed <slot> elements — only one default slot is allowed. Give extra slots a name attribute.`;
29
+ }
30
+ const seen = new Set<string>();
31
+ for (const n of names) {
32
+ if (n && seen.has(n)) {
33
+ return `Duplicate slot name "${n}" — slot names must be unique within a component.`;
34
+ }
35
+ seen.add(n);
36
+ }
37
+ return null;
38
+ }
39
+
19
40
  /**
20
41
  * Generate and download a CEM 2.1.0 manifest for the current document.
21
42
  *
@@ -43,8 +43,18 @@ export async function locateDocument(name: string) {
43
43
  return platform.locateFile(name);
44
44
  }
45
45
 
46
+ /** Schema returned by a plugin's `$prototype` module (loose superset used across panels). */
47
+ export interface PluginSchema {
48
+ type?: string;
49
+ description?: string;
50
+ properties?: Record<string, unknown>;
51
+ required?: string[];
52
+ returns?: { type?: string };
53
+ [key: string]: unknown;
54
+ }
55
+
46
56
  /** Cache of plugin schemas keyed by "$src::$prototype". */
47
- export const pluginSchemaCache = new Map();
57
+ export const pluginSchemaCache = new Map<string, PluginSchema | null>();
48
58
 
49
59
  /**
50
60
  * Fetch and cache the schema for a $prototype module via the server. Works for both external
@@ -77,7 +87,11 @@ export async function fetchPluginSchema(
77
87
  }
78
88
  const base =
79
89
  !importedPath && state.documentPath ? `${location.origin}/${state.documentPath}` : undefined;
80
- const schema = await platform.fetchPluginSchema(src, def.$prototype, base);
90
+ const schema = (await platform.fetchPluginSchema(
91
+ src,
92
+ def.$prototype,
93
+ base,
94
+ )) as PluginSchema | null;
81
95
  pluginSchemaCache.set(cacheKey, schema);
82
96
  return schema;
83
97
  } catch {
@@ -18,11 +18,12 @@ const WORKER_PATHS: Record<string, string> = {
18
18
 
19
19
  self.MonacoEnvironment = {
20
20
  getWorker(_, label: string) {
21
- const path = WORKER_PATHS[label] || WORKER_PATHS.editorWorkerService;
21
+ const path = WORKER_PATHS[label] || WORKER_PATHS.editorWorkerService!;
22
22
  return new Worker(path, { type: "module" });
23
23
  },
24
24
  };
25
25
 
26
+ // oxlint-disable-next-line typescript/no-unsafe-call, typescript/no-unsafe-member-access -- jsonDefaults is imported from Monaco's untyped ESM contribution (see @ts-expect-error above); no type declarations exist for this named export
26
27
  jsonDefaults.setDiagnosticsOptions({
27
28
  allowComments: false,
28
29
  schemas: [
@@ -80,7 +80,7 @@ function handleNewContentType(rerender: () => void) {
80
80
  rerender();
81
81
 
82
82
  // Persist in background
83
- saveProjectConfig().then(async () => {
83
+ void saveProjectConfig().then(async () => {
84
84
  const platform = getPlatform();
85
85
  await platform.writeFile(`content/${slug}/.gitkeep`, "");
86
86
  });
@@ -116,7 +116,7 @@ function handleAddField(rerender: () => void) {
116
116
  showAddField = false;
117
117
  newFieldState = { format: "", name: "", required: false, type: "string" };
118
118
  rerender();
119
- saveProjectConfig();
119
+ void saveProjectConfig();
120
120
  }
121
121
 
122
122
  /**
@@ -135,7 +135,7 @@ function handleDeleteField(fieldName: string, rerender: () => void) {
135
135
  }
136
136
 
137
137
  rerender();
138
- saveProjectConfig();
138
+ void saveProjectConfig();
139
139
  }
140
140
 
141
141
  /**
@@ -159,7 +159,7 @@ function handleToggleRequired(fieldName: string, rerender: () => void) {
159
159
  }
160
160
 
161
161
  rerender();
162
- saveProjectConfig();
162
+ void saveProjectConfig();
163
163
  }
164
164
 
165
165
  /**
@@ -185,7 +185,7 @@ function handleRenameField(oldName: string, newName: string, rerender: () => voi
185
185
  }
186
186
 
187
187
  rerender();
188
- saveProjectConfig();
188
+ void saveProjectConfig();
189
189
  }
190
190
 
191
191
  /**
@@ -205,7 +205,7 @@ function handleChangeType(fieldName: string, newType: string, rerender: () => vo
205
205
  : undefined;
206
206
  schema.properties[fieldName] = schemaForType(newType, oldFormat || undefined);
207
207
  rerender();
208
- saveProjectConfig();
208
+ void saveProjectConfig();
209
209
  }
210
210
 
211
211
  /**
@@ -223,7 +223,7 @@ function handleChangeFormat(fieldName: string, format: string, rerender: () => v
223
223
  const type = prop.type || "string";
224
224
  schema.properties[fieldName] = schemaForType(type, format || undefined);
225
225
  rerender();
226
- saveProjectConfig();
226
+ void saveProjectConfig();
227
227
  }
228
228
 
229
229
  /**
@@ -239,7 +239,7 @@ function handleChangeRefTarget(fieldName: string, target: string, rerender: () =
239
239
 
240
240
  schema.properties[fieldName] = { $ref: `#/contentTypes/${target}` };
241
241
  rerender();
242
- saveProjectConfig();
242
+ void saveProjectConfig();
243
243
  }
244
244
 
245
245
  // ─── Nested field handlers ───────────────────────────────────────────────────
@@ -280,7 +280,7 @@ function handleAddNestedField(
280
280
  }
281
281
 
282
282
  rerender();
283
- saveProjectConfig();
283
+ void saveProjectConfig();
284
284
  }
285
285
 
286
286
  /**
@@ -301,7 +301,7 @@ function handleDeleteNested(parentName: string, childName: string, rerender: ()
301
301
  }
302
302
 
303
303
  rerender();
304
- saveProjectConfig();
304
+ void saveProjectConfig();
305
305
  }
306
306
 
307
307
  /**
@@ -327,7 +327,7 @@ function handleToggleNestedRequired(parentName: string, childName: string, reren
327
327
  }
328
328
 
329
329
  rerender();
330
- saveProjectConfig();
330
+ void saveProjectConfig();
331
331
  }
332
332
 
333
333
  /**
@@ -360,7 +360,7 @@ function handleRenameNested(
360
360
  }
361
361
 
362
362
  rerender();
363
- saveProjectConfig();
363
+ void saveProjectConfig();
364
364
  }
365
365
 
366
366
  /**
@@ -387,7 +387,7 @@ function handleChangeNestedType(
387
387
  : undefined;
388
388
  parent.properties[childName] = schemaForType(newType, oldFormat || undefined);
389
389
  rerender();
390
- saveProjectConfig();
390
+ void saveProjectConfig();
391
391
  }
392
392
 
393
393
  /**
@@ -412,7 +412,7 @@ function handleChangeNestedFormat(
412
412
  const type = prop.type || "string";
413
413
  parent.properties[childName] = schemaForType(type, format || undefined);
414
414
  rerender();
415
- saveProjectConfig();
415
+ void saveProjectConfig();
416
416
  }
417
417
 
418
418
  /** @param {() => void} rerender */
@@ -429,7 +429,7 @@ function handleDeleteContentType(rerender: () => void) {
429
429
  selectedContentType = null;
430
430
 
431
431
  rerender();
432
- saveProjectConfig();
432
+ void saveProjectConfig();
433
433
  }
434
434
 
435
435
  // ─── Render ───────────────────────────────────────────────────────────────────
@@ -508,7 +508,7 @@ export function renderContentTypesEditor(container: HTMLElement) {
508
508
  if (!selectedContentType || !contentTypes[selectedContentType]) {
509
509
  editorTpl = html`<div class="settings-empty-state">Select or create a content type</div>`;
510
510
  } else {
511
- const col = contentTypes[selectedContentType];
511
+ const col = contentTypes[selectedContentType]!;
512
512
  const schema = (col.schema || {}) as ContentTypeSchema;
513
513
  const properties = schema.properties || {};
514
514
  const required = schema.required || [];
@@ -53,7 +53,7 @@ export function renderCssVarsEditor(container: HTMLElement) {
53
53
  const mediaNames = media ? Object.keys(media).filter((m) => m !== "--") : [];
54
54
 
55
55
  const save = () => {
56
- updateSiteConfig({ style: { ...rootStyle } });
56
+ void updateSiteConfig({ style: { ...rootStyle } });
57
57
  };
58
58
 
59
59
  const updateVar = (name: string, val: string) => {
@@ -297,7 +297,7 @@ function renderMediaOverrides(varName: string, rootStyle: JxStyle, mediaNames: s
297
297
  (rootStyle[`@${o.mediaName}`] as Record<string, unknown>)[varName] = (
298
298
  e.target as HTMLInputElement
299
299
  ).value;
300
- updateSiteConfig({ style: { ...rootStyle } });
300
+ void updateSiteConfig({ style: { ...rootStyle } });
301
301
  }}
302
302
  style="max-width:120px"
303
303
  ></sp-textfield>
@@ -73,7 +73,7 @@ function handleNewDef(rerender: () => void) {
73
73
  showNewDef = false;
74
74
  newDefName = "";
75
75
  rerender();
76
- saveProjectConfig();
76
+ void saveProjectConfig();
77
77
  }
78
78
 
79
79
  /** @param {() => void} rerender */
@@ -105,7 +105,7 @@ function handleAddField(rerender: () => void) {
105
105
  showAddField = false;
106
106
  newFieldState = { format: "", name: "", required: false, type: "string" };
107
107
  rerender();
108
- saveProjectConfig();
108
+ void saveProjectConfig();
109
109
  }
110
110
 
111
111
  /**
@@ -124,7 +124,7 @@ function handleDeleteField(fieldName: string, rerender: () => void) {
124
124
  }
125
125
 
126
126
  rerender();
127
- saveProjectConfig();
127
+ void saveProjectConfig();
128
128
  }
129
129
 
130
130
  /**
@@ -148,7 +148,7 @@ function handleToggleRequired(fieldName: string, rerender: () => void) {
148
148
  }
149
149
 
150
150
  rerender();
151
- saveProjectConfig();
151
+ void saveProjectConfig();
152
152
  }
153
153
 
154
154
  /**
@@ -173,7 +173,7 @@ function handleRenameField(oldName: string, newName: string, rerender: () => voi
173
173
  }
174
174
 
175
175
  rerender();
176
- saveProjectConfig();
176
+ void saveProjectConfig();
177
177
  }
178
178
 
179
179
  /**
@@ -193,7 +193,7 @@ function handleChangeType(fieldName: string, newType: string, rerender: () => vo
193
193
  : undefined;
194
194
  def.properties[fieldName] = schemaForType(newType, oldFormat || undefined);
195
195
  rerender();
196
- saveProjectConfig();
196
+ void saveProjectConfig();
197
197
  }
198
198
 
199
199
  /**
@@ -211,7 +211,7 @@ function handleChangeFormat(fieldName: string, format: string, rerender: () => v
211
211
  const type = prop.type || "string";
212
212
  def.properties[fieldName] = schemaForType(type, format || undefined);
213
213
  rerender();
214
- saveProjectConfig();
214
+ void saveProjectConfig();
215
215
  }
216
216
 
217
217
  // ─── Nested field handlers ───────────────────────────────────────────────────
@@ -247,7 +247,7 @@ function handleAddNestedField(
247
247
  }
248
248
 
249
249
  rerender();
250
- saveProjectConfig();
250
+ void saveProjectConfig();
251
251
  }
252
252
 
253
253
  /**
@@ -268,7 +268,7 @@ function handleDeleteNested(parentName: string, childName: string, rerender: ()
268
268
  }
269
269
 
270
270
  rerender();
271
- saveProjectConfig();
271
+ void saveProjectConfig();
272
272
  }
273
273
 
274
274
  /**
@@ -294,7 +294,7 @@ function handleToggleNestedRequired(parentName: string, childName: string, reren
294
294
  }
295
295
 
296
296
  rerender();
297
- saveProjectConfig();
297
+ void saveProjectConfig();
298
298
  }
299
299
 
300
300
  /**
@@ -326,7 +326,7 @@ function handleRenameNested(
326
326
  }
327
327
 
328
328
  rerender();
329
- saveProjectConfig();
329
+ void saveProjectConfig();
330
330
  }
331
331
 
332
332
  /**
@@ -353,7 +353,7 @@ function handleChangeNestedType(
353
353
  : undefined;
354
354
  parent.properties[childName] = schemaForType(newType, oldFormat || undefined);
355
355
  rerender();
356
- saveProjectConfig();
356
+ void saveProjectConfig();
357
357
  }
358
358
 
359
359
  /**
@@ -378,7 +378,7 @@ function handleChangeNestedFormat(
378
378
  const type = prop.type || "string";
379
379
  parent.properties[childName] = schemaForType(type, format || undefined);
380
380
  rerender();
381
- saveProjectConfig();
381
+ void saveProjectConfig();
382
382
  }
383
383
 
384
384
  /** @param {() => void} rerender */
@@ -395,7 +395,7 @@ function handleDeleteDef(rerender: () => void) {
395
395
  selectedDef = null;
396
396
 
397
397
  rerender();
398
- saveProjectConfig();
398
+ void saveProjectConfig();
399
399
  }
400
400
 
401
401
  // ─── Render ───────────────────────────────────────────────────────────────────