@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
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
/**
|
|
3
|
+
* The Media workspace's library, as a mounted document.
|
|
4
|
+
*
|
|
5
|
+
* This is the adapter. `media/media-library.ts` is the flow: which files a filter keeps, in what
|
|
6
|
+
* order, what each count may claim, which tile is selected, where an arrow lands and what Replace,
|
|
7
|
+
* Delete and an upload write. It hands this module one projection, and this module hands the
|
|
8
|
+
* document a scope and reports the nodes the flow needs to hold.
|
|
9
|
+
*
|
|
10
|
+
* **Four islands, all through `onNodeCreated`.** The flow needs four nodes the document creates:
|
|
11
|
+
* each tile (watched for viewport entry, so a thumbnail is requested only when it is looked at, and
|
|
12
|
+
* focused when the keyboard moves the selection onto it), the grid (measured, so ArrowDown lands on
|
|
13
|
+
* the tile drawn under the current one), and the two file inputs (opened by `media.upload` and
|
|
14
|
+
* `media.replace`). Each is announced as it is created, which is the one seam for a node a document
|
|
15
|
+
* made (`studio-ui-guidelines.md` §9.4); a node found by selector once would be detached DOM the
|
|
16
|
+
* moment a `$switch` moved.
|
|
17
|
+
*
|
|
18
|
+
* **The host is cleared in the pane and not in the dialog.** The pane's stage is handed to the mode
|
|
19
|
+
* that owns it whole, and emptying it is what stops two libraries stacking when a re-render lands
|
|
20
|
+
* before the first mount settled. The dialog's slot is this document's alone already.
|
|
21
|
+
*
|
|
22
|
+
* @docs studio/projects/media
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { isNonNullObject, isString } from "@jxsuite/schema/guards";
|
|
26
|
+
import { reactive } from "../reactivity";
|
|
27
|
+
import { mountSurface, registerSurface } from "../ui/surface";
|
|
28
|
+
import mediaLibraryDoc from "./media-library.json";
|
|
29
|
+
import type { JxElement } from "@jxsuite/schema/types";
|
|
30
|
+
import type { JxScope } from "@jxsuite/runtime/types";
|
|
31
|
+
import type { SurfaceHandle } from "../ui/surface";
|
|
32
|
+
import type { MediaUsageRow } from "./media-pane";
|
|
33
|
+
|
|
34
|
+
registerSurface("media-library", mediaLibraryDoc);
|
|
35
|
+
|
|
36
|
+
/** One filter in the left column. */
|
|
37
|
+
export interface MediaFilterRow {
|
|
38
|
+
key: string;
|
|
39
|
+
label: string;
|
|
40
|
+
/** The count, already a string; `""` while it is not known (Unused before its sweep). */
|
|
41
|
+
count: string;
|
|
42
|
+
pressed: "true" | "false";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** One tile in the grid. */
|
|
46
|
+
export interface MediaTileRow {
|
|
47
|
+
/** The row's identity across a filter or a sort: the file's path on disk. */
|
|
48
|
+
key: string;
|
|
49
|
+
file: string;
|
|
50
|
+
name: string;
|
|
51
|
+
/** "1200 × 800 · 84 KB", or the file's kind when nothing about it is measured yet. */
|
|
52
|
+
caption: string;
|
|
53
|
+
selected: "true" | "false";
|
|
54
|
+
/** The roving tab stop: `"0"` on exactly one tile, `"-1"` on every other. */
|
|
55
|
+
tabindex: "0" | "-1";
|
|
56
|
+
/** `pending` until the tile has scrolled into view, then `image`; a file with no picture: `glyph`. */
|
|
57
|
+
thumbState: "image" | "pending" | "glyph";
|
|
58
|
+
thumbSrc: string;
|
|
59
|
+
glyph: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Everything the document draws, as one value. */
|
|
63
|
+
export interface MediaLibraryView {
|
|
64
|
+
region: string;
|
|
65
|
+
mode: "manage" | "pick";
|
|
66
|
+
dropActive: "true" | "false";
|
|
67
|
+
filters: MediaFilterRow[];
|
|
68
|
+
title: string;
|
|
69
|
+
titleId: string;
|
|
70
|
+
query: string;
|
|
71
|
+
sort: string;
|
|
72
|
+
sortOptions: { label: string; value: string }[];
|
|
73
|
+
uploadAccept: string;
|
|
74
|
+
/** Where an upload lands, said before the files are chosen: "Upload into public/media/". */
|
|
75
|
+
uploadHint: string;
|
|
76
|
+
gridState: "loading" | "counting" | "empty" | "nomatch" | "listed";
|
|
77
|
+
countingLabel: string;
|
|
78
|
+
emptyTitle: string;
|
|
79
|
+
emptyDetail: string;
|
|
80
|
+
tiles: MediaTileRow[];
|
|
81
|
+
detailState: "none" | "shown";
|
|
82
|
+
detailHint: string;
|
|
83
|
+
selectedFile: string;
|
|
84
|
+
detailName: string;
|
|
85
|
+
detailFacts: string;
|
|
86
|
+
detailRef: string;
|
|
87
|
+
/**
|
|
88
|
+
* The viewer's stage for the file (`media/media-pane.ts`), minus the ones a side pane cannot
|
|
89
|
+
* hold.
|
|
90
|
+
*/
|
|
91
|
+
previewKind: "image" | "video" | "audio" | "font" | "glyph";
|
|
92
|
+
previewSrc: string;
|
|
93
|
+
previewGlyph: string;
|
|
94
|
+
/** A font's scoped `@font-face`, the declaration naming its family, and the pangram; else `""`. */
|
|
95
|
+
previewFontFace: string;
|
|
96
|
+
previewSpecimenFamily: string;
|
|
97
|
+
previewSpecimen: string;
|
|
98
|
+
/** The Replace input's `accept`: the selected file's own format only. */
|
|
99
|
+
replaceAccept: string;
|
|
100
|
+
usageState: "hidden" | "shown";
|
|
101
|
+
usageHeadline: string;
|
|
102
|
+
usageFiles: MediaUsageRow[];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** What a control asks the flow to do. */
|
|
106
|
+
export interface MediaLibraryActions {
|
|
107
|
+
setFilter: (key: string) => void;
|
|
108
|
+
setQuery: (value: string) => void;
|
|
109
|
+
setSort: (value: string) => void;
|
|
110
|
+
upload: (input: HTMLInputElement) => void;
|
|
111
|
+
dragOver: () => void;
|
|
112
|
+
dragOut: () => void;
|
|
113
|
+
drop: (files: FileList | null) => void;
|
|
114
|
+
select: (file: string) => void;
|
|
115
|
+
/** A tile received focus: selection follows it, so the first Enter acts on the tile in the ring. */
|
|
116
|
+
focusTile: (file: string) => void;
|
|
117
|
+
/** A double-click: open the file in its viewer, or in pick mode, use it. */
|
|
118
|
+
activate: (file: string) => void;
|
|
119
|
+
/** Enter on the grid: the same, for the selected file. */
|
|
120
|
+
activateSelected: () => void;
|
|
121
|
+
key: (key: string, ctrl: boolean) => void;
|
|
122
|
+
measure: (file: string, width: number, height: number) => void;
|
|
123
|
+
copyRef: () => void;
|
|
124
|
+
openDocument: (path: string) => void;
|
|
125
|
+
replace: (input: HTMLInputElement) => void;
|
|
126
|
+
remove: () => void;
|
|
127
|
+
openViewer: () => void;
|
|
128
|
+
reset: () => void;
|
|
129
|
+
/** A tile node was created for `file`. */
|
|
130
|
+
tileHost: (element: HTMLElement, file: string) => void;
|
|
131
|
+
/** The grid node was created. */
|
|
132
|
+
gridHost: (element: HTMLElement) => void;
|
|
133
|
+
/** A file input was created: the toolbar's Upload (`upload`) or the detail's Replace. */
|
|
134
|
+
inputHost: (element: HTMLInputElement, role: "upload" | "replace") => void;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface MediaLibrarySurfaceHandle {
|
|
138
|
+
ready: Promise<HTMLElement>;
|
|
139
|
+
/** Whether what was mounted is still inside the host (a mount still in flight counts). */
|
|
140
|
+
attached: () => boolean;
|
|
141
|
+
update: (view: MediaLibraryView) => void;
|
|
142
|
+
dispose: () => void;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** The scope the document reads. */
|
|
146
|
+
interface MediaLibraryScope
|
|
147
|
+
extends MediaLibraryView, Omit<MediaLibraryActions, "tileHost" | "gridHost" | "inputHost"> {
|
|
148
|
+
hasUsageFiles: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** An empty view: what a scope holds before the first projection. */
|
|
152
|
+
export function emptyMediaLibraryView(): MediaLibraryView {
|
|
153
|
+
return {
|
|
154
|
+
countingLabel: "",
|
|
155
|
+
detailFacts: "",
|
|
156
|
+
detailHint: "",
|
|
157
|
+
detailName: "",
|
|
158
|
+
detailRef: "",
|
|
159
|
+
detailState: "none",
|
|
160
|
+
dropActive: "false",
|
|
161
|
+
emptyDetail: "",
|
|
162
|
+
emptyTitle: "",
|
|
163
|
+
filters: [],
|
|
164
|
+
gridState: "loading",
|
|
165
|
+
mode: "manage",
|
|
166
|
+
previewFontFace: "",
|
|
167
|
+
previewGlyph: "",
|
|
168
|
+
previewKind: "glyph",
|
|
169
|
+
previewSpecimen: "",
|
|
170
|
+
previewSpecimenFamily: "",
|
|
171
|
+
previewSrc: "",
|
|
172
|
+
query: "",
|
|
173
|
+
region: "",
|
|
174
|
+
replaceAccept: "",
|
|
175
|
+
selectedFile: "",
|
|
176
|
+
sort: "",
|
|
177
|
+
sortOptions: [],
|
|
178
|
+
tiles: [],
|
|
179
|
+
title: "",
|
|
180
|
+
titleId: "",
|
|
181
|
+
uploadAccept: "",
|
|
182
|
+
uploadHint: "",
|
|
183
|
+
usageFiles: [],
|
|
184
|
+
usageHeadline: "",
|
|
185
|
+
usageState: "hidden",
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function project(scope: MediaLibraryScope, view: MediaLibraryView): void {
|
|
190
|
+
Object.assign(scope, view);
|
|
191
|
+
scope.hasUsageFiles = view.usageFiles.length > 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** The `part` a definition declares, or `""`. */
|
|
195
|
+
function partOf(def: JxElement | string): string {
|
|
196
|
+
const part = isString(def) ? undefined : def.attributes?.["part"];
|
|
197
|
+
return isString(part) ? part : "";
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** The file a mapped tile stands for, read off its `$map` item. */
|
|
201
|
+
function mappedFile(state: JxScope | undefined): string | null {
|
|
202
|
+
const map = state?.["$map"];
|
|
203
|
+
const item = isNonNullObject(map) && "item" in map ? map.item : undefined;
|
|
204
|
+
return isNonNullObject(item) && "file" in item && isString(item.file) ? item.file : null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Mount the library into `host`.
|
|
209
|
+
*
|
|
210
|
+
* @param {HTMLElement} host - The pane's stage, or the pick dialog's slot
|
|
211
|
+
* @param {MediaLibraryView} view - What to draw right now
|
|
212
|
+
* @param {MediaLibraryActions} actions - What each control does
|
|
213
|
+
* @param {{ clear: boolean }} options - Whether the host is a stage to be emptied first
|
|
214
|
+
* @returns {MediaLibrarySurfaceHandle}
|
|
215
|
+
*/
|
|
216
|
+
export function mountMediaLibrarySurface(
|
|
217
|
+
host: HTMLElement,
|
|
218
|
+
view: MediaLibraryView,
|
|
219
|
+
actions: MediaLibraryActions,
|
|
220
|
+
options: { clear: boolean },
|
|
221
|
+
): MediaLibrarySurfaceHandle {
|
|
222
|
+
if (options.clear) {
|
|
223
|
+
host.replaceChildren();
|
|
224
|
+
}
|
|
225
|
+
const { tileHost, gridHost, inputHost, ...controls } = actions;
|
|
226
|
+
const scope = reactive<MediaLibraryScope>({
|
|
227
|
+
...emptyMediaLibraryView(),
|
|
228
|
+
...controls,
|
|
229
|
+
hasUsageFiles: false,
|
|
230
|
+
});
|
|
231
|
+
project(scope, view);
|
|
232
|
+
|
|
233
|
+
let mounted: SurfaceHandle | null = null;
|
|
234
|
+
let disposed = false;
|
|
235
|
+
const ready = mountSurface("media-library", scope, host, {
|
|
236
|
+
onNodeCreated: (element, _path, def, state) => {
|
|
237
|
+
// `disposed` first: nodes are still created while a mount that was taken down runs out.
|
|
238
|
+
if (disposed || !(element instanceof HTMLElement)) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
const part = partOf(def);
|
|
242
|
+
if (part === "tile") {
|
|
243
|
+
const file = mappedFile(state);
|
|
244
|
+
if (file !== null) {
|
|
245
|
+
tileHost(element, file);
|
|
246
|
+
}
|
|
247
|
+
} else if (part === "grid") {
|
|
248
|
+
gridHost(element);
|
|
249
|
+
} else if (element instanceof HTMLInputElement && part === "upload-input") {
|
|
250
|
+
inputHost(element, "upload");
|
|
251
|
+
} else if (element instanceof HTMLInputElement && part === "replace-input") {
|
|
252
|
+
inputHost(element, "replace");
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
}).then((surface) => {
|
|
256
|
+
if (disposed) {
|
|
257
|
+
surface.dispose();
|
|
258
|
+
} else {
|
|
259
|
+
mounted = surface;
|
|
260
|
+
}
|
|
261
|
+
/* SAFETY: `media-library.json`'s root definition is a `div` element, so the runtime mounts an
|
|
262
|
+
HTMLElement; a `Text` root only comes from a document whose root is a bare string. */
|
|
263
|
+
return surface.root as HTMLElement;
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
attached: () => {
|
|
268
|
+
if (disposed) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
const root = mounted?.root;
|
|
272
|
+
return root === undefined ? true : root instanceof HTMLElement && host.contains(root);
|
|
273
|
+
},
|
|
274
|
+
dispose() {
|
|
275
|
+
disposed = true;
|
|
276
|
+
mounted?.dispose();
|
|
277
|
+
mounted = null;
|
|
278
|
+
},
|
|
279
|
+
ready,
|
|
280
|
+
update: (next) => project(scope, next),
|
|
281
|
+
};
|
|
282
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://jxsuite.com/schema/v1",
|
|
3
|
+
"$id": "StudioMediaPickDialog",
|
|
4
|
+
"$description": "The full-size media picker (`specs/studio.md` §9.3): a `jx-dialog` whose body is the Media workspace's own library surface in pick mode, so the picker and the workspace are one surface at two sizes. The dialog owns modality, Escape, the backdrop and focus restoration, and its footer is the answer: Cancel, or Use <file>, disabled until a file is chosen. The body is an ISLAND: this document draws the slot and nothing in it, and `surfaces/media-pick-dialog.ts` mounts `media-library.json` there through `onNodeCreated`.",
|
|
5
|
+
"tagName": "jx-dialog",
|
|
6
|
+
"$props": {
|
|
7
|
+
"headline": {
|
|
8
|
+
"$ref": "#/state/headline"
|
|
9
|
+
},
|
|
10
|
+
"confirmLabel": {
|
|
11
|
+
"$ref": "#/state/confirmLabel"
|
|
12
|
+
},
|
|
13
|
+
"confirmDisabled": {
|
|
14
|
+
"$ref": "#/state/blocked"
|
|
15
|
+
},
|
|
16
|
+
"cancelLabel": "Cancel",
|
|
17
|
+
"dismissible": true,
|
|
18
|
+
"size": "xl"
|
|
19
|
+
},
|
|
20
|
+
"attributes": {
|
|
21
|
+
"part": "media-pick"
|
|
22
|
+
},
|
|
23
|
+
"onconfirm": {
|
|
24
|
+
"$prototype": "Function",
|
|
25
|
+
"body": [
|
|
26
|
+
{
|
|
27
|
+
"operator": "call",
|
|
28
|
+
"target": {
|
|
29
|
+
"$ref": "#/state/confirm"
|
|
30
|
+
},
|
|
31
|
+
"value": []
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"oncancel": {
|
|
36
|
+
"$prototype": "Function",
|
|
37
|
+
"body": [
|
|
38
|
+
{
|
|
39
|
+
"operator": "call",
|
|
40
|
+
"target": {
|
|
41
|
+
"$ref": "#/state/closed"
|
|
42
|
+
},
|
|
43
|
+
"value": []
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"onclose": {
|
|
48
|
+
"$prototype": "Function",
|
|
49
|
+
"body": [
|
|
50
|
+
{
|
|
51
|
+
"operator": "call",
|
|
52
|
+
"target": {
|
|
53
|
+
"$ref": "#/state/closed"
|
|
54
|
+
},
|
|
55
|
+
"value": []
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
"children": [
|
|
60
|
+
{
|
|
61
|
+
"tagName": "div",
|
|
62
|
+
"attributes": {
|
|
63
|
+
"part": "library-slot"
|
|
64
|
+
},
|
|
65
|
+
"style": {
|
|
66
|
+
"$description": "The library's box in pick mode is its own (`media-library.json`, `data-mode=\"pick\"`); the slot only gives it the dialog body's width.",
|
|
67
|
+
"minWidth": "0"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
/**
|
|
3
|
+
* The full-size media picker's dialog, as a mounted document.
|
|
4
|
+
*
|
|
5
|
+
* A `jx-dialog` in the dialog layer whose body is an island: `media-pick-dialog.json` draws the
|
|
6
|
+
* slot, and the flow (`media/media-library.ts`) mounts the Media workspace's own library into it in
|
|
7
|
+
* pick mode, through the `onNodeCreated` seam (`studio-ui-guidelines.md` §9.4). So the picker and
|
|
8
|
+
* the workspace are one surface at two sizes, and the dialog is only the frame and the answer:
|
|
9
|
+
* Cancel, or Use <file>.
|
|
10
|
+
*
|
|
11
|
+
* **Await the ELEMENT, not just the mount.** `mountSurface` resolving means the document rendered;
|
|
12
|
+
* a `jx-dialog`'s own template is one `connectedCallback` later, and `showModal` in between finds
|
|
13
|
+
* no `<dialog>` to open. `whenReady` is shared with `surfaces/dialog.ts`.
|
|
14
|
+
*
|
|
15
|
+
* @docs studio/projects/media
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { reactive } from "../reactivity";
|
|
19
|
+
import { mountSurface, registerSurface } from "../ui/surface";
|
|
20
|
+
import { overlayRegion, REGION_ATTR } from "../ui/regions";
|
|
21
|
+
import { close, showModal } from "@jxsuite/ui/behaviors/dialog";
|
|
22
|
+
import { whenReady } from "./dialog";
|
|
23
|
+
import pickDoc from "./media-pick-dialog.json";
|
|
24
|
+
import type { JxElement } from "@jxsuite/schema/types";
|
|
25
|
+
import type { DialogLayerHandle, SurfaceHandle } from "../ui/surface";
|
|
26
|
+
import { isString } from "@jxsuite/schema/guards";
|
|
27
|
+
|
|
28
|
+
registerSurface("media-pick-dialog", pickDoc);
|
|
29
|
+
|
|
30
|
+
/** The slot id, and so the dialog's region: `overlay.dialog:media-picker`. */
|
|
31
|
+
export const MEDIA_PICK_SLOT = "media-picker";
|
|
32
|
+
|
|
33
|
+
/** What the dialog's own chrome says. */
|
|
34
|
+
export interface MediaPickDialogView {
|
|
35
|
+
headline: string;
|
|
36
|
+
/** "Use hero.jpg", or "Use file" while nothing is chosen. */
|
|
37
|
+
confirmLabel: string;
|
|
38
|
+
/** No file is chosen, so there is nothing to use yet. */
|
|
39
|
+
blocked: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface MediaPickDialogOptions {
|
|
43
|
+
layer: HTMLElement;
|
|
44
|
+
view: MediaPickDialogView;
|
|
45
|
+
/** The body slot was created: where the flow mounts the library. */
|
|
46
|
+
body: (host: HTMLElement) => void;
|
|
47
|
+
confirm: () => void;
|
|
48
|
+
/** The dialog closed, for any reason, once. */
|
|
49
|
+
closed: () => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface MediaPickDialogHandle extends DialogLayerHandle {
|
|
53
|
+
update: (view: MediaPickDialogView) => void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface MediaPickScope extends MediaPickDialogView {
|
|
57
|
+
confirm: () => void;
|
|
58
|
+
closed: () => void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function partOf(def: JxElement | string): string {
|
|
62
|
+
const part = isString(def) ? undefined : def.attributes?.["part"];
|
|
63
|
+
return isString(part) ? part : "";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Open the dialog. The handle's `ready` resolves once it is showing. */
|
|
67
|
+
export function openMediaPickDialogSurface(options: MediaPickDialogOptions): MediaPickDialogHandle {
|
|
68
|
+
const slot = document.createElement("div");
|
|
69
|
+
/* The layer is `pointer-events: none`; every slot turns them back on for its own content, or a
|
|
70
|
+
modal `<dialog>` in it is painted above everything and hit-tests to nothing (surfaces/add-new). */
|
|
71
|
+
slot.style.pointerEvents = "auto";
|
|
72
|
+
slot.setAttribute(REGION_ATTR, overlayRegion("dialog", MEDIA_PICK_SLOT));
|
|
73
|
+
options.layer.append(slot);
|
|
74
|
+
|
|
75
|
+
let closed = false;
|
|
76
|
+
const finish = (): void => {
|
|
77
|
+
if (closed) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
closed = true;
|
|
81
|
+
options.closed();
|
|
82
|
+
};
|
|
83
|
+
/** Read through a call, so a check after an `await` is not narrowed to the value before it. */
|
|
84
|
+
const isClosed = (): boolean => closed;
|
|
85
|
+
|
|
86
|
+
const scope = reactive<MediaPickScope>({
|
|
87
|
+
...options.view,
|
|
88
|
+
closed: finish,
|
|
89
|
+
confirm: options.confirm,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
let mounted: SurfaceHandle | null = null;
|
|
93
|
+
const ready = mountSurface("media-pick-dialog", scope, slot, {
|
|
94
|
+
onNodeCreated: (element, _path, def) => {
|
|
95
|
+
if (!isClosed() && element instanceof HTMLElement && partOf(def) === "library-slot") {
|
|
96
|
+
options.body(element);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
}).then(async (surface) => {
|
|
100
|
+
mounted = surface;
|
|
101
|
+
/* SAFETY: `media-pick-dialog.json`'s root definition is a `jx-dialog` element, so the runtime
|
|
102
|
+
mounts an HTMLElement. */
|
|
103
|
+
const element = surface.root as HTMLElement;
|
|
104
|
+
if (isClosed()) {
|
|
105
|
+
surface.dispose();
|
|
106
|
+
return element;
|
|
107
|
+
}
|
|
108
|
+
await whenReady(element);
|
|
109
|
+
if (!isClosed()) {
|
|
110
|
+
showModal(element);
|
|
111
|
+
}
|
|
112
|
+
return element;
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
close() {
|
|
117
|
+
const element = mounted?.root;
|
|
118
|
+
if (element instanceof HTMLElement) {
|
|
119
|
+
close(element);
|
|
120
|
+
}
|
|
121
|
+
mounted?.dispose();
|
|
122
|
+
mounted = null;
|
|
123
|
+
slot.remove();
|
|
124
|
+
finish();
|
|
125
|
+
},
|
|
126
|
+
host: slot,
|
|
127
|
+
ready,
|
|
128
|
+
update(view) {
|
|
129
|
+
Object.assign(scope, view);
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
package/src/surfaces/menu.ts
CHANGED
|
@@ -257,6 +257,7 @@ export function openMenu(options: OpenMenuOptions): MenuHandle {
|
|
|
257
257
|
}
|
|
258
258
|
// Run BEFORE closing: a command reads its target synchronously on entry, and closing the
|
|
259
259
|
// Menu is what clears it.
|
|
260
|
+
handBackKeyboard(menu, options.opener ?? null);
|
|
260
261
|
if (row.run) {
|
|
261
262
|
row.run();
|
|
262
263
|
} else {
|
|
@@ -367,6 +368,23 @@ export function openMenu(options: OpenMenuOptions): MenuHandle {
|
|
|
367
368
|
return handle;
|
|
368
369
|
}
|
|
369
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Give the keyboard back to the opener BEFORE a chosen row runs, while the menu still holds it.
|
|
373
|
+
*
|
|
374
|
+
* A row that opens a modal dialog (Open Project Settings, a confirmation) calls `showModal()` from
|
|
375
|
+
* inside its run, and the platform records whatever is focused at that moment as the element to
|
|
376
|
+
* restore when the dialog closes. Left alone that is the menu ROW, which is gone by the time the
|
|
377
|
+
* dialog closes, so Escape dropped focus on `<body>` instead of the gear that opened the menu. The
|
|
378
|
+
* opener is where focus goes when the menu closes anyway ({@link restoreFocus}); doing it first
|
|
379
|
+
* only makes it the element the dialog remembers. Only when the menu holds focus, for the reason
|
|
380
|
+
* `restoreFocus` gives.
|
|
381
|
+
*/
|
|
382
|
+
function handBackKeyboard(menu: MenuElement | null, opener: HTMLElement | null): void {
|
|
383
|
+
if (menu?.contains(document.activeElement) && opener?.isConnected && opener !== document.body) {
|
|
384
|
+
opener.focus({ preventScroll: true });
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
370
388
|
/**
|
|
371
389
|
* Hand the keyboard back to the opener, only when the menu still held it: an outside CLICK has
|
|
372
390
|
* already moved focus somewhere the author chose, and yanking it back would fight them. In a
|