@jxsuite/studio 0.11.0 → 0.13.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 +4248 -3412
- package/dist/studio.js.map +49 -43
- package/package.json +5 -3
- package/src/canvas/canvas-diff.js +184 -0
- package/src/canvas/canvas-helpers.js +10 -14
- package/src/canvas/canvas-live-render.js +3 -2
- package/src/canvas/canvas-render.js +152 -20
- package/src/canvas/canvas-utils.js +21 -23
- package/src/editor/component-inline-edit.js +54 -41
- package/src/editor/content-inline-edit.js +46 -47
- package/src/editor/context-menu.js +63 -39
- package/src/editor/convert-to-component.js +11 -14
- package/src/editor/insertion-helper.js +8 -10
- package/src/editor/shortcuts.js +69 -39
- package/src/files/components.js +15 -4
- package/src/files/files.js +72 -24
- package/src/panels/activity-bar.js +29 -7
- package/src/panels/block-action-bar.js +104 -80
- package/src/panels/dnd.js +32 -28
- package/src/panels/editors.js +7 -14
- package/src/panels/elements-panel.js +9 -3
- package/src/panels/events-panel.js +44 -39
- package/src/panels/git-panel.js +45 -3
- package/src/panels/layers-panel.js +16 -11
- package/src/panels/left-panel.js +108 -43
- package/src/panels/overlays.js +91 -41
- package/src/panels/panel-events.js +9 -12
- package/src/panels/properties-panel.js +179 -104
- package/src/panels/right-panel.js +85 -37
- package/src/panels/shared.js +0 -22
- package/src/panels/signals-panel.js +125 -54
- package/src/panels/statusbar.js +23 -8
- package/src/panels/style-inputs.js +4 -2
- package/src/panels/style-panel.js +128 -105
- package/src/panels/stylebook-panel.js +11 -15
- package/src/panels/tab-strip.js +124 -0
- package/src/panels/toolbar.js +39 -9
- package/src/reactivity.js +28 -0
- package/src/settings/content-types-editor.js +56 -7
- package/src/settings/defs-editor.js +56 -7
- package/src/settings/schema-field-ui.js +60 -25
- package/src/state.js +0 -456
- package/src/store.js +97 -124
- package/src/studio.js +112 -121
- package/src/tabs/tab.js +153 -0
- package/src/tabs/transact.js +406 -0
- package/src/workspace/workspace.js +89 -0
|
@@ -2,24 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
import { html, nothing } from "lit-html";
|
|
4
4
|
import { live } from "lit-html/directives/live.js";
|
|
5
|
+
import { getNodeAtPath, debouncedStyleCommit, renderOnly, projectState } from "../store.js";
|
|
5
6
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
renameSwitchCase,
|
|
18
|
-
debouncedStyleCommit,
|
|
19
|
-
renderOnly,
|
|
20
|
-
updateUi,
|
|
21
|
-
projectState,
|
|
22
|
-
} from "../store.js";
|
|
7
|
+
transactDoc,
|
|
8
|
+
mutateUpdateProperty,
|
|
9
|
+
mutateUpdateAttribute,
|
|
10
|
+
mutateUpdateProp,
|
|
11
|
+
mutateUpdateFrontmatter,
|
|
12
|
+
mutateUpdateMedia,
|
|
13
|
+
mutateAddSwitchCase,
|
|
14
|
+
mutateRemoveSwitchCase,
|
|
15
|
+
mutateRenameSwitchCase,
|
|
16
|
+
} from "../tabs/transact.js";
|
|
17
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
23
18
|
import { view } from "../view.js";
|
|
24
19
|
import { componentRegistry } from "../files/components.js";
|
|
25
20
|
import { widgetForType } from "./style-inputs.js";
|
|
@@ -36,6 +31,7 @@ import { isCustomElementDoc, collectCssParts } from "./signals-panel.js";
|
|
|
36
31
|
import { mediaDisplayName } from "./shared.js";
|
|
37
32
|
import { getCssInitialMap } from "./style-utils.js";
|
|
38
33
|
import { renderMediaPicker } from "../ui/media-picker.js";
|
|
34
|
+
import { renderColorSelector } from "../ui/color-selector.js";
|
|
39
35
|
import { getEffectiveLayoutPath, invalidateLayoutCache } from "../site-context.js";
|
|
40
36
|
import { getPlatform } from "../platform.js";
|
|
41
37
|
import htmlMeta from "../../data/html-meta.json";
|
|
@@ -70,8 +66,8 @@ function bindableFieldRow(
|
|
|
70
66
|
/** @type {any} */ filterFn = null,
|
|
71
67
|
/** @type {any} */ extraSignals = null,
|
|
72
68
|
) {
|
|
73
|
-
const
|
|
74
|
-
const defs =
|
|
69
|
+
const tab = activeTab.value;
|
|
70
|
+
const defs = tab.doc.document.state || {};
|
|
75
71
|
const isBound = typeof rawValue === "object" && rawValue !== null && rawValue.$ref;
|
|
76
72
|
|
|
77
73
|
const signalDefs = Object.entries(defs).filter(([, d]) =>
|
|
@@ -211,9 +207,9 @@ function kvRow(
|
|
|
211
207
|
|
|
212
208
|
/** Frontmatter-only panel shown in content mode when no element is selected */
|
|
213
209
|
function renderFrontmatterOnlyPanel() {
|
|
214
|
-
const
|
|
215
|
-
const fm =
|
|
216
|
-
const col = findContentTypeSchema(
|
|
210
|
+
const tab = activeTab.value;
|
|
211
|
+
const fm = tab.doc.content?.frontmatter || {};
|
|
212
|
+
const col = findContentTypeSchema(tab.documentPath, projectState?.projectConfig);
|
|
217
213
|
const schemaProps = col?.schema?.properties;
|
|
218
214
|
const requiredFields = new Set(col?.schema?.required || []);
|
|
219
215
|
|
|
@@ -248,7 +244,7 @@ function renderFrontmatterOnlyPanel() {
|
|
|
248
244
|
return html`<div class="empty-state">No frontmatter. Select an element to inspect.</div>`;
|
|
249
245
|
}
|
|
250
246
|
|
|
251
|
-
const pageT = renderPageSection(
|
|
247
|
+
const pageT = renderPageSection(tab.doc.document || {});
|
|
252
248
|
|
|
253
249
|
return html`
|
|
254
250
|
<div class="style-sidebar">
|
|
@@ -271,12 +267,12 @@ function renderFmFieldRow(
|
|
|
271
267
|
/** @type {any} */ value,
|
|
272
268
|
/** @type {Set<string>} */ requiredFields,
|
|
273
269
|
) {
|
|
274
|
-
const S = getState();
|
|
275
270
|
const isRequired = requiredFields.has(field);
|
|
276
271
|
const label = field.replace(/([A-Z])/g, " $1").replace(/^./, (s) => s.toUpperCase());
|
|
277
272
|
const displayLabel = label + (isRequired ? " *" : "");
|
|
278
273
|
const hasVal = value !== undefined && value !== "" && value !== false;
|
|
279
|
-
const onClear = () =>
|
|
274
|
+
const onClear = () =>
|
|
275
|
+
transactDoc(activeTab.value, (t) => mutateUpdateFrontmatter(t, field, undefined));
|
|
280
276
|
|
|
281
277
|
if (entry.type === "boolean") {
|
|
282
278
|
return renderFieldRow({
|
|
@@ -289,7 +285,9 @@ function renderFmFieldRow(
|
|
|
289
285
|
size="s"
|
|
290
286
|
.checked=${live(!!value)}
|
|
291
287
|
@change=${(/** @type {any} */ e) =>
|
|
292
|
-
|
|
288
|
+
transactDoc(activeTab.value, (t) =>
|
|
289
|
+
mutateUpdateFrontmatter(t, field, e.target.checked || undefined),
|
|
290
|
+
)}
|
|
293
291
|
></sp-checkbox>
|
|
294
292
|
`,
|
|
295
293
|
});
|
|
@@ -314,7 +312,7 @@ function renderFmFieldRow(
|
|
|
314
312
|
.map((/** @type {string} */ s) => s.trim())
|
|
315
313
|
.filter(Boolean)
|
|
316
314
|
: undefined;
|
|
317
|
-
|
|
315
|
+
transactDoc(activeTab.value, (t) => mutateUpdateFrontmatter(t, field, arr));
|
|
318
316
|
})}
|
|
319
317
|
></sp-textfield>
|
|
320
318
|
`,
|
|
@@ -332,7 +330,9 @@ function renderFmFieldRow(
|
|
|
332
330
|
size="s"
|
|
333
331
|
.value=${live(value || "")}
|
|
334
332
|
@change=${(/** @type {any} */ e) =>
|
|
335
|
-
|
|
333
|
+
transactDoc(activeTab.value, (t) =>
|
|
334
|
+
mutateUpdateFrontmatter(t, field, e.target.value || undefined),
|
|
335
|
+
)}
|
|
336
336
|
>
|
|
337
337
|
${entry.enum.map(
|
|
338
338
|
(/** @type {string} */ opt) => html`<sp-menu-item value=${opt}>${opt}</sp-menu-item>`,
|
|
@@ -349,7 +349,7 @@ function renderFmFieldRow(
|
|
|
349
349
|
hasValue: hasVal,
|
|
350
350
|
onClear,
|
|
351
351
|
widget: renderMediaPicker(field, value, (v) =>
|
|
352
|
-
|
|
352
|
+
transactDoc(activeTab.value, (t) => mutateUpdateFrontmatter(t, field, v || undefined)),
|
|
353
353
|
),
|
|
354
354
|
});
|
|
355
355
|
}
|
|
@@ -374,7 +374,9 @@ function renderFmFieldRow(
|
|
|
374
374
|
title="Remove"
|
|
375
375
|
@click=${() => {
|
|
376
376
|
const next = images.filter((_, idx) => idx !== i);
|
|
377
|
-
|
|
377
|
+
transactDoc(activeTab.value, (t) =>
|
|
378
|
+
mutateUpdateFrontmatter(t, field, next.length ? next : undefined),
|
|
379
|
+
);
|
|
378
380
|
}}
|
|
379
381
|
>
|
|
380
382
|
<sp-icon-close slot="icon"></sp-icon-close>
|
|
@@ -386,7 +388,7 @@ function renderFmFieldRow(
|
|
|
386
388
|
${renderMediaPicker(`${field}:add`, "", (v) => {
|
|
387
389
|
if (!v) return;
|
|
388
390
|
const next = [...images, v];
|
|
389
|
-
|
|
391
|
+
transactDoc(activeTab.value, (t) => mutateUpdateFrontmatter(t, field, next));
|
|
390
392
|
})}
|
|
391
393
|
</div>
|
|
392
394
|
`,
|
|
@@ -397,7 +399,7 @@ function renderFmFieldRow(
|
|
|
397
399
|
const targetName = entry.$ref.replace("#/contentTypes/", "");
|
|
398
400
|
const targetDef = projectState?.projectConfig?.contentTypes?.[targetName];
|
|
399
401
|
const picker = renderReferencePicker(field, value, targetName, targetDef, (v) =>
|
|
400
|
-
|
|
402
|
+
transactDoc(activeTab.value, (t) => mutateUpdateFrontmatter(t, field, v || undefined)),
|
|
401
403
|
);
|
|
402
404
|
return renderFieldRow({
|
|
403
405
|
prop: field,
|
|
@@ -421,7 +423,9 @@ function renderFmFieldRow(
|
|
|
421
423
|
.value=${live(value !== undefined ? Number(value) : undefined)}
|
|
422
424
|
@change=${debouncedStyleCommit(`fm:${field}`, 400, (/** @type {any} */ e) => {
|
|
423
425
|
const v = e.target.value;
|
|
424
|
-
|
|
426
|
+
transactDoc(activeTab.value, (t) =>
|
|
427
|
+
mutateUpdateFrontmatter(t, field, isNaN(v) ? undefined : Number(v)),
|
|
428
|
+
);
|
|
425
429
|
})}
|
|
426
430
|
></sp-number-field>
|
|
427
431
|
`,
|
|
@@ -439,7 +443,9 @@ function renderFmFieldRow(
|
|
|
439
443
|
placeholder=${entry.format === "date" ? "YYYY-MM-DD" : ""}
|
|
440
444
|
.value=${live(value || "")}
|
|
441
445
|
@input=${debouncedStyleCommit(`fm:${field}`, 400, (/** @type {any} */ e) => {
|
|
442
|
-
|
|
446
|
+
transactDoc(activeTab.value, (t) =>
|
|
447
|
+
mutateUpdateFrontmatter(t, field, e.target.value || undefined),
|
|
448
|
+
);
|
|
443
449
|
})}
|
|
444
450
|
></sp-textfield>
|
|
445
451
|
`,
|
|
@@ -526,33 +532,42 @@ function renderRepeaterFieldsTemplate(
|
|
|
526
532
|
/** @type {any} */ path,
|
|
527
533
|
/** @type {any} */ _mapSignals,
|
|
528
534
|
) {
|
|
529
|
-
const S = getState();
|
|
530
535
|
return html`
|
|
531
536
|
${bindableFieldRow("Items", "text", node.items, (/** @type {any} */ v) =>
|
|
532
|
-
|
|
537
|
+
transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, path, "items", v)),
|
|
533
538
|
)}
|
|
534
539
|
${node.filter
|
|
535
540
|
? bindableFieldRow("Filter", "text", node.filter, (/** @type {any} */ v) =>
|
|
536
|
-
|
|
541
|
+
transactDoc(activeTab.value, (t) =>
|
|
542
|
+
mutateUpdateProperty(t, path, "filter", v || undefined),
|
|
543
|
+
),
|
|
537
544
|
)
|
|
538
545
|
: nothing}
|
|
539
546
|
${node.sort
|
|
540
547
|
? bindableFieldRow("Sort", "text", node.sort, (/** @type {any} */ v) =>
|
|
541
|
-
|
|
548
|
+
transactDoc(activeTab.value, (t) =>
|
|
549
|
+
mutateUpdateProperty(t, path, "sort", v || undefined),
|
|
550
|
+
),
|
|
542
551
|
)
|
|
543
552
|
: nothing}
|
|
544
553
|
<div style="display:flex;gap:8px;margin-top:4px">
|
|
545
554
|
${!node.filter
|
|
546
555
|
? html`<span
|
|
547
556
|
class="kv-add"
|
|
548
|
-
@click=${() =>
|
|
557
|
+
@click=${() =>
|
|
558
|
+
transactDoc(activeTab.value, (t) =>
|
|
559
|
+
mutateUpdateProperty(t, path, "filter", { $ref: "#/state/" }),
|
|
560
|
+
)}
|
|
549
561
|
>+ Add filter</span
|
|
550
562
|
>`
|
|
551
563
|
: nothing}
|
|
552
564
|
${!node.sort
|
|
553
565
|
? html`<span
|
|
554
566
|
class="kv-add"
|
|
555
|
-
@click=${() =>
|
|
567
|
+
@click=${() =>
|
|
568
|
+
transactDoc(activeTab.value, (t) =>
|
|
569
|
+
mutateUpdateProperty(t, path, "sort", { $ref: "#/state/" }),
|
|
570
|
+
)}
|
|
556
571
|
>+ Add sort</span
|
|
557
572
|
>`
|
|
558
573
|
: nothing}
|
|
@@ -562,7 +577,9 @@ function renderRepeaterFieldsTemplate(
|
|
|
562
577
|
<sp-action-button
|
|
563
578
|
size="s"
|
|
564
579
|
style="margin-top:8px;width:100%"
|
|
565
|
-
@click=${() =>
|
|
580
|
+
@click=${() => {
|
|
581
|
+
activeTab.value.session.selection = [...path, "map"];
|
|
582
|
+
}}
|
|
566
583
|
>Edit template →</sp-action-button
|
|
567
584
|
>
|
|
568
585
|
`
|
|
@@ -582,7 +599,8 @@ function renderSwitchFieldsTemplate(
|
|
|
582
599
|
"Expression",
|
|
583
600
|
"text",
|
|
584
601
|
node.$switch,
|
|
585
|
-
(/** @type {any} */ v) =>
|
|
602
|
+
(/** @type {any} */ v) =>
|
|
603
|
+
transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, path, "$switch", v)),
|
|
586
604
|
null,
|
|
587
605
|
mapSignals,
|
|
588
606
|
)}
|
|
@@ -604,7 +622,9 @@ function renderSwitchFieldsTemplate(
|
|
|
604
622
|
clearTimeout(debounce);
|
|
605
623
|
debounce = setTimeout(() => {
|
|
606
624
|
if (e.target.value && e.target.value !== caseName)
|
|
607
|
-
|
|
625
|
+
transactDoc(activeTab.value, (t) =>
|
|
626
|
+
mutateRenameSwitchCase(t, path, caseName, e.target.value),
|
|
627
|
+
);
|
|
608
628
|
}, 500);
|
|
609
629
|
}}
|
|
610
630
|
/>
|
|
@@ -614,7 +634,7 @@ function renderSwitchFieldsTemplate(
|
|
|
614
634
|
style="cursor:pointer"
|
|
615
635
|
@click=${(/** @type {any} */ e) => {
|
|
616
636
|
e.stopPropagation();
|
|
617
|
-
|
|
637
|
+
activeTab.value.session.selection = [...path, "cases", caseName];
|
|
618
638
|
}}
|
|
619
639
|
>→</span
|
|
620
640
|
>
|
|
@@ -622,7 +642,7 @@ function renderSwitchFieldsTemplate(
|
|
|
622
642
|
style="cursor:pointer;color:var(--danger);font-size:11px"
|
|
623
643
|
@click=${(/** @type {any} */ e) => {
|
|
624
644
|
e.stopPropagation();
|
|
625
|
-
|
|
645
|
+
transactDoc(activeTab.value, (t) => mutateRemoveSwitchCase(t, path, caseName));
|
|
626
646
|
}}
|
|
627
647
|
>✕</span
|
|
628
648
|
>
|
|
@@ -632,7 +652,9 @@ function renderSwitchFieldsTemplate(
|
|
|
632
652
|
<span
|
|
633
653
|
class="kv-add"
|
|
634
654
|
@click=${() => {
|
|
635
|
-
|
|
655
|
+
transactDoc(activeTab.value, (t) =>
|
|
656
|
+
mutateAddSwitchCase(t, path, `case${caseNames.length + 1}`),
|
|
657
|
+
);
|
|
636
658
|
}}
|
|
637
659
|
>+ Add case</span
|
|
638
660
|
>
|
|
@@ -646,18 +668,20 @@ function renderComponentPropsFieldsTemplate(
|
|
|
646
668
|
/** @type {any} */ mapSignals,
|
|
647
669
|
/** @type {(path: string) => void} */ navigateToComponent,
|
|
648
670
|
) {
|
|
649
|
-
const
|
|
671
|
+
const tab = activeTab.value;
|
|
650
672
|
const comp = componentRegistry.find((c) => c.tagName === node.tagName);
|
|
651
|
-
if (!comp) return html`<div class="empty-state">Component not found</div>`;
|
|
673
|
+
if (!comp || !comp.props) return html`<div class="empty-state">Component not found</div>`;
|
|
652
674
|
const isNpm = comp.source === "npm";
|
|
653
675
|
const currentVals = isNpm ? node.attributes || {} : node.$props || {};
|
|
654
676
|
const updateFn = isNpm
|
|
655
677
|
? (/** @type {string} */ name, /** @type {any} */ v) =>
|
|
656
|
-
|
|
678
|
+
transactDoc(activeTab.value, (t) =>
|
|
679
|
+
mutateUpdateAttribute(t, path, name, v === "" ? undefined : v),
|
|
680
|
+
)
|
|
657
681
|
: (/** @type {string} */ name, /** @type {any} */ v) =>
|
|
658
|
-
|
|
682
|
+
transactDoc(activeTab.value, (t) => mutateUpdateProp(t, path, name, v));
|
|
659
683
|
|
|
660
|
-
const defs =
|
|
684
|
+
const defs = tab.doc.document.state || {};
|
|
661
685
|
const signalDefs = Object.entries(defs).filter(
|
|
662
686
|
([, d]) => !d.$handler && d.$prototype !== "Function",
|
|
663
687
|
);
|
|
@@ -727,7 +751,21 @@ function renderComponentPropsFieldsTemplate(
|
|
|
727
751
|
const staticVal = isBound ? "" : (rawValue ?? "");
|
|
728
752
|
/** @type {any} */
|
|
729
753
|
let widgetTpl;
|
|
730
|
-
if (
|
|
754
|
+
if (prop.format === "image") {
|
|
755
|
+
widgetTpl = renderMediaPicker(prop.name, staticVal, onChange);
|
|
756
|
+
} else if (prop.format === "color") {
|
|
757
|
+
widgetTpl = renderColorSelector(prop.name, staticVal, onChange);
|
|
758
|
+
} else if (prop.format === "date") {
|
|
759
|
+
widgetTpl = html`<sp-textfield
|
|
760
|
+
size="s"
|
|
761
|
+
placeholder="YYYY-MM-DD"
|
|
762
|
+
value=${staticVal}
|
|
763
|
+
@input=${(/** @type {any} */ e) => {
|
|
764
|
+
clearTimeout(debounce);
|
|
765
|
+
debounce = setTimeout(() => onChange(e.target.value), 400);
|
|
766
|
+
}}
|
|
767
|
+
></sp-textfield>`;
|
|
768
|
+
} else if (parsed.kind === "boolean") {
|
|
731
769
|
widgetTpl = html`<sp-checkbox
|
|
732
770
|
size="s"
|
|
733
771
|
.checked=${live(!!staticVal)}
|
|
@@ -806,19 +844,22 @@ function renderCustomAttrsFieldsTemplate(
|
|
|
806
844
|
attr,
|
|
807
845
|
String(val),
|
|
808
846
|
(/** @type {any} */ newAttr, /** @type {any} */ newVal) => {
|
|
809
|
-
const S = getState();
|
|
810
847
|
if (newAttr !== attr) {
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
848
|
+
transactDoc(activeTab.value, (t) => {
|
|
849
|
+
mutateUpdateAttribute(t, path, attr, undefined);
|
|
850
|
+
mutateUpdateAttribute(t, path, newAttr, newVal);
|
|
851
|
+
});
|
|
814
852
|
} else {
|
|
815
|
-
|
|
853
|
+
transactDoc(activeTab.value, (t) => mutateUpdateAttribute(t, path, attr, newVal));
|
|
816
854
|
}
|
|
817
855
|
},
|
|
818
|
-
() =>
|
|
856
|
+
() => transactDoc(activeTab.value, (t) => mutateUpdateAttribute(t, path, attr, undefined)),
|
|
819
857
|
),
|
|
820
858
|
)}
|
|
821
|
-
<span
|
|
859
|
+
<span
|
|
860
|
+
class="kv-add"
|
|
861
|
+
@click=${() =>
|
|
862
|
+
transactDoc(activeTab.value, (t) => mutateUpdateAttribute(t, path, "data-", ""))}
|
|
822
863
|
>+ Add attribute</span
|
|
823
864
|
>
|
|
824
865
|
`;
|
|
@@ -845,12 +886,15 @@ function renderMediaFieldsTemplate(/** @type {any} */ node) {
|
|
|
845
886
|
clearTimeout(baseDebounce);
|
|
846
887
|
baseDebounce = setTimeout(() => {
|
|
847
888
|
const val = e.target.value.trim();
|
|
848
|
-
|
|
889
|
+
transactDoc(activeTab.value, (t) => mutateUpdateMedia(t, "--", val || undefined));
|
|
849
890
|
}, 400);
|
|
850
891
|
}}
|
|
851
892
|
/>
|
|
852
893
|
${media["--"]
|
|
853
|
-
? html`<span
|
|
894
|
+
? html`<span
|
|
895
|
+
class="kv-del"
|
|
896
|
+
@click=${() =>
|
|
897
|
+
transactDoc(activeTab.value, (t) => mutateUpdateMedia(t, "--", undefined))}
|
|
854
898
|
>✕</span
|
|
855
899
|
>`
|
|
856
900
|
: nothing}
|
|
@@ -901,7 +945,7 @@ function renderMediaFieldsTemplate(/** @type {any} */ node) {
|
|
|
901
945
|
if (key && queryVal) {
|
|
902
946
|
view.showAddBreakpointForm = false;
|
|
903
947
|
view.addBreakpointPreview = "";
|
|
904
|
-
|
|
948
|
+
transactDoc(activeTab.value, (t) => mutateUpdateMedia(t, key, queryVal));
|
|
905
949
|
}
|
|
906
950
|
}}
|
|
907
951
|
>
|
|
@@ -949,10 +993,10 @@ function mediaBreakpointRowTemplate(/** @type {any} */ name, /** @type {any} */
|
|
|
949
993
|
const queryEl = e.target
|
|
950
994
|
.closest("div[style]")
|
|
951
995
|
?.parentElement?.querySelector(".bp-query-input");
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
996
|
+
transactDoc(activeTab.value, (t) => {
|
|
997
|
+
mutateUpdateMedia(t, name, undefined);
|
|
998
|
+
mutateUpdateMedia(t, newKey, queryEl?.value || query);
|
|
999
|
+
});
|
|
956
1000
|
}
|
|
957
1001
|
}, 600);
|
|
958
1002
|
}}
|
|
@@ -962,7 +1006,9 @@ function mediaBreakpointRowTemplate(/** @type {any} */ name, /** @type {any} */
|
|
|
962
1006
|
style="font-size:10px;color:var(--fg-dim);font-family:'SF Mono','Fira Code',monospace;white-space:nowrap"
|
|
963
1007
|
>${name}</span
|
|
964
1008
|
>
|
|
965
|
-
<span
|
|
1009
|
+
<span
|
|
1010
|
+
class="kv-del"
|
|
1011
|
+
@click=${() => transactDoc(activeTab.value, (t) => mutateUpdateMedia(t, name, undefined))}
|
|
966
1012
|
>✕</span
|
|
967
1013
|
>
|
|
968
1014
|
</div>
|
|
@@ -974,7 +1020,7 @@ function mediaBreakpointRowTemplate(/** @type {any} */ name, /** @type {any} */
|
|
|
974
1020
|
@input=${(/** @type {any} */ e) => {
|
|
975
1021
|
clearTimeout(debounceTimer);
|
|
976
1022
|
debounceTimer = setTimeout(
|
|
977
|
-
() =>
|
|
1023
|
+
() => transactDoc(activeTab.value, (t) => mutateUpdateMedia(t, name, e.target.value)),
|
|
978
1024
|
400,
|
|
979
1025
|
);
|
|
980
1026
|
}}
|
|
@@ -1015,8 +1061,8 @@ function isPageDocument(/** @type {any} */ documentPath) {
|
|
|
1015
1061
|
}
|
|
1016
1062
|
|
|
1017
1063
|
function renderPageSection(/** @type {any} */ node) {
|
|
1018
|
-
const
|
|
1019
|
-
if (!isPageDocument(
|
|
1064
|
+
const tab = activeTab.value;
|
|
1065
|
+
if (!isPageDocument(tab.documentPath)) return nothing;
|
|
1020
1066
|
|
|
1021
1067
|
if (layoutEntries === null) {
|
|
1022
1068
|
loadLayoutEntries();
|
|
@@ -1040,7 +1086,9 @@ function renderPageSection(/** @type {any} */ node) {
|
|
|
1040
1086
|
title="Reset to default"
|
|
1041
1087
|
@click=${(/** @type {any} */ e) => {
|
|
1042
1088
|
e.stopPropagation();
|
|
1043
|
-
|
|
1089
|
+
transactDoc(activeTab.value, (t) =>
|
|
1090
|
+
mutateUpdateProperty(t, [], "$layout", undefined),
|
|
1091
|
+
);
|
|
1044
1092
|
}}
|
|
1045
1093
|
></span>`
|
|
1046
1094
|
: nothing}
|
|
@@ -1052,11 +1100,13 @@ function renderPageSection(/** @type {any} */ node) {
|
|
|
1052
1100
|
@change=${(/** @type {any} */ e) => {
|
|
1053
1101
|
const val = e.target.value;
|
|
1054
1102
|
if (val === "__default__") {
|
|
1055
|
-
|
|
1103
|
+
transactDoc(activeTab.value, (t) =>
|
|
1104
|
+
mutateUpdateProperty(t, [], "$layout", undefined),
|
|
1105
|
+
);
|
|
1056
1106
|
} else if (val === "__none__") {
|
|
1057
|
-
|
|
1107
|
+
transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, [], "$layout", false));
|
|
1058
1108
|
} else {
|
|
1059
|
-
|
|
1109
|
+
transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, [], "$layout", val));
|
|
1060
1110
|
}
|
|
1061
1111
|
invalidateLayoutCache();
|
|
1062
1112
|
}}
|
|
@@ -1135,23 +1185,24 @@ function renderLayoutSelectionPanel(/** @type {any} */ ctx) {
|
|
|
1135
1185
|
* @param {{ navigateToComponent: (path: string) => void }} ctx
|
|
1136
1186
|
*/
|
|
1137
1187
|
export function renderPropertiesPanelTemplate(ctx) {
|
|
1138
|
-
const
|
|
1188
|
+
const tab = activeTab.value;
|
|
1189
|
+
if (!tab) return html`<div class="empty-state">No document loaded</div>`;
|
|
1139
1190
|
|
|
1140
1191
|
// Layout element selected — show read-only info with link to open layout
|
|
1141
1192
|
if (view.layoutSelection) {
|
|
1142
1193
|
return renderLayoutSelectionPanel(ctx);
|
|
1143
1194
|
}
|
|
1144
1195
|
|
|
1145
|
-
if (!
|
|
1146
|
-
if (
|
|
1196
|
+
if (!tab.session.selection) {
|
|
1197
|
+
if (tab.doc.mode === "content") {
|
|
1147
1198
|
return renderFrontmatterOnlyPanel();
|
|
1148
1199
|
}
|
|
1149
1200
|
return html`<div class="empty-state">Select an element to inspect</div>`;
|
|
1150
1201
|
}
|
|
1151
|
-
const node = getNodeAtPath(
|
|
1202
|
+
const node = getNodeAtPath(tab.doc.document, tab.session.selection);
|
|
1152
1203
|
if (!node) return html`<div class="empty-state">Node not found</div>`;
|
|
1153
1204
|
|
|
1154
|
-
const path =
|
|
1205
|
+
const path = tab.session.selection;
|
|
1155
1206
|
const isMapNode = node.$prototype === "Array";
|
|
1156
1207
|
const isMapParent =
|
|
1157
1208
|
node.children && typeof node.children === "object" && node.children.$prototype === "Array";
|
|
@@ -1181,13 +1232,16 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1181
1232
|
prop: attr,
|
|
1182
1233
|
label: attrLabel(entry, attr),
|
|
1183
1234
|
hasValue: hasVal,
|
|
1184
|
-
onClear: () =>
|
|
1235
|
+
onClear: () =>
|
|
1236
|
+
transactDoc(activeTab.value, (t) => mutateUpdateAttribute(t, path, attr, undefined)),
|
|
1185
1237
|
widget: html`
|
|
1186
1238
|
<sp-checkbox
|
|
1187
1239
|
size="s"
|
|
1188
1240
|
.checked=${live(!!value)}
|
|
1189
1241
|
@change=${(/** @type {any} */ e) =>
|
|
1190
|
-
|
|
1242
|
+
transactDoc(activeTab.value, (t) =>
|
|
1243
|
+
mutateUpdateAttribute(t, path, attr, e.target.checked || undefined),
|
|
1244
|
+
)}
|
|
1191
1245
|
>
|
|
1192
1246
|
</sp-checkbox>
|
|
1193
1247
|
`,
|
|
@@ -1198,9 +1252,10 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1198
1252
|
prop: attr,
|
|
1199
1253
|
label: attrLabel(entry, attr),
|
|
1200
1254
|
hasValue: hasVal,
|
|
1201
|
-
onClear: () =>
|
|
1255
|
+
onClear: () =>
|
|
1256
|
+
transactDoc(activeTab.value, (t) => mutateUpdateAttribute(t, path, attr, undefined)),
|
|
1202
1257
|
widget: widgetForType(type, entry, attr, value || "", (/** @type {any} */ v) =>
|
|
1203
|
-
|
|
1258
|
+
transactDoc(activeTab.value, (t) => mutateUpdateAttribute(t, path, attr, v || undefined)),
|
|
1204
1259
|
),
|
|
1205
1260
|
});
|
|
1206
1261
|
}
|
|
@@ -1228,7 +1283,7 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1228
1283
|
const knownAttrNames = new Set(Object.keys(applicableAttrs));
|
|
1229
1284
|
if (isCustomInstance) {
|
|
1230
1285
|
const comp = componentRegistry.find((c) => c.tagName === node.tagName);
|
|
1231
|
-
if (comp) for (const p of comp.props) knownAttrNames.add(p.name);
|
|
1286
|
+
if (comp?.props) for (const p of comp.props) knownAttrNames.add(p.name);
|
|
1232
1287
|
}
|
|
1233
1288
|
const customAttrs = Object.entries(attrs).filter(([k]) => !knownAttrNames.has(k));
|
|
1234
1289
|
|
|
@@ -1240,13 +1295,17 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1240
1295
|
if (customAttrs.length > 0) autoOpen.add("__custom");
|
|
1241
1296
|
|
|
1242
1297
|
function isSectionOpen(/** @type {any} */ key) {
|
|
1243
|
-
if (
|
|
1298
|
+
if (tab.session.ui.inspectorSections[key] !== undefined)
|
|
1299
|
+
return tab.session.ui.inspectorSections[key];
|
|
1244
1300
|
return autoOpen.has(key);
|
|
1245
1301
|
}
|
|
1246
1302
|
|
|
1247
1303
|
function toggleSection(/** @type {any} */ key) {
|
|
1248
1304
|
const current = isSectionOpen(key);
|
|
1249
|
-
|
|
1305
|
+
activeTab.value.session.ui.inspectorSections = {
|
|
1306
|
+
...activeTab.value.session.ui.inspectorSections,
|
|
1307
|
+
[key]: !current,
|
|
1308
|
+
};
|
|
1250
1309
|
}
|
|
1251
1310
|
|
|
1252
1311
|
// ── Build section templates ─────────────────────────────────────────
|
|
@@ -1268,7 +1327,9 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1268
1327
|
autocomplete="off"
|
|
1269
1328
|
list="tag-names"
|
|
1270
1329
|
@input=${debouncedStyleCommit("prop:tagName", 400, (/** @type {any} */ e) => {
|
|
1271
|
-
|
|
1330
|
+
transactDoc(activeTab.value, (t) =>
|
|
1331
|
+
mutateUpdateProperty(t, path, "tagName", e.target.value || undefined),
|
|
1332
|
+
);
|
|
1272
1333
|
})}
|
|
1273
1334
|
></sp-textfield>
|
|
1274
1335
|
</div>
|
|
@@ -1280,7 +1341,9 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1280
1341
|
title="Clear $id"
|
|
1281
1342
|
@click=${(/** @type {any} */ e) => {
|
|
1282
1343
|
e.stopPropagation();
|
|
1283
|
-
|
|
1344
|
+
transactDoc(activeTab.value, (t) =>
|
|
1345
|
+
mutateUpdateProperty(t, path, "$id", undefined),
|
|
1346
|
+
);
|
|
1284
1347
|
}}
|
|
1285
1348
|
></span>`
|
|
1286
1349
|
: nothing}
|
|
@@ -1290,7 +1353,9 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1290
1353
|
size="s"
|
|
1291
1354
|
.value=${live(node.$id || "")}
|
|
1292
1355
|
@input=${debouncedStyleCommit("prop:$id", 400, (/** @type {any} */ e) => {
|
|
1293
|
-
|
|
1356
|
+
transactDoc(activeTab.value, (t) =>
|
|
1357
|
+
mutateUpdateProperty(t, path, "$id", e.target.value || undefined),
|
|
1358
|
+
);
|
|
1294
1359
|
})}
|
|
1295
1360
|
></sp-textfield>
|
|
1296
1361
|
</div>
|
|
@@ -1302,7 +1367,9 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1302
1367
|
title="Clear class"
|
|
1303
1368
|
@click=${(/** @type {any} */ e) => {
|
|
1304
1369
|
e.stopPropagation();
|
|
1305
|
-
|
|
1370
|
+
transactDoc(activeTab.value, (t) =>
|
|
1371
|
+
mutateUpdateProperty(t, path, "className", undefined),
|
|
1372
|
+
);
|
|
1306
1373
|
}}
|
|
1307
1374
|
></span>`
|
|
1308
1375
|
: nothing}
|
|
@@ -1312,7 +1379,9 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1312
1379
|
size="s"
|
|
1313
1380
|
.value=${live(node.className || "")}
|
|
1314
1381
|
@input=${debouncedStyleCommit("prop:className", 400, (/** @type {any} */ e) => {
|
|
1315
|
-
|
|
1382
|
+
transactDoc(activeTab.value, (t) =>
|
|
1383
|
+
mutateUpdateProperty(t, path, "className", e.target.value || undefined),
|
|
1384
|
+
);
|
|
1316
1385
|
})}
|
|
1317
1386
|
></sp-textfield>
|
|
1318
1387
|
</div>
|
|
@@ -1326,7 +1395,9 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1326
1395
|
title="Clear text"
|
|
1327
1396
|
@click=${(/** @type {any} */ e) => {
|
|
1328
1397
|
e.stopPropagation();
|
|
1329
|
-
|
|
1398
|
+
transactDoc(activeTab.value, (t) =>
|
|
1399
|
+
mutateUpdateProperty(t, path, "textContent", undefined),
|
|
1400
|
+
);
|
|
1330
1401
|
}}
|
|
1331
1402
|
></span>`
|
|
1332
1403
|
: nothing}
|
|
@@ -1341,8 +1412,8 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1341
1412
|
: (node.textContent ?? ""),
|
|
1342
1413
|
)}
|
|
1343
1414
|
@input=${debouncedStyleCommit("prop:textContent", 400, (/** @type {any} */ e) => {
|
|
1344
|
-
|
|
1345
|
-
|
|
1415
|
+
transactDoc(activeTab.value, (t) =>
|
|
1416
|
+
mutateUpdateProperty(t, path, "textContent", e.target.value || undefined),
|
|
1346
1417
|
);
|
|
1347
1418
|
})}
|
|
1348
1419
|
></sp-textfield>
|
|
@@ -1357,7 +1428,9 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1357
1428
|
title="Clear hidden"
|
|
1358
1429
|
@click=${(/** @type {any} */ e) => {
|
|
1359
1430
|
e.stopPropagation();
|
|
1360
|
-
|
|
1431
|
+
transactDoc(activeTab.value, (t) =>
|
|
1432
|
+
mutateUpdateProperty(t, path, "hidden", undefined),
|
|
1433
|
+
);
|
|
1361
1434
|
}}
|
|
1362
1435
|
></span>`
|
|
1363
1436
|
: nothing}
|
|
@@ -1367,7 +1440,9 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1367
1440
|
size="s"
|
|
1368
1441
|
.checked=${live(!!node.hidden)}
|
|
1369
1442
|
@change=${(/** @type {any} */ e) =>
|
|
1370
|
-
|
|
1443
|
+
transactDoc(activeTab.value, (t) =>
|
|
1444
|
+
mutateUpdateProperty(t, path, "hidden", e.target.checked || undefined),
|
|
1445
|
+
)}
|
|
1371
1446
|
>
|
|
1372
1447
|
</sp-checkbox>
|
|
1373
1448
|
</div>
|
|
@@ -1403,9 +1478,9 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1403
1478
|
: nothing;
|
|
1404
1479
|
|
|
1405
1480
|
const observedAttrsT =
|
|
1406
|
-
isCustomElementDoc(
|
|
1481
|
+
isCustomElementDoc({ document: tab.doc.document }) && isRoot
|
|
1407
1482
|
? (() => {
|
|
1408
|
-
const state =
|
|
1483
|
+
const state = tab.doc.document.state || {};
|
|
1409
1484
|
const entries = Object.entries(state).filter(([, d]) => d.attribute);
|
|
1410
1485
|
return html`
|
|
1411
1486
|
<sp-accordion-item label="Observed Attributes" ?open=${isSectionOpen("__observed")}>
|
|
@@ -1508,7 +1583,7 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1508
1583
|
: nothing;
|
|
1509
1584
|
|
|
1510
1585
|
const cssPropsT =
|
|
1511
|
-
isCustomElementDoc(
|
|
1586
|
+
isCustomElementDoc({ document: tab.doc.document }) && isRoot
|
|
1512
1587
|
? (() => {
|
|
1513
1588
|
const style = node.style || {};
|
|
1514
1589
|
const cssProps = Object.entries(style).filter(([k]) => k.startsWith("--"));
|
|
@@ -1537,9 +1612,9 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1537
1612
|
: nothing;
|
|
1538
1613
|
|
|
1539
1614
|
const cssPartsT =
|
|
1540
|
-
isCustomElementDoc(
|
|
1615
|
+
isCustomElementDoc({ document: tab.doc.document }) && isRoot
|
|
1541
1616
|
? (() => {
|
|
1542
|
-
const parts = collectCssParts(
|
|
1617
|
+
const parts = collectCssParts(tab.doc.document);
|
|
1543
1618
|
if (parts.length === 0) return nothing;
|
|
1544
1619
|
return html`
|
|
1545
1620
|
<sp-accordion-item
|
|
@@ -1565,10 +1640,10 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1565
1640
|
: nothing;
|
|
1566
1641
|
|
|
1567
1642
|
const frontmatterT =
|
|
1568
|
-
|
|
1643
|
+
tab.doc.mode === "content"
|
|
1569
1644
|
? (() => {
|
|
1570
|
-
const fm =
|
|
1571
|
-
const col = findContentTypeSchema(
|
|
1645
|
+
const fm = tab.doc.content?.frontmatter || {};
|
|
1646
|
+
const col = findContentTypeSchema(tab.documentPath, projectState?.projectConfig);
|
|
1572
1647
|
const schemaProps = col?.schema?.properties;
|
|
1573
1648
|
const requiredFields = new Set(col?.schema?.required || []);
|
|
1574
1649
|
|