@jxsuite/studio 6.1.2 → 8.0.1
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
package/src/grid/grid-open.ts
CHANGED
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
} from "./sources/content-source";
|
|
21
21
|
import { createConnectorSource } from "./sources/connector-source";
|
|
22
22
|
import { GRID_STUB_DOCUMENT, makeGridTabId, parseGridTabId } from "./grid-source";
|
|
23
|
-
import { libraryTabId } from "../browse/library-source";
|
|
24
23
|
import { argsSchema, optionalStringArg, stringArg, stringProperty } from "../commands/command-args";
|
|
25
24
|
import type { GridSource } from "./grid-source";
|
|
26
25
|
import type { GridOpenSurfaceHandle, GridSourceGroup } from "../surfaces/grid-open";
|
|
@@ -99,32 +98,6 @@ export function openCollectionGrid(typeName: string): Tab {
|
|
|
99
98
|
return openVirtualGridTab(createCollectionSource(typeName));
|
|
100
99
|
}
|
|
101
100
|
|
|
102
|
-
/**
|
|
103
|
-
* Open (or activate) the Library.
|
|
104
|
-
*
|
|
105
|
-
* A real tab, in the primary pane, with `manage` as its only mode — `commands/context.ts` maps that
|
|
106
|
-
* to the `library` editor kind, so the status bar and the pane context bar can name what is on
|
|
107
|
-
* screen. It is opened here rather than in `browse/` because this is the module that knows how a
|
|
108
|
-
* virtual tab is made; the pane itself resolves its own source and its own view state.
|
|
109
|
-
*
|
|
110
|
-
* Idempotent by construction, which is what lets a shot say "the Library is open" without caring
|
|
111
|
-
* whether an earlier step opened it.
|
|
112
|
-
*/
|
|
113
|
-
export function openLibraryTab(): Tab {
|
|
114
|
-
const id = libraryTabId();
|
|
115
|
-
const existing = workspace.tabs.get(id);
|
|
116
|
-
if (existing) {
|
|
117
|
-
activateTab(id);
|
|
118
|
-
return existing;
|
|
119
|
-
}
|
|
120
|
-
return openTab({
|
|
121
|
-
capabilities: { modes: ["manage"] },
|
|
122
|
-
document: structuredClone(GRID_STUB_DOCUMENT),
|
|
123
|
-
documentPath: null,
|
|
124
|
-
id,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
|
|
128
101
|
/** Open (or activate) the pages metadata grid. */
|
|
129
102
|
export function openPagesGrid(): Tab {
|
|
130
103
|
const id = makeGridTabId({ kind: "pages" });
|
package/src/grid/grid-panel.ts
CHANGED
|
@@ -41,6 +41,7 @@ import { createGridController, getGridController } from "./grid-controller";
|
|
|
41
41
|
import { createCsvFileSource } from "./sources/csv-file-source";
|
|
42
42
|
import { loadedGridEngine, loadGridEngine } from "./grid-lazy";
|
|
43
43
|
import { parseGridTabId } from "./grid-source";
|
|
44
|
+
import { clearGridReveal, pendingGridReveal } from "./grid-reveal";
|
|
44
45
|
import { mountGridPanelSurface } from "../surfaces/grid-panel";
|
|
45
46
|
import { openGridViewsSurface } from "../surfaces/grid-views";
|
|
46
47
|
import { openGridReplaceSurface } from "../surfaces/grid-replace";
|
|
@@ -615,15 +616,66 @@ export function renderGridMode(surface: CanvasSurface, tab: Tab) {
|
|
|
615
616
|
}
|
|
616
617
|
restored = true;
|
|
617
618
|
const layout = loadGridLayout(engine.source.id);
|
|
618
|
-
|
|
619
|
-
engine.setGrouping(layout?.groupBy ?? null);
|
|
619
|
+
restoreStoredOrder(engine, layout);
|
|
620
620
|
view.setSearch(layout?.filter ?? "");
|
|
621
621
|
});
|
|
622
622
|
}
|
|
623
|
+
revealRequested(engine, panel, tab.documentPath);
|
|
623
624
|
});
|
|
624
625
|
});
|
|
625
626
|
}
|
|
626
627
|
|
|
628
|
+
/**
|
|
629
|
+
* Put the stored sort and grouping back on a freshly built engine, asking the controller only for
|
|
630
|
+
* what differs from the order it already holds.
|
|
631
|
+
*
|
|
632
|
+
* The controller outlives the panel: leaving the grid's workspace and coming back rebuilds the
|
|
633
|
+
* table but keeps the controller, so on every return after the first the stored order IS the
|
|
634
|
+
* controller's order. Asking for it anyway was not free. Each of `setSort` and `setGrouping`
|
|
635
|
+
* re-projects the rows into the view, and a view still building parks that as a refresh it runs the
|
|
636
|
+
* moment it is built, so every open rendered its rows twice (the second time measuring every row's
|
|
637
|
+
* height again); a remote-sorting source re-queried its backend for the same rows on top. An
|
|
638
|
+
* explicit change (a saved view applied, a header in the Views popover) still goes straight to the
|
|
639
|
+
* controller, so re-asking for the order you are looking at keeps its meaning there.
|
|
640
|
+
*/
|
|
641
|
+
export function restoreStoredOrder(engine: GridController, layout: GridLayout | null): void {
|
|
642
|
+
const sort = layout?.sort ?? null;
|
|
643
|
+
const { query, grouping } = engine.state;
|
|
644
|
+
const sortHeld =
|
|
645
|
+
sort === null
|
|
646
|
+
? query.orderBy === undefined
|
|
647
|
+
: query.orderBy === sort.field && query.dir === sort.dir;
|
|
648
|
+
if (!sortHeld) {
|
|
649
|
+
void engine.setSort(sort);
|
|
650
|
+
}
|
|
651
|
+
const groupBy = layout?.groupBy ?? null;
|
|
652
|
+
if (grouping !== groupBy) {
|
|
653
|
+
engine.setGrouping(groupBy);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Land on the row a record's opener asked for (`grid-reveal.ts`), once the engine and the rows
|
|
659
|
+
* exist. The position counts the file's data rows as loaded, so it names a row whatever order a
|
|
660
|
+
* saved view sorts them into. The request is taken either way: a row that is gone is not revealed
|
|
661
|
+
* later by surprise.
|
|
662
|
+
*/
|
|
663
|
+
function revealRequested(
|
|
664
|
+
engine: GridController,
|
|
665
|
+
panel: ActiveGridPanel,
|
|
666
|
+
path: string | null | undefined,
|
|
667
|
+
): void {
|
|
668
|
+
const position = pendingGridReveal(path);
|
|
669
|
+
if (position === undefined || !path || !panel.view || engine.state.loading) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
clearGridReveal(path);
|
|
673
|
+
const row = engine.state.rows[position];
|
|
674
|
+
if (row) {
|
|
675
|
+
void panel.view.revealRow(row.key);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
627
679
|
/** The frame a tab with no grid source draws: one sentence, and no controls to press. */
|
|
628
680
|
function missingView(): GridPanelView {
|
|
629
681
|
return {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The row a file's grid should land on when it opens: a file-backed collection's record.
|
|
3
|
+
*
|
|
4
|
+
* A collection whose `source` names one file (`./content/projects.csv`) is written in that file's
|
|
5
|
+
* grid, so opening one of its records in the Content home opens the file (`specs/studio.md` §22.5).
|
|
6
|
+
* Opening the whole table and leaving the reader to find the row they clicked is not opening the
|
|
7
|
+
* record, so the opener names the record's position here and the grid selects that row and scrolls
|
|
8
|
+
* it into view once its rows have loaded.
|
|
9
|
+
*
|
|
10
|
+
* Reactive and keyed by the file's path, so a request reaches a grid that is already on screen as
|
|
11
|
+
* well as one still loading: the grid panel's one effect reads it, applies it and takes it.
|
|
12
|
+
*
|
|
13
|
+
* @docs studio/projects/browse
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { reactive } from "../reactivity";
|
|
17
|
+
|
|
18
|
+
/** File path → the position of the row to reveal, among the file's data rows from 0. */
|
|
19
|
+
const pending = reactive(new Map<string, number>());
|
|
20
|
+
|
|
21
|
+
/** Ask the grid of `path` to reveal the row at `position` (its place among the data rows, from 0). */
|
|
22
|
+
export function requestGridReveal(path: string, position: number): void {
|
|
23
|
+
pending.set(path, position);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** The row the grid of `path` is asked to reveal, or `undefined`. Reading it tracks it. */
|
|
27
|
+
export function pendingGridReveal(path: string | null | undefined): number | undefined {
|
|
28
|
+
return path ? pending.get(path) : undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Drop the request for `path` once the grid has acted on it. */
|
|
32
|
+
export function clearGridReveal(path: string): void {
|
|
33
|
+
pending.delete(path);
|
|
34
|
+
}
|
package/src/grid/grid-source.ts
CHANGED
|
@@ -143,19 +143,9 @@ const GRID_SCHEME = "grid://";
|
|
|
143
143
|
*/
|
|
144
144
|
export const GRID_STUB_DOCUMENT = { children: [], tagName: "div" };
|
|
145
145
|
|
|
146
|
-
/**
|
|
147
|
-
* The virtual sources a tab id can name.
|
|
148
|
-
*
|
|
149
|
-
* `library` is the odd one and deliberately so: the Library is a source over this same contract
|
|
150
|
-
* (`browse/library-source.ts`) but its tab's editor kind is `library`, not `grid` — Table is one of
|
|
151
|
-
* its five layouts rather than its identity. It lives in this id space because "which virtual tab
|
|
152
|
-
* is this?" has exactly one answer per window and there is no second scheme worth inventing.
|
|
153
|
-
* Category and layout are VIEW state and are deliberately absent from the id: a Library that
|
|
154
|
-
* changed identity when you clicked "Pages" would open a second tab per filter.
|
|
155
|
-
*/
|
|
146
|
+
/** The virtual sources a grid tab id can name. */
|
|
156
147
|
export type GridTabRef =
|
|
157
148
|
| { kind: "pages" }
|
|
158
|
-
| { kind: "library" }
|
|
159
149
|
| { kind: "collection"; name: string }
|
|
160
150
|
| { kind: "data"; connection: string; table: string };
|
|
161
151
|
|
|
@@ -165,9 +155,6 @@ export function makeGridTabId(ref: GridTabRef): string {
|
|
|
165
155
|
case "pages": {
|
|
166
156
|
return `${GRID_SCHEME}pages`;
|
|
167
157
|
}
|
|
168
|
-
case "library": {
|
|
169
|
-
return `${GRID_SCHEME}library`;
|
|
170
|
-
}
|
|
171
158
|
case "collection": {
|
|
172
159
|
return `${GRID_SCHEME}collection/${encodeURIComponent(ref.name)}`;
|
|
173
160
|
}
|
|
@@ -191,9 +178,6 @@ export function parseGridTabId(id: string): GridTabRef | null {
|
|
|
191
178
|
if (parts[0] === "pages" && parts.length === 1) {
|
|
192
179
|
return { kind: "pages" };
|
|
193
180
|
}
|
|
194
|
-
if (parts[0] === "library" && parts.length === 1) {
|
|
195
|
-
return { kind: "library" };
|
|
196
|
-
}
|
|
197
181
|
if (parts[0] === "collection" && parts.length === 2 && parts[1]) {
|
|
198
182
|
return { kind: "collection", name: decodeURIComponent(parts[1]) };
|
|
199
183
|
}
|
|
@@ -208,11 +192,8 @@ export function parseGridTabId(id: string): GridTabRef | null {
|
|
|
208
192
|
}
|
|
209
193
|
|
|
210
194
|
/**
|
|
211
|
-
* Short human label for a virtual tab ("posts · grid", "users @ main · grid", "Pages · grid").
|
|
212
|
-
*
|
|
213
|
-
* The Library is just "Library": the `· grid` suffix distinguishes a grid over a source from the
|
|
214
|
-
* document tab of the same name, and the Library has no document tab to be confused with — nor is a
|
|
215
|
-
* grid what it is, only what one of its layouts draws.
|
|
195
|
+
* Short human label for a virtual tab ("posts · grid", "users @ main · grid", "Pages · grid"). The
|
|
196
|
+
* `· grid` suffix distinguishes a grid over a source from the document tab of the same name.
|
|
216
197
|
*/
|
|
217
198
|
export function gridTabLabel(id: string): string | null {
|
|
218
199
|
const ref = parseGridTabId(id);
|
|
@@ -223,9 +204,6 @@ export function gridTabLabel(id: string): string | null {
|
|
|
223
204
|
case "pages": {
|
|
224
205
|
return "Pages · grid";
|
|
225
206
|
}
|
|
226
|
-
case "library": {
|
|
227
|
-
return "Library";
|
|
228
|
-
}
|
|
229
207
|
case "collection": {
|
|
230
208
|
return `${ref.name} · grid`;
|
|
231
209
|
}
|
package/src/grid/grid-view.ts
CHANGED
|
@@ -52,6 +52,12 @@ export interface GridView {
|
|
|
52
52
|
getSelectedRowKeys: () => string[];
|
|
53
53
|
/** Local text filter across all columns (empty clears). */
|
|
54
54
|
setSearch: (term: string) => void;
|
|
55
|
+
/**
|
|
56
|
+
* Select one row's cells and scroll it into view, once the table is built.
|
|
57
|
+
*
|
|
58
|
+
* @returns False when no row the table shows carries the key (filtered out, or gone).
|
|
59
|
+
*/
|
|
60
|
+
revealRow: (rowKey: string) => Promise<boolean>;
|
|
55
61
|
destroy: () => void;
|
|
56
62
|
}
|
|
57
63
|
|
|
@@ -216,12 +222,17 @@ export function createGridView(host: HTMLElement, controller: GridController): G
|
|
|
216
222
|
|
|
217
223
|
controller.bindView({ refreshData });
|
|
218
224
|
|
|
219
|
-
table
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
+
/* Settles once the table is built: a reveal asked for before then waits for it, because the
|
|
226
|
+
engine has no rows to find, and its default range would replace the one the reveal drew. */
|
|
227
|
+
const whenBuilt = new Promise<void>((resolve) => {
|
|
228
|
+
table.on("tableBuilt", () => {
|
|
229
|
+
built = true;
|
|
230
|
+
if (pendingRefresh) {
|
|
231
|
+
pendingRefresh = false;
|
|
232
|
+
refreshData();
|
|
233
|
+
}
|
|
234
|
+
resolve();
|
|
235
|
+
});
|
|
225
236
|
});
|
|
226
237
|
|
|
227
238
|
/*
|
|
@@ -384,6 +395,20 @@ export function createGridView(host: HTMLElement, controller: GridController): G
|
|
|
384
395
|
|
|
385
396
|
refreshData,
|
|
386
397
|
|
|
398
|
+
async revealRow(rowKey: string) {
|
|
399
|
+
await whenBuilt;
|
|
400
|
+
const row = table.getRow(rowKey);
|
|
401
|
+
const cells = row ? row.getCells() : [];
|
|
402
|
+
const [first] = cells;
|
|
403
|
+
const last = cells.at(-1);
|
|
404
|
+
if (!first || !last) {
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
table.addRange(first, last);
|
|
408
|
+
await table.scrollToRow(rowKey, "center", false).catch(() => {});
|
|
409
|
+
return true;
|
|
410
|
+
},
|
|
411
|
+
|
|
387
412
|
setSearch(term: string) {
|
|
388
413
|
const query = term.trim().toLowerCase();
|
|
389
414
|
if (query === "") {
|
|
@@ -39,6 +39,10 @@ import {
|
|
|
39
39
|
loadFormats,
|
|
40
40
|
} from "../../format/format-host";
|
|
41
41
|
import { parseSourceForPath } from "../../files/file-ops";
|
|
42
|
+
import { collectionForDirectory } from "../../content/collection-match";
|
|
43
|
+
import { collectionSourceFile } from "../../content/content-collections";
|
|
44
|
+
import { fileRecordIds as recordIdsOf } from "../../content/file-record-ids";
|
|
45
|
+
import { LOCALE_PLACEHOLDER } from "../../canvas/asset-resolve";
|
|
42
46
|
import { columnsFromSchema, inferColumnsFromRows } from "../schema-columns";
|
|
43
47
|
import { makeGridTabId } from "../grid-source";
|
|
44
48
|
import type {
|
|
@@ -129,14 +133,77 @@ async function listEntryFiles(dir: string, exts: string[]): Promise<string[]> {
|
|
|
129
133
|
return results.toSorted();
|
|
130
134
|
}
|
|
131
135
|
|
|
132
|
-
/**
|
|
136
|
+
/**
|
|
137
|
+
* The real directories a collection's entries live in.
|
|
138
|
+
*
|
|
139
|
+
* A plain source is its one directory. A localized source (`content/exhibitions/{locale}`) is one
|
|
140
|
+
* directory per language, and the literal path with the placeholder is a directory nobody has:
|
|
141
|
+
* listing it is a 500 from the dev server and an empty collection on screen. So the placeholder's
|
|
142
|
+
* parent is listed, and each subdirectory is kept when the one collection matcher
|
|
143
|
+
* (`content/collection-match.ts`) says that directory IS this collection's, which is what keeps an
|
|
144
|
+
* undeclared language or a co-located `images/` folder out.
|
|
145
|
+
*/
|
|
146
|
+
export async function collectionSourceDirs(info: { dir: string; name: string }): Promise<string[]> {
|
|
147
|
+
const cut = info.dir.indexOf(LOCALE_PLACEHOLDER);
|
|
148
|
+
if (cut === -1) {
|
|
149
|
+
return [info.dir];
|
|
150
|
+
}
|
|
151
|
+
const head = info.dir.slice(0, cut).replace(/\/$/, "");
|
|
152
|
+
const tail = info.dir.slice(cut + LOCALE_PLACEHOLDER.length);
|
|
153
|
+
let children;
|
|
154
|
+
try {
|
|
155
|
+
children = await getPlatform().listDirectory(head);
|
|
156
|
+
} catch {
|
|
157
|
+
return []; // No parent directory yet: a localized collection with no entries in any language.
|
|
158
|
+
}
|
|
159
|
+
return children
|
|
160
|
+
.filter((entry) => entry.type === "directory")
|
|
161
|
+
.map((entry) => `${entry.path}${tail}`.replace(/\/$/, ""))
|
|
162
|
+
.filter((dir) => collectionForDirectory(dir)?.name === info.name)
|
|
163
|
+
.toSorted();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Every entry file of a collection, across all of its directories. */
|
|
167
|
+
async function listCollectionFiles(info: CollectionInfo): Promise<string[]> {
|
|
168
|
+
const dirs = await collectionSourceDirs(info);
|
|
169
|
+
const lists = await Promise.all(dirs.map((dir) => listEntryFiles(dir, [info.ext])));
|
|
170
|
+
return lists.flat().toSorted();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* The ids of a file-backed collection's records (`content/file-records.ts`), or `null` when the
|
|
175
|
+
* source is a directory. A source that names a file is read, never listed: listing a CSV as a
|
|
176
|
+
* directory is a 500 from the dev server and a collection of no entries on screen.
|
|
177
|
+
*/
|
|
178
|
+
async function fileRecordIds(info: CollectionInfo): Promise<string[] | null> {
|
|
179
|
+
const file = collectionSourceFile(info.name);
|
|
180
|
+
if (file === null) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
let text: string;
|
|
184
|
+
try {
|
|
185
|
+
text = await getPlatform().readFile(file);
|
|
186
|
+
} catch {
|
|
187
|
+
return []; // A source file not written yet: an empty collection.
|
|
188
|
+
}
|
|
189
|
+
return recordIdsOf(file, text, info.def.idField);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Entry ids of a collection (file stems, or a file-backed collection's record ids), used by
|
|
194
|
+
* relationship cell editors and the Site panel's counts.
|
|
195
|
+
*/
|
|
133
196
|
export async function listCollectionEntryIds(typeName: string): Promise<string[]> {
|
|
134
197
|
await loadFormats().catch(() => {}); // Extension resolution needs the format registry.
|
|
135
198
|
const info = collectionInfo(typeName);
|
|
136
199
|
if (!info) {
|
|
137
200
|
return [];
|
|
138
201
|
}
|
|
139
|
-
const
|
|
202
|
+
const recordIds = await fileRecordIds(info);
|
|
203
|
+
if (recordIds !== null) {
|
|
204
|
+
return recordIds;
|
|
205
|
+
}
|
|
206
|
+
const files = await listCollectionFiles(info);
|
|
140
207
|
return files.map((path) => {
|
|
141
208
|
const base = path.split("/").pop() ?? path;
|
|
142
209
|
return base.slice(0, base.length - info.ext.length);
|
|
@@ -421,7 +488,11 @@ function buildEntryFileSource(opts: EntryFileSourceOptions): GridSource {
|
|
|
421
488
|
}
|
|
422
489
|
const doc = entry.document;
|
|
423
490
|
const fullDoc = { ...frontmatter, ...doc, children: doc.children ?? [] };
|
|
424
|
-
|
|
491
|
+
// The entry's own text, so only the patched keys are re-spelled.
|
|
492
|
+
const text = await formatSerialize(entry.formatName, fullDoc, {
|
|
493
|
+
mode: "roundtrip",
|
|
494
|
+
source: entry.rawText,
|
|
495
|
+
});
|
|
425
496
|
markLocalMutation(path);
|
|
426
497
|
await platform.writeFile(path, text);
|
|
427
498
|
entries.set(path, { ...entry, frontmatter, rawText: text });
|
|
@@ -628,10 +699,7 @@ export function createCollectionSource(typeName: string): GridSource {
|
|
|
628
699
|
);
|
|
629
700
|
},
|
|
630
701
|
label: typeName,
|
|
631
|
-
list: () =>
|
|
632
|
-
const info = requireInfo();
|
|
633
|
-
return listEntryFiles(info.dir, [info.ext]);
|
|
634
|
-
},
|
|
702
|
+
list: () => listCollectionFiles(requireInfo()),
|
|
635
703
|
resolveInsertPath: (raw) => {
|
|
636
704
|
const info = requireInfo();
|
|
637
705
|
let path = raw.trim().replace(/^\.\//, "").replace(/^\//, "");
|
|
@@ -114,6 +114,12 @@ declare module "tabulator-tables" {
|
|
|
114
114
|
replaceData(data: RowData<Value>[]): Promise<void>;
|
|
115
115
|
getRows(activeOnly?: string): RowComponent<Value>[];
|
|
116
116
|
getRanges(): RangeComponent<Value>[];
|
|
117
|
+
/** The row whose `index` field holds `index`, or `false` for none. */
|
|
118
|
+
getRow(index: string): RowComponent<Value> | false;
|
|
119
|
+
/** Scroll a row into view; rejects when no row holds `index`. */
|
|
120
|
+
scrollToRow(index: string, position?: string, ifVisible?: boolean): Promise<void>;
|
|
121
|
+
/** Select the range between two cells, replacing the oldest range past `selectableRange`. */
|
|
122
|
+
addRange(start: CellComponent<Value>, end: CellComponent<Value>): RangeComponent<Value>;
|
|
117
123
|
setFilter(filter: (data: RowData<Value>) => boolean): void;
|
|
118
124
|
clearFilter(includeHeaderFilters?: boolean): void;
|
|
119
125
|
redraw(force?: boolean): void;
|
package/src/hosting/document.ts
CHANGED
|
@@ -109,8 +109,8 @@ export function studioShellHtml(options: DocumentOptions = {}): string {
|
|
|
109
109
|
const boot = (options.boot ?? []).map((mod) => ` ${scriptTag(mod)}`).join("\n");
|
|
110
110
|
/* Only when there IS a boot module: `packages/studio/index.html` is generated with none and CI
|
|
111
111
|
diffs it byte for byte, and the dev server it is served by is the one host that should fall
|
|
112
|
-
back. The `<head>` below stays a literal, attribute-free tag —
|
|
113
|
-
injects
|
|
112
|
+
back. The `<head>` below stays a literal, attribute-free tag — a host that
|
|
113
|
+
injects a preload splices it in after that exact string. */
|
|
114
114
|
const bootMeta = boot
|
|
115
115
|
? ` <meta name="${BOOT_META.name}" content="${BOOT_META.content}" />\n`
|
|
116
116
|
: "";
|
|
@@ -127,6 +127,7 @@ function siblingPath(documentPath: string, locale: string): string | null {
|
|
|
127
127
|
if (i18n === null || !i18n.locales.includes(locale)) {
|
|
128
128
|
notify.error(`This project does not declare ${localeLabel(locale)}.`, {
|
|
129
129
|
action: "settings.open",
|
|
130
|
+
actionArgs: { section: "locales" },
|
|
130
131
|
detail: "Add it in Settings › Locales, or with the Add Language command.",
|
|
131
132
|
source: "Languages",
|
|
132
133
|
});
|
|
@@ -321,7 +322,7 @@ export function i18nCommands(): AnyCommand[] {
|
|
|
321
322
|
when: (ctx) => ctx.project.isMultilingual,
|
|
322
323
|
/* No `aiTool`, by studio-ui-guidelines.md §12.4's first deletion rule: the panel is a surface
|
|
323
324
|
for a person. */
|
|
324
|
-
/* The panel is off the rail — it spends no rail slot
|
|
325
|
+
/* The panel is off the rail — it spends no rail slot — so this
|
|
325
326
|
command and the generated `panel.focus.i18n` are its only two doors, and both open the
|
|
326
327
|
same one. The TITLES must differ even though the behaviour does not: the palette is a flat
|
|
327
328
|
searchable list where "View: Show Languages" twice is two rows a reader cannot choose
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Media workspace's verbs, as command records (studio.md §22.6, §9.3).
|
|
3
|
+
*
|
|
4
|
+
* - `media.browse { filter?, query?, select? }` shows the Media home, set to a filter, a search or
|
|
5
|
+
* one file selected. It is what Add new › Media runs after an upload, so the reader lands on what
|
|
6
|
+
* arrived, and what a script or the palette runs to ask "what is unused".
|
|
7
|
+
* - `media.upload` shows the Media home and opens its Upload picker.
|
|
8
|
+
* - `media.replace { path }` selects a file and opens its Replace picker: the one overwrite.
|
|
9
|
+
* - `media.delete { path }` deletes a file, through the confirmation that states its usage.
|
|
10
|
+
*
|
|
11
|
+
* Every one runs the flow, never a copy of it: the surface's Upload label and Replace input are the
|
|
12
|
+
* same controls these open (`HTMLInputElement.showPicker()`), and Delete is the detail pane's own.
|
|
13
|
+
*
|
|
14
|
+
* DOM-free at import: the flow and the home opener are loaded on the first run, so
|
|
15
|
+
* `commands/app-commands.ts` can list these records in a bare Bun process.
|
|
16
|
+
*
|
|
17
|
+
* @docs studio/projects/media
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
argsSchema,
|
|
22
|
+
enumArg,
|
|
23
|
+
enumProperty,
|
|
24
|
+
optionalStringArg,
|
|
25
|
+
stringArg,
|
|
26
|
+
stringProperty,
|
|
27
|
+
} from "../commands/command-args";
|
|
28
|
+
import { isMediaFile } from "../files/media-upload";
|
|
29
|
+
import { MEDIA_FILTER_KEYS } from "./media-library-model";
|
|
30
|
+
import type { MediaBrowseRequest } from "./media-library";
|
|
31
|
+
import type { CommandArgValues } from "../commands/command-args";
|
|
32
|
+
import type { AnyCommand, CommandRegistry } from "../commands/registry";
|
|
33
|
+
import type * as MediaLibraryModule from "./media-library";
|
|
34
|
+
import type * as WorkspaceHomeModule from "../workspace/workspace-home";
|
|
35
|
+
|
|
36
|
+
/** The ids, which Add new and the Site panel run. */
|
|
37
|
+
export const MEDIA_BROWSE = "media.browse";
|
|
38
|
+
export const MEDIA_UPLOAD = "media.upload";
|
|
39
|
+
export const MEDIA_REPLACE = "media.replace";
|
|
40
|
+
export const MEDIA_DELETE = "media.delete";
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The flow and the home opener, each imported once. One cached promise per module: two dynamic
|
|
44
|
+
* imports of one module in flight at once is the shape that loses its coverage record.
|
|
45
|
+
*/
|
|
46
|
+
let _library: Promise<typeof MediaLibraryModule> | null = null;
|
|
47
|
+
let _home: Promise<typeof WorkspaceHomeModule> | null = null;
|
|
48
|
+
|
|
49
|
+
function library(): Promise<typeof MediaLibraryModule> {
|
|
50
|
+
_library ??= import("./media-library");
|
|
51
|
+
return _library;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function home(): Promise<typeof WorkspaceHomeModule> {
|
|
55
|
+
_home ??= import("../workspace/workspace-home");
|
|
56
|
+
return _home;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** A path argument that must name a media file. Refused with the sentence a person can act on. */
|
|
60
|
+
function mediaPathArg(commandId: string, args: CommandArgValues, key: string): string {
|
|
61
|
+
const path = stringArg(commandId, args, key).replace(/^\.\//, "");
|
|
62
|
+
if (!isMediaFile(path)) {
|
|
63
|
+
throw new RangeError(
|
|
64
|
+
`command "${commandId}": "${path}" is not a media file (an image, video, audio, PDF or font).`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return path;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The `media.browse` arguments as a request, each read with the reader its schema names. */
|
|
71
|
+
function browseRequestOf(args: CommandArgValues): MediaBrowseRequest {
|
|
72
|
+
const request: MediaBrowseRequest = {};
|
|
73
|
+
if (args.filter !== undefined) {
|
|
74
|
+
request.filter = enumArg(MEDIA_BROWSE, args, "filter", MEDIA_FILTER_KEYS);
|
|
75
|
+
}
|
|
76
|
+
const query = optionalStringArg(MEDIA_BROWSE, args, "query");
|
|
77
|
+
if (query !== undefined) {
|
|
78
|
+
request.query = query;
|
|
79
|
+
}
|
|
80
|
+
if (args.select !== undefined) {
|
|
81
|
+
request.select = mediaPathArg(MEDIA_BROWSE, args, "select");
|
|
82
|
+
}
|
|
83
|
+
return request;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Open the Media home tab, which enters the Media workspace by being activated (§22.1). */
|
|
87
|
+
async function showHome(): Promise<void> {
|
|
88
|
+
const { openWorkspaceHome } = await home();
|
|
89
|
+
openWorkspaceHome("media");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const PATH_DESCRIPTION = "The media file's project path, such as public/media/hero.jpg.";
|
|
93
|
+
|
|
94
|
+
/** The four records. */
|
|
95
|
+
export function mediaCommands(): AnyCommand[] {
|
|
96
|
+
const base = {
|
|
97
|
+
category: "Project",
|
|
98
|
+
group: "1_media",
|
|
99
|
+
level: "project",
|
|
100
|
+
menus: ["palette"],
|
|
101
|
+
requires: "an open project",
|
|
102
|
+
when: (ctx) => ctx.project.open,
|
|
103
|
+
} satisfies Partial<AnyCommand>;
|
|
104
|
+
return [
|
|
105
|
+
{
|
|
106
|
+
...base,
|
|
107
|
+
args: argsSchema(
|
|
108
|
+
{
|
|
109
|
+
filter: enumProperty(
|
|
110
|
+
MEDIA_FILTER_KEYS,
|
|
111
|
+
"Which media to show: all, image, video, audio, document, font, or unused (files no document references).",
|
|
112
|
+
),
|
|
113
|
+
query: stringProperty("Search the media by name or path."),
|
|
114
|
+
select: stringProperty(`A file to select and describe. ${PATH_DESCRIPTION}`),
|
|
115
|
+
},
|
|
116
|
+
[],
|
|
117
|
+
),
|
|
118
|
+
icon: "image",
|
|
119
|
+
id: MEDIA_BROWSE,
|
|
120
|
+
run: async (_ctx, args) => {
|
|
121
|
+
const request = browseRequestOf(args);
|
|
122
|
+
const flow = await library();
|
|
123
|
+
flow.applyMediaBrowse(request);
|
|
124
|
+
await showHome();
|
|
125
|
+
},
|
|
126
|
+
title: "Browse Media",
|
|
127
|
+
undo: "none",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
...base,
|
|
131
|
+
icon: "upload-simple",
|
|
132
|
+
id: MEDIA_UPLOAD,
|
|
133
|
+
run: async () => {
|
|
134
|
+
const flow = await library();
|
|
135
|
+
await showHome();
|
|
136
|
+
flow.requestMediaUpload();
|
|
137
|
+
},
|
|
138
|
+
title: "Upload Media…",
|
|
139
|
+
undo: "none",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
...base,
|
|
143
|
+
args: argsSchema({ path: stringProperty(`The file to replace. ${PATH_DESCRIPTION}`) }, [
|
|
144
|
+
"path",
|
|
145
|
+
]),
|
|
146
|
+
icon: "arrows-clockwise",
|
|
147
|
+
id: MEDIA_REPLACE,
|
|
148
|
+
run: async (_ctx, args) => {
|
|
149
|
+
const path = mediaPathArg(MEDIA_REPLACE, args, "path");
|
|
150
|
+
const flow = await library();
|
|
151
|
+
await showHome();
|
|
152
|
+
flow.requestMediaReplace(path);
|
|
153
|
+
},
|
|
154
|
+
title: "Replace Media File…",
|
|
155
|
+
undo: "none",
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
...base,
|
|
159
|
+
args: argsSchema({ path: stringProperty(`The file to delete. ${PATH_DESCRIPTION}`) }, [
|
|
160
|
+
"path",
|
|
161
|
+
]),
|
|
162
|
+
// A delete is a danger row wherever it is listed, and sorts last (studio-ui-guidelines.md §12.3).
|
|
163
|
+
destructive: true,
|
|
164
|
+
group: "9_danger",
|
|
165
|
+
icon: "trash",
|
|
166
|
+
id: MEDIA_DELETE,
|
|
167
|
+
run: async (_ctx, args) => {
|
|
168
|
+
const path = mediaPathArg(MEDIA_DELETE, args, "path");
|
|
169
|
+
const flow = await library();
|
|
170
|
+
await flow.deleteMediaFile(path);
|
|
171
|
+
},
|
|
172
|
+
title: "Delete Media File…",
|
|
173
|
+
undo: "none",
|
|
174
|
+
},
|
|
175
|
+
];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Register the Media records. */
|
|
179
|
+
export function registerMediaCommands(registry: CommandRegistry): void {
|
|
180
|
+
registry.registerAll(mediaCommands());
|
|
181
|
+
}
|