@jxsuite/studio 0.28.2 → 0.28.4

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 (104) hide show
  1. package/dist/studio.js +86144 -83971
  2. package/dist/studio.js.map +229 -219
  3. package/package.json +5 -5
  4. package/src/browse/browse-modal.ts +9 -3
  5. package/src/browse/browse.ts +139 -82
  6. package/src/canvas/canvas-diff.ts +35 -18
  7. package/src/canvas/canvas-helpers.ts +55 -21
  8. package/src/canvas/canvas-live-render.ts +140 -89
  9. package/src/canvas/canvas-render.ts +127 -82
  10. package/src/canvas/canvas-utils.ts +68 -42
  11. package/src/canvas/nested-site-style.ts +15 -7
  12. package/src/editor/component-inline-edit.ts +90 -51
  13. package/src/editor/content-inline-edit.ts +75 -72
  14. package/src/editor/context-menu.ts +125 -71
  15. package/src/editor/convert-targets.ts +17 -13
  16. package/src/editor/convert-to-component.ts +51 -28
  17. package/src/editor/convert-to-repeater.ts +44 -19
  18. package/src/editor/inline-edit.ts +107 -52
  19. package/src/editor/inline-format.ts +128 -60
  20. package/src/editor/insertion-helper.ts +26 -14
  21. package/src/editor/shortcuts.ts +89 -43
  22. package/src/editor/slash-menu.ts +41 -26
  23. package/src/files/components.ts +9 -7
  24. package/src/files/file-ops.ts +53 -33
  25. package/src/files/files.ts +246 -143
  26. package/src/format/constraints.ts +25 -15
  27. package/src/format/format-host.ts +33 -10
  28. package/src/github/github-auth.ts +24 -14
  29. package/src/github/github-publish.ts +20 -14
  30. package/src/new-project/new-project-modal.ts +27 -18
  31. package/src/panels/activity-bar.ts +27 -26
  32. package/src/panels/ai-panel.ts +100 -44
  33. package/src/panels/block-action-bar.ts +84 -52
  34. package/src/panels/canvas-dnd.ts +47 -28
  35. package/src/panels/data-explorer.ts +24 -11
  36. package/src/panels/dnd.ts +148 -100
  37. package/src/panels/editors.ts +54 -37
  38. package/src/panels/elements-panel.ts +23 -13
  39. package/src/panels/events-panel.ts +48 -38
  40. package/src/panels/git-panel.ts +95 -60
  41. package/src/panels/head-panel.ts +138 -84
  42. package/src/panels/imports-panel.ts +66 -29
  43. package/src/panels/layers-panel.ts +80 -36
  44. package/src/panels/left-panel.ts +86 -53
  45. package/src/panels/overlays.ts +36 -19
  46. package/src/panels/panel-events.ts +48 -27
  47. package/src/panels/panel-scheduler.ts +41 -23
  48. package/src/panels/preview-render.ts +26 -26
  49. package/src/panels/properties-panel.ts +155 -118
  50. package/src/panels/pseudo-preview.ts +31 -15
  51. package/src/panels/quick-search.ts +28 -13
  52. package/src/panels/right-panel.ts +35 -24
  53. package/src/panels/shared.ts +46 -29
  54. package/src/panels/signals-panel.ts +303 -186
  55. package/src/panels/statusbar.ts +27 -12
  56. package/src/panels/style-inputs.ts +30 -24
  57. package/src/panels/style-panel.ts +130 -90
  58. package/src/panels/style-utils.ts +63 -23
  59. package/src/panels/stylebook-layers-panel.ts +27 -30
  60. package/src/panels/stylebook-panel.ts +140 -72
  61. package/src/panels/tab-strip.ts +21 -10
  62. package/src/panels/toolbar.ts +52 -40
  63. package/src/panels/welcome-screen.ts +1 -1
  64. package/src/platform.ts +2 -1
  65. package/src/platforms/devserver.ts +221 -105
  66. package/src/recent-projects.ts +15 -7
  67. package/src/resize-edges.ts +7 -5
  68. package/src/services/cem-export.ts +73 -57
  69. package/src/services/code-services.ts +52 -25
  70. package/src/services/monaco-setup.ts +5 -5
  71. package/src/settings/content-types-editor.ts +133 -62
  72. package/src/settings/css-vars-editor.ts +31 -14
  73. package/src/settings/defs-editor.ts +125 -63
  74. package/src/settings/general-settings.ts +11 -6
  75. package/src/settings/head-editor.ts +30 -15
  76. package/src/settings/schema-field-ui.ts +75 -35
  77. package/src/settings/settings-modal.ts +35 -17
  78. package/src/site-context.ts +80 -47
  79. package/src/state.ts +98 -65
  80. package/src/store.ts +42 -23
  81. package/src/studio.ts +165 -144
  82. package/src/tabs/tab.ts +51 -45
  83. package/src/tabs/transact.ts +230 -109
  84. package/src/types.ts +60 -51
  85. package/src/ui/button-group.ts +3 -2
  86. package/src/ui/color-selector.ts +33 -15
  87. package/src/ui/expression-editor.ts +139 -86
  88. package/src/ui/field-input.ts +35 -18
  89. package/src/ui/field-row.ts +1 -1
  90. package/src/ui/icons.ts +5 -6
  91. package/src/ui/layers.ts +33 -26
  92. package/src/ui/media-picker.ts +13 -7
  93. package/src/ui/panel-resize.ts +39 -20
  94. package/src/ui/spectrum.ts +4 -2
  95. package/src/ui/unit-selector.ts +20 -11
  96. package/src/ui/value-selector.ts +13 -7
  97. package/src/ui/widgets.ts +34 -18
  98. package/src/utils/canvas-media.ts +66 -33
  99. package/src/utils/edit-display.ts +43 -44
  100. package/src/utils/google-fonts.ts +10 -6
  101. package/src/utils/inherited-style.ts +22 -8
  102. package/src/utils/studio-utils.ts +88 -45
  103. package/src/view.ts +9 -5
  104. package/src/workspace/workspace.ts +49 -29
@@ -10,15 +10,20 @@
10
10
  import { html, render as litRender } from "lit-html";
11
11
  import { getPlatform } from "../platform";
12
12
  import { projectState } from "../store";
13
- import { fieldCardTpl, addFieldFormTpl, schemaForType, detectFieldFormat } from "./schema-field-ui";
13
+ import { addFieldFormTpl, detectFieldFormat, fieldCardTpl, schemaForType } from "./schema-field-ui";
14
14
 
15
- import type { ProjectConfig, ContentTypeSchema } from "@jxsuite/schema/types";
15
+ import type { FieldHandlers, SchemaProperty } from "./schema-field-ui.js";
16
+ import type {
17
+ ContentTypeSchema,
18
+ ContentTypeSchemaField,
19
+ ProjectConfig,
20
+ } from "@jxsuite/schema/types";
16
21
 
17
22
  // ─── Module state ─────────────────────────────────────────────────────────────
18
23
 
19
24
  let selectedDef: string | null = null;
20
25
  let showAddField = false;
21
- let newFieldState = { name: "", type: "string", format: "", required: false };
26
+ let newFieldState = { format: "", name: "", required: false, type: "string" };
22
27
  let showNewDef = false;
23
28
  let newDefName = "";
24
29
 
@@ -43,17 +48,25 @@ function getSelectedDef(): ContentTypeSchema | undefined {
43
48
  /** @param {() => void} rerender */
44
49
  function handleNewDef(rerender: () => void) {
45
50
  const name = newDefName.trim();
46
- if (!name) return;
51
+ if (!name) {
52
+ return;
53
+ }
47
54
 
48
55
  const config = projectState?.projectConfig;
49
- if (!config) return;
50
- if (!config.$defs) config.$defs = {};
51
- if (config.$defs[name]) return; // already exists
56
+ if (!config) {
57
+ return;
58
+ }
59
+ if (!config.$defs) {
60
+ config.$defs = {};
61
+ }
62
+ if (config.$defs[name]) {
63
+ return;
64
+ } // Already exists
52
65
 
53
66
  config.$defs[name] = {
54
- type: "object",
55
67
  properties: {},
56
68
  required: [],
69
+ type: "object",
57
70
  };
58
71
 
59
72
  selectedDef = name;
@@ -66,21 +79,31 @@ function handleNewDef(rerender: () => void) {
66
79
  /** @param {() => void} rerender */
67
80
  function handleAddField(rerender: () => void) {
68
81
  const name = newFieldState.name.trim();
69
- if (!name || !selectedDef) return;
82
+ if (!name || !selectedDef) {
83
+ return;
84
+ }
70
85
 
71
86
  const def = getSelectedDef();
72
- if (!def) return;
87
+ if (!def) {
88
+ return;
89
+ }
73
90
 
74
- if (!def.properties) def.properties = {};
91
+ if (!def.properties) {
92
+ def.properties = {};
93
+ }
75
94
  def.properties[name] = schemaForType(newFieldState.type, newFieldState.format || undefined);
76
95
 
77
96
  if (newFieldState.required) {
78
- if (!def.required) def.required = [];
79
- if (!def.required.includes(name)) def.required.push(name);
97
+ if (!def.required) {
98
+ def.required = [];
99
+ }
100
+ if (!def.required.includes(name)) {
101
+ def.required.push(name);
102
+ }
80
103
  }
81
104
 
82
105
  showAddField = false;
83
- newFieldState = { name: "", type: "string", format: "", required: false };
106
+ newFieldState = { format: "", name: "", required: false, type: "string" };
84
107
  rerender();
85
108
  saveProjectConfig();
86
109
  }
@@ -91,7 +114,9 @@ function handleAddField(rerender: () => void) {
91
114
  */
92
115
  function handleDeleteField(fieldName: string, rerender: () => void) {
93
116
  const def = getSelectedDef();
94
- if (!def?.properties) return;
117
+ if (!def?.properties) {
118
+ return;
119
+ }
95
120
 
96
121
  delete def.properties[fieldName];
97
122
  if (def.required) {
@@ -108,12 +133,19 @@ function handleDeleteField(fieldName: string, rerender: () => void) {
108
133
  */
109
134
  function handleToggleRequired(fieldName: string, rerender: () => void) {
110
135
  const def = getSelectedDef();
111
- if (!def) return;
112
- if (!def.required) def.required = [];
136
+ if (!def) {
137
+ return;
138
+ }
139
+ if (!def.required) {
140
+ def.required = [];
141
+ }
113
142
 
114
143
  const idx = def.required.indexOf(fieldName);
115
- if (idx >= 0) def.required.splice(idx, 1);
116
- else def.required.push(fieldName);
144
+ if (idx !== -1) {
145
+ def.required.splice(idx, 1);
146
+ } else {
147
+ def.required.push(fieldName);
148
+ }
117
149
 
118
150
  rerender();
119
151
  saveProjectConfig();
@@ -126,9 +158,11 @@ function handleToggleRequired(fieldName: string, rerender: () => void) {
126
158
  */
127
159
  function handleRenameField(oldName: string, newName: string, rerender: () => void) {
128
160
  const def = getSelectedDef();
129
- if (!def?.properties || !newName || def.properties[newName]) return;
161
+ if (!def?.properties || !newName || def.properties[newName]) {
162
+ return;
163
+ }
130
164
 
131
- const newProps: Record<string, unknown> = {};
165
+ const newProps: Record<string, ContentTypeSchemaField> = {};
132
166
  for (const [key, val] of Object.entries(def.properties)) {
133
167
  newProps[key === oldName ? newName : key] = val;
134
168
  }
@@ -149,7 +183,9 @@ function handleRenameField(oldName: string, newName: string, rerender: () => voi
149
183
  */
150
184
  function handleChangeType(fieldName: string, newType: string, rerender: () => void) {
151
185
  const def = getSelectedDef();
152
- if (!def?.properties?.[fieldName]) return;
186
+ if (!def?.properties?.[fieldName]) {
187
+ return;
188
+ }
153
189
 
154
190
  const oldFormat =
155
191
  newType === "string" || newType === "array"
@@ -167,7 +203,9 @@ function handleChangeType(fieldName: string, newType: string, rerender: () => vo
167
203
  */
168
204
  function handleChangeFormat(fieldName: string, format: string, rerender: () => void) {
169
205
  const def = getSelectedDef();
170
- if (!def?.properties?.[fieldName]) return;
206
+ if (!def?.properties?.[fieldName]) {
207
+ return;
208
+ }
171
209
 
172
210
  const prop = def.properties[fieldName];
173
211
  const type = prop.type || "string";
@@ -190,14 +228,22 @@ function handleAddNestedField(
190
228
  ) {
191
229
  const def = getSelectedDef();
192
230
  const parent = def?.properties?.[parentName];
193
- if (!parent) return;
231
+ if (!parent) {
232
+ return;
233
+ }
194
234
 
195
- if (!parent.properties) parent.properties = {};
235
+ if (!parent.properties) {
236
+ parent.properties = {};
237
+ }
196
238
  parent.properties[fieldState.name] = schemaForType(fieldState.type);
197
239
 
198
240
  if (fieldState.required) {
199
- if (!parent.required) parent.required = [];
200
- if (!parent.required.includes(fieldState.name)) parent.required.push(fieldState.name);
241
+ if (!parent.required) {
242
+ parent.required = [];
243
+ }
244
+ if (!parent.required.includes(fieldState.name)) {
245
+ parent.required.push(fieldState.name);
246
+ }
201
247
  }
202
248
 
203
249
  rerender();
@@ -212,7 +258,9 @@ function handleAddNestedField(
212
258
  function handleDeleteNested(parentName: string, childName: string, rerender: () => void) {
213
259
  const def = getSelectedDef();
214
260
  const parent = def?.properties?.[parentName];
215
- if (!parent?.properties) return;
261
+ if (!parent?.properties) {
262
+ return;
263
+ }
216
264
 
217
265
  delete parent.properties[childName];
218
266
  if (parent.required) {
@@ -231,12 +279,19 @@ function handleDeleteNested(parentName: string, childName: string, rerender: ()
231
279
  function handleToggleNestedRequired(parentName: string, childName: string, rerender: () => void) {
232
280
  const def = getSelectedDef();
233
281
  const parent = def?.properties?.[parentName];
234
- if (!parent) return;
235
- if (!parent.required) parent.required = [];
282
+ if (!parent) {
283
+ return;
284
+ }
285
+ if (!parent.required) {
286
+ parent.required = [];
287
+ }
236
288
 
237
289
  const idx = parent.required.indexOf(childName);
238
- if (idx >= 0) parent.required.splice(idx, 1);
239
- else parent.required.push(childName);
290
+ if (idx !== -1) {
291
+ parent.required.splice(idx, 1);
292
+ } else {
293
+ parent.required.push(childName);
294
+ }
240
295
 
241
296
  rerender();
242
297
  saveProjectConfig();
@@ -256,9 +311,11 @@ function handleRenameNested(
256
311
  ) {
257
312
  const def = getSelectedDef();
258
313
  const parent = def?.properties?.[parentName];
259
- if (!parent?.properties || !newChild || parent.properties[newChild]) return;
314
+ if (!parent?.properties || !newChild || parent.properties[newChild]) {
315
+ return;
316
+ }
260
317
 
261
- const newProps: Record<string, unknown> = {};
318
+ const newProps: Record<string, ContentTypeSchemaField> = {};
262
319
  for (const [key, val] of Object.entries(parent.properties)) {
263
320
  newProps[key === oldChild ? newChild : key] = val;
264
321
  }
@@ -286,7 +343,9 @@ function handleChangeNestedType(
286
343
  ) {
287
344
  const def = getSelectedDef();
288
345
  const parent = def?.properties?.[parentName];
289
- if (!parent?.properties?.[childName]) return;
346
+ if (!parent?.properties?.[childName]) {
347
+ return;
348
+ }
290
349
 
291
350
  const oldFormat =
292
351
  newType === "string" || newType === "array"
@@ -311,7 +370,9 @@ function handleChangeNestedFormat(
311
370
  ) {
312
371
  const def = getSelectedDef();
313
372
  const parent = def?.properties?.[parentName];
314
- if (!parent?.properties?.[childName]) return;
373
+ if (!parent?.properties?.[childName]) {
374
+ return;
375
+ }
315
376
 
316
377
  const prop = parent.properties[childName];
317
378
  const type = prop.type || "string";
@@ -322,9 +383,13 @@ function handleChangeNestedFormat(
322
383
 
323
384
  /** @param {() => void} rerender */
324
385
  function handleDeleteDef(rerender: () => void) {
325
- if (!selectedDef) return;
386
+ if (!selectedDef) {
387
+ return;
388
+ }
326
389
  const config = projectState?.projectConfig;
327
- if (!config?.$defs?.[selectedDef]) return;
390
+ if (!config?.$defs?.[selectedDef]) {
391
+ return;
392
+ }
328
393
 
329
394
  delete config.$defs[selectedDef];
330
395
  selectedDef = null;
@@ -375,7 +440,9 @@ export function renderDefsEditor(container: HTMLElement) {
375
440
  newDefName = (e.target as HTMLInputElement).value;
376
441
  }}
377
442
  @keydown=${(e: KeyboardEvent) => {
378
- if (e.key === "Enter") handleNewDef(rerender);
443
+ if (e.key === "Enter") {
444
+ handleNewDef(rerender);
445
+ }
379
446
  if (e.key === "Escape") {
380
447
  showNewDef = false;
381
448
  rerender();
@@ -411,30 +478,25 @@ export function renderDefsEditor(container: HTMLElement) {
411
478
  const properties = def.properties || {};
412
479
  const required = def.required || [];
413
480
 
414
- const handlers: import("./schema-field-ui.js").FieldHandlers = {
415
- onDelete: (n: string) => handleDeleteField(n, rerender),
416
- onToggleRequired: (n: string) => handleToggleRequired(n, rerender),
417
- onRename: (oldN: string, newN: string) => handleRenameField(oldN, newN, rerender),
418
- onChangeType: (n: string, t: string) => handleChangeType(n, t, rerender),
419
- onChangeFormat: (n: string, f: string) => handleChangeFormat(n, f, rerender),
481
+ const handlers: FieldHandlers = {
420
482
  onAddNestedField: (p: string, s: { name: string; type: string; required: boolean }) =>
421
483
  handleAddNestedField(p, s, rerender),
422
- onDeleteNested: (p: string, c: string) => handleDeleteNested(p, c, rerender),
423
- onToggleNestedRequired: (p: string, c: string) => handleToggleNestedRequired(p, c, rerender),
424
- onRenameNested: (p: string, o: string, n: string) => handleRenameNested(p, o, n, rerender),
425
- onChangeNestedType: (p: string, c: string, t: string) =>
426
- handleChangeNestedType(p, c, t, rerender),
484
+ onChangeFormat: (n: string, f: string) => handleChangeFormat(n, f, rerender),
427
485
  onChangeNestedFormat: (p: string, c: string, f: string) =>
428
486
  handleChangeNestedFormat(p, c, f, rerender),
487
+ onChangeNestedType: (p: string, c: string, t: string) =>
488
+ handleChangeNestedType(p, c, t, rerender),
489
+ onChangeType: (n: string, t: string) => handleChangeType(n, t, rerender),
490
+ onDelete: (n: string) => handleDeleteField(n, rerender),
491
+ onDeleteNested: (p: string, c: string) => handleDeleteNested(p, c, rerender),
492
+ onRename: (oldN: string, newN: string) => handleRenameField(oldN, newN, rerender),
493
+ onRenameNested: (p: string, o: string, n: string) => handleRenameNested(p, o, n, rerender),
494
+ onToggleNestedRequired: (p: string, c: string) => handleToggleNestedRequired(p, c, rerender),
495
+ onToggleRequired: (n: string) => handleToggleRequired(n, rerender),
429
496
  };
430
497
 
431
498
  const fieldCards = Object.entries(properties).map(([name, fieldDef]) =>
432
- fieldCardTpl(
433
- name,
434
- fieldDef as import("./schema-field-ui.js").SchemaProperty,
435
- required.includes(name),
436
- handlers,
437
- ),
499
+ fieldCardTpl(name, fieldDef as SchemaProperty, required.includes(name), handlers),
438
500
  );
439
501
 
440
502
  editorTpl = html`
@@ -453,21 +515,21 @@ export function renderDefsEditor(container: HTMLElement) {
453
515
  <div class="schema-field-list">${fieldCards}</div>
454
516
  ${showAddField
455
517
  ? addFieldFormTpl(newFieldState, {
456
- onInput: (field, value) => {
457
- newFieldState = { ...newFieldState, [field]: value };
458
- rerender();
459
- },
460
- onConfirm: () => handleAddField(rerender),
461
518
  onCancel: () => {
462
519
  showAddField = false;
463
520
  newFieldState = {
464
- name: "",
465
- type: "string",
466
521
  format: "",
522
+ name: "",
467
523
  required: false,
524
+ type: "string",
468
525
  };
469
526
  rerender();
470
527
  },
528
+ onConfirm: () => handleAddField(rerender),
529
+ onInput: (field, value) => {
530
+ newFieldState = { ...newFieldState, [field]: value };
531
+ rerender();
532
+ },
471
533
  })
472
534
  : html`
473
535
  <sp-action-button
@@ -6,7 +6,6 @@ import { projectState } from "../store";
6
6
  import { updateSiteConfig } from "../site-context";
7
7
  import { getPlatform } from "../platform";
8
8
  import { openFileInTab } from "../files/files";
9
- import { closeSettingsModal } from "./settings-modal";
10
9
 
11
10
  import type { ProjectConfig } from "@jxsuite/schema/types";
12
11
 
@@ -18,14 +17,16 @@ export function renderGeneralSettings(container: HTMLElement) {
18
17
  const input = document.createElement("input");
19
18
  input.type = "file";
20
19
  input.accept = "image/*,.ico,.svg";
21
- input.onchange = async () => {
20
+ input.addEventListener("change", async () => {
22
21
  const file = input.files?.[0];
23
- if (!file) return;
22
+ if (!file) {
23
+ return;
24
+ }
24
25
  const platform = getPlatform();
25
26
  await platform.uploadFile("public/favicon.ico", file);
26
27
  await updateSiteConfig({ favicon: "/favicon.ico" });
27
28
  renderGeneralSettings(container);
28
- };
29
+ });
29
30
  input.click();
30
31
  };
31
32
 
@@ -35,7 +36,9 @@ export function renderGeneralSettings(container: HTMLElement) {
35
36
  });
36
37
  };
37
38
 
38
- const onEditGlobalStyles = () => {
39
+ const onEditGlobalStyles = async () => {
40
+ // Lazy import breaks the general-settings ↔ settings-modal module cycle
41
+ const { closeSettingsModal } = await import("./settings-modal");
39
42
  closeSettingsModal();
40
43
  openFileInTab("project.json");
41
44
  };
@@ -53,7 +56,9 @@ export function renderGeneralSettings(container: HTMLElement) {
53
56
  const onMediaNameChange = (oldKey: string) => (e: Event) => {
54
57
  const rawName = (e.target as HTMLInputElement).value.trim();
55
58
  const newKey = rawName.startsWith("--") ? rawName : `--${rawName}`;
56
- if (newKey === oldKey) return;
59
+ if (newKey === oldKey) {
60
+ return;
61
+ }
57
62
  const updated = {} as Record<string, string>;
58
63
  for (const [k, v] of Object.entries(media)) {
59
64
  updated[k === oldKey ? newKey : k] = v;
@@ -8,11 +8,11 @@ import { html, render as litRender, nothing } from "lit-html";
8
8
  import { projectState } from "../store";
9
9
  import { updateSiteConfig } from "../site-context";
10
10
  import {
11
- isGoogleFontEntry,
12
- extractFontFamily,
13
11
  buildGoogleFontUrl,
14
- ensureGoogleFontPreconnects,
15
12
  cleanupGoogleFontPreconnects,
13
+ ensureGoogleFontPreconnects,
14
+ extractFontFamily,
15
+ isGoogleFontEntry,
16
16
  } from "../utils/google-fonts";
17
17
 
18
18
  import type { JxHeadEntry, ProjectConfig } from "@jxsuite/schema/types";
@@ -28,7 +28,7 @@ export function renderHeadEditor(container: HTMLElement) {
28
28
 
29
29
  const addEntry = (tag: string) => {
30
30
  const attrs: Record<string, string | boolean> = {};
31
- const entry: JxHeadEntry = { tagName: tag, attributes: attrs };
31
+ const entry: JxHeadEntry = { attributes: attrs, tagName: tag };
32
32
  if (tag === "link") {
33
33
  attrs.rel = "stylesheet";
34
34
  attrs.href = "";
@@ -56,7 +56,9 @@ export function renderHeadEditor(container: HTMLElement) {
56
56
  if (key === "content" && (entry.tagName === "script" || entry.tagName === "style")) {
57
57
  entry.textContent = val;
58
58
  } else {
59
- if (!entry.attributes) entry.attributes = {};
59
+ if (!entry.attributes) {
60
+ entry.attributes = {};
61
+ }
60
62
  entry.attributes[key] = val;
61
63
  }
62
64
  save();
@@ -126,7 +128,7 @@ function renderEntryFields(
126
128
  const attrs = entry.attributes || {};
127
129
 
128
130
  switch (entry.tagName) {
129
- case "link":
131
+ case "link": {
130
132
  return html`
131
133
  <div class="settings-field-row">
132
134
  <sp-textfield
@@ -144,7 +146,8 @@ function renderEntryFields(
144
146
  ></sp-textfield>
145
147
  </div>
146
148
  `;
147
- case "meta":
149
+ }
150
+ case "meta": {
148
151
  return html`
149
152
  <div class="settings-field-row">
150
153
  <sp-textfield
@@ -162,7 +165,8 @@ function renderEntryFields(
162
165
  ></sp-textfield>
163
166
  </div>
164
167
  `;
165
- case "script":
168
+ }
169
+ case "script": {
166
170
  return html`
167
171
  <div class="settings-field-row">
168
172
  <sp-textfield
@@ -189,7 +193,8 @@ function renderEntryFields(
189
193
  `
190
194
  : nothing}
191
195
  `;
192
- case "style":
196
+ }
197
+ case "style": {
193
198
  return html`
194
199
  <div class="head-entry-body">
195
200
  <label class="settings-field-label">Style body</label>
@@ -202,8 +207,10 @@ function renderEntryFields(
202
207
  ></textarea>
203
208
  </div>
204
209
  `;
205
- default:
210
+ }
211
+ default: {
206
212
  return nothing;
213
+ }
207
214
  }
208
215
  }
209
216
 
@@ -224,8 +231,8 @@ function renderGoogleFontsSection(
224
231
  const addFont = (family: string) => {
225
232
  ensureGoogleFontPreconnects(headEntries);
226
233
  headEntries.push({
234
+ attributes: { href: buildGoogleFontUrl(family), rel: "stylesheet" },
227
235
  tagName: "link",
228
- attributes: { rel: "stylesheet", href: buildGoogleFontUrl(family) },
229
236
  });
230
237
  save();
231
238
  rerender();
@@ -233,7 +240,9 @@ function renderGoogleFontsSection(
233
240
 
234
241
  const removeFont = (entry: JxHeadEntry) => {
235
242
  const idx = headEntries.indexOf(entry);
236
- if (idx >= 0) headEntries.splice(idx, 1);
243
+ if (idx !== -1) {
244
+ headEntries.splice(idx, 1);
245
+ }
237
246
  const cleaned = cleanupGoogleFontPreconnects(headEntries);
238
247
  if (cleaned !== headEntries) {
239
248
  headEntries.length = 0;
@@ -266,9 +275,13 @@ function renderGoogleFontsSection(
266
275
  placeholder="Font family name…"
267
276
  style="flex:1"
268
277
  @keydown=${(e: KeyboardEvent) => {
269
- if (e.key !== "Enter") return;
278
+ if (e.key !== "Enter") {
279
+ return;
280
+ }
270
281
  const family = (e.target as HTMLInputElement).value?.trim();
271
- if (!family) return;
282
+ if (!family) {
283
+ return;
284
+ }
272
285
  (e.target as HTMLInputElement).value = "";
273
286
  addFont(family);
274
287
  }}
@@ -278,7 +291,9 @@ function renderGoogleFontsSection(
278
291
  @click=${(e: MouseEvent) => {
279
292
  const input = (e.target as HTMLElement).closest("div")?.querySelector("sp-textfield");
280
293
  const family = (input as HTMLInputElement | null)?.value?.trim();
281
- if (!family) return;
294
+ if (!family) {
295
+ return;
296
+ }
282
297
  (input as HTMLInputElement).value = "";
283
298
  addFont(family);
284
299
  }}