@jxsuite/studio 0.17.0 → 0.18.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.
- package/dist/studio.js +1678 -1512
- package/dist/studio.js.map +20 -16
- package/package.json +2 -2
- package/src/canvas/canvas-render.js +2 -2
- package/src/canvas/canvas-utils.js +1 -1
- package/src/files/files.js +13 -1
- package/src/panels/activity-bar.js +12 -0
- package/src/panels/left-panel.js +2 -2
- package/src/panels/overlays.js +2 -2
- package/src/panels/style-panel.js +1 -1
- package/src/panels/stylebook-panel.js +30 -529
- package/src/panels/toolbar.js +7 -2
- package/src/settings/css-vars-editor.js +307 -0
- package/src/settings/general-settings.js +94 -0
- package/src/settings/head-editor.js +184 -0
- package/src/settings/settings-modal.js +117 -0
package/dist/studio.js
CHANGED
|
@@ -47856,7 +47856,7 @@ function applyTransform() {
|
|
|
47856
47856
|
view.panzoomWrap.style.transform = `translate(${view.panX}px, ${view.panY}px) scale(${zoom})`;
|
|
47857
47857
|
renderZoomIndicator();
|
|
47858
47858
|
renderOnly("overlays");
|
|
47859
|
-
if (_ctx4.getCanvasMode() === "
|
|
47859
|
+
if (_ctx4.getCanvasMode() === "stylebook")
|
|
47860
47860
|
_ctx4.renderStylebookOverlays();
|
|
47861
47861
|
}
|
|
47862
47862
|
function fitToScreen() {
|
|
@@ -180100,1071 +180100,6 @@ init_components();
|
|
|
180100
180100
|
init_store();
|
|
180101
180101
|
init_workspace();
|
|
180102
180102
|
init_components();
|
|
180103
|
-
|
|
180104
|
-
// src/utils/studio-utils.js
|
|
180105
|
-
function camelToKebab2(str) {
|
|
180106
|
-
return str.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase());
|
|
180107
|
-
}
|
|
180108
|
-
function camelToLabel(prop) {
|
|
180109
|
-
return prop.replace(/([A-Z])/g, " $1").replace(/^./, (c) => c.toUpperCase());
|
|
180110
|
-
}
|
|
180111
|
-
function kebabToLabel(val) {
|
|
180112
|
-
return val.replace(/(^|-)(\w)/g, (_, sep2, c) => (sep2 ? " " : "") + c.toUpperCase());
|
|
180113
|
-
}
|
|
180114
|
-
function propLabel(entry, prop) {
|
|
180115
|
-
return entry?.$label || camelToLabel(prop);
|
|
180116
|
-
}
|
|
180117
|
-
function attrLabel(entry, attr) {
|
|
180118
|
-
if (entry?.$label)
|
|
180119
|
-
return entry.$label;
|
|
180120
|
-
if (attr.includes("-"))
|
|
180121
|
-
return attr.replace(/(^|-)(\w)/g, (_, sep2, c) => (sep2 ? " " : "") + c.toUpperCase());
|
|
180122
|
-
return camelToLabel(attr);
|
|
180123
|
-
}
|
|
180124
|
-
function abbreviateValue(val) {
|
|
180125
|
-
const map6 = {
|
|
180126
|
-
inline: "inl",
|
|
180127
|
-
"inline-block": "i-blk",
|
|
180128
|
-
"inline-flex": "i-flx",
|
|
180129
|
-
"inline-grid": "i-grd",
|
|
180130
|
-
contents: "cnt",
|
|
180131
|
-
"flow-root": "flow",
|
|
180132
|
-
nowrap: "no-wr",
|
|
180133
|
-
"wrap-reverse": "wr-rev",
|
|
180134
|
-
"flex-start": "start",
|
|
180135
|
-
"flex-end": "end",
|
|
180136
|
-
"space-between": "betw",
|
|
180137
|
-
"space-around": "arnd",
|
|
180138
|
-
"space-evenly": "even",
|
|
180139
|
-
stretch: "str",
|
|
180140
|
-
baseline: "base",
|
|
180141
|
-
normal: "norm",
|
|
180142
|
-
"row-reverse": "row-r",
|
|
180143
|
-
"column-reverse": "col-r",
|
|
180144
|
-
column: "col"
|
|
180145
|
-
};
|
|
180146
|
-
return map6[val] || val;
|
|
180147
|
-
}
|
|
180148
|
-
function inferInputType(entry) {
|
|
180149
|
-
if (entry.$shorthand === true)
|
|
180150
|
-
return "shorthand";
|
|
180151
|
-
if (entry.$input === "button-group")
|
|
180152
|
-
return "button-group";
|
|
180153
|
-
if (entry.$input === "media")
|
|
180154
|
-
return "media";
|
|
180155
|
-
if (entry.format === "color")
|
|
180156
|
-
return "color";
|
|
180157
|
-
if (entry.format === "uri-reference")
|
|
180158
|
-
return "media";
|
|
180159
|
-
if (entry.$units !== undefined)
|
|
180160
|
-
return "number-unit";
|
|
180161
|
-
if (entry.type === "number")
|
|
180162
|
-
return "number";
|
|
180163
|
-
if (Array.isArray(entry.enum))
|
|
180164
|
-
return "select";
|
|
180165
|
-
if (Array.isArray(entry.examples) || Array.isArray(entry.presets))
|
|
180166
|
-
return "combobox";
|
|
180167
|
-
return "text";
|
|
180168
|
-
}
|
|
180169
|
-
function findContentTypeSchema(documentPath, projectConfig) {
|
|
180170
|
-
if (!documentPath || !projectConfig?.contentTypes)
|
|
180171
|
-
return null;
|
|
180172
|
-
for (const [name, def3] of Object.entries(projectConfig.contentTypes)) {
|
|
180173
|
-
if (!def3.source || !def3.schema)
|
|
180174
|
-
continue;
|
|
180175
|
-
const src = def3.source.replace(/^\.\//, "");
|
|
180176
|
-
const dir = src.split("/")[0];
|
|
180177
|
-
const ext = src.includes("*.md") ? ".md" : src.includes("*.json") ? ".json" : src.includes("*.csv") ? ".csv" : null;
|
|
180178
|
-
if (documentPath.startsWith(dir + "/") && (!ext || documentPath.endsWith(ext))) {
|
|
180179
|
-
return { name, schema: def3.schema };
|
|
180180
|
-
}
|
|
180181
|
-
}
|
|
180182
|
-
return null;
|
|
180183
|
-
}
|
|
180184
|
-
function friendlyNameToVar(name, prefix) {
|
|
180185
|
-
const slug = name.trim().toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
180186
|
-
if (!slug)
|
|
180187
|
-
return "";
|
|
180188
|
-
return `${prefix}${slug}`;
|
|
180189
|
-
}
|
|
180190
|
-
function varDisplayName(varName, prefix) {
|
|
180191
|
-
return varName.replace(new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`), "").replace(/^--/, "").replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) || varName;
|
|
180192
|
-
}
|
|
180193
|
-
function parseCemType(typeText) {
|
|
180194
|
-
if (!typeText)
|
|
180195
|
-
return { kind: "text" };
|
|
180196
|
-
const t = typeText.trim().replace(/\s*\|\s*undefined\b/g, "").trim();
|
|
180197
|
-
if (t === "boolean")
|
|
180198
|
-
return { kind: "boolean" };
|
|
180199
|
-
if (t === "number")
|
|
180200
|
-
return { kind: "number" };
|
|
180201
|
-
const enumMatch = t.match(/^'[^']*'(\s*\|\s*'[^']*')+$/);
|
|
180202
|
-
if (enumMatch) {
|
|
180203
|
-
const options = [...t.matchAll(/'([^']*)'/g)].map((m) => m[1]);
|
|
180204
|
-
return { kind: "combobox", options };
|
|
180205
|
-
}
|
|
180206
|
-
return { kind: "text" };
|
|
180207
|
-
}
|
|
180208
|
-
|
|
180209
|
-
// src/settings/defs-editor.js
|
|
180210
|
-
init_platform();
|
|
180211
|
-
init_store();
|
|
180212
|
-
|
|
180213
|
-
// src/settings/schema-field-ui.js
|
|
180214
|
-
var FIELD_TYPES = ["string", "number", "boolean", "array", "object", "reference"];
|
|
180215
|
-
var FORMAT_OPTIONS = ["", "image", "date", "color"];
|
|
180216
|
-
function detectFieldType(schema4) {
|
|
180217
|
-
if (schema4.$ref)
|
|
180218
|
-
return "reference";
|
|
180219
|
-
return schema4.type || "string";
|
|
180220
|
-
}
|
|
180221
|
-
function detectFieldFormat(schema4) {
|
|
180222
|
-
if (schema4.type === "array" && schema4.items?.format)
|
|
180223
|
-
return schema4.items.format;
|
|
180224
|
-
return schema4.format || "";
|
|
180225
|
-
}
|
|
180226
|
-
function fieldCardTpl(fieldName, fieldSchema, isRequired, handlers, contentTypeNames = []) {
|
|
180227
|
-
const type = detectFieldType(fieldSchema);
|
|
180228
|
-
const format2 = detectFieldFormat(fieldSchema);
|
|
180229
|
-
const isNested = type === "object";
|
|
180230
|
-
const isRef2 = type === "reference";
|
|
180231
|
-
const nestedProps = fieldSchema.properties || {};
|
|
180232
|
-
const nestedRequired = fieldSchema.required || [];
|
|
180233
|
-
const refTarget = fieldSchema.$ref ? fieldSchema.$ref.replace("#/contentTypes/", "") : "";
|
|
180234
|
-
return html`
|
|
180235
|
-
<div class="schema-field-card">
|
|
180236
|
-
<div class="schema-field-row">
|
|
180237
|
-
<sp-textfield
|
|
180238
|
-
size="s"
|
|
180239
|
-
quiet
|
|
180240
|
-
value=${fieldName}
|
|
180241
|
-
class="schema-field-name-input"
|
|
180242
|
-
@change=${(e) => {
|
|
180243
|
-
const newName = e.target.value.trim();
|
|
180244
|
-
if (newName && newName !== fieldName)
|
|
180245
|
-
handlers.onRename(fieldName, newName);
|
|
180246
|
-
else
|
|
180247
|
-
e.target.value = fieldName;
|
|
180248
|
-
}}
|
|
180249
|
-
@keydown=${(e) => {
|
|
180250
|
-
if (e.key === "Enter")
|
|
180251
|
-
e.target.blur();
|
|
180252
|
-
if (e.key === "Escape") {
|
|
180253
|
-
e.target.value = fieldName;
|
|
180254
|
-
e.target.blur();
|
|
180255
|
-
}
|
|
180256
|
-
}}
|
|
180257
|
-
></sp-textfield>
|
|
180258
|
-
${typePickerTpl(type, (newType) => handlers.onChangeType(fieldName, newType))}
|
|
180259
|
-
${type === "string" || type === "array" ? formatPickerTpl(format2, (f) => {
|
|
180260
|
-
if (handlers.onChangeFormat)
|
|
180261
|
-
handlers.onChangeFormat(fieldName, f);
|
|
180262
|
-
}) : nothing}
|
|
180263
|
-
<sp-switch
|
|
180264
|
-
size="s"
|
|
180265
|
-
?checked=${isRequired}
|
|
180266
|
-
@change=${() => handlers.onToggleRequired(fieldName)}
|
|
180267
|
-
>
|
|
180268
|
-
Req
|
|
180269
|
-
</sp-switch>
|
|
180270
|
-
<sp-action-button
|
|
180271
|
-
size="xs"
|
|
180272
|
-
quiet
|
|
180273
|
-
title="Delete field"
|
|
180274
|
-
@click=${() => handlers.onDelete(fieldName)}
|
|
180275
|
-
>
|
|
180276
|
-
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
180277
|
-
</sp-action-button>
|
|
180278
|
-
</div>
|
|
180279
|
-
${isRef2 && contentTypeNames.length ? html`
|
|
180280
|
-
<div class="schema-field-ref-target">
|
|
180281
|
-
<sp-picker
|
|
180282
|
-
size="s"
|
|
180283
|
-
label="Target"
|
|
180284
|
-
value=${refTarget}
|
|
180285
|
-
@change=${(e) => {
|
|
180286
|
-
if (handlers.onChangeRefTarget)
|
|
180287
|
-
handlers.onChangeRefTarget(fieldName, e.target.value);
|
|
180288
|
-
}}
|
|
180289
|
-
>
|
|
180290
|
-
${contentTypeNames.map((n2) => html`<sp-menu-item value=${n2}>${n2}</sp-menu-item>`)}
|
|
180291
|
-
</sp-picker>
|
|
180292
|
-
</div>
|
|
180293
|
-
` : nothing}
|
|
180294
|
-
${isNested ? html`
|
|
180295
|
-
<div class="schema-field-nested">
|
|
180296
|
-
${Object.entries(nestedProps).map(([name, sub]) => nestedFieldCardTpl(fieldName, name, sub, nestedRequired.includes(name), handlers))}
|
|
180297
|
-
${nestedAddFieldTpl(fieldName, handlers)}
|
|
180298
|
-
</div>
|
|
180299
|
-
` : nothing}
|
|
180300
|
-
</div>
|
|
180301
|
-
`;
|
|
180302
|
-
}
|
|
180303
|
-
function nestedFieldCardTpl(parentName, childName, childSchema, isRequired, handlers) {
|
|
180304
|
-
const type = detectFieldType(childSchema);
|
|
180305
|
-
const format2 = detectFieldFormat(childSchema);
|
|
180306
|
-
return html`
|
|
180307
|
-
<div class="schema-field-card schema-field-card--nested">
|
|
180308
|
-
<div class="schema-field-row">
|
|
180309
|
-
<sp-textfield
|
|
180310
|
-
size="s"
|
|
180311
|
-
quiet
|
|
180312
|
-
value=${childName}
|
|
180313
|
-
class="schema-field-name-input"
|
|
180314
|
-
@change=${(e) => {
|
|
180315
|
-
const newName = e.target.value.trim();
|
|
180316
|
-
if (newName && newName !== childName && handlers.onRenameNested) {
|
|
180317
|
-
handlers.onRenameNested(parentName, childName, newName);
|
|
180318
|
-
} else {
|
|
180319
|
-
e.target.value = childName;
|
|
180320
|
-
}
|
|
180321
|
-
}}
|
|
180322
|
-
@keydown=${(e) => {
|
|
180323
|
-
if (e.key === "Enter")
|
|
180324
|
-
e.target.blur();
|
|
180325
|
-
if (e.key === "Escape") {
|
|
180326
|
-
e.target.value = childName;
|
|
180327
|
-
e.target.blur();
|
|
180328
|
-
}
|
|
180329
|
-
}}
|
|
180330
|
-
></sp-textfield>
|
|
180331
|
-
${typePickerTpl(type, (newType) => {
|
|
180332
|
-
if (handlers.onChangeNestedType)
|
|
180333
|
-
handlers.onChangeNestedType(parentName, childName, newType);
|
|
180334
|
-
})}
|
|
180335
|
-
${type === "string" || type === "array" ? formatPickerTpl(format2, (f) => {
|
|
180336
|
-
if (handlers.onChangeNestedFormat)
|
|
180337
|
-
handlers.onChangeNestedFormat(parentName, childName, f);
|
|
180338
|
-
}) : nothing}
|
|
180339
|
-
<sp-switch
|
|
180340
|
-
size="s"
|
|
180341
|
-
?checked=${isRequired}
|
|
180342
|
-
@change=${() => {
|
|
180343
|
-
if (handlers.onToggleNestedRequired)
|
|
180344
|
-
handlers.onToggleNestedRequired(parentName, childName);
|
|
180345
|
-
}}
|
|
180346
|
-
>
|
|
180347
|
-
Req
|
|
180348
|
-
</sp-switch>
|
|
180349
|
-
<sp-action-button
|
|
180350
|
-
size="xs"
|
|
180351
|
-
quiet
|
|
180352
|
-
title="Delete field"
|
|
180353
|
-
@click=${() => {
|
|
180354
|
-
if (handlers.onDeleteNested)
|
|
180355
|
-
handlers.onDeleteNested(parentName, childName);
|
|
180356
|
-
}}
|
|
180357
|
-
>
|
|
180358
|
-
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
180359
|
-
</sp-action-button>
|
|
180360
|
-
</div>
|
|
180361
|
-
</div>
|
|
180362
|
-
`;
|
|
180363
|
-
}
|
|
180364
|
-
function nestedAddFieldTpl(parentName, handlers) {
|
|
180365
|
-
return html`
|
|
180366
|
-
<div class="schema-nested-add">
|
|
180367
|
-
<sp-textfield
|
|
180368
|
-
size="s"
|
|
180369
|
-
placeholder="field name"
|
|
180370
|
-
class="schema-nested-add-name"
|
|
180371
|
-
@keydown=${(e) => {
|
|
180372
|
-
if (e.key === "Enter") {
|
|
180373
|
-
const row = e.target.closest(".schema-nested-add");
|
|
180374
|
-
const name = e.target.value.trim();
|
|
180375
|
-
const typePicker = row?.querySelector("sp-picker");
|
|
180376
|
-
const type = typePicker?.value || "string";
|
|
180377
|
-
if (name && handlers.onAddNestedField) {
|
|
180378
|
-
handlers.onAddNestedField(parentName, { name, type, required: false });
|
|
180379
|
-
e.target.value = "";
|
|
180380
|
-
}
|
|
180381
|
-
}
|
|
180382
|
-
}}
|
|
180383
|
-
></sp-textfield>
|
|
180384
|
-
${typePickerTpl("string", () => {})}
|
|
180385
|
-
<sp-action-button
|
|
180386
|
-
size="xs"
|
|
180387
|
-
quiet
|
|
180388
|
-
title="Add nested field"
|
|
180389
|
-
@click=${(e) => {
|
|
180390
|
-
const row = e.target.closest(".schema-nested-add");
|
|
180391
|
-
const nameInput = row?.querySelector(".schema-nested-add-name");
|
|
180392
|
-
const typePicker = row?.querySelector("sp-picker");
|
|
180393
|
-
const name = nameInput?.value?.trim();
|
|
180394
|
-
const type = typePicker?.value || "string";
|
|
180395
|
-
if (name && handlers.onAddNestedField) {
|
|
180396
|
-
handlers.onAddNestedField(parentName, { name, type, required: false });
|
|
180397
|
-
nameInput.value = "";
|
|
180398
|
-
}
|
|
180399
|
-
}}
|
|
180400
|
-
>
|
|
180401
|
-
<sp-icon-add slot="icon"></sp-icon-add>
|
|
180402
|
-
</sp-action-button>
|
|
180403
|
-
</div>
|
|
180404
|
-
`;
|
|
180405
|
-
}
|
|
180406
|
-
function typePickerTpl(value2, onChange) {
|
|
180407
|
-
return html`
|
|
180408
|
-
<sp-picker
|
|
180409
|
-
size="s"
|
|
180410
|
-
label="Type"
|
|
180411
|
-
value=${value2}
|
|
180412
|
-
@change=${(e) => onChange(e.target.value)}
|
|
180413
|
-
>
|
|
180414
|
-
${FIELD_TYPES.map((t) => html`<sp-menu-item value=${t}>${t}</sp-menu-item>`)}
|
|
180415
|
-
</sp-picker>
|
|
180416
|
-
`;
|
|
180417
|
-
}
|
|
180418
|
-
function formatPickerTpl(value2, onChange) {
|
|
180419
|
-
return html`
|
|
180420
|
-
<sp-picker
|
|
180421
|
-
size="s"
|
|
180422
|
-
label="Format"
|
|
180423
|
-
value=${value2}
|
|
180424
|
-
@change=${(e) => onChange(e.target.value)}
|
|
180425
|
-
>
|
|
180426
|
-
${FORMAT_OPTIONS.map((f) => html`<sp-menu-item value=${f}>${f || "(none)"}</sp-menu-item>`)}
|
|
180427
|
-
</sp-picker>
|
|
180428
|
-
`;
|
|
180429
|
-
}
|
|
180430
|
-
function addFieldFormTpl(state, handlers) {
|
|
180431
|
-
return html`
|
|
180432
|
-
<div class="schema-add-field">
|
|
180433
|
-
<sp-textfield
|
|
180434
|
-
size="s"
|
|
180435
|
-
placeholder="Field name"
|
|
180436
|
-
.value=${state.name}
|
|
180437
|
-
@input=${(e) => handlers.onInput("name", e.target.value)}
|
|
180438
|
-
@keydown=${(e) => {
|
|
180439
|
-
if (e.key === "Enter")
|
|
180440
|
-
handlers.onConfirm();
|
|
180441
|
-
if (e.key === "Escape")
|
|
180442
|
-
handlers.onCancel();
|
|
180443
|
-
}}
|
|
180444
|
-
></sp-textfield>
|
|
180445
|
-
${typePickerTpl(state.type, (t) => handlers.onInput("type", t))}
|
|
180446
|
-
${state.type === "string" || state.type === "array" ? formatPickerTpl(state.format || "", (f) => handlers.onInput("format", f)) : nothing}
|
|
180447
|
-
<sp-switch
|
|
180448
|
-
size="s"
|
|
180449
|
-
?checked=${state.required}
|
|
180450
|
-
@change=${(e) => handlers.onInput("required", e.target.checked)}
|
|
180451
|
-
>
|
|
180452
|
-
Required
|
|
180453
|
-
</sp-switch>
|
|
180454
|
-
<sp-action-button size="s" @click=${handlers.onConfirm}>Add</sp-action-button>
|
|
180455
|
-
<sp-action-button size="s" quiet @click=${handlers.onCancel}>Cancel</sp-action-button>
|
|
180456
|
-
</div>
|
|
180457
|
-
`;
|
|
180458
|
-
}
|
|
180459
|
-
function schemaForType(type, format2) {
|
|
180460
|
-
switch (type) {
|
|
180461
|
-
case "number":
|
|
180462
|
-
return { type: "number" };
|
|
180463
|
-
case "boolean":
|
|
180464
|
-
return { type: "boolean" };
|
|
180465
|
-
case "array":
|
|
180466
|
-
return format2 ? { type: "array", items: { type: "string", format: format2 } } : { type: "array", items: { type: "string" } };
|
|
180467
|
-
case "object":
|
|
180468
|
-
return { type: "object", properties: {}, required: [] };
|
|
180469
|
-
case "reference":
|
|
180470
|
-
return { $ref: "#/contentTypes/" };
|
|
180471
|
-
default:
|
|
180472
|
-
return format2 ? { type: "string", format: format2 } : { type: "string" };
|
|
180473
|
-
}
|
|
180474
|
-
}
|
|
180475
|
-
function yamlDefault(type, format2) {
|
|
180476
|
-
if (format2 === "date")
|
|
180477
|
-
return new Date().toISOString().split("T")[0];
|
|
180478
|
-
if (format2 === "image")
|
|
180479
|
-
return '""';
|
|
180480
|
-
switch (type) {
|
|
180481
|
-
case "boolean":
|
|
180482
|
-
return "false";
|
|
180483
|
-
case "number":
|
|
180484
|
-
return "0";
|
|
180485
|
-
case "array":
|
|
180486
|
-
return "[]";
|
|
180487
|
-
case "object":
|
|
180488
|
-
return "{}";
|
|
180489
|
-
default:
|
|
180490
|
-
return '""';
|
|
180491
|
-
}
|
|
180492
|
-
}
|
|
180493
|
-
|
|
180494
|
-
// src/settings/defs-editor.js
|
|
180495
|
-
var selectedDef = null;
|
|
180496
|
-
var showAddField = false;
|
|
180497
|
-
var newFieldState = { name: "", type: "string", format: "", required: false };
|
|
180498
|
-
var showNewDef = false;
|
|
180499
|
-
var newDefName = "";
|
|
180500
|
-
async function saveProjectConfig() {
|
|
180501
|
-
const platform3 = getPlatform();
|
|
180502
|
-
const config = projectState.projectConfig;
|
|
180503
|
-
await platform3.writeFile("project.json", JSON.stringify(config, null, "\t"));
|
|
180504
|
-
}
|
|
180505
|
-
function getSelectedDef() {
|
|
180506
|
-
const config = projectState?.projectConfig;
|
|
180507
|
-
return config?.$defs?.[selectedDef];
|
|
180508
|
-
}
|
|
180509
|
-
function handleNewDef(rerender) {
|
|
180510
|
-
const name = newDefName.trim();
|
|
180511
|
-
if (!name)
|
|
180512
|
-
return;
|
|
180513
|
-
const config = projectState?.projectConfig;
|
|
180514
|
-
if (!config)
|
|
180515
|
-
return;
|
|
180516
|
-
if (!config.$defs)
|
|
180517
|
-
config.$defs = {};
|
|
180518
|
-
if (config.$defs[name])
|
|
180519
|
-
return;
|
|
180520
|
-
config.$defs[name] = {
|
|
180521
|
-
type: "object",
|
|
180522
|
-
properties: {},
|
|
180523
|
-
required: []
|
|
180524
|
-
};
|
|
180525
|
-
selectedDef = name;
|
|
180526
|
-
showNewDef = false;
|
|
180527
|
-
newDefName = "";
|
|
180528
|
-
rerender();
|
|
180529
|
-
saveProjectConfig();
|
|
180530
|
-
}
|
|
180531
|
-
function handleAddField(rerender) {
|
|
180532
|
-
const name = newFieldState.name.trim();
|
|
180533
|
-
if (!name || !selectedDef)
|
|
180534
|
-
return;
|
|
180535
|
-
const def3 = getSelectedDef();
|
|
180536
|
-
if (!def3)
|
|
180537
|
-
return;
|
|
180538
|
-
if (!def3.properties)
|
|
180539
|
-
def3.properties = {};
|
|
180540
|
-
def3.properties[name] = schemaForType(newFieldState.type, newFieldState.format || undefined);
|
|
180541
|
-
if (newFieldState.required) {
|
|
180542
|
-
if (!def3.required)
|
|
180543
|
-
def3.required = [];
|
|
180544
|
-
if (!def3.required.includes(name))
|
|
180545
|
-
def3.required.push(name);
|
|
180546
|
-
}
|
|
180547
|
-
showAddField = false;
|
|
180548
|
-
newFieldState = { name: "", type: "string", format: "", required: false };
|
|
180549
|
-
rerender();
|
|
180550
|
-
saveProjectConfig();
|
|
180551
|
-
}
|
|
180552
|
-
function handleDeleteField(fieldName, rerender) {
|
|
180553
|
-
const def3 = getSelectedDef();
|
|
180554
|
-
if (!def3?.properties)
|
|
180555
|
-
return;
|
|
180556
|
-
delete def3.properties[fieldName];
|
|
180557
|
-
if (def3.required) {
|
|
180558
|
-
def3.required = def3.required.filter((r) => r !== fieldName);
|
|
180559
|
-
}
|
|
180560
|
-
rerender();
|
|
180561
|
-
saveProjectConfig();
|
|
180562
|
-
}
|
|
180563
|
-
function handleToggleRequired(fieldName, rerender) {
|
|
180564
|
-
const def3 = getSelectedDef();
|
|
180565
|
-
if (!def3)
|
|
180566
|
-
return;
|
|
180567
|
-
if (!def3.required)
|
|
180568
|
-
def3.required = [];
|
|
180569
|
-
const idx = def3.required.indexOf(fieldName);
|
|
180570
|
-
if (idx >= 0)
|
|
180571
|
-
def3.required.splice(idx, 1);
|
|
180572
|
-
else
|
|
180573
|
-
def3.required.push(fieldName);
|
|
180574
|
-
rerender();
|
|
180575
|
-
saveProjectConfig();
|
|
180576
|
-
}
|
|
180577
|
-
function handleRenameField(oldName, newName, rerender) {
|
|
180578
|
-
const def3 = getSelectedDef();
|
|
180579
|
-
if (!def3?.properties || !newName || def3.properties[newName])
|
|
180580
|
-
return;
|
|
180581
|
-
const newProps = {};
|
|
180582
|
-
for (const [key, val] of Object.entries(def3.properties)) {
|
|
180583
|
-
newProps[key === oldName ? newName : key] = val;
|
|
180584
|
-
}
|
|
180585
|
-
def3.properties = newProps;
|
|
180586
|
-
if (def3.required) {
|
|
180587
|
-
def3.required = def3.required.map((r) => r === oldName ? newName : r);
|
|
180588
|
-
}
|
|
180589
|
-
rerender();
|
|
180590
|
-
saveProjectConfig();
|
|
180591
|
-
}
|
|
180592
|
-
function handleChangeType(fieldName, newType, rerender) {
|
|
180593
|
-
const def3 = getSelectedDef();
|
|
180594
|
-
if (!def3?.properties?.[fieldName])
|
|
180595
|
-
return;
|
|
180596
|
-
const oldFormat = newType === "string" || newType === "array" ? detectFieldFormat(def3.properties[fieldName]) : undefined;
|
|
180597
|
-
def3.properties[fieldName] = schemaForType(newType, oldFormat || undefined);
|
|
180598
|
-
rerender();
|
|
180599
|
-
saveProjectConfig();
|
|
180600
|
-
}
|
|
180601
|
-
function handleChangeFormat(fieldName, format2, rerender) {
|
|
180602
|
-
const def3 = getSelectedDef();
|
|
180603
|
-
if (!def3?.properties?.[fieldName])
|
|
180604
|
-
return;
|
|
180605
|
-
const prop = def3.properties[fieldName];
|
|
180606
|
-
const type = prop.type || "string";
|
|
180607
|
-
def3.properties[fieldName] = schemaForType(type, format2 || undefined);
|
|
180608
|
-
rerender();
|
|
180609
|
-
saveProjectConfig();
|
|
180610
|
-
}
|
|
180611
|
-
function handleAddNestedField(parentName, fieldState, rerender) {
|
|
180612
|
-
const def3 = getSelectedDef();
|
|
180613
|
-
const parent = def3?.properties?.[parentName];
|
|
180614
|
-
if (!parent)
|
|
180615
|
-
return;
|
|
180616
|
-
if (!parent.properties)
|
|
180617
|
-
parent.properties = {};
|
|
180618
|
-
parent.properties[fieldState.name] = schemaForType(fieldState.type);
|
|
180619
|
-
if (fieldState.required) {
|
|
180620
|
-
if (!parent.required)
|
|
180621
|
-
parent.required = [];
|
|
180622
|
-
if (!parent.required.includes(fieldState.name))
|
|
180623
|
-
parent.required.push(fieldState.name);
|
|
180624
|
-
}
|
|
180625
|
-
rerender();
|
|
180626
|
-
saveProjectConfig();
|
|
180627
|
-
}
|
|
180628
|
-
function handleDeleteNested(parentName, childName, rerender) {
|
|
180629
|
-
const def3 = getSelectedDef();
|
|
180630
|
-
const parent = def3?.properties?.[parentName];
|
|
180631
|
-
if (!parent?.properties)
|
|
180632
|
-
return;
|
|
180633
|
-
delete parent.properties[childName];
|
|
180634
|
-
if (parent.required) {
|
|
180635
|
-
parent.required = parent.required.filter((r) => r !== childName);
|
|
180636
|
-
}
|
|
180637
|
-
rerender();
|
|
180638
|
-
saveProjectConfig();
|
|
180639
|
-
}
|
|
180640
|
-
function handleToggleNestedRequired(parentName, childName, rerender) {
|
|
180641
|
-
const def3 = getSelectedDef();
|
|
180642
|
-
const parent = def3?.properties?.[parentName];
|
|
180643
|
-
if (!parent)
|
|
180644
|
-
return;
|
|
180645
|
-
if (!parent.required)
|
|
180646
|
-
parent.required = [];
|
|
180647
|
-
const idx = parent.required.indexOf(childName);
|
|
180648
|
-
if (idx >= 0)
|
|
180649
|
-
parent.required.splice(idx, 1);
|
|
180650
|
-
else
|
|
180651
|
-
parent.required.push(childName);
|
|
180652
|
-
rerender();
|
|
180653
|
-
saveProjectConfig();
|
|
180654
|
-
}
|
|
180655
|
-
function handleRenameNested(parentName, oldChild, newChild, rerender) {
|
|
180656
|
-
const def3 = getSelectedDef();
|
|
180657
|
-
const parent = def3?.properties?.[parentName];
|
|
180658
|
-
if (!parent?.properties || !newChild || parent.properties[newChild])
|
|
180659
|
-
return;
|
|
180660
|
-
const newProps = {};
|
|
180661
|
-
for (const [key, val] of Object.entries(parent.properties)) {
|
|
180662
|
-
newProps[key === oldChild ? newChild : key] = val;
|
|
180663
|
-
}
|
|
180664
|
-
parent.properties = newProps;
|
|
180665
|
-
if (parent.required) {
|
|
180666
|
-
parent.required = parent.required.map((r) => r === oldChild ? newChild : r);
|
|
180667
|
-
}
|
|
180668
|
-
rerender();
|
|
180669
|
-
saveProjectConfig();
|
|
180670
|
-
}
|
|
180671
|
-
function handleChangeNestedType(parentName, childName, newType, rerender) {
|
|
180672
|
-
const def3 = getSelectedDef();
|
|
180673
|
-
const parent = def3?.properties?.[parentName];
|
|
180674
|
-
if (!parent?.properties?.[childName])
|
|
180675
|
-
return;
|
|
180676
|
-
const oldFormat = newType === "string" || newType === "array" ? detectFieldFormat(parent.properties[childName]) : undefined;
|
|
180677
|
-
parent.properties[childName] = schemaForType(newType, oldFormat || undefined);
|
|
180678
|
-
rerender();
|
|
180679
|
-
saveProjectConfig();
|
|
180680
|
-
}
|
|
180681
|
-
function handleChangeNestedFormat(parentName, childName, format2, rerender) {
|
|
180682
|
-
const def3 = getSelectedDef();
|
|
180683
|
-
const parent = def3?.properties?.[parentName];
|
|
180684
|
-
if (!parent?.properties?.[childName])
|
|
180685
|
-
return;
|
|
180686
|
-
const prop = parent.properties[childName];
|
|
180687
|
-
const type = prop.type || "string";
|
|
180688
|
-
parent.properties[childName] = schemaForType(type, format2 || undefined);
|
|
180689
|
-
rerender();
|
|
180690
|
-
saveProjectConfig();
|
|
180691
|
-
}
|
|
180692
|
-
function handleDeleteDef(rerender) {
|
|
180693
|
-
if (!selectedDef)
|
|
180694
|
-
return;
|
|
180695
|
-
const config = projectState?.projectConfig;
|
|
180696
|
-
if (!config?.$defs?.[selectedDef])
|
|
180697
|
-
return;
|
|
180698
|
-
delete config.$defs[selectedDef];
|
|
180699
|
-
selectedDef = null;
|
|
180700
|
-
rerender();
|
|
180701
|
-
saveProjectConfig();
|
|
180702
|
-
}
|
|
180703
|
-
function renderDefsEditor(container) {
|
|
180704
|
-
const rerender = () => renderDefsEditor(container);
|
|
180705
|
-
const config = projectState?.projectConfig;
|
|
180706
|
-
const defs = config?.$defs || {};
|
|
180707
|
-
const defNames = Object.keys(defs);
|
|
180708
|
-
const listTpl = html`
|
|
180709
|
-
<div class="settings-list-panel">
|
|
180710
|
-
${defNames.map((name) => html`
|
|
180711
|
-
<sp-action-button
|
|
180712
|
-
size="s"
|
|
180713
|
-
?selected=${selectedDef === name}
|
|
180714
|
-
@click=${() => {
|
|
180715
|
-
selectedDef = name;
|
|
180716
|
-
showAddField = false;
|
|
180717
|
-
rerender();
|
|
180718
|
-
}}
|
|
180719
|
-
>
|
|
180720
|
-
${name}
|
|
180721
|
-
</sp-action-button>
|
|
180722
|
-
`)}
|
|
180723
|
-
${showNewDef ? html`
|
|
180724
|
-
<div class="settings-inline-form">
|
|
180725
|
-
<sp-textfield
|
|
180726
|
-
size="s"
|
|
180727
|
-
placeholder="TypeName"
|
|
180728
|
-
.value=${newDefName}
|
|
180729
|
-
@input=${(e) => {
|
|
180730
|
-
newDefName = e.target.value;
|
|
180731
|
-
}}
|
|
180732
|
-
@keydown=${(e) => {
|
|
180733
|
-
if (e.key === "Enter")
|
|
180734
|
-
handleNewDef(rerender);
|
|
180735
|
-
if (e.key === "Escape") {
|
|
180736
|
-
showNewDef = false;
|
|
180737
|
-
rerender();
|
|
180738
|
-
}
|
|
180739
|
-
}}
|
|
180740
|
-
></sp-textfield>
|
|
180741
|
-
<sp-action-button size="s" @click=${() => handleNewDef(rerender)}>
|
|
180742
|
-
Create
|
|
180743
|
-
</sp-action-button>
|
|
180744
|
-
</div>
|
|
180745
|
-
` : html`
|
|
180746
|
-
<sp-action-button
|
|
180747
|
-
size="s"
|
|
180748
|
-
quiet
|
|
180749
|
-
@click=${() => {
|
|
180750
|
-
showNewDef = true;
|
|
180751
|
-
rerender();
|
|
180752
|
-
}}
|
|
180753
|
-
>
|
|
180754
|
-
<sp-icon-add slot="icon"></sp-icon-add> New Definition
|
|
180755
|
-
</sp-action-button>
|
|
180756
|
-
`}
|
|
180757
|
-
</div>
|
|
180758
|
-
`;
|
|
180759
|
-
let editorTpl;
|
|
180760
|
-
if (!selectedDef || !defs[selectedDef]) {
|
|
180761
|
-
editorTpl = html`<div class="settings-empty-state">Select or create a type definition</div>`;
|
|
180762
|
-
} else {
|
|
180763
|
-
const def3 = defs[selectedDef];
|
|
180764
|
-
const properties = def3.properties || {};
|
|
180765
|
-
const required = def3.required || [];
|
|
180766
|
-
const handlers = {
|
|
180767
|
-
onDelete: (n2) => handleDeleteField(n2, rerender),
|
|
180768
|
-
onToggleRequired: (n2) => handleToggleRequired(n2, rerender),
|
|
180769
|
-
onRename: (oldN, newN) => handleRenameField(oldN, newN, rerender),
|
|
180770
|
-
onChangeType: (n2, t) => handleChangeType(n2, t, rerender),
|
|
180771
|
-
onChangeFormat: (n2, f) => handleChangeFormat(n2, f, rerender),
|
|
180772
|
-
onAddNestedField: (p, s2) => handleAddNestedField(p, s2, rerender),
|
|
180773
|
-
onDeleteNested: (p, c) => handleDeleteNested(p, c, rerender),
|
|
180774
|
-
onToggleNestedRequired: (p, c) => handleToggleNestedRequired(p, c, rerender),
|
|
180775
|
-
onRenameNested: (p, o, n2) => handleRenameNested(p, o, n2, rerender),
|
|
180776
|
-
onChangeNestedType: (p, c, t) => handleChangeNestedType(p, c, t, rerender),
|
|
180777
|
-
onChangeNestedFormat: (p, c, f) => handleChangeNestedFormat(p, c, f, rerender)
|
|
180778
|
-
};
|
|
180779
|
-
const fieldCards = Object.entries(properties).map(([name, fieldDef]) => fieldCardTpl(name, fieldDef, required.includes(name), handlers));
|
|
180780
|
-
editorTpl = html`
|
|
180781
|
-
<div class="settings-editor-panel">
|
|
180782
|
-
<div class="settings-editor-header">
|
|
180783
|
-
<h3>${selectedDef}</h3>
|
|
180784
|
-
<sp-action-button
|
|
180785
|
-
size="xs"
|
|
180786
|
-
quiet
|
|
180787
|
-
title="Delete definition"
|
|
180788
|
-
@click=${() => handleDeleteDef(rerender)}
|
|
180789
|
-
>
|
|
180790
|
-
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
180791
|
-
</sp-action-button>
|
|
180792
|
-
</div>
|
|
180793
|
-
<div class="schema-field-list">${fieldCards}</div>
|
|
180794
|
-
${showAddField ? addFieldFormTpl(newFieldState, {
|
|
180795
|
-
onInput: (field, value2) => {
|
|
180796
|
-
newFieldState = { ...newFieldState, [field]: value2 };
|
|
180797
|
-
rerender();
|
|
180798
|
-
},
|
|
180799
|
-
onConfirm: () => handleAddField(rerender),
|
|
180800
|
-
onCancel: () => {
|
|
180801
|
-
showAddField = false;
|
|
180802
|
-
newFieldState = { name: "", type: "string", format: "", required: false };
|
|
180803
|
-
rerender();
|
|
180804
|
-
}
|
|
180805
|
-
}) : html`
|
|
180806
|
-
<sp-action-button
|
|
180807
|
-
size="s"
|
|
180808
|
-
quiet
|
|
180809
|
-
@click=${() => {
|
|
180810
|
-
showAddField = true;
|
|
180811
|
-
rerender();
|
|
180812
|
-
}}
|
|
180813
|
-
>
|
|
180814
|
-
<sp-icon-add slot="icon"></sp-icon-add> Add Field
|
|
180815
|
-
</sp-action-button>
|
|
180816
|
-
`}
|
|
180817
|
-
</div>
|
|
180818
|
-
`;
|
|
180819
|
-
}
|
|
180820
|
-
const tpl = html` <div class="settings-two-col">${listTpl} ${editorTpl}</div> `;
|
|
180821
|
-
render2(tpl, container);
|
|
180822
|
-
}
|
|
180823
|
-
|
|
180824
|
-
// src/settings/content-types-editor.js
|
|
180825
|
-
init_platform();
|
|
180826
|
-
init_store();
|
|
180827
|
-
var selectedContentType = null;
|
|
180828
|
-
var showAddField2 = false;
|
|
180829
|
-
var newFieldState2 = { name: "", type: "string", format: "", required: false };
|
|
180830
|
-
var showNewContentType = false;
|
|
180831
|
-
var newContentTypeName = "";
|
|
180832
|
-
async function saveProjectConfig2() {
|
|
180833
|
-
const platform3 = getPlatform();
|
|
180834
|
-
const config = projectState.projectConfig;
|
|
180835
|
-
await platform3.writeFile("project.json", JSON.stringify(config, null, "\t"));
|
|
180836
|
-
}
|
|
180837
|
-
function getSelectedSchema() {
|
|
180838
|
-
const config = projectState?.projectConfig;
|
|
180839
|
-
return config?.contentTypes?.[selectedContentType]?.schema;
|
|
180840
|
-
}
|
|
180841
|
-
function handleNewContentType(rerender) {
|
|
180842
|
-
const slug = newContentTypeName.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
180843
|
-
if (!slug)
|
|
180844
|
-
return;
|
|
180845
|
-
const config = projectState?.projectConfig;
|
|
180846
|
-
if (!config)
|
|
180847
|
-
return;
|
|
180848
|
-
if (!config.contentTypes)
|
|
180849
|
-
config.contentTypes = {};
|
|
180850
|
-
if (config.contentTypes[slug])
|
|
180851
|
-
return;
|
|
180852
|
-
config.contentTypes[slug] = {
|
|
180853
|
-
source: `./content/${slug}/**/*.md`,
|
|
180854
|
-
schema: { type: "object", properties: {}, required: [] }
|
|
180855
|
-
};
|
|
180856
|
-
selectedContentType = slug;
|
|
180857
|
-
showNewContentType = false;
|
|
180858
|
-
newContentTypeName = "";
|
|
180859
|
-
rerender();
|
|
180860
|
-
saveProjectConfig2().then(async () => {
|
|
180861
|
-
const platform3 = getPlatform();
|
|
180862
|
-
await platform3.writeFile(`content/${slug}/.gitkeep`, "");
|
|
180863
|
-
});
|
|
180864
|
-
}
|
|
180865
|
-
function handleAddField2(rerender) {
|
|
180866
|
-
const name = newFieldState2.name.trim();
|
|
180867
|
-
if (!name || !selectedContentType)
|
|
180868
|
-
return;
|
|
180869
|
-
const schema4 = getSelectedSchema();
|
|
180870
|
-
if (!schema4)
|
|
180871
|
-
return;
|
|
180872
|
-
if (!schema4.properties)
|
|
180873
|
-
schema4.properties = {};
|
|
180874
|
-
schema4.properties[name] = schemaForType(newFieldState2.type, newFieldState2.format || undefined);
|
|
180875
|
-
if (newFieldState2.required) {
|
|
180876
|
-
if (!schema4.required)
|
|
180877
|
-
schema4.required = [];
|
|
180878
|
-
if (!schema4.required.includes(name))
|
|
180879
|
-
schema4.required.push(name);
|
|
180880
|
-
}
|
|
180881
|
-
showAddField2 = false;
|
|
180882
|
-
newFieldState2 = { name: "", type: "string", format: "", required: false };
|
|
180883
|
-
rerender();
|
|
180884
|
-
saveProjectConfig2();
|
|
180885
|
-
}
|
|
180886
|
-
function handleDeleteField2(fieldName, rerender) {
|
|
180887
|
-
const schema4 = getSelectedSchema();
|
|
180888
|
-
if (!schema4?.properties)
|
|
180889
|
-
return;
|
|
180890
|
-
delete schema4.properties[fieldName];
|
|
180891
|
-
if (schema4.required) {
|
|
180892
|
-
schema4.required = schema4.required.filter((r) => r !== fieldName);
|
|
180893
|
-
}
|
|
180894
|
-
rerender();
|
|
180895
|
-
saveProjectConfig2();
|
|
180896
|
-
}
|
|
180897
|
-
function handleToggleRequired2(fieldName, rerender) {
|
|
180898
|
-
const schema4 = getSelectedSchema();
|
|
180899
|
-
if (!schema4)
|
|
180900
|
-
return;
|
|
180901
|
-
if (!schema4.required)
|
|
180902
|
-
schema4.required = [];
|
|
180903
|
-
const idx = schema4.required.indexOf(fieldName);
|
|
180904
|
-
if (idx >= 0)
|
|
180905
|
-
schema4.required.splice(idx, 1);
|
|
180906
|
-
else
|
|
180907
|
-
schema4.required.push(fieldName);
|
|
180908
|
-
rerender();
|
|
180909
|
-
saveProjectConfig2();
|
|
180910
|
-
}
|
|
180911
|
-
function handleRenameField2(oldName, newName, rerender) {
|
|
180912
|
-
const schema4 = getSelectedSchema();
|
|
180913
|
-
if (!schema4?.properties || !newName || schema4.properties[newName])
|
|
180914
|
-
return;
|
|
180915
|
-
const newProps = {};
|
|
180916
|
-
for (const [key, val] of Object.entries(schema4.properties)) {
|
|
180917
|
-
newProps[key === oldName ? newName : key] = val;
|
|
180918
|
-
}
|
|
180919
|
-
schema4.properties = newProps;
|
|
180920
|
-
if (schema4.required) {
|
|
180921
|
-
schema4.required = schema4.required.map((r) => r === oldName ? newName : r);
|
|
180922
|
-
}
|
|
180923
|
-
rerender();
|
|
180924
|
-
saveProjectConfig2();
|
|
180925
|
-
}
|
|
180926
|
-
function handleChangeType2(fieldName, newType, rerender) {
|
|
180927
|
-
const schema4 = getSelectedSchema();
|
|
180928
|
-
if (!schema4?.properties?.[fieldName])
|
|
180929
|
-
return;
|
|
180930
|
-
const oldFormat = newType === "string" || newType === "array" ? detectFieldFormat(schema4.properties[fieldName]) : undefined;
|
|
180931
|
-
schema4.properties[fieldName] = schemaForType(newType, oldFormat || undefined);
|
|
180932
|
-
rerender();
|
|
180933
|
-
saveProjectConfig2();
|
|
180934
|
-
}
|
|
180935
|
-
function handleChangeFormat2(fieldName, format2, rerender) {
|
|
180936
|
-
const schema4 = getSelectedSchema();
|
|
180937
|
-
if (!schema4?.properties?.[fieldName])
|
|
180938
|
-
return;
|
|
180939
|
-
const prop = schema4.properties[fieldName];
|
|
180940
|
-
const type = prop.type || "string";
|
|
180941
|
-
schema4.properties[fieldName] = schemaForType(type, format2 || undefined);
|
|
180942
|
-
rerender();
|
|
180943
|
-
saveProjectConfig2();
|
|
180944
|
-
}
|
|
180945
|
-
function handleChangeRefTarget(fieldName, target, rerender) {
|
|
180946
|
-
const schema4 = getSelectedSchema();
|
|
180947
|
-
if (!schema4?.properties)
|
|
180948
|
-
return;
|
|
180949
|
-
schema4.properties[fieldName] = { $ref: `#/contentTypes/${target}` };
|
|
180950
|
-
rerender();
|
|
180951
|
-
saveProjectConfig2();
|
|
180952
|
-
}
|
|
180953
|
-
function handleAddNestedField2(parentName, fieldState, rerender) {
|
|
180954
|
-
const schema4 = getSelectedSchema();
|
|
180955
|
-
const parent = schema4?.properties?.[parentName];
|
|
180956
|
-
if (!parent)
|
|
180957
|
-
return;
|
|
180958
|
-
if (!parent.properties)
|
|
180959
|
-
parent.properties = {};
|
|
180960
|
-
parent.properties[fieldState.name] = schemaForType(fieldState.type);
|
|
180961
|
-
if (fieldState.required) {
|
|
180962
|
-
if (!parent.required)
|
|
180963
|
-
parent.required = [];
|
|
180964
|
-
if (!parent.required.includes(fieldState.name))
|
|
180965
|
-
parent.required.push(fieldState.name);
|
|
180966
|
-
}
|
|
180967
|
-
rerender();
|
|
180968
|
-
saveProjectConfig2();
|
|
180969
|
-
}
|
|
180970
|
-
function handleDeleteNested2(parentName, childName, rerender) {
|
|
180971
|
-
const schema4 = getSelectedSchema();
|
|
180972
|
-
const parent = schema4?.properties?.[parentName];
|
|
180973
|
-
if (!parent?.properties)
|
|
180974
|
-
return;
|
|
180975
|
-
delete parent.properties[childName];
|
|
180976
|
-
if (parent.required) {
|
|
180977
|
-
parent.required = parent.required.filter((r) => r !== childName);
|
|
180978
|
-
}
|
|
180979
|
-
rerender();
|
|
180980
|
-
saveProjectConfig2();
|
|
180981
|
-
}
|
|
180982
|
-
function handleToggleNestedRequired2(parentName, childName, rerender) {
|
|
180983
|
-
const schema4 = getSelectedSchema();
|
|
180984
|
-
const parent = schema4?.properties?.[parentName];
|
|
180985
|
-
if (!parent)
|
|
180986
|
-
return;
|
|
180987
|
-
if (!parent.required)
|
|
180988
|
-
parent.required = [];
|
|
180989
|
-
const idx = parent.required.indexOf(childName);
|
|
180990
|
-
if (idx >= 0)
|
|
180991
|
-
parent.required.splice(idx, 1);
|
|
180992
|
-
else
|
|
180993
|
-
parent.required.push(childName);
|
|
180994
|
-
rerender();
|
|
180995
|
-
saveProjectConfig2();
|
|
180996
|
-
}
|
|
180997
|
-
function handleRenameNested2(parentName, oldChild, newChild, rerender) {
|
|
180998
|
-
const schema4 = getSelectedSchema();
|
|
180999
|
-
const parent = schema4?.properties?.[parentName];
|
|
181000
|
-
if (!parent?.properties || !newChild || parent.properties[newChild])
|
|
181001
|
-
return;
|
|
181002
|
-
const newProps = {};
|
|
181003
|
-
for (const [key, val] of Object.entries(parent.properties)) {
|
|
181004
|
-
newProps[key === oldChild ? newChild : key] = val;
|
|
181005
|
-
}
|
|
181006
|
-
parent.properties = newProps;
|
|
181007
|
-
if (parent.required) {
|
|
181008
|
-
parent.required = parent.required.map((r) => r === oldChild ? newChild : r);
|
|
181009
|
-
}
|
|
181010
|
-
rerender();
|
|
181011
|
-
saveProjectConfig2();
|
|
181012
|
-
}
|
|
181013
|
-
function handleChangeNestedType2(parentName, childName, newType, rerender) {
|
|
181014
|
-
const schema4 = getSelectedSchema();
|
|
181015
|
-
const parent = schema4?.properties?.[parentName];
|
|
181016
|
-
if (!parent?.properties?.[childName])
|
|
181017
|
-
return;
|
|
181018
|
-
const oldFormat = newType === "string" || newType === "array" ? detectFieldFormat(parent.properties[childName]) : undefined;
|
|
181019
|
-
parent.properties[childName] = schemaForType(newType, oldFormat || undefined);
|
|
181020
|
-
rerender();
|
|
181021
|
-
saveProjectConfig2();
|
|
181022
|
-
}
|
|
181023
|
-
function handleChangeNestedFormat2(parentName, childName, format2, rerender) {
|
|
181024
|
-
const schema4 = getSelectedSchema();
|
|
181025
|
-
const parent = schema4?.properties?.[parentName];
|
|
181026
|
-
if (!parent?.properties?.[childName])
|
|
181027
|
-
return;
|
|
181028
|
-
const prop = parent.properties[childName];
|
|
181029
|
-
const type = prop.type || "string";
|
|
181030
|
-
parent.properties[childName] = schemaForType(type, format2 || undefined);
|
|
181031
|
-
rerender();
|
|
181032
|
-
saveProjectConfig2();
|
|
181033
|
-
}
|
|
181034
|
-
function handleDeleteContentType(rerender) {
|
|
181035
|
-
if (!selectedContentType)
|
|
181036
|
-
return;
|
|
181037
|
-
const config = projectState?.projectConfig;
|
|
181038
|
-
if (!config?.contentTypes?.[selectedContentType])
|
|
181039
|
-
return;
|
|
181040
|
-
delete config.contentTypes[selectedContentType];
|
|
181041
|
-
selectedContentType = null;
|
|
181042
|
-
rerender();
|
|
181043
|
-
saveProjectConfig2();
|
|
181044
|
-
}
|
|
181045
|
-
function renderContentTypesEditor(container) {
|
|
181046
|
-
const rerender = () => renderContentTypesEditor(container);
|
|
181047
|
-
const config = projectState?.projectConfig;
|
|
181048
|
-
const contentTypes = config?.contentTypes || {};
|
|
181049
|
-
const contentTypeNames = Object.keys(contentTypes);
|
|
181050
|
-
const listTpl = html`
|
|
181051
|
-
<div class="settings-list-panel">
|
|
181052
|
-
${contentTypeNames.map((name) => html`
|
|
181053
|
-
<sp-action-button
|
|
181054
|
-
size="s"
|
|
181055
|
-
?selected=${selectedContentType === name}
|
|
181056
|
-
@click=${() => {
|
|
181057
|
-
selectedContentType = name;
|
|
181058
|
-
showAddField2 = false;
|
|
181059
|
-
rerender();
|
|
181060
|
-
}}
|
|
181061
|
-
>
|
|
181062
|
-
${name}
|
|
181063
|
-
</sp-action-button>
|
|
181064
|
-
`)}
|
|
181065
|
-
${showNewContentType ? html`
|
|
181066
|
-
<div class="settings-inline-form">
|
|
181067
|
-
<sp-textfield
|
|
181068
|
-
size="s"
|
|
181069
|
-
placeholder="content-type-name"
|
|
181070
|
-
.value=${newContentTypeName}
|
|
181071
|
-
@input=${(e) => {
|
|
181072
|
-
newContentTypeName = e.target.value;
|
|
181073
|
-
}}
|
|
181074
|
-
@keydown=${(e) => {
|
|
181075
|
-
if (e.key === "Enter")
|
|
181076
|
-
handleNewContentType(rerender);
|
|
181077
|
-
if (e.key === "Escape") {
|
|
181078
|
-
showNewContentType = false;
|
|
181079
|
-
rerender();
|
|
181080
|
-
}
|
|
181081
|
-
}}
|
|
181082
|
-
></sp-textfield>
|
|
181083
|
-
<sp-action-button size="s" @click=${() => handleNewContentType(rerender)}>
|
|
181084
|
-
Create
|
|
181085
|
-
</sp-action-button>
|
|
181086
|
-
</div>
|
|
181087
|
-
` : html`
|
|
181088
|
-
<sp-action-button
|
|
181089
|
-
size="s"
|
|
181090
|
-
quiet
|
|
181091
|
-
@click=${() => {
|
|
181092
|
-
showNewContentType = true;
|
|
181093
|
-
rerender();
|
|
181094
|
-
}}
|
|
181095
|
-
>
|
|
181096
|
-
<sp-icon-add slot="icon"></sp-icon-add> New Content Type
|
|
181097
|
-
</sp-action-button>
|
|
181098
|
-
`}
|
|
181099
|
-
</div>
|
|
181100
|
-
`;
|
|
181101
|
-
let editorTpl;
|
|
181102
|
-
if (!selectedContentType || !contentTypes[selectedContentType]) {
|
|
181103
|
-
editorTpl = html`<div class="settings-empty-state">Select or create a content type</div>`;
|
|
181104
|
-
} else {
|
|
181105
|
-
const col = contentTypes[selectedContentType];
|
|
181106
|
-
const schema4 = col.schema || {};
|
|
181107
|
-
const properties = schema4.properties || {};
|
|
181108
|
-
const required = schema4.required || [];
|
|
181109
|
-
const handlers = {
|
|
181110
|
-
onDelete: (n2) => handleDeleteField2(n2, rerender),
|
|
181111
|
-
onToggleRequired: (n2) => handleToggleRequired2(n2, rerender),
|
|
181112
|
-
onRename: (oldN, newN) => handleRenameField2(oldN, newN, rerender),
|
|
181113
|
-
onChangeType: (n2, t) => handleChangeType2(n2, t, rerender),
|
|
181114
|
-
onChangeFormat: (n2, f) => handleChangeFormat2(n2, f, rerender),
|
|
181115
|
-
onChangeRefTarget: (n2, target) => handleChangeRefTarget(n2, target, rerender),
|
|
181116
|
-
onAddNestedField: (p, s2) => handleAddNestedField2(p, s2, rerender),
|
|
181117
|
-
onDeleteNested: (p, c) => handleDeleteNested2(p, c, rerender),
|
|
181118
|
-
onToggleNestedRequired: (p, c) => handleToggleNestedRequired2(p, c, rerender),
|
|
181119
|
-
onRenameNested: (p, o, n2) => handleRenameNested2(p, o, n2, rerender),
|
|
181120
|
-
onChangeNestedType: (p, c, t) => handleChangeNestedType2(p, c, t, rerender),
|
|
181121
|
-
onChangeNestedFormat: (p, c, f) => handleChangeNestedFormat2(p, c, f, rerender)
|
|
181122
|
-
};
|
|
181123
|
-
const fieldCards = Object.entries(properties).map(([name, def3]) => fieldCardTpl(name, def3, required.includes(name), handlers, contentTypeNames));
|
|
181124
|
-
editorTpl = html`
|
|
181125
|
-
<div class="settings-editor-panel">
|
|
181126
|
-
<div class="settings-editor-header">
|
|
181127
|
-
<h3>${selectedContentType}</h3>
|
|
181128
|
-
<sp-field-label size="s">Source: ${col.source || "—"}</sp-field-label>
|
|
181129
|
-
<sp-action-button
|
|
181130
|
-
size="xs"
|
|
181131
|
-
quiet
|
|
181132
|
-
title="Delete content type"
|
|
181133
|
-
@click=${() => handleDeleteContentType(rerender)}
|
|
181134
|
-
>
|
|
181135
|
-
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
181136
|
-
</sp-action-button>
|
|
181137
|
-
</div>
|
|
181138
|
-
<div class="schema-field-list">${fieldCards}</div>
|
|
181139
|
-
${showAddField2 ? addFieldFormTpl(newFieldState2, {
|
|
181140
|
-
onInput: (field, value2) => {
|
|
181141
|
-
newFieldState2 = { ...newFieldState2, [field]: value2 };
|
|
181142
|
-
rerender();
|
|
181143
|
-
},
|
|
181144
|
-
onConfirm: () => handleAddField2(rerender),
|
|
181145
|
-
onCancel: () => {
|
|
181146
|
-
showAddField2 = false;
|
|
181147
|
-
newFieldState2 = { name: "", type: "string", format: "", required: false };
|
|
181148
|
-
rerender();
|
|
181149
|
-
}
|
|
181150
|
-
}) : html`
|
|
181151
|
-
<sp-action-button
|
|
181152
|
-
size="s"
|
|
181153
|
-
quiet
|
|
181154
|
-
@click=${() => {
|
|
181155
|
-
showAddField2 = true;
|
|
181156
|
-
rerender();
|
|
181157
|
-
}}
|
|
181158
|
-
>
|
|
181159
|
-
<sp-icon-add slot="icon"></sp-icon-add> Add Field
|
|
181160
|
-
</sp-action-button>
|
|
181161
|
-
`}
|
|
181162
|
-
</div>
|
|
181163
|
-
`;
|
|
181164
|
-
}
|
|
181165
|
-
const tpl = html` <div class="settings-two-col">${listTpl} ${editorTpl}</div> `;
|
|
181166
|
-
render2(tpl, container);
|
|
181167
|
-
}
|
|
181168
180103
|
// data/stylebook-meta.json
|
|
181169
180104
|
var stylebook_meta_default = {
|
|
181170
180105
|
$sections: [
|
|
@@ -181375,39 +180310,6 @@ registerBlockType( name, settings );`
|
|
|
181375
180310
|
var _ctx6 = null;
|
|
181376
180311
|
function renderStylebookMode(ctx) {
|
|
181377
180312
|
_ctx6 = ctx;
|
|
181378
|
-
const settingsTab = activeTab.value?.session.ui.settingsTab || "stylebook";
|
|
181379
|
-
const settingsChromeBarTpl = html`
|
|
181380
|
-
<div
|
|
181381
|
-
class="sb-chrome settings-top-chrome"
|
|
181382
|
-
style="position:absolute;top:0;left:0;right:0;z-index:16;background:var(--bg-panel);border-bottom:1px solid var(--border)"
|
|
181383
|
-
>
|
|
181384
|
-
<sp-tabs
|
|
181385
|
-
size="s"
|
|
181386
|
-
selected=${settingsTab}
|
|
181387
|
-
@change=${(e) => {
|
|
181388
|
-
updateUi("settingsTab", e.target.selected);
|
|
181389
|
-
}}
|
|
181390
|
-
>
|
|
181391
|
-
<sp-tab label="Stylebook" value="stylebook"></sp-tab>
|
|
181392
|
-
<sp-tab label="Definitions" value="definitions"></sp-tab>
|
|
181393
|
-
<sp-tab label="Content Types" value="contentTypes"></sp-tab>
|
|
181394
|
-
</sp-tabs>
|
|
181395
|
-
</div>
|
|
181396
|
-
`;
|
|
181397
|
-
if (settingsTab === "definitions" || settingsTab === "contentTypes") {
|
|
181398
|
-
canvasWrap.style.overflow = "hidden";
|
|
181399
|
-
render2(html`${settingsChromeBarTpl}
|
|
181400
|
-
<div
|
|
181401
|
-
class="settings-editor-container"
|
|
181402
|
-
style="position:absolute;inset:40px 0 0 0;overflow:auto"
|
|
181403
|
-
></div>`, canvasWrap);
|
|
181404
|
-
const container = canvasWrap.querySelector(".settings-editor-container");
|
|
181405
|
-
if (settingsTab === "definitions")
|
|
181406
|
-
renderDefsEditor(container);
|
|
181407
|
-
else
|
|
181408
|
-
renderContentTypesEditor(container);
|
|
181409
|
-
return;
|
|
181410
|
-
}
|
|
181411
180313
|
view.stylebookElToTag = new WeakMap;
|
|
181412
180314
|
const tab = activeTab.value;
|
|
181413
180315
|
const rootStyle = getEffectiveStyle(tab?.doc.document?.style);
|
|
@@ -181415,9 +180317,6 @@ function renderStylebookMode(ctx) {
|
|
|
181415
180317
|
const customizedOnly = tab?.session.ui.stylebookCustomizedOnly;
|
|
181416
180318
|
const { sizeBreakpoints, baseWidth } = parseMediaEntries(getEffectiveMedia(tab?.doc.document?.$media));
|
|
181417
180319
|
const hasMedia = sizeBreakpoints.length > 0;
|
|
181418
|
-
const onTabClick = (t) => {
|
|
181419
|
-
updateUi("stylebookTab", t);
|
|
181420
|
-
};
|
|
181421
180320
|
const onFilterInput2 = (e) => {
|
|
181422
180321
|
updateUi("stylebookFilter", e.target.value);
|
|
181423
180322
|
};
|
|
@@ -181425,38 +180324,25 @@ function renderStylebookMode(ctx) {
|
|
|
181425
180324
|
updateUi("stylebookCustomizedOnly", !tab?.session.ui.stylebookCustomizedOnly);
|
|
181426
180325
|
};
|
|
181427
180326
|
const chromeBarTpl = html`
|
|
181428
|
-
${settingsChromeBarTpl}
|
|
181429
180327
|
<div
|
|
181430
180328
|
class="sb-chrome"
|
|
181431
|
-
style="position:absolute;top:
|
|
180329
|
+
style="position:absolute;top:0;left:0;right:0;z-index:15;background:var(--bg-panel);border-bottom:1px solid var(--border)"
|
|
181432
180330
|
>
|
|
181433
|
-
<
|
|
181434
|
-
|
|
181435
|
-
|
|
181436
|
-
|
|
181437
|
-
|
|
181438
|
-
|
|
181439
|
-
|
|
181440
|
-
|
|
181441
|
-
|
|
181442
|
-
|
|
181443
|
-
|
|
181444
|
-
|
|
181445
|
-
|
|
181446
|
-
|
|
181447
|
-
|
|
181448
|
-
placeholder="Filter…"
|
|
181449
|
-
.value=${tab?.session.ui.stylebookFilter}
|
|
181450
|
-
@input=${onFilterInput2}
|
|
181451
|
-
/>
|
|
181452
|
-
<button
|
|
181453
|
-
class="tb-toggle${tab?.session.ui.stylebookCustomizedOnly ? " active" : ""}"
|
|
181454
|
-
style="margin-left:4px"
|
|
181455
|
-
@click=${onCustomizedToggle}
|
|
181456
|
-
>
|
|
181457
|
-
Customized
|
|
181458
|
-
</button>
|
|
181459
|
-
` : nothing}
|
|
180331
|
+
<div style="display:flex;align-items:center;padding:4px 8px;gap:4px">
|
|
180332
|
+
<input
|
|
180333
|
+
class="field-input"
|
|
180334
|
+
style="flex:1;max-width:200px"
|
|
180335
|
+
placeholder="Filter…"
|
|
180336
|
+
.value=${tab?.session.ui.stylebookFilter}
|
|
180337
|
+
@input=${onFilterInput2}
|
|
180338
|
+
/>
|
|
180339
|
+
<button
|
|
180340
|
+
class="tb-toggle${tab?.session.ui.stylebookCustomizedOnly ? " active" : ""}"
|
|
180341
|
+
@click=${onCustomizedToggle}
|
|
180342
|
+
>
|
|
180343
|
+
Customized
|
|
180344
|
+
</button>
|
|
180345
|
+
</div>
|
|
181460
180346
|
</div>
|
|
181461
180347
|
`;
|
|
181462
180348
|
canvasWrap.style.overflow = "hidden";
|
|
@@ -181479,16 +180365,11 @@ function renderStylebookMode(ctx) {
|
|
|
181479
180365
|
}
|
|
181480
180366
|
const renderIntoPanel = (panel, activeBreakpoints) => {
|
|
181481
180367
|
panel.canvas.classList.add("sb-canvas");
|
|
181482
|
-
|
|
181483
|
-
|
|
181484
|
-
|
|
181485
|
-
child.style.pointerEvents = "none";
|
|
181486
|
-
}
|
|
181487
|
-
registerStylebookPanelEvents(panel);
|
|
181488
|
-
} else {
|
|
181489
|
-
renderStylebookVarsIntoCanvas(panel.canvas, rootStyle);
|
|
181490
|
-
panel.overlayClk.style.pointerEvents = "none";
|
|
180368
|
+
renderStylebookElementsIntoCanvas(panel.canvas, rootStyle, filter, customizedOnly, activeBreakpoints);
|
|
180369
|
+
for (const child of panel.canvas.querySelectorAll("*")) {
|
|
180370
|
+
child.style.pointerEvents = "none";
|
|
181491
180371
|
}
|
|
180372
|
+
registerStylebookPanelEvents(panel);
|
|
181492
180373
|
};
|
|
181493
180374
|
let panelEntries;
|
|
181494
180375
|
if (!hasMedia) {
|
|
@@ -181508,7 +180389,7 @@ function renderStylebookMode(ctx) {
|
|
|
181508
180389
|
${chromeBarTpl}
|
|
181509
180390
|
<div
|
|
181510
180391
|
class="panzoom-wrap"
|
|
181511
|
-
style="transform-origin:0 0;padding-top:
|
|
180392
|
+
style="transform-origin:0 0;padding-top:40px"
|
|
181512
180393
|
${ref2((el) => {
|
|
181513
180394
|
if (el)
|
|
181514
180395
|
view.panzoomWrap = el;
|
|
@@ -181798,363 +180679,6 @@ function renderStylebookElementsIntoCanvas(canvasEl, rootStyle, filter, customiz
|
|
|
181798
180679
|
render2(html`${sectionTemplates}`, canvasEl);
|
|
181799
180680
|
}
|
|
181800
180681
|
}
|
|
181801
|
-
function renderStylebookVarsIntoCanvas(canvasEl, rootStyle) {
|
|
181802
|
-
const varCats = stylebook_meta_default.$variables;
|
|
181803
|
-
const groups = {};
|
|
181804
|
-
for (const key of Object.keys(varCats))
|
|
181805
|
-
groups[key] = [];
|
|
181806
|
-
for (const [k, v] of Object.entries(rootStyle)) {
|
|
181807
|
-
if (!k.startsWith("--"))
|
|
181808
|
-
continue;
|
|
181809
|
-
if (typeof v !== "string" && typeof v !== "number")
|
|
181810
|
-
continue;
|
|
181811
|
-
if (k.startsWith("--color"))
|
|
181812
|
-
groups.color.push([k, v]);
|
|
181813
|
-
else if (k.startsWith("--font"))
|
|
181814
|
-
groups.font.push([k, v]);
|
|
181815
|
-
else if (k.startsWith("--size") || k.startsWith("--spacing") || k.startsWith("--radius"))
|
|
181816
|
-
groups.size.push([k, v]);
|
|
181817
|
-
else
|
|
181818
|
-
groups.other.push([k, v]);
|
|
181819
|
-
}
|
|
181820
|
-
const bodyRefs = new Map;
|
|
181821
|
-
const sectionTemplates = Object.entries(varCats).map(([catKey, catMeta]) => {
|
|
181822
|
-
const vars = groups[catKey];
|
|
181823
|
-
const onAdd = () => {
|
|
181824
|
-
const bodyEl = bodyRefs.get(catKey);
|
|
181825
|
-
if (!bodyEl)
|
|
181826
|
-
return;
|
|
181827
|
-
const addBtn = bodyEl.querySelector(".sb-var-add-btn");
|
|
181828
|
-
const row = renderVarRow(catKey, catMeta, null, "", true);
|
|
181829
|
-
bodyEl.insertBefore(row, addBtn);
|
|
181830
|
-
if (addBtn)
|
|
181831
|
-
addBtn.style.display = "none";
|
|
181832
|
-
const nameField = row.querySelector("sp-textfield");
|
|
181833
|
-
if (nameField)
|
|
181834
|
-
requestAnimationFrame(() => nameField.focus());
|
|
181835
|
-
};
|
|
181836
|
-
return html`
|
|
181837
|
-
<div class="sb-section">
|
|
181838
|
-
<div class="sb-label">${catMeta.label}</div>
|
|
181839
|
-
<div
|
|
181840
|
-
class="sb-body"
|
|
181841
|
-
${ref2((el) => {
|
|
181842
|
-
if (el)
|
|
181843
|
-
bodyRefs.set(catKey, el);
|
|
181844
|
-
})}
|
|
181845
|
-
>
|
|
181846
|
-
${vars.length > 0 ? vars.map(([varName, varVal]) => renderVarRow(catKey, catMeta, varName, String(varVal), false)) : html`<div class="sb-var-empty">
|
|
181847
|
-
No ${catMeta.label.toLowerCase()} variables yet.
|
|
181848
|
-
</div>`}
|
|
181849
|
-
<button class="sb-var-add-btn" @click=${onAdd}>
|
|
181850
|
-
<span class="sb-var-add-icon">+</span> Add ${catMeta.label}
|
|
181851
|
-
</button>
|
|
181852
|
-
</div>
|
|
181853
|
-
</div>
|
|
181854
|
-
`;
|
|
181855
|
-
});
|
|
181856
|
-
render2(html`${sectionTemplates}`, canvasEl);
|
|
181857
|
-
}
|
|
181858
|
-
function renderVarRow(catKey, catMeta, varName, varVal, isNew) {
|
|
181859
|
-
const row = document.createElement("div");
|
|
181860
|
-
row.className = isNew ? "sb-var-row is-new" : "sb-var-row";
|
|
181861
|
-
let colorPicker = null;
|
|
181862
|
-
let nameField = null;
|
|
181863
|
-
let getValueFn;
|
|
181864
|
-
let hexField = null;
|
|
181865
|
-
const swatchTpl = catKey === "color" ? html`
|
|
181866
|
-
<div
|
|
181867
|
-
class="sb-var-swatch"
|
|
181868
|
-
style=${styleMap({ backgroundColor: varVal || "var(--accent)" })}
|
|
181869
|
-
>
|
|
181870
|
-
<input
|
|
181871
|
-
type="color"
|
|
181872
|
-
.value=${varVal && varVal.startsWith("#") ? varVal : "#007acc"}
|
|
181873
|
-
${ref2((el) => {
|
|
181874
|
-
if (el)
|
|
181875
|
-
colorPicker = el;
|
|
181876
|
-
})}
|
|
181877
|
-
@input=${() => {
|
|
181878
|
-
if (!colorPicker || !hexField)
|
|
181879
|
-
return;
|
|
181880
|
-
hexField.value = colorPicker.value;
|
|
181881
|
-
const swatch = row.querySelector(".sb-var-swatch");
|
|
181882
|
-
if (swatch)
|
|
181883
|
-
swatch.style.backgroundColor = colorPicker.value;
|
|
181884
|
-
if (!isNew && varName) {
|
|
181885
|
-
transactDoc(activeTab.value, (t) => mutateUpdateStyle(t, [], varName, colorPicker.value));
|
|
181886
|
-
}
|
|
181887
|
-
}}
|
|
181888
|
-
/>
|
|
181889
|
-
</div>
|
|
181890
|
-
` : nothing;
|
|
181891
|
-
const namePlaceholder = catKey === "color" ? "Primary Blue" : catKey === "font" ? "Body Serif" : catKey === "size" ? "Spacing Large" : "Border Radius";
|
|
181892
|
-
const nameColTpl = isNew ? html`
|
|
181893
|
-
<div class="sb-var-col-name">
|
|
181894
|
-
<div class="sb-var-col-label">Name</div>
|
|
181895
|
-
<sp-textfield
|
|
181896
|
-
size="s"
|
|
181897
|
-
placeholder=${namePlaceholder}
|
|
181898
|
-
style="pointer-events:auto"
|
|
181899
|
-
${ref2((el) => {
|
|
181900
|
-
if (el)
|
|
181901
|
-
nameField = el;
|
|
181902
|
-
})}
|
|
181903
|
-
></sp-textfield>
|
|
181904
|
-
</div>
|
|
181905
|
-
` : nothing;
|
|
181906
|
-
let valueContent;
|
|
181907
|
-
if (catKey === "color") {
|
|
181908
|
-
let debounce;
|
|
181909
|
-
valueContent = html`
|
|
181910
|
-
<sp-textfield
|
|
181911
|
-
size="s"
|
|
181912
|
-
.value=${varVal || "#007acc"}
|
|
181913
|
-
placeholder="#007acc"
|
|
181914
|
-
style="pointer-events:auto"
|
|
181915
|
-
${ref2((el) => {
|
|
181916
|
-
if (el)
|
|
181917
|
-
hexField = el;
|
|
181918
|
-
})}
|
|
181919
|
-
@input=${() => {
|
|
181920
|
-
clearTimeout(debounce);
|
|
181921
|
-
debounce = setTimeout(() => {
|
|
181922
|
-
if (!hexField)
|
|
181923
|
-
return;
|
|
181924
|
-
const v = hexField.value;
|
|
181925
|
-
try {
|
|
181926
|
-
if (colorPicker)
|
|
181927
|
-
colorPicker.value = v.startsWith("#") ? v : colorPicker.value;
|
|
181928
|
-
} catch {}
|
|
181929
|
-
const swatch = row.querySelector(".sb-var-swatch");
|
|
181930
|
-
if (swatch)
|
|
181931
|
-
swatch.style.backgroundColor = v;
|
|
181932
|
-
if (!isNew && varName) {
|
|
181933
|
-
transactDoc(activeTab.value, (t) => mutateUpdateStyle(t, [], varName, v));
|
|
181934
|
-
}
|
|
181935
|
-
}, 400);
|
|
181936
|
-
}}
|
|
181937
|
-
></sp-textfield>
|
|
181938
|
-
`;
|
|
181939
|
-
getValueFn = () => hexField?.value?.trim() || "";
|
|
181940
|
-
} else if (catKey === "size") {
|
|
181941
|
-
const ui = createUnitInput(varVal || "16px", {
|
|
181942
|
-
onChange: (newVal) => {
|
|
181943
|
-
const bar = row.querySelector(".sb-var-size-bar");
|
|
181944
|
-
if (bar)
|
|
181945
|
-
bar.style.width = newVal;
|
|
181946
|
-
if (!isNew && varName) {
|
|
181947
|
-
transactDoc(activeTab.value, (t) => mutateUpdateStyle(t, [], varName, newVal));
|
|
181948
|
-
}
|
|
181949
|
-
}
|
|
181950
|
-
});
|
|
181951
|
-
if (isNew)
|
|
181952
|
-
ui.textfield.value = "";
|
|
181953
|
-
valueContent = html`<div
|
|
181954
|
-
${ref2((el) => {
|
|
181955
|
-
if (el && !el.firstChild)
|
|
181956
|
-
el.appendChild(ui.wrap);
|
|
181957
|
-
})}
|
|
181958
|
-
></div>`;
|
|
181959
|
-
getValueFn = () => ui.getValue();
|
|
181960
|
-
} else {
|
|
181961
|
-
let textFieldEl = null;
|
|
181962
|
-
let debounce;
|
|
181963
|
-
valueContent = html`
|
|
181964
|
-
<sp-textfield
|
|
181965
|
-
size="s"
|
|
181966
|
-
.value=${varVal}
|
|
181967
|
-
placeholder=${catMeta.placeholder}
|
|
181968
|
-
style="pointer-events:auto"
|
|
181969
|
-
${ref2((el) => {
|
|
181970
|
-
if (el)
|
|
181971
|
-
textFieldEl = el;
|
|
181972
|
-
})}
|
|
181973
|
-
@input=${() => {
|
|
181974
|
-
if (!textFieldEl || isNew || !varName)
|
|
181975
|
-
return;
|
|
181976
|
-
clearTimeout(debounce);
|
|
181977
|
-
debounce = setTimeout(() => {
|
|
181978
|
-
const v = textFieldEl.value;
|
|
181979
|
-
const fontPrev = row.querySelector(".sb-var-font-preview");
|
|
181980
|
-
if (fontPrev)
|
|
181981
|
-
fontPrev.style.fontFamily = v;
|
|
181982
|
-
transactDoc(activeTab.value, (t) => mutateUpdateStyle(t, [], varName, v));
|
|
181983
|
-
}, 400);
|
|
181984
|
-
}}
|
|
181985
|
-
></sp-textfield>
|
|
181986
|
-
`;
|
|
181987
|
-
getValueFn = () => textFieldEl?.value?.trim() || "";
|
|
181988
|
-
}
|
|
181989
|
-
const valColTpl = html`
|
|
181990
|
-
<div class="sb-var-col-value">
|
|
181991
|
-
${isNew ? html`<div class="sb-var-col-label">Value</div>` : nothing} ${valueContent}
|
|
181992
|
-
</div>
|
|
181993
|
-
`;
|
|
181994
|
-
const actionsTpl = isNew ? html`
|
|
181995
|
-
<div class="sb-var-add-actions">
|
|
181996
|
-
<sp-action-button
|
|
181997
|
-
size="s"
|
|
181998
|
-
style="pointer-events:auto"
|
|
181999
|
-
@click=${() => {
|
|
182000
|
-
const name = (nameField?.value || "").trim();
|
|
182001
|
-
const val = getValueFn();
|
|
182002
|
-
const generatedVar = friendlyNameToVar(name, catMeta.prefix);
|
|
182003
|
-
if (!generatedVar || !val)
|
|
182004
|
-
return;
|
|
182005
|
-
transactDoc(activeTab.value, (t) => mutateUpdateStyle(t, [], generatedVar, val));
|
|
182006
|
-
}}
|
|
182007
|
-
>Add</sp-action-button
|
|
182008
|
-
>
|
|
182009
|
-
<sp-action-button
|
|
182010
|
-
size="s"
|
|
182011
|
-
quiet
|
|
182012
|
-
style="pointer-events:auto"
|
|
182013
|
-
@click=${() => {
|
|
182014
|
-
const body = row.parentElement;
|
|
182015
|
-
row.remove();
|
|
182016
|
-
const addBtn = body?.querySelector(".sb-var-add-btn");
|
|
182017
|
-
if (addBtn)
|
|
182018
|
-
addBtn.style.display = "";
|
|
182019
|
-
}}
|
|
182020
|
-
>
|
|
182021
|
-
<sp-icon-close slot="icon"></sp-icon-close>
|
|
182022
|
-
</sp-action-button>
|
|
182023
|
-
</div>
|
|
182024
|
-
` : nothing;
|
|
182025
|
-
const headerTpl = !isNew && varName ? html`
|
|
182026
|
-
<div class="sb-var-row-header">
|
|
182027
|
-
<span class="sb-var-row-title">${varDisplayName(varName, catMeta.prefix)}</span>
|
|
182028
|
-
<span class="sb-var-row-ref">${varName}</span>
|
|
182029
|
-
<sp-action-button
|
|
182030
|
-
size="s"
|
|
182031
|
-
quiet
|
|
182032
|
-
class="sb-var-del"
|
|
182033
|
-
style="pointer-events:auto"
|
|
182034
|
-
@click=${() => {
|
|
182035
|
-
transactDoc(activeTab.value, (t) => mutateUpdateStyle(t, [], varName, undefined));
|
|
182036
|
-
}}
|
|
182037
|
-
>
|
|
182038
|
-
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
182039
|
-
</sp-action-button>
|
|
182040
|
-
</div>
|
|
182041
|
-
` : nothing;
|
|
182042
|
-
const addPreviewTpl = isNew ? html`
|
|
182043
|
-
<div
|
|
182044
|
-
class="sb-var-add-preview"
|
|
182045
|
-
${ref2((el) => {
|
|
182046
|
-
if (!el || !nameField)
|
|
182047
|
-
return;
|
|
182048
|
-
nameField.addEventListener("input", () => {
|
|
182049
|
-
el.textContent = friendlyNameToVar(nameField.value || "", catMeta.prefix);
|
|
182050
|
-
});
|
|
182051
|
-
})}
|
|
182052
|
-
></div>
|
|
182053
|
-
` : nothing;
|
|
182054
|
-
const typePrevTpl = catKey === "font" && varVal ? html`
|
|
182055
|
-
<div class="sb-var-preview">
|
|
182056
|
-
<div class="sb-var-font-preview" style=${styleMap({ fontFamily: varVal })}>
|
|
182057
|
-
The quick brown fox jumps over the lazy dog
|
|
182058
|
-
</div>
|
|
182059
|
-
</div>
|
|
182060
|
-
` : catKey === "size" && varVal ? html`
|
|
182061
|
-
<div class="sb-var-preview">
|
|
182062
|
-
<div class="sb-var-size-track">
|
|
182063
|
-
<div class="sb-var-size-bar" style=${styleMap({ width: varVal })}></div>
|
|
182064
|
-
</div>
|
|
182065
|
-
</div>
|
|
182066
|
-
` : nothing;
|
|
182067
|
-
render2(html`
|
|
182068
|
-
${headerTpl}
|
|
182069
|
-
<div class="sb-var-input-row">${swatchTpl} ${nameColTpl} ${valColTpl} ${actionsTpl}</div>
|
|
182070
|
-
${addPreviewTpl} ${typePrevTpl}
|
|
182071
|
-
`, row);
|
|
182072
|
-
return row;
|
|
182073
|
-
}
|
|
182074
|
-
function createUnitInput(initialValue, { onChange, size = "s" } = {}) {
|
|
182075
|
-
const match2 = String(initialValue).match(/^(-?[\d.]+)\s*(px|em|rem|vw|vh|%|ch|ex|vmin|vmax|pt|cm|mm|in)?$/);
|
|
182076
|
-
let numVal = match2 ? match2[1] : initialValue;
|
|
182077
|
-
let unitVal = match2 ? match2[2] || "px" : "";
|
|
182078
|
-
const isNumeric = !!match2;
|
|
182079
|
-
const wrap4 = document.createElement("div");
|
|
182080
|
-
wrap4.className = "sb-unit-input";
|
|
182081
|
-
wrap4.style.pointerEvents = "auto";
|
|
182082
|
-
let textfield = null;
|
|
182083
|
-
let picker = null;
|
|
182084
|
-
const units = [
|
|
182085
|
-
{ value: "px", label: "px" },
|
|
182086
|
-
{ value: "rem", label: "rem" },
|
|
182087
|
-
{ value: "em", label: "em" },
|
|
182088
|
-
{ value: "%", label: "%" },
|
|
182089
|
-
{ value: "vw", label: "vw" },
|
|
182090
|
-
{ value: "vh", label: "vh" },
|
|
182091
|
-
{ value: "ch", label: "ch" },
|
|
182092
|
-
{ value: "pt", label: "pt" },
|
|
182093
|
-
{ divider: true },
|
|
182094
|
-
{ value: "auto", label: "auto" },
|
|
182095
|
-
{ value: "fit-content", label: "fit-content" }
|
|
182096
|
-
];
|
|
182097
|
-
let debounce;
|
|
182098
|
-
function getValue() {
|
|
182099
|
-
const num = textfield?.value;
|
|
182100
|
-
const unit = picker?.value;
|
|
182101
|
-
if (unit === "auto" || unit === "fit-content")
|
|
182102
|
-
return unit;
|
|
182103
|
-
return num ? `${num}${unit}` : "";
|
|
182104
|
-
}
|
|
182105
|
-
render2(html`
|
|
182106
|
-
<sp-textfield
|
|
182107
|
-
.value=${numVal}
|
|
182108
|
-
size=${size}
|
|
182109
|
-
${ref2((el) => {
|
|
182110
|
-
if (el)
|
|
182111
|
-
textfield = el;
|
|
182112
|
-
})}
|
|
182113
|
-
@input=${() => {
|
|
182114
|
-
clearTimeout(debounce);
|
|
182115
|
-
const raw = textfield?.value?.trim();
|
|
182116
|
-
const looksNumeric = /^-?[\d.]+$/.test(raw || "");
|
|
182117
|
-
if (picker)
|
|
182118
|
-
picker.style.display = looksNumeric ? "" : "none";
|
|
182119
|
-
debounce = setTimeout(() => {
|
|
182120
|
-
if (onChange)
|
|
182121
|
-
onChange(looksNumeric ? `${raw}${picker?.value}` : raw);
|
|
182122
|
-
}, 400);
|
|
182123
|
-
}}
|
|
182124
|
-
></sp-textfield>
|
|
182125
|
-
<sp-picker
|
|
182126
|
-
quiet
|
|
182127
|
-
size=${size}
|
|
182128
|
-
style=${styleMap({ display: isNumeric ? "" : "none" })}
|
|
182129
|
-
${ref2((el) => {
|
|
182130
|
-
if (el) {
|
|
182131
|
-
picker = el;
|
|
182132
|
-
requestAnimationFrame(() => {
|
|
182133
|
-
el.value = unitVal || "px";
|
|
182134
|
-
});
|
|
182135
|
-
}
|
|
182136
|
-
})}
|
|
182137
|
-
@change=${() => {
|
|
182138
|
-
const unit = picker?.value;
|
|
182139
|
-
if (unit === "auto" || unit === "fit-content") {
|
|
182140
|
-
if (textfield)
|
|
182141
|
-
textfield.value = unit;
|
|
182142
|
-
if (picker)
|
|
182143
|
-
picker.style.display = "none";
|
|
182144
|
-
if (onChange)
|
|
182145
|
-
onChange(unit);
|
|
182146
|
-
} else {
|
|
182147
|
-
unitVal = unit;
|
|
182148
|
-
if (onChange)
|
|
182149
|
-
onChange(getValue());
|
|
182150
|
-
}
|
|
182151
|
-
}}
|
|
182152
|
-
>
|
|
182153
|
-
${units.map((u) => u.divider ? html`<sp-menu-divider></sp-menu-divider>` : html`<sp-menu-item value=${u.value}>${u.label}</sp-menu-item>`)}
|
|
182154
|
-
</sp-picker>
|
|
182155
|
-
`, wrap4);
|
|
182156
|
-
return { wrap: wrap4, textfield, picker, getValue };
|
|
182157
|
-
}
|
|
182158
180682
|
function registerStylebookPanelEvents(panel) {
|
|
182159
180683
|
const { canvas, overlayClk } = panel;
|
|
182160
180684
|
overlayClk.addEventListener("click", (e) => {
|
|
@@ -183310,6 +181834,289 @@ function updateForcedPseudoPreview() {
|
|
|
183310
181834
|
// src/browse/browse.js
|
|
183311
181835
|
init_platform();
|
|
183312
181836
|
init_store();
|
|
181837
|
+
|
|
181838
|
+
// src/settings/schema-field-ui.js
|
|
181839
|
+
var FIELD_TYPES = ["string", "number", "boolean", "array", "object", "reference"];
|
|
181840
|
+
var FORMAT_OPTIONS = ["", "image", "date", "color"];
|
|
181841
|
+
function detectFieldType(schema4) {
|
|
181842
|
+
if (schema4.$ref)
|
|
181843
|
+
return "reference";
|
|
181844
|
+
return schema4.type || "string";
|
|
181845
|
+
}
|
|
181846
|
+
function detectFieldFormat(schema4) {
|
|
181847
|
+
if (schema4.type === "array" && schema4.items?.format)
|
|
181848
|
+
return schema4.items.format;
|
|
181849
|
+
return schema4.format || "";
|
|
181850
|
+
}
|
|
181851
|
+
function fieldCardTpl(fieldName, fieldSchema, isRequired, handlers, contentTypeNames = []) {
|
|
181852
|
+
const type = detectFieldType(fieldSchema);
|
|
181853
|
+
const format2 = detectFieldFormat(fieldSchema);
|
|
181854
|
+
const isNested = type === "object";
|
|
181855
|
+
const isRef2 = type === "reference";
|
|
181856
|
+
const nestedProps = fieldSchema.properties || {};
|
|
181857
|
+
const nestedRequired = fieldSchema.required || [];
|
|
181858
|
+
const refTarget = fieldSchema.$ref ? fieldSchema.$ref.replace("#/contentTypes/", "") : "";
|
|
181859
|
+
return html`
|
|
181860
|
+
<div class="schema-field-card">
|
|
181861
|
+
<div class="schema-field-row">
|
|
181862
|
+
<sp-textfield
|
|
181863
|
+
size="s"
|
|
181864
|
+
quiet
|
|
181865
|
+
value=${fieldName}
|
|
181866
|
+
class="schema-field-name-input"
|
|
181867
|
+
@change=${(e) => {
|
|
181868
|
+
const newName = e.target.value.trim();
|
|
181869
|
+
if (newName && newName !== fieldName)
|
|
181870
|
+
handlers.onRename(fieldName, newName);
|
|
181871
|
+
else
|
|
181872
|
+
e.target.value = fieldName;
|
|
181873
|
+
}}
|
|
181874
|
+
@keydown=${(e) => {
|
|
181875
|
+
if (e.key === "Enter")
|
|
181876
|
+
e.target.blur();
|
|
181877
|
+
if (e.key === "Escape") {
|
|
181878
|
+
e.target.value = fieldName;
|
|
181879
|
+
e.target.blur();
|
|
181880
|
+
}
|
|
181881
|
+
}}
|
|
181882
|
+
></sp-textfield>
|
|
181883
|
+
${typePickerTpl(type, (newType) => handlers.onChangeType(fieldName, newType))}
|
|
181884
|
+
${type === "string" || type === "array" ? formatPickerTpl(format2, (f) => {
|
|
181885
|
+
if (handlers.onChangeFormat)
|
|
181886
|
+
handlers.onChangeFormat(fieldName, f);
|
|
181887
|
+
}) : nothing}
|
|
181888
|
+
<sp-switch
|
|
181889
|
+
size="s"
|
|
181890
|
+
?checked=${isRequired}
|
|
181891
|
+
@change=${() => handlers.onToggleRequired(fieldName)}
|
|
181892
|
+
>
|
|
181893
|
+
Req
|
|
181894
|
+
</sp-switch>
|
|
181895
|
+
<sp-action-button
|
|
181896
|
+
size="xs"
|
|
181897
|
+
quiet
|
|
181898
|
+
title="Delete field"
|
|
181899
|
+
@click=${() => handlers.onDelete(fieldName)}
|
|
181900
|
+
>
|
|
181901
|
+
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
181902
|
+
</sp-action-button>
|
|
181903
|
+
</div>
|
|
181904
|
+
${isRef2 && contentTypeNames.length ? html`
|
|
181905
|
+
<div class="schema-field-ref-target">
|
|
181906
|
+
<sp-picker
|
|
181907
|
+
size="s"
|
|
181908
|
+
label="Target"
|
|
181909
|
+
value=${refTarget}
|
|
181910
|
+
@change=${(e) => {
|
|
181911
|
+
if (handlers.onChangeRefTarget)
|
|
181912
|
+
handlers.onChangeRefTarget(fieldName, e.target.value);
|
|
181913
|
+
}}
|
|
181914
|
+
>
|
|
181915
|
+
${contentTypeNames.map((n2) => html`<sp-menu-item value=${n2}>${n2}</sp-menu-item>`)}
|
|
181916
|
+
</sp-picker>
|
|
181917
|
+
</div>
|
|
181918
|
+
` : nothing}
|
|
181919
|
+
${isNested ? html`
|
|
181920
|
+
<div class="schema-field-nested">
|
|
181921
|
+
${Object.entries(nestedProps).map(([name, sub]) => nestedFieldCardTpl(fieldName, name, sub, nestedRequired.includes(name), handlers))}
|
|
181922
|
+
${nestedAddFieldTpl(fieldName, handlers)}
|
|
181923
|
+
</div>
|
|
181924
|
+
` : nothing}
|
|
181925
|
+
</div>
|
|
181926
|
+
`;
|
|
181927
|
+
}
|
|
181928
|
+
function nestedFieldCardTpl(parentName, childName, childSchema, isRequired, handlers) {
|
|
181929
|
+
const type = detectFieldType(childSchema);
|
|
181930
|
+
const format2 = detectFieldFormat(childSchema);
|
|
181931
|
+
return html`
|
|
181932
|
+
<div class="schema-field-card schema-field-card--nested">
|
|
181933
|
+
<div class="schema-field-row">
|
|
181934
|
+
<sp-textfield
|
|
181935
|
+
size="s"
|
|
181936
|
+
quiet
|
|
181937
|
+
value=${childName}
|
|
181938
|
+
class="schema-field-name-input"
|
|
181939
|
+
@change=${(e) => {
|
|
181940
|
+
const newName = e.target.value.trim();
|
|
181941
|
+
if (newName && newName !== childName && handlers.onRenameNested) {
|
|
181942
|
+
handlers.onRenameNested(parentName, childName, newName);
|
|
181943
|
+
} else {
|
|
181944
|
+
e.target.value = childName;
|
|
181945
|
+
}
|
|
181946
|
+
}}
|
|
181947
|
+
@keydown=${(e) => {
|
|
181948
|
+
if (e.key === "Enter")
|
|
181949
|
+
e.target.blur();
|
|
181950
|
+
if (e.key === "Escape") {
|
|
181951
|
+
e.target.value = childName;
|
|
181952
|
+
e.target.blur();
|
|
181953
|
+
}
|
|
181954
|
+
}}
|
|
181955
|
+
></sp-textfield>
|
|
181956
|
+
${typePickerTpl(type, (newType) => {
|
|
181957
|
+
if (handlers.onChangeNestedType)
|
|
181958
|
+
handlers.onChangeNestedType(parentName, childName, newType);
|
|
181959
|
+
})}
|
|
181960
|
+
${type === "string" || type === "array" ? formatPickerTpl(format2, (f) => {
|
|
181961
|
+
if (handlers.onChangeNestedFormat)
|
|
181962
|
+
handlers.onChangeNestedFormat(parentName, childName, f);
|
|
181963
|
+
}) : nothing}
|
|
181964
|
+
<sp-switch
|
|
181965
|
+
size="s"
|
|
181966
|
+
?checked=${isRequired}
|
|
181967
|
+
@change=${() => {
|
|
181968
|
+
if (handlers.onToggleNestedRequired)
|
|
181969
|
+
handlers.onToggleNestedRequired(parentName, childName);
|
|
181970
|
+
}}
|
|
181971
|
+
>
|
|
181972
|
+
Req
|
|
181973
|
+
</sp-switch>
|
|
181974
|
+
<sp-action-button
|
|
181975
|
+
size="xs"
|
|
181976
|
+
quiet
|
|
181977
|
+
title="Delete field"
|
|
181978
|
+
@click=${() => {
|
|
181979
|
+
if (handlers.onDeleteNested)
|
|
181980
|
+
handlers.onDeleteNested(parentName, childName);
|
|
181981
|
+
}}
|
|
181982
|
+
>
|
|
181983
|
+
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
181984
|
+
</sp-action-button>
|
|
181985
|
+
</div>
|
|
181986
|
+
</div>
|
|
181987
|
+
`;
|
|
181988
|
+
}
|
|
181989
|
+
function nestedAddFieldTpl(parentName, handlers) {
|
|
181990
|
+
return html`
|
|
181991
|
+
<div class="schema-nested-add">
|
|
181992
|
+
<sp-textfield
|
|
181993
|
+
size="s"
|
|
181994
|
+
placeholder="field name"
|
|
181995
|
+
class="schema-nested-add-name"
|
|
181996
|
+
@keydown=${(e) => {
|
|
181997
|
+
if (e.key === "Enter") {
|
|
181998
|
+
const row = e.target.closest(".schema-nested-add");
|
|
181999
|
+
const name = e.target.value.trim();
|
|
182000
|
+
const typePicker = row?.querySelector("sp-picker");
|
|
182001
|
+
const type = typePicker?.value || "string";
|
|
182002
|
+
if (name && handlers.onAddNestedField) {
|
|
182003
|
+
handlers.onAddNestedField(parentName, { name, type, required: false });
|
|
182004
|
+
e.target.value = "";
|
|
182005
|
+
}
|
|
182006
|
+
}
|
|
182007
|
+
}}
|
|
182008
|
+
></sp-textfield>
|
|
182009
|
+
${typePickerTpl("string", () => {})}
|
|
182010
|
+
<sp-action-button
|
|
182011
|
+
size="xs"
|
|
182012
|
+
quiet
|
|
182013
|
+
title="Add nested field"
|
|
182014
|
+
@click=${(e) => {
|
|
182015
|
+
const row = e.target.closest(".schema-nested-add");
|
|
182016
|
+
const nameInput = row?.querySelector(".schema-nested-add-name");
|
|
182017
|
+
const typePicker = row?.querySelector("sp-picker");
|
|
182018
|
+
const name = nameInput?.value?.trim();
|
|
182019
|
+
const type = typePicker?.value || "string";
|
|
182020
|
+
if (name && handlers.onAddNestedField) {
|
|
182021
|
+
handlers.onAddNestedField(parentName, { name, type, required: false });
|
|
182022
|
+
nameInput.value = "";
|
|
182023
|
+
}
|
|
182024
|
+
}}
|
|
182025
|
+
>
|
|
182026
|
+
<sp-icon-add slot="icon"></sp-icon-add>
|
|
182027
|
+
</sp-action-button>
|
|
182028
|
+
</div>
|
|
182029
|
+
`;
|
|
182030
|
+
}
|
|
182031
|
+
function typePickerTpl(value2, onChange) {
|
|
182032
|
+
return html`
|
|
182033
|
+
<sp-picker
|
|
182034
|
+
size="s"
|
|
182035
|
+
label="Type"
|
|
182036
|
+
value=${value2}
|
|
182037
|
+
@change=${(e) => onChange(e.target.value)}
|
|
182038
|
+
>
|
|
182039
|
+
${FIELD_TYPES.map((t) => html`<sp-menu-item value=${t}>${t}</sp-menu-item>`)}
|
|
182040
|
+
</sp-picker>
|
|
182041
|
+
`;
|
|
182042
|
+
}
|
|
182043
|
+
function formatPickerTpl(value2, onChange) {
|
|
182044
|
+
return html`
|
|
182045
|
+
<sp-picker
|
|
182046
|
+
size="s"
|
|
182047
|
+
label="Format"
|
|
182048
|
+
value=${value2}
|
|
182049
|
+
@change=${(e) => onChange(e.target.value)}
|
|
182050
|
+
>
|
|
182051
|
+
${FORMAT_OPTIONS.map((f) => html`<sp-menu-item value=${f}>${f || "(none)"}</sp-menu-item>`)}
|
|
182052
|
+
</sp-picker>
|
|
182053
|
+
`;
|
|
182054
|
+
}
|
|
182055
|
+
function addFieldFormTpl(state, handlers) {
|
|
182056
|
+
return html`
|
|
182057
|
+
<div class="schema-add-field">
|
|
182058
|
+
<sp-textfield
|
|
182059
|
+
size="s"
|
|
182060
|
+
placeholder="Field name"
|
|
182061
|
+
.value=${state.name}
|
|
182062
|
+
@input=${(e) => handlers.onInput("name", e.target.value)}
|
|
182063
|
+
@keydown=${(e) => {
|
|
182064
|
+
if (e.key === "Enter")
|
|
182065
|
+
handlers.onConfirm();
|
|
182066
|
+
if (e.key === "Escape")
|
|
182067
|
+
handlers.onCancel();
|
|
182068
|
+
}}
|
|
182069
|
+
></sp-textfield>
|
|
182070
|
+
${typePickerTpl(state.type, (t) => handlers.onInput("type", t))}
|
|
182071
|
+
${state.type === "string" || state.type === "array" ? formatPickerTpl(state.format || "", (f) => handlers.onInput("format", f)) : nothing}
|
|
182072
|
+
<sp-switch
|
|
182073
|
+
size="s"
|
|
182074
|
+
?checked=${state.required}
|
|
182075
|
+
@change=${(e) => handlers.onInput("required", e.target.checked)}
|
|
182076
|
+
>
|
|
182077
|
+
Required
|
|
182078
|
+
</sp-switch>
|
|
182079
|
+
<sp-action-button size="s" @click=${handlers.onConfirm}>Add</sp-action-button>
|
|
182080
|
+
<sp-action-button size="s" quiet @click=${handlers.onCancel}>Cancel</sp-action-button>
|
|
182081
|
+
</div>
|
|
182082
|
+
`;
|
|
182083
|
+
}
|
|
182084
|
+
function schemaForType(type, format2) {
|
|
182085
|
+
switch (type) {
|
|
182086
|
+
case "number":
|
|
182087
|
+
return { type: "number" };
|
|
182088
|
+
case "boolean":
|
|
182089
|
+
return { type: "boolean" };
|
|
182090
|
+
case "array":
|
|
182091
|
+
return format2 ? { type: "array", items: { type: "string", format: format2 } } : { type: "array", items: { type: "string" } };
|
|
182092
|
+
case "object":
|
|
182093
|
+
return { type: "object", properties: {}, required: [] };
|
|
182094
|
+
case "reference":
|
|
182095
|
+
return { $ref: "#/contentTypes/" };
|
|
182096
|
+
default:
|
|
182097
|
+
return format2 ? { type: "string", format: format2 } : { type: "string" };
|
|
182098
|
+
}
|
|
182099
|
+
}
|
|
182100
|
+
function yamlDefault(type, format2) {
|
|
182101
|
+
if (format2 === "date")
|
|
182102
|
+
return new Date().toISOString().split("T")[0];
|
|
182103
|
+
if (format2 === "image")
|
|
182104
|
+
return '""';
|
|
182105
|
+
switch (type) {
|
|
182106
|
+
case "boolean":
|
|
182107
|
+
return "false";
|
|
182108
|
+
case "number":
|
|
182109
|
+
return "0";
|
|
182110
|
+
case "array":
|
|
182111
|
+
return "[]";
|
|
182112
|
+
case "object":
|
|
182113
|
+
return "{}";
|
|
182114
|
+
default:
|
|
182115
|
+
return '""';
|
|
182116
|
+
}
|
|
182117
|
+
}
|
|
182118
|
+
|
|
182119
|
+
// src/browse/browse.js
|
|
183313
182120
|
var CATEGORIES = [
|
|
183314
182121
|
{ key: "all", label: "All" },
|
|
183315
182122
|
{ key: "pages", label: "Pages", dir: "pages" },
|
|
@@ -183850,7 +182657,7 @@ function _flush() {
|
|
|
183850
182657
|
const { selection, hover } = tab.session;
|
|
183851
182658
|
const { stylebookTab } = tab.session.ui;
|
|
183852
182659
|
const canvasMode = _ctx9.getCanvasMode();
|
|
183853
|
-
if (canvasMode !== "design" && canvasMode !== "edit" && canvasMode !== "
|
|
182660
|
+
if (canvasMode !== "design" && canvasMode !== "edit" && canvasMode !== "stylebook") {
|
|
183854
182661
|
for (const p of canvasPanels) {
|
|
183855
182662
|
render2(nothing, p.overlay);
|
|
183856
182663
|
p.overlayClk.style.pointerEvents = "none";
|
|
@@ -183861,7 +182668,7 @@ function _flush() {
|
|
|
183861
182668
|
}
|
|
183862
182669
|
return;
|
|
183863
182670
|
}
|
|
183864
|
-
if (canvasMode === "
|
|
182671
|
+
if (canvasMode === "stylebook") {
|
|
183865
182672
|
const enable = stylebookTab === "elements";
|
|
183866
182673
|
for (const p of canvasPanels) {
|
|
183867
182674
|
p.overlayClk.style.pointerEvents = enable ? "" : "none";
|
|
@@ -184006,7 +182813,7 @@ function renderCanvas() {
|
|
|
184006
182813
|
});
|
|
184007
182814
|
return;
|
|
184008
182815
|
}
|
|
184009
|
-
if (canvasMode === "
|
|
182816
|
+
if (canvasMode === "stylebook") {
|
|
184010
182817
|
renderStylebookMode({
|
|
184011
182818
|
canvasPanelTemplate,
|
|
184012
182819
|
applyTransform,
|
|
@@ -184810,6 +183617,11 @@ async function openFileInTab(path2) {
|
|
|
184810
183617
|
});
|
|
184811
183618
|
projectState.selectedPath = path2;
|
|
184812
183619
|
trackRecentFile({ path: path2, name: path2.split("/").pop() || path2 });
|
|
183620
|
+
if (path2 === "project.json") {
|
|
183621
|
+
const tab = activeTab.value;
|
|
183622
|
+
if (tab)
|
|
183623
|
+
tab.session.ui.canvasMode = "stylebook";
|
|
183624
|
+
}
|
|
184813
183625
|
statusMessage(`Opened ${path2.split("/").pop()}`);
|
|
184814
183626
|
} catch (e) {
|
|
184815
183627
|
statusMessage(`Error: ${e.message}`);
|
|
@@ -210905,6 +209717,113 @@ class JxValueSelector extends LitElement {
|
|
|
210905
209717
|
init_lit();
|
|
210906
209718
|
init_store();
|
|
210907
209719
|
init_workspace();
|
|
209720
|
+
|
|
209721
|
+
// src/utils/studio-utils.js
|
|
209722
|
+
function camelToKebab2(str) {
|
|
209723
|
+
return str.replace(/[A-Z]/g, (c6) => "-" + c6.toLowerCase());
|
|
209724
|
+
}
|
|
209725
|
+
function camelToLabel(prop) {
|
|
209726
|
+
return prop.replace(/([A-Z])/g, " $1").replace(/^./, (c6) => c6.toUpperCase());
|
|
209727
|
+
}
|
|
209728
|
+
function kebabToLabel(val) {
|
|
209729
|
+
return val.replace(/(^|-)(\w)/g, (_, sep2, c6) => (sep2 ? " " : "") + c6.toUpperCase());
|
|
209730
|
+
}
|
|
209731
|
+
function propLabel(entry, prop) {
|
|
209732
|
+
return entry?.$label || camelToLabel(prop);
|
|
209733
|
+
}
|
|
209734
|
+
function attrLabel(entry, attr) {
|
|
209735
|
+
if (entry?.$label)
|
|
209736
|
+
return entry.$label;
|
|
209737
|
+
if (attr.includes("-"))
|
|
209738
|
+
return attr.replace(/(^|-)(\w)/g, (_, sep2, c6) => (sep2 ? " " : "") + c6.toUpperCase());
|
|
209739
|
+
return camelToLabel(attr);
|
|
209740
|
+
}
|
|
209741
|
+
function abbreviateValue(val) {
|
|
209742
|
+
const map6 = {
|
|
209743
|
+
inline: "inl",
|
|
209744
|
+
"inline-block": "i-blk",
|
|
209745
|
+
"inline-flex": "i-flx",
|
|
209746
|
+
"inline-grid": "i-grd",
|
|
209747
|
+
contents: "cnt",
|
|
209748
|
+
"flow-root": "flow",
|
|
209749
|
+
nowrap: "no-wr",
|
|
209750
|
+
"wrap-reverse": "wr-rev",
|
|
209751
|
+
"flex-start": "start",
|
|
209752
|
+
"flex-end": "end",
|
|
209753
|
+
"space-between": "betw",
|
|
209754
|
+
"space-around": "arnd",
|
|
209755
|
+
"space-evenly": "even",
|
|
209756
|
+
stretch: "str",
|
|
209757
|
+
baseline: "base",
|
|
209758
|
+
normal: "norm",
|
|
209759
|
+
"row-reverse": "row-r",
|
|
209760
|
+
"column-reverse": "col-r",
|
|
209761
|
+
column: "col"
|
|
209762
|
+
};
|
|
209763
|
+
return map6[val] || val;
|
|
209764
|
+
}
|
|
209765
|
+
function inferInputType(entry) {
|
|
209766
|
+
if (entry.$shorthand === true)
|
|
209767
|
+
return "shorthand";
|
|
209768
|
+
if (entry.$input === "button-group")
|
|
209769
|
+
return "button-group";
|
|
209770
|
+
if (entry.$input === "media")
|
|
209771
|
+
return "media";
|
|
209772
|
+
if (entry.format === "color")
|
|
209773
|
+
return "color";
|
|
209774
|
+
if (entry.format === "uri-reference")
|
|
209775
|
+
return "media";
|
|
209776
|
+
if (entry.$units !== undefined)
|
|
209777
|
+
return "number-unit";
|
|
209778
|
+
if (entry.type === "number")
|
|
209779
|
+
return "number";
|
|
209780
|
+
if (Array.isArray(entry.enum))
|
|
209781
|
+
return "select";
|
|
209782
|
+
if (Array.isArray(entry.examples) || Array.isArray(entry.presets))
|
|
209783
|
+
return "combobox";
|
|
209784
|
+
return "text";
|
|
209785
|
+
}
|
|
209786
|
+
function findContentTypeSchema(documentPath, projectConfig) {
|
|
209787
|
+
if (!documentPath || !projectConfig?.contentTypes)
|
|
209788
|
+
return null;
|
|
209789
|
+
for (const [name, def3] of Object.entries(projectConfig.contentTypes)) {
|
|
209790
|
+
if (!def3.source || !def3.schema)
|
|
209791
|
+
continue;
|
|
209792
|
+
const src = def3.source.replace(/^\.\//, "");
|
|
209793
|
+
const dir = src.split("/")[0];
|
|
209794
|
+
const ext = src.includes("*.md") ? ".md" : src.includes("*.json") ? ".json" : src.includes("*.csv") ? ".csv" : null;
|
|
209795
|
+
if (documentPath.startsWith(dir + "/") && (!ext || documentPath.endsWith(ext))) {
|
|
209796
|
+
return { name, schema: def3.schema };
|
|
209797
|
+
}
|
|
209798
|
+
}
|
|
209799
|
+
return null;
|
|
209800
|
+
}
|
|
209801
|
+
function friendlyNameToVar(name, prefix) {
|
|
209802
|
+
const slug = name.trim().toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
209803
|
+
if (!slug)
|
|
209804
|
+
return "";
|
|
209805
|
+
return `${prefix}${slug}`;
|
|
209806
|
+
}
|
|
209807
|
+
function varDisplayName(varName, prefix) {
|
|
209808
|
+
return varName.replace(new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`), "").replace(/^--/, "").replace(/-/g, " ").replace(/\b\w/g, (c6) => c6.toUpperCase()) || varName;
|
|
209809
|
+
}
|
|
209810
|
+
function parseCemType(typeText) {
|
|
209811
|
+
if (!typeText)
|
|
209812
|
+
return { kind: "text" };
|
|
209813
|
+
const t15 = typeText.trim().replace(/\s*\|\s*undefined\b/g, "").trim();
|
|
209814
|
+
if (t15 === "boolean")
|
|
209815
|
+
return { kind: "boolean" };
|
|
209816
|
+
if (t15 === "number")
|
|
209817
|
+
return { kind: "number" };
|
|
209818
|
+
const enumMatch = t15.match(/^'[^']*'(\s*\|\s*'[^']*')+$/);
|
|
209819
|
+
if (enumMatch) {
|
|
209820
|
+
const options = [...t15.matchAll(/'([^']*)'/g)].map((m3) => m3[1]);
|
|
209821
|
+
return { kind: "combobox", options };
|
|
209822
|
+
}
|
|
209823
|
+
return { kind: "text" };
|
|
209824
|
+
}
|
|
209825
|
+
|
|
209826
|
+
// src/ui/color-selector.js
|
|
210908
209827
|
function getColorVars() {
|
|
210909
209828
|
const style2 = getEffectiveStyle(activeTab.value?.doc.document?.style);
|
|
210910
209829
|
if (!style2)
|
|
@@ -211724,6 +210643,1237 @@ function navigateSelection(direction = -1) {
|
|
|
211724
210643
|
init_store();
|
|
211725
210644
|
init_reactivity();
|
|
211726
210645
|
init_workspace();
|
|
210646
|
+
|
|
210647
|
+
// src/settings/defs-editor.js
|
|
210648
|
+
init_platform();
|
|
210649
|
+
init_store();
|
|
210650
|
+
var selectedDef = null;
|
|
210651
|
+
var showAddField = false;
|
|
210652
|
+
var newFieldState = { name: "", type: "string", format: "", required: false };
|
|
210653
|
+
var showNewDef = false;
|
|
210654
|
+
var newDefName = "";
|
|
210655
|
+
async function saveProjectConfig() {
|
|
210656
|
+
const platform4 = getPlatform();
|
|
210657
|
+
const config = projectState.projectConfig;
|
|
210658
|
+
await platform4.writeFile("project.json", JSON.stringify(config, null, "\t"));
|
|
210659
|
+
}
|
|
210660
|
+
function getSelectedDef() {
|
|
210661
|
+
const config = projectState?.projectConfig;
|
|
210662
|
+
return config?.$defs?.[selectedDef];
|
|
210663
|
+
}
|
|
210664
|
+
function handleNewDef(rerender) {
|
|
210665
|
+
const name = newDefName.trim();
|
|
210666
|
+
if (!name)
|
|
210667
|
+
return;
|
|
210668
|
+
const config = projectState?.projectConfig;
|
|
210669
|
+
if (!config)
|
|
210670
|
+
return;
|
|
210671
|
+
if (!config.$defs)
|
|
210672
|
+
config.$defs = {};
|
|
210673
|
+
if (config.$defs[name])
|
|
210674
|
+
return;
|
|
210675
|
+
config.$defs[name] = {
|
|
210676
|
+
type: "object",
|
|
210677
|
+
properties: {},
|
|
210678
|
+
required: []
|
|
210679
|
+
};
|
|
210680
|
+
selectedDef = name;
|
|
210681
|
+
showNewDef = false;
|
|
210682
|
+
newDefName = "";
|
|
210683
|
+
rerender();
|
|
210684
|
+
saveProjectConfig();
|
|
210685
|
+
}
|
|
210686
|
+
function handleAddField(rerender) {
|
|
210687
|
+
const name = newFieldState.name.trim();
|
|
210688
|
+
if (!name || !selectedDef)
|
|
210689
|
+
return;
|
|
210690
|
+
const def3 = getSelectedDef();
|
|
210691
|
+
if (!def3)
|
|
210692
|
+
return;
|
|
210693
|
+
if (!def3.properties)
|
|
210694
|
+
def3.properties = {};
|
|
210695
|
+
def3.properties[name] = schemaForType(newFieldState.type, newFieldState.format || undefined);
|
|
210696
|
+
if (newFieldState.required) {
|
|
210697
|
+
if (!def3.required)
|
|
210698
|
+
def3.required = [];
|
|
210699
|
+
if (!def3.required.includes(name))
|
|
210700
|
+
def3.required.push(name);
|
|
210701
|
+
}
|
|
210702
|
+
showAddField = false;
|
|
210703
|
+
newFieldState = { name: "", type: "string", format: "", required: false };
|
|
210704
|
+
rerender();
|
|
210705
|
+
saveProjectConfig();
|
|
210706
|
+
}
|
|
210707
|
+
function handleDeleteField(fieldName, rerender) {
|
|
210708
|
+
const def3 = getSelectedDef();
|
|
210709
|
+
if (!def3?.properties)
|
|
210710
|
+
return;
|
|
210711
|
+
delete def3.properties[fieldName];
|
|
210712
|
+
if (def3.required) {
|
|
210713
|
+
def3.required = def3.required.filter((r8) => r8 !== fieldName);
|
|
210714
|
+
}
|
|
210715
|
+
rerender();
|
|
210716
|
+
saveProjectConfig();
|
|
210717
|
+
}
|
|
210718
|
+
function handleToggleRequired(fieldName, rerender) {
|
|
210719
|
+
const def3 = getSelectedDef();
|
|
210720
|
+
if (!def3)
|
|
210721
|
+
return;
|
|
210722
|
+
if (!def3.required)
|
|
210723
|
+
def3.required = [];
|
|
210724
|
+
const idx = def3.required.indexOf(fieldName);
|
|
210725
|
+
if (idx >= 0)
|
|
210726
|
+
def3.required.splice(idx, 1);
|
|
210727
|
+
else
|
|
210728
|
+
def3.required.push(fieldName);
|
|
210729
|
+
rerender();
|
|
210730
|
+
saveProjectConfig();
|
|
210731
|
+
}
|
|
210732
|
+
function handleRenameField(oldName, newName, rerender) {
|
|
210733
|
+
const def3 = getSelectedDef();
|
|
210734
|
+
if (!def3?.properties || !newName || def3.properties[newName])
|
|
210735
|
+
return;
|
|
210736
|
+
const newProps = {};
|
|
210737
|
+
for (const [key, val] of Object.entries(def3.properties)) {
|
|
210738
|
+
newProps[key === oldName ? newName : key] = val;
|
|
210739
|
+
}
|
|
210740
|
+
def3.properties = newProps;
|
|
210741
|
+
if (def3.required) {
|
|
210742
|
+
def3.required = def3.required.map((r8) => r8 === oldName ? newName : r8);
|
|
210743
|
+
}
|
|
210744
|
+
rerender();
|
|
210745
|
+
saveProjectConfig();
|
|
210746
|
+
}
|
|
210747
|
+
function handleChangeType(fieldName, newType, rerender) {
|
|
210748
|
+
const def3 = getSelectedDef();
|
|
210749
|
+
if (!def3?.properties?.[fieldName])
|
|
210750
|
+
return;
|
|
210751
|
+
const oldFormat = newType === "string" || newType === "array" ? detectFieldFormat(def3.properties[fieldName]) : undefined;
|
|
210752
|
+
def3.properties[fieldName] = schemaForType(newType, oldFormat || undefined);
|
|
210753
|
+
rerender();
|
|
210754
|
+
saveProjectConfig();
|
|
210755
|
+
}
|
|
210756
|
+
function handleChangeFormat(fieldName, format2, rerender) {
|
|
210757
|
+
const def3 = getSelectedDef();
|
|
210758
|
+
if (!def3?.properties?.[fieldName])
|
|
210759
|
+
return;
|
|
210760
|
+
const prop = def3.properties[fieldName];
|
|
210761
|
+
const type2 = prop.type || "string";
|
|
210762
|
+
def3.properties[fieldName] = schemaForType(type2, format2 || undefined);
|
|
210763
|
+
rerender();
|
|
210764
|
+
saveProjectConfig();
|
|
210765
|
+
}
|
|
210766
|
+
function handleAddNestedField(parentName, fieldState, rerender) {
|
|
210767
|
+
const def3 = getSelectedDef();
|
|
210768
|
+
const parent = def3?.properties?.[parentName];
|
|
210769
|
+
if (!parent)
|
|
210770
|
+
return;
|
|
210771
|
+
if (!parent.properties)
|
|
210772
|
+
parent.properties = {};
|
|
210773
|
+
parent.properties[fieldState.name] = schemaForType(fieldState.type);
|
|
210774
|
+
if (fieldState.required) {
|
|
210775
|
+
if (!parent.required)
|
|
210776
|
+
parent.required = [];
|
|
210777
|
+
if (!parent.required.includes(fieldState.name))
|
|
210778
|
+
parent.required.push(fieldState.name);
|
|
210779
|
+
}
|
|
210780
|
+
rerender();
|
|
210781
|
+
saveProjectConfig();
|
|
210782
|
+
}
|
|
210783
|
+
function handleDeleteNested(parentName, childName, rerender) {
|
|
210784
|
+
const def3 = getSelectedDef();
|
|
210785
|
+
const parent = def3?.properties?.[parentName];
|
|
210786
|
+
if (!parent?.properties)
|
|
210787
|
+
return;
|
|
210788
|
+
delete parent.properties[childName];
|
|
210789
|
+
if (parent.required) {
|
|
210790
|
+
parent.required = parent.required.filter((r8) => r8 !== childName);
|
|
210791
|
+
}
|
|
210792
|
+
rerender();
|
|
210793
|
+
saveProjectConfig();
|
|
210794
|
+
}
|
|
210795
|
+
function handleToggleNestedRequired(parentName, childName, rerender) {
|
|
210796
|
+
const def3 = getSelectedDef();
|
|
210797
|
+
const parent = def3?.properties?.[parentName];
|
|
210798
|
+
if (!parent)
|
|
210799
|
+
return;
|
|
210800
|
+
if (!parent.required)
|
|
210801
|
+
parent.required = [];
|
|
210802
|
+
const idx = parent.required.indexOf(childName);
|
|
210803
|
+
if (idx >= 0)
|
|
210804
|
+
parent.required.splice(idx, 1);
|
|
210805
|
+
else
|
|
210806
|
+
parent.required.push(childName);
|
|
210807
|
+
rerender();
|
|
210808
|
+
saveProjectConfig();
|
|
210809
|
+
}
|
|
210810
|
+
function handleRenameNested(parentName, oldChild, newChild, rerender) {
|
|
210811
|
+
const def3 = getSelectedDef();
|
|
210812
|
+
const parent = def3?.properties?.[parentName];
|
|
210813
|
+
if (!parent?.properties || !newChild || parent.properties[newChild])
|
|
210814
|
+
return;
|
|
210815
|
+
const newProps = {};
|
|
210816
|
+
for (const [key, val] of Object.entries(parent.properties)) {
|
|
210817
|
+
newProps[key === oldChild ? newChild : key] = val;
|
|
210818
|
+
}
|
|
210819
|
+
parent.properties = newProps;
|
|
210820
|
+
if (parent.required) {
|
|
210821
|
+
parent.required = parent.required.map((r8) => r8 === oldChild ? newChild : r8);
|
|
210822
|
+
}
|
|
210823
|
+
rerender();
|
|
210824
|
+
saveProjectConfig();
|
|
210825
|
+
}
|
|
210826
|
+
function handleChangeNestedType(parentName, childName, newType, rerender) {
|
|
210827
|
+
const def3 = getSelectedDef();
|
|
210828
|
+
const parent = def3?.properties?.[parentName];
|
|
210829
|
+
if (!parent?.properties?.[childName])
|
|
210830
|
+
return;
|
|
210831
|
+
const oldFormat = newType === "string" || newType === "array" ? detectFieldFormat(parent.properties[childName]) : undefined;
|
|
210832
|
+
parent.properties[childName] = schemaForType(newType, oldFormat || undefined);
|
|
210833
|
+
rerender();
|
|
210834
|
+
saveProjectConfig();
|
|
210835
|
+
}
|
|
210836
|
+
function handleChangeNestedFormat(parentName, childName, format2, rerender) {
|
|
210837
|
+
const def3 = getSelectedDef();
|
|
210838
|
+
const parent = def3?.properties?.[parentName];
|
|
210839
|
+
if (!parent?.properties?.[childName])
|
|
210840
|
+
return;
|
|
210841
|
+
const prop = parent.properties[childName];
|
|
210842
|
+
const type2 = prop.type || "string";
|
|
210843
|
+
parent.properties[childName] = schemaForType(type2, format2 || undefined);
|
|
210844
|
+
rerender();
|
|
210845
|
+
saveProjectConfig();
|
|
210846
|
+
}
|
|
210847
|
+
function handleDeleteDef(rerender) {
|
|
210848
|
+
if (!selectedDef)
|
|
210849
|
+
return;
|
|
210850
|
+
const config = projectState?.projectConfig;
|
|
210851
|
+
if (!config?.$defs?.[selectedDef])
|
|
210852
|
+
return;
|
|
210853
|
+
delete config.$defs[selectedDef];
|
|
210854
|
+
selectedDef = null;
|
|
210855
|
+
rerender();
|
|
210856
|
+
saveProjectConfig();
|
|
210857
|
+
}
|
|
210858
|
+
function renderDefsEditor(container) {
|
|
210859
|
+
const rerender = () => renderDefsEditor(container);
|
|
210860
|
+
const config = projectState?.projectConfig;
|
|
210861
|
+
const defs = config?.$defs || {};
|
|
210862
|
+
const defNames = Object.keys(defs);
|
|
210863
|
+
const listTpl = html`
|
|
210864
|
+
<div class="settings-list-panel">
|
|
210865
|
+
${defNames.map((name) => html`
|
|
210866
|
+
<sp-action-button
|
|
210867
|
+
size="s"
|
|
210868
|
+
?selected=${selectedDef === name}
|
|
210869
|
+
@click=${() => {
|
|
210870
|
+
selectedDef = name;
|
|
210871
|
+
showAddField = false;
|
|
210872
|
+
rerender();
|
|
210873
|
+
}}
|
|
210874
|
+
>
|
|
210875
|
+
${name}
|
|
210876
|
+
</sp-action-button>
|
|
210877
|
+
`)}
|
|
210878
|
+
${showNewDef ? html`
|
|
210879
|
+
<div class="settings-inline-form">
|
|
210880
|
+
<sp-textfield
|
|
210881
|
+
size="s"
|
|
210882
|
+
placeholder="TypeName"
|
|
210883
|
+
.value=${newDefName}
|
|
210884
|
+
@input=${(e20) => {
|
|
210885
|
+
newDefName = e20.target.value;
|
|
210886
|
+
}}
|
|
210887
|
+
@keydown=${(e20) => {
|
|
210888
|
+
if (e20.key === "Enter")
|
|
210889
|
+
handleNewDef(rerender);
|
|
210890
|
+
if (e20.key === "Escape") {
|
|
210891
|
+
showNewDef = false;
|
|
210892
|
+
rerender();
|
|
210893
|
+
}
|
|
210894
|
+
}}
|
|
210895
|
+
></sp-textfield>
|
|
210896
|
+
<sp-action-button size="s" @click=${() => handleNewDef(rerender)}>
|
|
210897
|
+
Create
|
|
210898
|
+
</sp-action-button>
|
|
210899
|
+
</div>
|
|
210900
|
+
` : html`
|
|
210901
|
+
<sp-action-button
|
|
210902
|
+
size="s"
|
|
210903
|
+
quiet
|
|
210904
|
+
@click=${() => {
|
|
210905
|
+
showNewDef = true;
|
|
210906
|
+
rerender();
|
|
210907
|
+
}}
|
|
210908
|
+
>
|
|
210909
|
+
<sp-icon-add slot="icon"></sp-icon-add> New Definition
|
|
210910
|
+
</sp-action-button>
|
|
210911
|
+
`}
|
|
210912
|
+
</div>
|
|
210913
|
+
`;
|
|
210914
|
+
let editorTpl;
|
|
210915
|
+
if (!selectedDef || !defs[selectedDef]) {
|
|
210916
|
+
editorTpl = html`<div class="settings-empty-state">Select or create a type definition</div>`;
|
|
210917
|
+
} else {
|
|
210918
|
+
const def3 = defs[selectedDef];
|
|
210919
|
+
const properties = def3.properties || {};
|
|
210920
|
+
const required = def3.required || [];
|
|
210921
|
+
const handlers = {
|
|
210922
|
+
onDelete: (n4) => handleDeleteField(n4, rerender),
|
|
210923
|
+
onToggleRequired: (n4) => handleToggleRequired(n4, rerender),
|
|
210924
|
+
onRename: (oldN, newN) => handleRenameField(oldN, newN, rerender),
|
|
210925
|
+
onChangeType: (n4, t15) => handleChangeType(n4, t15, rerender),
|
|
210926
|
+
onChangeFormat: (n4, f) => handleChangeFormat(n4, f, rerender),
|
|
210927
|
+
onAddNestedField: (p2, s2) => handleAddNestedField(p2, s2, rerender),
|
|
210928
|
+
onDeleteNested: (p2, c6) => handleDeleteNested(p2, c6, rerender),
|
|
210929
|
+
onToggleNestedRequired: (p2, c6) => handleToggleNestedRequired(p2, c6, rerender),
|
|
210930
|
+
onRenameNested: (p2, o19, n4) => handleRenameNested(p2, o19, n4, rerender),
|
|
210931
|
+
onChangeNestedType: (p2, c6, t15) => handleChangeNestedType(p2, c6, t15, rerender),
|
|
210932
|
+
onChangeNestedFormat: (p2, c6, f) => handleChangeNestedFormat(p2, c6, f, rerender)
|
|
210933
|
+
};
|
|
210934
|
+
const fieldCards = Object.entries(properties).map(([name, fieldDef]) => fieldCardTpl(name, fieldDef, required.includes(name), handlers));
|
|
210935
|
+
editorTpl = html`
|
|
210936
|
+
<div class="settings-editor-panel">
|
|
210937
|
+
<div class="settings-editor-header">
|
|
210938
|
+
<h3>${selectedDef}</h3>
|
|
210939
|
+
<sp-action-button
|
|
210940
|
+
size="xs"
|
|
210941
|
+
quiet
|
|
210942
|
+
title="Delete definition"
|
|
210943
|
+
@click=${() => handleDeleteDef(rerender)}
|
|
210944
|
+
>
|
|
210945
|
+
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
210946
|
+
</sp-action-button>
|
|
210947
|
+
</div>
|
|
210948
|
+
<div class="schema-field-list">${fieldCards}</div>
|
|
210949
|
+
${showAddField ? addFieldFormTpl(newFieldState, {
|
|
210950
|
+
onInput: (field, value2) => {
|
|
210951
|
+
newFieldState = { ...newFieldState, [field]: value2 };
|
|
210952
|
+
rerender();
|
|
210953
|
+
},
|
|
210954
|
+
onConfirm: () => handleAddField(rerender),
|
|
210955
|
+
onCancel: () => {
|
|
210956
|
+
showAddField = false;
|
|
210957
|
+
newFieldState = { name: "", type: "string", format: "", required: false };
|
|
210958
|
+
rerender();
|
|
210959
|
+
}
|
|
210960
|
+
}) : html`
|
|
210961
|
+
<sp-action-button
|
|
210962
|
+
size="s"
|
|
210963
|
+
quiet
|
|
210964
|
+
@click=${() => {
|
|
210965
|
+
showAddField = true;
|
|
210966
|
+
rerender();
|
|
210967
|
+
}}
|
|
210968
|
+
>
|
|
210969
|
+
<sp-icon-add slot="icon"></sp-icon-add> Add Field
|
|
210970
|
+
</sp-action-button>
|
|
210971
|
+
`}
|
|
210972
|
+
</div>
|
|
210973
|
+
`;
|
|
210974
|
+
}
|
|
210975
|
+
const tpl = html` <div class="settings-two-col">${listTpl} ${editorTpl}</div> `;
|
|
210976
|
+
render2(tpl, container);
|
|
210977
|
+
}
|
|
210978
|
+
|
|
210979
|
+
// src/settings/content-types-editor.js
|
|
210980
|
+
init_platform();
|
|
210981
|
+
init_store();
|
|
210982
|
+
var selectedContentType = null;
|
|
210983
|
+
var showAddField2 = false;
|
|
210984
|
+
var newFieldState2 = { name: "", type: "string", format: "", required: false };
|
|
210985
|
+
var showNewContentType = false;
|
|
210986
|
+
var newContentTypeName = "";
|
|
210987
|
+
async function saveProjectConfig2() {
|
|
210988
|
+
const platform4 = getPlatform();
|
|
210989
|
+
const config = projectState.projectConfig;
|
|
210990
|
+
await platform4.writeFile("project.json", JSON.stringify(config, null, "\t"));
|
|
210991
|
+
}
|
|
210992
|
+
function getSelectedSchema() {
|
|
210993
|
+
const config = projectState?.projectConfig;
|
|
210994
|
+
return config?.contentTypes?.[selectedContentType]?.schema;
|
|
210995
|
+
}
|
|
210996
|
+
function handleNewContentType(rerender) {
|
|
210997
|
+
const slug = newContentTypeName.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
210998
|
+
if (!slug)
|
|
210999
|
+
return;
|
|
211000
|
+
const config = projectState?.projectConfig;
|
|
211001
|
+
if (!config)
|
|
211002
|
+
return;
|
|
211003
|
+
if (!config.contentTypes)
|
|
211004
|
+
config.contentTypes = {};
|
|
211005
|
+
if (config.contentTypes[slug])
|
|
211006
|
+
return;
|
|
211007
|
+
config.contentTypes[slug] = {
|
|
211008
|
+
source: `./content/${slug}/**/*.md`,
|
|
211009
|
+
schema: { type: "object", properties: {}, required: [] }
|
|
211010
|
+
};
|
|
211011
|
+
selectedContentType = slug;
|
|
211012
|
+
showNewContentType = false;
|
|
211013
|
+
newContentTypeName = "";
|
|
211014
|
+
rerender();
|
|
211015
|
+
saveProjectConfig2().then(async () => {
|
|
211016
|
+
const platform4 = getPlatform();
|
|
211017
|
+
await platform4.writeFile(`content/${slug}/.gitkeep`, "");
|
|
211018
|
+
});
|
|
211019
|
+
}
|
|
211020
|
+
function handleAddField2(rerender) {
|
|
211021
|
+
const name = newFieldState2.name.trim();
|
|
211022
|
+
if (!name || !selectedContentType)
|
|
211023
|
+
return;
|
|
211024
|
+
const schema4 = getSelectedSchema();
|
|
211025
|
+
if (!schema4)
|
|
211026
|
+
return;
|
|
211027
|
+
if (!schema4.properties)
|
|
211028
|
+
schema4.properties = {};
|
|
211029
|
+
schema4.properties[name] = schemaForType(newFieldState2.type, newFieldState2.format || undefined);
|
|
211030
|
+
if (newFieldState2.required) {
|
|
211031
|
+
if (!schema4.required)
|
|
211032
|
+
schema4.required = [];
|
|
211033
|
+
if (!schema4.required.includes(name))
|
|
211034
|
+
schema4.required.push(name);
|
|
211035
|
+
}
|
|
211036
|
+
showAddField2 = false;
|
|
211037
|
+
newFieldState2 = { name: "", type: "string", format: "", required: false };
|
|
211038
|
+
rerender();
|
|
211039
|
+
saveProjectConfig2();
|
|
211040
|
+
}
|
|
211041
|
+
function handleDeleteField2(fieldName, rerender) {
|
|
211042
|
+
const schema4 = getSelectedSchema();
|
|
211043
|
+
if (!schema4?.properties)
|
|
211044
|
+
return;
|
|
211045
|
+
delete schema4.properties[fieldName];
|
|
211046
|
+
if (schema4.required) {
|
|
211047
|
+
schema4.required = schema4.required.filter((r8) => r8 !== fieldName);
|
|
211048
|
+
}
|
|
211049
|
+
rerender();
|
|
211050
|
+
saveProjectConfig2();
|
|
211051
|
+
}
|
|
211052
|
+
function handleToggleRequired2(fieldName, rerender) {
|
|
211053
|
+
const schema4 = getSelectedSchema();
|
|
211054
|
+
if (!schema4)
|
|
211055
|
+
return;
|
|
211056
|
+
if (!schema4.required)
|
|
211057
|
+
schema4.required = [];
|
|
211058
|
+
const idx = schema4.required.indexOf(fieldName);
|
|
211059
|
+
if (idx >= 0)
|
|
211060
|
+
schema4.required.splice(idx, 1);
|
|
211061
|
+
else
|
|
211062
|
+
schema4.required.push(fieldName);
|
|
211063
|
+
rerender();
|
|
211064
|
+
saveProjectConfig2();
|
|
211065
|
+
}
|
|
211066
|
+
function handleRenameField2(oldName, newName, rerender) {
|
|
211067
|
+
const schema4 = getSelectedSchema();
|
|
211068
|
+
if (!schema4?.properties || !newName || schema4.properties[newName])
|
|
211069
|
+
return;
|
|
211070
|
+
const newProps = {};
|
|
211071
|
+
for (const [key, val] of Object.entries(schema4.properties)) {
|
|
211072
|
+
newProps[key === oldName ? newName : key] = val;
|
|
211073
|
+
}
|
|
211074
|
+
schema4.properties = newProps;
|
|
211075
|
+
if (schema4.required) {
|
|
211076
|
+
schema4.required = schema4.required.map((r8) => r8 === oldName ? newName : r8);
|
|
211077
|
+
}
|
|
211078
|
+
rerender();
|
|
211079
|
+
saveProjectConfig2();
|
|
211080
|
+
}
|
|
211081
|
+
function handleChangeType2(fieldName, newType, rerender) {
|
|
211082
|
+
const schema4 = getSelectedSchema();
|
|
211083
|
+
if (!schema4?.properties?.[fieldName])
|
|
211084
|
+
return;
|
|
211085
|
+
const oldFormat = newType === "string" || newType === "array" ? detectFieldFormat(schema4.properties[fieldName]) : undefined;
|
|
211086
|
+
schema4.properties[fieldName] = schemaForType(newType, oldFormat || undefined);
|
|
211087
|
+
rerender();
|
|
211088
|
+
saveProjectConfig2();
|
|
211089
|
+
}
|
|
211090
|
+
function handleChangeFormat2(fieldName, format2, rerender) {
|
|
211091
|
+
const schema4 = getSelectedSchema();
|
|
211092
|
+
if (!schema4?.properties?.[fieldName])
|
|
211093
|
+
return;
|
|
211094
|
+
const prop = schema4.properties[fieldName];
|
|
211095
|
+
const type2 = prop.type || "string";
|
|
211096
|
+
schema4.properties[fieldName] = schemaForType(type2, format2 || undefined);
|
|
211097
|
+
rerender();
|
|
211098
|
+
saveProjectConfig2();
|
|
211099
|
+
}
|
|
211100
|
+
function handleChangeRefTarget(fieldName, target, rerender) {
|
|
211101
|
+
const schema4 = getSelectedSchema();
|
|
211102
|
+
if (!schema4?.properties)
|
|
211103
|
+
return;
|
|
211104
|
+
schema4.properties[fieldName] = { $ref: `#/contentTypes/${target}` };
|
|
211105
|
+
rerender();
|
|
211106
|
+
saveProjectConfig2();
|
|
211107
|
+
}
|
|
211108
|
+
function handleAddNestedField2(parentName, fieldState, rerender) {
|
|
211109
|
+
const schema4 = getSelectedSchema();
|
|
211110
|
+
const parent = schema4?.properties?.[parentName];
|
|
211111
|
+
if (!parent)
|
|
211112
|
+
return;
|
|
211113
|
+
if (!parent.properties)
|
|
211114
|
+
parent.properties = {};
|
|
211115
|
+
parent.properties[fieldState.name] = schemaForType(fieldState.type);
|
|
211116
|
+
if (fieldState.required) {
|
|
211117
|
+
if (!parent.required)
|
|
211118
|
+
parent.required = [];
|
|
211119
|
+
if (!parent.required.includes(fieldState.name))
|
|
211120
|
+
parent.required.push(fieldState.name);
|
|
211121
|
+
}
|
|
211122
|
+
rerender();
|
|
211123
|
+
saveProjectConfig2();
|
|
211124
|
+
}
|
|
211125
|
+
function handleDeleteNested2(parentName, childName, rerender) {
|
|
211126
|
+
const schema4 = getSelectedSchema();
|
|
211127
|
+
const parent = schema4?.properties?.[parentName];
|
|
211128
|
+
if (!parent?.properties)
|
|
211129
|
+
return;
|
|
211130
|
+
delete parent.properties[childName];
|
|
211131
|
+
if (parent.required) {
|
|
211132
|
+
parent.required = parent.required.filter((r8) => r8 !== childName);
|
|
211133
|
+
}
|
|
211134
|
+
rerender();
|
|
211135
|
+
saveProjectConfig2();
|
|
211136
|
+
}
|
|
211137
|
+
function handleToggleNestedRequired2(parentName, childName, rerender) {
|
|
211138
|
+
const schema4 = getSelectedSchema();
|
|
211139
|
+
const parent = schema4?.properties?.[parentName];
|
|
211140
|
+
if (!parent)
|
|
211141
|
+
return;
|
|
211142
|
+
if (!parent.required)
|
|
211143
|
+
parent.required = [];
|
|
211144
|
+
const idx = parent.required.indexOf(childName);
|
|
211145
|
+
if (idx >= 0)
|
|
211146
|
+
parent.required.splice(idx, 1);
|
|
211147
|
+
else
|
|
211148
|
+
parent.required.push(childName);
|
|
211149
|
+
rerender();
|
|
211150
|
+
saveProjectConfig2();
|
|
211151
|
+
}
|
|
211152
|
+
function handleRenameNested2(parentName, oldChild, newChild, rerender) {
|
|
211153
|
+
const schema4 = getSelectedSchema();
|
|
211154
|
+
const parent = schema4?.properties?.[parentName];
|
|
211155
|
+
if (!parent?.properties || !newChild || parent.properties[newChild])
|
|
211156
|
+
return;
|
|
211157
|
+
const newProps = {};
|
|
211158
|
+
for (const [key, val] of Object.entries(parent.properties)) {
|
|
211159
|
+
newProps[key === oldChild ? newChild : key] = val;
|
|
211160
|
+
}
|
|
211161
|
+
parent.properties = newProps;
|
|
211162
|
+
if (parent.required) {
|
|
211163
|
+
parent.required = parent.required.map((r8) => r8 === oldChild ? newChild : r8);
|
|
211164
|
+
}
|
|
211165
|
+
rerender();
|
|
211166
|
+
saveProjectConfig2();
|
|
211167
|
+
}
|
|
211168
|
+
function handleChangeNestedType2(parentName, childName, newType, rerender) {
|
|
211169
|
+
const schema4 = getSelectedSchema();
|
|
211170
|
+
const parent = schema4?.properties?.[parentName];
|
|
211171
|
+
if (!parent?.properties?.[childName])
|
|
211172
|
+
return;
|
|
211173
|
+
const oldFormat = newType === "string" || newType === "array" ? detectFieldFormat(parent.properties[childName]) : undefined;
|
|
211174
|
+
parent.properties[childName] = schemaForType(newType, oldFormat || undefined);
|
|
211175
|
+
rerender();
|
|
211176
|
+
saveProjectConfig2();
|
|
211177
|
+
}
|
|
211178
|
+
function handleChangeNestedFormat2(parentName, childName, format2, rerender) {
|
|
211179
|
+
const schema4 = getSelectedSchema();
|
|
211180
|
+
const parent = schema4?.properties?.[parentName];
|
|
211181
|
+
if (!parent?.properties?.[childName])
|
|
211182
|
+
return;
|
|
211183
|
+
const prop = parent.properties[childName];
|
|
211184
|
+
const type2 = prop.type || "string";
|
|
211185
|
+
parent.properties[childName] = schemaForType(type2, format2 || undefined);
|
|
211186
|
+
rerender();
|
|
211187
|
+
saveProjectConfig2();
|
|
211188
|
+
}
|
|
211189
|
+
function handleDeleteContentType(rerender) {
|
|
211190
|
+
if (!selectedContentType)
|
|
211191
|
+
return;
|
|
211192
|
+
const config = projectState?.projectConfig;
|
|
211193
|
+
if (!config?.contentTypes?.[selectedContentType])
|
|
211194
|
+
return;
|
|
211195
|
+
delete config.contentTypes[selectedContentType];
|
|
211196
|
+
selectedContentType = null;
|
|
211197
|
+
rerender();
|
|
211198
|
+
saveProjectConfig2();
|
|
211199
|
+
}
|
|
211200
|
+
function renderContentTypesEditor(container) {
|
|
211201
|
+
const rerender = () => renderContentTypesEditor(container);
|
|
211202
|
+
const config = projectState?.projectConfig;
|
|
211203
|
+
const contentTypes = config?.contentTypes || {};
|
|
211204
|
+
const contentTypeNames = Object.keys(contentTypes);
|
|
211205
|
+
const listTpl = html`
|
|
211206
|
+
<div class="settings-list-panel">
|
|
211207
|
+
${contentTypeNames.map((name) => html`
|
|
211208
|
+
<sp-action-button
|
|
211209
|
+
size="s"
|
|
211210
|
+
?selected=${selectedContentType === name}
|
|
211211
|
+
@click=${() => {
|
|
211212
|
+
selectedContentType = name;
|
|
211213
|
+
showAddField2 = false;
|
|
211214
|
+
rerender();
|
|
211215
|
+
}}
|
|
211216
|
+
>
|
|
211217
|
+
${name}
|
|
211218
|
+
</sp-action-button>
|
|
211219
|
+
`)}
|
|
211220
|
+
${showNewContentType ? html`
|
|
211221
|
+
<div class="settings-inline-form">
|
|
211222
|
+
<sp-textfield
|
|
211223
|
+
size="s"
|
|
211224
|
+
placeholder="content-type-name"
|
|
211225
|
+
.value=${newContentTypeName}
|
|
211226
|
+
@input=${(e20) => {
|
|
211227
|
+
newContentTypeName = e20.target.value;
|
|
211228
|
+
}}
|
|
211229
|
+
@keydown=${(e20) => {
|
|
211230
|
+
if (e20.key === "Enter")
|
|
211231
|
+
handleNewContentType(rerender);
|
|
211232
|
+
if (e20.key === "Escape") {
|
|
211233
|
+
showNewContentType = false;
|
|
211234
|
+
rerender();
|
|
211235
|
+
}
|
|
211236
|
+
}}
|
|
211237
|
+
></sp-textfield>
|
|
211238
|
+
<sp-action-button size="s" @click=${() => handleNewContentType(rerender)}>
|
|
211239
|
+
Create
|
|
211240
|
+
</sp-action-button>
|
|
211241
|
+
</div>
|
|
211242
|
+
` : html`
|
|
211243
|
+
<sp-action-button
|
|
211244
|
+
size="s"
|
|
211245
|
+
quiet
|
|
211246
|
+
@click=${() => {
|
|
211247
|
+
showNewContentType = true;
|
|
211248
|
+
rerender();
|
|
211249
|
+
}}
|
|
211250
|
+
>
|
|
211251
|
+
<sp-icon-add slot="icon"></sp-icon-add> New Content Type
|
|
211252
|
+
</sp-action-button>
|
|
211253
|
+
`}
|
|
211254
|
+
</div>
|
|
211255
|
+
`;
|
|
211256
|
+
let editorTpl;
|
|
211257
|
+
if (!selectedContentType || !contentTypes[selectedContentType]) {
|
|
211258
|
+
editorTpl = html`<div class="settings-empty-state">Select or create a content type</div>`;
|
|
211259
|
+
} else {
|
|
211260
|
+
const col = contentTypes[selectedContentType];
|
|
211261
|
+
const schema4 = col.schema || {};
|
|
211262
|
+
const properties = schema4.properties || {};
|
|
211263
|
+
const required = schema4.required || [];
|
|
211264
|
+
const handlers = {
|
|
211265
|
+
onDelete: (n4) => handleDeleteField2(n4, rerender),
|
|
211266
|
+
onToggleRequired: (n4) => handleToggleRequired2(n4, rerender),
|
|
211267
|
+
onRename: (oldN, newN) => handleRenameField2(oldN, newN, rerender),
|
|
211268
|
+
onChangeType: (n4, t15) => handleChangeType2(n4, t15, rerender),
|
|
211269
|
+
onChangeFormat: (n4, f) => handleChangeFormat2(n4, f, rerender),
|
|
211270
|
+
onChangeRefTarget: (n4, target) => handleChangeRefTarget(n4, target, rerender),
|
|
211271
|
+
onAddNestedField: (p2, s2) => handleAddNestedField2(p2, s2, rerender),
|
|
211272
|
+
onDeleteNested: (p2, c6) => handleDeleteNested2(p2, c6, rerender),
|
|
211273
|
+
onToggleNestedRequired: (p2, c6) => handleToggleNestedRequired2(p2, c6, rerender),
|
|
211274
|
+
onRenameNested: (p2, o19, n4) => handleRenameNested2(p2, o19, n4, rerender),
|
|
211275
|
+
onChangeNestedType: (p2, c6, t15) => handleChangeNestedType2(p2, c6, t15, rerender),
|
|
211276
|
+
onChangeNestedFormat: (p2, c6, f) => handleChangeNestedFormat2(p2, c6, f, rerender)
|
|
211277
|
+
};
|
|
211278
|
+
const fieldCards = Object.entries(properties).map(([name, def3]) => fieldCardTpl(name, def3, required.includes(name), handlers, contentTypeNames));
|
|
211279
|
+
editorTpl = html`
|
|
211280
|
+
<div class="settings-editor-panel">
|
|
211281
|
+
<div class="settings-editor-header">
|
|
211282
|
+
<h3>${selectedContentType}</h3>
|
|
211283
|
+
<sp-field-label size="s">Source: ${col.source || "—"}</sp-field-label>
|
|
211284
|
+
<sp-action-button
|
|
211285
|
+
size="xs"
|
|
211286
|
+
quiet
|
|
211287
|
+
title="Delete content type"
|
|
211288
|
+
@click=${() => handleDeleteContentType(rerender)}
|
|
211289
|
+
>
|
|
211290
|
+
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
211291
|
+
</sp-action-button>
|
|
211292
|
+
</div>
|
|
211293
|
+
<div class="schema-field-list">${fieldCards}</div>
|
|
211294
|
+
${showAddField2 ? addFieldFormTpl(newFieldState2, {
|
|
211295
|
+
onInput: (field, value2) => {
|
|
211296
|
+
newFieldState2 = { ...newFieldState2, [field]: value2 };
|
|
211297
|
+
rerender();
|
|
211298
|
+
},
|
|
211299
|
+
onConfirm: () => handleAddField2(rerender),
|
|
211300
|
+
onCancel: () => {
|
|
211301
|
+
showAddField2 = false;
|
|
211302
|
+
newFieldState2 = { name: "", type: "string", format: "", required: false };
|
|
211303
|
+
rerender();
|
|
211304
|
+
}
|
|
211305
|
+
}) : html`
|
|
211306
|
+
<sp-action-button
|
|
211307
|
+
size="s"
|
|
211308
|
+
quiet
|
|
211309
|
+
@click=${() => {
|
|
211310
|
+
showAddField2 = true;
|
|
211311
|
+
rerender();
|
|
211312
|
+
}}
|
|
211313
|
+
>
|
|
211314
|
+
<sp-icon-add slot="icon"></sp-icon-add> Add Field
|
|
211315
|
+
</sp-action-button>
|
|
211316
|
+
`}
|
|
211317
|
+
</div>
|
|
211318
|
+
`;
|
|
211319
|
+
}
|
|
211320
|
+
const tpl = html` <div class="settings-two-col">${listTpl} ${editorTpl}</div> `;
|
|
211321
|
+
render2(tpl, container);
|
|
211322
|
+
}
|
|
211323
|
+
|
|
211324
|
+
// src/settings/css-vars-editor.js
|
|
211325
|
+
init_store();
|
|
211326
|
+
function renderCssVarsEditor(container) {
|
|
211327
|
+
const config = projectState.projectConfig || {};
|
|
211328
|
+
const rootStyle = config.style || {};
|
|
211329
|
+
const media = getEffectiveMedia(config.$media);
|
|
211330
|
+
const groups = { color: [], font: [], size: [], other: [] };
|
|
211331
|
+
for (const [k, v] of Object.entries(rootStyle)) {
|
|
211332
|
+
if (!k.startsWith("--"))
|
|
211333
|
+
continue;
|
|
211334
|
+
if (typeof v !== "string" && typeof v !== "number")
|
|
211335
|
+
continue;
|
|
211336
|
+
if (k.startsWith("--color"))
|
|
211337
|
+
groups.color.push([k, v]);
|
|
211338
|
+
else if (k.startsWith("--font"))
|
|
211339
|
+
groups.font.push([k, v]);
|
|
211340
|
+
else if (k.startsWith("--size") || k.startsWith("--spacing") || k.startsWith("--radius"))
|
|
211341
|
+
groups.size.push([k, v]);
|
|
211342
|
+
else
|
|
211343
|
+
groups.other.push([k, v]);
|
|
211344
|
+
}
|
|
211345
|
+
const mediaNames = media ? Object.keys(media).filter((m3) => m3 !== "--") : [];
|
|
211346
|
+
const save = () => {
|
|
211347
|
+
updateSiteConfig({ style: { ...rootStyle } });
|
|
211348
|
+
};
|
|
211349
|
+
const updateVar = (name, val) => {
|
|
211350
|
+
rootStyle[name] = val;
|
|
211351
|
+
save();
|
|
211352
|
+
};
|
|
211353
|
+
const deleteVar = (name) => {
|
|
211354
|
+
delete rootStyle[name];
|
|
211355
|
+
save();
|
|
211356
|
+
renderCssVarsEditor(container);
|
|
211357
|
+
};
|
|
211358
|
+
const addVar = (prefix, friendlyName, val) => {
|
|
211359
|
+
const varName = friendlyNameToVar(friendlyName, prefix);
|
|
211360
|
+
if (!varName || !val)
|
|
211361
|
+
return;
|
|
211362
|
+
rootStyle[varName] = val;
|
|
211363
|
+
save();
|
|
211364
|
+
renderCssVarsEditor(container);
|
|
211365
|
+
};
|
|
211366
|
+
const tpl = html`
|
|
211367
|
+
<div class="settings-section">
|
|
211368
|
+
<h3 class="settings-section-title">CSS Variables</h3>
|
|
211369
|
+
|
|
211370
|
+
${renderColorSection(groups.color, updateVar, deleteVar, addVar)}
|
|
211371
|
+
${renderFontSection(groups.font, updateVar, deleteVar, addVar)}
|
|
211372
|
+
${renderSizeSection(groups.size, updateVar, deleteVar, addVar, rootStyle, mediaNames)}
|
|
211373
|
+
${groups.other.length > 0 ? renderOtherSection(groups.other, updateVar, deleteVar, addVar, rootStyle, mediaNames) : nothing}
|
|
211374
|
+
</div>
|
|
211375
|
+
`;
|
|
211376
|
+
render2(tpl, container);
|
|
211377
|
+
}
|
|
211378
|
+
function renderColorSection(vars, updateVar, deleteVar, addVar) {
|
|
211379
|
+
return html`
|
|
211380
|
+
<div class="css-vars-group">
|
|
211381
|
+
<h4 class="css-vars-group-title">Colors</h4>
|
|
211382
|
+
${vars.map(([name, val]) => html`
|
|
211383
|
+
<div class="css-var-row">
|
|
211384
|
+
<div class="css-var-swatch" style="background:${val}">
|
|
211385
|
+
<input
|
|
211386
|
+
type="color"
|
|
211387
|
+
.value=${val && val.startsWith("#") ? val : "#007acc"}
|
|
211388
|
+
@input=${(e20) => updateVar(name, e20.target.value)}
|
|
211389
|
+
/>
|
|
211390
|
+
</div>
|
|
211391
|
+
<span class="css-var-name">${varDisplayName(name, "--color-")}</span>
|
|
211392
|
+
<sp-textfield
|
|
211393
|
+
size="s"
|
|
211394
|
+
.value=${String(val)}
|
|
211395
|
+
@change=${(e20) => updateVar(name, e20.target.value)}
|
|
211396
|
+
style="flex:1;max-width:160px"
|
|
211397
|
+
></sp-textfield>
|
|
211398
|
+
<sp-action-button quiet size="s" @click=${() => deleteVar(name)}>
|
|
211399
|
+
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
211400
|
+
</sp-action-button>
|
|
211401
|
+
</div>
|
|
211402
|
+
`)}
|
|
211403
|
+
${renderAddRow("--color-", "Primary Blue", "#007acc", addVar)}
|
|
211404
|
+
</div>
|
|
211405
|
+
`;
|
|
211406
|
+
}
|
|
211407
|
+
function renderFontSection(vars, updateVar, deleteVar, addVar) {
|
|
211408
|
+
return html`
|
|
211409
|
+
<div class="css-vars-group">
|
|
211410
|
+
<h4 class="css-vars-group-title">Fonts</h4>
|
|
211411
|
+
${vars.map(([name, val]) => html`
|
|
211412
|
+
<div class="css-var-row">
|
|
211413
|
+
<span class="css-var-name">${varDisplayName(name, "--font-")}</span>
|
|
211414
|
+
<sp-textfield
|
|
211415
|
+
size="s"
|
|
211416
|
+
.value=${String(val)}
|
|
211417
|
+
@change=${(e20) => updateVar(name, e20.target.value)}
|
|
211418
|
+
style="flex:1"
|
|
211419
|
+
></sp-textfield>
|
|
211420
|
+
<sp-action-button quiet size="s" @click=${() => deleteVar(name)}>
|
|
211421
|
+
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
211422
|
+
</sp-action-button>
|
|
211423
|
+
</div>
|
|
211424
|
+
<div class="css-var-font-preview" style="font-family:${val}">
|
|
211425
|
+
The quick brown fox jumps over the lazy dog
|
|
211426
|
+
</div>
|
|
211427
|
+
`)}
|
|
211428
|
+
${renderAddRow("--font-", "Body Serif", "'Georgia', serif", addVar)}
|
|
211429
|
+
</div>
|
|
211430
|
+
`;
|
|
211431
|
+
}
|
|
211432
|
+
function renderSizeSection(vars, updateVar, deleteVar, addVar, rootStyle, mediaNames) {
|
|
211433
|
+
return html`
|
|
211434
|
+
<div class="css-vars-group">
|
|
211435
|
+
<h4 class="css-vars-group-title">Sizes & Spacing</h4>
|
|
211436
|
+
${vars.map(([name, val]) => html`
|
|
211437
|
+
<div class="css-var-row">
|
|
211438
|
+
<span class="css-var-name"
|
|
211439
|
+
>${varDisplayName(name, "--size-") || varDisplayName(name, "--spacing-") || varDisplayName(name, "--radius-") || name}</span
|
|
211440
|
+
>
|
|
211441
|
+
<sp-textfield
|
|
211442
|
+
size="s"
|
|
211443
|
+
.value=${String(val)}
|
|
211444
|
+
@change=${(e20) => updateVar(name, e20.target.value)}
|
|
211445
|
+
style="max-width:120px"
|
|
211446
|
+
></sp-textfield>
|
|
211447
|
+
<sp-action-button quiet size="s" @click=${() => deleteVar(name)}>
|
|
211448
|
+
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
211449
|
+
</sp-action-button>
|
|
211450
|
+
</div>
|
|
211451
|
+
${mediaNames.length > 0 ? renderMediaOverrides(name, rootStyle, mediaNames) : nothing}
|
|
211452
|
+
`)}
|
|
211453
|
+
${renderAddRow("--size-", "Spacing Large", "32px", addVar)}
|
|
211454
|
+
</div>
|
|
211455
|
+
`;
|
|
211456
|
+
}
|
|
211457
|
+
function renderOtherSection(vars, updateVar, deleteVar, addVar, rootStyle, mediaNames) {
|
|
211458
|
+
return html`
|
|
211459
|
+
<div class="css-vars-group">
|
|
211460
|
+
<h4 class="css-vars-group-title">Other</h4>
|
|
211461
|
+
${vars.map(([name, val]) => html`
|
|
211462
|
+
<div class="css-var-row">
|
|
211463
|
+
<span class="css-var-name">${name}</span>
|
|
211464
|
+
<sp-textfield
|
|
211465
|
+
size="s"
|
|
211466
|
+
.value=${String(val)}
|
|
211467
|
+
@change=${(e20) => updateVar(name, e20.target.value)}
|
|
211468
|
+
style="flex:1"
|
|
211469
|
+
></sp-textfield>
|
|
211470
|
+
<sp-action-button quiet size="s" @click=${() => deleteVar(name)}>
|
|
211471
|
+
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
211472
|
+
</sp-action-button>
|
|
211473
|
+
</div>
|
|
211474
|
+
${mediaNames.length > 0 ? renderMediaOverrides(name, rootStyle, mediaNames) : nothing}
|
|
211475
|
+
`)}
|
|
211476
|
+
${renderAddRow("--", "Custom Var", "value", addVar)}
|
|
211477
|
+
</div>
|
|
211478
|
+
`;
|
|
211479
|
+
}
|
|
211480
|
+
function renderMediaOverrides(varName, rootStyle, mediaNames) {
|
|
211481
|
+
const overrides = [];
|
|
211482
|
+
for (const mediaName of mediaNames) {
|
|
211483
|
+
const mediaKey = `@${mediaName}`;
|
|
211484
|
+
const mediaBlock = rootStyle[mediaKey];
|
|
211485
|
+
if (mediaBlock && typeof mediaBlock === "object" && mediaBlock[varName]) {
|
|
211486
|
+
overrides.push({ mediaName, value: mediaBlock[varName] });
|
|
211487
|
+
}
|
|
211488
|
+
}
|
|
211489
|
+
if (overrides.length === 0)
|
|
211490
|
+
return nothing;
|
|
211491
|
+
return html`
|
|
211492
|
+
<div class="css-var-media-overrides">
|
|
211493
|
+
${overrides.map((o19) => html`
|
|
211494
|
+
<div class="css-var-media-row">
|
|
211495
|
+
<span class="css-var-media-label">@${o19.mediaName}</span>
|
|
211496
|
+
<sp-textfield
|
|
211497
|
+
size="s"
|
|
211498
|
+
.value=${String(o19.value)}
|
|
211499
|
+
@change=${(e20) => {
|
|
211500
|
+
if (!rootStyle[`@${o19.mediaName}`])
|
|
211501
|
+
rootStyle[`@${o19.mediaName}`] = {};
|
|
211502
|
+
rootStyle[`@${o19.mediaName}`][varName] = e20.target.value;
|
|
211503
|
+
updateSiteConfig({ style: { ...rootStyle } });
|
|
211504
|
+
}}
|
|
211505
|
+
style="max-width:120px"
|
|
211506
|
+
></sp-textfield>
|
|
211507
|
+
</div>
|
|
211508
|
+
`)}
|
|
211509
|
+
</div>
|
|
211510
|
+
`;
|
|
211511
|
+
}
|
|
211512
|
+
function renderAddRow(prefix, placeholder, valuePlaceholder, addVar) {
|
|
211513
|
+
let nameEl = null;
|
|
211514
|
+
let valEl = null;
|
|
211515
|
+
return html`
|
|
211516
|
+
<div class="css-var-add-row">
|
|
211517
|
+
<sp-textfield
|
|
211518
|
+
size="s"
|
|
211519
|
+
placeholder=${placeholder}
|
|
211520
|
+
${ref2((el) => {
|
|
211521
|
+
if (el)
|
|
211522
|
+
nameEl = el;
|
|
211523
|
+
})}
|
|
211524
|
+
></sp-textfield>
|
|
211525
|
+
<sp-textfield
|
|
211526
|
+
size="s"
|
|
211527
|
+
placeholder=${valuePlaceholder}
|
|
211528
|
+
${ref2((el) => {
|
|
211529
|
+
if (el)
|
|
211530
|
+
valEl = el;
|
|
211531
|
+
})}
|
|
211532
|
+
></sp-textfield>
|
|
211533
|
+
<sp-action-button
|
|
211534
|
+
size="s"
|
|
211535
|
+
@click=${() => {
|
|
211536
|
+
if (nameEl && valEl) {
|
|
211537
|
+
addVar(prefix, nameEl.value, valEl.value);
|
|
211538
|
+
}
|
|
211539
|
+
}}
|
|
211540
|
+
>Add</sp-action-button
|
|
211541
|
+
>
|
|
211542
|
+
</div>
|
|
211543
|
+
`;
|
|
211544
|
+
}
|
|
211545
|
+
|
|
211546
|
+
// src/settings/head-editor.js
|
|
211547
|
+
init_store();
|
|
211548
|
+
function renderHeadEditor(container) {
|
|
211549
|
+
const config = projectState.projectConfig || {};
|
|
211550
|
+
const headEntries = config.$head || [];
|
|
211551
|
+
const save = () => {
|
|
211552
|
+
updateSiteConfig({ $head: headEntries });
|
|
211553
|
+
};
|
|
211554
|
+
const addEntry = (tag7) => {
|
|
211555
|
+
const entry = { tag: tag7 };
|
|
211556
|
+
if (tag7 === "link") {
|
|
211557
|
+
entry.rel = "stylesheet";
|
|
211558
|
+
entry.href = "";
|
|
211559
|
+
} else if (tag7 === "meta") {
|
|
211560
|
+
entry.name = "";
|
|
211561
|
+
entry.content = "";
|
|
211562
|
+
} else if (tag7 === "script") {
|
|
211563
|
+
entry.src = "";
|
|
211564
|
+
entry.body = "";
|
|
211565
|
+
} else if (tag7 === "style") {
|
|
211566
|
+
entry.body = "";
|
|
211567
|
+
}
|
|
211568
|
+
headEntries.push(entry);
|
|
211569
|
+
save();
|
|
211570
|
+
renderHeadEditor(container);
|
|
211571
|
+
};
|
|
211572
|
+
const removeEntry = (idx) => {
|
|
211573
|
+
headEntries.splice(idx, 1);
|
|
211574
|
+
save();
|
|
211575
|
+
renderHeadEditor(container);
|
|
211576
|
+
};
|
|
211577
|
+
const updateEntry = (idx, key, val) => {
|
|
211578
|
+
headEntries[idx][key] = val;
|
|
211579
|
+
save();
|
|
211580
|
+
};
|
|
211581
|
+
const tpl = html`
|
|
211582
|
+
<div class="settings-section">
|
|
211583
|
+
<h3 class="settings-section-title">Head</h3>
|
|
211584
|
+
<p class="settings-field-desc">
|
|
211585
|
+
Manage global <head> tags — stylesheets, meta tags, scripts, and inline styles.
|
|
211586
|
+
</p>
|
|
211587
|
+
|
|
211588
|
+
<div class="head-entries">
|
|
211589
|
+
${headEntries.map((entry, idx) => html`
|
|
211590
|
+
<div class="head-entry">
|
|
211591
|
+
<div class="head-entry-header">
|
|
211592
|
+
<span class="head-entry-tag"><${entry.tag}></span>
|
|
211593
|
+
<sp-action-button quiet size="s" @click=${() => removeEntry(idx)}>
|
|
211594
|
+
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
211595
|
+
</sp-action-button>
|
|
211596
|
+
</div>
|
|
211597
|
+
<div class="head-entry-fields">${renderEntryFields(entry, idx, updateEntry)}</div>
|
|
211598
|
+
</div>
|
|
211599
|
+
`)}
|
|
211600
|
+
</div>
|
|
211601
|
+
|
|
211602
|
+
<div class="head-add-actions" style="margin-top:12px;display:flex;gap:8px">
|
|
211603
|
+
<sp-action-button size="s" @click=${() => addEntry("link")}> + Link </sp-action-button>
|
|
211604
|
+
<sp-action-button size="s" @click=${() => addEntry("meta")}> + Meta </sp-action-button>
|
|
211605
|
+
<sp-action-button size="s" @click=${() => addEntry("script")}> + Script </sp-action-button>
|
|
211606
|
+
<sp-action-button size="s" @click=${() => addEntry("style")}> + Style </sp-action-button>
|
|
211607
|
+
</div>
|
|
211608
|
+
</div>
|
|
211609
|
+
`;
|
|
211610
|
+
render2(tpl, container);
|
|
211611
|
+
}
|
|
211612
|
+
function renderEntryFields(entry, idx, updateEntry) {
|
|
211613
|
+
let debounce;
|
|
211614
|
+
const onFieldChange = (key) => (e20) => {
|
|
211615
|
+
clearTimeout(debounce);
|
|
211616
|
+
debounce = setTimeout(() => {
|
|
211617
|
+
updateEntry(idx, key, e20.target.value);
|
|
211618
|
+
}, 300);
|
|
211619
|
+
};
|
|
211620
|
+
switch (entry.tag) {
|
|
211621
|
+
case "link":
|
|
211622
|
+
return html`
|
|
211623
|
+
<div class="settings-field-row">
|
|
211624
|
+
<sp-textfield
|
|
211625
|
+
size="s"
|
|
211626
|
+
label="rel"
|
|
211627
|
+
.value=${entry.rel || ""}
|
|
211628
|
+
@change=${onFieldChange("rel")}
|
|
211629
|
+
></sp-textfield>
|
|
211630
|
+
<sp-textfield
|
|
211631
|
+
size="s"
|
|
211632
|
+
label="href"
|
|
211633
|
+
.value=${entry.href || ""}
|
|
211634
|
+
@change=${onFieldChange("href")}
|
|
211635
|
+
style="flex:1"
|
|
211636
|
+
></sp-textfield>
|
|
211637
|
+
</div>
|
|
211638
|
+
`;
|
|
211639
|
+
case "meta":
|
|
211640
|
+
return html`
|
|
211641
|
+
<div class="settings-field-row">
|
|
211642
|
+
<sp-textfield
|
|
211643
|
+
size="s"
|
|
211644
|
+
label="name"
|
|
211645
|
+
.value=${entry.name || ""}
|
|
211646
|
+
@change=${onFieldChange("name")}
|
|
211647
|
+
></sp-textfield>
|
|
211648
|
+
<sp-textfield
|
|
211649
|
+
size="s"
|
|
211650
|
+
label="content"
|
|
211651
|
+
.value=${entry.content || ""}
|
|
211652
|
+
@change=${onFieldChange("content")}
|
|
211653
|
+
style="flex:1"
|
|
211654
|
+
></sp-textfield>
|
|
211655
|
+
</div>
|
|
211656
|
+
`;
|
|
211657
|
+
case "script":
|
|
211658
|
+
return html`
|
|
211659
|
+
<div class="settings-field-row">
|
|
211660
|
+
<sp-textfield
|
|
211661
|
+
size="s"
|
|
211662
|
+
label="src"
|
|
211663
|
+
.value=${entry.src || ""}
|
|
211664
|
+
@change=${onFieldChange("src")}
|
|
211665
|
+
placeholder="URL or leave empty for inline"
|
|
211666
|
+
style="flex:1"
|
|
211667
|
+
></sp-textfield>
|
|
211668
|
+
</div>
|
|
211669
|
+
${!entry.src ? html`
|
|
211670
|
+
<div class="head-entry-body">
|
|
211671
|
+
<label class="settings-field-label">Script body</label>
|
|
211672
|
+
<textarea
|
|
211673
|
+
class="head-code-editor"
|
|
211674
|
+
.value=${entry.body || ""}
|
|
211675
|
+
@input=${onFieldChange("body")}
|
|
211676
|
+
rows="6"
|
|
211677
|
+
spellcheck="false"
|
|
211678
|
+
></textarea>
|
|
211679
|
+
</div>
|
|
211680
|
+
` : nothing}
|
|
211681
|
+
`;
|
|
211682
|
+
case "style":
|
|
211683
|
+
return html`
|
|
211684
|
+
<div class="head-entry-body">
|
|
211685
|
+
<label class="settings-field-label">Style body</label>
|
|
211686
|
+
<textarea
|
|
211687
|
+
class="head-code-editor"
|
|
211688
|
+
.value=${entry.body || ""}
|
|
211689
|
+
@input=${onFieldChange("body")}
|
|
211690
|
+
rows="8"
|
|
211691
|
+
spellcheck="false"
|
|
211692
|
+
></textarea>
|
|
211693
|
+
</div>
|
|
211694
|
+
`;
|
|
211695
|
+
default:
|
|
211696
|
+
return nothing;
|
|
211697
|
+
}
|
|
211698
|
+
}
|
|
211699
|
+
|
|
211700
|
+
// src/settings/general-settings.js
|
|
211701
|
+
init_store();
|
|
211702
|
+
init_platform();
|
|
211703
|
+
function renderGeneralSettings(container) {
|
|
211704
|
+
const config = projectState.projectConfig || {};
|
|
211705
|
+
const onFaviconUpload = async () => {
|
|
211706
|
+
const input = document.createElement("input");
|
|
211707
|
+
input.type = "file";
|
|
211708
|
+
input.accept = "image/*,.ico,.svg";
|
|
211709
|
+
input.onchange = async () => {
|
|
211710
|
+
const file = input.files?.[0];
|
|
211711
|
+
if (!file)
|
|
211712
|
+
return;
|
|
211713
|
+
const platform4 = getPlatform();
|
|
211714
|
+
await platform4.uploadFile("public/favicon.ico", file);
|
|
211715
|
+
await updateSiteConfig({ favicon: "/favicon.ico" });
|
|
211716
|
+
renderGeneralSettings(container);
|
|
211717
|
+
};
|
|
211718
|
+
input.click();
|
|
211719
|
+
};
|
|
211720
|
+
const onAdapterChange = (e20) => {
|
|
211721
|
+
updateSiteConfig({ adapter: e20.target.value });
|
|
211722
|
+
};
|
|
211723
|
+
const onEditGlobalStyles = () => {
|
|
211724
|
+
closeSettingsModal();
|
|
211725
|
+
openFileInTab("project.json");
|
|
211726
|
+
};
|
|
211727
|
+
const currentFavicon = config.favicon;
|
|
211728
|
+
const tpl = html`
|
|
211729
|
+
<div class="settings-section">
|
|
211730
|
+
<h3 class="settings-section-title">General</h3>
|
|
211731
|
+
|
|
211732
|
+
<div class="settings-field">
|
|
211733
|
+
<label class="settings-field-label">Favicon</label>
|
|
211734
|
+
<p class="settings-field-desc">Upload an image to use as the site favicon.</p>
|
|
211735
|
+
<div style="display:flex;align-items:center;gap:12px">
|
|
211736
|
+
${currentFavicon ? html`<img
|
|
211737
|
+
src=${currentFavicon}
|
|
211738
|
+
alt="Current favicon"
|
|
211739
|
+
style="width:32px;height:32px;object-fit:contain;border:1px solid var(--border);border-radius:4px;padding:2px"
|
|
211740
|
+
/>` : html`<div
|
|
211741
|
+
style="width:32px;height:32px;border:1px dashed var(--border);border-radius:4px;display:flex;align-items:center;justify-content:center;color:var(--fg-dim);font-size:11px"
|
|
211742
|
+
>
|
|
211743
|
+
—
|
|
211744
|
+
</div>`}
|
|
211745
|
+
<sp-action-button size="s" @click=${onFaviconUpload}> Upload Favicon </sp-action-button>
|
|
211746
|
+
${currentFavicon ? html`<span style="font-size:11px;color:var(--fg-dim)">${currentFavicon}</span>` : nothing}
|
|
211747
|
+
</div>
|
|
211748
|
+
</div>
|
|
211749
|
+
|
|
211750
|
+
<div class="settings-field">
|
|
211751
|
+
<label class="settings-field-label">Platform Adapter</label>
|
|
211752
|
+
<p class="settings-field-desc">Build adapter for deployment target.</p>
|
|
211753
|
+
<sp-picker
|
|
211754
|
+
size="s"
|
|
211755
|
+
label="Platform Adapter"
|
|
211756
|
+
.value=${config.adapter || "static"}
|
|
211757
|
+
@change=${onAdapterChange}
|
|
211758
|
+
>
|
|
211759
|
+
<sp-menu-item value="static">Static</sp-menu-item>
|
|
211760
|
+
<sp-menu-item value="bun">Bun</sp-menu-item>
|
|
211761
|
+
<sp-menu-item value="node">Node</sp-menu-item>
|
|
211762
|
+
<sp-menu-item value="cloudflare-workers">Cloudflare Workers</sp-menu-item>
|
|
211763
|
+
<sp-menu-item value="cloudflare-pages">Cloudflare Pages</sp-menu-item>
|
|
211764
|
+
</sp-picker>
|
|
211765
|
+
</div>
|
|
211766
|
+
|
|
211767
|
+
<div class="settings-field">
|
|
211768
|
+
<label class="settings-field-label">Global Styles</label>
|
|
211769
|
+
<p class="settings-field-desc">Edit default element styles that apply across all pages.</p>
|
|
211770
|
+
<sp-action-button size="s" @click=${onEditGlobalStyles}>
|
|
211771
|
+
Edit Global Styles
|
|
211772
|
+
</sp-action-button>
|
|
211773
|
+
</div>
|
|
211774
|
+
</div>
|
|
211775
|
+
`;
|
|
211776
|
+
render2(tpl, container);
|
|
211777
|
+
}
|
|
211778
|
+
|
|
211779
|
+
// src/settings/settings-modal.js
|
|
211780
|
+
var _host = null;
|
|
211781
|
+
var _activeSection = "general";
|
|
211782
|
+
var sections = [
|
|
211783
|
+
{ key: "general", label: "General", icon: "sp-icon-properties" },
|
|
211784
|
+
{ key: "head", label: "Head", icon: "sp-icon-file-single-web-page" },
|
|
211785
|
+
{ key: "cssVars", label: "CSS Variables", icon: "sp-icon-brush" },
|
|
211786
|
+
{ key: "definitions", label: "Definitions", icon: "sp-icon-data" },
|
|
211787
|
+
{ key: "contentTypes", label: "Content Types", icon: "sp-icon-view-grid" }
|
|
211788
|
+
];
|
|
211789
|
+
function openSettingsModal() {
|
|
211790
|
+
if (_host)
|
|
211791
|
+
return;
|
|
211792
|
+
_host = document.createElement("div");
|
|
211793
|
+
_host.style.display = "contents";
|
|
211794
|
+
const themeRoot = document.querySelector("sp-theme") || document.body;
|
|
211795
|
+
themeRoot.appendChild(_host);
|
|
211796
|
+
_activeSection = "general";
|
|
211797
|
+
renderModal();
|
|
211798
|
+
}
|
|
211799
|
+
function closeSettingsModal() {
|
|
211800
|
+
if (!_host)
|
|
211801
|
+
return;
|
|
211802
|
+
_host.remove();
|
|
211803
|
+
_host = null;
|
|
211804
|
+
}
|
|
211805
|
+
function renderModal() {
|
|
211806
|
+
if (!_host)
|
|
211807
|
+
return;
|
|
211808
|
+
const onNavClick = (key) => {
|
|
211809
|
+
_activeSection = key;
|
|
211810
|
+
renderModal();
|
|
211811
|
+
renderActiveSection();
|
|
211812
|
+
};
|
|
211813
|
+
const tpl = html`
|
|
211814
|
+
<sp-underlay open @close=${closeSettingsModal}></sp-underlay>
|
|
211815
|
+
<div
|
|
211816
|
+
class="settings-modal"
|
|
211817
|
+
@keydown=${(e20) => {
|
|
211818
|
+
if (e20.key === "Escape")
|
|
211819
|
+
closeSettingsModal();
|
|
211820
|
+
}}
|
|
211821
|
+
>
|
|
211822
|
+
<div class="settings-modal-header">
|
|
211823
|
+
<h2 class="settings-modal-title">Settings</h2>
|
|
211824
|
+
<sp-action-button quiet size="s" @click=${closeSettingsModal} title="Close">
|
|
211825
|
+
<sp-icon-close slot="icon"></sp-icon-close>
|
|
211826
|
+
</sp-action-button>
|
|
211827
|
+
</div>
|
|
211828
|
+
<div class="settings-modal-body">
|
|
211829
|
+
<nav class="settings-modal-nav">
|
|
211830
|
+
${sections.map((s2) => html`
|
|
211831
|
+
<button
|
|
211832
|
+
class="settings-nav-item${_activeSection === s2.key ? " active" : ""}"
|
|
211833
|
+
@click=${() => onNavClick(s2.key)}
|
|
211834
|
+
>
|
|
211835
|
+
${s2.label}
|
|
211836
|
+
</button>
|
|
211837
|
+
`)}
|
|
211838
|
+
</nav>
|
|
211839
|
+
<div
|
|
211840
|
+
class="settings-modal-content"
|
|
211841
|
+
${ref2((el) => {
|
|
211842
|
+
if (el)
|
|
211843
|
+
requestAnimationFrame(() => renderActiveSection());
|
|
211844
|
+
})}
|
|
211845
|
+
></div>
|
|
211846
|
+
</div>
|
|
211847
|
+
</div>
|
|
211848
|
+
`;
|
|
211849
|
+
render2(tpl, _host);
|
|
211850
|
+
}
|
|
211851
|
+
function renderActiveSection() {
|
|
211852
|
+
if (!_host)
|
|
211853
|
+
return;
|
|
211854
|
+
const container = _host.querySelector(".settings-modal-content");
|
|
211855
|
+
if (!container)
|
|
211856
|
+
return;
|
|
211857
|
+
switch (_activeSection) {
|
|
211858
|
+
case "general":
|
|
211859
|
+
renderGeneralSettings(container);
|
|
211860
|
+
break;
|
|
211861
|
+
case "head":
|
|
211862
|
+
renderHeadEditor(container);
|
|
211863
|
+
break;
|
|
211864
|
+
case "cssVars":
|
|
211865
|
+
renderCssVarsEditor(container);
|
|
211866
|
+
break;
|
|
211867
|
+
case "definitions":
|
|
211868
|
+
renderDefsEditor(container);
|
|
211869
|
+
break;
|
|
211870
|
+
case "contentTypes":
|
|
211871
|
+
renderContentTypesEditor(container);
|
|
211872
|
+
break;
|
|
211873
|
+
}
|
|
211874
|
+
}
|
|
211875
|
+
|
|
211876
|
+
// src/panels/activity-bar.js
|
|
211727
211877
|
var _scope3 = null;
|
|
211728
211878
|
function mount4() {
|
|
211729
211879
|
_scope3 = effectScope();
|
|
@@ -211805,6 +211955,17 @@ function renderActivityBar() {
|
|
|
211805
211955
|
</sp-tab>
|
|
211806
211956
|
`)}
|
|
211807
211957
|
</sp-tabs>
|
|
211958
|
+
<div style="margin-top:auto;padding:8px 0;display:flex;justify-content:center">
|
|
211959
|
+
<sp-action-button
|
|
211960
|
+
quiet
|
|
211961
|
+
size="m"
|
|
211962
|
+
title="Settings"
|
|
211963
|
+
aria-label="Settings"
|
|
211964
|
+
@click=${() => openSettingsModal()}
|
|
211965
|
+
>
|
|
211966
|
+
<sp-icon-gears slot="icon"></sp-icon-gears>
|
|
211967
|
+
</sp-action-button>
|
|
211968
|
+
</div>
|
|
211808
211969
|
`;
|
|
211809
211970
|
render2(tpl, activityBar);
|
|
211810
211971
|
}
|
|
@@ -211947,17 +212108,22 @@ function toolbarTemplate() {
|
|
|
211947
212108
|
{ key: "design", label: "Design", iconTag: "sp-icon-artboard" },
|
|
211948
212109
|
{ key: "preview", label: "Preview", iconTag: "sp-icon-preview" },
|
|
211949
212110
|
{ key: "source", label: "Code", iconTag: "sp-icon-code" },
|
|
211950
|
-
{ key: "
|
|
212111
|
+
{ key: "stylebook", label: "Stylebook", iconTag: "sp-icon-brush" }
|
|
211951
212112
|
];
|
|
212113
|
+
const isProjectFile = S.documentPath === "project.json";
|
|
212114
|
+
const allowedModes = isProjectFile ? new Set(["stylebook", "source"]) : null;
|
|
211952
212115
|
const modeSwitcherTpl = html`
|
|
211953
212116
|
<sp-action-group selects="single" size="s" compact>
|
|
211954
212117
|
${modes.map((m3) => html`
|
|
211955
212118
|
<sp-action-button
|
|
211956
212119
|
size="s"
|
|
211957
212120
|
?selected=${canvasMode === m3.key}
|
|
212121
|
+
?disabled=${allowedModes && !allowedModes.has(m3.key)}
|
|
211958
212122
|
@click=${() => {
|
|
211959
212123
|
if (canvasMode === m3.key)
|
|
211960
212124
|
return;
|
|
212125
|
+
if (allowedModes && !allowedModes.has(m3.key))
|
|
212126
|
+
return;
|
|
211961
212127
|
if (S.ui.editingFunction) {
|
|
211962
212128
|
if (view.functionEditor) {
|
|
211963
212129
|
view.functionEditor.dispose();
|
|
@@ -211968,7 +212134,7 @@ function toolbarTemplate() {
|
|
|
211968
212134
|
view.panX = 0;
|
|
211969
212135
|
view.panY = 0;
|
|
211970
212136
|
const uiPatch = { editingFunction: null };
|
|
211971
|
-
if (m3.key === "
|
|
212137
|
+
if (m3.key === "stylebook")
|
|
211972
212138
|
uiPatch.rightTab = "style";
|
|
211973
212139
|
if (m3.key === "manage")
|
|
211974
212140
|
view.leftTab = "files";
|
|
@@ -214777,7 +214943,7 @@ function renderStylePanelTemplate(ctx) {
|
|
|
214777
214943
|
const tab = activeTab.value;
|
|
214778
214944
|
if (!tab)
|
|
214779
214945
|
return html`<div class="empty-state">No document loaded</div>`;
|
|
214780
|
-
if (ctx.getCanvasMode() === "
|
|
214946
|
+
if (ctx.getCanvasMode() === "stylebook" && tab.session.ui.stylebookSelection) {
|
|
214781
214947
|
const node3 = tab.doc.document;
|
|
214782
214948
|
if (!node3)
|
|
214783
214949
|
return html`<div class="empty-state">No document loaded</div>`;
|
|
@@ -217035,7 +217201,7 @@ function _render() {
|
|
|
217035
217201
|
const tab = view.leftTab;
|
|
217036
217202
|
let content3;
|
|
217037
217203
|
if (tab === "layers")
|
|
217038
|
-
content3 = ctx.getCanvasMode() === "
|
|
217204
|
+
content3 = ctx.getCanvasMode() === "stylebook" ? renderStylebookLayersTemplate({
|
|
217039
217205
|
selectStylebookTag,
|
|
217040
217206
|
stylebookMeta: stylebook_meta_default
|
|
217041
217207
|
}) : renderLayersTemplate({
|
|
@@ -217097,7 +217263,7 @@ function _render() {
|
|
|
217097
217263
|
else
|
|
217098
217264
|
content3 = nothing;
|
|
217099
217265
|
render2(html`<div class="panel-body">${content3}</div>`, leftPanel);
|
|
217100
|
-
if (tab === "layers" && ctx.getCanvasMode() !== "
|
|
217266
|
+
if (tab === "layers" && ctx.getCanvasMode() !== "stylebook")
|
|
217101
217267
|
ctx.registerLayersDnD();
|
|
217102
217268
|
else if (tab === "imports") {} else if (tab === "blocks") {
|
|
217103
217269
|
ctx.registerElementsDnD();
|
|
@@ -217112,10 +217278,10 @@ function _render() {
|
|
|
217112
217278
|
// src/panels/tab-strip.js
|
|
217113
217279
|
init_reactivity();
|
|
217114
217280
|
init_workspace();
|
|
217115
|
-
var
|
|
217281
|
+
var _host2 = null;
|
|
217116
217282
|
var _scope7 = null;
|
|
217117
217283
|
function mount8(host2) {
|
|
217118
|
-
|
|
217284
|
+
_host2 = host2;
|
|
217119
217285
|
_scope7 = effectScope();
|
|
217120
217286
|
_scope7.run(() => {
|
|
217121
217287
|
effect(() => {
|
|
@@ -217130,10 +217296,10 @@ function mount8(host2) {
|
|
|
217130
217296
|
});
|
|
217131
217297
|
}
|
|
217132
217298
|
function render10() {
|
|
217133
|
-
if (!
|
|
217299
|
+
if (!_host2)
|
|
217134
217300
|
return;
|
|
217135
217301
|
if (workspace.tabOrder.length <= 1) {
|
|
217136
|
-
render2(nothing,
|
|
217302
|
+
render2(nothing, _host2);
|
|
217137
217303
|
return;
|
|
217138
217304
|
}
|
|
217139
217305
|
render2(html`
|
|
@@ -217172,7 +217338,7 @@ function render10() {
|
|
|
217172
217338
|
`;
|
|
217173
217339
|
})}
|
|
217174
217340
|
</div>
|
|
217175
|
-
`,
|
|
217341
|
+
`, _host2);
|
|
217176
217342
|
}
|
|
217177
217343
|
function tabLabel(tab) {
|
|
217178
217344
|
const path2 = tab.documentPath;
|
|
@@ -217658,5 +217824,5 @@ effect(() => {
|
|
|
217658
217824
|
scheduleAutosave();
|
|
217659
217825
|
});
|
|
217660
217826
|
|
|
217661
|
-
//# debugId=
|
|
217827
|
+
//# debugId=2C5B0A9299ACFF5964756E2164756E21
|
|
217662
217828
|
//# sourceMappingURL=studio.js.map
|