@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
package/src/ui/media-picker.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
/**
|
|
3
|
-
* Media Picker — the project's media, and the
|
|
3
|
+
* Media Picker — the project's media, and the compact surfaces that offer them.
|
|
4
4
|
*
|
|
5
5
|
* The FIELD (`surfaces/media-field.json`) is a control a row draws: a thumbnail of what the value
|
|
6
6
|
* names, the value itself, an Upload button that adds a new file to the project and assigns it, and
|
|
7
|
-
* a Browse button. The BROWSER (`surfaces/media-browser.json`) is the panel Browse opens: a
|
|
8
|
-
* box over the
|
|
7
|
+
* a Browse button. The BROWSER (`surfaces/media-browser.json`) is the compact panel Browse opens: a
|
|
8
|
+
* search box over the project's media, with thumbnail previews for images, and a last row, **Browse
|
|
9
|
+
* all media…**, that opens the Media workspace's own surface in a dialog in pick mode
|
|
10
|
+
* (`media/media-library.ts`, studio.md §9.3). One picker, two sizes.
|
|
11
|
+
*
|
|
12
|
+
* **What media the project has is answered here, once.** {@link projectMediaFiles} walks
|
|
13
|
+
* `public/**` and every collection's `content/<collection>/images/**`, and the Media workspace, the
|
|
14
|
+
* Site panel's Media section, both pickers and the search-appearance dialog all read that one
|
|
15
|
+
* listing, so "what media does this project have" cannot have two answers in two choosers.
|
|
9
16
|
*
|
|
10
17
|
* **This module is the flow, not the markup.** Both surfaces are Jx documents now; what is left
|
|
11
18
|
* here is which files count as media, what a row's caption says, how long a keystroke waits before
|
|
@@ -20,13 +27,13 @@
|
|
|
20
27
|
* `<img>` has finished by the time `load` fires, so the caption is a read of work already done
|
|
21
28
|
* rather than a second fetch per row. What is NOT here is a usage count: `findReferences` sweeps
|
|
22
29
|
* every document in the project per query, and fifty of those to caption a list is the wrong trade.
|
|
23
|
-
* Usage is asked
|
|
24
|
-
*
|
|
30
|
+
* Usage is asked about one file where it changes a decision (the delete confirmation, the Media
|
|
31
|
+
* workspace's detail pane), and about every file only when the reader asks for **Unused**.
|
|
25
32
|
*
|
|
26
33
|
* @docs studio/projects/media
|
|
27
34
|
*/
|
|
28
35
|
|
|
29
|
-
import { getPlatform } from "../platform";
|
|
36
|
+
import { getPlatform, hasPlatform } from "../platform";
|
|
30
37
|
import { debouncedStyleCommit, renderOnly } from "../store";
|
|
31
38
|
import { mountMediaFieldSurface } from "../surfaces/media-field";
|
|
32
39
|
import { openMediaBrowserSurface } from "../surfaces/media-browser";
|
|
@@ -35,12 +42,15 @@ import { previewAssetSrc } from "../canvas/asset-refs";
|
|
|
35
42
|
import {
|
|
36
43
|
IMAGE_EXTENSIONS,
|
|
37
44
|
MEDIA_EXTENSIONS,
|
|
38
|
-
uploadAccept,
|
|
39
45
|
extensionOf,
|
|
46
|
+
mediaFileKind,
|
|
47
|
+
uploadAccept,
|
|
40
48
|
uploadAssets,
|
|
41
49
|
} from "../files/media-upload";
|
|
42
50
|
import {
|
|
43
51
|
invalidateMediaMeta,
|
|
52
|
+
loadMediaMeta,
|
|
53
|
+
mediaMetaSettled,
|
|
44
54
|
mediaMetaSummary,
|
|
45
55
|
peekMediaMeta,
|
|
46
56
|
recordImageSize,
|
|
@@ -48,28 +58,51 @@ import {
|
|
|
48
58
|
} from "../files/media-meta";
|
|
49
59
|
import { mediaSiteUrl, previewFileSrc } from "../files/media-paths";
|
|
50
60
|
import { notedListing } from "../files/asset-versions";
|
|
61
|
+
import type { MediaFileKind } from "../files/media-upload";
|
|
51
62
|
import type {
|
|
52
63
|
MediaBrowserRow,
|
|
53
64
|
MediaBrowserSurfaceHandle,
|
|
54
65
|
MediaBrowserView,
|
|
55
66
|
} from "../surfaces/media-browser";
|
|
56
67
|
import type { MediaFieldHandle, MediaFieldView } from "../surfaces/media-field";
|
|
68
|
+
import type * as MediaLibraryModule from "../media/media-library";
|
|
57
69
|
|
|
58
70
|
// ─── Media file cache ────────────────────────────────────────────────────────
|
|
59
71
|
|
|
60
|
-
/** One
|
|
61
|
-
interface
|
|
62
|
-
/** The
|
|
63
|
-
path: string;
|
|
64
|
-
/** Project-relative path on disk, the key everything in `media-meta` is stored under. */
|
|
72
|
+
/** One media file of the project, as every browsing surface lists it. */
|
|
73
|
+
export interface ProjectMediaFile {
|
|
74
|
+
/** The project-relative path on disk, the key everything in `media-meta` is stored under. */
|
|
65
75
|
file: string;
|
|
66
76
|
name: string;
|
|
77
|
+
/** The site URL a document authors for it, which is what a pick commits. */
|
|
78
|
+
ref: string;
|
|
67
79
|
isImage: boolean;
|
|
80
|
+
/** What the file is, as the Media workspace's filters name it. */
|
|
81
|
+
kind: MediaFileKind;
|
|
68
82
|
}
|
|
69
83
|
|
|
70
|
-
let mediaCache:
|
|
84
|
+
let mediaCache: ProjectMediaFile[] = [];
|
|
71
85
|
let mediaCacheLoaded = false;
|
|
72
86
|
|
|
87
|
+
/**
|
|
88
|
+
* The directory every collection co-locates its uploads in (`files/media-upload.ts`
|
|
89
|
+
* `uploadDirFor`).
|
|
90
|
+
*/
|
|
91
|
+
const COLLECTION_IMAGES = "images";
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Whether a project path is inside the media scan: under `public/`, or under a collection's
|
|
95
|
+
* `content/<collection>/images/`. A filesystem batch that touches neither cannot change what
|
|
96
|
+
* {@link projectMediaFiles} lists, so the listing is kept.
|
|
97
|
+
*/
|
|
98
|
+
export function isMediaScanPath(path: string): boolean {
|
|
99
|
+
const normalized = path.replaceAll("\\", "/");
|
|
100
|
+
return (
|
|
101
|
+
normalized.startsWith("public/") ||
|
|
102
|
+
new RegExp(`^content/[^/]+/${COLLECTION_IMAGES}/`).test(normalized)
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
73
106
|
/**
|
|
74
107
|
* Recursively collect media files from a directory, seeding {@link seedMediaMeta} with the listing
|
|
75
108
|
* on the way past — the size of every row is already in the response that enumerated it.
|
|
@@ -77,8 +110,8 @@ let mediaCacheLoaded = false;
|
|
|
77
110
|
async function collectMedia(
|
|
78
111
|
dir: string,
|
|
79
112
|
platform: ReturnType<typeof getPlatform>,
|
|
80
|
-
): Promise<
|
|
81
|
-
const results:
|
|
113
|
+
): Promise<ProjectMediaFile[]> {
|
|
114
|
+
const results: ProjectMediaFile[] = [];
|
|
82
115
|
try {
|
|
83
116
|
const entries = await notedListing(() => platform.listDirectory(dir));
|
|
84
117
|
seedMediaMeta(entries);
|
|
@@ -92,10 +125,11 @@ async function collectMedia(
|
|
|
92
125
|
results.push({
|
|
93
126
|
file: entry.path,
|
|
94
127
|
isImage: IMAGE_EXTENSIONS.has(ext),
|
|
128
|
+
kind: mediaFileKind(entry.name),
|
|
95
129
|
name: entry.name,
|
|
96
130
|
// The site URL is what production serves and therefore what a document should say.
|
|
97
|
-
// Media-paths holds the one definition of that mapping.
|
|
98
|
-
|
|
131
|
+
// Media-paths holds the one definition of that mapping, a collection's mount included.
|
|
132
|
+
ref: mediaSiteUrl(entry.path),
|
|
99
133
|
});
|
|
100
134
|
}
|
|
101
135
|
}
|
|
@@ -106,16 +140,141 @@ async function collectMedia(
|
|
|
106
140
|
return results;
|
|
107
141
|
}
|
|
108
142
|
|
|
109
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Every collection's co-located `images/` folder: `content/<collection>/images/**`.
|
|
145
|
+
*
|
|
146
|
+
* Listed from `content/` itself rather than from `project.json`'s declarations, because the folder
|
|
147
|
+
* is where an upload from an entry lands whatever its collection's `source` says, and a picker that
|
|
148
|
+
* could not see it could not choose a cover image the author had just uploaded to an entry.
|
|
149
|
+
*/
|
|
150
|
+
async function collectCollectionImages(
|
|
151
|
+
platform: ReturnType<typeof getPlatform>,
|
|
152
|
+
): Promise<ProjectMediaFile[]> {
|
|
153
|
+
let collections: string[];
|
|
154
|
+
try {
|
|
155
|
+
const entries = await platform.listDirectory("content");
|
|
156
|
+
collections = entries.filter((entry) => entry.type === "directory").map((entry) => entry.path);
|
|
157
|
+
} catch {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
const results: ProjectMediaFile[] = [];
|
|
161
|
+
for (const dir of collections) {
|
|
162
|
+
/* Descend only into an `images/` the collection actually holds: most collections have none,
|
|
163
|
+
and asking the backend to list a directory that does not exist is a 404 in the console. */
|
|
164
|
+
let holdsImages = false;
|
|
165
|
+
try {
|
|
166
|
+
const entries = await platform.listDirectory(dir);
|
|
167
|
+
holdsImages = entries.some(
|
|
168
|
+
(entry) => entry.type === "directory" && entry.name === COLLECTION_IMAGES,
|
|
169
|
+
);
|
|
170
|
+
} catch {
|
|
171
|
+
// An unreadable collection folder has no images to offer.
|
|
172
|
+
}
|
|
173
|
+
if (holdsImages) {
|
|
174
|
+
results.push(...(await collectMedia(`${dir}/${COLLECTION_IMAGES}`, platform)));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return results;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Which of the two scan roots, `public` and `content`, the project root holds as folders. A root
|
|
182
|
+
* listing that fails says nothing either way, so both are then walked, each tolerating its own
|
|
183
|
+
* absence as it always has.
|
|
184
|
+
*/
|
|
185
|
+
async function projectScanRoots(platform: ReturnType<typeof getPlatform>): Promise<Set<string>> {
|
|
186
|
+
try {
|
|
187
|
+
const entries = await platform.listDirectory(".");
|
|
188
|
+
return new Set(
|
|
189
|
+
entries
|
|
190
|
+
.filter((entry) => entry.type === "directory")
|
|
191
|
+
.map((entry) => entry.name)
|
|
192
|
+
.filter((name) => name === "public" || name === "content"),
|
|
193
|
+
);
|
|
194
|
+
} catch {
|
|
195
|
+
return new Set(["public", "content"]);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** The walk in flight, so every caller that asks while it runs shares it instead of starting one. */
|
|
200
|
+
let _mediaLoading: Promise<void> | null = null;
|
|
201
|
+
/** Bumped by {@link invalidateMediaCache}, so a walk that started before it cannot land after it. */
|
|
202
|
+
let _mediaEpoch = 0;
|
|
203
|
+
|
|
204
|
+
/** Surfaces that own their own repaint and want to hear when the listing moves. */
|
|
205
|
+
const _listeners = new Set<() => void>();
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Hear about every change to the listing: a walk landing, or an invalidation dropping it.
|
|
209
|
+
*
|
|
210
|
+
* For a surface that is not one of the named panels {@link loadMediaCache} repaints — the Media
|
|
211
|
+
* workspace's library, which owns its own reactivity the way the Library pane does.
|
|
212
|
+
*
|
|
213
|
+
* @returns The unsubscribe.
|
|
214
|
+
*/
|
|
215
|
+
export function onProjectMediaChange(listener: () => void): () => void {
|
|
216
|
+
_listeners.add(listener);
|
|
217
|
+
return () => {
|
|
218
|
+
_listeners.delete(listener);
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function announce(): void {
|
|
223
|
+
for (const listener of _listeners) {
|
|
224
|
+
listener();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function loadMediaCache(): Promise<void> {
|
|
110
229
|
if (mediaCacheLoaded) {
|
|
111
|
-
return;
|
|
230
|
+
return Promise.resolve();
|
|
231
|
+
}
|
|
232
|
+
if (_mediaLoading) {
|
|
233
|
+
return _mediaLoading;
|
|
234
|
+
}
|
|
235
|
+
if (!hasPlatform()) {
|
|
236
|
+
// No backend to list (a window still booting): nothing is known, and nothing is claimed.
|
|
237
|
+
return Promise.resolve();
|
|
238
|
+
}
|
|
239
|
+
const epoch = _mediaEpoch;
|
|
240
|
+
const walk = (async () => {
|
|
241
|
+
const platform = getPlatform();
|
|
242
|
+
/* Descend only into the scan roots the project actually has: a project with no `content/` (a
|
|
243
|
+
site of pages alone) or no `public/` would otherwise ask the backend to list a folder that
|
|
244
|
+
does not exist, which is a 404 in the console on every visit to Media. */
|
|
245
|
+
const roots = await projectScanRoots(platform);
|
|
246
|
+
const [published, collections] = await Promise.all([
|
|
247
|
+
roots.has("public") ? collectMedia("public", platform) : [],
|
|
248
|
+
roots.has("content") ? collectCollectionImages(platform) : [],
|
|
249
|
+
]);
|
|
250
|
+
if (epoch !== _mediaEpoch) {
|
|
251
|
+
// Superseded: this listing is older than the invalidation, and a newer walk owns the cache.
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
mediaCache = [...published, ...collections];
|
|
255
|
+
mediaCacheLoaded = true;
|
|
256
|
+
_mediaLoading = null;
|
|
257
|
+
// Re-render the host panels so the browse panel has entries to show once the async listing
|
|
258
|
+
// Resolves. Mirrors loadLayoutEntries()'s renderOnly() in head-panel.
|
|
259
|
+
renderOnly("leftPanel", "rightPanel");
|
|
260
|
+
announce();
|
|
261
|
+
})();
|
|
262
|
+
_mediaLoading = walk;
|
|
263
|
+
return walk;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* The project's media, or `null` while the listing is still being read — the same cache the picker
|
|
268
|
+
* browses, so the Site panel's Media section, the Media workspace and the image field cannot
|
|
269
|
+
* disagree about what exists. Asking starts the read; it repaints the panels that read it when it
|
|
270
|
+
* lands, and tells every {@link onProjectMediaChange} listener.
|
|
271
|
+
*/
|
|
272
|
+
export function projectMediaFiles(): readonly ProjectMediaFile[] | null {
|
|
273
|
+
if (!mediaCacheLoaded) {
|
|
274
|
+
void loadMediaCache();
|
|
275
|
+
return null;
|
|
112
276
|
}
|
|
113
|
-
|
|
114
|
-
mediaCache = await collectMedia("public", platform);
|
|
115
|
-
mediaCacheLoaded = true;
|
|
116
|
-
// Re-render the host panels so the browse panel has entries to show once the async listing
|
|
117
|
-
// Resolves. Mirrors loadLayoutEntries()'s renderOnly() in head-panel.
|
|
118
|
-
renderOnly("leftPanel", "rightPanel", "frontmatterPanel");
|
|
277
|
+
return mediaCache;
|
|
119
278
|
}
|
|
120
279
|
|
|
121
280
|
/**
|
|
@@ -127,30 +286,74 @@ async function loadMediaCache() {
|
|
|
127
286
|
export function invalidateMediaCache() {
|
|
128
287
|
mediaCache = [];
|
|
129
288
|
mediaCacheLoaded = false;
|
|
289
|
+
_mediaEpoch += 1;
|
|
290
|
+
_mediaLoading = null;
|
|
130
291
|
invalidateMediaMeta();
|
|
292
|
+
announce();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ─── The full-size picker ────────────────────────────────────────────────────
|
|
296
|
+
|
|
297
|
+
/** What a chooser offers: every media file, or only images, for a field whose value is a picture. */
|
|
298
|
+
export interface MediaPickOptions {
|
|
299
|
+
images?: boolean;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* The library flow, imported on the first **Browse all media…**. One cached promise, because two
|
|
304
|
+
* dynamic imports of a module in flight at once is the shape that loses its coverage record, and a
|
|
305
|
+
* field nobody opened the big picker from should not load the workspace's surface.
|
|
306
|
+
*/
|
|
307
|
+
let _library: Promise<typeof MediaLibraryModule> | null = null;
|
|
308
|
+
|
|
309
|
+
function mediaLibrary(): Promise<typeof MediaLibraryModule> {
|
|
310
|
+
_library ??= import("../media/media-library");
|
|
311
|
+
return _library;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Open the Media workspace's surface in a dialog, in pick mode, and commit what is chosen.
|
|
316
|
+
*
|
|
317
|
+
* @param {(val: string) => void} onCommit - What a pick writes: the file's site URL
|
|
318
|
+
* @param {MediaPickOptions} options - Images only, for a field that holds a picture
|
|
319
|
+
*/
|
|
320
|
+
export async function openFullMediaPicker(
|
|
321
|
+
onCommit: (val: string) => void,
|
|
322
|
+
options: MediaPickOptions = {},
|
|
323
|
+
): Promise<void> {
|
|
324
|
+
const library = await mediaLibrary();
|
|
325
|
+
library.openMediaPickDialog({ images: options.images === true, onPick: onCommit });
|
|
131
326
|
}
|
|
132
327
|
|
|
133
328
|
// ─── The browser ─────────────────────────────────────────────────────────────
|
|
134
329
|
|
|
135
|
-
/**
|
|
330
|
+
/**
|
|
331
|
+
* How many rows the panel lists at first, and how many more each **Show N more** adds. Past the cap
|
|
332
|
+
* the rows are counted, and the count is the control that lists them, so every file the Media
|
|
333
|
+
* workspace has can be reached from here: the cap bounds the first paint, never the answer.
|
|
334
|
+
*/
|
|
136
335
|
const BROWSE_LIMIT = 50;
|
|
137
336
|
|
|
138
337
|
/** The open panel, and what a pick in it commits. One at a time. */
|
|
139
338
|
let _browser: MediaBrowserSurfaceHandle | null = null;
|
|
140
339
|
let _browserCommit: ((value: string) => void) | null = null;
|
|
141
340
|
let _browserFilter = "";
|
|
341
|
+
/** How many rows the open panel lists; raised by Show more, and never lowered while it is open. */
|
|
342
|
+
let _browserLimit = BROWSE_LIMIT;
|
|
343
|
+
let _browserOptions: MediaPickOptions = {};
|
|
142
344
|
/** Where the panel was opened, kept so a filter keystroke does not move it. */
|
|
143
345
|
let _browserOrigin = { x: 0, y: 0 };
|
|
144
346
|
|
|
145
347
|
/** What the panel shows for the filter that stands now. */
|
|
146
348
|
function browserView(): MediaBrowserView {
|
|
147
349
|
const query = _browserFilter.toLowerCase();
|
|
350
|
+
const offered = _browserOptions.images ? mediaCache.filter((m) => m.isImage) : mediaCache;
|
|
148
351
|
const filtered = query
|
|
149
|
-
?
|
|
150
|
-
(m) => m.
|
|
352
|
+
? offered.filter(
|
|
353
|
+
(m) => m.ref.toLowerCase().includes(query) || m.name.toLowerCase().includes(query),
|
|
151
354
|
)
|
|
152
|
-
:
|
|
153
|
-
const listed = filtered.slice(0,
|
|
355
|
+
: offered;
|
|
356
|
+
const listed = filtered.slice(0, _browserLimit);
|
|
154
357
|
const rows: MediaBrowserRow[] = listed.map((m) => {
|
|
155
358
|
const caption = mediaMetaSummary(peekMediaMeta(m.file));
|
|
156
359
|
return {
|
|
@@ -159,16 +362,24 @@ function browserView(): MediaBrowserView {
|
|
|
159
362
|
file: m.file,
|
|
160
363
|
key: m.file,
|
|
161
364
|
name: m.name,
|
|
162
|
-
path: m.
|
|
365
|
+
path: m.ref,
|
|
163
366
|
thumbSrc: m.isImage ? previewFileSrc(m.file) : "",
|
|
164
367
|
thumbState: m.isImage ? "shown" : "hidden",
|
|
165
368
|
};
|
|
166
369
|
});
|
|
167
370
|
return {
|
|
168
|
-
|
|
371
|
+
browseAllLabel: _browserOptions.images ? "Browse all images…" : "Browse all media…",
|
|
372
|
+
/* An empty project says so, rather than "No matches" over a search nobody typed: the Upload
|
|
373
|
+
beside the field and the row below are what to do about it. */
|
|
374
|
+
emptyLabel:
|
|
375
|
+
offered.length > 0
|
|
376
|
+
? "No matches"
|
|
377
|
+
: _browserOptions.images
|
|
378
|
+
? "No images in this project yet"
|
|
379
|
+
: "No media in this project yet",
|
|
169
380
|
filter: _browserFilter,
|
|
170
|
-
moreLabel:
|
|
171
|
-
moreState: filtered.length >
|
|
381
|
+
moreLabel: `Show ${Math.min(filtered.length - _browserLimit, BROWSE_LIMIT)} more`,
|
|
382
|
+
moreState: filtered.length > _browserLimit ? "shown" : "hidden",
|
|
172
383
|
rows,
|
|
173
384
|
rowsState: rows.length > 0 ? "listed" : "empty",
|
|
174
385
|
x: _browserOrigin.x,
|
|
@@ -182,6 +393,7 @@ function dismissMediaPickerPopover() {
|
|
|
182
393
|
_browser = null;
|
|
183
394
|
_browserCommit = null;
|
|
184
395
|
_browserFilter = "";
|
|
396
|
+
_browserLimit = BROWSE_LIMIT;
|
|
185
397
|
open?.close();
|
|
186
398
|
}
|
|
187
399
|
|
|
@@ -198,7 +410,14 @@ let _sizeRepaint = 0;
|
|
|
198
410
|
* because the measurement has not changed.
|
|
199
411
|
*/
|
|
200
412
|
function noteImageSize(file: string, width: number, height: number) {
|
|
201
|
-
if (!recordImageSize(file, width, height)
|
|
413
|
+
if (!recordImageSize(file, width, height)) {
|
|
414
|
+
if (!mediaMetaSettled(file)) {
|
|
415
|
+
// An SVG's decode is not its size: the file says what it is, read once.
|
|
416
|
+
void loadMediaMeta(file).then(() => _browser?.update(browserView()));
|
|
417
|
+
}
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
if (_sizeRepaint !== 0) {
|
|
202
421
|
return;
|
|
203
422
|
}
|
|
204
423
|
_sizeRepaint = requestAnimationFrame(() => {
|
|
@@ -212,14 +431,23 @@ function noteImageSize(file: string, width: number, height: number) {
|
|
|
212
431
|
*
|
|
213
432
|
* Exported because the panel is a SURFACE OF ITS OWN — it renders into the popover layer, not into
|
|
214
433
|
* the field that opened it — so a converted surface can offer the same browser without
|
|
215
|
-
* re-implementing it.
|
|
434
|
+
* re-implementing it. The Inspector's Page tab's Browse button is one such caller, and the
|
|
435
|
+
* search-appearance dialog's image rows another: the panel is a top-layer `jx-popover`, so it
|
|
436
|
+
* paints over the modal that opened it.
|
|
216
437
|
*
|
|
217
438
|
* @param {HTMLElement} anchorEl
|
|
218
439
|
* @param {(val: string) => void} onCommit
|
|
440
|
+
* @param {MediaPickOptions} options - Images only, for a field that holds a picture
|
|
219
441
|
*/
|
|
220
|
-
export function showMediaPickerPopover(
|
|
442
|
+
export function showMediaPickerPopover(
|
|
443
|
+
anchorEl: HTMLElement,
|
|
444
|
+
onCommit: (val: string) => void,
|
|
445
|
+
options: MediaPickOptions = {},
|
|
446
|
+
) {
|
|
221
447
|
dismissMediaPickerPopover();
|
|
448
|
+
void loadMediaCache().then(() => _browser?.update(browserView()));
|
|
222
449
|
_browserCommit = onCommit;
|
|
450
|
+
_browserOptions = options;
|
|
223
451
|
const box = rectOf(anchorEl);
|
|
224
452
|
/* Where it opens, once. `jx-popover` clamps a freshly shown panel into the viewport itself, so
|
|
225
453
|
nothing here measures the panel or flips it above a low anchor — that was the layer-div
|
|
@@ -228,10 +456,19 @@ export function showMediaPickerPopover(anchorEl: HTMLElement, onCommit: (val: st
|
|
|
228
456
|
_browser = openMediaBrowserSurface(
|
|
229
457
|
browserView(),
|
|
230
458
|
{
|
|
459
|
+
browseAll: () => {
|
|
460
|
+
const commit = _browserCommit;
|
|
461
|
+
const chosen = _browserOptions;
|
|
462
|
+
dismissMediaPickerPopover();
|
|
463
|
+
if (commit) {
|
|
464
|
+
void openFullMediaPicker(commit, chosen);
|
|
465
|
+
}
|
|
466
|
+
},
|
|
231
467
|
dismissed: () => {
|
|
232
468
|
_browser = null;
|
|
233
469
|
_browserCommit = null;
|
|
234
470
|
_browserFilter = "";
|
|
471
|
+
_browserLimit = BROWSE_LIMIT;
|
|
235
472
|
},
|
|
236
473
|
measure: noteImageSize,
|
|
237
474
|
pick: (path: string) => {
|
|
@@ -239,6 +476,10 @@ export function showMediaPickerPopover(anchorEl: HTMLElement, onCommit: (val: st
|
|
|
239
476
|
dismissMediaPickerPopover();
|
|
240
477
|
commit?.(path);
|
|
241
478
|
},
|
|
479
|
+
showMore: () => {
|
|
480
|
+
_browserLimit += BROWSE_LIMIT;
|
|
481
|
+
_browser?.update(browserView());
|
|
482
|
+
},
|
|
242
483
|
setFilter: (value: string) => {
|
|
243
484
|
_browserFilter = value;
|
|
244
485
|
_browser?.update(browserView());
|
|
@@ -301,6 +542,7 @@ const FIELD_DEBOUNCE_MS = 400;
|
|
|
301
542
|
interface FieldBinding {
|
|
302
543
|
prop: string;
|
|
303
544
|
commit: (value: string) => void;
|
|
545
|
+
options: MediaPickOptions;
|
|
304
546
|
}
|
|
305
547
|
|
|
306
548
|
interface FieldEntry extends FieldBinding {
|
|
@@ -339,12 +581,14 @@ function fieldView(prop: string, value: string): MediaFieldView {
|
|
|
339
581
|
* @param {string} prop - The attribute or prop being edited; the field's accessible name
|
|
340
582
|
* @param {string} value - What it holds now
|
|
341
583
|
* @param {(val: string) => void} onCommit - What a pick or a settled keystroke writes
|
|
584
|
+
* @param {MediaPickOptions} options - Images only, for a field whose value is a picture
|
|
342
585
|
*/
|
|
343
586
|
export function mountMediaPicker(
|
|
344
587
|
host: HTMLElement,
|
|
345
588
|
prop: string,
|
|
346
589
|
value: string,
|
|
347
590
|
onCommit: (val: string) => void,
|
|
591
|
+
options: MediaPickOptions = {},
|
|
348
592
|
): void {
|
|
349
593
|
// Kick off async load (won't block the mount)
|
|
350
594
|
void loadMediaCache();
|
|
@@ -352,14 +596,14 @@ export function mountMediaPicker(
|
|
|
352
596
|
if (existing) {
|
|
353
597
|
existing.prop = prop;
|
|
354
598
|
existing.commit = onCommit;
|
|
599
|
+
existing.options = options;
|
|
355
600
|
existing.handle.update(fieldView(prop, value));
|
|
356
601
|
return;
|
|
357
602
|
}
|
|
358
|
-
const entry: FieldBinding = { commit: onCommit, prop };
|
|
603
|
+
const entry: FieldBinding = { commit: onCommit, options, prop };
|
|
359
604
|
const handle = mountMediaFieldSurface(host, fieldView(prop, value), {
|
|
360
605
|
browse: (anchor: HTMLElement) => {
|
|
361
|
-
|
|
362
|
-
showMediaPickerPopover(anchor, (val: string) => entry.commit(val));
|
|
606
|
+
showMediaPickerPopover(anchor, (val: string) => entry.commit(val), entry.options);
|
|
363
607
|
},
|
|
364
608
|
/* Debounced under the prop's own key, so the canvas follows the typing without a document write
|
|
365
609
|
per keystroke and two fields never share a timer. */
|
package/src/ui/regions.ts
CHANGED
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
*
|
|
15
15
|
* ```text
|
|
16
16
|
* navigator the Navigator dock
|
|
17
|
-
* navigator/panel:
|
|
18
|
-
*
|
|
17
|
+
* navigator/panel:site the Site panel inside it
|
|
18
|
+
* pane.primary/publish the Publish home in the primary pane
|
|
19
|
+
* pane.primary/publish/commit a leaf within it (Review changes' destination card)
|
|
19
20
|
* inspector/tab:style the Style tab's body
|
|
20
21
|
* inspector/field:href a field row (the Inspector's documents already emit `data-prop`)
|
|
21
22
|
* pane.primary/tabs the primary pane's own tab strip
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
* inspector stamps `inspector/tab:${key}` from its own tab records; `getLayerSlot()` stamps
|
|
28
29
|
* `overlay.<kind>:<id>` from the key it already builds. A panel rename then propagates to the
|
|
29
30
|
* region automatically, and a stale id fails the shot contract instead of photographing the wrong
|
|
30
|
-
* box. Only leaves — `
|
|
31
|
+
* box. Only leaves — `pane.primary/publish/commit` — are hand-stamped, and those are counted.
|
|
31
32
|
*
|
|
32
33
|
* Resolution reads the live DOM rather than a registration table, for the same reason
|
|
33
34
|
* {@link import("./layers").isModalOpen} does: whatever is on screen is the answer, and there is no
|
|
@@ -61,7 +62,7 @@ export interface ParsedRegionId {
|
|
|
61
62
|
surface: RegionSurface;
|
|
62
63
|
/** `primary` in `pane.primary`, `dialog` in `overlay.dialog:settings`. */
|
|
63
64
|
instance?: string;
|
|
64
|
-
/** Everything after the first `/` — `
|
|
65
|
+
/** Everything after the first `/` — `publish/commit` in `pane.primary/publish/commit`. */
|
|
65
66
|
part?: string;
|
|
66
67
|
}
|
|
67
68
|
|
|
@@ -159,8 +160,8 @@ const DERIVED_RESOLVERS: readonly {
|
|
|
159
160
|
* external hook it has (`ui.md` §3.1), which is exactly what a derived region wants.
|
|
160
161
|
*
|
|
161
162
|
* `ui/media-picker.ts` used to STAMP this id on the button itself, and the id was a lie about
|
|
162
|
-
* where the button is: the same picker
|
|
163
|
-
* and that card
|
|
163
|
+
* where the button is: the same picker drew the Document Header card's Icon and og:image fields,
|
|
164
|
+
* and that card (since removed, studio.md §4.2) was rendered inside a PANE's stage. So `inspector/field:icon/browse` resolved to
|
|
164
165
|
* two elements the moment the grid split — one per pane, neither of them in the Inspector — and
|
|
165
166
|
* `resolveRegion` takes the LAST, which is the side pane's. `paneRegion` could never have reached
|
|
166
167
|
* it: an `inspector/…` id on an element outside the Inspector is not a pane-scoping problem, it
|