@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/panels/head-panel.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
/**
|
|
3
|
-
* The
|
|
4
|
-
* its body: its frontmatter, the layout it is poured into, its title,
|
|
5
|
-
* icon, its OpenGraph card, and the raw `$head` tags no structured
|
|
3
|
+
* The Page panel — the Inspector's first tab (§6) — everything the open document says about itself
|
|
4
|
+
* before anybody reads its body: its frontmatter, the layout it is poured into, its title,
|
|
5
|
+
* description, viewport and icon, its OpenGraph card, and the raw `$head` tags no structured
|
|
6
|
+
* control owns.
|
|
6
7
|
*
|
|
7
8
|
* This is the FLOW. The markup is `surfaces/panel-page.json` and the scope that feeds it is
|
|
8
9
|
* `surfaces/panel-page.ts`; what stays here is every decision — what a `$head` entry means, which
|
|
@@ -13,29 +14,27 @@
|
|
|
13
14
|
* read at submit time, which is a spelling a document does not have: a binding writes only when the
|
|
14
15
|
* SCOPE moves, so every field's setter states what the control now holds before anything is decided
|
|
15
16
|
* about it, and clearing one after a successful add is then a real change the runtime carries back.
|
|
16
|
-
* `sync()` — not
|
|
17
|
-
*
|
|
18
|
-
* field.
|
|
17
|
+
* `sync()` — not a dock repaint — is what an echo calls: it assigns into the standing scope in the
|
|
18
|
+
* same turn, where a dock repaint is a frame away and would rebuild far more than one field.
|
|
19
19
|
*
|
|
20
|
-
* **What a frontmatter row looks like is `panels/frontmatter-fields.ts`'s answer.** The
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* **What a frontmatter row looks like is `panels/frontmatter-fields.ts`'s answer.** The Page tab is
|
|
21
|
+
* the one surface that edits a page's frontmatter (the Document Header card that once drew the same
|
|
22
|
+
* field set above the canvas is gone, studio.md §4.2), and keeping the row vocabulary in its own
|
|
23
|
+
* module is what stopped two surfaces disagreeing about `title`.
|
|
24
24
|
*
|
|
25
25
|
* The other half of this module has no surface at all: the merged-`$head` preview model that
|
|
26
|
-
* `panels/seo-modal.ts`
|
|
26
|
+
* `panels/seo-modal.ts` reads. It is pure, and pinned against
|
|
27
27
|
* `packages/compiler/src/site/head-merger.ts` by `tests/head-panel.test.ts`.
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
import { isString } from "@jxsuite/schema/guards";
|
|
31
|
-
import {
|
|
32
|
-
import { projectState, renderOnly } from "../store";
|
|
31
|
+
import { withPageDescription } from "@jxsuite/site/head-merger";
|
|
32
|
+
import { projectState, renderOnly, updateSession } from "../store";
|
|
33
33
|
import type { DirEntry, JsonValue } from "../types";
|
|
34
34
|
import { activeTab } from "../workspace/workspace";
|
|
35
35
|
import { activeRegistry } from "../commands/active-registry";
|
|
36
36
|
import { clearProblems, notify } from "../services/notify";
|
|
37
37
|
import type { NotifyOptions } from "../services/notify";
|
|
38
|
-
import { registerPanel } from "./panel-registry";
|
|
39
38
|
import { mutateUpdateFrontmatter, transact, transactDoc } from "../tabs/transact";
|
|
40
39
|
import { collectFmFields, projectFmField } from "./frontmatter-fields";
|
|
41
40
|
import { isGoogleFontEntry, isGoogleFontPreconnect } from "../utils/google-fonts";
|
|
@@ -45,7 +44,15 @@ import { pageRoute } from "./tab-strip";
|
|
|
45
44
|
import { LIVE_PREVIEW } from "../ui/timing";
|
|
46
45
|
import { previewAssetSrc } from "../canvas/asset-refs";
|
|
47
46
|
import { IMAGE_EXTENSIONS, extensionOf } from "../files/media-upload";
|
|
48
|
-
import { renderPagePanelSurface } from "../surfaces/panel-page";
|
|
47
|
+
import { renderPagePanelSurface, unmountPagePanelSurface } from "../surfaces/panel-page";
|
|
48
|
+
import { emptyState } from "../surfaces/empty-state";
|
|
49
|
+
import type { EmptyStateSpec } from "./empty-state";
|
|
50
|
+
import { collectionForFile } from "../content/collection-match";
|
|
51
|
+
import {
|
|
52
|
+
isEntryTab,
|
|
53
|
+
renderEntryInspector,
|
|
54
|
+
unmountEntryInspector,
|
|
55
|
+
} from "../content/entry-inspector";
|
|
49
56
|
|
|
50
57
|
import type { JxHeadEntry, JxMutableNode } from "@jxsuite/schema/types";
|
|
51
58
|
import type { Tab } from "../tabs/tab";
|
|
@@ -73,7 +80,14 @@ interface MetaField {
|
|
|
73
80
|
/** @type {{ name: string; path: string }[] | null} */
|
|
74
81
|
let layoutEntries: { name: string; path: string }[] | null = null;
|
|
75
82
|
|
|
76
|
-
|
|
83
|
+
/**
|
|
84
|
+
* List `layouts/` now and keep the answer as the picker's cache.
|
|
85
|
+
*
|
|
86
|
+
* The one reading of which files are layouts and what each is called: the Page panel's picker and
|
|
87
|
+
* Add new (`create/add-new-flow.ts`) both read it, so they cannot name different templates. JSON
|
|
88
|
+
* only, because a layout is resolved as JSON.
|
|
89
|
+
*/
|
|
90
|
+
export async function listLayoutEntries(): Promise<{ name: string; path: string }[]> {
|
|
77
91
|
try {
|
|
78
92
|
const platform = getPlatform();
|
|
79
93
|
const listing = await platform.listDirectory("layouts");
|
|
@@ -89,8 +103,12 @@ async function loadLayoutEntries() {
|
|
|
89
103
|
} catch {
|
|
90
104
|
layoutEntries = [];
|
|
91
105
|
}
|
|
92
|
-
|
|
93
|
-
|
|
106
|
+
return layoutEntries;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function loadLayoutEntries() {
|
|
110
|
+
await listLayoutEntries();
|
|
111
|
+
renderOnly("rightPanel", "seoModal");
|
|
94
112
|
}
|
|
95
113
|
|
|
96
114
|
/**
|
|
@@ -106,11 +124,10 @@ export function invalidateLayoutPickerCache() {
|
|
|
106
124
|
/**
|
|
107
125
|
* The layouts the picker offers, or `null` while the directory is still being listed.
|
|
108
126
|
*
|
|
109
|
-
* The listing
|
|
110
|
-
* (`src/surfaces/panel-page.json`) and
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* only one of them. Asking starts the read; it repaints both surfaces when it lands.
|
|
127
|
+
* The listing the Page tab's Layout picker is drawn from, handed over as DATA: the Page panel
|
|
128
|
+
* (`src/surfaces/panel-page.json`) is a document and reads it as a projection. One listing is one
|
|
129
|
+
* cache with one lifetime, which {@link invalidateLayoutPickerCache} forgets. Asking starts the
|
|
130
|
+
* read; it repaints the Inspector when it lands.
|
|
114
131
|
*
|
|
115
132
|
* @returns {{ name: string; path: string }[] | null}
|
|
116
133
|
*/
|
|
@@ -144,17 +161,64 @@ export const OG_FIELDS: MetaField[] = [
|
|
|
144
161
|
const MANAGED_META_KEYS = new Set([...PAGE_FIELDS, ...OG_FIELDS].map((f) => f.key));
|
|
145
162
|
|
|
146
163
|
/**
|
|
147
|
-
* Frontmatter keys the
|
|
148
|
-
*
|
|
164
|
+
* Frontmatter keys the Page tab owns with a dedicated control, so the generic field list must not
|
|
165
|
+
* print them a second time.
|
|
149
166
|
*
|
|
150
|
-
* **The one policy.**
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* two field sets was only safe once one of the two policies won, and it is this one: a document has
|
|
154
|
-
* ONE title, and the surface that gives it a named row is the one that owns the key.
|
|
167
|
+
* **The one policy.** A document has ONE title, and the surface that gives it a named row is the
|
|
168
|
+
* one that owns the key: listed as a generic field as well, the same key rendered twice with two
|
|
169
|
+
* different commit paths.
|
|
155
170
|
*/
|
|
156
171
|
export const RESERVED_FM_KEYS = new Set(["title"]);
|
|
157
172
|
|
|
173
|
+
/**
|
|
174
|
+
* The frontmatter keys a document's head controls own, so its generic field list leaves them out:
|
|
175
|
+
* {@link RESERVED_FM_KEYS}, plus `description` on a PAGE, whose description the build writes as its
|
|
176
|
+
* description meta (`withPageDescription` in `@jxsuite/site/head-merger`). Listed as a frontmatter
|
|
177
|
+
* field as well, a Markdown page showed two Description rows with two values (the frontmatter's and
|
|
178
|
+
* the head's empty one), and only one of them reaches the browser. A layout's or component's
|
|
179
|
+
* `description` is data the build does not read, so it stays an ordinary field.
|
|
180
|
+
*
|
|
181
|
+
* @param tab The document whose fields are listed.
|
|
182
|
+
*/
|
|
183
|
+
export function reservedFmKeys(tab: Tab | null): ReadonlySet<string> {
|
|
184
|
+
return isPageDocument(tab) ? new Set([...RESERVED_FM_KEYS, "description"]) : RESERVED_FM_KEYS;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* A page's description, from wherever the build reads it: a `<meta name="description">` in its own
|
|
189
|
+
* `$head` when it has one (stated last, it wins), else its `description` property.
|
|
190
|
+
*
|
|
191
|
+
* @param doc The head-bearing view of the document (`buildHeadDoc` for a Markdown page).
|
|
192
|
+
*/
|
|
193
|
+
export function pageDescription(doc: JxMutableNode): string {
|
|
194
|
+
const meta = findMetaEntry(doc.$head ?? [], "name", "description");
|
|
195
|
+
if (meta) {
|
|
196
|
+
return String(meta.attributes?.content ?? "");
|
|
197
|
+
}
|
|
198
|
+
return isString(doc.description) ? doc.description : "";
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Write a page's description where {@link pageDescription} reads it, so the one field never edits a
|
|
203
|
+
* value the build then overrules: the `description` property when that is where the page keeps it
|
|
204
|
+
* and its `$head` states none, else the head's meta (a page with neither gets the meta, as before).
|
|
205
|
+
* An empty value removes it.
|
|
206
|
+
*
|
|
207
|
+
* @param doc The head-bearing view of the document.
|
|
208
|
+
* @param value The new description, already trimmed.
|
|
209
|
+
*/
|
|
210
|
+
export function setPageDescription(doc: JxMutableNode, value: string): void {
|
|
211
|
+
if (!findMetaEntry(doc.$head ?? [], "name", "description") && isString(doc.description)) {
|
|
212
|
+
if (value) {
|
|
213
|
+
doc.description = value;
|
|
214
|
+
} else {
|
|
215
|
+
delete doc.description;
|
|
216
|
+
}
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
upsertMeta(doc, "name", "description", value);
|
|
220
|
+
}
|
|
221
|
+
|
|
158
222
|
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
159
223
|
|
|
160
224
|
/**
|
|
@@ -752,11 +816,10 @@ async function loadLayoutHead(path: string): Promise<void> {
|
|
|
752
816
|
_layoutHeadPending = null;
|
|
753
817
|
_layoutHeadPath = path;
|
|
754
818
|
_layoutHead = doc?.$head ?? [];
|
|
755
|
-
//
|
|
756
|
-
//
|
|
757
|
-
// Off this list renders the page's own head with the layout layer missing — which reads as
|
|
819
|
+
// Search appearance shows the merged head, and the layout layer arrives asynchronously. A modal
|
|
820
|
+
// Left off this list renders the page's own head with the layout layer missing — which reads as
|
|
758
821
|
// "you have no description" on a page that inherits one.
|
|
759
|
-
renderOnly("
|
|
822
|
+
renderOnly("seoModal");
|
|
760
823
|
}
|
|
761
824
|
|
|
762
825
|
/**
|
|
@@ -833,7 +896,8 @@ export function seoPreviewFor(tab: Tab | null, doc: JxMutableNode): SeoPreview {
|
|
|
833
896
|
{
|
|
834
897
|
layout: layout.entries,
|
|
835
898
|
layoutName: layout.name,
|
|
836
|
-
page:
|
|
899
|
+
// The page level as the build merges it: its `description` property leads its own head.
|
|
900
|
+
page: withPageDescription(doc.$head ?? [], doc.description),
|
|
837
901
|
site: config?.$head ?? [],
|
|
838
902
|
},
|
|
839
903
|
ctx,
|
|
@@ -854,7 +918,7 @@ export function seoPreviewFor(tab: Tab | null, doc: JxMutableNode): SeoPreview {
|
|
|
854
918
|
* component GAINED a header card because the focused tab was a page — and the Layout picker
|
|
855
919
|
* appeared or vanished in the pane you were editing according to the document in the other one.
|
|
856
920
|
*
|
|
857
|
-
* A caller whose subject genuinely IS the focused document (the
|
|
921
|
+
* A caller whose subject genuinely IS the focused document (the Inspector's Page tab) passes
|
|
858
922
|
* `activeTab.value`, where a reviewer can see it. The same bargain `setCanvasMode` made.
|
|
859
923
|
*
|
|
860
924
|
* @param {Tab | null} tab
|
|
@@ -877,10 +941,18 @@ export function buildHeadDoc(
|
|
|
877
941
|
const headDoc: JxMutableNode = { ...doc };
|
|
878
942
|
if (fm.title !== undefined) {
|
|
879
943
|
/* SAFETY: deliberately not narrowed — a YAML title can be a number, and it is overlaid as
|
|
880
|
-
written. Every reader of a head doc's `title` (titleRow here
|
|
881
|
-
|
|
944
|
+
written. Every reader of a head doc's `title` (titleRow here, seoPreviewFor) tests it with
|
|
945
|
+
isString before using it as text. */
|
|
882
946
|
headDoc.title = fm.title as string;
|
|
883
947
|
}
|
|
948
|
+
if (isString(fm.description)) {
|
|
949
|
+
// The page's description property, which the build writes as its description meta.
|
|
950
|
+
headDoc.description = fm.description;
|
|
951
|
+
}
|
|
952
|
+
if (isString(fm.$layout) || fm.$layout === false) {
|
|
953
|
+
// A Markdown page's layout override is its frontmatter `$layout`, which the build reads.
|
|
954
|
+
headDoc.$layout = fm.$layout;
|
|
955
|
+
}
|
|
884
956
|
if (fm.$head !== undefined) {
|
|
885
957
|
/* SAFETY: Studio's one writer of the frontmatter `$head` key is applyContentMutation below,
|
|
886
958
|
which stores a JxHeadEntry[] or deletes the key. A hand-written one is trusted exactly as
|
|
@@ -897,13 +969,10 @@ export function buildHeadDoc(
|
|
|
897
969
|
* adapts one to the other in the module that owns both, instead of in the Navigator orchestrator
|
|
898
970
|
* that owns neither.
|
|
899
971
|
*
|
|
900
|
-
* **`tab` is a parameter for the same reason every other frontmatter helper's is.**
|
|
901
|
-
*
|
|
902
|
-
*
|
|
903
|
-
*
|
|
904
|
-
* on a markdown page the card in one pane retitled the document in the other. The comment beside
|
|
905
|
-
* the card's JSON branch has claimed since the pane grid landed that this was fixed; the fix
|
|
906
|
-
* reached the JSON branch only, and this is the half that was left.
|
|
972
|
+
* **`tab` is a parameter for the same reason every other frontmatter helper's is.** Search
|
|
973
|
+
* appearance commits through it for the tab it was opened over, which need not be the focused
|
|
974
|
+
* pane's; resolving `activeTab.value` here once let an edit in one pane retitle the document in the
|
|
975
|
+
* other.
|
|
907
976
|
*
|
|
908
977
|
* @param {Tab | null} tab The document to commit into.
|
|
909
978
|
* @param {() => void} rerender
|
|
@@ -924,18 +993,54 @@ export function applyContentMutation(
|
|
|
924
993
|
if (isString(fmNow.title)) {
|
|
925
994
|
tmp.title = fmNow.title;
|
|
926
995
|
}
|
|
996
|
+
if (isString(fmNow.description)) {
|
|
997
|
+
tmp.description = fmNow.description;
|
|
998
|
+
}
|
|
927
999
|
if (fmHead) {
|
|
928
1000
|
tmp.$head = [...fmHead];
|
|
929
1001
|
}
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
mutateUpdateFrontmatter(tab, "title", tmp.title);
|
|
1002
|
+
if (isString(fmNow.$layout) || fmNow.$layout === false) {
|
|
1003
|
+
tmp.$layout = fmNow.$layout;
|
|
933
1004
|
}
|
|
1005
|
+
fn(tmp);
|
|
934
1006
|
const newHead = tmp.$head && tmp.$head.length > 0 ? tmp.$head : undefined;
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1007
|
+
const layoutChanged = tmp.$layout !== fmNow.$layout;
|
|
1008
|
+
const titleChanged = tmp.title !== fmNow.title;
|
|
1009
|
+
const descriptionChanged = isString(fmNow.description) && tmp.description !== fmNow.description;
|
|
1010
|
+
const headChanged = JSON.stringify(newHead) !== JSON.stringify(fmHead);
|
|
1011
|
+
/* The writes go through the transaction log, as every other frontmatter commit does. That is what
|
|
1012
|
+
records them for undo and swaps the document's root reference, which is the signal the canvas
|
|
1013
|
+
redraws on: a Layout pick on a Markdown page that only mutated the frontmatter in place was
|
|
1014
|
+
stored, and the page went on showing its old layout until something unrelated redrew it. A
|
|
1015
|
+
mutation that changed nothing records nothing, since an empty transaction is still an undo step. */
|
|
1016
|
+
if (layoutChanged || titleChanged || descriptionChanged || headChanged) {
|
|
1017
|
+
transactDoc(tab, (t) => {
|
|
1018
|
+
// The Layout picker writes `$layout`, and on a Markdown page that is a frontmatter key.
|
|
1019
|
+
if (layoutChanged) {
|
|
1020
|
+
mutateUpdateFrontmatter(
|
|
1021
|
+
t,
|
|
1022
|
+
"$layout",
|
|
1023
|
+
isString(tmp.$layout) || tmp.$layout === false ? tmp.$layout : undefined,
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
if (titleChanged) {
|
|
1027
|
+
mutateUpdateFrontmatter(t, "title", tmp.title);
|
|
1028
|
+
}
|
|
1029
|
+
if (descriptionChanged) {
|
|
1030
|
+
mutateUpdateFrontmatter(
|
|
1031
|
+
t,
|
|
1032
|
+
"description",
|
|
1033
|
+
isString(tmp.description) ? tmp.description : undefined,
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
if (headChanged) {
|
|
1037
|
+
/* SAFETY: every entry here either came from the frontmatter (JSON, from the format's split)
|
|
1038
|
+
or was built by a head editor out of string attributes; JxHeadEntry is wider than
|
|
1039
|
+
JsonValue only through its open index signature, which no head editor writes. */
|
|
1040
|
+
mutateUpdateFrontmatter(t, "$head", newHead as JsonValue | undefined);
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
939
1044
|
rerender();
|
|
940
1045
|
}
|
|
941
1046
|
|
|
@@ -947,15 +1052,22 @@ interface RowCommit {
|
|
|
947
1052
|
clear: () => void;
|
|
948
1053
|
/** Commit what the control settled on — a string from every kind but the checkbox. */
|
|
949
1054
|
commit: (raw: string | boolean) => void;
|
|
1055
|
+
/**
|
|
1056
|
+
* Whether this row's Browse lists only pictures (studio.md §9.3): the social image and the icon
|
|
1057
|
+
* always do, a frontmatter media field only when its schema says `format: "image"` — a video or a
|
|
1058
|
+
* PDF field browsing a picture-only list could never be filled from it.
|
|
1059
|
+
*/
|
|
1060
|
+
images: boolean;
|
|
950
1061
|
}
|
|
951
1062
|
|
|
952
|
-
/** What the panel is drawn against
|
|
1063
|
+
/** What the panel is drawn against: the focused document, reduced to what this reads. */
|
|
953
1064
|
export interface PagePanelContext {
|
|
954
1065
|
/** The head-bearing view of the open document — `buildHeadDoc` for a content page. */
|
|
955
1066
|
document: JxMutableNode;
|
|
956
1067
|
/** How a change to that view is written back into whichever realm it belongs to. */
|
|
957
1068
|
applyMutation: (fn: (doc: JxMutableNode) => void) => void;
|
|
958
|
-
|
|
1069
|
+
/** Repaint the host the panel is drawn in — the Inspector's Page tab. */
|
|
1070
|
+
rerender: () => void;
|
|
959
1071
|
}
|
|
960
1072
|
|
|
961
1073
|
/** Where the surface is mounted, and the context it was last drawn against. */
|
|
@@ -976,6 +1088,22 @@ let _addValue = "";
|
|
|
976
1088
|
/** This projection's rows, by key. Rebuilt with each projection. */
|
|
977
1089
|
const _commits = new Map<string, RowCommit>();
|
|
978
1090
|
|
|
1091
|
+
/** This projection's row buttons ({@link PageRow.action}), by row key. Rebuilt with each projection. */
|
|
1092
|
+
const _rowActions = new Map<string, () => void>();
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* The page's URL, derived from where its file sits under `pages/` (`pages/about.md` → `/about`), as
|
|
1096
|
+
* a readout: changing a URL is moving the file, which the file tree owns. `null` for anything that
|
|
1097
|
+
* is not a page.
|
|
1098
|
+
*/
|
|
1099
|
+
function urlRow(tab: Tab | null): PageRow | null {
|
|
1100
|
+
const route = tab?.documentPath ? pageRoute(tab.documentPath) : null;
|
|
1101
|
+
if (route === null) {
|
|
1102
|
+
return null;
|
|
1103
|
+
}
|
|
1104
|
+
return { ...blankRow("__url", "url", "URL"), kind: "readout", value: route };
|
|
1105
|
+
}
|
|
1106
|
+
|
|
979
1107
|
/**
|
|
980
1108
|
* This projection's custom `$head` entries, by the key they were drawn with.
|
|
981
1109
|
*
|
|
@@ -999,8 +1127,10 @@ const TAG_OPTIONS: SurfaceOption[] = [
|
|
|
999
1127
|
/** A blank row, so every kind carries every field the document's bindings read. */
|
|
1000
1128
|
function blankRow(key: string, prop: string, label: string): PageRow {
|
|
1001
1129
|
return {
|
|
1130
|
+
action: "",
|
|
1002
1131
|
checked: false,
|
|
1003
1132
|
clearLabel: `Clear ${prop}`,
|
|
1133
|
+
hasAction: false,
|
|
1004
1134
|
hasNote: false,
|
|
1005
1135
|
hasThumb: false,
|
|
1006
1136
|
isSet: false,
|
|
@@ -1026,6 +1156,7 @@ function thumbFor(row: PageRow, value: string): void {
|
|
|
1026
1156
|
function titleRow(doc: JxMutableNode, apply: PagePanelContext["applyMutation"]): PageRow {
|
|
1027
1157
|
const title = isString(doc.title) ? doc.title : "";
|
|
1028
1158
|
_commits.set("title", {
|
|
1159
|
+
images: false,
|
|
1029
1160
|
clear: () =>
|
|
1030
1161
|
apply((d) => {
|
|
1031
1162
|
delete d.title;
|
|
@@ -1058,6 +1189,7 @@ function metaRow(
|
|
|
1058
1189
|
const value = String(findMetaEntry(head, field.attr, field.key)?.attributes?.content ?? "");
|
|
1059
1190
|
const key = `meta:${field.attr}:${field.key}`;
|
|
1060
1191
|
_commits.set(key, {
|
|
1192
|
+
images: field.media === true,
|
|
1061
1193
|
clear: () => apply((d) => upsertMeta(d, field.attr, field.key, "")),
|
|
1062
1194
|
/* A media field is not trimmed: a path is committed exactly as it was chosen, and the browser
|
|
1063
1195
|
and the upload both hand one back already clean. */
|
|
@@ -1080,10 +1212,31 @@ function metaRow(
|
|
|
1080
1212
|
return row;
|
|
1081
1213
|
}
|
|
1082
1214
|
|
|
1215
|
+
/**
|
|
1216
|
+
* The page's one Description: the value the build writes as its description meta, read and written
|
|
1217
|
+
* where it lives ({@link pageDescription}, {@link setPageDescription}).
|
|
1218
|
+
*/
|
|
1219
|
+
function descriptionRow(doc: JxMutableNode, apply: PagePanelContext["applyMutation"]): PageRow {
|
|
1220
|
+
const value = pageDescription(doc);
|
|
1221
|
+
const key = "meta:name:description";
|
|
1222
|
+
_commits.set(key, {
|
|
1223
|
+
images: false,
|
|
1224
|
+
clear: () => apply((d) => setPageDescription(d, "")),
|
|
1225
|
+
commit: (raw) => apply((d) => setPageDescription(d, String(raw).trim())),
|
|
1226
|
+
});
|
|
1227
|
+
const row = blankRow(key, "description", "Description");
|
|
1228
|
+
row.isSet = Boolean(value);
|
|
1229
|
+
row.value = value;
|
|
1230
|
+
row.kind = "text";
|
|
1231
|
+
row.placeholder = "Description…";
|
|
1232
|
+
return row;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1083
1235
|
/** The favicon, which is a `<link rel="icon">` rather than a `<meta>`. */
|
|
1084
1236
|
function iconRow(head: JxHeadEntry[], apply: PagePanelContext["applyMutation"]): PageRow {
|
|
1085
1237
|
const value = String(findLinkEntry(head, "icon")?.attributes?.href ?? "");
|
|
1086
1238
|
_commits.set("link:icon", {
|
|
1239
|
+
images: true,
|
|
1087
1240
|
clear: () => apply((d) => upsertLink(d, "icon", "")),
|
|
1088
1241
|
commit: (raw) => apply((d) => upsertLink(d, "icon", String(raw))),
|
|
1089
1242
|
});
|
|
@@ -1099,8 +1252,7 @@ function iconRow(head: JxHeadEntry[], apply: PagePanelContext["applyMutation"]):
|
|
|
1099
1252
|
* The Layout picker, or `null` when this document takes none — a component has no layout, and a
|
|
1100
1253
|
* page whose layouts directory is still being listed has nothing to offer yet.
|
|
1101
1254
|
*
|
|
1102
|
-
* The listing is {@link layoutPickerEntries}',
|
|
1103
|
-
* layout invalidates one cache rather than two.
|
|
1255
|
+
* The listing is {@link layoutPickerEntries}', one cache that creating a layout invalidates.
|
|
1104
1256
|
*/
|
|
1105
1257
|
function layoutSection(
|
|
1106
1258
|
tab: Tab | null,
|
|
@@ -1118,6 +1270,7 @@ function layoutSection(
|
|
|
1118
1270
|
const defaultPath = projectState?.projectConfig?.defaults?.layout;
|
|
1119
1271
|
const defaultLabel = defaultPath ? layoutDisplayName(defaultPath) : "";
|
|
1120
1272
|
_commits.set("__layout", {
|
|
1273
|
+
images: false,
|
|
1121
1274
|
clear: () =>
|
|
1122
1275
|
apply((d) => {
|
|
1123
1276
|
delete d.$layout;
|
|
@@ -1145,6 +1298,17 @@ function layoutSection(
|
|
|
1145
1298
|
...entries.map((l) => ({ label: l.name, value: l.path })),
|
|
1146
1299
|
];
|
|
1147
1300
|
row.value = current === false ? "__none__" : current || "__default__";
|
|
1301
|
+
// The template the page is drawn in, opened in Design: its own `$layout`, else the default.
|
|
1302
|
+
const effective = getEffectiveLayoutPath(current);
|
|
1303
|
+
if (effective) {
|
|
1304
|
+
row.action = "Edit template";
|
|
1305
|
+
row.hasAction = true;
|
|
1306
|
+
_rowActions.set("__layout", () => {
|
|
1307
|
+
void import("../files/files").then(({ openFileInTab }) =>
|
|
1308
|
+
openFileInTab(effective.replace(/^\.\//, "")),
|
|
1309
|
+
);
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1148
1312
|
return { hasSeo: false, key: "layout", rows: [row], title: "Layout" };
|
|
1149
1313
|
}
|
|
1150
1314
|
|
|
@@ -1161,7 +1325,7 @@ function frontmatterSection(tab: Tab | null): PageSection | null {
|
|
|
1161
1325
|
const { collection, fields, hasSchema, requiredFields } = collectFmFields(
|
|
1162
1326
|
tab,
|
|
1163
1327
|
projectState?.projectConfig,
|
|
1164
|
-
|
|
1328
|
+
reservedFmKeys(tab),
|
|
1165
1329
|
);
|
|
1166
1330
|
if (fields.length === 0 && !hasSchema) {
|
|
1167
1331
|
return null;
|
|
@@ -1172,6 +1336,7 @@ function frontmatterSection(tab: Tab | null): PageSection | null {
|
|
|
1172
1336
|
rerender: sync,
|
|
1173
1337
|
});
|
|
1174
1338
|
_commits.set(key, {
|
|
1339
|
+
images: row.kind === "media" && f.entry.format === "image",
|
|
1175
1340
|
clear: () => transactDoc(tab, (t) => mutateUpdateFrontmatter(t, f.field)),
|
|
1176
1341
|
commit: (raw) => transactDoc(tab, (t) => mutateUpdateFrontmatter(t, f.field, parse(raw))),
|
|
1177
1342
|
});
|
|
@@ -1207,12 +1372,13 @@ function customEntries(head: JxHeadEntry[]): PageTagRow[] {
|
|
|
1207
1372
|
/**
|
|
1208
1373
|
* Everything the panel says about one document, and the commit table behind it.
|
|
1209
1374
|
*
|
|
1210
|
-
* `activeTab.value` is read HERE and nowhere below it: the
|
|
1375
|
+
* `activeTab.value` is read HERE and nowhere below it: the Inspector's Page tab is an app-level
|
|
1211
1376
|
* surface that shows the focused document by definition, and spelling that out once at the top is
|
|
1212
1377
|
* what lets every helper under it take a tab instead of asking for one.
|
|
1213
1378
|
*/
|
|
1214
1379
|
function view(ctx: PagePanelContext): PagePanelView {
|
|
1215
1380
|
_commits.clear();
|
|
1381
|
+
_rowActions.clear();
|
|
1216
1382
|
const tab = activeTab.value;
|
|
1217
1383
|
const doc = ctx.document;
|
|
1218
1384
|
const head = doc.$head ?? [];
|
|
@@ -1226,13 +1392,19 @@ function view(ctx: PagePanelContext): PagePanelView {
|
|
|
1226
1392
|
if (layout) {
|
|
1227
1393
|
sections.push(layout);
|
|
1228
1394
|
}
|
|
1395
|
+
const url = urlRow(tab);
|
|
1229
1396
|
sections.push(
|
|
1230
1397
|
{
|
|
1231
1398
|
hasSeo: true,
|
|
1232
1399
|
key: "page",
|
|
1233
1400
|
rows: [
|
|
1234
1401
|
titleRow(doc, ctx.applyMutation),
|
|
1235
|
-
...
|
|
1402
|
+
...(url ? [url] : []),
|
|
1403
|
+
...PAGE_FIELDS.map((field) =>
|
|
1404
|
+
field.key === "description"
|
|
1405
|
+
? descriptionRow(doc, ctx.applyMutation)
|
|
1406
|
+
: metaRow(field, head, ctx.applyMutation),
|
|
1407
|
+
),
|
|
1236
1408
|
iconRow(head, ctx.applyMutation),
|
|
1237
1409
|
],
|
|
1238
1410
|
title: "Page",
|
|
@@ -1324,7 +1496,7 @@ function addEntry(): void {
|
|
|
1324
1496
|
}
|
|
1325
1497
|
d.$head.push(entry);
|
|
1326
1498
|
});
|
|
1327
|
-
ctx.
|
|
1499
|
+
ctx.rerender();
|
|
1328
1500
|
}
|
|
1329
1501
|
|
|
1330
1502
|
/** Take one custom `$head` entry away, by the entry the key was drawn for. */
|
|
@@ -1343,7 +1515,7 @@ function removeEntry(key: string): void {
|
|
|
1343
1515
|
d.$head.splice(idx, 1);
|
|
1344
1516
|
}
|
|
1345
1517
|
});
|
|
1346
|
-
ctx.
|
|
1518
|
+
ctx.rerender();
|
|
1347
1519
|
}
|
|
1348
1520
|
|
|
1349
1521
|
/**
|
|
@@ -1376,9 +1548,13 @@ const ACTIONS: PagePanelActions = {
|
|
|
1376
1548
|
flushPending(key);
|
|
1377
1549
|
void mediaPicker().then((m) => {
|
|
1378
1550
|
if (anchor instanceof HTMLElement) {
|
|
1379
|
-
m.showMediaPickerPopover(
|
|
1380
|
-
|
|
1381
|
-
|
|
1551
|
+
m.showMediaPickerPopover(
|
|
1552
|
+
anchor,
|
|
1553
|
+
(val: string) => {
|
|
1554
|
+
_commits.get(key)?.commit(val);
|
|
1555
|
+
},
|
|
1556
|
+
{ images: _commits.get(key)?.images ?? true },
|
|
1557
|
+
);
|
|
1382
1558
|
}
|
|
1383
1559
|
});
|
|
1384
1560
|
},
|
|
@@ -1411,11 +1587,14 @@ const ACTIONS: PagePanelActions = {
|
|
|
1411
1587
|
sync();
|
|
1412
1588
|
},
|
|
1413
1589
|
openSeo: () => {
|
|
1414
|
-
/* The COMMAND, not a local open()
|
|
1415
|
-
|
|
1590
|
+
/* The COMMAND, not a local open(): no surface owns the capability, so the palette has it by
|
|
1591
|
+
name. */
|
|
1416
1592
|
void activeRegistry()?.run("document.openSeo");
|
|
1417
1593
|
},
|
|
1418
1594
|
removeEntry,
|
|
1595
|
+
runRowAction: (key) => {
|
|
1596
|
+
_rowActions.get(key)?.();
|
|
1597
|
+
},
|
|
1419
1598
|
setBoolean: (key, checked) => commitNow(key, checked),
|
|
1420
1599
|
setChoice: (key, value) => commitNow(key, value),
|
|
1421
1600
|
setNumber: (key, value) => commitNow(key, value),
|
|
@@ -1458,50 +1637,137 @@ export function renderPagePanel(host: HTMLElement, ctx: PagePanelContext): void
|
|
|
1458
1637
|
sync();
|
|
1459
1638
|
}
|
|
1460
1639
|
|
|
1640
|
+
// ─── The Inspector's Page tab ────────────────────────────────────────────
|
|
1641
|
+
|
|
1642
|
+
/**
|
|
1643
|
+
* What the Page tab is called for a document (§6): Page under `pages/`, Template under `layouts/`,
|
|
1644
|
+
* Component under `components/`, Entry for a file a collection owns, Document for anything else.
|
|
1645
|
+
*
|
|
1646
|
+
* Path-based, like the rest of the site model: a file's role in a project is where it lives, and
|
|
1647
|
+
* `collectionForFile` is the one matcher that answers "is this an entry" (§22.1 uses it too).
|
|
1648
|
+
*/
|
|
1649
|
+
export function pageTabTitle(path: string | null | undefined): string {
|
|
1650
|
+
if (!path) {
|
|
1651
|
+
return "Document";
|
|
1652
|
+
}
|
|
1653
|
+
if (path.startsWith("pages/")) {
|
|
1654
|
+
return "Page";
|
|
1655
|
+
}
|
|
1656
|
+
if (path.startsWith("layouts/")) {
|
|
1657
|
+
return "Template";
|
|
1658
|
+
}
|
|
1659
|
+
if (path.startsWith("components/")) {
|
|
1660
|
+
return "Component";
|
|
1661
|
+
}
|
|
1662
|
+
const collection = collectionForFile(path);
|
|
1663
|
+
return collection && !collection.fileBacked ? "Entry" : "Document";
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
/** What the Component tab says: why there is no page form, and the one verb that reaches its own. */
|
|
1667
|
+
export const COMPONENT_TAB_MESSAGE =
|
|
1668
|
+
"A component has no page settings: its title, search and social details come from the page that uses it.";
|
|
1669
|
+
|
|
1670
|
+
/** The Component tab's empty state, for a component document's tab. */
|
|
1671
|
+
export function componentTabSpec(tab: Tab): EmptyStateSpec {
|
|
1672
|
+
return {
|
|
1673
|
+
actions: [
|
|
1674
|
+
{
|
|
1675
|
+
label: "Edit tag and props",
|
|
1676
|
+
run: () => {
|
|
1677
|
+
// The root is the component's definition; selecting it opens Element › Content on its
|
|
1678
|
+
// Tag name and "Component Defaults" (`panels/properties-panel.ts`).
|
|
1679
|
+
updateSession(tab, { selection: [[]], ui: { rightTab: "properties" } });
|
|
1680
|
+
},
|
|
1681
|
+
},
|
|
1682
|
+
],
|
|
1683
|
+
detail: "Its tag name and the props it takes are set on its root element.",
|
|
1684
|
+
message: COMPONENT_TAB_MESSAGE,
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
/** The sentence the Page tab shows with no document open. */
|
|
1689
|
+
export const PAGE_TAB_EMPTY = "Open a page to edit its title, description and social preview.";
|
|
1690
|
+
|
|
1691
|
+
/** The Inspector's Page body host, handed over once by the dock. */
|
|
1692
|
+
let _tabHost: HTMLElement | null = null;
|
|
1693
|
+
|
|
1461
1694
|
/**
|
|
1462
|
-
*
|
|
1695
|
+
* Take the Inspector's Page body host, or let it go (`null`, when the dock is taken down).
|
|
1463
1696
|
*
|
|
1464
|
-
*
|
|
1465
|
-
*
|
|
1466
|
-
*
|
|
1467
|
-
* fallback path has no content area; mounting into the body itself is then still correct.
|
|
1697
|
+
* The Page panel moved here from the Navigator (§6): the same surface, re-hosted unchanged, drawn
|
|
1698
|
+
* against the focused document. `panels/right-panel.ts` hands the host over and calls
|
|
1699
|
+
* {@link renderPageTab} whenever the dock repaints.
|
|
1468
1700
|
*/
|
|
1469
|
-
function
|
|
1470
|
-
|
|
1701
|
+
export function bindPageHost(host: HTMLElement | null): void {
|
|
1702
|
+
if (_tabHost && _tabHost !== host) {
|
|
1703
|
+
unmountEntryInspector(_tabHost);
|
|
1704
|
+
unmountPagePanelSurface(_tabHost);
|
|
1705
|
+
if (_host === _tabHost) {
|
|
1706
|
+
_host = null;
|
|
1707
|
+
_ctx = null;
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
_tabHost = host;
|
|
1711
|
+
renderPageTab();
|
|
1471
1712
|
}
|
|
1472
1713
|
|
|
1473
1714
|
/**
|
|
1474
|
-
*
|
|
1715
|
+
* Draw the Page tab against the focused document, or empty it when nothing is open.
|
|
1475
1716
|
*
|
|
1476
|
-
*
|
|
1477
|
-
*
|
|
1478
|
-
* the surface Page. The record is what keeps the surface addressable under one name.
|
|
1717
|
+
* Idempotent and cheap: a standing surface is only assigned to (`renderPagePanelSurface`), so the
|
|
1718
|
+
* dock can call this on every repaint without taking the caret out of a field.
|
|
1479
1719
|
*/
|
|
1480
|
-
export function
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1720
|
+
export function renderPageTab(): void {
|
|
1721
|
+
const host = _tabHost;
|
|
1722
|
+
if (!host) {
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
const tab = activeTab.value;
|
|
1726
|
+
if (!tab || isEntryTab(tab)) {
|
|
1727
|
+
unmountPagePanelSurface(host);
|
|
1728
|
+
if (_host === host) {
|
|
1729
|
+
_host = null;
|
|
1730
|
+
_ctx = null;
|
|
1731
|
+
}
|
|
1732
|
+
/* A collection entry's tab is titled Entry and draws the entry's own settings — status,
|
|
1733
|
+
fields, page URL, file (§22.5) — rather than a page's title, head and SEO preview: the
|
|
1734
|
+
entry's page is its collection's template, and its head is that page's. */
|
|
1735
|
+
if (tab) {
|
|
1736
|
+
renderEntryInspector(host, tab);
|
|
1737
|
+
} else {
|
|
1738
|
+
unmountEntryInspector(host);
|
|
1739
|
+
}
|
|
1740
|
+
return;
|
|
1741
|
+
}
|
|
1742
|
+
unmountEntryInspector(host);
|
|
1743
|
+
/* A component is not a page: it has no title, no head and no social card of its own — the pages
|
|
1744
|
+
that use it do. Its own settings (its tag, the props it takes and their defaults) are its ROOT
|
|
1745
|
+
element's, edited in Element › Content, so the tab says that and goes there. */
|
|
1746
|
+
if (pageTabTitle(tab.documentPath) === "Component") {
|
|
1747
|
+
unmountPagePanelSurface(host);
|
|
1748
|
+
if (_host === host) {
|
|
1749
|
+
_host = null;
|
|
1750
|
+
_ctx = null;
|
|
1751
|
+
}
|
|
1752
|
+
// Assigned on every repaint; placed only when it is not already the tab's one child.
|
|
1753
|
+
const state = emptyState(host, componentTabSpec(tab));
|
|
1754
|
+
if (host.firstChild !== state || host.childNodes.length !== 1) {
|
|
1755
|
+
host.replaceChildren(state);
|
|
1756
|
+
}
|
|
1757
|
+
return;
|
|
1758
|
+
}
|
|
1759
|
+
const isContent = tab.doc.mode === "content";
|
|
1760
|
+
renderPagePanel(host, {
|
|
1761
|
+
/* The Page tab IS an app-level surface: it shows the focused document by definition, so it
|
|
1762
|
+
spells that out at the call site instead of leaving it to a helper two other surfaces share. */
|
|
1763
|
+
applyMutation: isContent
|
|
1764
|
+
? (fn) => applyContentMutation(activeTab.value, renderPageTab, fn)
|
|
1765
|
+
: (fn) => {
|
|
1766
|
+
transact(activeTab.value, fn);
|
|
1767
|
+
},
|
|
1768
|
+
document: isContent
|
|
1769
|
+
? buildHeadDoc(tab.doc.document, tab.doc.content.frontmatter)
|
|
1770
|
+
: tab.doc.document,
|
|
1771
|
+
rerender: renderPageTab,
|
|
1506
1772
|
});
|
|
1507
1773
|
}
|