@jxsuite/studio 6.1.1 → 8.0.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/README.md +5 -5
- package/dist/chunks/2020-5wtzce5g.js +4 -0
- package/dist/chunks/{action-group-2mqby73c.js → action-group-4aan7g6g.js} +3 -3
- package/dist/chunks/adapter-5tphg7xe.js +4 -0
- package/dist/chunks/add-new-flow-psym14sn.js +4 -0
- package/dist/chunks/cf-account-picker-haqazq5r.js +4 -0
- package/dist/chunks/cf-settings-h2wrh6hq.js +4 -0
- package/dist/chunks/color-area-6rbra5dy.js +4 -0
- package/dist/chunks/color-field-gy56c94g.js +4 -0
- package/dist/chunks/color-m7r1vk54.js +4 -0
- package/dist/chunks/color-slider-sqqdyf6c.js +4 -0
- package/dist/chunks/combobox-72h32ejk.js +4 -0
- package/dist/chunks/components-r22xca09.js +4 -0
- package/dist/chunks/content-source-mmppmcyg.js +4 -0
- package/dist/chunks/convert-to-repeater-he70fc2b.js +4 -0
- package/dist/chunks/dialog-c4x05vyx.js +4 -0
- package/dist/chunks/editor-ccd9jwsa.js +4 -0
- package/dist/chunks/entry-commands-6qxz9e8p.js +4 -0
- package/dist/chunks/extension-sections-0egb88dm.js +4 -0
- package/dist/chunks/file-ops-qnab36ek.js +4 -0
- package/dist/chunks/files-rcn56qvm.js +4 -0
- package/dist/chunks/format-host-yxz0kjjs.js +4 -0
- package/dist/chunks/fs-events-d1qxq4z4.js +4 -0
- package/dist/chunks/grid-open-4z1g062t.js +4 -0
- package/dist/chunks/grid-view-qg1b5eqx.js +51 -0
- package/dist/chunks/icons-63bjtbs7.js +4 -0
- package/dist/chunks/iframe-entry-2zggzajv.js +5 -0
- package/dist/chunks/iframe-entry-76j8vwwx.js +5 -0
- package/dist/chunks/iframe-entry-99nr5zfb.js +5 -0
- package/dist/chunks/iframe-entry-pkgmcgp0.js +5 -0
- package/dist/chunks/iframe-host-ssv7kyj0.js +4 -0
- package/dist/chunks/{index-2veqmnhr.js → index-32eqzv1k.js} +3 -3
- package/dist/chunks/index-f0jn7nnp.js +4 -0
- package/dist/chunks/{javascript-8nbvs12s.js → javascript-pjp734q4.js} +3 -3
- package/dist/chunks/jsonMode-njkvsa02.js +13 -0
- package/dist/chunks/jx-validate-ttasrbh7.js +4 -0
- package/dist/chunks/layers-bgyr11tr.js +4 -0
- package/dist/chunks/layers-panel-7ez66zeq.js +4 -0
- package/dist/chunks/listbox-c421cf8c.js +4 -0
- package/dist/chunks/lit-html-v9gh687x.js +4 -0
- package/dist/chunks/media-library-sq468wts.js +4 -0
- package/dist/chunks/media-picker-shfe59pt.js +4 -0
- package/dist/chunks/menu-bdpzbfvs.js +4 -0
- package/dist/chunks/monaco-binding-vq1hgnn0.js +4 -0
- package/dist/chunks/monaco-buffer-3afgwvbp.js +4 -0
- package/dist/chunks/monaco-setup-xrd2cbkp.js +4 -0
- package/dist/chunks/notify-kzrjr3fv.js +4 -0
- package/dist/chunks/number-field-y62xq0ty.js +4 -0
- package/dist/chunks/parse-v77xtwv7.js +4 -0
- package/dist/chunks/platform-7v4rtd7m.js +4 -0
- package/dist/chunks/popover-b89sfej7.js +4 -0
- package/dist/chunks/publish-panel-2z9gw8ee.js +4 -0
- package/dist/chunks/publish-quick-qx2455cj.js +4 -0
- package/dist/chunks/publish-workspace-wrc1dgwx.js +4 -0
- package/dist/chunks/quick-search-n0t0eqxb.js +4 -0
- package/dist/chunks/references-9547rg5s.js +4 -0
- package/dist/chunks/right-panel-wsgnr9pn.js +4 -0
- package/dist/chunks/select-rzds841e.js +4 -0
- package/dist/chunks/source-control-kwktr9y6.js +4 -0
- package/dist/chunks/split-c9zfdgxf.js +4 -0
- package/dist/chunks/studio-03ct6yfv.js +9 -0
- package/dist/chunks/studio-07rcfje3.js +5 -0
- package/dist/chunks/studio-3e54qh39.js +7 -0
- package/dist/chunks/studio-3exxhz5g.js +5 -0
- package/dist/chunks/studio-3q40e3rj.js +5 -0
- package/dist/chunks/studio-3qeh9t0h.js +6 -0
- package/dist/chunks/studio-3qqjeep5.js +6 -0
- package/dist/chunks/studio-43sd2mam.js +4 -0
- package/dist/chunks/studio-4hp8pkyt.js +5 -0
- package/dist/chunks/studio-58xg6j2q.js +9 -0
- package/dist/chunks/studio-5g0rprdt.js +7 -0
- package/dist/chunks/studio-6m380wp7.js +5 -0
- package/dist/chunks/studio-80jyh1xh.js +4 -0
- package/dist/chunks/studio-83mk4bgg.js +9 -0
- package/dist/chunks/{studio-kggey1jt.js → studio-881z1xrx.js} +4 -4
- package/dist/chunks/studio-8m0y2qe7.js +5 -0
- package/dist/chunks/studio-8tr6cfrc.js +5 -0
- package/dist/chunks/studio-8vcvfs7r.js +6 -0
- package/dist/chunks/studio-9tmebf0s.js +5 -0
- package/dist/chunks/studio-a2bvaptz.js +5 -0
- package/dist/chunks/studio-b7y5mr30.js +5 -0
- package/dist/chunks/studio-bavb31r0.js +5 -0
- package/dist/chunks/studio-bbe4c90a.js +5 -0
- package/dist/chunks/studio-c05wdnt3.js +5 -0
- package/dist/chunks/studio-ccv2svc6.js +5 -0
- package/dist/chunks/studio-d5grtb9e.js +16 -0
- package/dist/chunks/studio-df0baea9.js +5 -0
- package/dist/chunks/studio-dgk4dqtx.js +7 -0
- package/dist/chunks/studio-e1g64q43.js +5 -0
- package/dist/chunks/studio-ed1hp6zf.js +6 -0
- package/dist/chunks/studio-ed3xx40h.js +7 -0
- package/dist/chunks/studio-f14e1zdj.js +5 -0
- package/dist/chunks/studio-fbhxxc5f.js +5 -0
- package/dist/chunks/studio-fyh0jc96.js +5 -0
- package/dist/chunks/studio-gcxk20n7.js +5 -0
- package/dist/chunks/studio-jaaz8ce9.js +5 -0
- package/dist/chunks/{studio-8grwxtqk.js → studio-jcnsdaep.js} +4 -4
- package/dist/chunks/studio-jsgf2q5k.js +7 -0
- package/dist/chunks/{studio-1q1sh2ax.js → studio-k3xxbagr.js} +3 -3
- package/dist/chunks/studio-k7evxqh4.js +5 -0
- package/dist/chunks/studio-kt3pjkwd.js +5 -0
- package/dist/chunks/studio-m8g16bgz.js +5 -0
- package/dist/chunks/studio-mn0476ag.js +5 -0
- package/dist/chunks/studio-mx1smyq0.js +5 -0
- package/dist/chunks/studio-n0c5h6mf.js +5 -0
- package/dist/chunks/{studio-rv11kfwa.js → studio-nh6d342n.js} +7 -7
- package/dist/chunks/studio-nqxqf8p2.js +4 -0
- package/dist/chunks/studio-p4nrkvqr.js +5 -0
- package/dist/chunks/{studio-h46vtzk8.js → studio-pbxa5fpc.js} +4 -4
- package/dist/chunks/studio-pgmvdv7z.js +7 -0
- package/dist/chunks/studio-ph84avkf.js +5 -0
- package/dist/chunks/{studio-wszjyh0j.js → studio-q0mvbaq6.js} +4 -4
- package/dist/chunks/studio-q0tccd43.js +5 -0
- package/dist/chunks/studio-q7rtx6zq.js +14 -0
- package/dist/chunks/{studio-pre0skq6.js → studio-qgh13jtz.js} +4 -4
- package/dist/chunks/studio-qmn1xc24.js +5 -0
- package/dist/chunks/studio-qwdqwkj0.js +5 -0
- package/dist/chunks/{studio-t6b6qe31.js → studio-r5qy30vx.js} +4 -4
- package/dist/chunks/studio-rj9eamf8.js +5 -0
- package/dist/chunks/studio-rptgsk96.js +5 -0
- package/dist/chunks/studio-rthyg42z.js +928 -0
- package/dist/chunks/studio-s19tp1xr.js +5 -0
- package/dist/chunks/studio-s32578pn.js +5 -0
- package/dist/chunks/studio-svb7yyve.js +5 -0
- package/dist/chunks/studio-syreb644.js +5 -0
- package/dist/chunks/studio-tf9jryb1.js +10 -0
- package/dist/chunks/studio-tfkznv39.js +5 -0
- package/dist/chunks/studio-tft6r105.js +5 -0
- package/dist/chunks/studio-txf2kysx.js +5 -0
- package/dist/chunks/studio-v8867ddv.js +12 -0
- package/dist/chunks/studio-vxq7fcvv.js +5 -0
- package/dist/chunks/studio-w4rz9hdk.js +5 -0
- package/dist/chunks/studio-wbcjbhy1.js +9 -0
- package/dist/chunks/studio-wc649wss.js +5 -0
- package/dist/chunks/studio-wys23mxz.js +5 -0
- package/dist/chunks/studio-x1x167jh.js +5 -0
- package/dist/chunks/studio-x9hsze61.js +5 -0
- package/dist/chunks/studio-y0n7yxea.js +9 -0
- package/dist/chunks/studio-y3ahhdpx.js +5 -0
- package/dist/chunks/studio-z97c48fk.js +5 -0
- package/dist/chunks/studio-zat62m0c.js +5 -0
- package/dist/chunks/studio-zcd2e7yz.js +5 -0
- package/dist/chunks/studio-zz9qe3rw.js +64 -0
- package/dist/chunks/swatch-group-ewh24n1f.js +4 -0
- package/dist/chunks/tabs-f5cw3tgp.js +4 -0
- package/dist/chunks/textfield-wewc8rq2.js +4 -0
- package/dist/chunks/toast-1gv0h53h.js +4 -0
- package/dist/chunks/toast-host-1yg5ar8d.js +4 -0
- package/dist/chunks/{toolbar-2nnzqhrj.js → toolbar-44cm5cta.js} +3 -3
- package/dist/chunks/tooltip-yypz28gq.js +4 -0
- package/dist/chunks/tree-9xa4hm7n.js +4 -0
- package/dist/chunks/tsMode-epyfz1hv.js +14 -0
- package/dist/chunks/workspace-dz7nxfhv.js +4 -0
- package/dist/chunks/workspace-home-xsd7hkjw.js +4 -0
- package/dist/chunks/ws-client-bv552sfx.js +4 -0
- package/dist/iframe-entry.js +20 -16
- package/dist/manifest.json +77 -63
- package/dist/studio.js +106 -107
- package/dist/workers/ts.worker.js +3 -3
- package/package.json +17 -16
- package/src/about/about-modal.ts +4 -3
- package/src/account/action-flow.ts +45 -38
- package/src/account-status.ts +7 -2
- package/src/browse/component-examples.ts +272 -0
- package/src/browse/library-commands.ts +64 -104
- package/src/browse/library-layouts.ts +270 -53
- package/src/browse/library-model.ts +194 -134
- package/src/browse/library-pane.ts +748 -440
- package/src/browse/library-preview.ts +161 -26
- package/src/browse/library-source.ts +308 -41
- package/src/browse/preview-root.ts +78 -0
- package/src/browse/preview-style.ts +155 -0
- package/src/canvas/asset-refs.ts +40 -10
- package/src/canvas/asset-resolve.ts +19 -23
- package/src/canvas/canvas-helpers.ts +7 -6
- package/src/canvas/canvas-live-render.ts +260 -77
- package/src/canvas/canvas-origin.ts +37 -12
- package/src/canvas/canvas-page-context.ts +112 -0
- package/src/canvas/canvas-patcher.ts +30 -30
- package/src/canvas/canvas-perf.ts +11 -5
- package/src/canvas/canvas-render.ts +175 -148
- package/src/canvas/canvas-surface.ts +5 -3
- package/src/canvas/canvas-utils.ts +147 -61
- package/src/canvas/dialog-path.ts +9 -4
- package/src/canvas/dialog-state.ts +1 -2
- package/src/canvas/diff-marks.ts +9 -16
- package/src/canvas/diff-toolbar.ts +8 -7
- package/src/canvas/edit-width-drag.ts +4 -1
- package/src/canvas/edit-width.ts +1 -3
- package/src/canvas/iframe-channel.ts +27 -13
- package/src/canvas/iframe-drop.ts +120 -12
- package/src/canvas/iframe-editable-root.ts +29 -16
- package/src/canvas/iframe-entry.ts +104 -75
- package/src/canvas/iframe-eval.ts +73 -49
- package/src/canvas/iframe-host.ts +269 -158
- package/src/canvas/iframe-inline-edit.ts +49 -30
- package/src/canvas/iframe-insert.ts +5 -4
- package/src/canvas/iframe-interaction.ts +30 -25
- package/src/canvas/iframe-keys.ts +11 -0
- package/src/canvas/iframe-overlay.ts +1 -1
- package/src/canvas/iframe-patch.ts +25 -21
- package/src/canvas/iframe-position.ts +12 -6
- package/src/canvas/iframe-protocol.ts +36 -21
- package/src/canvas/iframe-render.ts +84 -35
- package/src/canvas/iframe-slash.ts +24 -11
- package/src/canvas/iframe-subtree.ts +10 -3
- package/src/canvas/npm-modules.ts +22 -0
- package/src/canvas/path-mapping.ts +19 -12
- package/src/canvas/popover-path.ts +7 -3
- package/src/canvas/popover-state.ts +1 -2
- package/src/canvas/preview-navigate.ts +16 -0
- package/src/canvas/repeater-sources.ts +200 -0
- package/src/canvas/serialize-scope.ts +13 -5
- package/src/canvas/stage-scrollbar.ts +7 -2
- package/src/canvas/surface-registry.ts +27 -3
- package/src/collab/collab-commands.ts +7 -2
- package/src/collab/collab-session.ts +84 -45
- package/src/collab/collab-state.ts +4 -3
- package/src/collab/monaco-binding.ts +16 -13
- package/src/collab/monaco-cursors.ts +5 -2
- package/src/commands/app-commands.ts +16 -6
- package/src/commands/budget.ts +13 -11
- package/src/commands/command-args.ts +103 -71
- package/src/commands/context.ts +55 -28
- package/src/commands/defaults.ts +69 -45
- package/src/commands/keymap.ts +56 -43
- package/src/commands/levels.ts +14 -8
- package/src/commands/live-context.ts +9 -7
- package/src/commands/reference.ts +2 -1
- package/src/commands/registry.ts +22 -11
- package/src/commands/run-reported.ts +15 -10
- package/src/component-props.ts +46 -30
- package/src/content/collection-match.ts +11 -9
- package/src/content/collection-pages.ts +412 -0
- package/src/content/content-collections.ts +270 -0
- package/src/content/draft-state.ts +4 -3
- package/src/content/entry-commands.ts +54 -62
- package/src/content/entry-editor.ts +137 -32
- package/src/content/entry-fields.ts +25 -8
- package/src/content/entry-inspector.ts +225 -0
- package/src/content/entry-model.ts +38 -15
- package/src/content/entry-open.ts +80 -0
- package/src/content/entry-summaries.ts +440 -0
- package/src/content/file-record-ids.ts +80 -0
- package/src/content/file-records.ts +125 -0
- package/src/content/page-summaries.ts +129 -0
- package/src/content/record-entry.ts +201 -0
- package/src/content/writing-banner.ts +177 -0
- package/src/create/add-new-commands.ts +185 -0
- package/src/create/add-new-flow.ts +1116 -0
- package/src/create/add-new.ts +1014 -0
- package/src/create/element-name.ts +121 -0
- package/src/editor/canvas-context-menu.ts +1 -2
- package/src/editor/canvas-slash-bridge.ts +3 -6
- package/src/editor/content-slice.ts +16 -14
- package/src/editor/context-menu.ts +35 -22
- package/src/editor/convert-targets.ts +30 -35
- package/src/editor/convert-to-component.ts +78 -48
- package/src/editor/convert-to-repeater.ts +34 -22
- package/src/editor/editable-tags.ts +17 -2
- package/src/editor/file-drop-action.ts +29 -22
- package/src/editor/inline-edit-apply.ts +79 -17
- package/src/editor/inline-edit.ts +76 -50
- package/src/editor/inline-format.ts +59 -56
- package/src/editor/inline-link.ts +40 -15
- package/src/editor/insert-component.ts +91 -0
- package/src/editor/insert-zone-action.ts +14 -5
- package/src/editor/merge-tags.ts +58 -19
- package/src/editor/repeater-scope.ts +39 -28
- package/src/editor/shortcuts.ts +76 -42
- package/src/editor/slash-menu.ts +52 -14
- package/src/files/asset-versions.ts +4 -4
- package/src/files/components.ts +89 -20
- package/src/files/elements.ts +5 -6
- package/src/files/file-ops.ts +92 -41
- package/src/files/files.ts +248 -239
- package/src/files/fs-events.ts +18 -7
- package/src/files/media-meta.ts +97 -2
- package/src/files/media-paths.ts +3 -0
- package/src/files/media-types.ts +55 -0
- package/src/files/media-upload.ts +209 -52
- package/src/files/new-file-name.ts +156 -0
- package/src/files/serialize-document.ts +15 -5
- package/src/format/constraints.ts +1 -1
- package/src/format/convert-file.ts +27 -13
- package/src/format/format-choices.ts +2 -1
- package/src/format/format-host.ts +91 -50
- package/src/github/github-auth-status.ts +27 -3
- package/src/github/github-auth.ts +21 -11
- package/src/github/github-publish.ts +28 -23
- package/src/grid/cell-editors.ts +46 -31
- package/src/grid/cell-popovers.ts +5 -3
- package/src/grid/edit-buffer.ts +10 -9
- package/src/grid/grid-controller.ts +25 -23
- package/src/grid/grid-layout.ts +21 -5
- package/src/grid/grid-open.ts +3 -33
- package/src/grid/grid-panel.ts +65 -6
- package/src/grid/grid-reveal.ts +34 -0
- package/src/grid/grid-source.ts +9 -31
- package/src/grid/grid-view.ts +45 -23
- package/src/grid/redirects-grid.ts +14 -5
- package/src/grid/redirects.ts +16 -14
- package/src/grid/schema-columns.ts +25 -19
- package/src/grid/sources/connector-source.ts +47 -20
- package/src/grid/sources/content-source.ts +100 -31
- package/src/grid/sources/csv-file-source.ts +2 -2
- package/src/grid/tabulator-tables.d.ts +61 -34
- package/src/hosting/document.ts +2 -2
- package/src/hosting/stage.ts +7 -7
- package/src/i18n/i18n-commands.ts +3 -3
- package/src/media/media-commands.ts +181 -0
- package/src/media/media-library-model.ts +232 -0
- package/src/media/media-library.ts +1283 -0
- package/src/media/media-pane.ts +51 -20
- package/src/new-project/add-repo-modal.ts +20 -19
- package/src/new-project/import-tab.ts +10 -14
- package/src/new-project/location-fields.ts +2 -1
- package/src/new-project/new-project-modal.ts +12 -9
- package/src/packages/jxsuite-update.ts +4 -3
- package/src/packages/pull-package-sync.ts +26 -25
- package/src/page-params.ts +97 -68
- package/src/panels/activity-panel.ts +38 -14
- package/src/panels/ai-chat/attached-context.ts +13 -7
- package/src/panels/ai-chat/chat-markdown.ts +8 -8
- package/src/panels/ai-chat/chat-view.ts +80 -73
- package/src/panels/ai-chat/composer.ts +30 -33
- package/src/panels/ai-chat/sessions-view.ts +1 -2
- package/src/panels/ai-panel.ts +23 -20
- package/src/panels/block-action-bar.ts +57 -46
- package/src/panels/bottom-dock.ts +5 -3
- package/src/panels/canvas-dnd-bridge.ts +36 -26
- package/src/panels/chat-panel.ts +6 -6
- package/src/panels/collections-panel.ts +306 -0
- package/src/panels/component-preview.ts +35 -13
- package/src/panels/data-explorer.ts +63 -26
- package/src/panels/data-grid.ts +18 -11
- package/src/panels/dnd.ts +115 -138
- package/src/panels/editors.ts +52 -21
- package/src/panels/elements-panel.ts +19 -5
- package/src/panels/empty-state.ts +2 -2
- package/src/panels/events-panel.ts +84 -69
- package/src/panels/formula-workspace.ts +78 -54
- package/src/panels/frontmatter-fields.ts +33 -28
- package/src/panels/git-diff-open.ts +10 -6
- package/src/panels/head-panel.ts +488 -202
- package/src/panels/i18n-panel.ts +20 -12
- package/src/panels/imports-panel.ts +25 -47
- package/src/panels/jump-bar.ts +75 -31
- package/src/panels/layers-panel.ts +130 -101
- package/src/panels/left-panel.ts +25 -11
- package/src/panels/navigator-panels.ts +23 -11
- package/src/panels/pane-context.ts +140 -49
- package/src/panels/pane-grid.ts +55 -26
- package/src/panels/panel-registry.ts +36 -46
- package/src/panels/properties-panel.ts +182 -124
- package/src/panels/quick-search.ts +53 -31
- package/src/panels/right-panel.ts +201 -45
- package/src/panels/seo-modal.ts +54 -125
- package/src/panels/settings-menu.ts +63 -61
- package/src/panels/settings-pane.ts +268 -162
- package/src/panels/signals-panel.ts +156 -135
- package/src/panels/site-model.ts +367 -0
- package/src/panels/site-panel.ts +638 -0
- package/src/panels/statement-editor.ts +230 -140
- package/src/panels/style-panel.ts +253 -206
- package/src/panels/style-utils.ts +114 -51
- package/src/panels/stylebook-doc.ts +195 -60
- package/src/panels/stylebook-layers-panel.ts +48 -89
- package/src/panels/stylebook-panel.ts +60 -27
- package/src/panels/styles-panel.ts +327 -0
- package/src/panels/tab-drop.ts +27 -11
- package/src/panels/tab-strip.ts +143 -49
- package/src/platform-errors.ts +57 -43
- package/src/platform.ts +59 -31
- package/src/platforms/cloud.ts +373 -176
- package/src/platforms/default-platform.ts +14 -8
- package/src/platforms/devserver.ts +180 -142
- package/src/platforms/read-batcher.ts +16 -8
- package/src/project-list.ts +2 -1
- package/src/publish/deploy-checklist.ts +20 -9
- package/src/publish/pages-service.ts +66 -27
- package/src/publish/publish-changes.ts +491 -0
- package/src/publish/publish-commands.ts +88 -22
- package/src/publish/publish-ids.ts +49 -0
- package/src/publish/publish-panel.ts +10 -6
- package/src/publish/publish-quick.ts +148 -0
- package/src/publish/publish-workspace.ts +926 -0
- package/src/publish/source-control.ts +907 -0
- package/src/recent-projects.ts +15 -8
- package/src/resize-edges.ts +26 -36
- package/src/services/a11y-report.ts +26 -19
- package/src/services/agent-seed.ts +23 -11
- package/src/services/ai-ask.ts +9 -12
- package/src/services/ai-command-tools.ts +68 -62
- package/src/services/ai-import-tools.ts +80 -67
- package/src/services/ai-models.ts +17 -13
- package/src/services/ai-project-tools.ts +68 -32
- package/src/services/ai-session-store.ts +19 -10
- package/src/services/ai-system-prompt.ts +46 -20
- package/src/services/ai-tools.ts +71 -27
- package/src/services/ai-write-report.ts +20 -12
- package/src/services/ai-writes.ts +4 -4
- package/src/services/automation.ts +74 -51
- package/src/services/cem-export.ts +149 -58
- package/src/services/cf-settings.ts +4 -4
- package/src/services/clock.ts +6 -1
- package/src/services/code-services.ts +37 -31
- package/src/services/connection.ts +2 -2
- package/src/services/context-manager.ts +14 -10
- package/src/services/context-resolver.ts +20 -8
- package/src/services/data-service.ts +19 -15
- package/src/services/directory-picker.ts +20 -14
- package/src/services/document-assistant.ts +39 -19
- package/src/services/gated-registry.ts +10 -6
- package/src/services/harness/chat-reducer.ts +23 -1
- package/src/services/import-client.ts +91 -38
- package/src/services/import-seed.ts +8 -2
- package/src/services/jx-validate.ts +28 -31
- package/src/services/live-preview.ts +0 -0
- package/src/services/live-surfaces.ts +6 -3
- package/src/services/model-uri.ts +10 -2
- package/src/services/module-preloads.ts +12 -8
- package/src/services/monaco-buffer.ts +43 -24
- package/src/services/monaco-lazy.ts +7 -8
- package/src/services/monaco-setup.ts +23 -16
- package/src/services/notify.ts +44 -17
- package/src/services/popover-report.ts +25 -10
- package/src/services/preview-eval.ts +20 -8
- package/src/services/profile.ts +13 -6
- package/src/services/project-path.ts +13 -10
- package/src/services/references.ts +0 -0
- package/src/services/render-critic.ts +29 -9
- package/src/services/settings/definitions.ts +25 -14
- package/src/services/settings/kernel.ts +13 -4
- package/src/services/settings/write-queue.ts +9 -9
- package/src/services/token-lint.ts +12 -10
- package/src/services/tool-executor.ts +17 -5
- package/src/services/tool-outcomes.ts +44 -24
- package/src/services/trusted-types.ts +24 -7
- package/src/settings/contexts-section.ts +11 -8
- package/src/settings/contributed-section.ts +128 -70
- package/src/settings/css-vars-editor.ts +119 -32
- package/src/settings/defs-editor.ts +38 -35
- package/src/settings/developer-panels.ts +43 -0
- package/src/settings/extension-rows.ts +16 -8
- package/src/settings/extension-sections.ts +42 -14
- package/src/settings/extensions-section.ts +4 -1
- package/src/settings/general-settings.ts +101 -28
- package/src/settings/locales-section.ts +20 -12
- package/src/settings/preferences-dialog.ts +17 -7
- package/src/settings/preferences-keymap.ts +7 -5
- package/src/settings/preferences-sections.ts +11 -5
- package/src/settings/project-sections.ts +28 -11
- package/src/settings/schema-field-ui.ts +12 -1
- package/src/settings/section-registry.ts +116 -11
- package/src/settings/settings-dialog.ts +192 -0
- package/src/settings/settings-document.ts +186 -166
- package/src/shell/tree.ts +6 -5
- package/src/shell.ts +536 -477
- package/src/site-context.ts +101 -32
- package/src/state.ts +68 -57
- package/src/store.ts +54 -34
- package/src/studio.ts +219 -116
- package/src/style/live-preview.ts +1 -1
- package/src/style/project-styles.ts +40 -7
- package/src/style/stylebook-specimens.ts +89 -0
- package/src/style/stylebook-target.ts +26 -0
- package/src/style/tag-inheritance.ts +105 -0
- package/src/style/token-ref.ts +5 -4
- package/src/style/variables-focus.ts +61 -0
- package/src/style/variables-view.ts +235 -0
- package/src/surfaces/about.ts +18 -19
- package/src/surfaces/add-new.json +1482 -0
- package/src/surfaces/add-new.ts +325 -0
- package/src/surfaces/add-repo.ts +17 -19
- package/src/surfaces/ai-chat.ts +33 -38
- package/src/surfaces/ai-credentials-form.ts +6 -16
- package/src/surfaces/ai-managed-connect.ts +3 -4
- package/src/surfaces/ai-model-picker.ts +3 -5
- package/src/surfaces/block-action-bar.ts +22 -11
- package/src/surfaces/boot-failure.json +4 -3
- package/src/surfaces/boot-failure.ts +4 -5
- package/src/surfaces/bottom-dock.json +1 -1
- package/src/surfaces/bottom-dock.ts +9 -18
- package/src/surfaces/canvas-stage.json +30 -19
- package/src/surfaces/canvas-stage.ts +17 -16
- package/src/surfaces/cf-account-picker.ts +17 -18
- package/src/surfaces/collections-panel.json +615 -0
- package/src/surfaces/collections-panel.ts +168 -0
- package/src/surfaces/commandbar.json +241 -96
- package/src/surfaces/commandbar.ts +145 -105
- package/src/surfaces/convert-repeater.ts +17 -19
- package/src/surfaces/data-actions.ts +9 -7
- package/src/surfaces/dialog.ts +26 -28
- package/src/surfaces/diff-toolbar.ts +9 -7
- package/src/surfaces/empty-state.json +1 -1
- package/src/surfaces/empty-state.ts +7 -10
- package/src/surfaces/entry-editor.json +136 -0
- package/src/surfaces/entry-editor.ts +35 -11
- package/src/surfaces/entry-inspector.json +371 -0
- package/src/surfaces/entry-inspector.ts +143 -0
- package/src/surfaces/expression-editor.ts +11 -25
- package/src/surfaces/files-panel.ts +27 -14
- package/src/surfaces/formula-palette.ts +4 -5
- package/src/surfaces/github-auth.ts +16 -20
- package/src/surfaces/github-publish.ts +17 -22
- package/src/surfaces/grid-cell.ts +30 -31
- package/src/surfaces/grid-open.ts +10 -14
- package/src/surfaces/grid-panel.ts +13 -9
- package/src/surfaces/grid-replace.ts +13 -13
- package/src/surfaces/grid-views.ts +15 -18
- package/src/surfaces/inspector-dock.json +162 -14
- package/src/surfaces/inspector-dock.ts +59 -31
- package/src/surfaces/jump-bar.ts +5 -6
- package/src/surfaces/jxsuite-update.ts +16 -22
- package/src/surfaces/library-pane.json +670 -437
- package/src/surfaces/library-pane.ts +111 -77
- package/src/surfaces/logic-panel.ts +17 -30
- package/src/surfaces/logic-workspace.ts +13 -15
- package/src/surfaces/media-browser.json +62 -6
- package/src/surfaces/media-browser.ts +26 -11
- package/src/surfaces/media-field.json +1 -1
- package/src/surfaces/media-field.ts +4 -5
- package/src/surfaces/media-library.json +1586 -0
- package/src/surfaces/media-library.ts +282 -0
- package/src/surfaces/media-pane.ts +8 -7
- package/src/surfaces/media-pick-dialog.json +71 -0
- package/src/surfaces/media-pick-dialog.ts +132 -0
- package/src/surfaces/menu.ts +47 -23
- package/src/surfaces/navigator-dock.ts +11 -12
- package/src/surfaces/new-project.ts +27 -26
- package/src/surfaces/pane-context.json +144 -77
- package/src/surfaces/pane-context.ts +44 -25
- package/src/surfaces/pane-grid.ts +15 -10
- package/src/surfaces/panel-activity.ts +5 -6
- package/src/surfaces/panel-data.ts +3 -4
- package/src/surfaces/panel-deploy-checklist.json +12 -6
- package/src/surfaces/panel-deploy-checklist.ts +8 -7
- package/src/surfaces/panel-elements.ts +5 -7
- package/src/surfaces/panel-i18n.ts +5 -7
- package/src/surfaces/panel-imports.ts +6 -13
- package/src/surfaces/panel-outline.ts +25 -19
- package/src/surfaces/panel-page.json +72 -2
- package/src/surfaces/panel-page.ts +49 -25
- package/src/surfaces/panel-problems.ts +5 -7
- package/src/surfaces/panel-signals.json +1 -1
- package/src/surfaces/panel-signals.ts +25 -39
- package/src/surfaces/panel-stylebook-layers.json +71 -204
- package/src/surfaces/panel-stylebook-layers.ts +5 -29
- package/src/surfaces/preferences.json +120 -48
- package/src/surfaces/preferences.ts +29 -11
- package/src/surfaces/progress-modal.ts +15 -9
- package/src/surfaces/properties-panel.json +1 -1
- package/src/surfaces/properties-panel.ts +20 -42
- package/src/surfaces/publish-quick.json +428 -0
- package/src/surfaces/publish-quick.ts +165 -0
- package/src/surfaces/publish-workspace.json +2892 -0
- package/src/surfaces/publish-workspace.ts +391 -0
- package/src/surfaces/publish.ts +18 -18
- package/src/surfaces/push-plan.ts +7 -8
- package/src/surfaces/rail.json +1 -159
- package/src/surfaces/rail.ts +22 -79
- package/src/surfaces/reference-field.ts +11 -15
- package/src/surfaces/schema-builder.ts +16 -22
- package/src/surfaces/schema-form.json +10 -2
- package/src/surfaces/schema-form.ts +17 -25
- package/src/surfaces/secret-field.ts +9 -6
- package/src/surfaces/seo.json +1 -1
- package/src/surfaces/seo.ts +21 -15
- package/src/surfaces/settings-contexts.ts +9 -7
- package/src/surfaces/settings-contributed.json +5 -5
- package/src/surfaces/settings-contributed.ts +32 -12
- package/src/surfaces/settings-css-vars.json +44 -36
- package/src/surfaces/settings-css-vars.ts +82 -16
- package/src/surfaces/settings-defs.json +2 -2
- package/src/surfaces/settings-defs.ts +17 -21
- package/src/surfaces/settings-deploy.json +2 -15
- package/src/surfaces/settings-deploy.ts +9 -16
- package/src/surfaces/settings-dialog.json +51 -0
- package/src/surfaces/settings-dialog.ts +169 -0
- package/src/surfaces/settings-extensions.ts +5 -6
- package/src/surfaces/settings-head.ts +33 -39
- package/src/surfaces/settings-locales.ts +8 -20
- package/src/surfaces/settings-overview.json +3 -3
- package/src/surfaces/settings-overview.ts +14 -8
- package/src/surfaces/settings-packages.ts +8 -7
- package/src/surfaces/settings-pane.json +269 -46
- package/src/surfaces/settings-pane.ts +88 -36
- package/src/surfaces/settings-rawjson.ts +5 -6
- package/src/surfaces/shell.json +2 -1
- package/src/surfaces/shell.ts +1 -2
- package/src/surfaces/site-panel.json +1029 -0
- package/src/surfaces/site-panel.ts +233 -0
- package/src/surfaces/slash-menu.json +63 -21
- package/src/surfaces/slash-menu.ts +42 -9
- package/src/surfaces/statements.ts +13 -18
- package/src/surfaces/statusbar.ts +56 -29
- package/src/surfaces/style-panel.ts +18 -31
- package/src/surfaces/stylebook-chrome.ts +3 -5
- package/src/surfaces/styles-panel.json +735 -0
- package/src/surfaces/styles-panel.ts +195 -0
- package/src/surfaces/styles-variables.json +149 -0
- package/src/surfaces/styles-variables.ts +145 -0
- package/src/surfaces/tab-strip.json +4 -4
- package/src/surfaces/tab-strip.ts +14 -13
- package/src/surfaces/target-line.ts +6 -7
- package/src/surfaces/welcome.ts +6 -8
- package/src/surfaces/writing-banner.json +94 -0
- package/src/surfaces/writing-banner.ts +80 -0
- package/src/tabs/bare-repeaters.ts +101 -0
- package/src/tabs/patch-ops.ts +4 -3
- package/src/tabs/project-config.ts +32 -21
- package/src/tabs/selection.ts +36 -8
- package/src/tabs/tab.ts +99 -48
- package/src/tabs/transact.ts +98 -53
- package/src/types.ts +53 -43
- package/src/ui/ai-credentials-form.ts +10 -13
- package/src/ui/ai-model-picker.ts +3 -3
- package/src/ui/cf-account-picker.ts +7 -9
- package/src/ui/color-selector.ts +9 -5
- package/src/ui/dynamic-slot.ts +7 -8
- package/src/ui/expression-editor.ts +112 -105
- package/src/ui/form-controls.ts +104 -56
- package/src/ui/formula-catalog.ts +239 -230
- package/src/ui/formula-chips.ts +33 -18
- package/src/ui/layers.ts +57 -31
- package/src/ui/media-picker.ts +300 -47
- package/src/ui/popover-surface.ts +20 -12
- package/src/ui/progress-modal.ts +13 -8
- package/src/ui/regions.ts +14 -12
- package/src/ui/schema-form.ts +372 -97
- package/src/ui/surface.ts +71 -8
- package/src/ui/timing.ts +9 -0
- package/src/ui/value-source.ts +44 -31
- package/src/ui/virtual-window.ts +29 -3
- package/src/utils/base64.ts +6 -4
- package/src/utils/edit-display.ts +366 -111
- package/src/utils/element-selectors.ts +3 -2
- package/src/utils/google-fonts.ts +15 -14
- package/src/utils/inherited-style.ts +18 -20
- package/src/utils/link-target.ts +1 -1
- package/src/utils/local-day.ts +18 -0
- package/src/utils/preview-format.ts +25 -3
- package/src/utils/strip-events.ts +49 -22
- package/src/utils/studio-utils.ts +29 -14
- package/src/view.ts +2 -3
- package/src/workspace/pane-derive.ts +14 -16
- package/src/workspace/session.ts +81 -50
- package/src/workspace/tab-workspace.ts +137 -0
- package/src/workspace/workspace-back.ts +189 -0
- package/src/workspace/workspace-home.ts +133 -0
- package/src/workspace/workspace-ids.ts +129 -0
- package/src/workspace/workspace-switch.ts +372 -0
- package/src/workspace/workspace.ts +165 -47
- package/styles/shell-frame.css +30 -7
- package/styles/shell-frame.json +15 -3
- package/dist/chunks/2020-va2wwra4.js +0 -4
- package/dist/chunks/adapter-f93pafd7.js +0 -4
- package/dist/chunks/cf-account-picker-af1h381v.js +0 -4
- package/dist/chunks/cf-settings-125tdm0w.js +0 -4
- package/dist/chunks/color-area-jqhna8zy.js +0 -4
- package/dist/chunks/color-field-embq5ctg.js +0 -4
- package/dist/chunks/color-fpezvnkt.js +0 -4
- package/dist/chunks/color-slider-1qmw7wqy.js +0 -4
- package/dist/chunks/combobox-773hdzyj.js +0 -4
- package/dist/chunks/components-ax2f1fnn.js +0 -4
- package/dist/chunks/content-source-108aqtk1.js +0 -4
- package/dist/chunks/convert-to-repeater-ry5rmppr.js +0 -4
- package/dist/chunks/dialog-mtm9fq5t.js +0 -4
- package/dist/chunks/editor-pfgdmkb0.js +0 -4
- package/dist/chunks/entry-commands-t0k1q0kx.js +0 -4
- package/dist/chunks/extension-sections-6mwh2hf8.js +0 -4
- package/dist/chunks/file-ops-meej5mqf.js +0 -4
- package/dist/chunks/files-44vp8a2s.js +0 -4
- package/dist/chunks/format-host-7q1petb9.js +0 -4
- package/dist/chunks/fs-events-h2mwnxaz.js +0 -4
- package/dist/chunks/git-panel-zngbymvd.js +0 -4
- package/dist/chunks/grid-view-sx9vynsp.js +0 -51
- package/dist/chunks/icons-p44xhjf0.js +0 -4
- package/dist/chunks/iframe-entry-f7mqjwpm.js +0 -5
- package/dist/chunks/iframe-entry-haw7jkvz.js +0 -5
- package/dist/chunks/iframe-entry-z6qrytv3.js +0 -5
- package/dist/chunks/iframe-host-qk3ycq87.js +0 -4
- package/dist/chunks/index-wkyfjeqz.js +0 -4
- package/dist/chunks/jsonMode-s8kt9dsj.js +0 -13
- package/dist/chunks/jx-validate-psgd7vsx.js +0 -4
- package/dist/chunks/layers-panel-1kq2jyfk.js +0 -4
- package/dist/chunks/layers-xd92fg8d.js +0 -4
- package/dist/chunks/listbox-s8fkmnq8.js +0 -4
- package/dist/chunks/lit-html-ac4cwwr4.js +0 -4
- package/dist/chunks/media-picker-0svzkdyb.js +0 -4
- package/dist/chunks/menu-37d07z9m.js +0 -4
- package/dist/chunks/menu-5c55ea3d.js +0 -4
- package/dist/chunks/monaco-binding-z6r70pdt.js +0 -4
- package/dist/chunks/monaco-buffer-fz6gq5y7.js +0 -4
- package/dist/chunks/monaco-setup-q4axkgj7.js +0 -4
- package/dist/chunks/notify-0xgtn8q1.js +0 -4
- package/dist/chunks/number-field-gw7sq8rn.js +0 -4
- package/dist/chunks/parse-897fvqah.js +0 -4
- package/dist/chunks/platform-v1qctbe6.js +0 -4
- package/dist/chunks/popover-8htra4er.js +0 -4
- package/dist/chunks/publish-panel-ym1fmgq5.js +0 -4
- package/dist/chunks/quick-search-a54e6t5n.js +0 -4
- package/dist/chunks/references-gmqp82fs.js +0 -4
- package/dist/chunks/right-panel-v7t9awbk.js +0 -4
- package/dist/chunks/section-registry-vhyf3zsr.js +0 -4
- package/dist/chunks/select-n3v7gwas.js +0 -4
- package/dist/chunks/split-pff8f3ze.js +0 -4
- package/dist/chunks/studio-0bhx5hz6.js +0 -5
- package/dist/chunks/studio-0k1ddnyy.js +0 -6
- package/dist/chunks/studio-1b04pd0j.js +0 -64
- package/dist/chunks/studio-4dwbj6gb.js +0 -5
- package/dist/chunks/studio-4tanh828.js +0 -5
- package/dist/chunks/studio-55h4mstj.js +0 -4
- package/dist/chunks/studio-5tnvjkz4.js +0 -5
- package/dist/chunks/studio-60dqgb0c.js +0 -5
- package/dist/chunks/studio-61dp09qg.js +0 -9
- package/dist/chunks/studio-63epcrj2.js +0 -5
- package/dist/chunks/studio-74k29dy9.js +0 -5
- package/dist/chunks/studio-7vg6jcd3.js +0 -5
- package/dist/chunks/studio-7zx31knw.js +0 -9
- package/dist/chunks/studio-8bxk97km.js +0 -5
- package/dist/chunks/studio-933e30n4.js +0 -5
- package/dist/chunks/studio-98wnwhbg.js +0 -5
- package/dist/chunks/studio-9mccjwjh.js +0 -14
- package/dist/chunks/studio-a2ke1e4t.js +0 -5
- package/dist/chunks/studio-e3a2a6v3.js +0 -4
- package/dist/chunks/studio-e81q1vb9.js +0 -5
- package/dist/chunks/studio-epgrmqqm.js +0 -5
- package/dist/chunks/studio-ery42sqr.js +0 -4
- package/dist/chunks/studio-g1shz58g.js +0 -5
- package/dist/chunks/studio-gf0fz03c.js +0 -5
- package/dist/chunks/studio-gjtpe62q.js +0 -6
- package/dist/chunks/studio-gkskhg7m.js +0 -5
- package/dist/chunks/studio-gx4dnjzd.js +0 -5
- package/dist/chunks/studio-j03zga3y.js +0 -9
- package/dist/chunks/studio-jhpxeckz.js +0 -5
- package/dist/chunks/studio-k01r7ykp.js +0 -5
- package/dist/chunks/studio-kk7h30ne.js +0 -7
- package/dist/chunks/studio-kt1zqney.js +0 -5
- package/dist/chunks/studio-m3cwhvye.js +0 -5
- package/dist/chunks/studio-mcayt6hz.js +0 -5
- package/dist/chunks/studio-myjcghv3.js +0 -5
- package/dist/chunks/studio-n8bxwc3y.js +0 -4
- package/dist/chunks/studio-ngm8gvpb.js +0 -5
- package/dist/chunks/studio-nj5a01jy.js +0 -928
- package/dist/chunks/studio-pcgz8heb.js +0 -5
- package/dist/chunks/studio-qcsq0t15.js +0 -5
- package/dist/chunks/studio-qkstvve9.js +0 -5
- package/dist/chunks/studio-qv1pwd3c.js +0 -5
- package/dist/chunks/studio-rc6dvxjb.js +0 -7
- package/dist/chunks/studio-s8dmmz66.js +0 -5
- package/dist/chunks/studio-sp5xzbj4.js +0 -6
- package/dist/chunks/studio-szsjbj0g.js +0 -5
- package/dist/chunks/studio-t1xzawhr.js +0 -16
- package/dist/chunks/studio-tdcprbqb.js +0 -5
- package/dist/chunks/studio-vrmcjf9g.js +0 -7
- package/dist/chunks/studio-vyc0b4he.js +0 -10
- package/dist/chunks/studio-w0cgf6jg.js +0 -5
- package/dist/chunks/studio-w3hskzqk.js +0 -5
- package/dist/chunks/studio-xgrgcdyh.js +0 -9
- package/dist/chunks/studio-xz2yxfcb.js +0 -5
- package/dist/chunks/studio-y6n42tgs.js +0 -5
- package/dist/chunks/studio-yb560vxr.js +0 -5
- package/dist/chunks/studio-yn9f8vtx.js +0 -5
- package/dist/chunks/studio-yw05w8f0.js +0 -7
- package/dist/chunks/studio-z16sgavc.js +0 -5
- package/dist/chunks/studio-zey7zsdw.js +0 -12
- package/dist/chunks/studio-zyqn40w4.js +0 -5
- package/dist/chunks/swatch-group-8qh9a3bm.js +0 -4
- package/dist/chunks/tabs-jwthg40w.js +0 -4
- package/dist/chunks/textfield-spy3hc77.js +0 -4
- package/dist/chunks/toast-4d35zd64.js +0 -4
- package/dist/chunks/toast-host-8sjmfed3.js +0 -4
- package/dist/chunks/tooltip-fd6g9xy3.js +0 -4
- package/dist/chunks/tree-h19v1jfz.js +0 -4
- package/dist/chunks/tsMode-2frah6h1.js +0 -14
- package/dist/chunks/workspace-zt81g1ms.js +0 -4
- package/dist/chunks/ws-client-n2bh4hkf.js +0 -4
- package/src/panels/frontmatter-panel.ts +0 -701
- package/src/panels/git-panel.ts +0 -1120
- package/src/surfaces/doc-header.json +0 -776
- package/src/surfaces/doc-header.ts +0 -231
- package/src/surfaces/git-panel.json +0 -1880
- package/src/surfaces/git-panel.ts +0 -272
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ids of a file-backed collection's records, as the content loader gives them.
|
|
3
|
+
*
|
|
4
|
+
* A collection whose `source` names one file (`./content/projects.csv`) has one entry per record,
|
|
5
|
+
* and the build names each one: a CSV row by the collection's `idField`, else its `id`, `sku`,
|
|
6
|
+
* `slug` or `Slug` cell, else its row number from 0 (`coerceCSVRows` in `@jxsuite/parser`); an
|
|
7
|
+
* object of a JSON array by its `id`, else `<file stem>-<n>` (`loadJSONEntries`). The Site panel
|
|
8
|
+
* counts these and a reference picker offers them, so both must agree with the build.
|
|
9
|
+
*
|
|
10
|
+
* A leaf over the file's text: `grid/sources/content-source.ts` reads the file and hands it here.
|
|
11
|
+
* `content/file-records.ts` names the same records for the Content home, but it reaches the entry
|
|
12
|
+
* summaries, which reach the content source, so this module imports only the CSV codec.
|
|
13
|
+
*
|
|
14
|
+
* @docs studio/projects/browse
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { isNonNullObject, isNumber, isString } from "@jxsuite/schema/guards";
|
|
18
|
+
import { parseCsv } from "../grid/csv-codec";
|
|
19
|
+
import type { JsonValue } from "@jxsuite/schema/types";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The ids the CSV loader gives each data row; blank rows are skipped, as the Content home skips
|
|
23
|
+
* them.
|
|
24
|
+
*/
|
|
25
|
+
function csvIds(text: string, idField: string | undefined): string[] {
|
|
26
|
+
const { headers, rows } = parseCsv(text);
|
|
27
|
+
const candidates = [idField, "id", "sku", "slug", "Slug"].flatMap((name) => {
|
|
28
|
+
const at = name === undefined ? -1 : headers.indexOf(name);
|
|
29
|
+
return at === -1 ? [] : [at];
|
|
30
|
+
});
|
|
31
|
+
return rows
|
|
32
|
+
.filter((row) => row.some((cell) => cell.trim() !== ""))
|
|
33
|
+
.map((row, index) => {
|
|
34
|
+
const at = candidates.find((candidate) => (row[candidate] ?? "") !== "");
|
|
35
|
+
return at === undefined ? String(index) : row[at]!;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The ids the JSON loader gives: each object of an array, `<stem>-<n>` where it names none; a file
|
|
41
|
+
* that is ONE object (`docs/nav.json`) is one entry, its own `id` else the file's stem
|
|
42
|
+
* (`loadJSONEntries`). `[]` for text that is not JSON.
|
|
43
|
+
*/
|
|
44
|
+
function jsonIds(text: string, stem: string): string[] {
|
|
45
|
+
let parsed: JsonValue;
|
|
46
|
+
try {
|
|
47
|
+
// SAFETY: JSON.parse only ever yields JSON values; its `any` return type says nothing more.
|
|
48
|
+
parsed = JSON.parse(text) as JsonValue;
|
|
49
|
+
} catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
const idOf = (item: JsonValue, fallback: string): string => {
|
|
53
|
+
const id = isNonNullObject(item) && !Array.isArray(item) ? item.id : undefined;
|
|
54
|
+
return isString(id) || isNumber(id) ? String(id) : fallback;
|
|
55
|
+
};
|
|
56
|
+
if (!Array.isArray(parsed)) {
|
|
57
|
+
return [idOf(parsed, stem)];
|
|
58
|
+
}
|
|
59
|
+
return parsed.map((item, index) => idOf(item, `${stem}-${index}`));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The record ids a collection file holds.
|
|
64
|
+
*
|
|
65
|
+
* @param path The file's project-relative path; its extension says how to read it.
|
|
66
|
+
* @param text The file's text.
|
|
67
|
+
* @param idField The collection's declared `idField`, if any.
|
|
68
|
+
* @returns The ids in file order, or `[]` for a file kind that holds no records Studio reads.
|
|
69
|
+
*/
|
|
70
|
+
export function fileRecordIds(path: string, text: string, idField?: string): string[] {
|
|
71
|
+
const lower = path.toLowerCase();
|
|
72
|
+
if (lower.endsWith(".csv")) {
|
|
73
|
+
return csvIds(text, idField);
|
|
74
|
+
}
|
|
75
|
+
if (lower.endsWith(".json")) {
|
|
76
|
+
const stem = (path.split("/").pop() ?? path).replace(/\.json$/i, "");
|
|
77
|
+
return jsonIds(text, stem);
|
|
78
|
+
}
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The entries of a file-backed collection: the records of the one file its `source` names.
|
|
3
|
+
*
|
|
4
|
+
* A collection whose `source` is a file (`content.pricing.source: "./content/pricing/prices.csv"`)
|
|
5
|
+
* has one entry per record, exactly as the parser's loader reads it: a CSV row under the header, or
|
|
6
|
+
* an object of a JSON array. The Content workspace lists those records, so a fifteen-row price list
|
|
7
|
+
* reads "Pricing 15" with a row per record rather than "Pricing 1" and the file's name
|
|
8
|
+
* (`specs/studio.md` §22.5). The records are written in the file's own grid, which opening any of
|
|
9
|
+
* them opens.
|
|
10
|
+
*
|
|
11
|
+
* Pure over the file's text: the scan (`browse/library-source.ts`) reads the file and hands it
|
|
12
|
+
* here.
|
|
13
|
+
*
|
|
14
|
+
* @docs studio/projects/browse
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { isNonNullObject } from "@jxsuite/schema/guards";
|
|
18
|
+
import { parseCsv } from "../grid/csv-codec";
|
|
19
|
+
import { fieldText, titleFieldOf } from "./entry-summaries";
|
|
20
|
+
import type { ContentTypeSchema, JsonValue } from "@jxsuite/schema/types";
|
|
21
|
+
|
|
22
|
+
/** One record of a file-backed collection. */
|
|
23
|
+
export interface FileRecord {
|
|
24
|
+
/** Its position among the file's records, from 0. */
|
|
25
|
+
index: number;
|
|
26
|
+
/**
|
|
27
|
+
* Its position among the file's data rows, from 0, blank rows counted: the row its file's grid
|
|
28
|
+
* shows it in, which a blank row above it makes differ from {@link index}.
|
|
29
|
+
*/
|
|
30
|
+
row: number;
|
|
31
|
+
/** What it is called: its title field, else its first text, else its row number. */
|
|
32
|
+
title: string;
|
|
33
|
+
/** Every field it holds, as list text, keyed by field name. */
|
|
34
|
+
fields: Readonly<Record<string, string>>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The title of one record, as the Content home names it. */
|
|
38
|
+
function recordTitle(
|
|
39
|
+
schema: ContentTypeSchema | null | undefined,
|
|
40
|
+
fields: Readonly<Record<string, string>>,
|
|
41
|
+
index: number,
|
|
42
|
+
fallback: string,
|
|
43
|
+
): string {
|
|
44
|
+
const field = titleFieldOf(schema, fields);
|
|
45
|
+
const named = field === null ? "" : (fields[field] ?? "");
|
|
46
|
+
const first = Object.values(fields).find((value) => value.trim() !== "");
|
|
47
|
+
return (named || first || fallback || `Row ${index + 1}`).trim();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** One record's fields, and the data row it was read from. */
|
|
51
|
+
interface RecordFields {
|
|
52
|
+
fields: Record<string, string>;
|
|
53
|
+
row: number;
|
|
54
|
+
/** The file is this one record, so it is called what the file is called when it says nothing. */
|
|
55
|
+
single?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The records of a CSV text: one per data row under the header, blank rows skipped. */
|
|
59
|
+
function csvFields(text: string): RecordFields[] {
|
|
60
|
+
const { headers, rows } = parseCsv(text);
|
|
61
|
+
return rows.flatMap((cells, row) =>
|
|
62
|
+
cells.some((cell) => cell.trim() !== "")
|
|
63
|
+
? [{ fields: Object.fromEntries(headers.map((header, i) => [header, cells[i] ?? ""])), row }]
|
|
64
|
+
: [],
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The records of a JSON text: one per object of an array, or ONE for a file that is a single object
|
|
70
|
+
* (`docs/nav.json`), which the loader reads as one entry named by the file. Null for text that is
|
|
71
|
+
* not JSON, which is then listed as the file it is.
|
|
72
|
+
*/
|
|
73
|
+
function jsonFields(text: string): RecordFields[] | null {
|
|
74
|
+
let parsed: JsonValue;
|
|
75
|
+
try {
|
|
76
|
+
// SAFETY: JSON.parse only ever yields JSON values; its `any` return type says nothing more.
|
|
77
|
+
parsed = JSON.parse(text) as JsonValue;
|
|
78
|
+
} catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const fieldsOf = (item: JsonValue): Record<string, string> =>
|
|
82
|
+
isNonNullObject(item) && !Array.isArray(item)
|
|
83
|
+
? Object.fromEntries(Object.entries(item).map(([key, value]) => [key, fieldText(value)]))
|
|
84
|
+
: {};
|
|
85
|
+
if (!Array.isArray(parsed)) {
|
|
86
|
+
return [{ fields: fieldsOf(parsed), row: 0, single: true }];
|
|
87
|
+
}
|
|
88
|
+
return parsed.map((item, row) => ({ fields: fieldsOf(item), row }));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The records a collection file holds, or null when its kind is not one the Content workspace reads
|
|
93
|
+
* records from (it is then listed as the file it is).
|
|
94
|
+
*
|
|
95
|
+
* @param path The file's project-relative path; its extension says how to read it.
|
|
96
|
+
* @param text The file's text.
|
|
97
|
+
* @param schema The collection's schema, which names the title field.
|
|
98
|
+
*/
|
|
99
|
+
export function fileRecords(
|
|
100
|
+
path: string,
|
|
101
|
+
text: string,
|
|
102
|
+
schema?: ContentTypeSchema | null,
|
|
103
|
+
): FileRecord[] | null {
|
|
104
|
+
const lower = path.toLowerCase();
|
|
105
|
+
const rows = lower.endsWith(".csv")
|
|
106
|
+
? csvFields(text)
|
|
107
|
+
: lower.endsWith(".json")
|
|
108
|
+
? jsonFields(text)
|
|
109
|
+
: null;
|
|
110
|
+
if (rows === null) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const stem = (path.split("/").pop() ?? path).replace(/\.[^.]+$/, "");
|
|
114
|
+
return rows.map(({ fields, row, single }, index) => ({
|
|
115
|
+
fields,
|
|
116
|
+
index,
|
|
117
|
+
row,
|
|
118
|
+
title: recordTitle(schema, fields, index, single ? stem : ""),
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** The key a record's row is listed under: its file and its place in it, `prices.csv#3`. */
|
|
123
|
+
export function recordKey(path: string, index: number): string {
|
|
124
|
+
return `${path}#${index + 1}`;
|
|
125
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
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, and whether it is marked a draft
|
|
4
|
+
* (`specs/studio.md` §22.5, the Content-Pages board: Title, URL, Template, Status, Edited).
|
|
5
|
+
*
|
|
6
|
+
* The scan behind the Content home knows each page's FILE; its `$layout`, `$paths` and `draft` are
|
|
7
|
+
* inside it. So this reads each page once, through the same parse the editor uses, and keeps the
|
|
8
|
+
* answer until a file event drops it ({@link invalidatePageSummaries}, wired beside the
|
|
9
|
+
* template-page index in `studio.ts`). The read never rejects: a page that cannot be read states
|
|
10
|
+
* nothing, and is listed by its route alone.
|
|
11
|
+
*
|
|
12
|
+
* @docs studio/projects/browse
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { isNonNullObject } from "@jxsuite/schema/guards";
|
|
16
|
+
import { getPlatform, hasPlatform } from "../platform";
|
|
17
|
+
import { pageFactsOf } from "./collection-pages";
|
|
18
|
+
import { DRAFT_FIELD } from "./draft-state";
|
|
19
|
+
import type { FormatFrontmatter } from "../format/format-host";
|
|
20
|
+
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
21
|
+
|
|
22
|
+
/** One page, as the Pages list states it. */
|
|
23
|
+
export interface PageSummary {
|
|
24
|
+
path: string;
|
|
25
|
+
/** Its `$layout`: a path, `false` (no layout), or `undefined` (the project's default). */
|
|
26
|
+
layout: string | false | undefined;
|
|
27
|
+
/** The collection its `$paths` expands over, or `null` for a page of its own. */
|
|
28
|
+
contentType: string | null;
|
|
29
|
+
/** Whether the page carries `draft: true`, the one draft marker a page can have. */
|
|
30
|
+
draft: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** What a page that could not be read states: nothing. */
|
|
34
|
+
function unread(path: string): PageSummary {
|
|
35
|
+
return { contentType: null, draft: false, layout: undefined, path };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The summary a parsed page states. Exported so the rule is assertable without a platform. */
|
|
39
|
+
export function pageSummaryOf(
|
|
40
|
+
path: string,
|
|
41
|
+
document: JxMutableNode | null,
|
|
42
|
+
frontmatter: FormatFrontmatter | null,
|
|
43
|
+
): PageSummary {
|
|
44
|
+
const { contentType, layout } = pageFactsOf(document, frontmatter);
|
|
45
|
+
const draft = frontmatter?.[DRAFT_FIELD] === true || document?.[DRAFT_FIELD] === true;
|
|
46
|
+
return { contentType, draft, layout, path };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Read one page. */
|
|
50
|
+
async function readSummary(path: string): Promise<PageSummary> {
|
|
51
|
+
try {
|
|
52
|
+
const source = await getPlatform().readFile(path);
|
|
53
|
+
if (path.endsWith(".json")) {
|
|
54
|
+
const parsed: unknown = JSON.parse(source);
|
|
55
|
+
/* SAFETY: a `.json` file under `pages/` is a page document by the project layout; one that is
|
|
56
|
+
not an object states nothing, which is the branch below. */
|
|
57
|
+
return isNonNullObject(parsed) && !Array.isArray(parsed)
|
|
58
|
+
? pageSummaryOf(path, parsed as JxMutableNode, null)
|
|
59
|
+
: unread(path);
|
|
60
|
+
}
|
|
61
|
+
const { parseSourceForPath } = await import("../files/file-ops");
|
|
62
|
+
const parsed = await parseSourceForPath(path, source);
|
|
63
|
+
/* SAFETY: a format class's parser returns its file as a Jx element tree (the format contract);
|
|
64
|
+
only `$layout`, `$paths` and `draft` are read off it. */
|
|
65
|
+
return pageSummaryOf(path, parsed.document as JxMutableNode, parsed.frontmatter);
|
|
66
|
+
} catch {
|
|
67
|
+
return unread(path);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ─── The cache ───────────────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
/** At most this many pages are read for one list; the rest are listed by route alone. */
|
|
74
|
+
export const PAGE_READ_LIMIT = 300;
|
|
75
|
+
|
|
76
|
+
let _cache: { key: string; pages: Map<string, PageSummary> } | null = null;
|
|
77
|
+
let _loading: { key: string; read: Promise<void> } | null = null;
|
|
78
|
+
let _epoch = 0;
|
|
79
|
+
const _listeners = new Set<() => void>();
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The pages' summaries as they stand, keyed by path: the map, or `null` while they are being read
|
|
83
|
+
* (which starts the read; listeners are told when it lands).
|
|
84
|
+
*
|
|
85
|
+
* @param paths The page files, in the order the list draws them.
|
|
86
|
+
*/
|
|
87
|
+
export function peekPageSummaries(
|
|
88
|
+
paths: readonly string[],
|
|
89
|
+
): ReadonlyMap<string, PageSummary> | null {
|
|
90
|
+
const key = paths.join("\n");
|
|
91
|
+
if (_cache?.key === key) {
|
|
92
|
+
return _cache.pages;
|
|
93
|
+
}
|
|
94
|
+
if (!hasPlatform()) {
|
|
95
|
+
return new Map(paths.map((path) => [path, unread(path)]));
|
|
96
|
+
}
|
|
97
|
+
if (_loading?.key !== key) {
|
|
98
|
+
const epoch = _epoch;
|
|
99
|
+
const read = Promise.all(paths.slice(0, PAGE_READ_LIMIT).map((path) => readSummary(path))).then(
|
|
100
|
+
(summaries) => {
|
|
101
|
+
if (epoch !== _epoch || _loading?.read !== read) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
_loading = null;
|
|
105
|
+
_cache = { key, pages: new Map(summaries.map((summary) => [summary.path, summary])) };
|
|
106
|
+
for (const listener of _listeners) {
|
|
107
|
+
listener();
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
_loading = { key, read };
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Forget every summary. The next list re-reads its pages. */
|
|
117
|
+
export function invalidatePageSummaries(): void {
|
|
118
|
+
_epoch += 1;
|
|
119
|
+
_cache = null;
|
|
120
|
+
_loading = null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Be told when a read lands. Returns the function that stops it. */
|
|
124
|
+
export function onPageSummariesRead(listener: () => void): () => void {
|
|
125
|
+
_listeners.add(listener);
|
|
126
|
+
return () => {
|
|
127
|
+
_listeners.delete(listener);
|
|
128
|
+
};
|
|
129
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One record of a file-backed collection, edited as an entry: a form over one CSV row.
|
|
3
|
+
*
|
|
4
|
+
* A collection whose `source` names one file (`./content/projects.csv`) has one entry per record
|
|
5
|
+
* (`content/file-records.ts`). The file's own grid edits all of them at once; this is the form-only
|
|
6
|
+
* entry editor (`content/entry-editor.ts`, `specs/studio.md` §22.5) over ONE of them, so opening a
|
|
7
|
+
* project from the Content workspace opens that project's fields, typed by the collection's schema,
|
|
8
|
+
* rather than a table the reader has to find the row in.
|
|
9
|
+
*
|
|
10
|
+
* **It is an editor of the file's grid tab, not a second document.** The record's fields are read
|
|
11
|
+
* from, and written into, the grid controller's edit buffer (`grid/edit-buffer.ts`), the same
|
|
12
|
+
* buffer the grid's cells write. So ⌘S saves the file through the grid's one commit path (with its
|
|
13
|
+
* staleness check and its untouched-cell rule), ⌘Z undoes a field edit exactly as it undoes a cell,
|
|
14
|
+
* the tab's dirty dot is the buffer's, and switching the tab's editor to Grid shows the edit as a
|
|
15
|
+
* pending cell.
|
|
16
|
+
*
|
|
17
|
+
* Which record the form shows is per tab, here, keyed by the tab's id. An opener names the record
|
|
18
|
+
* by its place among the file's data rows, because that is all a scan knows and the rows may not
|
|
19
|
+
* have loaded yet; the place resolves to the row's key the moment they have, reactively, so the
|
|
20
|
+
* form draws the record as soon as it exists. Previous / Next then name rows by key, which a save
|
|
21
|
+
* that re-reads the file does not move.
|
|
22
|
+
*
|
|
23
|
+
* @docs studio/projects/content-types
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { onScopeDispose, reactive } from "../reactivity";
|
|
27
|
+
import { activateTab, workspace } from "../workspace/workspace";
|
|
28
|
+
import { getGridController } from "../grid/grid-controller";
|
|
29
|
+
import { isBoolean, isNonNullObject, isNumber, isString } from "@jxsuite/schema/guards";
|
|
30
|
+
import { entryDay } from "./entry-summaries";
|
|
31
|
+
import type { GridCellValue } from "../grid/grid-source";
|
|
32
|
+
import type { EntryFields } from "./entry-fields";
|
|
33
|
+
import type { JsonValue } from "../types";
|
|
34
|
+
import type { Tab } from "../tabs/tab";
|
|
35
|
+
|
|
36
|
+
/** A value that already starts with a calendar day, which a date input reads as it is. */
|
|
37
|
+
const ISO_DAY = /^\d{4}-\d{2}-\d{2}/;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The `canvasMode` the entry form draws under. Defined here, with the record it may be over, and
|
|
41
|
+
* re-exported by `entry-editor.ts`, which imports this module.
|
|
42
|
+
*/
|
|
43
|
+
export const ENTRY_MODE = "entry";
|
|
44
|
+
|
|
45
|
+
/** A tab's record: by its row key, or (until the rows load) by its place among the data rows. */
|
|
46
|
+
type ShownRecord = { key: string } | { row: number };
|
|
47
|
+
|
|
48
|
+
/** Tab id → the record its entry form shows. */
|
|
49
|
+
const shown = reactive(new Map<string, ShownRecord>());
|
|
50
|
+
|
|
51
|
+
/** The key of the record a tab's entry form shows, or null when it shows none (yet). Tracks. */
|
|
52
|
+
export function recordOf(tab: Tab): string | null {
|
|
53
|
+
const record = shown.get(tab.id);
|
|
54
|
+
if (record === undefined) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
if ("key" in record) {
|
|
58
|
+
return record.key;
|
|
59
|
+
}
|
|
60
|
+
return getGridController(tab)?.state.rows[record.row]?.key ?? null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Whether the tab's entry form is over a record of its file, chosen or still loading. Tracks. */
|
|
64
|
+
export function hasRecord(tab: Tab): boolean {
|
|
65
|
+
return shown.has(tab.id);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Show the record keyed `rowKey` in this tab's entry form. */
|
|
69
|
+
export function showRecord(tab: Tab, rowKey: string): void {
|
|
70
|
+
shown.set(tab.id, { key: rowKey });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Forget the tab's record (its tab closed, or the record is gone). */
|
|
74
|
+
export function forgetRecord(tab: Tab): void {
|
|
75
|
+
shown.delete(tab.id);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Whether this tab's entry form is over a record of its grid rather than over its own document. */
|
|
79
|
+
export function isRecordTab(tab: Tab): boolean {
|
|
80
|
+
return hasRecord(tab) && getGridController(tab) !== null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The record's fields as the form reads them: every column's current value, pending edits included,
|
|
85
|
+
* typed as the grid types it (a number column's cell is a number, a list column's a list of
|
|
86
|
+
* strings). A cell with nothing in it is absent, as an empty frontmatter key would be.
|
|
87
|
+
*/
|
|
88
|
+
export function recordFields(tab: Tab): EntryFields {
|
|
89
|
+
const controller = getGridController(tab);
|
|
90
|
+
const rowKey = recordOf(tab);
|
|
91
|
+
if (!controller || rowKey === null) {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
const fields: EntryFields = {};
|
|
95
|
+
for (const column of controller.state.columns) {
|
|
96
|
+
const value = controller.buffer.effectiveValue(rowKey, column.field);
|
|
97
|
+
if (value === null || value === "") {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (column.kind === "date" && isString(value) && !ISO_DAY.test(value)) {
|
|
101
|
+
/* A CSV spells a date the way its spreadsheet did (`6/3/2026`), which a date input cannot
|
|
102
|
+
show. The form is shown the day it means; the cell keeps its spelling until it is edited. */
|
|
103
|
+
fields[column.field] = entryDay(value) || value;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
fields[column.field] = isNonNullObject(value) && !Array.isArray(value) ? value.$ref : value;
|
|
107
|
+
}
|
|
108
|
+
return fields;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** A form value as the cell it is written into. */
|
|
112
|
+
function cellValue(value: JsonValue | undefined): GridCellValue {
|
|
113
|
+
if (value === undefined || value === null) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
if (Array.isArray(value)) {
|
|
117
|
+
return value.map((item) => (isString(item) ? item : JSON.stringify(item)));
|
|
118
|
+
}
|
|
119
|
+
if (isString(value) || isNumber(value) || isBoolean(value)) {
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
// A cell holds text: an object a field produced is written as its JSON.
|
|
123
|
+
return JSON.stringify(value);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Write a form patch into the record's row as one undo step. A field with no column is ignored: the
|
|
128
|
+
* file has nowhere to keep it, and adding a column is the grid's decision, not a form's.
|
|
129
|
+
*/
|
|
130
|
+
export function commitRecordFields(tab: Tab, patch: Record<string, JsonValue | undefined>): void {
|
|
131
|
+
const controller = getGridController(tab);
|
|
132
|
+
const rowKey = recordOf(tab);
|
|
133
|
+
if (!controller || rowKey === null) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const columns = new Set(controller.state.columns.map((column) => column.field));
|
|
137
|
+
controller.buffer.group(() => {
|
|
138
|
+
for (const [field, value] of Object.entries(patch)) {
|
|
139
|
+
if (columns.has(field)) {
|
|
140
|
+
controller.buffer.setCell(rowKey, field, cellValue(value));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Where the shown record sits among the file's rows, for the form's Previous / Next. */
|
|
147
|
+
export interface RecordPlace {
|
|
148
|
+
/** Its place among the rows, from 0; -1 when the record is not (or no longer) among them. */
|
|
149
|
+
index: number;
|
|
150
|
+
total: number;
|
|
151
|
+
previous: string | null;
|
|
152
|
+
next: string | null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** The shown record's place among the grid's rows, in file order. Tracks the rows. */
|
|
156
|
+
export function recordPlace(tab: Tab): RecordPlace {
|
|
157
|
+
const controller = getGridController(tab);
|
|
158
|
+
const rowKey = recordOf(tab);
|
|
159
|
+
const rows = controller?.state.rows ?? [];
|
|
160
|
+
const index = rowKey === null ? -1 : rows.findIndex((row) => row.key === rowKey);
|
|
161
|
+
return {
|
|
162
|
+
index,
|
|
163
|
+
next: index === -1 ? null : (rows[index + 1]?.key ?? null),
|
|
164
|
+
previous: index <= 0 ? null : (rows[index - 1]?.key ?? null),
|
|
165
|
+
total: rows.length,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Open one record of a file-backed collection in its entry form.
|
|
171
|
+
*
|
|
172
|
+
* Opens (or reveals) the file's grid tab, names the record at `row` (its place among the file's
|
|
173
|
+
* data rows, as `content/file-records.ts` numbers it) and puts the tab in the entry editor. The
|
|
174
|
+
* form draws the record once the grid's rows have loaded ({@link recordOf}).
|
|
175
|
+
*
|
|
176
|
+
* @returns The tab, or null when the file could not be opened at all.
|
|
177
|
+
*/
|
|
178
|
+
export async function openRecordEntry(path: string, row: number): Promise<Tab | null> {
|
|
179
|
+
/* Dynamic for the reason `entry-open.ts` gives: `files/files.ts` reaches the platform, the format
|
|
180
|
+
registry and the packages layer. */
|
|
181
|
+
const { openFileInTab } = await import("../files/files");
|
|
182
|
+
await openFileInTab(path);
|
|
183
|
+
const tab = workspace.tabs.get(path) ?? null;
|
|
184
|
+
if (!tab || !getGridController(tab)) {
|
|
185
|
+
return tab;
|
|
186
|
+
}
|
|
187
|
+
if (!shown.has(tab.id)) {
|
|
188
|
+
// The choice lives as long as the tab: a file reopened later starts on no record.
|
|
189
|
+
tab.scope.run(() => {
|
|
190
|
+
onScopeDispose(() => forgetRecord(tab));
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
shown.set(tab.id, { row });
|
|
194
|
+
if (!tab.capabilities.modes.includes(ENTRY_MODE)) {
|
|
195
|
+
tab.capabilities.modes.unshift(ENTRY_MODE);
|
|
196
|
+
}
|
|
197
|
+
tab.session.ui.canvasMode = ENTRY_MODE;
|
|
198
|
+
tab.session.ui.preview = false;
|
|
199
|
+
activateTab(tab.id);
|
|
200
|
+
return tab;
|
|
201
|
+
}
|