@jxsuite/studio 10.4.0 → 10.5.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/chunks/2020-7sx2vexa.js +4 -0
- package/dist/chunks/adapter-12dxr3z7.js +4 -0
- package/dist/chunks/{add-new-flow-v0pm96mr.js → add-new-flow-any4pw2q.js} +3 -3
- package/dist/chunks/{cf-account-picker-xb8vvt2h.js → cf-account-picker-ajh60k54.js} +3 -3
- package/dist/chunks/cf-settings-hbj16kh3.js +4 -0
- package/dist/chunks/{color-area-h8dbasp1.js → color-area-gbcftzh3.js} +2 -2
- package/dist/chunks/{color-field-qv5r2b16.js → color-field-85136598.js} +3 -3
- package/dist/chunks/{color-slider-nz2mrzky.js → color-slider-w1s3ndd8.js} +2 -2
- package/dist/chunks/color-thzpppyc.js +4 -0
- package/dist/chunks/{combobox-6hs00996.js → combobox-dk98wc5x.js} +3 -3
- package/dist/chunks/components-a98hzqw9.js +4 -0
- package/dist/chunks/content-source-jq3fjs3w.js +4 -0
- package/dist/chunks/{convert-to-repeater-r8sfxjjm.js → convert-to-repeater-g66r6qp1.js} +3 -3
- package/dist/chunks/editor-rth1rzj8.js +4 -0
- package/dist/chunks/entry-commands-02j0s19w.js +4 -0
- package/dist/chunks/{extension-sections-vjzw46xb.js → extension-sections-rqrs7dr0.js} +3 -3
- package/dist/chunks/file-ops-6a49er4c.js +4 -0
- package/dist/chunks/files-b2p9y0yz.js +4 -0
- package/dist/chunks/format-host-7xe57k1s.js +4 -0
- package/dist/chunks/fs-events-0xsy168g.js +4 -0
- package/dist/chunks/grid-open-h3q4fjy5.js +4 -0
- package/dist/chunks/{grid-view-2f03wcgw.js → grid-view-0v4hjspb.js} +17 -17
- package/dist/chunks/{icons-gb967qc8.js → icons-j9eksgew.js} +2 -2
- package/dist/chunks/{iframe-entry-btck0e9s.js → iframe-entry-4zqtn3ry.js} +4 -4
- package/dist/chunks/{iframe-entry-b5qgggyp.js → iframe-entry-54kwwhmg.js} +4 -4
- package/dist/chunks/{iframe-entry-42hpmwz5.js → iframe-entry-m24fza30.js} +4 -4
- package/dist/chunks/iframe-host-8jcmaktg.js +4 -0
- package/dist/chunks/{image-bake-4afr4y6a.js → image-bake-m8gzaan9.js} +3 -3
- package/dist/chunks/{image-editor-x7t8jbjn.js → image-editor-0afyws7c.js} +3 -3
- package/dist/chunks/{index-yjrfeks8.js → index-2q88g57p.js} +3 -3
- package/dist/chunks/index-7nj2375a.js +4 -0
- package/dist/chunks/{javascript-hy2mws8v.js → javascript-5sxtr1g7.js} +3 -3
- package/dist/chunks/jsonMode-m6sf47hq.js +13 -0
- package/dist/chunks/jx-validate-808zgbgy.js +4 -0
- package/dist/chunks/layers-0ffdtqjn.js +4 -0
- package/dist/chunks/layers-panel-rw62wtvf.js +4 -0
- package/dist/chunks/{listbox-7gka1h86.js → listbox-k5p41049.js} +2 -2
- package/dist/chunks/lit-html-9bz9rcfv.js +4 -0
- package/dist/chunks/media-library-2a5hdbek.js +4 -0
- package/dist/chunks/media-open-069kzhj4.js +4 -0
- package/dist/chunks/media-picker-4p4pd28y.js +4 -0
- package/dist/chunks/menu-26nez152.js +4 -0
- package/dist/chunks/{monaco-binding-a51py33c.js → monaco-binding-1s2b8snq.js} +3 -3
- package/dist/chunks/monaco-buffer-m9fd2dka.js +4 -0
- package/dist/chunks/monaco-setup-e0crh3v6.js +4 -0
- package/dist/chunks/notify-re3js157.js +4 -0
- package/dist/chunks/platform-rywy3tkg.js +4 -0
- package/dist/chunks/popover-fhdnt4be.js +4 -0
- package/dist/chunks/publish-panel-czkpg8m4.js +4 -0
- package/dist/chunks/{publish-quick-4fwngwrw.js → publish-quick-b9wbhqj6.js} +3 -3
- package/dist/chunks/publish-workspace-yy6d23b7.js +4 -0
- package/dist/chunks/quick-search-tp7pjnmv.js +4 -0
- package/dist/chunks/references-wd2yj8s1.js +4 -0
- package/dist/chunks/right-panel-x971k0zc.js +4 -0
- package/dist/chunks/source-control-k4p00hpe.js +4 -0
- package/dist/chunks/{split-m5rwn31z.js → split-qh21j2b0.js} +2 -2
- package/dist/chunks/{studio-v5wktj6f.js → studio-151kgn69.js} +4 -4
- package/dist/chunks/studio-15mfah02.js +10 -0
- package/dist/chunks/studio-1a7zshnv.js +5 -0
- package/dist/chunks/studio-1d8m5s38.js +16 -0
- package/dist/chunks/studio-35pmfjtx.js +5 -0
- package/dist/chunks/{studio-3na1r48j.js → studio-3yr1zdp9.js} +8 -8
- package/dist/chunks/studio-43pynnhz.js +5 -0
- package/dist/chunks/studio-44xqe0b7.js +4 -0
- package/dist/chunks/{studio-hh394phe.js → studio-48yzqzqr.js} +4 -4
- package/dist/chunks/{studio-9gr7xs9r.js → studio-4jy967rj.js} +5 -5
- package/dist/chunks/{studio-4bgvvzhv.js → studio-4ncxzw5m.js} +4 -4
- package/dist/chunks/{studio-pm17wdc4.js → studio-4s5bka8t.js} +4 -4
- package/dist/chunks/{studio-5tet77de.js → studio-4z0cdh16.js} +3 -3
- package/dist/chunks/studio-5rq3e4q5.js +5 -0
- package/dist/chunks/{studio-wmjmsw0p.js → studio-69g6a0jn.js} +4 -4
- package/dist/chunks/studio-6hcfm9q3.js +5 -0
- package/dist/chunks/studio-6mf31e16.js +5 -0
- package/dist/chunks/studio-6sxnqqw8.js +5 -0
- package/dist/chunks/{studio-0939t1q1.js → studio-7qqb7hb8.js} +4 -4
- package/dist/chunks/{studio-bg8pzprg.js → studio-7qr8k0v2.js} +4 -4
- package/dist/chunks/{studio-0x6f3nk9.js → studio-8ncm9zwx.js} +4 -4
- package/dist/chunks/studio-8w5pa88p.js +7 -0
- package/dist/chunks/{studio-3qr08sxq.js → studio-9f5vcb4w.js} +3 -3
- package/dist/chunks/studio-aaw7wxv8.js +5 -0
- package/dist/chunks/studio-afx9brsc.js +5 -0
- package/dist/chunks/studio-ajfq4fmr.js +5 -0
- package/dist/chunks/{studio-evq47j2z.js → studio-ber1dgte.js} +4 -4
- package/dist/chunks/{studio-sce30r7b.js → studio-bk0anq3b.js} +3 -3
- package/dist/chunks/{studio-0gvvqj3h.js → studio-bmwgj8ez.js} +4 -4
- package/dist/chunks/{studio-s24mhq0z.js → studio-bpzwhn88.js} +4 -4
- package/dist/chunks/{studio-g9brbewz.js → studio-c72v1kwc.js} +4 -4
- package/dist/chunks/{studio-tpxcryf2.js → studio-cv6gnjqd.js} +4 -4
- package/dist/chunks/{studio-90zkgne5.js → studio-czpmzs9r.js} +7 -7
- package/dist/chunks/{studio-2pk7kc7z.js → studio-d5f7sskn.js} +4 -4
- package/dist/chunks/studio-dmgh2rrj.js +5 -0
- package/dist/chunks/{studio-nwf0c32p.js → studio-dst8y8vh.js} +4 -4
- package/dist/chunks/studio-ehj3csbm.js +6 -0
- package/dist/chunks/{studio-hfqpfmpk.js → studio-ek4fb4eb.js} +4 -4
- package/dist/chunks/studio-ep1384t9.js +5 -0
- package/dist/chunks/{studio-abh9kfdh.js → studio-eq5vqn1q.js} +4 -4
- package/dist/chunks/studio-erjccagy.js +5 -0
- package/dist/chunks/studio-ex1644g2.js +5 -0
- package/dist/chunks/studio-f3awr5nc.js +12 -0
- package/dist/chunks/studio-f69wcajk.js +9 -0
- package/dist/chunks/{studio-jma2q8r9.js → studio-g65t6svn.js} +4 -4
- package/dist/chunks/{studio-r6mx7vm5.js → studio-gw83z798.js} +5 -5
- package/dist/chunks/{studio-2x972v58.js → studio-hy936516.js} +4 -4
- package/dist/chunks/studio-j3bq30yz.js +5 -0
- package/dist/chunks/studio-j52ks8q7.js +5 -0
- package/dist/chunks/{studio-g0hxtcga.js → studio-jqvke4ac.js} +4 -4
- package/dist/chunks/{studio-bnc50f9h.js → studio-k05g7efh.js} +4 -4
- package/dist/chunks/studio-kefs4sz4.js +5 -0
- package/dist/chunks/{studio-2e4hry72.js → studio-ketrgtph.js} +5 -5
- package/dist/chunks/studio-m3jrjfhy.js +14 -0
- package/dist/chunks/{studio-45mj4jqe.js → studio-m8hesdrw.js} +4 -4
- package/dist/chunks/{studio-k0xzrse2.js → studio-n271vdsg.js} +36 -36
- package/dist/chunks/studio-n4pnf0sw.js +5 -0
- package/dist/chunks/{studio-gnazwtv3.js → studio-ncwx602y.js} +4 -4
- package/dist/chunks/{studio-amaz545p.js → studio-nngc45ct.js} +4 -4
- package/dist/chunks/{studio-eqcddvc2.js → studio-nv5eq0f7.js} +173 -173
- package/dist/chunks/{studio-az3wvyh2.js → studio-nzp0sqjg.js} +4 -4
- package/dist/chunks/{studio-k2evngrj.js → studio-pgze9p34.js} +5 -5
- package/dist/chunks/studio-ph17an19.js +5 -0
- package/dist/chunks/studio-pte5w7rf.js +9 -0
- package/dist/chunks/studio-pv1ts08p.js +6 -0
- package/dist/chunks/studio-q509sqt8.js +5 -0
- package/dist/chunks/{studio-rr00ex23.js → studio-qarg1xf9.js} +6 -6
- package/dist/chunks/studio-r5g7fx7q.js +9 -0
- package/dist/chunks/studio-s49y6m5b.js +5 -0
- package/dist/chunks/studio-s4k1v7dt.js +7 -0
- package/dist/chunks/studio-t46rt6gx.js +5 -0
- package/dist/chunks/{studio-61rg2cz5.js → studio-td3h1644.js} +4 -4
- package/dist/chunks/{studio-v7a7yb5w.js → studio-thgexj3w.js} +7 -7
- package/dist/chunks/{studio-vpevkfxw.js → studio-veh91hjv.js} +4 -4
- package/dist/chunks/studio-vswmz40d.js +6 -0
- package/dist/chunks/{studio-daksw8kh.js → studio-w3aafbpk.js} +4 -4
- package/dist/chunks/studio-w4mfaaam.js +5 -0
- package/dist/chunks/{studio-4zd6an2b.js → studio-wn2cmf9q.js} +4 -4
- package/dist/chunks/studio-x5ffyssw.js +5 -0
- package/dist/chunks/{studio-swedpd82.js → studio-x5py815g.js} +4 -4
- package/dist/chunks/{studio-4fnb5648.js → studio-xh6rf05g.js} +4 -4
- package/dist/chunks/studio-z6s56dzz.js +5 -0
- package/dist/chunks/{studio-nvcn2ykc.js → studio-zav9hgb3.js} +4 -4
- package/dist/chunks/{studio-a88ej93p.js → studio-ze1sm147.js} +6 -6
- package/dist/chunks/{studio-pj216fmn.js → studio-zkkt13qd.js} +4 -4
- package/dist/chunks/studio-zn7ysyzb.js +5 -0
- package/dist/chunks/{studio-xx3r3sb0.js → studio-ztzf8mce.js} +4 -4
- package/dist/chunks/{studio-1410q5dw.js → studio-zzgp5yka.js} +4 -4
- package/dist/chunks/{tooltip-dj1pvgz7.js → tooltip-gtx40bjg.js} +3 -3
- package/dist/chunks/{tree-j0ebwr5s.js → tree-a19s1c05.js} +2 -2
- package/dist/chunks/tsMode-3f116hth.js +14 -0
- package/dist/chunks/workspace-home-61n1jz3q.js +4 -0
- package/dist/chunks/workspace-sheek6zw.js +4 -0
- package/dist/chunks/{ws-client-arz2qsy9.js → ws-client-cb49m87s.js} +3 -3
- package/dist/iframe-entry.js +14 -14
- package/dist/manifest.json +82 -82
- package/dist/studio.js +97 -96
- package/dist/workers/ts.worker.js +3 -3
- package/package.json +11 -11
- package/src/browse/library-layouts.ts +9 -12
- package/src/browse/library-pane.ts +22 -16
- package/src/browse/library-source.ts +3 -4
- package/src/canvas/canvas-live-render.ts +2 -5
- package/src/canvas/canvas-page-context.ts +5 -0
- package/src/content/entry-commands.ts +32 -27
- package/src/content/entry-editor.ts +46 -44
- package/src/content/entry-inspector.ts +15 -12
- package/src/content/entry-model.ts +3 -3
- package/src/content/entry-summaries.ts +5 -5
- package/src/content/hold-state.ts +172 -0
- package/src/content/page-summaries.ts +10 -14
- package/src/editor/inline-edit.ts +6 -1
- package/src/editor/repeater-scope.ts +333 -74
- package/src/grid/sources/content-source.ts +42 -66
- package/src/panels/block-action-bar.ts +3 -4
- package/src/panels/events-panel.ts +6 -2
- package/src/panels/formula-workspace.ts +9 -0
- package/src/panels/properties-panel.ts +127 -35
- package/src/panels/statement-editor.ts +4 -0
- package/src/panels/tab-strip.ts +29 -26
- package/src/services/ai-project-tools.ts +3 -3
- package/src/services/ai-tools.ts +5 -5
- package/src/services/ai-write-report.ts +22 -4
- package/src/services/context-manager.ts +110 -2
- package/src/services/document-assistant.ts +2 -2
- package/src/services/harness/chat-reducer.ts +9 -4
- package/src/services/render-critic.ts +234 -12
- package/src/services/tool-executor.ts +16 -8
- package/src/surfaces/collections-panel.json +1 -1
- package/src/surfaces/collections-panel.ts +1 -1
- package/src/surfaces/entry-editor.json +10 -10
- package/src/surfaces/entry-editor.ts +22 -19
- package/src/surfaces/entry-inspector.json +11 -11
- package/src/surfaces/entry-inspector.ts +17 -10
- package/src/surfaces/tab-strip.json +5 -5
- package/src/surfaces/tab-strip.ts +2 -2
- package/src/types.ts +2 -0
- package/src/ui/expression-editor.ts +10 -5
- package/src/ui/schema-form.ts +18 -1
- package/src/utils/edit-display.ts +27 -1
- package/dist/chunks/2020-906vx2dq.js +0 -4
- package/dist/chunks/adapter-hnvad61x.js +0 -4
- package/dist/chunks/cf-settings-mkbcz4z2.js +0 -4
- package/dist/chunks/color-04361ttk.js +0 -4
- package/dist/chunks/components-w5zqwva1.js +0 -4
- package/dist/chunks/content-source-g38w0d3f.js +0 -4
- package/dist/chunks/editor-wvf5e6j9.js +0 -4
- package/dist/chunks/entry-commands-t13dyw3c.js +0 -4
- package/dist/chunks/file-ops-1sghzgcr.js +0 -4
- package/dist/chunks/files-zrqtgt41.js +0 -4
- package/dist/chunks/format-host-9gy76erh.js +0 -4
- package/dist/chunks/fs-events-45rhg3me.js +0 -4
- package/dist/chunks/grid-open-t4fq6bbm.js +0 -4
- package/dist/chunks/iframe-host-9aq8ze3q.js +0 -4
- package/dist/chunks/index-f9qr7kzh.js +0 -4
- package/dist/chunks/jsonMode-vj30te2h.js +0 -13
- package/dist/chunks/jx-validate-r4ax11e3.js +0 -4
- package/dist/chunks/layers-m49nmwvx.js +0 -4
- package/dist/chunks/layers-panel-zr92pwdp.js +0 -4
- package/dist/chunks/lit-html-k2mb9k0r.js +0 -4
- package/dist/chunks/media-library-j74rt0w4.js +0 -4
- package/dist/chunks/media-open-mypr0k6b.js +0 -4
- package/dist/chunks/media-picker-pemtbsjv.js +0 -4
- package/dist/chunks/menu-d2jxpgma.js +0 -4
- package/dist/chunks/monaco-buffer-pa0g6ax7.js +0 -4
- package/dist/chunks/monaco-setup-0hw9f835.js +0 -4
- package/dist/chunks/notify-bwehrp7q.js +0 -4
- package/dist/chunks/platform-kv0k65cy.js +0 -4
- package/dist/chunks/popover-9tf4cjrk.js +0 -4
- package/dist/chunks/publish-panel-vg1n880b.js +0 -4
- package/dist/chunks/publish-workspace-jv4w2bx2.js +0 -4
- package/dist/chunks/quick-search-nh29rt8a.js +0 -4
- package/dist/chunks/references-vqyr5s9x.js +0 -4
- package/dist/chunks/right-panel-3ws9f2w0.js +0 -4
- package/dist/chunks/source-control-h3hcs5gy.js +0 -4
- package/dist/chunks/studio-0khd8xtx.js +0 -4
- package/dist/chunks/studio-1ctnw3xv.js +0 -5
- package/dist/chunks/studio-1ks5d9y4.js +0 -5
- package/dist/chunks/studio-1y9mab9m.js +0 -5
- package/dist/chunks/studio-2kpxm0eh.js +0 -7
- package/dist/chunks/studio-2zz4qbv9.js +0 -5
- package/dist/chunks/studio-3pz4t5a2.js +0 -5
- package/dist/chunks/studio-4f7thq0p.js +0 -5
- package/dist/chunks/studio-7rrdbvex.js +0 -5
- package/dist/chunks/studio-9dq7rwc7.js +0 -5
- package/dist/chunks/studio-acer0g8w.js +0 -5
- package/dist/chunks/studio-brh8d2pr.js +0 -5
- package/dist/chunks/studio-c2fa783p.js +0 -9
- package/dist/chunks/studio-cfnn70h1.js +0 -5
- package/dist/chunks/studio-cqxj4fcy.js +0 -5
- package/dist/chunks/studio-cw11h078.js +0 -5
- package/dist/chunks/studio-dn56z7sn.js +0 -5
- package/dist/chunks/studio-egnjpyde.js +0 -9
- package/dist/chunks/studio-ejrypvmq.js +0 -5
- package/dist/chunks/studio-frc16j8v.js +0 -10
- package/dist/chunks/studio-j0zf5x2x.js +0 -5
- package/dist/chunks/studio-j5fcp6gj.js +0 -5
- package/dist/chunks/studio-jd1qb1st.js +0 -7
- package/dist/chunks/studio-k1xqrgm5.js +0 -5
- package/dist/chunks/studio-k3we3kqb.js +0 -5
- package/dist/chunks/studio-knsjyx7n.js +0 -5
- package/dist/chunks/studio-mrjtnm62.js +0 -5
- package/dist/chunks/studio-n9qba0gx.js +0 -5
- package/dist/chunks/studio-nzemfbkk.js +0 -14
- package/dist/chunks/studio-pwctzka3.js +0 -16
- package/dist/chunks/studio-q80fg1d6.js +0 -12
- package/dist/chunks/studio-q9hx2qp0.js +0 -5
- package/dist/chunks/studio-qrzmfaf6.js +0 -5
- package/dist/chunks/studio-tmfcn347.js +0 -6
- package/dist/chunks/studio-wqfaqw4m.js +0 -5
- package/dist/chunks/studio-xpx91fby.js +0 -6
- package/dist/chunks/studio-xva3hzkv.js +0 -5
- package/dist/chunks/studio-z3fztxrp.js +0 -9
- package/dist/chunks/studio-z9453ypn.js +0 -6
- package/dist/chunks/tsMode-jg1dtphj.js +0 -14
- package/dist/chunks/workspace-6ztqcrsh.js +0 -4
- package/dist/chunks/workspace-home-rse9h8a0.js +0 -4
- package/src/content/draft-state.ts +0 -108
|
@@ -76,6 +76,7 @@ import {
|
|
|
76
76
|
bindableSignalNames,
|
|
77
77
|
defaultAsString,
|
|
78
78
|
isInspectorSectionOpen,
|
|
79
|
+
mapListSignalsFor,
|
|
79
80
|
mapSignalsFor,
|
|
80
81
|
setInspectorSection,
|
|
81
82
|
} from "./properties-panel";
|
|
@@ -348,6 +349,7 @@ function addField(
|
|
|
348
349
|
(next) => spec.onChange({ $expression: next } as JsonValue),
|
|
349
350
|
{
|
|
350
351
|
allowEventRef: false,
|
|
352
|
+
extraRefs: spec.extraSignals,
|
|
351
353
|
stateDefs: [...spec.stateDefs],
|
|
352
354
|
stateEntries: null,
|
|
353
355
|
},
|
|
@@ -452,7 +454,7 @@ export function projectLogicPanel(): LogicView {
|
|
|
452
454
|
view.repeaterFields = [
|
|
453
455
|
addField(fields, `prop|${key}|items`, "items", {
|
|
454
456
|
caps: "repeaterItems",
|
|
455
|
-
extraSignals:
|
|
457
|
+
extraSignals: mapListSignalsFor(doc, selection),
|
|
456
458
|
label: "Items",
|
|
457
459
|
literalDefault: literalDefaultOf(node.items),
|
|
458
460
|
onChange: (v?: JsonValue) =>
|
|
@@ -475,7 +477,7 @@ export function projectLogicPanel(): LogicView {
|
|
|
475
477
|
// `SwitchDef` is a $ref and nothing else, so From data… is the only rung — a $switch is
|
|
476
478
|
// Inherently dynamic, and de-escalating it would delete the key and demote the node.
|
|
477
479
|
caps: "switchDiscriminant",
|
|
478
|
-
extraSignals: mapSignalsFor(selection),
|
|
480
|
+
extraSignals: mapSignalsFor(doc, selection),
|
|
479
481
|
label: "Expression",
|
|
480
482
|
literalDefault: literalDefaultOf(node.$switch),
|
|
481
483
|
onChange: (v?: JsonValue) =>
|
|
@@ -620,6 +622,7 @@ function bindingView(
|
|
|
620
622
|
{
|
|
621
623
|
allowEventRef: true,
|
|
622
624
|
emits: fn.emits ?? [],
|
|
625
|
+
extraRefs: mapSignalsFor(tab.doc.document, selection),
|
|
623
626
|
/* The Inspector's copy, named by the handler it edits. It answered to
|
|
624
627
|
`navigator/statements` until the docks were split, and then to a bare
|
|
625
628
|
`inspector/statements` — which the binding list stamps once per structured handler, so
|
|
@@ -657,6 +660,7 @@ function bindingView(
|
|
|
657
660
|
plan.paint = (host) =>
|
|
658
661
|
mountExpressionEditor(host, expression, commitExpression, {
|
|
659
662
|
allowEventRef: true,
|
|
663
|
+
extraRefs: mapSignalsFor(tab.doc.document, selection),
|
|
660
664
|
onInsertDef: insertDef,
|
|
661
665
|
preview,
|
|
662
666
|
stateDefs: Object.keys(defs),
|
|
@@ -59,6 +59,7 @@ import { livePreviewExpression } from "../services/live-preview";
|
|
|
59
59
|
import { closeFunctionEditor, syncFunctionEditor } from "./editors";
|
|
60
60
|
import { registerPanel } from "./panel-registry";
|
|
61
61
|
import { dataTypeLabel, paintDataTree, unwrapSignal } from "./data-explorer";
|
|
62
|
+
import { mapSignalsFor } from "./properties-panel";
|
|
62
63
|
import { disposeDetachedDataTrees } from "../surfaces/panel-data";
|
|
63
64
|
import { emptyLogicWorkspaceView, mountLogicWorkspace } from "../surfaces/logic-workspace";
|
|
64
65
|
|
|
@@ -72,6 +73,7 @@ import {
|
|
|
72
73
|
|
|
73
74
|
import type { AnyCommand, CommandRegistry } from "../commands/registry";
|
|
74
75
|
import type { PanelBody } from "./panel-registry";
|
|
76
|
+
import type { SurfaceOption } from "../ui/surface";
|
|
75
77
|
import type { ExpressionPreview } from "../services/preview-eval";
|
|
76
78
|
import type { FormulaEditDef, FunctionEditDef } from "../types";
|
|
77
79
|
import type { Tab } from "../tabs/tab";
|
|
@@ -314,6 +316,8 @@ interface Resolved {
|
|
|
314
316
|
stateEntries: Record<string, JxStateDefinition>;
|
|
315
317
|
preview: ExpressionPreview | null;
|
|
316
318
|
allowEventRef: boolean;
|
|
319
|
+
/** The repeater item's fields, for an event handler inside a template. */
|
|
320
|
+
extraRefs: SurfaceOption[] | null;
|
|
317
321
|
/** Chip key → node path, so a chip click needs no path parsing (a `switch` case may hold "/"). */
|
|
318
322
|
chipPaths: Map<string, NodePath>;
|
|
319
323
|
/** Rail entry name → the value its tree draws. */
|
|
@@ -475,6 +479,10 @@ export function workspaceView(): LogicWorkspaceView {
|
|
|
475
479
|
allowEventRef: editing.type === "event",
|
|
476
480
|
chipPaths,
|
|
477
481
|
editing,
|
|
482
|
+
extraRefs:
|
|
483
|
+
editing.type === "event" && editing.path
|
|
484
|
+
? mapSignalsFor(tab.doc.document, editing.path)
|
|
485
|
+
: null,
|
|
478
486
|
preview,
|
|
479
487
|
root,
|
|
480
488
|
scopeValues: rail.values,
|
|
@@ -528,6 +536,7 @@ function paintEditor(): void {
|
|
|
528
536
|
mountExpressionEditor(host, held.selected, writeSelected, {
|
|
529
537
|
allowEventRef: held.allowEventRef,
|
|
530
538
|
depth: 1,
|
|
539
|
+
extraRefs: held.extraRefs,
|
|
531
540
|
onInsertDef: insertDef,
|
|
532
541
|
path: held.selectedPath,
|
|
533
542
|
preview: held.preview,
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
* @docs studio/design/properties
|
|
42
42
|
*/
|
|
43
43
|
|
|
44
|
-
import { getNodeAtPath } from "../store";
|
|
44
|
+
import { getNodeAtPath, projectState } from "../store";
|
|
45
45
|
import { effect, effectScope, reactive } from "../reactivity";
|
|
46
46
|
import {
|
|
47
47
|
displayTagName,
|
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
isRef,
|
|
51
51
|
isString,
|
|
52
52
|
isTagExpression,
|
|
53
|
+
isTemplateString,
|
|
53
54
|
} from "@jxsuite/schema/guards";
|
|
54
55
|
import type { JsonValue } from "../types";
|
|
55
56
|
import {
|
|
@@ -114,6 +115,13 @@ import type { LinkKind } from "../utils/link-target";
|
|
|
114
115
|
import { clickAnythingTo, openPageAction, staleSelectionMessage } from "./empty-state";
|
|
115
116
|
import { mountMediaPicker, unmountMediaPicker } from "../ui/media-picker";
|
|
116
117
|
import { colorTokens, resolvedColor } from "../ui/color-selector";
|
|
118
|
+
import {
|
|
119
|
+
childrenTemplate,
|
|
120
|
+
fieldOfChildrenTemplate,
|
|
121
|
+
mapRefLabel,
|
|
122
|
+
repeaterItemFields,
|
|
123
|
+
} from "../editor/repeater-scope";
|
|
124
|
+
import type { RepeaterField } from "../editor/repeater-scope";
|
|
117
125
|
import { mountExpressionEditor } from "../ui/expression-editor";
|
|
118
126
|
import type { ExpressionEditorOpts } from "../ui/expression-editor";
|
|
119
127
|
import {
|
|
@@ -182,20 +190,45 @@ function nodeAtPath(doc: JxMutableNode, path: JxPath): JxMutableNode | undefined
|
|
|
182
190
|
// ─── The binding vocabulary, shared with the Logic tab ───────────────────────
|
|
183
191
|
|
|
184
192
|
/**
|
|
185
|
-
* The
|
|
193
|
+
* The extra signals a position inside a repeater template can bind to, or `null` outside one: the
|
|
194
|
+
* item, every field of the item the document and project declare, and its index.
|
|
186
195
|
*
|
|
187
196
|
* A "map" segment addresses a repeater template (`[…, "children", i, "map", …]`, or the legacy `[…,
|
|
188
197
|
* "children", "map", …]`), so anything at or below it is inside one.
|
|
189
198
|
*
|
|
190
199
|
* Exported because Content and Logic both offer them and there is one right answer: a `$switch`
|
|
191
|
-
* inside a `$map` can read `$map/item`, and so can the `alt` attribute two nodes below
|
|
200
|
+
* inside a `$map` can read `$map/item/data/title`, and so can the `alt` attribute two nodes below
|
|
201
|
+
* it. `$map/item` stays first, because a fresh binding lands on the first signal offered.
|
|
202
|
+
*
|
|
203
|
+
* `withNodes` adds the fields holding child nodes, for the one position that can draw them.
|
|
204
|
+
*/
|
|
205
|
+
export function mapSignalsFor(
|
|
206
|
+
doc: JxMutableNode,
|
|
207
|
+
path: JxPath,
|
|
208
|
+
options: { withNodes?: boolean } = {},
|
|
209
|
+
): SurfaceOption[] | null {
|
|
210
|
+
if (!path.includes("map")) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
const fields = repeaterItemFields(doc, path, projectState?.projectConfig, options);
|
|
214
|
+
return [
|
|
215
|
+
{ label: "item", value: "$map/item" },
|
|
216
|
+
...fields.map((field) => ({ label: field.token, value: field.pointer })),
|
|
217
|
+
{ label: "index", value: "$map/index" },
|
|
218
|
+
];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* What a repeater's own Items can read from the repeater around it: the lists on the outer item.
|
|
223
|
+
* Only those, because an Items position takes a list, and the index or a text field would render
|
|
224
|
+
* nothing.
|
|
192
225
|
*/
|
|
193
|
-
export function
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
226
|
+
export function mapListSignalsFor(doc: JxMutableNode, path: JxPath): SurfaceOption[] | null {
|
|
227
|
+
const lists = repeaterItemFields(doc, path, projectState?.projectConfig).filter(
|
|
228
|
+
(field) => field.isArray,
|
|
229
|
+
);
|
|
230
|
+
return lists.length > 0
|
|
231
|
+
? lists.map((field) => ({ label: field.token, value: field.pointer }))
|
|
199
232
|
: null;
|
|
200
233
|
}
|
|
201
234
|
|
|
@@ -228,7 +261,7 @@ export function defaultAsString(def: JxStateDefinition | undefined) {
|
|
|
228
261
|
function bindingDonor(value: JxDocumentValue | undefined): string {
|
|
229
262
|
if (isRef(value)) {
|
|
230
263
|
const ref = value.$ref;
|
|
231
|
-
return ref.startsWith("#/state/") ? ref.slice(8) : ref || "nothing yet";
|
|
264
|
+
return ref.startsWith("#/state/") ? ref.slice(8) : mapRefLabel(ref) || ref || "nothing yet";
|
|
232
265
|
}
|
|
233
266
|
return isString(value) ? "a template" : "a formula";
|
|
234
267
|
}
|
|
@@ -1098,6 +1131,7 @@ function applyLadder(row: ContentRowView, plan: RowPlan, opts: LadderOpts): void
|
|
|
1098
1131
|
plan.draft = undefined;
|
|
1099
1132
|
const editorOpts: ExpressionEditorOpts = {
|
|
1100
1133
|
allowEventRef: false,
|
|
1134
|
+
extraRefs: opts.extraSignals,
|
|
1101
1135
|
preview: null,
|
|
1102
1136
|
stateDefs: opts.stateDefs,
|
|
1103
1137
|
stateEntries: null,
|
|
@@ -1230,7 +1264,7 @@ function sectionsFor(node: JxMutableNode, path: JxPath): ContentSectionView[] {
|
|
|
1230
1264
|
const tagName = displayTagName(node.tagName) || "div";
|
|
1231
1265
|
const isCustomInstance = displayTagName(node.tagName).includes("-");
|
|
1232
1266
|
const attrs = node.attributes || {};
|
|
1233
|
-
const mapSignals = mapSignalsFor(path);
|
|
1267
|
+
const mapSignals = mapSignalsFor(doc, path);
|
|
1234
1268
|
const bindableSignals = bindableSignalNames(doc);
|
|
1235
1269
|
|
|
1236
1270
|
// ── Which attributes this tag has, by section ──
|
|
@@ -1276,7 +1310,7 @@ function sectionsFor(node: JxMutableNode, path: JxPath): ContentSectionView[] {
|
|
|
1276
1310
|
const sections: ContentSectionView[] = [
|
|
1277
1311
|
sectionView("__element", "Element", isInspectorSectionOpen("__element", false), [], 0),
|
|
1278
1312
|
];
|
|
1279
|
-
sections[0]!.rows = elementRows(node, path, tagName,
|
|
1313
|
+
sections[0]!.rows = elementRows(node, path, tagName, bindableSignals);
|
|
1280
1314
|
|
|
1281
1315
|
if (isCustomInstance) {
|
|
1282
1316
|
const definitionOfThis = isComponentDefinitionOpen(node);
|
|
@@ -1366,11 +1400,90 @@ function draftKey(path: JxPath, name: string): string {
|
|
|
1366
1400
|
return `prop:${path.join("/")}:${name}`;
|
|
1367
1401
|
}
|
|
1368
1402
|
|
|
1403
|
+
/**
|
|
1404
|
+
* What a write to the content row leaves in `children`: the template to hold, or `null` for none.
|
|
1405
|
+
*
|
|
1406
|
+
* `$children` is a list of nodes, so it cannot be held in `textContent`, which would draw it as
|
|
1407
|
+
* `[object Object]`. Picking it, or typing a template that reads it, moves the binding to the
|
|
1408
|
+
* computed-children form the compiler already expands (spec.md §8.4). A template the row already
|
|
1409
|
+
* held there that names no such field (`${state.entry.$children}`) stays there as it is edited.
|
|
1410
|
+
*/
|
|
1411
|
+
function computedChildrenFrom(
|
|
1412
|
+
value: JsonValue | undefined,
|
|
1413
|
+
nodeFields: readonly RepeaterField[],
|
|
1414
|
+
keepsTemplate: boolean,
|
|
1415
|
+
): string | null {
|
|
1416
|
+
if (isRef(value)) {
|
|
1417
|
+
const field = nodeFields.find((f) => f.pointer === value.$ref);
|
|
1418
|
+
return field ? childrenTemplate(field) : null;
|
|
1419
|
+
}
|
|
1420
|
+
if (!isTemplateString(value)) {
|
|
1421
|
+
return null;
|
|
1422
|
+
}
|
|
1423
|
+
return keepsTemplate || fieldOfChildrenTemplate(value, nodeFields) ? value : null;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
/**
|
|
1427
|
+
* The Text Content row, which is also where an element's child nodes are bound.
|
|
1428
|
+
*
|
|
1429
|
+
* One row for two properties, because they are one question: what fills this element. A node with
|
|
1430
|
+
* computed children has no text to show, and it used to draw this row empty and on Fixed value, so
|
|
1431
|
+
* the binding could not be seen and typing here wrote text the compiled page never drew.
|
|
1432
|
+
*/
|
|
1433
|
+
function contentRow(
|
|
1434
|
+
node: JxMutableNode,
|
|
1435
|
+
path: JxPath,
|
|
1436
|
+
bindableSignals: string[],
|
|
1437
|
+
propertyChip: (name: string, human: string) => FieldProvenance,
|
|
1438
|
+
): ContentRowView {
|
|
1439
|
+
const doc = activeTab.value!.doc.document;
|
|
1440
|
+
const nodeFields = repeaterItemFields(doc, path, projectState?.projectConfig, {
|
|
1441
|
+
withNodes: true,
|
|
1442
|
+
}).filter((field) => field.holdsNodes);
|
|
1443
|
+
const computed = isTemplateString(node.children) ? node.children : null;
|
|
1444
|
+
const bound = computed === null ? undefined : fieldOfChildrenTemplate(computed, nodeFields);
|
|
1445
|
+
const value = bound ? { $ref: bound.pointer } : (computed ?? node.textContent);
|
|
1446
|
+
const write = (next?: JsonValue): void => {
|
|
1447
|
+
const template = computedChildrenFrom(next, nodeFields, computed !== null && !bound);
|
|
1448
|
+
transactDoc(activeTab.value, (t) => {
|
|
1449
|
+
if (template === null || node.textContent !== undefined) {
|
|
1450
|
+
mutateUpdateProperty(t, path, "textContent", template === null ? next : undefined);
|
|
1451
|
+
}
|
|
1452
|
+
if (template !== computed) {
|
|
1453
|
+
mutateUpdateProperty(t, path, "children", template ?? undefined);
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
};
|
|
1457
|
+
|
|
1458
|
+
const textDraft = draft(draftKey(path, "textContent"));
|
|
1459
|
+
const text = blankRow(
|
|
1460
|
+
"prop|textContent",
|
|
1461
|
+
"textContent",
|
|
1462
|
+
computed === null ? "Text Content" : "Content",
|
|
1463
|
+
);
|
|
1464
|
+
text.kind = "textarea";
|
|
1465
|
+
text.value = getFieldValue(textDraft, isString(value) ? value : "");
|
|
1466
|
+
const plan: RowPlan = { commit: write, debounceMs: LIVE_PREVIEW, draft: textDraft };
|
|
1467
|
+
applyLadder(text, plan, {
|
|
1468
|
+
caps: "textProperty",
|
|
1469
|
+
extraSignals: mapSignalsFor(doc, path, { withNodes: true }),
|
|
1470
|
+
fieldKey: `prop|${path.join("/")}|textContent`,
|
|
1471
|
+
onChange: write,
|
|
1472
|
+
stateDefs: bindableSignals,
|
|
1473
|
+
value,
|
|
1474
|
+
});
|
|
1475
|
+
if (computed !== null) {
|
|
1476
|
+
applyChip(text, plan, "textContent", propertyChip("children", "content"));
|
|
1477
|
+
} else if (node.textContent !== undefined) {
|
|
1478
|
+
applyChip(text, plan, "textContent", propertyChip("textContent", "text"));
|
|
1479
|
+
}
|
|
1480
|
+
return withPlan(text, plan);
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1369
1483
|
function elementRows(
|
|
1370
1484
|
node: JxMutableNode,
|
|
1371
1485
|
path: JxPath,
|
|
1372
1486
|
tagName: string,
|
|
1373
|
-
mapSignals: SurfaceOption[] | null,
|
|
1374
1487
|
bindableSignals: string[],
|
|
1375
1488
|
): ContentRowView[] {
|
|
1376
1489
|
const chosenTag = isTagExpression(node.tagName) ? node.tagName.$expression : null;
|
|
@@ -1459,28 +1572,7 @@ function elementRows(
|
|
|
1459
1572
|
rows.push(withPlan(cls.row, cls.plan));
|
|
1460
1573
|
|
|
1461
1574
|
if (!Array.isArray(node.children) || node.children.length === 0) {
|
|
1462
|
-
|
|
1463
|
-
const text = blankRow("prop|textContent", "textContent", "Text Content");
|
|
1464
|
-
text.kind = "textarea";
|
|
1465
|
-
text.value = getFieldValue(textDraft, isString(node.textContent) ? node.textContent : "");
|
|
1466
|
-
const plan: RowPlan = {
|
|
1467
|
-
commit: writeProperty("textContent"),
|
|
1468
|
-
debounceMs: LIVE_PREVIEW,
|
|
1469
|
-
draft: textDraft,
|
|
1470
|
-
};
|
|
1471
|
-
applyLadder(text, plan, {
|
|
1472
|
-
caps: "textProperty",
|
|
1473
|
-
extraSignals: mapSignals,
|
|
1474
|
-
fieldKey: `prop|${path.join("/")}|textContent`,
|
|
1475
|
-
onChange: (v) =>
|
|
1476
|
-
transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, path, "textContent", v)),
|
|
1477
|
-
stateDefs: bindableSignals,
|
|
1478
|
-
value: node.textContent,
|
|
1479
|
-
});
|
|
1480
|
-
if (node.textContent !== undefined) {
|
|
1481
|
-
applyChip(text, plan, "textContent", propertyChip("textContent", "text"));
|
|
1482
|
-
}
|
|
1483
|
-
rows.push(withPlan(text, plan));
|
|
1575
|
+
rows.push(contentRow(node, path, bindableSignals, propertyChip));
|
|
1484
1576
|
}
|
|
1485
1577
|
|
|
1486
1578
|
const hidden = blankRow("prop|hidden", "hidden", "Hidden");
|
|
@@ -62,6 +62,8 @@ export interface StatementEditorOpts {
|
|
|
62
62
|
stateDefs: string[];
|
|
63
63
|
stateEntries?: Record<string, JxStateDefinition> | null;
|
|
64
64
|
allowEventRef: boolean;
|
|
65
|
+
/** Pointers beyond the document's own signals: inside a repeater, its item and that item's fields. */
|
|
66
|
+
extraRefs?: readonly SurfaceOption[] | null | undefined;
|
|
65
67
|
/** The entry's declared CEM events — offered as dispatchEvent name completions. */
|
|
66
68
|
emits?: CemEvent[];
|
|
67
69
|
/**
|
|
@@ -342,6 +344,7 @@ function operandOpts(opts: StatementEditorOpts) {
|
|
|
342
344
|
return {
|
|
343
345
|
allowEventRef: opts.allowEventRef,
|
|
344
346
|
depth: 0,
|
|
347
|
+
extraRefs: opts.extraRefs,
|
|
345
348
|
stateDefs: opts.stateDefs,
|
|
346
349
|
stateEntries: opts.stateEntries ?? null,
|
|
347
350
|
};
|
|
@@ -578,6 +581,7 @@ export function flattenStatements(
|
|
|
578
581
|
(n) => commit(n as JxStatement),
|
|
579
582
|
{
|
|
580
583
|
allowEventRef: opts.allowEventRef,
|
|
584
|
+
extraRefs: opts.extraRefs,
|
|
581
585
|
stateDefs: opts.stateDefs,
|
|
582
586
|
stateEntries: opts.stateEntries ?? null,
|
|
583
587
|
},
|
package/src/panels/tab-strip.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* **The chip is a `jx-tab` now, and three of the things it used to draw by hand are the kit's**:
|
|
17
17
|
* the selected state (`aria-selected` on a real `tab` inside a real `tablist`, where an `.active`
|
|
18
18
|
* class used to be), the close button, and the dirty dot. The three that are Studio's own live in
|
|
19
|
-
* `jx-tab`'s slots — the drill-in marker in `icon`, the
|
|
19
|
+
* `jx-tab`'s slots — the drill-in marker in `icon`, the hold pill in `status`, the pin toggle in
|
|
20
20
|
* `actions` — which is the gap that kept this strip out of the tab pattern (`ui.md` §5.4). With it
|
|
21
21
|
* closed the strip is one stop in the tab order, the arrows walk it, Home and End reach its ends,
|
|
22
22
|
* and Delete closes a tab: a keyboard contract the × has never had.
|
|
@@ -57,8 +57,8 @@ import { gridTabLabel } from "../grid/grid-source";
|
|
|
57
57
|
import { workspaceTabOrder } from "../workspace/tab-workspace";
|
|
58
58
|
import { workspaceHomeLabel } from "../workspace/workspace-ids";
|
|
59
59
|
import { shell, workspaceDrawsTabStrip } from "../shell";
|
|
60
|
-
import {
|
|
61
|
-
import {
|
|
60
|
+
import { entryHoldPill } from "../content/entry-editor";
|
|
61
|
+
import { holdFields, holdSwitch } from "../content/hold-state";
|
|
62
62
|
import { entryFields } from "../content/entry-fields";
|
|
63
63
|
import { collectionOfPath } from "../content/entry-model";
|
|
64
64
|
import { activeRegistry } from "../commands/active-registry";
|
|
@@ -262,9 +262,11 @@ export function mount(host: HTMLElement) {
|
|
|
262
262
|
void tab.pinned;
|
|
263
263
|
void tab.preview;
|
|
264
264
|
void tab.session.openedFrom;
|
|
265
|
-
// The
|
|
266
|
-
//
|
|
267
|
-
|
|
265
|
+
// The hold pill is on the chip, so the chip has to repaint when a field its rules read
|
|
266
|
+
// Changes. Reading an absent key tracks its ADDITION too, which matters most.
|
|
267
|
+
for (const field of holdFields(collectionOfPath(tab.documentPath)?.def)) {
|
|
268
|
+
void tab.doc.content.frontmatter[field];
|
|
269
|
+
}
|
|
268
270
|
}
|
|
269
271
|
// An edit is a commitment: a preview tab with unsaved changes is no longer disposable, and
|
|
270
272
|
// The dirty flags this effect already tracks are exactly the signal.
|
|
@@ -685,15 +687,15 @@ function chipView(
|
|
|
685
687
|
if (!tab) {
|
|
686
688
|
return [];
|
|
687
689
|
}
|
|
688
|
-
/* The
|
|
690
|
+
/* The hold pill (`site-architecture.md` §7.6). On the CHIP, not only inside the entry editor:
|
|
689
691
|
the mistake this prevents — publishing something you believed was private — is made while
|
|
690
692
|
glancing at a row of tabs, and it is made about a document that may not even be the active
|
|
691
693
|
one. */
|
|
692
|
-
const pill =
|
|
694
|
+
const pill = entryHoldPill(tab);
|
|
693
695
|
return [
|
|
694
696
|
{
|
|
695
697
|
dirty: tab.doc.dirty || imageEditorDirty(tab),
|
|
696
|
-
|
|
698
|
+
held: pill?.held ?? false,
|
|
697
699
|
index,
|
|
698
700
|
key: id,
|
|
699
701
|
label: labels.get(id) ?? "Untitled",
|
|
@@ -1011,24 +1013,25 @@ export function dismissTabContextMenu() {
|
|
|
1011
1013
|
* A tab addresses ONE document, and this is everything the chip can say about it — keyed by the
|
|
1012
1014
|
* ARGUMENT NAME that asks for it.
|
|
1013
1015
|
*
|
|
1014
|
-
* The whole `context/tab` placement asks for exactly one argument today: `content.
|
|
1015
|
-
*
|
|
1016
|
+
* The whole `context/tab` placement asks for exactly one argument today: `content.setHold {hold}`.
|
|
1017
|
+
* The other five records take none, because they read the ACTIVE document — which is why
|
|
1016
1018
|
* {@link openTabContextMenu} activates the chip before it builds a single row.
|
|
1017
1019
|
*
|
|
1018
1020
|
* A fact is stated ONLY when it is true of this tab, and that is what decides whether a command
|
|
1019
|
-
* appears at all: `styles/main.css` is an entry of no collection, so it
|
|
1020
|
-
*
|
|
1021
|
-
* `content.
|
|
1022
|
-
*
|
|
1021
|
+
* appears at all: `styles/main.css` is an entry of no collection, so it has no hold rules, so "Set
|
|
1022
|
+
* Hold" is not offered on it. The condition is `collectionOfPath` — the same question
|
|
1023
|
+
* `content.setHold`'s own `enablement` asks — so the menu never invents a rule the command does not
|
|
1024
|
+
* have, and never renders a row into a refusal.
|
|
1023
1025
|
*
|
|
1024
1026
|
* The VALUE is the state the row would reach, not the state the tab is in: a setter is named for
|
|
1025
|
-
* where it lands (`content/
|
|
1026
|
-
*
|
|
1027
|
+
* where it lands (`content/hold-state.ts` says why), so the row offers the flip of what is true now
|
|
1028
|
+
* and {@link statedChecked} reads the current state back out of it for the checkmark.
|
|
1027
1029
|
*/
|
|
1028
1030
|
function tabRowFacts(tab: Tab): CommandArgValues {
|
|
1029
1031
|
const facts: CommandArgValues = {};
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
+
const toggle = holdSwitch(collectionOfPath(tab.documentPath)?.def);
|
|
1033
|
+
if (toggle) {
|
|
1034
|
+
facts.hold = entryFields(tab)[toggle.field] !== true;
|
|
1032
1035
|
}
|
|
1033
1036
|
return facts;
|
|
1034
1037
|
}
|
|
@@ -1040,16 +1043,16 @@ function tabRowFacts(tab: Tab): CommandArgValues {
|
|
|
1040
1043
|
* A menu is the one surface that is READ before it is used, so it is the one surface that can show
|
|
1041
1044
|
* a boolean instead of asking the author to remember it. That is an argument for the SETTER over
|
|
1042
1045
|
* the toggle, and it is why this is derived from the args rather than from a list of command ids:
|
|
1043
|
-
* `content.
|
|
1044
|
-
*
|
|
1045
|
-
*
|
|
1046
|
-
*
|
|
1047
|
-
*
|
|
1048
|
-
*
|
|
1046
|
+
* `content.setHold {hold}` names the state it reaches, so the row can say which state the tab is in
|
|
1047
|
+
* now and still land somewhere definite when clicked. `document.togglePinned` names no state — "Pin
|
|
1048
|
+
* / Unpin Document" is a title that admits it does not know which it will do — so it renders as a
|
|
1049
|
+
* plain row here. Its idempotent sibling `document.setPinned {pinned}` would state its own value,
|
|
1050
|
+
* with no edit to this file, on the day its record declares `context/tab`; that declaration lives
|
|
1051
|
+
* in `workspace/workspace.ts`, which is the only place it can be made.
|
|
1049
1052
|
*
|
|
1050
1053
|
* **It is a real checkbox row now, which it could not be under Spectrum.** `sp-menu` reassigned
|
|
1051
1054
|
* every item's role one frame after connect whenever the menu declared no `selects`, so
|
|
1052
|
-
* `role="menuitemcheckbox"` did not survive and the state had to be printed as a sentence ("
|
|
1055
|
+
* `role="menuitemcheckbox"` did not survive and the state had to be printed as a sentence ("Held:
|
|
1053
1056
|
* no") in the description line instead. `jx-menu-item` derives `role` and `aria-checked` from
|
|
1054
1057
|
* `checked` and nothing rewrites them, so the row states the boolean the way a screen reader
|
|
1055
1058
|
* already knows how to announce — and the five rows that carry no state stay plain `menuitem`s,
|
|
@@ -28,7 +28,7 @@ import { adoptCreatedProject } from "./project-adoption";
|
|
|
28
28
|
import { translateValidationError } from "./ai-tools";
|
|
29
29
|
import { validateDoc, validateProjectConfig } from "./jx-validate";
|
|
30
30
|
import { flagHardcodedTokens, formatTokenHints } from "./token-lint";
|
|
31
|
-
import type { CandidateDocument } from "./ai-write-report";
|
|
31
|
+
import type { CandidateDocument, RenderProbe } from "./ai-write-report";
|
|
32
32
|
import { invalidPathResult, normalizeRelPath } from "./project-path";
|
|
33
33
|
|
|
34
34
|
/** Directories the file tools never descend into or report. */
|
|
@@ -71,7 +71,7 @@ export interface ProjectToolsCtx {
|
|
|
71
71
|
* judges the live configuration and a candidate parsed from the model's text alike.
|
|
72
72
|
*/
|
|
73
73
|
validateProject?: (config: ProjectConfig | JsonValue) => Promise<string[]>;
|
|
74
|
-
renderCheck?:
|
|
74
|
+
renderCheck?: RenderProbe;
|
|
75
75
|
/** Live getter — the project style appears only after a project is open/bootstrapped. */
|
|
76
76
|
getProjectStyle?: () => Record<string, string> | undefined;
|
|
77
77
|
/** The open tab whose `documentPath` equals the given project-relative path, if any. */
|
|
@@ -323,7 +323,7 @@ export function registerProjectTools(
|
|
|
323
323
|
};
|
|
324
324
|
}
|
|
325
325
|
if (renderCheck) {
|
|
326
|
-
const renderResult = await renderCheck(parsed);
|
|
326
|
+
const renderResult = await renderCheck(parsed, { documentPath: relPath });
|
|
327
327
|
if (!renderResult.ok) {
|
|
328
328
|
return {
|
|
329
329
|
success: false,
|
package/src/services/ai-tools.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { validateDoc } from "./jx-validate";
|
|
|
18
18
|
import { invalidPathResult, normalizeRelPath } from "./project-path";
|
|
19
19
|
import { serializeJson } from "@jxsuite/schema/json-layout";
|
|
20
20
|
import { translateValidationError } from "./ai-write-report";
|
|
21
|
-
import type { CandidateDocument } from "./ai-write-report";
|
|
21
|
+
import type { CandidateDocument, RenderProbe } from "./ai-write-report";
|
|
22
22
|
import { studioDocumentHost, studioWriteReporter } from "./harness/studio-documents";
|
|
23
23
|
|
|
24
24
|
/* Re-exported from its new home so `ai-project-tools.ts` and the tests keep their import: the
|
|
@@ -34,7 +34,7 @@ export { translateValidationError } from "./ai-write-report";
|
|
|
34
34
|
* getTab: () => import("../tabs/tab").Tab | null;
|
|
35
35
|
* validate?: (doc: CandidateDocument) => Promise<string[]>;
|
|
36
36
|
* saveFile?: (relPath: string, content: string) => Promise<void>;
|
|
37
|
-
* renderCheck?:
|
|
37
|
+
* renderCheck?: RenderProbe;
|
|
38
38
|
* projectStyle?: Record<string, string>;
|
|
39
39
|
* getProjectStyle?: () => Record<string, string> | undefined;
|
|
40
40
|
* findOpenTab?: (path: string) => import("../tabs/tab").Tab | null;
|
|
@@ -56,7 +56,7 @@ export function registerAiTools(
|
|
|
56
56
|
getTab: () => Tab | null;
|
|
57
57
|
validate?: (doc: CandidateDocument) => Promise<string[]>;
|
|
58
58
|
saveFile?: (relPath: string, content: string) => Promise<void>;
|
|
59
|
-
renderCheck?:
|
|
59
|
+
renderCheck?: RenderProbe;
|
|
60
60
|
projectStyle?: Record<string, string> | undefined;
|
|
61
61
|
/**
|
|
62
62
|
* Live variant of `projectStyle` — takes precedence; re-read per tool call so a project
|
|
@@ -164,7 +164,7 @@ export function registerAiTools(
|
|
|
164
164
|
};
|
|
165
165
|
}
|
|
166
166
|
if (renderCheck) {
|
|
167
|
-
const renderResult = await renderCheck(content);
|
|
167
|
+
const renderResult = await renderCheck(content, { documentPath: relPath });
|
|
168
168
|
if (!renderResult.ok) {
|
|
169
169
|
return {
|
|
170
170
|
success: false,
|
|
@@ -252,7 +252,7 @@ export function registerAiTools(
|
|
|
252
252
|
};
|
|
253
253
|
}
|
|
254
254
|
if (renderCheck) {
|
|
255
|
-
const renderResult = await renderCheck(content);
|
|
255
|
+
const renderResult = await renderCheck(content, { documentPath: relPath });
|
|
256
256
|
if (!renderResult.ok) {
|
|
257
257
|
return {
|
|
258
258
|
success: false,
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import type { ToolResult } from "@jxsuite/ai/tools";
|
|
25
|
-
import type { JsonValue, JxMutableNode } from "@jxsuite/schema/types";
|
|
25
|
+
import type { JsonValue, JxDocumentValue, JxMutableNode } from "@jxsuite/schema/types";
|
|
26
26
|
import { toRaw } from "../reactivity";
|
|
27
27
|
import type { Tab } from "../tabs/tab";
|
|
28
28
|
import { flagHardcodedTokens, formatTokenHints } from "./token-lint";
|
|
@@ -37,12 +37,28 @@ export type RenderVerdict = { ok: true } | { ok: false; error: string };
|
|
|
37
37
|
*/
|
|
38
38
|
export type CandidateDocument = JxMutableNode | JsonValue;
|
|
39
39
|
|
|
40
|
+
/** Where a document under a render check lives: what its route and relative references resolve from. */
|
|
41
|
+
export interface RenderSource {
|
|
42
|
+
/** Its project-relative path, once it has one. */
|
|
43
|
+
documentPath?: string | null | undefined;
|
|
44
|
+
/** A format-class page's frontmatter, which is where its `$paths` is. */
|
|
45
|
+
frontmatter?: Record<string, JxDocumentValue | undefined> | null | undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A detached-DOM render probe. */
|
|
49
|
+
export type RenderProbe = (doc: CandidateDocument, source?: RenderSource) => Promise<RenderVerdict>;
|
|
50
|
+
|
|
51
|
+
/** Where an open tab's document lives, as a render check reads it. */
|
|
52
|
+
function renderSourceOf(tab: Tab): RenderSource {
|
|
53
|
+
return { documentPath: tab.documentPath, frontmatter: tab.doc.content.frontmatter };
|
|
54
|
+
}
|
|
55
|
+
|
|
40
56
|
/** What a write reporter needs. Every member is what `registerAiTools` already takes by injection. */
|
|
41
57
|
export interface WriteReportDeps {
|
|
42
58
|
/** Schema errors for a document, as raw ajv sentences. */
|
|
43
59
|
validate: (doc: CandidateDocument) => Promise<string[]>;
|
|
44
60
|
/** The detached-DOM render probe, when the host has one. */
|
|
45
|
-
renderCheck?:
|
|
61
|
+
renderCheck?: RenderProbe | undefined;
|
|
46
62
|
/** The project's design tokens, read per call so a project opened mid-session contributes. */
|
|
47
63
|
getProjectStyle?: (() => Record<string, string> | undefined) | undefined;
|
|
48
64
|
}
|
|
@@ -122,7 +138,9 @@ export function translateValidationError(rawError: string): string {
|
|
|
122
138
|
export async function snapshotBeforeWrite(tab: Tab, deps: WriteReportDeps): Promise<WriteSnapshot> {
|
|
123
139
|
const rawBefore = toRaw(tab.doc.document);
|
|
124
140
|
const errors = new Set(await deps.validate(rawBefore));
|
|
125
|
-
const rendered = deps.renderCheck
|
|
141
|
+
const rendered = deps.renderCheck
|
|
142
|
+
? await deps.renderCheck(rawBefore, renderSourceOf(tab))
|
|
143
|
+
: { ok: true };
|
|
126
144
|
return { errors, renderOk: rendered.ok };
|
|
127
145
|
}
|
|
128
146
|
|
|
@@ -159,7 +177,7 @@ export async function reportDocumentWrite(
|
|
|
159
177
|
}
|
|
160
178
|
|
|
161
179
|
if (deps.renderCheck && before.renderOk) {
|
|
162
|
-
const renderResult = await deps.renderCheck(rawAfter);
|
|
180
|
+
const renderResult = await deps.renderCheck(rawAfter, renderSourceOf(tab));
|
|
163
181
|
if (!renderResult.ok) {
|
|
164
182
|
return {
|
|
165
183
|
success: false,
|