@jxsuite/studio 10.4.0 → 10.6.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/data/locales/en/elements.json +434 -0
- package/data/webdata.json +116 -139
- package/dist/chunks/2020-rqm7qrje.js +4 -0
- package/dist/chunks/adapter-cyy8vbcx.js +4 -0
- package/dist/chunks/add-new-flow-8cgtejv2.js +4 -0
- package/dist/chunks/{cf-account-picker-xb8vvt2h.js → cf-account-picker-v3qd2dhw.js} +3 -3
- package/dist/chunks/cf-settings-1fnw874z.js +4 -0
- package/dist/chunks/{color-area-h8dbasp1.js → color-area-frd9y9q6.js} +2 -2
- package/dist/chunks/color-field-rebpbe33.js +4 -0
- package/dist/chunks/{color-slider-nz2mrzky.js → color-slider-6bzmk3s7.js} +2 -2
- package/dist/chunks/color-t2bnnj8y.js +4 -0
- package/dist/chunks/{combobox-6hs00996.js → combobox-c49x7gf1.js} +3 -3
- package/dist/chunks/components-y8yhdd7d.js +4 -0
- package/dist/chunks/content-source-9e7qvmsf.js +4 -0
- package/dist/chunks/convert-to-repeater-pd2w4z21.js +4 -0
- package/dist/chunks/editor-mb9jzdrw.js +4 -0
- package/dist/chunks/entry-commands-at0wnx3f.js +4 -0
- package/dist/chunks/extension-sections-4z9qq0gh.js +4 -0
- package/dist/chunks/file-ops-dtdvjmza.js +4 -0
- package/dist/chunks/files-8pf70854.js +4 -0
- package/dist/chunks/format-host-fd3m2y2a.js +4 -0
- package/dist/chunks/fs-events-hrk29vcv.js +4 -0
- package/dist/chunks/grid-open-bh74v82z.js +4 -0
- package/dist/chunks/{grid-view-2f03wcgw.js → grid-view-a51mgzmq.js} +17 -17
- package/dist/chunks/{icons-gb967qc8.js → icons-qp8fn1k8.js} +2 -2
- package/dist/chunks/{iframe-entry-btck0e9s.js → iframe-entry-mpwe5pwk.js} +4 -4
- package/dist/chunks/{iframe-entry-2x9bn5ng.js → iframe-entry-msd43x9h.js} +4 -4
- package/dist/chunks/iframe-entry-rt8jz5t8.js +5 -0
- package/dist/chunks/{iframe-entry-b5qgggyp.js → iframe-entry-v40cr813.js} +4 -4
- package/dist/chunks/iframe-host-q0s0wwy3.js +4 -0
- package/dist/chunks/{image-bake-4afr4y6a.js → image-bake-q9ajdjsj.js} +3 -3
- package/dist/chunks/{image-editor-x7t8jbjn.js → image-editor-ke9std9b.js} +3 -3
- package/dist/chunks/index-57ang7ma.js +4 -0
- package/dist/chunks/index-eytr7av4.js +17 -0
- package/dist/chunks/{index-yjrfeks8.js → index-x495reff.js} +3 -3
- package/dist/chunks/{javascript-hy2mws8v.js → javascript-qe34bvaz.js} +3 -3
- package/dist/chunks/jsonMode-m8rseh31.js +13 -0
- package/dist/chunks/jx-validate-bbd1mary.js +4 -0
- package/dist/chunks/layers-panel-wcjtdqsa.js +4 -0
- package/dist/chunks/layers-yscpys6q.js +4 -0
- package/dist/chunks/{listbox-7gka1h86.js → listbox-tktzq5xj.js} +2 -2
- package/dist/chunks/lit-html-qm5wbhqz.js +4 -0
- package/dist/chunks/media-library-2hfff88g.js +4 -0
- package/dist/chunks/media-open-4pb05cne.js +4 -0
- package/dist/chunks/media-picker-bzktmmwr.js +4 -0
- package/dist/chunks/menu-xc3cbgsd.js +4 -0
- package/dist/chunks/{monaco-binding-a51py33c.js → monaco-binding-7t38arh9.js} +3 -3
- package/dist/chunks/monaco-buffer-3vz94c9f.js +4 -0
- package/dist/chunks/monaco-setup-fstdew02.js +4 -0
- package/dist/chunks/notify-gbdcckrh.js +4 -0
- package/dist/chunks/parse-fpdzgk4c.js +4 -0
- package/dist/chunks/platform-9g72gdqr.js +4 -0
- package/dist/chunks/popover-cnmsav6b.js +4 -0
- package/dist/chunks/publish-panel-91x1cb5d.js +4 -0
- package/dist/chunks/publish-quick-e2dmf7eg.js +4 -0
- package/dist/chunks/publish-workspace-1x8mnzrj.js +4 -0
- package/dist/chunks/quick-search-2m4y6p1x.js +4 -0
- package/dist/chunks/references-yg1g9rwx.js +4 -0
- package/dist/chunks/right-panel-7mfyhzrk.js +4 -0
- package/dist/chunks/source-control-rrc4jgp5.js +4 -0
- package/dist/chunks/{split-m5rwn31z.js → split-d15yencd.js} +2 -2
- package/dist/chunks/studio-0jhhznxy.js +9 -0
- package/dist/chunks/studio-0nb57pjg.js +7 -0
- package/dist/chunks/{studio-s24mhq0z.js → studio-0wa4wv4s.js} +4 -4
- package/dist/chunks/studio-0z8kp4xk.js +5 -0
- package/dist/chunks/{studio-g0hxtcga.js → studio-18gc5t3v.js} +4 -4
- package/dist/chunks/{studio-4fnb5648.js → studio-1b79rpcb.js} +4 -4
- package/dist/chunks/studio-211q5j7m.js +4 -0
- package/dist/chunks/studio-213pyfxh.js +16 -0
- package/dist/chunks/studio-2b1smjvp.js +5 -0
- package/dist/chunks/{studio-nwf0c32p.js → studio-2v6tc9jp.js} +4 -4
- package/dist/chunks/studio-30j0wz99.js +5 -0
- package/dist/chunks/studio-3kmqhwtt.js +5 -0
- package/dist/chunks/studio-44drsmhw.js +5 -0
- package/dist/chunks/{studio-2pk7kc7z.js → studio-495b6f2r.js} +4 -4
- package/dist/chunks/{studio-jma2q8r9.js → studio-4gthb2fd.js} +4 -4
- package/dist/chunks/{studio-0gvvqj3h.js → studio-4xadhxj1.js} +4 -4
- package/dist/chunks/{studio-nvcn2ykc.js → studio-52mzbxqm.js} +4 -4
- package/dist/chunks/{studio-v7a7yb5w.js → studio-56mmnsw8.js} +7 -7
- package/dist/chunks/studio-59hz8f4r.js +5 -0
- package/dist/chunks/{studio-sce30r7b.js → studio-5k9mfafh.js} +3 -3
- package/dist/chunks/{studio-rr00ex23.js → studio-5qez462c.js} +6 -6
- package/dist/chunks/studio-6tnpg0vx.js +5 -0
- package/dist/chunks/studio-6w8mt734.js +6 -0
- package/dist/chunks/studio-7hm04x5z.js +5 -0
- package/dist/chunks/studio-8atmfjjm.js +7 -0
- package/dist/chunks/{studio-wmjmsw0p.js → studio-8n99z71s.js} +4 -4
- package/dist/chunks/studio-8vmad6x2.js +5 -0
- package/dist/chunks/studio-93p5k4r4.js +5 -0
- package/dist/chunks/{studio-2x972v58.js → studio-9gb0m81w.js} +4 -4
- package/dist/chunks/studio-a4ymg4vd.js +5 -0
- package/dist/chunks/studio-anc8067x.js +5 -0
- package/dist/chunks/{studio-2e4hry72.js → studio-dbdt67kr.js} +5 -5
- package/dist/chunks/studio-dbex091t.js +5 -0
- package/dist/chunks/{studio-amaz545p.js → studio-dj8ctxey.js} +4 -4
- package/dist/chunks/{studio-k2evngrj.js → studio-ed9bz68j.js} +5 -5
- package/dist/chunks/{studio-4bgvvzhv.js → studio-fe2d1fp5.js} +4 -4
- package/dist/chunks/{studio-45mj4jqe.js → studio-fgp4cyy3.js} +4 -4
- package/dist/chunks/studio-ft706q02.js +5 -0
- package/dist/chunks/studio-g79s1dyg.js +6 -0
- package/dist/chunks/{studio-pj216fmn.js → studio-gahswxp4.js} +4 -4
- package/dist/chunks/studio-gbqc12bk.js +5 -0
- package/dist/chunks/studio-gxphbetb.js +6 -0
- package/dist/chunks/studio-hdf01j7q.js +7 -0
- package/dist/chunks/studio-hf259xzd.js +48 -0
- package/dist/chunks/studio-jbva4wxw.js +5 -0
- package/dist/chunks/studio-jj9gf1qc.js +5 -0
- package/dist/chunks/studio-jsjg3mjv.js +5 -0
- package/dist/chunks/studio-jtvb8hd7.js +5 -0
- package/dist/chunks/{studio-g9brbewz.js → studio-kp4vzrct.js} +4 -4
- package/dist/chunks/studio-kpx02gaa.js +7 -0
- package/dist/chunks/{studio-3na1r48j.js → studio-kv1728ex.js} +8 -8
- package/dist/chunks/studio-m25ry0mf.js +5 -0
- package/dist/chunks/studio-mcbwmkyh.js +5 -0
- package/dist/chunks/studio-meajkr51.js +5 -0
- package/dist/chunks/studio-mkjrjpr2.js +6 -0
- package/dist/chunks/{studio-eqcddvc2.js → studio-mr084886.js} +201 -201
- package/dist/chunks/studio-mrb10hv3.js +5 -0
- package/dist/chunks/{studio-0939t1q1.js → studio-nhpg405j.js} +4 -4
- package/dist/chunks/studio-nsec0hxm.js +64 -0
- package/dist/chunks/{studio-xx3r3sb0.js → studio-nv0mh0p5.js} +4 -4
- package/dist/chunks/studio-pafmb06w.js +5 -0
- package/dist/chunks/{studio-pm17wdc4.js → studio-pgftd79b.js} +4 -4
- package/dist/chunks/{studio-4zd6an2b.js → studio-pk961xgz.js} +4 -4
- package/dist/chunks/studio-pzbr7g05.js +5 -0
- package/dist/chunks/studio-qcc5hsw2.js +10 -0
- package/dist/chunks/studio-qfq58c8n.js +5 -0
- package/dist/chunks/studio-qvb2jr83.js +5 -0
- package/dist/chunks/studio-r85zw6ay.js +5 -0
- package/dist/chunks/studio-s0kwce5w.js +9 -0
- package/dist/chunks/studio-s6ymkf3q.js +14 -0
- package/dist/chunks/{studio-0x6f3nk9.js → studio-s7p5ccxa.js} +4 -4
- package/dist/chunks/{studio-1410q5dw.js → studio-s9jjap3x.js} +4 -4
- package/dist/chunks/{studio-evq47j2z.js → studio-t5hde1ws.js} +4 -4
- package/dist/chunks/studio-vj9yv897.js +5 -0
- package/dist/chunks/studio-wmak8xjw.js +5 -0
- package/dist/chunks/{studio-bnc50f9h.js → studio-wqsmvtqj.js} +4 -4
- package/dist/chunks/studio-x3z3rhej.js +5 -0
- package/dist/chunks/{studio-bg8pzprg.js → studio-xb55kpmy.js} +4 -4
- package/dist/chunks/studio-xxtdajmv.js +12 -0
- package/dist/chunks/studio-xxttcym1.js +5 -0
- package/dist/chunks/{studio-90zkgne5.js → studio-y2zsnzye.js} +7 -7
- package/dist/chunks/{studio-3qr08sxq.js → studio-y3tjh2xb.js} +3 -3
- package/dist/chunks/studio-y4f1rdsj.js +5 -0
- package/dist/chunks/studio-ybkd5w8k.js +5 -0
- package/dist/chunks/{studio-tpxcryf2.js → studio-yetr4gg1.js} +4 -4
- package/dist/chunks/studio-z295anhs.js +6 -0
- package/dist/chunks/studio-z89e6wpg.js +9 -0
- package/dist/chunks/{studio-5tet77de.js → studio-zbwr6prc.js} +3 -3
- package/dist/chunks/{studio-daksw8kh.js → studio-zdbsv5q2.js} +4 -4
- package/dist/chunks/{studio-abh9kfdh.js → studio-zvthj8hg.js} +4 -4
- package/dist/chunks/toast-2kn2vrn4.js +4 -0
- package/dist/chunks/{toast-host-g3r2s0kn.js → toast-host-3rxeyj3d.js} +3 -3
- package/dist/chunks/{tooltip-dj1pvgz7.js → tooltip-ghkxkasp.js} +3 -3
- package/dist/chunks/{tree-j0ebwr5s.js → tree-vw4nc63s.js} +2 -2
- package/dist/chunks/tsMode-7gngmamr.js +14 -0
- package/dist/chunks/workspace-gv0hxgsm.js +4 -0
- package/dist/chunks/workspace-home-j7e0mjy5.js +4 -0
- package/dist/chunks/{ws-client-arz2qsy9.js → ws-client-vs11sxc5.js} +3 -3
- package/dist/iframe-entry.js +14 -14
- package/dist/manifest.json +84 -83
- package/dist/studio.js +99 -98
- package/dist/workers/ts.worker.js +3 -3
- package/package.json +12 -12
- 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/browse/preview-style.ts +2 -1
- package/src/canvas/canvas-live-render.ts +2 -5
- package/src/canvas/canvas-page-context.ts +5 -0
- package/src/canvas/canvas-render.ts +6 -1
- package/src/canvas/iframe-drop.ts +84 -1
- package/src/canvas/iframe-entry.ts +9 -6
- package/src/canvas/iframe-render.ts +13 -0
- package/src/collab/collab-session.ts +2 -2
- package/src/commands/levels.ts +1 -1
- 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/create/add-new-flow.ts +2 -1
- package/src/editor/convert-targets.ts +2 -23
- package/src/editor/inline-edit.ts +6 -1
- package/src/editor/repeater-scope.ts +333 -74
- package/src/editor/slash-menu.ts +35 -17
- package/src/files/files.ts +18 -0
- package/src/format/format-choices.ts +5 -5
- package/src/format/format-host.ts +35 -3
- package/src/grid/redirects-grid.ts +4 -4
- package/src/grid/sources/content-source.ts +42 -66
- package/src/locale/element-names.ts +131 -0
- package/src/panels/block-action-bar.ts +3 -4
- package/src/panels/dnd.ts +3 -1
- package/src/panels/elements-panel.ts +93 -36
- package/src/panels/events-panel.ts +6 -2
- package/src/panels/formula-workspace.ts +9 -0
- package/src/panels/imports-panel.ts +3 -2
- package/src/panels/navigator-panels.ts +10 -10
- package/src/panels/panel-registry.ts +3 -3
- package/src/panels/properties-panel.ts +127 -35
- package/src/panels/site-panel.ts +2 -1
- package/src/panels/statement-editor.ts +4 -0
- package/src/panels/style-panel.ts +2 -1
- package/src/panels/styles-panel.ts +2 -2
- package/src/panels/tab-strip.ts +29 -26
- package/src/platforms/cloud.ts +5 -2
- package/src/services/ai-command-tools.ts +2 -1
- package/src/services/ai-project-tools.ts +21 -8
- 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/jxn-language.ts +110 -0
- package/src/services/model-uri.ts +3 -0
- package/src/services/monaco-setup.ts +6 -2
- package/src/services/render-critic.ts +234 -12
- package/src/services/tool-executor.ts +16 -8
- package/src/settings/contributed-section.ts +2 -1
- package/src/settings/extension-commands.ts +2 -1
- package/src/settings/general-settings.ts +2 -2
- package/src/settings/settings-document.ts +5 -5
- package/src/shell.ts +1 -1
- package/src/studio.ts +28 -11
- package/src/style/variables-view.ts +2 -2
- 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/panel-elements.json +466 -206
- package/src/surfaces/panel-elements.ts +61 -25
- package/src/surfaces/tab-strip.json +5 -5
- package/src/surfaces/tab-strip.ts +2 -2
- package/src/tabs/project-config.ts +13 -13
- package/src/tabs/tab.ts +7 -2
- package/src/types.ts +13 -2
- 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/src/view.ts +9 -2
- package/src/workspace/tab-workspace.ts +2 -2
- package/src/workspace/workspace-back.ts +2 -2
- package/dist/chunks/2020-906vx2dq.js +0 -4
- package/dist/chunks/adapter-hnvad61x.js +0 -4
- package/dist/chunks/add-new-flow-v0pm96mr.js +0 -4
- package/dist/chunks/cf-settings-mkbcz4z2.js +0 -4
- package/dist/chunks/color-04361ttk.js +0 -4
- package/dist/chunks/color-field-qv5r2b16.js +0 -4
- package/dist/chunks/components-w5zqwva1.js +0 -4
- package/dist/chunks/content-source-g38w0d3f.js +0 -4
- package/dist/chunks/convert-to-repeater-r8sfxjjm.js +0 -4
- package/dist/chunks/editor-wvf5e6j9.js +0 -4
- package/dist/chunks/entry-commands-t13dyw3c.js +0 -4
- package/dist/chunks/extension-sections-vjzw46xb.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-entry-42hpmwz5.js +0 -5
- 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/parse-hee7ne9b.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-quick-4fwngwrw.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-61rg2cz5.js +0 -5
- package/dist/chunks/studio-7rrdbvex.js +0 -5
- package/dist/chunks/studio-9c6cbq2c.js +0 -4
- package/dist/chunks/studio-9dq7rwc7.js +0 -5
- package/dist/chunks/studio-9gr7xs9r.js +0 -6
- package/dist/chunks/studio-a88ej93p.js +0 -7
- package/dist/chunks/studio-acer0g8w.js +0 -5
- package/dist/chunks/studio-az3wvyh2.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-gnazwtv3.js +0 -5
- package/dist/chunks/studio-hfqpfmpk.js +0 -5
- package/dist/chunks/studio-hh394phe.js +0 -5
- 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-k0xzrse2.js +0 -64
- 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-r6mx7vm5.js +0 -7
- package/dist/chunks/studio-swedpd82.js +0 -5
- package/dist/chunks/studio-tmfcn347.js +0 -6
- package/dist/chunks/studio-v5wktj6f.js +0 -5
- package/dist/chunks/studio-vpevkfxw.js +0 -5
- 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/toast-w11qpkyz.js +0 -4
- 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
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
/**
|
|
3
|
-
* Elements panel — the Insert palette: the blocks and components a page is built out of
|
|
3
|
+
* Elements panel — the Insert palette: the blocks and components a page is built out of, on two
|
|
4
|
+
* tabs, Elements and Components (§5.3). Each element card is named in the reader's language
|
|
5
|
+
* (`locale/element-names.ts`), with its tag beside the name, and the categories lead with what a
|
|
6
|
+
* first page is made of: text, lists, media and layout before forms, tables and inline formatting.
|
|
4
7
|
*
|
|
5
8
|
* **The body is a Jx document** (`surfaces/panel-elements.json`, mounted by
|
|
6
9
|
* `surfaces/panel-elements.ts`), so `render` draws nothing and `afterRender` mounts. What stays
|
|
@@ -30,6 +33,7 @@ import { mountElementsSurface } from "../surfaces/panel-elements";
|
|
|
30
33
|
import { mutateInsertNode, transactDoc } from "../tabs/transact";
|
|
31
34
|
import { primarySelection } from "../tabs/selection";
|
|
32
35
|
import { registerPanel } from "./panel-registry";
|
|
36
|
+
import { categoryName, elementDescription, elementName } from "../locale/element-names";
|
|
33
37
|
import { activeTab } from "../workspace/workspace";
|
|
34
38
|
import { view } from "../view";
|
|
35
39
|
|
|
@@ -38,8 +42,10 @@ import type {
|
|
|
38
42
|
ComponentCardView,
|
|
39
43
|
ElementCategoryView,
|
|
40
44
|
ElementsActions,
|
|
45
|
+
ElementsEmptyState,
|
|
41
46
|
ElementsSurfaceHandle,
|
|
42
47
|
ElementsValues,
|
|
48
|
+
InsertTab,
|
|
43
49
|
} from "../surfaces/panel-elements";
|
|
44
50
|
import type { JxElement, JxMutableNode } from "@jxsuite/schema/types";
|
|
45
51
|
import type { NavigatorPanelContext, NavigatorPanelDeps, PanelBody } from "./panel-registry";
|
|
@@ -49,8 +55,13 @@ interface ElementsWebdata {
|
|
|
49
55
|
elements?: Record<string, { tag: string }[]>;
|
|
50
56
|
}
|
|
51
57
|
|
|
52
|
-
/**
|
|
53
|
-
|
|
58
|
+
/**
|
|
59
|
+
* The categories that start closed: inline formatting and the advanced remainder (§5.3). Both are
|
|
60
|
+
* long, and neither is where somebody building their first page looks, so a newcomer sees the
|
|
61
|
+
* everyday blocks first and opens these when they want them. A filter opens them too, unless the
|
|
62
|
+
* reader has closed one by hand.
|
|
63
|
+
*/
|
|
64
|
+
const CLOSED_AT_FIRST: ReadonlySet<string> = new Set(["inline", "advanced"]);
|
|
54
65
|
|
|
55
66
|
/**
|
|
56
67
|
* What the palette is built from, held at module scope rather than closed over at mount.
|
|
@@ -76,18 +87,37 @@ function needle(): string {
|
|
|
76
87
|
return view.elementsFilter.toLowerCase();
|
|
77
88
|
}
|
|
78
89
|
|
|
79
|
-
/**
|
|
90
|
+
/** Whether a section is drawn open: the reader's own choice, else its default. */
|
|
91
|
+
function sectionOpen(key: string, filter: string): boolean {
|
|
92
|
+
return view.elementsOpen.get(key) ?? (filter !== "" || !CLOSED_AT_FIRST.has(key));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The categories that still have something in them, with the filter applied, in the order
|
|
97
|
+
* `webdata.elements` declares them (§5.3). A card matches on its name in the reader's language or
|
|
98
|
+
* on its tag, so "image" and "img" both find the same card.
|
|
99
|
+
*/
|
|
80
100
|
function categoryViews(webdata: ElementsWebdata, filter: string): ElementCategoryView[] {
|
|
81
101
|
const sections: ElementCategoryView[] = [];
|
|
82
|
-
for (const [
|
|
83
|
-
const
|
|
102
|
+
for (const [key, elements] of Object.entries(webdata.elements ?? {})) {
|
|
103
|
+
const cards = elements.map(({ tag }) => ({
|
|
104
|
+
description: elementDescription(tag),
|
|
105
|
+
name: elementName(tag),
|
|
106
|
+
tag,
|
|
107
|
+
}));
|
|
108
|
+
const matched = filter
|
|
109
|
+
? cards.filter(
|
|
110
|
+
(card) => card.tag.includes(filter) || card.name.toLowerCase().includes(filter),
|
|
111
|
+
)
|
|
112
|
+
: cards;
|
|
84
113
|
if (matched.length === 0) {
|
|
85
114
|
continue;
|
|
86
115
|
}
|
|
87
116
|
sections.push({
|
|
88
|
-
elements: matched
|
|
89
|
-
|
|
90
|
-
|
|
117
|
+
elements: matched,
|
|
118
|
+
key,
|
|
119
|
+
name: categoryName(key),
|
|
120
|
+
open: sectionOpen(key, filter),
|
|
91
121
|
});
|
|
92
122
|
}
|
|
93
123
|
return sections;
|
|
@@ -145,23 +175,51 @@ function componentViews(filter: string): ComponentCardView[] {
|
|
|
145
175
|
}));
|
|
146
176
|
}
|
|
147
177
|
|
|
178
|
+
/** Which body a tab shows: its cards, or why it has none. */
|
|
179
|
+
function emptyState(count: number, filter: string): ElementsEmptyState {
|
|
180
|
+
if (count > 0) {
|
|
181
|
+
return "none";
|
|
182
|
+
}
|
|
183
|
+
return filter ? "filtered" : "empty";
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** The other tab, which a filtered-out tab offers to switch to. */
|
|
187
|
+
function otherTab(tab: InsertTab): InsertTab {
|
|
188
|
+
return tab === "elements" ? "components" : "elements";
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** "Show 1 matching component", "Show 3 matching elements": the other tab's offer. */
|
|
192
|
+
function otherMatchesLabel(tab: InsertTab, count: number): string {
|
|
193
|
+
const noun = tab === "elements" ? "element" : "component";
|
|
194
|
+
return `Show ${count} matching ${noun}${count === 1 ? "" : "s"}`;
|
|
195
|
+
}
|
|
196
|
+
|
|
148
197
|
/** Everything the document draws, as one value. */
|
|
149
198
|
function panelValues(source: NavigatorPanelDeps): ElementsValues {
|
|
150
199
|
const filter = needle();
|
|
200
|
+
const tab = view.insertTab;
|
|
151
201
|
/* SAFETY: `webdata` is wired in studio.ts from the bundled data/webdata.json, whose `elements`
|
|
152
|
-
maps each category
|
|
202
|
+
maps each category key to an array of `{ tag }` entries. */
|
|
153
203
|
const categories = categoryViews(source.webdata as ElementsWebdata, filter);
|
|
154
204
|
const components = componentViews(filter);
|
|
155
|
-
const
|
|
205
|
+
const elementCount = categories.reduce((sum, section) => sum + section.elements.length, 0);
|
|
206
|
+
const counts: Record<InsertTab, number> = {
|
|
207
|
+
components: components.length,
|
|
208
|
+
elements: elementCount,
|
|
209
|
+
};
|
|
210
|
+
const other = otherTab(tab);
|
|
211
|
+
const showOffer = filter !== "" && counts[tab] === 0 && counts[other] > 0;
|
|
156
212
|
return {
|
|
157
213
|
categories,
|
|
158
214
|
components,
|
|
159
|
-
|
|
160
|
-
|
|
215
|
+
componentsEmpty: emptyState(components.length, filter),
|
|
216
|
+
elementsEmpty: emptyState(elementCount, filter),
|
|
161
217
|
/* The reader's own text, not `filter`: the field is bound to this, and a scope holding the
|
|
162
218
|
lower-cased form would rewrite what was typed on every keystroke. */
|
|
163
219
|
filter: view.elementsFilter,
|
|
164
|
-
|
|
220
|
+
filterLabel: tab === "elements" ? "Filter elements" : "Filter components",
|
|
221
|
+
otherMatches: showOffer ? otherMatchesLabel(other, counts[other]) : "",
|
|
222
|
+
tab,
|
|
165
223
|
};
|
|
166
224
|
}
|
|
167
225
|
|
|
@@ -207,6 +265,17 @@ function refresh(): void {
|
|
|
207
265
|
|
|
208
266
|
/** What the document's controls do. Every one of them is a decision this module owns. */
|
|
209
267
|
const ACTIONS: ElementsActions = {
|
|
268
|
+
/* The strip announces any value its tabs carry; only the two this panel draws are taken. */
|
|
269
|
+
selectTab: (tab) => {
|
|
270
|
+
if (tab === "elements" || tab === "components") {
|
|
271
|
+
view.insertTab = tab;
|
|
272
|
+
refresh();
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
showOtherTab: () => {
|
|
276
|
+
view.insertTab = otherTab(view.insertTab);
|
|
277
|
+
refresh();
|
|
278
|
+
},
|
|
210
279
|
clearFilter: () => {
|
|
211
280
|
view.elementsFilter = "";
|
|
212
281
|
refresh();
|
|
@@ -223,31 +292,20 @@ const ACTIONS: ElementsActions = {
|
|
|
223
292
|
insertAtSelection(panelDeps.defaultDef(tag) as JxMutableNode);
|
|
224
293
|
}
|
|
225
294
|
},
|
|
226
|
-
setComponentsOpen: (open) => {
|
|
227
|
-
setCollapsed(COMPONENTS, !open);
|
|
228
|
-
refresh();
|
|
229
|
-
},
|
|
230
295
|
/* The echo: what the field now holds goes into the state the palette is filtered by, before
|
|
231
296
|
anything is decided about it. */
|
|
232
297
|
setFilter: (value) => {
|
|
233
298
|
view.elementsFilter = value;
|
|
234
299
|
refresh();
|
|
235
300
|
},
|
|
236
|
-
|
|
237
|
-
|
|
301
|
+
/* Remembered across repaints and panel switches, and from then on it outranks the section's
|
|
302
|
+
default, the filter's included. */
|
|
303
|
+
setSectionOpen: (key, open) => {
|
|
304
|
+
view.elementsOpen.set(key, open);
|
|
238
305
|
refresh();
|
|
239
306
|
},
|
|
240
307
|
};
|
|
241
308
|
|
|
242
|
-
/** Remember a section's disclosure across repaints and across panel switches. */
|
|
243
|
-
function setCollapsed(name: string, collapsed: boolean): void {
|
|
244
|
-
if (collapsed) {
|
|
245
|
-
view.elementsCollapsed.add(name);
|
|
246
|
-
} else {
|
|
247
|
-
view.elementsCollapsed.delete(name);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
309
|
/**
|
|
252
310
|
* Draw the panel body — mounting the document the first time, updating it every time after.
|
|
253
311
|
*
|
|
@@ -277,13 +335,13 @@ export function mountElementsPanel(ctx: NavigatorPanelContext, host: HTMLElement
|
|
|
277
335
|
}
|
|
278
336
|
|
|
279
337
|
/**
|
|
280
|
-
* Contribute the Insert panel —
|
|
338
|
+
* Contribute the Insert panel — the first button of the rail's DOCUMENT group (§5.1).
|
|
281
339
|
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
340
|
+
* It is the place an author picks an element or component from and drags it onto the canvas, so it
|
|
341
|
+
* stays on screen while they work: a card is a choice to look at, compare and drag, and a palette
|
|
342
|
+
* that has to be summoned by name before it can be seen is one only a reader of the command list
|
|
343
|
+
* finds. The slash menu and the canvas `+` stay the inserts at the caret; this is the one an author
|
|
344
|
+
* browses.
|
|
287
345
|
*
|
|
288
346
|
* `level: "document"`, per §13.2's own worked example: it READS the project's component registry
|
|
289
347
|
* and WRITES the document tree.
|
|
@@ -295,7 +353,6 @@ export function registerInsertPanel(): void {
|
|
|
295
353
|
level: "document",
|
|
296
354
|
dock: "navigator",
|
|
297
355
|
icon: "grid-four",
|
|
298
|
-
rail: false,
|
|
299
356
|
// The body is a document, so lit draws nothing and the mount happens against the painted DOM.
|
|
300
357
|
// `afterRender` runs on every repaint; {@link mountElementsPanel} is idempotent.
|
|
301
358
|
render: (): PanelBody => nothing,
|
|
@@ -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,
|
|
@@ -50,6 +50,7 @@ import type {
|
|
|
50
50
|
} from "../surfaces/panel-imports";
|
|
51
51
|
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
52
52
|
import type { SurfaceOption } from "../ui/surface";
|
|
53
|
+
import { isProjectConfigPath } from "@jxsuite/schema/project-file";
|
|
53
54
|
|
|
54
55
|
/** What the panel is drawn against. `left-panel.ts`'s context, reduced to what this reads. */
|
|
55
56
|
export interface ImportsContext {
|
|
@@ -204,7 +205,7 @@ function documentView(ctx: ImportsContext): ImportsView {
|
|
|
204
205
|
|
|
205
206
|
/** What the panel shows right now. `project.json` is the one document with the other mood. */
|
|
206
207
|
function view(ctx: ImportsContext): ImportsView {
|
|
207
|
-
return ctx.documentPath
|
|
208
|
+
return isProjectConfigPath(ctx.documentPath) ? siteView() : documentView(ctx);
|
|
208
209
|
}
|
|
209
210
|
|
|
210
211
|
/** Push the current projection into the standing surface, in this turn. */
|
|
@@ -295,7 +296,7 @@ async function onToggleComponent(id: string, checked: boolean): Promise<void> {
|
|
|
295
296
|
if (!ctx || !comp) {
|
|
296
297
|
return;
|
|
297
298
|
}
|
|
298
|
-
if (ctx.documentPath
|
|
299
|
+
if (isProjectConfigPath(ctx.documentPath)) {
|
|
299
300
|
const before = siteElements();
|
|
300
301
|
const updated = checked
|
|
301
302
|
? enableElement(before, comp, null)
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* the second definition site the registry exists to prevent.
|
|
9
9
|
*
|
|
10
10
|
* **Registration order is rail order** within a level group: PROJECT (Site, then Files with the
|
|
11
|
-
* developer panels on) above the divider, DOCUMENT (Outline · Data, then Packages with the
|
|
12
|
-
* developer panels on) below it (§5.1). Search
|
|
13
|
-
*
|
|
11
|
+
* developer panels on) above the divider, DOCUMENT (Insert · Outline · Data, then Packages with the
|
|
12
|
+
* developer panels on) below it (§5.1). Search and Languages are registered rail-less: reachable by
|
|
13
|
+
* `view.setActivity` and the palette, with no button.
|
|
14
14
|
*
|
|
15
15
|
* **Every rail button opens the Navigator.** Problems used to hold the fourth PROJECT slot while
|
|
16
16
|
* its body was drawn in the Bottom dock, and paying for that took a per-dock branch in three
|
|
@@ -84,13 +84,13 @@ export function registerNavigatorPanels(): void {
|
|
|
84
84
|
registerI18nPanel();
|
|
85
85
|
|
|
86
86
|
// ── DOCUMENT group ──────────────────────────────────────────────────────────
|
|
87
|
+
/* Insert leads the group: it is where a page's elements and components are picked from and
|
|
88
|
+
dragged onto the canvas, the verb a Design session is mostly made of (§5.1, §5.3). */
|
|
89
|
+
registerInsertPanel();
|
|
87
90
|
registerLayersPanel();
|
|
88
91
|
registerDataPanel();
|
|
89
92
|
registerPackagesPanel();
|
|
90
93
|
|
|
91
|
-
// ── Off-rail: reachable by command and palette, no rail button ──────────────
|
|
92
|
-
registerInsertPanel();
|
|
93
|
-
|
|
94
94
|
// ── The Bottom dock's tabs (§16.3): Problems · Logic · Activity ──────────────
|
|
95
95
|
// Composed here, in the one place panel records are gathered, so the shell has ONE composition
|
|
96
96
|
// Site rather than one per dock. Importing the module is also what attaches the dock to the
|
|
@@ -122,10 +122,10 @@ export function navigatorPanelSet(): readonly PanelRecord[] {
|
|
|
122
122
|
* The roster `panel.focus.*` is generated from: rail order first, then the rail-less Navigator
|
|
123
123
|
* panels.
|
|
124
124
|
*
|
|
125
|
-
* Not `railPanelSet()`:
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
125
|
+
* Not `railPanelSet()`: Languages has no rail button and no chord, and it still needs a `Show …`
|
|
126
|
+
* command and a palette row, which is the whole point of `rail: false` (a surface reachable by
|
|
127
|
+
* name, not by number). State and Insert were rail-less beside it once: State's editor is the Data
|
|
128
|
+
* panel's now, and Insert is back on the rail (§5.1).
|
|
129
129
|
*
|
|
130
130
|
* The Bottom dock's three tabs are deliberately absent — Problems as of this change, and Logic and
|
|
131
131
|
* Activity all along. Each is `rail: false` AND hosted there, so each is addressed by
|
|
@@ -153,9 +153,9 @@ export interface PanelRecord {
|
|
|
153
153
|
*
|
|
154
154
|
* Not a hiding mechanism — a rail-less panel is still reachable by `view.setActivity`, by the
|
|
155
155
|
* palette and by another surface linking to it. It is how a record survives the interval between
|
|
156
|
-
* losing its rail slot and gaining its real home (
|
|
157
|
-
*
|
|
158
|
-
*
|
|
156
|
+
* losing its rail slot and gaining its real home (State, folded into Data — §5.1). Declaring it
|
|
157
|
+
* `false` also removes the panel's `rail/<level>` placement, which is what keeps the rail inside
|
|
158
|
+
* its four-per-group budget.
|
|
159
159
|
*
|
|
160
160
|
* `"developer"` is a rail button only while **Show developer panels** is on (§5.1,
|
|
161
161
|
* `settings/developer-panels.ts`): Files and Packages, which a site author does not need on the
|
|
@@ -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");
|
package/src/panels/site-panel.ts
CHANGED
|
@@ -71,6 +71,7 @@ import type {
|
|
|
71
71
|
SiteSectionState,
|
|
72
72
|
} from "../surfaces/site-panel";
|
|
73
73
|
import type { SiteComponentRow, SiteMediaRow, SitePageFact } from "./site-model";
|
|
74
|
+
import { isProjectConfigPath } from "@jxsuite/schema/project-file";
|
|
74
75
|
|
|
75
76
|
// ─── The project index ───────────────────────────────────────────────────────
|
|
76
77
|
|
|
@@ -213,7 +214,7 @@ export function mayDefineComponent(path: string): boolean {
|
|
|
213
214
|
}
|
|
214
215
|
return (
|
|
215
216
|
path.endsWith(".json") &&
|
|
216
|
-
path
|
|
217
|
+
!isProjectConfigPath(path) &&
|
|
217
218
|
!NON_COMPONENT_DIRS.some((dir) => path.startsWith(dir))
|
|
218
219
|
);
|
|
219
220
|
}
|