@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,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* What the Content workspace's Pages list says about each page beyond its file: the template it is
|
|
3
|
-
* drawn in, the collection a dynamic page expands over
|
|
4
|
-
*
|
|
3
|
+
* drawn in, the collection a dynamic page expands over (`specs/studio.md` §22.5, the Content-Pages
|
|
4
|
+
* board: Title, URL, Template, Status, Edited).
|
|
5
5
|
*
|
|
6
|
-
* The scan behind the Content home knows each page's FILE; its `$layout
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* The scan behind the Content home knows each page's FILE; its `$layout` and `$paths` are inside
|
|
7
|
+
* it. So this reads each page once, through the same parse the editor uses, and keeps the answer
|
|
8
|
+
* until a file event drops it ({@link invalidatePageSummaries}, wired beside the template-page
|
|
9
|
+
* index in `studio.ts`). The read never rejects: a page that cannot be read states nothing, and is
|
|
10
|
+
* listed by its route alone.
|
|
11
11
|
*
|
|
12
12
|
* @docs studio/projects/browse
|
|
13
13
|
*/
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
import { isNonNullObject } from "@jxsuite/schema/guards";
|
|
16
16
|
import { getPlatform, hasPlatform } from "../platform";
|
|
17
17
|
import { pageFactsOf } from "./collection-pages";
|
|
18
|
-
import { DRAFT_FIELD } from "./draft-state";
|
|
19
18
|
import type { FormatFrontmatter } from "../format/format-host";
|
|
20
19
|
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
21
20
|
|
|
@@ -26,13 +25,11 @@ export interface PageSummary {
|
|
|
26
25
|
layout: string | false | undefined;
|
|
27
26
|
/** The collection its `$paths` expands over, or `null` for a page of its own. */
|
|
28
27
|
contentType: string | null;
|
|
29
|
-
/** Whether the page carries `draft: true`, the one draft marker a page can have. */
|
|
30
|
-
draft: boolean;
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
/** What a page that could not be read states: nothing. */
|
|
34
31
|
function unread(path: string): PageSummary {
|
|
35
|
-
return { contentType: null,
|
|
32
|
+
return { contentType: null, layout: undefined, path };
|
|
36
33
|
}
|
|
37
34
|
|
|
38
35
|
/** The summary a parsed page states. Exported so the rule is assertable without a platform. */
|
|
@@ -42,8 +39,7 @@ export function pageSummaryOf(
|
|
|
42
39
|
frontmatter: FormatFrontmatter | null,
|
|
43
40
|
): PageSummary {
|
|
44
41
|
const { contentType, layout } = pageFactsOf(document, frontmatter);
|
|
45
|
-
|
|
46
|
-
return { contentType, draft, layout, path };
|
|
42
|
+
return { contentType, layout, path };
|
|
47
43
|
}
|
|
48
44
|
|
|
49
45
|
/** Read one page. */
|
|
@@ -61,7 +57,7 @@ async function readSummary(path: string): Promise<PageSummary> {
|
|
|
61
57
|
const { parseSourceForPath } = await import("../files/file-ops");
|
|
62
58
|
const parsed = await parseSourceForPath(path, source);
|
|
63
59
|
/* SAFETY: a format class's parser returns its file as a Jx element tree (the format contract);
|
|
64
|
-
only `$layout
|
|
60
|
+
only `$layout` and `$paths` are read off it. */
|
|
65
61
|
return pageSummaryOf(path, parsed.document as JxMutableNode, parsed.frontmatter);
|
|
66
62
|
} catch {
|
|
67
63
|
return unread(path);
|
|
@@ -101,6 +101,7 @@ import type {
|
|
|
101
101
|
AddNewView,
|
|
102
102
|
} from "../surfaces/add-new";
|
|
103
103
|
import type { JsonObject, JxMutableNode } from "@jxsuite/schema/types";
|
|
104
|
+
import { projectConfigPath } from "../tabs/tab";
|
|
104
105
|
|
|
105
106
|
/** The `source` every Problem this flow files carries. */
|
|
106
107
|
const SOURCE = "Add new";
|
|
@@ -385,7 +386,7 @@ async function performCollection(creation: Creation): Promise<AddNewResult | nul
|
|
|
385
386
|
if (!committed.ok) {
|
|
386
387
|
return null;
|
|
387
388
|
}
|
|
388
|
-
const wrote = [
|
|
389
|
+
const wrote = [projectConfigPath()];
|
|
389
390
|
const missed: string[] = [];
|
|
390
391
|
for (const write of [{ body: creation.content, path: creation.path }, ...creation.extraWrites]) {
|
|
391
392
|
try {
|
|
@@ -1,32 +1,11 @@
|
|
|
1
1
|
import elementsMeta from "../../data/elements-meta.json";
|
|
2
2
|
|
|
3
|
+
import { elementName } from "../locale/element-names";
|
|
3
4
|
import { ELEMENT_DEFS } from "./editable-tags";
|
|
4
5
|
import type { SlashCommand } from "./inline-edit";
|
|
5
6
|
|
|
6
7
|
export type { SlashCommand } from "./inline-edit";
|
|
7
8
|
|
|
8
|
-
const TAG_LABELS = new Map<string, string>([
|
|
9
|
-
["article", "Article"],
|
|
10
|
-
["aside", "Aside"],
|
|
11
|
-
["blockquote", "Blockquote"],
|
|
12
|
-
["div", "Div"],
|
|
13
|
-
["footer", "Footer"],
|
|
14
|
-
["h1", "Heading 1"],
|
|
15
|
-
["h2", "Heading 2"],
|
|
16
|
-
["h3", "Heading 3"],
|
|
17
|
-
["h4", "Heading 4"],
|
|
18
|
-
["h5", "Heading 5"],
|
|
19
|
-
["h6", "Heading 6"],
|
|
20
|
-
["header", "Header"],
|
|
21
|
-
["main", "Main"],
|
|
22
|
-
["nav", "Nav"],
|
|
23
|
-
["ol", "Numbered List"],
|
|
24
|
-
["p", "Paragraph"],
|
|
25
|
-
["search", "Search"],
|
|
26
|
-
["section", "Section"],
|
|
27
|
-
["ul", "Bulleted List"],
|
|
28
|
-
]);
|
|
29
|
-
|
|
30
9
|
const groups: Readonly<Record<string, string[]>> = elementsMeta.$convertGroups;
|
|
31
10
|
|
|
32
11
|
/**
|
|
@@ -56,7 +35,7 @@ export function getConvertTargets(currentTag: string, isEmpty: boolean): SlashCo
|
|
|
56
35
|
|
|
57
36
|
return [...tags].map((tag) => ({
|
|
58
37
|
description: "",
|
|
59
|
-
label:
|
|
38
|
+
label: elementName(tag),
|
|
60
39
|
tag,
|
|
61
40
|
}));
|
|
62
41
|
}
|
|
@@ -234,7 +234,12 @@ export function setEditableVerdicts(verdicts: EditableVerdicts): void {
|
|
|
234
234
|
* {@link file://./editable-tags.ts}.
|
|
235
235
|
*/
|
|
236
236
|
export function isEditableBlock(el: HTMLElement) {
|
|
237
|
-
|
|
237
|
+
/* Closed explicitly, whatever its tag. Edit mode closes the label it draws for computed children
|
|
238
|
+
(`prepareForEditMode`), because activating that label commits its words as the element's
|
|
239
|
+
`textContent` when the caret leaves, and the binding is gone without a key being pressed. */
|
|
240
|
+
return (
|
|
241
|
+
el.getAttribute("contenteditable") !== "false" && isEditableTag(el.tagName, editableVerdicts)
|
|
242
|
+
);
|
|
238
243
|
}
|
|
239
244
|
|
|
240
245
|
/**
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Repeater scope resolution
|
|
3
|
-
* the
|
|
2
|
+
* Repeater scope resolution: what the item of a repeater (`$prototype: "Array"`) holds, deduced
|
|
3
|
+
* from the document and the project without running either.
|
|
4
|
+
*
|
|
5
|
+
* Two surfaces ask. Inline text edited INSIDE a repeater inserts a `${item.data.title}` token, and
|
|
6
|
+
* the `From data…` rung binds a `$map/item/data/title` pointer. They are the same field spelled for
|
|
7
|
+
* two grammars, so there is one walk and each field carries both spellings. A third surface asks
|
|
8
|
+
* for the one field that is neither text nor a value: `$children`, which an element draws by
|
|
9
|
+
* holding it as computed children (spec.md §8.4).
|
|
4
10
|
*
|
|
5
11
|
* There is no live `$map` in edit mode: `prepareForEditMode` renders a mapped array as a
|
|
6
12
|
* `repeater-perimeter` wrapping one glyph-ified template instance, so `renderMappedArrayInto`
|
|
@@ -9,23 +15,30 @@
|
|
|
9
15
|
* document path (which carries a real `"map"` segment — see canvas/path-mapping.ts
|
|
10
16
|
* `classifyRenderNode`).
|
|
11
17
|
*
|
|
12
|
-
* Pure and DOM-free: deps limited to `@jxsuite/schema` guards/types
|
|
18
|
+
* Pure and DOM-free: deps limited to `@jxsuite/schema` guards/types, the runtime's pointer grammar
|
|
19
|
+
*
|
|
20
|
+
* - {@link getNodeAtPath}.
|
|
21
|
+
*
|
|
22
|
+
* @docs studio/design/repeaters
|
|
13
23
|
*/
|
|
14
24
|
|
|
25
|
+
import { escapeToken, refSegments } from "@jxsuite/runtime/pointer";
|
|
15
26
|
import { getNodeAtPath } from "@jxsuite/schema/doc-ops";
|
|
16
27
|
import {
|
|
17
28
|
isJsonObject,
|
|
18
29
|
isMappedArray,
|
|
19
|
-
isNonNullObject,
|
|
20
30
|
isRef,
|
|
31
|
+
isSchemaOnlyDef,
|
|
21
32
|
isString,
|
|
22
33
|
} from "@jxsuite/schema/guards";
|
|
23
34
|
|
|
24
35
|
import type { JxPath } from "../state";
|
|
25
36
|
import type {
|
|
37
|
+
JsonObject,
|
|
38
|
+
JsonValue,
|
|
39
|
+
JxDocumentValue,
|
|
26
40
|
JxMappedArray,
|
|
27
41
|
JxMutableNode,
|
|
28
|
-
JxStateDefinition,
|
|
29
42
|
ProjectConfig,
|
|
30
43
|
} from "@jxsuite/schema/types";
|
|
31
44
|
|
|
@@ -58,93 +71,339 @@ export function findEnclosingRepeater(
|
|
|
58
71
|
return isMappedArray(node) ? node : null;
|
|
59
72
|
}
|
|
60
73
|
|
|
61
|
-
/**
|
|
62
|
-
|
|
74
|
+
/** One field of a repeater's item, in each grammar that can name it. */
|
|
75
|
+
export interface RepeaterField {
|
|
76
|
+
/** The members walked from the item, unescaped. */
|
|
77
|
+
path: string[];
|
|
78
|
+
/** What a template reads and a person is shown: `item.data.title`. */
|
|
79
|
+
token: string;
|
|
80
|
+
/** What a `$ref` holds: `$map/item/data/title`, with each member escaped (RFC 6901 §3). */
|
|
81
|
+
pointer: string;
|
|
82
|
+
/** Whether the field holds a list, which is all a nested repeater's Items can read. */
|
|
83
|
+
isArray: boolean;
|
|
84
|
+
/** Whether the field holds child nodes, which only an element's computed children can draw. */
|
|
85
|
+
holdsNodes: boolean;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** The one `$`-prefixed member an item offers: the Jx tree a parsed body came out as. */
|
|
89
|
+
const NODES_MEMBER = "$children";
|
|
90
|
+
|
|
91
|
+
/** The content format whose loader parses an entry's body into {@link NODES_MEMBER}. */
|
|
92
|
+
const NODES_FORMAT = "Markdown";
|
|
93
|
+
|
|
94
|
+
/** What every step of the walk reads from. */
|
|
95
|
+
interface ItemSources {
|
|
96
|
+
doc: JxMutableNode;
|
|
97
|
+
config: ProjectConfig | null | undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Marks a schema as an entry of this content type, so a relationship cycle can be stopped. */
|
|
101
|
+
const ENTRY_TYPE = "$entryType";
|
|
102
|
+
|
|
103
|
+
/** Marks an entry's `data` member, which is a level of the path rather than a field to offer. */
|
|
104
|
+
const ENTRY_DATA = "$entryData";
|
|
105
|
+
|
|
106
|
+
/** How many members a sample list contributes to the shape it implies. */
|
|
107
|
+
const SAMPLE_CAP = 20;
|
|
108
|
+
|
|
109
|
+
/** How many members deep a field path may run, so a walk of a deep schema stays a short list. */
|
|
110
|
+
const DEPTH_CAP = 6;
|
|
111
|
+
|
|
112
|
+
/** How many pointers a `$ref` chain may follow, so `a → b → a` ends. */
|
|
113
|
+
const HOP_CAP = 8;
|
|
114
|
+
|
|
115
|
+
/** The shape of what a content type's loader hands back (`ContentLoaderEntry`). */
|
|
116
|
+
function entrySchema(contentType: string, ctx: ItemSources): JsonObject {
|
|
117
|
+
/* The `content` section is contributed by an extension and absorbed by the project config's index
|
|
118
|
+
signature, so each level is checked before it is read. */
|
|
119
|
+
const content: unknown = ctx.config?.content;
|
|
120
|
+
const type = isJsonObject(content) ? content[contentType] : undefined;
|
|
121
|
+
const data = isJsonObject(type) && isJsonObject(type.schema) ? type.schema : {};
|
|
122
|
+
// A CSV row has no body to parse. A type naming no format is not known to lack one.
|
|
123
|
+
const format = isJsonObject(type) ? type.format : undefined;
|
|
124
|
+
const hasNodes = !isString(format) || format === NODES_FORMAT;
|
|
125
|
+
// Built from entries because the order is what the picker lists: id and body, then the fields.
|
|
126
|
+
const members: [string, JsonValue][] = [
|
|
127
|
+
["id", {}],
|
|
128
|
+
["body", {}],
|
|
129
|
+
...(hasNodes ? ([[NODES_MEMBER, {}]] satisfies [string, JsonValue][]) : []),
|
|
130
|
+
["data", { ...data, [ENTRY_DATA]: true }],
|
|
131
|
+
];
|
|
132
|
+
return { [ENTRY_TYPE]: contentType, properties: Object.fromEntries(members) };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Whether a schema's own keys say what is inside it, so a sample has nothing to add. */
|
|
136
|
+
function describesFields(schema: JsonObject | null, ctx: ItemSources): boolean {
|
|
137
|
+
return (
|
|
138
|
+
isJsonObject(schema?.properties) || isJsonObject(followPointers(schema?.items, ctx)?.properties)
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Follow a schema's pointers to the schema they name, or `null` for one this walk cannot read.
|
|
144
|
+
*
|
|
145
|
+
* Three spellings reach another schema: `{ "$ref": "#/content/authors" }` is a relationship, whose
|
|
146
|
+
* value the loader resolves to the full entry; `{ "$ref": "#/$defs/Member" }` is a shared type; and
|
|
147
|
+
* `{ "type": { "$ref": … } }` is a typed value naming one.
|
|
148
|
+
*/
|
|
149
|
+
function followPointers(
|
|
150
|
+
schema: JxDocumentValue | undefined,
|
|
151
|
+
ctx: ItemSources,
|
|
152
|
+
hops = 0,
|
|
153
|
+
): JsonObject | null {
|
|
154
|
+
if (!isJsonObject(schema) || hops > HOP_CAP) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
const pointer = isRef(schema) ? schema.$ref : isRef(schema.type) ? schema.type.$ref : null;
|
|
158
|
+
if (pointer === null) {
|
|
159
|
+
return schema;
|
|
160
|
+
}
|
|
161
|
+
if (pointer.startsWith("#/content/")) {
|
|
162
|
+
return entrySchema(pointer.slice("#/content/".length), ctx);
|
|
163
|
+
}
|
|
164
|
+
if (pointer.startsWith("#/$defs/")) {
|
|
165
|
+
const [name, ...rest] = refSegments(pointer.slice("#/$defs/".length));
|
|
166
|
+
const projectDefs: unknown = ctx.config?.$defs;
|
|
167
|
+
const declared =
|
|
168
|
+
name === undefined
|
|
169
|
+
? undefined
|
|
170
|
+
: (ctx.doc.$defs?.[name] ?? (isJsonObject(projectDefs) ? projectDefs[name] : undefined));
|
|
171
|
+
return walkSchema(followPointers(declared, ctx, hops + 1), rest, ctx);
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** The union of two implied shapes, so an optional field the first row lacks is still found. */
|
|
177
|
+
function mergeSamples(a: JsonObject, b: JsonObject): JsonObject {
|
|
178
|
+
const merged: JsonObject = { ...a, ...b };
|
|
179
|
+
if (isJsonObject(a.items) && isJsonObject(b.items)) {
|
|
180
|
+
merged.items = mergeSamples(a.items, b.items);
|
|
181
|
+
}
|
|
182
|
+
if (isJsonObject(a.properties) && isJsonObject(b.properties)) {
|
|
183
|
+
const properties: JsonObject = { ...a.properties, ...b.properties };
|
|
184
|
+
for (const [key, left] of Object.entries(a.properties)) {
|
|
185
|
+
const right = b.properties[key];
|
|
186
|
+
if (isJsonObject(left) && isJsonObject(right)) {
|
|
187
|
+
properties[key] = mergeSamples(left, right);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
merged.properties = properties;
|
|
191
|
+
}
|
|
192
|
+
return merged;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** The shape a sample value implies. */
|
|
196
|
+
function sampleSchema(value: JsonValue | undefined): JsonObject {
|
|
197
|
+
if (Array.isArray(value)) {
|
|
198
|
+
let items: JsonObject = {};
|
|
199
|
+
for (const member of value.slice(0, SAMPLE_CAP)) {
|
|
200
|
+
items = mergeSamples(items, sampleSchema(member));
|
|
201
|
+
}
|
|
202
|
+
return { items };
|
|
203
|
+
}
|
|
204
|
+
if (isJsonObject(value)) {
|
|
205
|
+
return {
|
|
206
|
+
properties: Object.fromEntries(
|
|
207
|
+
Object.entries(value).map(([key, member]) => [key, sampleSchema(member)]),
|
|
208
|
+
),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
return {};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** The shape of a state entry's value, or `null` when only running it would say. */
|
|
215
|
+
function stateSchema(def: JxDocumentValue | undefined, ctx: ItemSources): JsonObject | null {
|
|
216
|
+
if (Array.isArray(def)) {
|
|
217
|
+
// SAFETY: a naked array state entry is the JSON value it reads as (schema/types `JxStateDefinition`).
|
|
218
|
+
return sampleSchema(def as JsonValue[]);
|
|
219
|
+
}
|
|
220
|
+
if (!isJsonObject(def)) {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
/* Detected on the `contentType` string, not the prototype's name, so an import-aliased
|
|
224
|
+
`$prototype` still matches. An `id` is what asks for one entry rather than the list. */
|
|
225
|
+
if (isString(def.contentType)) {
|
|
226
|
+
const entry = entrySchema(def.contentType, ctx);
|
|
227
|
+
return def.$prototype === "ContentEntry" || def.id !== undefined ? entry : { items: entry };
|
|
228
|
+
}
|
|
229
|
+
const declared = followPointers(def, ctx);
|
|
230
|
+
if (describesFields(declared, ctx)) {
|
|
231
|
+
return declared;
|
|
232
|
+
}
|
|
233
|
+
if ("default" in def) {
|
|
234
|
+
return sampleSchema(def.default);
|
|
235
|
+
}
|
|
236
|
+
const isNakedObject = !isSchemaOnlyDef(def) && !Object.keys(def).some((k) => k.startsWith("$"));
|
|
237
|
+
return isNakedObject ? sampleSchema(def) : declared;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Follow members down a schema, or `null` where the schema stops saying. */
|
|
241
|
+
function walkSchema(
|
|
242
|
+
schema: JsonObject | null,
|
|
243
|
+
segments: readonly string[],
|
|
244
|
+
ctx: ItemSources,
|
|
245
|
+
): JsonObject | null {
|
|
246
|
+
let current = followPointers(schema, ctx);
|
|
247
|
+
for (const segment of segments) {
|
|
248
|
+
const member = isJsonObject(current?.properties) ? current.properties[segment] : undefined;
|
|
249
|
+
current = followPointers(member ?? (/^\d+$/.test(segment) ? current?.items : undefined), ctx);
|
|
250
|
+
}
|
|
251
|
+
return current;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** The shape of what a repeater's `items` binding reads, in the scope of the repeater itself. */
|
|
255
|
+
function bindingSchema(
|
|
256
|
+
binding: JxMappedArray["items"],
|
|
257
|
+
repeaterPath: JxPath,
|
|
258
|
+
ctx: ItemSources,
|
|
259
|
+
): JsonObject | null {
|
|
260
|
+
if (Array.isArray(binding)) {
|
|
261
|
+
// SAFETY: an inline `items` list is read straight out of the JSON document.
|
|
262
|
+
return sampleSchema(binding as JsonValue[]);
|
|
263
|
+
}
|
|
63
264
|
if (!isRef(binding)) {
|
|
64
265
|
return null;
|
|
65
266
|
}
|
|
66
|
-
const
|
|
67
|
-
|
|
267
|
+
const ref = binding.$ref.replace(/^#\/\$map\//, "$map/");
|
|
268
|
+
if (ref.startsWith("#/state/")) {
|
|
269
|
+
const [name, ...rest] = refSegments(ref.slice("#/state/".length));
|
|
270
|
+
const def = name === undefined ? undefined : ctx.doc.state?.[name];
|
|
271
|
+
return walkSchema(stateSchema(def, ctx), rest, ctx);
|
|
272
|
+
}
|
|
273
|
+
// A nested repeater reads a member of the repeater around it, so the walk starts from that item.
|
|
274
|
+
if (ref === "$map/item" || ref.startsWith("$map/item/")) {
|
|
275
|
+
const rest = refSegments(ref.slice("$map/item/".length));
|
|
276
|
+
return walkSchema(itemSchemaAt(repeaterPath, ctx), rest, ctx);
|
|
277
|
+
}
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** The shape of the item in scope at `path`, or `null` outside a repeater or past what is known. */
|
|
282
|
+
function itemSchemaAt(path: JxPath, ctx: ItemSources): JsonObject | null {
|
|
283
|
+
const repeater = findEnclosingRepeater(ctx.doc, path);
|
|
284
|
+
if (!repeater) {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
const list = bindingSchema(repeater.items, path.slice(0, path.lastIndexOf("map")), ctx);
|
|
288
|
+
return followPointers(list?.items, ctx);
|
|
68
289
|
}
|
|
69
290
|
|
|
70
|
-
/**
|
|
71
|
-
function
|
|
72
|
-
|
|
291
|
+
/** A field name as a template reads it: a member access where a name allows one, else a bracket. */
|
|
292
|
+
function fieldToken(path: readonly string[]): string {
|
|
293
|
+
let token = "item";
|
|
294
|
+
for (const member of path) {
|
|
295
|
+
token += /^[A-Za-z_$][\w$]*$/.test(member) ? `.${member}` : `[${JSON.stringify(member)}]`;
|
|
296
|
+
}
|
|
297
|
+
return token;
|
|
73
298
|
}
|
|
74
299
|
|
|
75
300
|
/**
|
|
76
|
-
*
|
|
77
|
-
* `index`; appends field tokens resolved from the repeater's binding:
|
|
301
|
+
* Every field the item in scope at `path` is known to hold, nearest the item first.
|
|
78
302
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* declared `items.properties`, else are inferred from a sample `default[0]`. Emitted FLAT
|
|
85
|
-
* (`item.<f>`, no `.data.`).
|
|
86
|
-
* - **Inline array** — `items` is a literal array; fields inferred from `items[0]` object keys.
|
|
87
|
-
* - **Fallback** — anything unresolvable yields exactly `["item", "index"]`.
|
|
303
|
+
* Walked a level at a time so {@link FIELD_CAP} cuts the deepest fields and never a top-level one.
|
|
304
|
+
* An object is offered as well as its members, because a component can take the whole value. A list
|
|
305
|
+
* is offered and not entered: one of its members has no single value to bind. A relationship is
|
|
306
|
+
* entered unless the branch already passed through that content type, which ends a post → author →
|
|
307
|
+
* post cycle.
|
|
88
308
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
309
|
+
* `$children` is left out unless asked for: it is a tree of nodes, so a text position, an attribute
|
|
310
|
+
* or a formula operand that read it would draw `[object Object]`.
|
|
311
|
+
*
|
|
312
|
+
* @param doc - The tab's document root (`tab.doc.document`).
|
|
313
|
+
* @param path - The path of the node asking.
|
|
91
314
|
* @param projectConfig - Parsed `project.json` (`projectState?.projectConfig`).
|
|
92
|
-
* @
|
|
315
|
+
* @param options - `withNodes` also lists the fields that hold child nodes.
|
|
316
|
+
* @returns The fields, empty outside a repeater and wherever the item's shape cannot be deduced.
|
|
93
317
|
*/
|
|
94
|
-
export function
|
|
95
|
-
|
|
96
|
-
|
|
318
|
+
export function repeaterItemFields(
|
|
319
|
+
doc: JxMutableNode,
|
|
320
|
+
path: JxPath | null | undefined,
|
|
97
321
|
projectConfig: ProjectConfig | null | undefined,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const out = [...base, "item.id", "item.body"];
|
|
117
|
-
if (isNonNullObject(props)) {
|
|
118
|
-
for (const f of safeFields(Object.keys(props))) {
|
|
119
|
-
out.push(`item.data.${f}`);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return out;
|
|
322
|
+
{ withNodes = false }: { withNodes?: boolean } = {},
|
|
323
|
+
): RepeaterField[] {
|
|
324
|
+
if (!Array.isArray(path)) {
|
|
325
|
+
return [];
|
|
326
|
+
}
|
|
327
|
+
const ctx: ItemSources = { config: projectConfig, doc };
|
|
328
|
+
const fields: RepeaterField[] = [];
|
|
329
|
+
const pending: { at: string[]; schema: JsonObject | null; via: string[] }[] = [
|
|
330
|
+
{ at: [], schema: itemSchemaAt(path, ctx), via: [] },
|
|
331
|
+
];
|
|
332
|
+
for (const { at, schema, via } of pending) {
|
|
333
|
+
const entryType = isString(schema?.[ENTRY_TYPE]) ? schema[ENTRY_TYPE] : null;
|
|
334
|
+
const passed = entryType === null ? via : [...via, entryType];
|
|
335
|
+
const members = isJsonObject(schema?.properties) ? Object.entries(schema.properties) : [];
|
|
336
|
+
for (const [key, member] of members) {
|
|
337
|
+
const holdsNodes = key === NODES_MEMBER;
|
|
338
|
+
if ((key.startsWith("$") && !(holdsNodes && withNodes)) || fields.length >= FIELD_CAP) {
|
|
339
|
+
continue;
|
|
123
340
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
341
|
+
const memberPath = [...at, key];
|
|
342
|
+
const resolved = followPointers(member, ctx);
|
|
343
|
+
if (resolved?.[ENTRY_DATA] !== true) {
|
|
344
|
+
fields.push({
|
|
345
|
+
holdsNodes,
|
|
346
|
+
isArray: resolved?.type === "array" || isJsonObject(resolved?.items),
|
|
347
|
+
path: memberPath,
|
|
348
|
+
pointer: `$map/item/${memberPath.map((m) => escapeToken(m)).join("/")}`,
|
|
349
|
+
token: fieldToken(memberPath),
|
|
350
|
+
});
|
|
130
351
|
}
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return [...base, ...safeFields(Object.keys(sample)).map((f) => `item.${f}`)];
|
|
352
|
+
const type = isString(resolved?.[ENTRY_TYPE]) ? resolved[ENTRY_TYPE] : null;
|
|
353
|
+
if (memberPath.length < DEPTH_CAP && !(type !== null && passed.includes(type))) {
|
|
354
|
+
pending.push({ at: memberPath, schema: resolved, via: passed });
|
|
135
355
|
}
|
|
136
356
|
}
|
|
137
|
-
return base;
|
|
138
357
|
}
|
|
358
|
+
return fields;
|
|
359
|
+
}
|
|
139
360
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
361
|
+
/**
|
|
362
|
+
* The local scope tokens for text edited inside a repeater: `item` and `index`, then the fields of
|
|
363
|
+
* the item in scope (see {@link repeaterItemFields}).
|
|
364
|
+
*
|
|
365
|
+
* @returns Token strings, e.g. `["item", "index", "item.id", "item.body", "item.data.title"]`.
|
|
366
|
+
*/
|
|
367
|
+
export function resolveRepeaterItemFields(
|
|
368
|
+
doc: JxMutableNode,
|
|
369
|
+
path: JxPath | null | undefined,
|
|
370
|
+
projectConfig: ProjectConfig | null | undefined,
|
|
371
|
+
): string[] {
|
|
372
|
+
return ["item", "index", ...repeaterItemFields(doc, path, projectConfig).map((f) => f.token)];
|
|
373
|
+
}
|
|
147
374
|
|
|
148
|
-
|
|
149
|
-
|
|
375
|
+
/**
|
|
376
|
+
* The `children` template that draws a field's nodes. The fallback is part of it because the field
|
|
377
|
+
* is only known to be possible: an entry loaded without a body has no `$children` to read.
|
|
378
|
+
*/
|
|
379
|
+
export function childrenTemplate(field: RepeaterField): string {
|
|
380
|
+
return `\${${field.token} ?? []}`;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* The field a `children` template reads, with or without the fallback {@link childrenTemplate}
|
|
385
|
+
* writes. Anything more than the bare field is the author's own expression and is left as theirs.
|
|
386
|
+
*/
|
|
387
|
+
export function fieldOfChildrenTemplate(
|
|
388
|
+
template: string,
|
|
389
|
+
fields: readonly RepeaterField[],
|
|
390
|
+
): RepeaterField | undefined {
|
|
391
|
+
const token = /^\$\{\s*(.+?)\s*(?:\?\?\s*\[\s*\]\s*)?\}$/u.exec(template)?.[1];
|
|
392
|
+
return fields.find((field) => field.token === token);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* A `$map/…` pointer in the words {@link repeaterItemFields} offers it under, or `null` for any
|
|
397
|
+
* other pointer. A bound chip says the same name the picker did: `item.data.title`.
|
|
398
|
+
*/
|
|
399
|
+
export function mapRefLabel(ref: string): string | null {
|
|
400
|
+
if (ref === "$map/index") {
|
|
401
|
+
return "index";
|
|
402
|
+
}
|
|
403
|
+
if (ref === "$map/item") {
|
|
404
|
+
return "item";
|
|
405
|
+
}
|
|
406
|
+
return ref.startsWith("$map/item/")
|
|
407
|
+
? fieldToken(refSegments(ref.slice("$map/item/".length)))
|
|
408
|
+
: null;
|
|
150
409
|
}
|