@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.
- package/dist/studio.js +86144 -83971
- package/dist/studio.js.map +229 -219
- package/package.json +5 -5
- package/src/browse/browse-modal.ts +9 -3
- package/src/browse/browse.ts +139 -82
- package/src/canvas/canvas-diff.ts +35 -18
- package/src/canvas/canvas-helpers.ts +55 -21
- package/src/canvas/canvas-live-render.ts +140 -89
- package/src/canvas/canvas-render.ts +127 -82
- package/src/canvas/canvas-utils.ts +68 -42
- package/src/canvas/nested-site-style.ts +15 -7
- package/src/editor/component-inline-edit.ts +90 -51
- package/src/editor/content-inline-edit.ts +75 -72
- package/src/editor/context-menu.ts +125 -71
- package/src/editor/convert-targets.ts +17 -13
- package/src/editor/convert-to-component.ts +51 -28
- package/src/editor/convert-to-repeater.ts +44 -19
- package/src/editor/inline-edit.ts +107 -52
- package/src/editor/inline-format.ts +128 -60
- package/src/editor/insertion-helper.ts +26 -14
- package/src/editor/shortcuts.ts +89 -43
- package/src/editor/slash-menu.ts +41 -26
- package/src/files/components.ts +9 -7
- package/src/files/file-ops.ts +53 -33
- package/src/files/files.ts +246 -143
- package/src/format/constraints.ts +25 -15
- package/src/format/format-host.ts +33 -10
- package/src/github/github-auth.ts +24 -14
- package/src/github/github-publish.ts +20 -14
- package/src/new-project/new-project-modal.ts +27 -18
- package/src/panels/activity-bar.ts +27 -26
- package/src/panels/ai-panel.ts +100 -44
- package/src/panels/block-action-bar.ts +84 -52
- package/src/panels/canvas-dnd.ts +47 -28
- package/src/panels/data-explorer.ts +24 -11
- package/src/panels/dnd.ts +148 -100
- package/src/panels/editors.ts +54 -37
- package/src/panels/elements-panel.ts +23 -13
- package/src/panels/events-panel.ts +48 -38
- package/src/panels/git-panel.ts +95 -60
- package/src/panels/head-panel.ts +138 -84
- package/src/panels/imports-panel.ts +66 -29
- package/src/panels/layers-panel.ts +80 -36
- package/src/panels/left-panel.ts +86 -53
- package/src/panels/overlays.ts +36 -19
- package/src/panels/panel-events.ts +48 -27
- package/src/panels/panel-scheduler.ts +41 -23
- package/src/panels/preview-render.ts +26 -26
- package/src/panels/properties-panel.ts +155 -118
- package/src/panels/pseudo-preview.ts +31 -15
- package/src/panels/quick-search.ts +28 -13
- package/src/panels/right-panel.ts +35 -24
- package/src/panels/shared.ts +46 -29
- package/src/panels/signals-panel.ts +303 -186
- package/src/panels/statusbar.ts +27 -12
- package/src/panels/style-inputs.ts +30 -24
- package/src/panels/style-panel.ts +130 -90
- package/src/panels/style-utils.ts +63 -23
- package/src/panels/stylebook-layers-panel.ts +27 -30
- package/src/panels/stylebook-panel.ts +140 -72
- package/src/panels/tab-strip.ts +21 -10
- package/src/panels/toolbar.ts +52 -40
- package/src/panels/welcome-screen.ts +1 -1
- package/src/platform.ts +2 -1
- package/src/platforms/devserver.ts +221 -105
- package/src/recent-projects.ts +15 -7
- package/src/resize-edges.ts +7 -5
- package/src/services/cem-export.ts +73 -57
- package/src/services/code-services.ts +52 -25
- package/src/services/monaco-setup.ts +5 -5
- package/src/settings/content-types-editor.ts +133 -62
- package/src/settings/css-vars-editor.ts +31 -14
- package/src/settings/defs-editor.ts +125 -63
- package/src/settings/general-settings.ts +11 -6
- package/src/settings/head-editor.ts +30 -15
- package/src/settings/schema-field-ui.ts +75 -35
- package/src/settings/settings-modal.ts +35 -17
- package/src/site-context.ts +80 -47
- package/src/state.ts +98 -65
- package/src/store.ts +42 -23
- package/src/studio.ts +165 -144
- package/src/tabs/tab.ts +51 -45
- package/src/tabs/transact.ts +230 -109
- package/src/types.ts +60 -51
- package/src/ui/button-group.ts +3 -2
- package/src/ui/color-selector.ts +33 -15
- package/src/ui/expression-editor.ts +139 -86
- package/src/ui/field-input.ts +35 -18
- package/src/ui/field-row.ts +1 -1
- package/src/ui/icons.ts +5 -6
- package/src/ui/layers.ts +33 -26
- package/src/ui/media-picker.ts +13 -7
- package/src/ui/panel-resize.ts +39 -20
- package/src/ui/spectrum.ts +4 -2
- package/src/ui/unit-selector.ts +20 -11
- package/src/ui/value-selector.ts +13 -7
- package/src/ui/widgets.ts +34 -18
- package/src/utils/canvas-media.ts +66 -33
- package/src/utils/edit-display.ts +43 -44
- package/src/utils/google-fonts.ts +10 -6
- package/src/utils/inherited-style.ts +22 -8
- package/src/utils/studio-utils.ts +88 -45
- package/src/view.ts +9 -5
- 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 {
|
|
13
|
+
import { addFieldFormTpl, detectFieldFormat, fieldCardTpl, schemaForType } from "./schema-field-ui";
|
|
14
14
|
|
|
15
|
-
import type {
|
|
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 = {
|
|
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)
|
|
51
|
+
if (!name) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
47
54
|
|
|
48
55
|
const config = projectState?.projectConfig;
|
|
49
|
-
if (!config)
|
|
50
|
-
|
|
51
|
-
|
|
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)
|
|
82
|
+
if (!name || !selectedDef) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
70
85
|
|
|
71
86
|
const def = getSelectedDef();
|
|
72
|
-
if (!def)
|
|
87
|
+
if (!def) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
73
90
|
|
|
74
|
-
if (!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)
|
|
79
|
-
|
|
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 = {
|
|
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)
|
|
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)
|
|
112
|
-
|
|
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
|
|
116
|
-
|
|
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])
|
|
161
|
+
if (!def?.properties || !newName || def.properties[newName]) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
130
164
|
|
|
131
|
-
const newProps: Record<string,
|
|
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])
|
|
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])
|
|
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)
|
|
231
|
+
if (!parent) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
194
234
|
|
|
195
|
-
if (!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)
|
|
200
|
-
|
|
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)
|
|
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)
|
|
235
|
-
|
|
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
|
|
239
|
-
|
|
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])
|
|
314
|
+
if (!parent?.properties || !newChild || parent.properties[newChild]) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
260
317
|
|
|
261
|
-
const newProps: Record<string,
|
|
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])
|
|
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])
|
|
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)
|
|
386
|
+
if (!selectedDef) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
326
389
|
const config = projectState?.projectConfig;
|
|
327
|
-
if (!config?.$defs?.[selectedDef])
|
|
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")
|
|
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:
|
|
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
|
-
|
|
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.
|
|
20
|
+
input.addEventListener("change", async () => {
|
|
22
21
|
const file = input.files?.[0];
|
|
23
|
-
if (!file)
|
|
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)
|
|
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 = {
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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")
|
|
278
|
+
if (e.key !== "Enter") {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
270
281
|
const family = (e.target as HTMLInputElement).value?.trim();
|
|
271
|
-
if (!family)
|
|
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)
|
|
294
|
+
if (!family) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
282
297
|
(input as HTMLInputElement).value = "";
|
|
283
298
|
addFont(family);
|
|
284
299
|
}}
|