@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
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What makes a component's tag name usable, decided once.
|
|
3
|
+
*
|
|
4
|
+
* Two rules used to answer this and they disagreed: Convert to Component accepted
|
|
5
|
+
* `/^[a-z][a-z0-9]*(-[a-z0-9]+)+$/`, which refused `x-card.v2` and `my_el-x`, and the Library's
|
|
6
|
+
* component seed accepted `/^[a-z][\d.a-z]*-[\d.a-z-]*$/`, which allowed a trailing hyphen the
|
|
7
|
+
* first refused. Neither was the rule the browser applies, which is the only one that matters: a
|
|
8
|
+
* definition whose tag `customElements.define` rejects is a component nothing can register. So this
|
|
9
|
+
* module states HTML's own production — the [valid custom element
|
|
10
|
+
* name](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name) —
|
|
11
|
+
* and every creation flow asks it: Add new (`create/add-new.ts`) and Convert to Component
|
|
12
|
+
* (`editor/convert-to-component.ts`).
|
|
13
|
+
*
|
|
14
|
+
* The DERIVER is the other half. A name the reader typed as a title ("Pricing table") becomes a tag
|
|
15
|
+
* (`pricing-table`), and a name with no hyphen gains a `site-` prefix rather than being refused,
|
|
16
|
+
* because "Hero" is a perfectly good thing to call a component and a refusal there teaches nothing
|
|
17
|
+
* but the syntax of a rule the app can satisfy itself (`specs/studio.md` §9.1.1).
|
|
18
|
+
*
|
|
19
|
+
* DOM-free, so the command records and the pure derivation can import it in a bare Bun process.
|
|
20
|
+
*
|
|
21
|
+
* @docs studio/design/components
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { slugify } from "../files/new-file-name";
|
|
25
|
+
|
|
26
|
+
/** The prefix a hyphenless slug gains, so the derived tag is a valid custom element name. */
|
|
27
|
+
export const TAG_PREFIX = "site-";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Names HTML reserves: they match the production but are SVG and MathML elements that predate it,
|
|
31
|
+
* so `customElements.define` refuses each one.
|
|
32
|
+
*/
|
|
33
|
+
export const RESERVED_ELEMENT_NAMES: ReadonlySet<string> = new Set([
|
|
34
|
+
"annotation-xml",
|
|
35
|
+
"color-profile",
|
|
36
|
+
"font-face",
|
|
37
|
+
"font-face-format",
|
|
38
|
+
"font-face-name",
|
|
39
|
+
"font-face-src",
|
|
40
|
+
"font-face-uri",
|
|
41
|
+
"missing-glyph",
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* HTML's `PCENChar`: the characters a potential custom element name may hold after its first
|
|
46
|
+
* letter. ASCII upper case is deliberately absent, which is why a tag name is lower case.
|
|
47
|
+
*/
|
|
48
|
+
const PCEN_RANGES: readonly (readonly [number, number])[] = [
|
|
49
|
+
[0xb7, 0xb7],
|
|
50
|
+
[0xc0, 0xd6],
|
|
51
|
+
[0xd8, 0xf6],
|
|
52
|
+
[0xf8, 0x3_7d],
|
|
53
|
+
[0x3_7f, 0x1f_ff],
|
|
54
|
+
[0x20_0c, 0x20_0d],
|
|
55
|
+
[0x20_3f, 0x20_40],
|
|
56
|
+
[0x20_70, 0x21_8f],
|
|
57
|
+
[0x2c_00, 0x2f_ef],
|
|
58
|
+
[0x30_01, 0xd7_ff],
|
|
59
|
+
[0xf9_00, 0xfd_cf],
|
|
60
|
+
[0xfd_f0, 0xff_fd],
|
|
61
|
+
[0x1_00_00, 0xe_ff_ff],
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
/** A code point as a `u`-flag regular expression escape, so no invisible character is in source. */
|
|
65
|
+
function escapeCodePoint(cp: number): string {
|
|
66
|
+
return `${String.fromCodePoint(92)}u{${cp.toString(16)}}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The whole class body: ASCII first, then each range by its escaped ends. */
|
|
70
|
+
const PCEN_CHAR = [
|
|
71
|
+
String.raw`\-.0-9_a-z`,
|
|
72
|
+
...PCEN_RANGES.map(([from, to]) => `${escapeCodePoint(from)}-${escapeCodePoint(to)}`),
|
|
73
|
+
].join("");
|
|
74
|
+
|
|
75
|
+
/** One character that may appear after the first letter. */
|
|
76
|
+
const ONE_PCEN_CHAR = new RegExp(`^[${PCEN_CHAR}]$`, "u");
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The sentence a tag name earns, or `""` when it is valid.
|
|
80
|
+
*
|
|
81
|
+
* Each sentence names the one thing to change, in the order a reader fixes them: say something,
|
|
82
|
+
* start with a letter, lose the character the browser refuses, add the hyphen, avoid a reserved
|
|
83
|
+
* name.
|
|
84
|
+
*/
|
|
85
|
+
export function customElementNameProblem(name: string): string {
|
|
86
|
+
if (name === "") {
|
|
87
|
+
return "Enter a tag name.";
|
|
88
|
+
}
|
|
89
|
+
if (!/^[a-z]/.test(name)) {
|
|
90
|
+
return "A tag name starts with a lower-case letter.";
|
|
91
|
+
}
|
|
92
|
+
for (const char of name.slice(1)) {
|
|
93
|
+
if (!ONE_PCEN_CHAR.test(char)) {
|
|
94
|
+
return /[A-Z]/.test(char)
|
|
95
|
+
? "A tag name is lower case."
|
|
96
|
+
: `“${char}” cannot appear in a tag name.`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (!name.includes("-")) {
|
|
100
|
+
return "A tag name needs a hyphen, such as my-card.";
|
|
101
|
+
}
|
|
102
|
+
if (RESERVED_ELEMENT_NAMES.has(name)) {
|
|
103
|
+
return `<${name}> is reserved by HTML.`;
|
|
104
|
+
}
|
|
105
|
+
return "";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The tag a display name derives to: slugified, its hyphens tidied, and prefixed with
|
|
110
|
+
* {@link TAG_PREFIX} when it would otherwise have no hyphen or would start with a digit.
|
|
111
|
+
*
|
|
112
|
+
* `"Pricing table"` → `pricing-table`, `"Hero"` → `site-hero`, `"2 columns"` → `site-2-columns`,
|
|
113
|
+
* and a name with nothing usable in it → `""`, which {@link customElementNameProblem} refuses.
|
|
114
|
+
*/
|
|
115
|
+
export function deriveTagName(input: string): string {
|
|
116
|
+
const slug = slugify(input).replaceAll(/-+/g, "-").replaceAll(/^-|-$/g, "");
|
|
117
|
+
if (slug === "") {
|
|
118
|
+
return "";
|
|
119
|
+
}
|
|
120
|
+
return /^[a-z]/.test(slug) && slug.includes("-") ? slug : `${TAG_PREFIX}${slug}`;
|
|
121
|
+
}
|
|
@@ -12,8 +12,11 @@
|
|
|
12
12
|
* was fixed.
|
|
13
13
|
*
|
|
14
14
|
* What is left is the flow, which is all this module ever owned that mattered: what the default
|
|
15
|
-
* name is,
|
|
16
|
-
*
|
|
15
|
+
* name is, whether the document actually took the reference, and what is written to disk once it
|
|
16
|
+
* has. What makes a name usable is no longer this module's: it is HTML's valid custom element name,
|
|
17
|
+
* stated once in `create/element-name.ts` and asked by Add new's Component kind too, which runs
|
|
18
|
+
* {@link convertSelectionToComponent} for **Start from: Current selection** (`specs/studio.md`
|
|
19
|
+
* §9.1.1).
|
|
17
20
|
*
|
|
18
21
|
* @docs studio/design/components
|
|
19
22
|
*/
|
|
@@ -31,33 +34,62 @@ import { serializeJson } from "@jxsuite/schema/json-layout";
|
|
|
31
34
|
import { notify } from "../services/notify";
|
|
32
35
|
import { showPromptDialog } from "../ui/layers";
|
|
33
36
|
import { validateComponentSlots } from "../services/cem-export";
|
|
37
|
+
import { customElementNameProblem, deriveTagName } from "../create/element-name";
|
|
34
38
|
|
|
35
|
-
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
39
|
+
import type { JxMutableNode, JxPath } from "@jxsuite/schema/types";
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
/**
|
|
42
|
+
* The selected element a conversion would lift out, or `null` when there is none to convert.
|
|
43
|
+
*
|
|
44
|
+
* The root is not convertible (it is the document, not an element in it), and neither is a
|
|
45
|
+
* selection the document cannot resolve to a node with a tag. Exported so Add new can say WHY its
|
|
46
|
+
* Current selection choice is disabled rather than offering a button that does nothing.
|
|
47
|
+
*/
|
|
48
|
+
export function convertibleSelection(): { path: JxPath; node: JxMutableNode } | null {
|
|
41
49
|
const tab = activeTab.value;
|
|
42
50
|
const selected = primarySelection(tab?.session.selection);
|
|
43
51
|
if (!tab || !selected || selected.length < 2) {
|
|
44
|
-
return;
|
|
52
|
+
return null;
|
|
45
53
|
}
|
|
46
|
-
|
|
47
54
|
const node = getNodeAtPath(tab.doc.document, selected);
|
|
48
55
|
if (!node || !node.tagName) {
|
|
49
|
-
return;
|
|
56
|
+
return null;
|
|
50
57
|
}
|
|
58
|
+
return { node, path: selected };
|
|
59
|
+
}
|
|
51
60
|
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
/** Convert the currently selected element into a reusable component. */
|
|
62
|
+
export async function convertToComponent() {
|
|
63
|
+
const subject = convertibleSelection();
|
|
64
|
+
if (!subject) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const chosen = await promptComponentName(deriveDefaultName(subject.node));
|
|
54
68
|
if (!chosen) {
|
|
55
69
|
return;
|
|
56
70
|
}
|
|
57
71
|
/* The prompt resolves the TRIMMED value; the case fold is this module's, because it is the case
|
|
58
72
|
fold `validateName` already judged the name in. A tag name is lower-case by the same rule that
|
|
59
73
|
made `Hero-Block` default to `hero-block`. */
|
|
60
|
-
|
|
74
|
+
await convertSelectionToComponent(chosen.toLowerCase());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Replace the selected element with an instance of a new component named `name`, and write the
|
|
79
|
+
* component's definition to `components/<name>.json`.
|
|
80
|
+
*
|
|
81
|
+
* The caller has validated `name` (`create/element-name.ts`). The reference goes into the document
|
|
82
|
+
* FIRST, in one transaction, and the file is written only if that transaction landed — see below.
|
|
83
|
+
*
|
|
84
|
+
* @returns The component file's path, or `null` when nothing was converted or the write failed.
|
|
85
|
+
*/
|
|
86
|
+
export async function convertSelectionToComponent(name: string): Promise<string | null> {
|
|
87
|
+
const tab = activeTab.value;
|
|
88
|
+
const subject = convertibleSelection();
|
|
89
|
+
if (!tab || !subject) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const { node, path: selected } = subject;
|
|
61
93
|
|
|
62
94
|
// Extract component definition
|
|
63
95
|
const componentDef = extractComponentDef(node);
|
|
@@ -107,7 +139,7 @@ export async function convertToComponent() {
|
|
|
107
139
|
document the author was told had been changed. The gate raises its own toast saying why the
|
|
108
140
|
edit is paused; the honest thing to add to it is nothing at all. */
|
|
109
141
|
if (!converted) {
|
|
110
|
-
return;
|
|
142
|
+
return null;
|
|
111
143
|
}
|
|
112
144
|
|
|
113
145
|
// Write component file and refresh registry
|
|
@@ -130,27 +162,31 @@ export async function convertToComponent() {
|
|
|
130
162
|
tier: "problem",
|
|
131
163
|
});
|
|
132
164
|
}
|
|
165
|
+
return componentFile;
|
|
133
166
|
} catch (error) {
|
|
134
167
|
notify.error(`Could not save the <${name}> component.`, {
|
|
135
168
|
detail: errorMessage(error),
|
|
136
169
|
path: componentFile,
|
|
137
170
|
source: "Components",
|
|
138
171
|
});
|
|
172
|
+
return null;
|
|
139
173
|
}
|
|
140
174
|
}
|
|
141
175
|
|
|
142
176
|
/**
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
* @returns {string}
|
|
177
|
+
* The default tag name for a node: its `$id` when it has one, else its tag, through the shared
|
|
178
|
+
* deriver — so `$id: "Hero"` proposes `site-hero` and a `<section>` proposes `site-section`, the
|
|
179
|
+
* same answer Add new gives a component typed as "Hero".
|
|
147
180
|
*/
|
|
148
181
|
function deriveDefaultName(node: JxMutableNode) {
|
|
149
|
-
if (node.$id
|
|
150
|
-
|
|
182
|
+
if (node.$id) {
|
|
183
|
+
const fromId = deriveTagName(node.$id);
|
|
184
|
+
if (fromId !== "") {
|
|
185
|
+
return fromId;
|
|
186
|
+
}
|
|
151
187
|
}
|
|
152
188
|
const tag = (displayTagName(node.tagName) || "div").toLowerCase();
|
|
153
|
-
return tag.includes("-") ? tag :
|
|
189
|
+
return tag.includes("-") ? tag : deriveTagName(tag);
|
|
154
190
|
}
|
|
155
191
|
|
|
156
192
|
/**
|
|
@@ -170,30 +206,18 @@ function extractComponentDef(node: JxMutableNode) {
|
|
|
170
206
|
}
|
|
171
207
|
|
|
172
208
|
/**
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
* @param {string} val
|
|
176
|
-
* @returns {{ valid: boolean; error: string }}
|
|
209
|
+
* The refusal a component name earns, or `""`: HTML's valid custom element name, then uniqueness
|
|
210
|
+
* against the registry.
|
|
177
211
|
*/
|
|
178
|
-
function validateName(val: string) {
|
|
212
|
+
function validateName(val: string): string {
|
|
179
213
|
const name = val.trim().toLowerCase();
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
valid: false,
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
if (!VALID_NAME.test(name)) {
|
|
187
|
-
return {
|
|
188
|
-
error: "Lowercase letters, digits, and hyphens only",
|
|
189
|
-
valid: false,
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
const exists = componentRegistry.some((c) => c.tagName === name);
|
|
193
|
-
if (exists) {
|
|
194
|
-
return { error: `Component <${name}> already exists`, valid: false };
|
|
214
|
+
const problem = customElementNameProblem(name);
|
|
215
|
+
if (problem !== "") {
|
|
216
|
+
return problem;
|
|
195
217
|
}
|
|
196
|
-
return
|
|
218
|
+
return componentRegistry.some((c) => c.tagName === name)
|
|
219
|
+
? `Component <${name}> already exists`
|
|
220
|
+
: "";
|
|
197
221
|
}
|
|
198
222
|
|
|
199
223
|
/**
|
|
@@ -212,7 +236,7 @@ function promptComponentName(defaultName: string): Promise<string | null> {
|
|
|
212
236
|
confirmLabel: "Convert",
|
|
213
237
|
message: "Enter a hyphenated tag name for the new component.",
|
|
214
238
|
placeholder: "my-component",
|
|
215
|
-
validate:
|
|
239
|
+
validate: validateName,
|
|
216
240
|
value: defaultName,
|
|
217
241
|
});
|
|
218
242
|
}
|
|
@@ -83,11 +83,19 @@ export function resolveFileDropTarget(
|
|
|
83
83
|
* The node an uploaded asset becomes when inserted. Images get a plain `<img>` (the build's
|
|
84
84
|
* optimizer picks it up); playable media gets native controls so it is usable straight away;
|
|
85
85
|
* anything else becomes a download link, which is the only sensible generic rendering.
|
|
86
|
+
*
|
|
87
|
+
* **An image arrives with no `alt` at all, never an empty one.** `alt=""` is a claim (the image is
|
|
88
|
+
* decorative, and a screen reader skips it), and a drop cannot know it: stamping it made every
|
|
89
|
+
* dropped picture invisible to assistive technology and silenced the accessibility report's
|
|
90
|
+
* `img-alt-missing`, the one check that would have asked. Leaving it out is what gets the image
|
|
91
|
+
* flagged, and the drop selects the new element (`applyDropInstruction`), so the Inspector is
|
|
92
|
+
* already on the field the author has to fill. Neither is the file name a description: `IMG_0412`
|
|
93
|
+
* read aloud is noise, not an alternative.
|
|
86
94
|
*/
|
|
87
95
|
export function elementForAsset(asset: UploadedAsset): JxMutableNode {
|
|
88
96
|
switch (asset.kind) {
|
|
89
97
|
case "image": {
|
|
90
|
-
return { attributes: {
|
|
98
|
+
return { attributes: { src: asset.ref }, tagName: "img" };
|
|
91
99
|
}
|
|
92
100
|
case "video": {
|
|
93
101
|
return { attributes: { controls: "", src: asset.ref }, tagName: "video" };
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
transactDoc,
|
|
21
21
|
} from "../tabs/transact";
|
|
22
22
|
import { defaultDef } from "../panels/shared";
|
|
23
|
+
import { componentInstance, mutateImportComponent } from "./insert-component";
|
|
23
24
|
|
|
24
25
|
import { contentLength, contentOf, spliceAcross, toStored } from "./content-slice";
|
|
25
26
|
import type { JxContentResult, SlashCommand } from "./inline-edit";
|
|
@@ -276,6 +277,45 @@ export function applyInlineSplit(
|
|
|
276
277
|
return newPath;
|
|
277
278
|
}
|
|
278
279
|
|
|
280
|
+
/**
|
|
281
|
+
* A slash pick from the Components group (§5.7): the empty block the "/" was typed in is REPLACED
|
|
282
|
+
* by an instance of the component, and a block with content keeps it and gets the instance after
|
|
283
|
+
* it. Either way the component is imported, as a drop of the same component would import it. An
|
|
284
|
+
* instance is a whole element with its own `$props` and slot content, so swapping a tag in place —
|
|
285
|
+
* what an HTML pick does — would leave a paragraph's leftovers inside a component.
|
|
286
|
+
*/
|
|
287
|
+
function insertComponentBlock(
|
|
288
|
+
tab: Tab | null,
|
|
289
|
+
path: JxPath,
|
|
290
|
+
instance: JxMutableNode,
|
|
291
|
+
commitData: JxContentResult | undefined,
|
|
292
|
+
): JxPath {
|
|
293
|
+
const { parentPath, index: idx } = blockSlot(path);
|
|
294
|
+
const replace = isEmptyContent(commitData);
|
|
295
|
+
const at = replace ? idx : idx + 1;
|
|
296
|
+
const newPath = [...parentPath, "children", at];
|
|
297
|
+
if (!tab) {
|
|
298
|
+
return newPath;
|
|
299
|
+
}
|
|
300
|
+
transactDoc(tab, (t) => {
|
|
301
|
+
if (replace) {
|
|
302
|
+
mutateRemoveNode(t, path);
|
|
303
|
+
} else if (commitData?.children) {
|
|
304
|
+
mutateUpdateProperty(t, path, "textContent");
|
|
305
|
+
mutateUpdateProperty(t, path, "children", commitData.children);
|
|
306
|
+
} else if (commitData?.textContent != null) {
|
|
307
|
+
mutateUpdateProperty(t, path, "children");
|
|
308
|
+
mutateUpdateProperty(t, path, "textContent", commitData.textContent);
|
|
309
|
+
}
|
|
310
|
+
mutateInsertNode(t, parentPath, at, instance);
|
|
311
|
+
mutateImportComponent(t, instance.tagName);
|
|
312
|
+
if (isTabActive(tab)) {
|
|
313
|
+
t.session.selection = [newPath];
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
return newPath;
|
|
317
|
+
}
|
|
318
|
+
|
|
279
319
|
/**
|
|
280
320
|
* Apply a slash-insert at `path` of `tab`'s document: swap the (empty) node's tag in place, or
|
|
281
321
|
* commit pending content and insert a new element after it. Returns the path to edit next (the
|
|
@@ -287,6 +327,10 @@ export function applyInlineInsert(
|
|
|
287
327
|
cmd: SlashCommand,
|
|
288
328
|
commitData: JxContentResult | undefined,
|
|
289
329
|
): JxPath {
|
|
330
|
+
const instance = cmd.component === true ? componentInstance(cmd.tag) : null;
|
|
331
|
+
if (instance) {
|
|
332
|
+
return insertComponentBlock(tab, path, instance, commitData);
|
|
333
|
+
}
|
|
290
334
|
if (isEmptyContent(commitData)) {
|
|
291
335
|
if (!tab) {
|
|
292
336
|
return path;
|
|
@@ -81,6 +81,16 @@ export interface SlashCommand {
|
|
|
81
81
|
label: string;
|
|
82
82
|
tag: string;
|
|
83
83
|
description: string;
|
|
84
|
+
/**
|
|
85
|
+
* The heading the offer is listed under. Absent for the HTML elements, which are the menu's
|
|
86
|
+
* first, unheaded group; `"Components"` for the project's components (§5.7).
|
|
87
|
+
*/
|
|
88
|
+
group?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Whether `tag` names a project component: the pick inserts `buildComponentInstance` and imports
|
|
91
|
+
* the component (`editor/insert-component.ts`) rather than an element's default definition.
|
|
92
|
+
*/
|
|
93
|
+
component?: boolean;
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
export interface JxContentResult {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inserting a component has one meaning everywhere (`specs/studio.md` §5.7, §9.1.3).
|
|
3
|
+
*
|
|
4
|
+
* A component instance is only a usable element in a document that imports it: the canvas drop did
|
|
5
|
+
* that, and the Insert panel's click, the slash menu and the canvas `+` did not, so a component
|
|
6
|
+
* clicked in rendered as an element the page could not resolve. This module is the one rule the
|
|
7
|
+
* four doors share — the instance comes from `buildComponentInstance`, and the import from the one
|
|
8
|
+
* `$elements` service, `files/elements.ts`'s `enableElement`.
|
|
9
|
+
*
|
|
10
|
+
* @docs studio/design/components
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { isString } from "@jxsuite/schema/guards";
|
|
14
|
+
import { buildComponentInstance, componentRegistry } from "../files/components";
|
|
15
|
+
import { enableElement } from "../files/elements";
|
|
16
|
+
import { mutateUpdateProperty } from "../tabs/transact";
|
|
17
|
+
import type { JxNodeValue } from "../tabs/transact";
|
|
18
|
+
import type { ElementsEntry } from "../files/elements";
|
|
19
|
+
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
20
|
+
import type { Tab } from "../tabs/tab";
|
|
21
|
+
import type { SlashCommand } from "./inline-edit";
|
|
22
|
+
|
|
23
|
+
/** The slash menu's group heading for the project's components. */
|
|
24
|
+
export const COMPONENTS_GROUP = "Components";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Import the component a node is an instance of into `tab`'s document, when it is one and is not
|
|
28
|
+
* imported yet. A node whose tag has no hyphen is an HTML element and is left alone, and a
|
|
29
|
+
* component the registry cannot name (an npm entry with no package, a local one with no path)
|
|
30
|
+
* changes nothing, so a document that had no `$elements` is not handed an empty one.
|
|
31
|
+
*
|
|
32
|
+
* A computed tag (`{ $expression }`) names no registered component, so it is left alone too.
|
|
33
|
+
*
|
|
34
|
+
* This is a MUTATION: call it inside the transaction that inserts the instance, so the instance and
|
|
35
|
+
* its import are one undo step. As a transaction of its own after the insert, the first ⌘Z took
|
|
36
|
+
* back only the import and left a document using a component it no longer imported, which saves as
|
|
37
|
+
* an instance the page cannot resolve.
|
|
38
|
+
*
|
|
39
|
+
* @param tab The document the node was inserted into, inside a `transactDoc`.
|
|
40
|
+
* @param tagName The inserted node's tag.
|
|
41
|
+
*/
|
|
42
|
+
export function mutateImportComponent(tab: Tab, tagName: JxMutableNode["tagName"]): void {
|
|
43
|
+
const after = elementsImporting(tab, tagName);
|
|
44
|
+
if (after) {
|
|
45
|
+
/* Through the recorded mutator, not a bare assignment: a transaction that records ops is
|
|
46
|
+
undone by replaying their inverses, so an unrecorded write beside the insert would survive
|
|
47
|
+
the ⌘Z that took the instance back, and never reach a co-editor.
|
|
48
|
+
SAFETY: `$elements` entries are JSON — module specifiers, `{ $ref }` imports and inline
|
|
49
|
+
element definitions — which is what a node key holds. */
|
|
50
|
+
mutateUpdateProperty(tab, [], "$elements", after as JxNodeValue);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The document's `$elements` with the component imported, or null when nothing would change. */
|
|
55
|
+
function elementsImporting(tab: Tab, tagName: JxMutableNode["tagName"]): ElementsEntry[] | null {
|
|
56
|
+
if (!isString(tagName) || !tagName.includes("-")) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const comp = componentRegistry.find((entry) => entry.tagName === tagName);
|
|
60
|
+
if (!comp) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
/* SAFETY: `$elements` holds module specifiers and `{ $ref }` imports; the document type and
|
|
64
|
+
files/elements.ts spell an inline entry as JxMutableNode and JxElement respectively, two editor
|
|
65
|
+
views of the same JSON object that enableElement only compares by `$ref`. */
|
|
66
|
+
const before = (tab.doc.document.$elements ?? []) as ElementsEntry[];
|
|
67
|
+
const after = enableElement(before, comp, tab.documentPath ?? null);
|
|
68
|
+
return after.length === before.length ? null : after;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** A fresh instance of a registered component, or null for a tag the registry does not know. */
|
|
72
|
+
export function componentInstance(tagName: string): JxMutableNode | null {
|
|
73
|
+
const comp = componentRegistry.find((entry) => entry.tagName === tagName);
|
|
74
|
+
return comp ? buildComponentInstance(comp) : null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Every registered component as a slash-menu offer, under the Components heading: the project's
|
|
79
|
+
* own, described by their definition file, and the ones installed packages provide, described by
|
|
80
|
+
* their package. A package component needs no prior enabling, because inserting one imports its
|
|
81
|
+
* package ({@link mutateImportComponent}), exactly as the canvas drop does.
|
|
82
|
+
*/
|
|
83
|
+
export function componentSlashCommands(): SlashCommand[] {
|
|
84
|
+
return componentRegistry.map((comp) => ({
|
|
85
|
+
component: true,
|
|
86
|
+
description: (comp.source === "npm" ? comp.package : comp.path) ?? "",
|
|
87
|
+
group: COMPONENTS_GROUP,
|
|
88
|
+
label: `<${comp.tagName}>`,
|
|
89
|
+
tag: comp.tagName,
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
* name via `setInsertZoneClickHandler(runInsertZoneAction)` in studio.ts.
|
|
8
8
|
*
|
|
9
9
|
* The cross-origin insertion "+" (drawn by the host from posted zones) runs the parent-realm
|
|
10
|
-
* slash-menu → mutateInsertNode flow on click. The
|
|
11
|
-
*
|
|
10
|
+
* slash-menu → mutateInsertNode flow on click. The menu offers the HTML elements and, under a
|
|
11
|
+
* Components heading, the project's components. The host captured the zone (parentPath + index);
|
|
12
|
+
* the new node is selected at its inserted path. Salvaged from the orphaned insertion-helper.ts.
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
import { showSlashMenu } from "./slash-menu";
|
|
16
|
+
import { componentInstance, mutateImportComponent } from "./insert-component";
|
|
15
17
|
import { defaultDef } from "../panels/shared";
|
|
16
18
|
import { mutateInsertNode, transactDoc } from "../tabs/transact";
|
|
17
19
|
import { activeTab } from "../workspace/workspace";
|
|
@@ -21,12 +23,19 @@ import type { JxPath } from "../state";
|
|
|
21
23
|
export function runInsertZoneAction(btn: HTMLElement, zone: InsertZone): void {
|
|
22
24
|
showSlashMenu(btn, "", {
|
|
23
25
|
onSelect: (cmd) => {
|
|
24
|
-
|
|
26
|
+
/* A pick from the Components group inserts an instance and imports the component, as a drop
|
|
27
|
+
of the same component does (§5.7); every other pick inserts the element's default. */
|
|
28
|
+
const instance = cmd.component === true ? componentInstance(cmd.tag) : null;
|
|
29
|
+
const def = instance ?? defaultDef(cmd.tag);
|
|
25
30
|
const insertPath: JxPath = zone.insertParentPath;
|
|
26
31
|
const idx = zone.index;
|
|
27
32
|
const newPath = [...insertPath, "children", idx];
|
|
28
|
-
|
|
33
|
+
const tab = activeTab.value;
|
|
34
|
+
transactDoc(tab, (t) => {
|
|
29
35
|
mutateInsertNode(t, insertPath, idx, def);
|
|
36
|
+
if (instance) {
|
|
37
|
+
mutateImportComponent(t, instance.tagName);
|
|
38
|
+
}
|
|
30
39
|
t.session.selection = [newPath];
|
|
31
40
|
});
|
|
32
41
|
},
|