@jxsuite/studio 6.1.2 → 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/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-0fj8a4ga.js → color-area-6rbra5dy.js} +2 -2
- package/dist/chunks/{color-field-xch8kx2g.js → color-field-gy56c94g.js} +3 -3
- package/dist/chunks/color-m7r1vk54.js +4 -0
- package/dist/chunks/{color-slider-jxd8v350.js → color-slider-sqqdyf6c.js} +2 -2
- package/dist/chunks/{combobox-p0efwjke.js → combobox-72h32ejk.js} +3 -3
- 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/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-d2wq9ezg.js → grid-view-qg1b5eqx.js} +25 -25
- package/dist/chunks/{icons-pnfz478q.js → icons-63bjtbs7.js} +2 -2
- package/dist/chunks/{iframe-entry-jdcktez8.js → iframe-entry-2zggzajv.js} +4 -4
- package/dist/chunks/{iframe-entry-zap785cw.js → iframe-entry-76j8vwwx.js} +4 -4
- package/dist/chunks/iframe-entry-99nr5zfb.js +5 -0
- package/dist/chunks/{iframe-entry-x0ygf2yr.js → iframe-entry-pkgmcgp0.js} +4 -4
- package/dist/chunks/iframe-host-ssv7kyj0.js +4 -0
- package/dist/chunks/{index-zpjmzqfk.js → index-32eqzv1k.js} +3 -3
- package/dist/chunks/index-f0jn7nnp.js +4 -0
- package/dist/chunks/{javascript-m54mnwp6.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-e74qjyq6.js → listbox-c421cf8c.js} +2 -2
- 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-n6yzswmy.js → menu-bdpzbfvs.js} +2 -2
- package/dist/chunks/{monaco-binding-7gx83x3r.js → monaco-binding-vq1hgnn0.js} +3 -3
- 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/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/source-control-kwktr9y6.js +4 -0
- package/dist/chunks/{split-n2gfpm5m.js → split-c9zfdgxf.js} +2 -2
- 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-2j1cb95x.js → studio-6m380wp7.js} +4 -4
- package/dist/chunks/studio-80jyh1xh.js +4 -0
- package/dist/chunks/studio-83mk4bgg.js +9 -0
- package/dist/chunks/{studio-v0jzqdjm.js → studio-881z1xrx.js} +4 -4
- package/dist/chunks/studio-8m0y2qe7.js +5 -0
- package/dist/chunks/{studio-d81xfh1g.js → studio-8tr6cfrc.js} +4 -4
- 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-dgz8d0c1.js → studio-bbe4c90a.js} +4 -4
- 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-90pq9km1.js → studio-jcnsdaep.js} +4 -4
- package/dist/chunks/studio-jsgf2q5k.js +7 -0
- package/dist/chunks/{studio-0q988f9w.js → studio-k3xxbagr.js} +3 -3
- package/dist/chunks/{studio-5ve6bbep.js → studio-k7evxqh4.js} +4 -4
- 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-485046te.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-1fxkaaea.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-tq0rtfwj.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-a3g98wjr.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-r5qy30vx.js +5 -0
- 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/toast-1gv0h53h.js +4 -0
- package/dist/chunks/{toast-host-ke5z4ng7.js → toast-host-1yg5ar8d.js} +3 -3
- package/dist/chunks/{tooltip-5v8ngwj2.js → tooltip-yypz28gq.js} +3 -3
- package/dist/chunks/{tree-mdq3v5cw.js → tree-9xa4hm7n.js} +2 -2
- 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-nze0k6mv.js → ws-client-bv552sfx.js} +3 -3
- package/dist/iframe-entry.js +20 -16
- package/dist/manifest.json +77 -63
- package/dist/studio.js +103 -104
- package/dist/workers/ts.worker.js +3 -3
- package/package.json +12 -11
- package/src/browse/component-examples.ts +272 -0
- package/src/browse/library-commands.ts +64 -104
- package/src/browse/library-layouts.ts +263 -49
- package/src/browse/library-model.ts +181 -133
- package/src/browse/library-pane.ts +738 -446
- package/src/browse/library-preview.ts +149 -19
- 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 +16 -1
- package/src/canvas/canvas-live-render.ts +222 -30
- package/src/canvas/canvas-origin.ts +37 -12
- package/src/canvas/canvas-page-context.ts +112 -0
- package/src/canvas/canvas-render.ts +113 -107
- package/src/canvas/canvas-utils.ts +57 -12
- package/src/canvas/iframe-drop.ts +107 -0
- package/src/canvas/iframe-entry.ts +24 -12
- package/src/canvas/iframe-host.ts +134 -62
- package/src/canvas/iframe-keys.ts +11 -0
- package/src/canvas/iframe-patch.ts +8 -7
- package/src/canvas/iframe-protocol.ts +24 -5
- package/src/canvas/iframe-render.ts +48 -1
- package/src/canvas/iframe-subtree.ts +5 -2
- package/src/canvas/npm-modules.ts +22 -0
- package/src/canvas/preview-navigate.ts +16 -0
- package/src/canvas/repeater-sources.ts +200 -0
- package/src/canvas/stage-scrollbar.ts +5 -1
- package/src/canvas/surface-registry.ts +6 -0
- package/src/commands/app-commands.ts +16 -6
- package/src/commands/budget.ts +10 -8
- package/src/commands/context.ts +35 -10
- package/src/commands/defaults.ts +69 -45
- package/src/commands/keymap.ts +9 -0
- package/src/commands/levels.ts +11 -5
- package/src/commands/live-context.ts +2 -0
- package/src/commands/registry.ts +10 -1
- package/src/content/collection-match.ts +6 -5
- package/src/content/collection-pages.ts +412 -0
- package/src/content/content-collections.ts +270 -0
- package/src/content/entry-commands.ts +51 -60
- package/src/content/entry-editor.ts +111 -18
- package/src/content/entry-fields.ts +13 -1
- package/src/content/entry-inspector.ts +225 -0
- package/src/content/entry-model.ts +17 -3
- 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/convert-to-component.ts +67 -43
- package/src/editor/file-drop-action.ts +9 -1
- package/src/editor/inline-edit-apply.ts +44 -0
- package/src/editor/inline-edit.ts +10 -0
- package/src/editor/insert-component.ts +91 -0
- package/src/editor/insert-zone-action.ts +13 -4
- package/src/editor/shortcuts.ts +44 -22
- package/src/editor/slash-menu.ts +51 -8
- package/src/files/components.ts +74 -10
- package/src/files/file-ops.ts +15 -5
- package/src/files/files.ts +164 -150
- package/src/files/fs-events.ts +18 -7
- package/src/files/media-meta.ts +95 -1
- 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 +13 -3
- package/src/format/format-host.ts +15 -0
- package/src/github/github-auth.ts +2 -2
- package/src/github/github-publish.ts +4 -3
- package/src/grid/grid-controller.ts +5 -1
- package/src/grid/grid-open.ts +0 -27
- package/src/grid/grid-panel.ts +54 -2
- package/src/grid/grid-reveal.ts +34 -0
- package/src/grid/grid-source.ts +3 -25
- package/src/grid/grid-view.ts +31 -6
- package/src/grid/sources/content-source.ts +75 -7
- package/src/grid/tabulator-tables.d.ts +6 -0
- package/src/hosting/document.ts +2 -2
- package/src/i18n/i18n-commands.ts +2 -1
- 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 +47 -19
- package/src/packages/pull-package-sync.ts +1 -1
- package/src/panels/activity-panel.ts +21 -5
- package/src/panels/ai-chat/chat-markdown.ts +8 -8
- package/src/panels/block-action-bar.ts +11 -11
- package/src/panels/canvas-dnd-bridge.ts +5 -2
- 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 +1 -1
- package/src/panels/data-grid.ts +4 -0
- package/src/panels/dnd.ts +17 -30
- package/src/panels/elements-panel.ts +9 -2
- package/src/panels/empty-state.ts +2 -2
- package/src/panels/formula-workspace.ts +2 -3
- package/src/panels/frontmatter-fields.ts +9 -9
- package/src/panels/git-diff-open.ts +9 -5
- package/src/panels/head-panel.ts +369 -103
- package/src/panels/i18n-panel.ts +11 -10
- package/src/panels/imports-panel.ts +2 -0
- package/src/panels/jump-bar.ts +27 -2
- package/src/panels/layers-panel.ts +36 -30
- package/src/panels/left-panel.ts +22 -8
- package/src/panels/navigator-panels.ts +23 -11
- package/src/panels/pane-context.ts +111 -29
- package/src/panels/pane-grid.ts +6 -6
- package/src/panels/panel-registry.ts +14 -9
- package/src/panels/properties-panel.ts +7 -3
- package/src/panels/right-panel.ts +199 -43
- package/src/panels/seo-modal.ts +41 -108
- package/src/panels/settings-menu.ts +52 -50
- package/src/panels/settings-pane.ts +268 -162
- package/src/panels/signals-panel.ts +3 -4
- package/src/panels/site-model.ts +367 -0
- package/src/panels/site-panel.ts +638 -0
- package/src/panels/style-panel.ts +37 -3
- package/src/panels/stylebook-doc.ts +142 -10
- package/src/panels/stylebook-layers-panel.ts +39 -80
- package/src/panels/stylebook-panel.ts +47 -10
- package/src/panels/styles-panel.ts +327 -0
- package/src/panels/tab-strip.ts +109 -25
- package/src/platform.ts +1 -1
- package/src/platforms/cloud.ts +28 -1
- package/src/platforms/default-platform.ts +4 -4
- package/src/platforms/devserver.ts +51 -25
- package/src/publish/deploy-checklist.ts +12 -7
- package/src/publish/pages-service.ts +38 -14
- package/src/publish/publish-changes.ts +491 -0
- package/src/publish/publish-commands.ts +83 -18
- package/src/publish/publish-ids.ts +49 -0
- 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/services/automation.ts +16 -20
- package/src/services/cf-settings.ts +4 -4
- package/src/services/connection.ts +2 -2
- package/src/services/directory-picker.ts +2 -2
- package/src/services/references.ts +0 -0
- package/src/services/render-critic.ts +21 -2
- package/src/services/settings/definitions.ts +25 -14
- package/src/settings/contributed-section.ts +49 -10
- package/src/settings/css-vars-editor.ts +93 -27
- package/src/settings/developer-panels.ts +43 -0
- package/src/settings/extension-sections.ts +20 -2
- package/src/settings/general-settings.ts +91 -21
- package/src/settings/preferences-dialog.ts +8 -1
- package/src/settings/project-sections.ts +27 -10
- package/src/settings/schema-field-ui.ts +3 -1
- package/src/settings/section-registry.ts +116 -11
- package/src/settings/settings-dialog.ts +192 -0
- package/src/settings/settings-document.ts +182 -162
- package/src/shell.ts +467 -431
- package/src/site-context.ts +64 -10
- package/src/state.ts +28 -9
- package/src/studio.ts +194 -41
- package/src/style/project-styles.ts +34 -0
- 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/variables-focus.ts +61 -0
- package/src/style/variables-view.ts +235 -0
- package/src/surfaces/add-new.json +1482 -0
- package/src/surfaces/add-new.ts +325 -0
- package/src/surfaces/ai-chat.ts +2 -2
- package/src/surfaces/boot-failure.json +4 -3
- package/src/surfaces/bottom-dock.json +1 -1
- package/src/surfaces/canvas-stage.json +30 -19
- package/src/surfaces/canvas-stage.ts +5 -5
- 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 +134 -99
- package/src/surfaces/empty-state.json +1 -1
- package/src/surfaces/entry-editor.json +136 -0
- package/src/surfaces/entry-editor.ts +21 -0
- package/src/surfaces/entry-inspector.json +371 -0
- package/src/surfaces/entry-inspector.ts +143 -0
- package/src/surfaces/inspector-dock.json +162 -14
- package/src/surfaces/inspector-dock.ts +46 -19
- package/src/surfaces/library-pane.json +670 -437
- package/src/surfaces/library-pane.ts +94 -59
- package/src/surfaces/media-browser.json +62 -6
- package/src/surfaces/media-browser.ts +15 -1
- package/src/surfaces/media-field.json +1 -1
- package/src/surfaces/media-library.json +1586 -0
- package/src/surfaces/media-library.ts +282 -0
- package/src/surfaces/media-pick-dialog.json +71 -0
- package/src/surfaces/media-pick-dialog.ts +132 -0
- package/src/surfaces/menu.ts +18 -0
- package/src/surfaces/pane-context.json +144 -77
- package/src/surfaces/pane-context.ts +24 -0
- package/src/surfaces/panel-deploy-checklist.json +12 -6
- package/src/surfaces/panel-deploy-checklist.ts +3 -1
- package/src/surfaces/panel-page.json +72 -2
- package/src/surfaces/panel-page.ts +38 -7
- package/src/surfaces/panel-signals.json +1 -1
- package/src/surfaces/panel-signals.ts +5 -5
- package/src/surfaces/panel-stylebook-layers.json +71 -204
- package/src/surfaces/panel-stylebook-layers.ts +0 -21
- package/src/surfaces/preferences.json +120 -48
- package/src/surfaces/preferences.ts +8 -0
- 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/rail.json +1 -159
- package/src/surfaces/rail.ts +18 -75
- package/src/surfaces/schema-form.json +10 -2
- package/src/surfaces/schema-form.ts +2 -0
- package/src/surfaces/seo.json +1 -1
- package/src/surfaces/settings-contributed.json +5 -5
- package/src/surfaces/settings-contributed.ts +18 -2
- package/src/surfaces/settings-css-vars.json +44 -36
- package/src/surfaces/settings-css-vars.ts +77 -4
- package/src/surfaces/settings-deploy.json +2 -15
- package/src/surfaces/settings-deploy.ts +1 -1
- package/src/surfaces/settings-dialog.json +51 -0
- package/src/surfaces/settings-dialog.ts +169 -0
- package/src/surfaces/settings-overview.json +3 -3
- package/src/surfaces/settings-overview.ts +9 -2
- package/src/surfaces/settings-pane.json +269 -46
- package/src/surfaces/settings-pane.ts +85 -24
- package/src/surfaces/shell.json +2 -1
- 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 +23 -0
- package/src/surfaces/statusbar.ts +39 -15
- 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/welcome.ts +1 -1
- 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/tab.ts +47 -7
- package/src/types.ts +21 -26
- package/src/ui/form-controls.ts +47 -8
- package/src/ui/layers.ts +13 -0
- package/src/ui/media-picker.ts +284 -40
- package/src/ui/regions.ts +7 -6
- package/src/ui/schema-form.ts +259 -27
- package/src/ui/virtual-window.ts +26 -1
- package/src/utils/base64.ts +3 -3
- package/src/utils/edit-display.ts +322 -67
- package/src/utils/local-day.ts +18 -0
- package/src/workspace/pane-derive.ts +4 -4
- package/src/workspace/session.ts +28 -7
- 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 +125 -11
- package/styles/shell-frame.css +30 -7
- package/styles/shell-frame.json +15 -3
- package/dist/chunks/2020-f2gkpy8d.js +0 -4
- package/dist/chunks/adapter-4912g339.js +0 -4
- package/dist/chunks/cf-account-picker-wccaenh6.js +0 -4
- package/dist/chunks/cf-settings-br6105tr.js +0 -4
- package/dist/chunks/color-28ksk2qm.js +0 -4
- package/dist/chunks/components-db68vsn7.js +0 -4
- package/dist/chunks/content-source-1449qesq.js +0 -4
- package/dist/chunks/convert-to-repeater-5h457egj.js +0 -4
- package/dist/chunks/editor-x9jn86a7.js +0 -4
- package/dist/chunks/entry-commands-may24cpt.js +0 -4
- package/dist/chunks/extension-sections-pvr14vxr.js +0 -4
- package/dist/chunks/file-ops-x9v54pcj.js +0 -4
- package/dist/chunks/files-j3knqf0s.js +0 -4
- package/dist/chunks/format-host-tyre2trb.js +0 -4
- package/dist/chunks/fs-events-64ma9htc.js +0 -4
- package/dist/chunks/git-panel-84ps5dex.js +0 -4
- package/dist/chunks/iframe-entry-fvbk89gm.js +0 -5
- package/dist/chunks/iframe-host-ck6vejt5.js +0 -4
- package/dist/chunks/index-b8xw3vy9.js +0 -4
- package/dist/chunks/jsonMode-w15cmrcm.js +0 -13
- package/dist/chunks/jx-validate-at3zn8wa.js +0 -4
- package/dist/chunks/layers-h4xp1pss.js +0 -4
- package/dist/chunks/layers-panel-m5hw1d7g.js +0 -4
- package/dist/chunks/lit-html-8t8t3dnr.js +0 -4
- package/dist/chunks/media-picker-2cweexc5.js +0 -4
- package/dist/chunks/menu-ynw13y0s.js +0 -4
- package/dist/chunks/monaco-buffer-qzxn567r.js +0 -4
- package/dist/chunks/monaco-setup-gxf6656g.js +0 -4
- package/dist/chunks/notify-cj5zbrs2.js +0 -4
- package/dist/chunks/parse-b7ddhr8m.js +0 -4
- package/dist/chunks/platform-3dfws9js.js +0 -4
- package/dist/chunks/popover-jt2gxmzs.js +0 -4
- package/dist/chunks/publish-panel-1gvxbj21.js +0 -4
- package/dist/chunks/quick-search-0dajrwqg.js +0 -4
- package/dist/chunks/references-49qgt404.js +0 -4
- package/dist/chunks/right-panel-jm1bwe53.js +0 -4
- package/dist/chunks/section-registry-6kwc4fky.js +0 -4
- package/dist/chunks/studio-0rt52vpq.js +0 -5
- package/dist/chunks/studio-18vgbhs5.js +0 -5
- package/dist/chunks/studio-24mccdrj.js +0 -5
- package/dist/chunks/studio-2eh507zk.js +0 -9
- package/dist/chunks/studio-2hcbccfv.js +0 -5
- package/dist/chunks/studio-2jbs3rxv.js +0 -5
- package/dist/chunks/studio-4h8k4dw9.js +0 -5
- package/dist/chunks/studio-5f6n18kx.js +0 -5
- package/dist/chunks/studio-5gthpky6.js +0 -5
- package/dist/chunks/studio-6ep3js6b.js +0 -9
- package/dist/chunks/studio-6wkmf3wz.js +0 -9
- package/dist/chunks/studio-841rf3cg.js +0 -5
- package/dist/chunks/studio-8gmwgheg.js +0 -5
- package/dist/chunks/studio-8qm0ep50.js +0 -5
- package/dist/chunks/studio-924p1tm1.js +0 -6
- package/dist/chunks/studio-a3shpz9y.js +0 -5
- package/dist/chunks/studio-amsbaaar.js +0 -6
- package/dist/chunks/studio-at5spjbc.js +0 -7
- package/dist/chunks/studio-b2xv945e.js +0 -5
- package/dist/chunks/studio-bn5nkafw.js +0 -5
- package/dist/chunks/studio-bw6q5xjf.js +0 -9
- package/dist/chunks/studio-c7q0t9x1.js +0 -5
- package/dist/chunks/studio-camnhc96.js +0 -6
- package/dist/chunks/studio-cffqvnzd.js +0 -5
- package/dist/chunks/studio-efvfrsf8.js +0 -5
- package/dist/chunks/studio-er30bpcx.js +0 -5
- package/dist/chunks/studio-ezscgr64.js +0 -5
- package/dist/chunks/studio-ffrrncvz.js +0 -5
- package/dist/chunks/studio-fpphgn8y.js +0 -10
- package/dist/chunks/studio-j1kvhdjk.js +0 -4
- package/dist/chunks/studio-j430n16g.js +0 -7
- package/dist/chunks/studio-jjkg84nj.js +0 -5
- package/dist/chunks/studio-m0q32ars.js +0 -4
- package/dist/chunks/studio-m6bdst11.js +0 -5
- package/dist/chunks/studio-nv2cptd4.js +0 -64
- package/dist/chunks/studio-nwfrc98n.js +0 -5
- package/dist/chunks/studio-p1wgvp7r.js +0 -5
- package/dist/chunks/studio-pag2wncm.js +0 -5
- package/dist/chunks/studio-qvanvzfd.js +0 -5
- package/dist/chunks/studio-qwa64kt7.js +0 -5
- package/dist/chunks/studio-rk1zc4x3.js +0 -928
- package/dist/chunks/studio-se2r3ptc.js +0 -5
- package/dist/chunks/studio-sg2a0dh0.js +0 -5
- package/dist/chunks/studio-tjzjhe68.js +0 -5
- package/dist/chunks/studio-v2w7q8r1.js +0 -7
- package/dist/chunks/studio-v5vjq1h0.js +0 -4
- package/dist/chunks/studio-v8aga3vp.js +0 -14
- package/dist/chunks/studio-vfcdrfmv.js +0 -5
- package/dist/chunks/studio-vmfkgdjf.js +0 -5
- package/dist/chunks/studio-wt1816jj.js +0 -5
- package/dist/chunks/studio-wz73q5kq.js +0 -4
- package/dist/chunks/studio-y04qtxvp.js +0 -7
- package/dist/chunks/studio-y21g8zzg.js +0 -5
- package/dist/chunks/studio-y64hrt8p.js +0 -12
- package/dist/chunks/studio-ynn90f58.js +0 -16
- package/dist/chunks/studio-yxz682sx.js +0 -5
- package/dist/chunks/studio-zp7snvkq.js +0 -5
- package/dist/chunks/studio-zq4z6tm6.js +0 -5
- package/dist/chunks/toast-c9fn075p.js +0 -4
- package/dist/chunks/tsMode-sf49ee9x.js +0 -14
- package/dist/chunks/workspace-jtfahpee.js +0 -4
- package/src/panels/frontmatter-panel.ts +0 -711
- package/src/panels/git-panel.ts +0 -1108
- package/src/surfaces/doc-header.json +0 -776
- package/src/surfaces/doc-header.ts +0 -222
- package/src/surfaces/git-panel.json +0 -1880
- package/src/surfaces/git-panel.ts +0 -264
|
@@ -22,19 +22,28 @@
|
|
|
22
22
|
import {
|
|
23
23
|
buildScope,
|
|
24
24
|
renderNode,
|
|
25
|
+
runScoped,
|
|
25
26
|
setCanvasAssetResolver,
|
|
27
|
+
setNpmModuleResolver,
|
|
28
|
+
setResolveToken,
|
|
26
29
|
setSkipServerFunctions,
|
|
30
|
+
setTemplateErrorHandler,
|
|
27
31
|
} from "@jxsuite/runtime";
|
|
28
|
-
import { isFunction } from "@jxsuite/schema/guards";
|
|
32
|
+
import { isFunction, isNonNullObject } from "@jxsuite/schema/guards";
|
|
29
33
|
import { assetContextFor, hostAssetDeclarations, resolveAssetRef } from "../canvas/asset-refs";
|
|
34
|
+
import { canvasBaseOrigin, documentBase, hostResolveToken } from "../canvas/canvas-origin";
|
|
35
|
+
import { npmModuleUrl } from "../canvas/npm-modules";
|
|
36
|
+
import { projectState } from "../store";
|
|
30
37
|
import { assetVersionsSnapshot } from "../files/asset-versions";
|
|
31
38
|
import { componentRegistry } from "../files/components";
|
|
32
39
|
import { getPlatform } from "../platform";
|
|
33
40
|
import { loadFormats, formatForPath } from "../format/format-host";
|
|
34
41
|
import { parseSourceForPath } from "../files/file-ops";
|
|
35
|
-
import { renderComponentPreview } from "../panels/component-preview";
|
|
42
|
+
import { defineComponent, renderComponentPreview } from "../panels/component-preview";
|
|
43
|
+
import { componentExampleProps, componentSampleRow } from "./component-examples";
|
|
44
|
+
import { loadProjectPreviewSheets } from "./preview-style";
|
|
36
45
|
import type { ComponentEntry } from "../files/components";
|
|
37
|
-
import type { JxDocument } from "@jxsuite/schema/types";
|
|
46
|
+
import type { JsonValue, JxDocument } from "@jxsuite/schema/types";
|
|
38
47
|
|
|
39
48
|
/**
|
|
40
49
|
* How many rendered previews stay alive at once.
|
|
@@ -113,6 +122,16 @@ export function createPreviewCache(limit: number = PREVIEW_CACHE_LIMIT): Preview
|
|
|
113
122
|
|
|
114
123
|
// ─── Rendering one preview ───────────────────────────────────────────────────
|
|
115
124
|
|
|
125
|
+
/**
|
|
126
|
+
* The URL a previewed document's relative refs resolve against: the file's own URL under the
|
|
127
|
+
* project, as the canvas spells it. A shell with an opaque origin (a custom-scheme shell such as
|
|
128
|
+
* `views://`) has no such URL to build, and keeps the shell's own address.
|
|
129
|
+
*/
|
|
130
|
+
function previewBase(filePath: string): string {
|
|
131
|
+
const base = documentBase(projectState?.projectRoot);
|
|
132
|
+
return URL.canParse(base) ? new URL(filePath, base).href : location.href;
|
|
133
|
+
}
|
|
134
|
+
|
|
116
135
|
/**
|
|
117
136
|
* Render a page/layout/content document to a detached element, or null when it cannot be rendered.
|
|
118
137
|
*
|
|
@@ -138,26 +157,128 @@ export async function renderDocPreview(filePath: string): Promise<HTMLElement |
|
|
|
138
157
|
JSON or a shape renderNode cannot draw throws into the catch below, which is "no preview". */
|
|
139
158
|
document_ = JSON.parse(content) as JxDocument;
|
|
140
159
|
}
|
|
141
|
-
|
|
142
|
-
/* This render lands in the PARENT document, so an `<img src="/hero.jpg">` inside it resolves
|
|
143
|
-
against `index.html` — which is the shell, not the site. Same hook the canvas installs, same
|
|
144
|
-
context, restored afterwards because the next preview is a different document and a stale
|
|
145
|
-
`documentDir` would resolve its relative refs against the wrong entry. The version map is
|
|
146
|
-
read as the render starts, after the reads above: the Library is the many-images screen,
|
|
147
|
-
and a versioned src is what lets its cards repaint from the browser cache. */
|
|
148
|
-
const assets = context && { ...context, versions: assetVersionsSnapshot() };
|
|
149
|
-
setCanvasAssetResolver((value) => resolveAssetRef(value, assets));
|
|
150
|
-
try {
|
|
151
|
-
const element = renderNode(document_, scope);
|
|
152
|
-
return element instanceof HTMLElement ? element : null;
|
|
153
|
-
} finally {
|
|
154
|
-
setCanvasAssetResolver(null);
|
|
155
|
-
}
|
|
160
|
+
return await renderDocument(document_, filePath, context);
|
|
156
161
|
} catch {
|
|
157
162
|
return null;
|
|
158
163
|
}
|
|
159
164
|
}
|
|
160
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Render a document as the file `filePath` holds it would be drawn: its refs resolved from that
|
|
168
|
+
* file's URL, its media through the canvas's resolver. Throws what the render throws.
|
|
169
|
+
*/
|
|
170
|
+
async function renderDocument(
|
|
171
|
+
document_: JxDocument,
|
|
172
|
+
filePath: string,
|
|
173
|
+
context = assetContextFor(filePath, hostAssetDeclarations()),
|
|
174
|
+
): Promise<HTMLElement | null> {
|
|
175
|
+
setSkipServerFunctions(true);
|
|
176
|
+
/* The document's own URL, as the canvas spells it, and the canvas's dev-proxy token. With the
|
|
177
|
+
shell's `location.href` as base, a page's `"$src": "../../scripts/x.class.json"` resolved
|
|
178
|
+
against `/__studio__/`, and without the token the proxy refused it before reading it. */
|
|
179
|
+
setResolveToken(hostResolveToken());
|
|
180
|
+
const scope = await buildScope(document_, {}, previewBase(filePath));
|
|
181
|
+
/* This render lands in the PARENT document, so an `<img src="/hero.jpg">` inside it resolves
|
|
182
|
+
against `index.html` — which is the shell, not the site. Same hook the canvas installs, same
|
|
183
|
+
context, restored afterwards because the next preview is a different document and a stale
|
|
184
|
+
`documentDir` would resolve its relative refs against the wrong entry. The version map is
|
|
185
|
+
read as the render starts, after the reads above: the Library is the many-images screen,
|
|
186
|
+
and a versioned src is what lets its cards repaint from the browser cache. */
|
|
187
|
+
const assets = context && { ...context, versions: assetVersionsSnapshot() };
|
|
188
|
+
setCanvasAssetResolver((value) => resolveAssetRef(value, assets));
|
|
189
|
+
try {
|
|
190
|
+
const element = renderNode(document_, scope);
|
|
191
|
+
return element instanceof HTMLElement ? element : null;
|
|
192
|
+
} finally {
|
|
193
|
+
setCanvasAssetResolver(null);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* A project component's preview: an instance given its sample props when it renders with them, else
|
|
199
|
+
* the first row of a list that draws it (`component-examples.ts`), else none.
|
|
200
|
+
*/
|
|
201
|
+
async function projectComponentPreview(
|
|
202
|
+
component: ComponentEntry,
|
|
203
|
+
path: string,
|
|
204
|
+
): Promise<HTMLElement | null> {
|
|
205
|
+
const definition = await readDefinition(path);
|
|
206
|
+
if (!definition) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
const props = await componentExampleProps(definition, path);
|
|
210
|
+
if (props.size > 0 && (await rendersWith(definition, path, props))) {
|
|
211
|
+
return renderComponentPreview(component, props);
|
|
212
|
+
}
|
|
213
|
+
const sample = await componentSampleRow(definition, path);
|
|
214
|
+
if (sample && (await defineComponent(component))) {
|
|
215
|
+
return renderDocument(sample.doc, sample.file);
|
|
216
|
+
}
|
|
217
|
+
return (await rendersWith(definition, path, props))
|
|
218
|
+
? renderComponentPreview(component, props)
|
|
219
|
+
: null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** A project component's definition, or null when its file is not one. */
|
|
223
|
+
export async function readDefinition(path: string): Promise<JxDocument | null> {
|
|
224
|
+
try {
|
|
225
|
+
const parsed: unknown = JSON.parse(await getPlatform().readFile(path));
|
|
226
|
+
/* SAFETY: a project component's `.json` is its definition, a Jx element document; a shape
|
|
227
|
+
renderNode cannot draw throws into `rendersWith`'s catch, which is "does not render". */
|
|
228
|
+
return isNonNullObject(parsed) && !Array.isArray(parsed) ? (parsed as JxDocument) : null;
|
|
229
|
+
} catch {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Whether `definition` renders given `props` (its sample props, `component-examples.ts`), tried on
|
|
236
|
+
* a detached copy before a live instance is made.
|
|
237
|
+
*
|
|
238
|
+
* A definition may not render with the values it is given: the museum starter's
|
|
239
|
+
* `<mu-exhibition-card>` formats a `date` that defaults to `""`, and `Intl/formatDate` of an empty
|
|
240
|
+
* date is a `RangeError`, exactly as `new Intl.DateTimeFormat().format(new Date(""))` is (`spec.md`
|
|
241
|
+
* §19.4c). An instance renders in its own async `connectedCallback`, where the throw is reported on
|
|
242
|
+
* the console once per card per paint. The same render, made here synchronously into an element
|
|
243
|
+
* nobody sees, can be caught, and a definition that cannot render gets no instance preview.
|
|
244
|
+
*/
|
|
245
|
+
export async function rendersWith(
|
|
246
|
+
definition: JxDocument,
|
|
247
|
+
path: string,
|
|
248
|
+
props: ReadonlyMap<string, JsonValue>,
|
|
249
|
+
): Promise<boolean> {
|
|
250
|
+
let stopRender: (() => void) | null = null;
|
|
251
|
+
try {
|
|
252
|
+
setSkipServerFunctions(true);
|
|
253
|
+
setResolveToken(hostResolveToken());
|
|
254
|
+
const scope = await buildScope(definition, {}, previewBase(path));
|
|
255
|
+
for (const [name, value] of props) {
|
|
256
|
+
scope[name] = value;
|
|
257
|
+
}
|
|
258
|
+
/* A template binding that throws no longer unwinds the render (spec.md §6.1): it draws nothing
|
|
259
|
+
and waits for its data. The trial still has to see it, so the throw is collected for the one
|
|
260
|
+
synchronous render, as the render critic collects it. */
|
|
261
|
+
let bindingThrew = false;
|
|
262
|
+
setTemplateErrorHandler(() => {
|
|
263
|
+
bindingThrew = true;
|
|
264
|
+
});
|
|
265
|
+
// Its own tag would make an instance, and the instance's render is the one that cannot be caught.
|
|
266
|
+
const { stop } = runScoped(() => renderNode({ ...definition, tagName: "div" }, scope));
|
|
267
|
+
stopRender = stop;
|
|
268
|
+
return !bindingThrew;
|
|
269
|
+
} catch {
|
|
270
|
+
return false;
|
|
271
|
+
} finally {
|
|
272
|
+
setTemplateErrorHandler(null);
|
|
273
|
+
stopRender?.();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** A component whose definition is a JSON file in the project, which is what can be tried first. */
|
|
278
|
+
function isProjectDefinition(component: ComponentEntry): boolean {
|
|
279
|
+
return component.source !== "npm" && (component.path?.endsWith(".json") ?? false);
|
|
280
|
+
}
|
|
281
|
+
|
|
161
282
|
/**
|
|
162
283
|
* The preview for one file — a registered component's own preview when there is one, the document
|
|
163
284
|
* render otherwise. Cached; a cache hit does no work at all.
|
|
@@ -172,8 +293,17 @@ export async function previewFor(
|
|
|
172
293
|
}
|
|
173
294
|
let rendered: HTMLElement | null | undefined;
|
|
174
295
|
try {
|
|
296
|
+
/* A component's `npm:` sidecar, from where the project's packages are served: a preview is a
|
|
297
|
+
live render in this window, and the runtime would otherwise hand `import()` the `npm:` URL. */
|
|
298
|
+
setNpmModuleResolver((specifier) => npmModuleUrl(specifier, `${canvasBaseOrigin()}/`));
|
|
299
|
+
// The project's stylesheet, ready for the box this preview is shown in (`preview-style.ts`).
|
|
300
|
+
await loadProjectPreviewSheets();
|
|
175
301
|
const component = componentRegistry.find((c: ComponentEntry) => c.path === path);
|
|
176
|
-
|
|
302
|
+
if (component && isProjectDefinition(component)) {
|
|
303
|
+
rendered = await projectComponentPreview(component, path);
|
|
304
|
+
} else {
|
|
305
|
+
rendered = component ? await renderComponentPreview(component) : await renderDocPreview(path);
|
|
306
|
+
}
|
|
177
307
|
} catch {
|
|
178
308
|
return undefined;
|
|
179
309
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
2
|
+
* The Content home's scan, as a {@link GridSource}.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* The scan is one more source over the same contract the CSV, collection, pages and connector grids
|
|
5
|
+
* already use: columns come from {@link libraryColumns}, rows are the scan, and every view (Table,
|
|
6
|
+
* Cards, Calendar, Board) draws the SAME rows through a different arrangement. A view switch never
|
|
7
|
+
* re-reads the project, and neither does showing another collection: the scan covers the project
|
|
8
|
+
* once, and each collection is a filter over it.
|
|
9
9
|
*
|
|
10
10
|
* It is a **read-only** source, and says so rather than pretending. Renaming, duplicating and
|
|
11
11
|
* deleting a file are real operations with real consequences (a rename rewrites references; a
|
|
@@ -16,8 +16,17 @@
|
|
|
16
16
|
|
|
17
17
|
import { getPlatform } from "../platform";
|
|
18
18
|
import { projectState } from "../store";
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
19
|
+
import { workspaceHomeTabId } from "../workspace/workspace-ids";
|
|
20
|
+
import { PAGES_COLLECTION, capitalise, humanName } from "../content/content-collections";
|
|
21
|
+
import { fileToRoute } from "@jxsuite/site/routes";
|
|
22
|
+
import { libraryScanDirs, scanLibrary } from "./library-model";
|
|
23
|
+
import { routedPageExtensions } from "../format/format-host";
|
|
24
|
+
import { DRAFT_FIELD } from "../content/draft-state";
|
|
25
|
+
import { isString } from "@jxsuite/schema/guards";
|
|
26
|
+
import { collectionForFile } from "../content/collection-match";
|
|
27
|
+
import { fileRecords, recordKey } from "../content/file-records";
|
|
28
|
+
import type { ContentTypeSchema } from "@jxsuite/schema/types";
|
|
29
|
+
import type { FileRecord } from "../content/file-records";
|
|
21
30
|
import type { LibraryFile, LibraryScan, ScanFailure } from "./library-model";
|
|
22
31
|
import type {
|
|
23
32
|
CommitResult,
|
|
@@ -30,44 +39,283 @@ import type {
|
|
|
30
39
|
|
|
31
40
|
/** Why a cell edit is refused — printed by the grid, so the author is told where to go instead. */
|
|
32
41
|
export const LIBRARY_READ_ONLY =
|
|
33
|
-
"The
|
|
34
|
-
"context menu, or open the file to edit it.";
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
export
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
"The Content workspace lists files; it does not edit them in the list. Rename, duplicate or " +
|
|
43
|
+
"delete from the file's context menu, or open the file to edit it.";
|
|
44
|
+
|
|
45
|
+
/** Which of an entry's own facts a collection's entries carry, so the Table draws a column for each. */
|
|
46
|
+
export interface EntryColumnFacts {
|
|
47
|
+
/** Some entry says Draft or Published: the collection has a draft workflow. */
|
|
48
|
+
status: boolean;
|
|
49
|
+
/** Some entry is dated by a field of its own. */
|
|
50
|
+
date: boolean;
|
|
51
|
+
/** The files are in more than one language (the Pages table draws a Language column). */
|
|
52
|
+
locale?: boolean;
|
|
53
|
+
/** The collection's own fields, each drawn as a column after Title, Status and Date. */
|
|
54
|
+
fields?: readonly EntryFieldColumn[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** One schema field the Table draws as a column of its own. */
|
|
58
|
+
export interface EntryFieldColumn {
|
|
59
|
+
/** The field's name in the entry. */
|
|
60
|
+
name: string;
|
|
61
|
+
/** The column heading. */
|
|
62
|
+
title: string;
|
|
63
|
+
/**
|
|
64
|
+
* The field holds a list (`tags: [Patios, Stone]`), which its list text joins with commas. A
|
|
65
|
+
* Board grouped by it puts an entry under each value, and the Filter matches any one of them.
|
|
66
|
+
*/
|
|
67
|
+
list?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* A field whose values are measures, not categories: a number (`works: 90`) or a date (`until`).
|
|
70
|
+
* Translations of one entry repeat them, which is not a grouping, so a Board never groups by
|
|
71
|
+
* one.
|
|
72
|
+
*/
|
|
73
|
+
measure?: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** The grid field a schema field's column is keyed by, kept apart from the file facts' names. */
|
|
77
|
+
export function entryFieldKey(name: string): string {
|
|
78
|
+
return `field:${name}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The schema field a column key names, or null for a column that is no schema field. */
|
|
82
|
+
export function entryFieldOfKey(key: string): string | null {
|
|
83
|
+
return key.startsWith("field:") ? key.slice("field:".length) : null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** At most this many schema fields become columns; the rest are one Edit in grid away. */
|
|
87
|
+
export const ENTRY_FIELD_COLUMN_LIMIT = 6;
|
|
88
|
+
|
|
89
|
+
/** Formats a list cell cannot show as text: an image is the Cards view's, a body is the editor's. */
|
|
90
|
+
const UNLISTED_FORMATS = new Set(["image", "markdown", "html", "richtext"]);
|
|
91
|
+
|
|
92
|
+
/** "publishedAt" → "Published at", "course" → "Course". */
|
|
93
|
+
export function fieldTitle(name: string): string {
|
|
94
|
+
return capitalise(humanName(name));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The schema fields a collection's Table draws, in declaration order: every scalar field except the
|
|
99
|
+
* ones the Table already names some other way (the title, the date, the draft flag) and the ones
|
|
100
|
+
* whose value is not text to read in a row (an image, a body, an object).
|
|
101
|
+
*
|
|
102
|
+
* @param schema The collection's schema.
|
|
103
|
+
* @param skip Field names drawn already: the title field and the date field.
|
|
104
|
+
*/
|
|
105
|
+
export function entryFieldColumns(
|
|
106
|
+
schema: ContentTypeSchema | null | undefined,
|
|
107
|
+
skip: ReadonlySet<string>,
|
|
108
|
+
): EntryFieldColumn[] {
|
|
109
|
+
return Object.entries(schema?.properties ?? {})
|
|
110
|
+
.filter(([name, field]) => {
|
|
111
|
+
if (skip.has(name) || name === DRAFT_FIELD) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
if (
|
|
115
|
+
field.type === "object" ||
|
|
116
|
+
(isString(field.format) && UNLISTED_FORMATS.has(field.format))
|
|
117
|
+
) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
// A list of images is a gallery, which no more reads as row text than one image does.
|
|
121
|
+
const itemFormat = field.items?.format;
|
|
122
|
+
return (
|
|
123
|
+
field.type !== "array" ||
|
|
124
|
+
(field.items?.type === "string" &&
|
|
125
|
+
!(isString(itemFormat) && UNLISTED_FORMATS.has(itemFormat)))
|
|
126
|
+
);
|
|
127
|
+
})
|
|
128
|
+
.slice(0, ENTRY_FIELD_COLUMN_LIMIT)
|
|
129
|
+
.map(([name, field]): EntryFieldColumn => {
|
|
130
|
+
const column: EntryFieldColumn = { name, title: fieldTitle(name) };
|
|
131
|
+
if (field.type === "array") {
|
|
132
|
+
column.list = true;
|
|
133
|
+
}
|
|
134
|
+
if (
|
|
135
|
+
field.type === "number" ||
|
|
136
|
+
field.type === "integer" ||
|
|
137
|
+
field.format === "date" ||
|
|
138
|
+
field.format === "date-time"
|
|
139
|
+
) {
|
|
140
|
+
column.measure = true;
|
|
141
|
+
}
|
|
142
|
+
return column;
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* The Pages table's columns (the Content-Pages board): **Title** (the name the route gives the
|
|
148
|
+
* page, as the Site panel names it), **URL**, **Template**, **Status** when some page is marked a
|
|
149
|
+
* draft, **Language** when the pages are in more than one, and **Edited**. No file name and no
|
|
150
|
+
* path: two pages are both `index.json`, and the URL already says where each one is.
|
|
151
|
+
*/
|
|
152
|
+
function pageColumns(facts?: EntryColumnFacts): GridColumn[] {
|
|
153
|
+
const columns: GridColumn[] = [
|
|
154
|
+
{ editable: false, field: "title", kind: "string", title: "Title", widthHint: 240 },
|
|
155
|
+
// The URL is what tells two pages apart: no two routed files publish the same one.
|
|
156
|
+
{ editable: false, field: "url", kind: "string", pk: true, title: "URL", widthHint: 200 },
|
|
157
|
+
{ editable: false, field: "template", kind: "string", title: "Template", widthHint: 140 },
|
|
158
|
+
];
|
|
159
|
+
if (facts?.status) {
|
|
160
|
+
columns.push({
|
|
161
|
+
editable: false,
|
|
162
|
+
field: "status",
|
|
163
|
+
kind: "string",
|
|
164
|
+
title: "Status",
|
|
165
|
+
widthHint: 100,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (facts?.locale) {
|
|
169
|
+
columns.push({
|
|
170
|
+
editable: false,
|
|
171
|
+
field: "locale",
|
|
172
|
+
kind: "string",
|
|
173
|
+
title: "Language",
|
|
174
|
+
widthHint: 110,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
columns.push({
|
|
178
|
+
editable: false,
|
|
179
|
+
field: "modified",
|
|
180
|
+
kind: "date",
|
|
181
|
+
title: "Edited",
|
|
182
|
+
widthHint: 120,
|
|
183
|
+
});
|
|
184
|
+
return columns;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* The Table view's columns for a collection. `path` is the row key and is never editable.
|
|
189
|
+
*
|
|
190
|
+
* Pages are listed by what an author calls them and where they are published ({@link pageColumns}).
|
|
191
|
+
* A declared collection leads with what an entry is called and where it stands (its **Title**, then
|
|
192
|
+
* its **Status** and **Date** when its entries have them), with the file it is stored in after: a
|
|
193
|
+
* post is "Hello world, Draft", not `hello-world.md`.
|
|
194
|
+
*/
|
|
195
|
+
export function libraryColumns(
|
|
196
|
+
collection: string = PAGES_COLLECTION,
|
|
197
|
+
facts?: EntryColumnFacts,
|
|
198
|
+
): GridColumn[] {
|
|
199
|
+
// The Locale column only when the entries are in more than one language: a column that is empty
|
|
200
|
+
// On every row of a one-language project is width taken from the Title for nothing.
|
|
201
|
+
const fileFacts: GridColumn[] = [
|
|
202
|
+
...(facts?.locale
|
|
203
|
+
? [
|
|
204
|
+
{
|
|
205
|
+
editable: false,
|
|
206
|
+
field: "locale",
|
|
207
|
+
kind: "string",
|
|
208
|
+
title: "Locale",
|
|
209
|
+
widthHint: 90,
|
|
210
|
+
} as const,
|
|
211
|
+
]
|
|
212
|
+
: []),
|
|
44
213
|
{ editable: false, field: "modified", kind: "date", title: "Modified", widthHint: 160 },
|
|
45
214
|
{ editable: false, field: "path", kind: "readonly", title: "Path", widthHint: 320 },
|
|
46
215
|
];
|
|
216
|
+
if (collection === PAGES_COLLECTION) {
|
|
217
|
+
return pageColumns(facts);
|
|
218
|
+
}
|
|
219
|
+
const entry: GridColumn[] = [
|
|
220
|
+
{ editable: false, field: "title", kind: "string", title: "Title", widthHint: 260 },
|
|
221
|
+
];
|
|
222
|
+
if (facts?.status) {
|
|
223
|
+
entry.push({
|
|
224
|
+
editable: false,
|
|
225
|
+
field: "status",
|
|
226
|
+
kind: "string",
|
|
227
|
+
title: "Status",
|
|
228
|
+
widthHint: 100,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (facts?.date) {
|
|
232
|
+
entry.push({ editable: false, field: "date", kind: "date", title: "Date", widthHint: 120 });
|
|
233
|
+
}
|
|
234
|
+
for (const field of facts?.fields ?? []) {
|
|
235
|
+
entry.push({
|
|
236
|
+
editable: false,
|
|
237
|
+
field: entryFieldKey(field.name),
|
|
238
|
+
kind: "string",
|
|
239
|
+
title: field.title,
|
|
240
|
+
widthHint: 140,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
// The Path names the file, so a File column beside it would say the same thing twice.
|
|
244
|
+
return [...entry, ...fileFacts];
|
|
47
245
|
}
|
|
48
246
|
|
|
49
247
|
/** One scanned file as a grid row. */
|
|
50
248
|
export function libraryRow(file: LibraryFile): GridRow {
|
|
51
249
|
return {
|
|
52
250
|
cells: {
|
|
53
|
-
category: file.category,
|
|
54
251
|
// Null, not "": the platform reported no locale directory for this file, and a grid cell
|
|
55
252
|
// Distinguishes "no value" from "the empty string" everywhere else in this contract.
|
|
56
253
|
locale: file.locale ?? null,
|
|
57
254
|
modified: file.modified ?? null,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
255
|
+
// The scan reads no file, so a status, a template and a title are the Content home's to add.
|
|
256
|
+
status: null,
|
|
257
|
+
template: null,
|
|
258
|
+
title: file.name,
|
|
259
|
+
// A page's own URL; an entry's is its collection page's, which the scan does not read.
|
|
260
|
+
url: file.collection === PAGES_COLLECTION ? fileToRoute(file.path).urlPattern : null,
|
|
62
261
|
},
|
|
63
262
|
key: file.path,
|
|
64
263
|
};
|
|
65
264
|
}
|
|
66
265
|
|
|
67
266
|
/**
|
|
68
|
-
* A
|
|
69
|
-
*
|
|
70
|
-
*
|
|
267
|
+
* A file-backed collection's file, replaced by its records (`content/file-records.ts`): a CSV of
|
|
268
|
+
* fifteen prices is fifteen entries of its collection, which is what the counts and the Content
|
|
269
|
+
* home list. The file is read once per scan. One that cannot be read, or whose kind holds no
|
|
270
|
+
* records Studio reads, stays listed as the file it is.
|
|
271
|
+
*/
|
|
272
|
+
export async function expandFileRecords(
|
|
273
|
+
files: readonly LibraryFile[],
|
|
274
|
+
readFile: (path: string) => Promise<string>,
|
|
275
|
+
): Promise<LibraryFile[]> {
|
|
276
|
+
const expanded = await Promise.all(
|
|
277
|
+
files.map(async (file): Promise<LibraryFile[]> => {
|
|
278
|
+
const match = file.collection === null ? null : collectionForFile(file.path);
|
|
279
|
+
if (!match?.fileBacked) {
|
|
280
|
+
return [file];
|
|
281
|
+
}
|
|
282
|
+
let text: string;
|
|
283
|
+
try {
|
|
284
|
+
text = await readFile(file.path);
|
|
285
|
+
} catch {
|
|
286
|
+
return [file];
|
|
287
|
+
}
|
|
288
|
+
const records = fileRecords(file.path, text, match.def.schema);
|
|
289
|
+
if (records === null) {
|
|
290
|
+
return [file];
|
|
291
|
+
}
|
|
292
|
+
return records.map((record) => recordRow(file, record));
|
|
293
|
+
}),
|
|
294
|
+
);
|
|
295
|
+
return expanded.flat();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** One record of `file`, listed as a row of its own. */
|
|
299
|
+
function recordRow(file: LibraryFile, record: FileRecord): LibraryFile {
|
|
300
|
+
return {
|
|
301
|
+
...file,
|
|
302
|
+
name: record.title,
|
|
303
|
+
path: recordKey(file.path, record.index),
|
|
304
|
+
record: { fields: record.fields, row: record.row, source: file.path },
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** {@link expandFileRecords} over a whole scan, reading through the platform. */
|
|
309
|
+
async function withRecords(scan: LibraryScan): Promise<LibraryScan> {
|
|
310
|
+
const platform = getPlatform();
|
|
311
|
+
const files = await expandFileRecords(scan.files, (path) => platform.readFile(path));
|
|
312
|
+
return { ...scan, files };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* The scan's source, plus the two questions a grid source cannot ask but the Content home must
|
|
317
|
+
* answer: what the scan could not read, and the files behind the rows (the non-table layouts want
|
|
318
|
+
* the typed record, not a cell bag).
|
|
71
319
|
*/
|
|
72
320
|
export interface LibrarySource extends GridSource {
|
|
73
321
|
/** The scanned files, in path order. Empty before the first {@link GridSource.rows} call. */
|
|
@@ -76,15 +324,19 @@ export interface LibrarySource extends GridSource {
|
|
|
76
324
|
failures: () => readonly ScanFailure[];
|
|
77
325
|
/** Whether a scan has completed at least once — "not loaded yet" is not "empty". */
|
|
78
326
|
scanned: () => boolean;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
327
|
+
/**
|
|
328
|
+
* Whether the completed scan walked a different directory list than the project has now.
|
|
329
|
+
*
|
|
330
|
+
* The list is read when the project's directories load and grows when a collection is declared,
|
|
331
|
+
* and either can happen after a scan already finished: a window restored straight into Content
|
|
332
|
+
* asks for its rows before the project's directories have loaded, and the walk of none is an
|
|
333
|
+
* answer of none. A stale scan is not an empty project, so its owner discards it.
|
|
334
|
+
*/
|
|
335
|
+
stale: () => boolean;
|
|
84
336
|
}
|
|
85
337
|
|
|
86
338
|
/**
|
|
87
|
-
* Build the
|
|
339
|
+
* Build the scan's source. The scan is lazy: nothing is read until the first `rows()`.
|
|
88
340
|
*
|
|
89
341
|
* @param {() => readonly string[]} [dirs] Project directories to scan, injectable for tests and for
|
|
90
342
|
* the perf measurement; defaults to the open project's own list.
|
|
@@ -92,8 +344,12 @@ export function libraryTabId(): string {
|
|
|
92
344
|
export function createLibrarySource(dirs?: () => readonly string[]): LibrarySource {
|
|
93
345
|
let scan: LibraryScan | null = null;
|
|
94
346
|
let inFlight: Promise<LibraryScan> | null = null;
|
|
347
|
+
/** The directory list the cached scan walked, joined: the key {@link LibrarySource.stale} tests. */
|
|
348
|
+
let scannedKey: string | null = null;
|
|
95
349
|
|
|
96
|
-
const projectDirs =
|
|
350
|
+
const projectDirs =
|
|
351
|
+
dirs ??
|
|
352
|
+
(() => libraryScanDirs(projectState?.projectDirs ?? [], projectState?.projectConfig ?? null));
|
|
97
353
|
|
|
98
354
|
async function ensureScan(): Promise<LibraryScan> {
|
|
99
355
|
if (scan) {
|
|
@@ -101,11 +357,21 @@ export function createLibrarySource(dirs?: () => readonly string[]): LibrarySour
|
|
|
101
357
|
}
|
|
102
358
|
// Coalesce: five layouts and a command can all ask at once on the first paint, and the answer
|
|
103
359
|
// Is one walk of the project, not six.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
360
|
+
const walked = projectDirs();
|
|
361
|
+
/* A collection source the project's own list does not hold may not exist yet (a collection
|
|
362
|
+
declared before its first entry): its absence is an empty collection, never a failure. */
|
|
363
|
+
const conventional = new Set(dirs ? walked : (projectState?.projectDirs ?? []));
|
|
364
|
+
const optional = new Set(walked.filter((dir) => !conventional.has(dir)));
|
|
365
|
+
// The extensions first: a page is the file the build routes, and that is a fact of the formats.
|
|
366
|
+
inFlight ??= routedPageExtensions()
|
|
367
|
+
.then((pageExtensions) => scanLibrary(walked, getPlatform(), optional, pageExtensions))
|
|
368
|
+
.then(withRecords)
|
|
369
|
+
.then((result) => {
|
|
370
|
+
scan = result;
|
|
371
|
+
scannedKey = walked.join("\n");
|
|
372
|
+
inFlight = null;
|
|
373
|
+
return result;
|
|
374
|
+
});
|
|
109
375
|
return inFlight;
|
|
110
376
|
}
|
|
111
377
|
|
|
@@ -134,8 +400,8 @@ export function createLibrarySource(dirs?: () => readonly string[]): LibrarySour
|
|
|
134
400
|
},
|
|
135
401
|
failures: () => scan?.failures ?? [],
|
|
136
402
|
files: () => scan?.files ?? [],
|
|
137
|
-
id:
|
|
138
|
-
label: "
|
|
403
|
+
id: workspaceHomeTabId("content"),
|
|
404
|
+
label: "Content",
|
|
139
405
|
async refresh() {
|
|
140
406
|
scan = null;
|
|
141
407
|
inFlight = null;
|
|
@@ -146,5 +412,6 @@ export function createLibrarySource(dirs?: () => readonly string[]): LibrarySour
|
|
|
146
412
|
return { rows: result.files.map((file) => libraryRow(file)), total: result.files.length };
|
|
147
413
|
},
|
|
148
414
|
scanned: () => scan !== null,
|
|
415
|
+
stale: () => scan !== null && scannedKey !== projectDirs().join("\n"),
|
|
149
416
|
};
|
|
150
417
|
}
|