@jxsuite/studio 6.1.1 → 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/{action-group-2mqby73c.js → action-group-4aan7g6g.js} +3 -3
- 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-6rbra5dy.js +4 -0
- package/dist/chunks/color-field-gy56c94g.js +4 -0
- package/dist/chunks/color-m7r1vk54.js +4 -0
- package/dist/chunks/color-slider-sqqdyf6c.js +4 -0
- package/dist/chunks/combobox-72h32ejk.js +4 -0
- 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/dialog-c4x05vyx.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-qg1b5eqx.js +51 -0
- package/dist/chunks/icons-63bjtbs7.js +4 -0
- package/dist/chunks/iframe-entry-2zggzajv.js +5 -0
- package/dist/chunks/iframe-entry-76j8vwwx.js +5 -0
- package/dist/chunks/iframe-entry-99nr5zfb.js +5 -0
- package/dist/chunks/iframe-entry-pkgmcgp0.js +5 -0
- package/dist/chunks/iframe-host-ssv7kyj0.js +4 -0
- package/dist/chunks/{index-2veqmnhr.js → index-32eqzv1k.js} +3 -3
- package/dist/chunks/index-f0jn7nnp.js +4 -0
- package/dist/chunks/{javascript-8nbvs12s.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-c421cf8c.js +4 -0
- 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-bdpzbfvs.js +4 -0
- package/dist/chunks/monaco-binding-vq1hgnn0.js +4 -0
- 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/number-field-y62xq0ty.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/select-rzds841e.js +4 -0
- package/dist/chunks/source-control-kwktr9y6.js +4 -0
- package/dist/chunks/split-c9zfdgxf.js +4 -0
- 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-6m380wp7.js +5 -0
- package/dist/chunks/studio-80jyh1xh.js +4 -0
- package/dist/chunks/studio-83mk4bgg.js +9 -0
- package/dist/chunks/{studio-kggey1jt.js → studio-881z1xrx.js} +4 -4
- package/dist/chunks/studio-8m0y2qe7.js +5 -0
- package/dist/chunks/studio-8tr6cfrc.js +5 -0
- 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-bbe4c90a.js +5 -0
- 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-8grwxtqk.js → studio-jcnsdaep.js} +4 -4
- package/dist/chunks/studio-jsgf2q5k.js +7 -0
- package/dist/chunks/{studio-1q1sh2ax.js → studio-k3xxbagr.js} +3 -3
- package/dist/chunks/studio-k7evxqh4.js +5 -0
- 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-rv11kfwa.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-h46vtzk8.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-wszjyh0j.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-pre0skq6.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-t6b6qe31.js → studio-r5qy30vx.js} +4 -4
- 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/swatch-group-ewh24n1f.js +4 -0
- package/dist/chunks/tabs-f5cw3tgp.js +4 -0
- package/dist/chunks/textfield-wewc8rq2.js +4 -0
- package/dist/chunks/toast-1gv0h53h.js +4 -0
- package/dist/chunks/toast-host-1yg5ar8d.js +4 -0
- package/dist/chunks/{toolbar-2nnzqhrj.js → toolbar-44cm5cta.js} +3 -3
- package/dist/chunks/tooltip-yypz28gq.js +4 -0
- package/dist/chunks/tree-9xa4hm7n.js +4 -0
- 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-bv552sfx.js +4 -0
- package/dist/iframe-entry.js +20 -16
- package/dist/manifest.json +77 -63
- package/dist/studio.js +106 -107
- package/dist/workers/ts.worker.js +3 -3
- package/package.json +17 -16
- package/src/about/about-modal.ts +4 -3
- package/src/account/action-flow.ts +45 -38
- package/src/account-status.ts +7 -2
- package/src/browse/component-examples.ts +272 -0
- package/src/browse/library-commands.ts +64 -104
- package/src/browse/library-layouts.ts +270 -53
- package/src/browse/library-model.ts +194 -134
- package/src/browse/library-pane.ts +748 -440
- package/src/browse/library-preview.ts +161 -26
- 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 +40 -10
- package/src/canvas/asset-resolve.ts +19 -23
- package/src/canvas/canvas-helpers.ts +7 -6
- package/src/canvas/canvas-live-render.ts +260 -77
- package/src/canvas/canvas-origin.ts +37 -12
- package/src/canvas/canvas-page-context.ts +112 -0
- package/src/canvas/canvas-patcher.ts +30 -30
- package/src/canvas/canvas-perf.ts +11 -5
- package/src/canvas/canvas-render.ts +175 -148
- package/src/canvas/canvas-surface.ts +5 -3
- package/src/canvas/canvas-utils.ts +147 -61
- package/src/canvas/dialog-path.ts +9 -4
- package/src/canvas/dialog-state.ts +1 -2
- package/src/canvas/diff-marks.ts +9 -16
- package/src/canvas/diff-toolbar.ts +8 -7
- package/src/canvas/edit-width-drag.ts +4 -1
- package/src/canvas/edit-width.ts +1 -3
- package/src/canvas/iframe-channel.ts +27 -13
- package/src/canvas/iframe-drop.ts +120 -12
- package/src/canvas/iframe-editable-root.ts +29 -16
- package/src/canvas/iframe-entry.ts +104 -75
- package/src/canvas/iframe-eval.ts +73 -49
- package/src/canvas/iframe-host.ts +269 -158
- package/src/canvas/iframe-inline-edit.ts +49 -30
- package/src/canvas/iframe-insert.ts +5 -4
- package/src/canvas/iframe-interaction.ts +30 -25
- package/src/canvas/iframe-keys.ts +11 -0
- package/src/canvas/iframe-overlay.ts +1 -1
- package/src/canvas/iframe-patch.ts +25 -21
- package/src/canvas/iframe-position.ts +12 -6
- package/src/canvas/iframe-protocol.ts +36 -21
- package/src/canvas/iframe-render.ts +84 -35
- package/src/canvas/iframe-slash.ts +24 -11
- package/src/canvas/iframe-subtree.ts +10 -3
- package/src/canvas/npm-modules.ts +22 -0
- package/src/canvas/path-mapping.ts +19 -12
- package/src/canvas/popover-path.ts +7 -3
- package/src/canvas/popover-state.ts +1 -2
- package/src/canvas/preview-navigate.ts +16 -0
- package/src/canvas/repeater-sources.ts +200 -0
- package/src/canvas/serialize-scope.ts +13 -5
- package/src/canvas/stage-scrollbar.ts +7 -2
- package/src/canvas/surface-registry.ts +27 -3
- package/src/collab/collab-commands.ts +7 -2
- package/src/collab/collab-session.ts +84 -45
- package/src/collab/collab-state.ts +4 -3
- package/src/collab/monaco-binding.ts +16 -13
- package/src/collab/monaco-cursors.ts +5 -2
- package/src/commands/app-commands.ts +16 -6
- package/src/commands/budget.ts +13 -11
- package/src/commands/command-args.ts +103 -71
- package/src/commands/context.ts +55 -28
- package/src/commands/defaults.ts +69 -45
- package/src/commands/keymap.ts +56 -43
- package/src/commands/levels.ts +14 -8
- package/src/commands/live-context.ts +9 -7
- package/src/commands/reference.ts +2 -1
- package/src/commands/registry.ts +22 -11
- package/src/commands/run-reported.ts +15 -10
- package/src/component-props.ts +46 -30
- package/src/content/collection-match.ts +11 -9
- package/src/content/collection-pages.ts +412 -0
- package/src/content/content-collections.ts +270 -0
- package/src/content/draft-state.ts +4 -3
- package/src/content/entry-commands.ts +54 -62
- package/src/content/entry-editor.ts +137 -32
- package/src/content/entry-fields.ts +25 -8
- package/src/content/entry-inspector.ts +225 -0
- package/src/content/entry-model.ts +38 -15
- 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/canvas-context-menu.ts +1 -2
- package/src/editor/canvas-slash-bridge.ts +3 -6
- package/src/editor/content-slice.ts +16 -14
- package/src/editor/context-menu.ts +35 -22
- package/src/editor/convert-targets.ts +30 -35
- package/src/editor/convert-to-component.ts +78 -48
- package/src/editor/convert-to-repeater.ts +34 -22
- package/src/editor/editable-tags.ts +17 -2
- package/src/editor/file-drop-action.ts +29 -22
- package/src/editor/inline-edit-apply.ts +79 -17
- package/src/editor/inline-edit.ts +76 -50
- package/src/editor/inline-format.ts +59 -56
- package/src/editor/inline-link.ts +40 -15
- package/src/editor/insert-component.ts +91 -0
- package/src/editor/insert-zone-action.ts +14 -5
- package/src/editor/merge-tags.ts +58 -19
- package/src/editor/repeater-scope.ts +39 -28
- package/src/editor/shortcuts.ts +76 -42
- package/src/editor/slash-menu.ts +52 -14
- package/src/files/asset-versions.ts +4 -4
- package/src/files/components.ts +89 -20
- package/src/files/elements.ts +5 -6
- package/src/files/file-ops.ts +92 -41
- package/src/files/files.ts +248 -239
- package/src/files/fs-events.ts +18 -7
- package/src/files/media-meta.ts +97 -2
- package/src/files/media-paths.ts +3 -0
- 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 +15 -5
- package/src/format/constraints.ts +1 -1
- package/src/format/convert-file.ts +27 -13
- package/src/format/format-choices.ts +2 -1
- package/src/format/format-host.ts +91 -50
- package/src/github/github-auth-status.ts +27 -3
- package/src/github/github-auth.ts +21 -11
- package/src/github/github-publish.ts +28 -23
- package/src/grid/cell-editors.ts +46 -31
- package/src/grid/cell-popovers.ts +5 -3
- package/src/grid/edit-buffer.ts +10 -9
- package/src/grid/grid-controller.ts +25 -23
- package/src/grid/grid-layout.ts +21 -5
- package/src/grid/grid-open.ts +3 -33
- package/src/grid/grid-panel.ts +65 -6
- package/src/grid/grid-reveal.ts +34 -0
- package/src/grid/grid-source.ts +9 -31
- package/src/grid/grid-view.ts +45 -23
- package/src/grid/redirects-grid.ts +14 -5
- package/src/grid/redirects.ts +16 -14
- package/src/grid/schema-columns.ts +25 -19
- package/src/grid/sources/connector-source.ts +47 -20
- package/src/grid/sources/content-source.ts +100 -31
- package/src/grid/sources/csv-file-source.ts +2 -2
- package/src/grid/tabulator-tables.d.ts +61 -34
- package/src/hosting/document.ts +2 -2
- package/src/hosting/stage.ts +7 -7
- package/src/i18n/i18n-commands.ts +3 -3
- 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 +51 -20
- package/src/new-project/add-repo-modal.ts +20 -19
- package/src/new-project/import-tab.ts +10 -14
- package/src/new-project/location-fields.ts +2 -1
- package/src/new-project/new-project-modal.ts +12 -9
- package/src/packages/jxsuite-update.ts +4 -3
- package/src/packages/pull-package-sync.ts +26 -25
- package/src/page-params.ts +97 -68
- package/src/panels/activity-panel.ts +38 -14
- package/src/panels/ai-chat/attached-context.ts +13 -7
- package/src/panels/ai-chat/chat-markdown.ts +8 -8
- package/src/panels/ai-chat/chat-view.ts +80 -73
- package/src/panels/ai-chat/composer.ts +30 -33
- package/src/panels/ai-chat/sessions-view.ts +1 -2
- package/src/panels/ai-panel.ts +23 -20
- package/src/panels/block-action-bar.ts +57 -46
- package/src/panels/bottom-dock.ts +5 -3
- package/src/panels/canvas-dnd-bridge.ts +36 -26
- 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 +63 -26
- package/src/panels/data-grid.ts +18 -11
- package/src/panels/dnd.ts +115 -138
- package/src/panels/editors.ts +52 -21
- package/src/panels/elements-panel.ts +19 -5
- package/src/panels/empty-state.ts +2 -2
- package/src/panels/events-panel.ts +84 -69
- package/src/panels/formula-workspace.ts +78 -54
- package/src/panels/frontmatter-fields.ts +33 -28
- package/src/panels/git-diff-open.ts +10 -6
- package/src/panels/head-panel.ts +488 -202
- package/src/panels/i18n-panel.ts +20 -12
- package/src/panels/imports-panel.ts +25 -47
- package/src/panels/jump-bar.ts +75 -31
- package/src/panels/layers-panel.ts +130 -101
- package/src/panels/left-panel.ts +25 -11
- package/src/panels/navigator-panels.ts +23 -11
- package/src/panels/pane-context.ts +140 -49
- package/src/panels/pane-grid.ts +55 -26
- package/src/panels/panel-registry.ts +36 -46
- package/src/panels/properties-panel.ts +182 -124
- package/src/panels/quick-search.ts +53 -31
- package/src/panels/right-panel.ts +201 -45
- package/src/panels/seo-modal.ts +54 -125
- package/src/panels/settings-menu.ts +63 -61
- package/src/panels/settings-pane.ts +268 -162
- package/src/panels/signals-panel.ts +156 -135
- package/src/panels/site-model.ts +367 -0
- package/src/panels/site-panel.ts +638 -0
- package/src/panels/statement-editor.ts +230 -140
- package/src/panels/style-panel.ts +253 -206
- package/src/panels/style-utils.ts +114 -51
- package/src/panels/stylebook-doc.ts +195 -60
- package/src/panels/stylebook-layers-panel.ts +48 -89
- package/src/panels/stylebook-panel.ts +60 -27
- package/src/panels/styles-panel.ts +327 -0
- package/src/panels/tab-drop.ts +27 -11
- package/src/panels/tab-strip.ts +143 -49
- package/src/platform-errors.ts +57 -43
- package/src/platform.ts +59 -31
- package/src/platforms/cloud.ts +373 -176
- package/src/platforms/default-platform.ts +14 -8
- package/src/platforms/devserver.ts +180 -142
- package/src/platforms/read-batcher.ts +16 -8
- package/src/project-list.ts +2 -1
- package/src/publish/deploy-checklist.ts +20 -9
- package/src/publish/pages-service.ts +66 -27
- package/src/publish/publish-changes.ts +491 -0
- package/src/publish/publish-commands.ts +88 -22
- package/src/publish/publish-ids.ts +49 -0
- package/src/publish/publish-panel.ts +10 -6
- 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/recent-projects.ts +15 -8
- package/src/resize-edges.ts +26 -36
- package/src/services/a11y-report.ts +26 -19
- package/src/services/agent-seed.ts +23 -11
- package/src/services/ai-ask.ts +9 -12
- package/src/services/ai-command-tools.ts +68 -62
- package/src/services/ai-import-tools.ts +80 -67
- package/src/services/ai-models.ts +17 -13
- package/src/services/ai-project-tools.ts +68 -32
- package/src/services/ai-session-store.ts +19 -10
- package/src/services/ai-system-prompt.ts +46 -20
- package/src/services/ai-tools.ts +71 -27
- package/src/services/ai-write-report.ts +20 -12
- package/src/services/ai-writes.ts +4 -4
- package/src/services/automation.ts +74 -51
- package/src/services/cem-export.ts +149 -58
- package/src/services/cf-settings.ts +4 -4
- package/src/services/clock.ts +6 -1
- package/src/services/code-services.ts +37 -31
- package/src/services/connection.ts +2 -2
- package/src/services/context-manager.ts +14 -10
- package/src/services/context-resolver.ts +20 -8
- package/src/services/data-service.ts +19 -15
- package/src/services/directory-picker.ts +20 -14
- package/src/services/document-assistant.ts +39 -19
- package/src/services/gated-registry.ts +10 -6
- package/src/services/harness/chat-reducer.ts +23 -1
- package/src/services/import-client.ts +91 -38
- package/src/services/import-seed.ts +8 -2
- package/src/services/jx-validate.ts +28 -31
- package/src/services/live-preview.ts +0 -0
- package/src/services/live-surfaces.ts +6 -3
- package/src/services/model-uri.ts +10 -2
- package/src/services/module-preloads.ts +12 -8
- package/src/services/monaco-buffer.ts +43 -24
- package/src/services/monaco-lazy.ts +7 -8
- package/src/services/monaco-setup.ts +23 -16
- package/src/services/notify.ts +44 -17
- package/src/services/popover-report.ts +25 -10
- package/src/services/preview-eval.ts +20 -8
- package/src/services/profile.ts +13 -6
- package/src/services/project-path.ts +13 -10
- package/src/services/references.ts +0 -0
- package/src/services/render-critic.ts +29 -9
- package/src/services/settings/definitions.ts +25 -14
- package/src/services/settings/kernel.ts +13 -4
- package/src/services/settings/write-queue.ts +9 -9
- package/src/services/token-lint.ts +12 -10
- package/src/services/tool-executor.ts +17 -5
- package/src/services/tool-outcomes.ts +44 -24
- package/src/services/trusted-types.ts +24 -7
- package/src/settings/contexts-section.ts +11 -8
- package/src/settings/contributed-section.ts +128 -70
- package/src/settings/css-vars-editor.ts +119 -32
- package/src/settings/defs-editor.ts +38 -35
- package/src/settings/developer-panels.ts +43 -0
- package/src/settings/extension-rows.ts +16 -8
- package/src/settings/extension-sections.ts +42 -14
- package/src/settings/extensions-section.ts +4 -1
- package/src/settings/general-settings.ts +101 -28
- package/src/settings/locales-section.ts +20 -12
- package/src/settings/preferences-dialog.ts +17 -7
- package/src/settings/preferences-keymap.ts +7 -5
- package/src/settings/preferences-sections.ts +11 -5
- package/src/settings/project-sections.ts +28 -11
- package/src/settings/schema-field-ui.ts +12 -1
- package/src/settings/section-registry.ts +116 -11
- package/src/settings/settings-dialog.ts +192 -0
- package/src/settings/settings-document.ts +186 -166
- package/src/shell/tree.ts +6 -5
- package/src/shell.ts +536 -477
- package/src/site-context.ts +101 -32
- package/src/state.ts +68 -57
- package/src/store.ts +54 -34
- package/src/studio.ts +219 -116
- package/src/style/live-preview.ts +1 -1
- package/src/style/project-styles.ts +40 -7
- 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/token-ref.ts +5 -4
- package/src/style/variables-focus.ts +61 -0
- package/src/style/variables-view.ts +235 -0
- package/src/surfaces/about.ts +18 -19
- package/src/surfaces/add-new.json +1482 -0
- package/src/surfaces/add-new.ts +325 -0
- package/src/surfaces/add-repo.ts +17 -19
- package/src/surfaces/ai-chat.ts +33 -38
- package/src/surfaces/ai-credentials-form.ts +6 -16
- package/src/surfaces/ai-managed-connect.ts +3 -4
- package/src/surfaces/ai-model-picker.ts +3 -5
- package/src/surfaces/block-action-bar.ts +22 -11
- package/src/surfaces/boot-failure.json +4 -3
- package/src/surfaces/boot-failure.ts +4 -5
- package/src/surfaces/bottom-dock.json +1 -1
- package/src/surfaces/bottom-dock.ts +9 -18
- package/src/surfaces/canvas-stage.json +30 -19
- package/src/surfaces/canvas-stage.ts +17 -16
- package/src/surfaces/cf-account-picker.ts +17 -18
- 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 +145 -105
- package/src/surfaces/convert-repeater.ts +17 -19
- package/src/surfaces/data-actions.ts +9 -7
- package/src/surfaces/dialog.ts +26 -28
- package/src/surfaces/diff-toolbar.ts +9 -7
- package/src/surfaces/empty-state.json +1 -1
- package/src/surfaces/empty-state.ts +7 -10
- package/src/surfaces/entry-editor.json +136 -0
- package/src/surfaces/entry-editor.ts +35 -11
- package/src/surfaces/entry-inspector.json +371 -0
- package/src/surfaces/entry-inspector.ts +143 -0
- package/src/surfaces/expression-editor.ts +11 -25
- package/src/surfaces/files-panel.ts +27 -14
- package/src/surfaces/formula-palette.ts +4 -5
- package/src/surfaces/github-auth.ts +16 -20
- package/src/surfaces/github-publish.ts +17 -22
- package/src/surfaces/grid-cell.ts +30 -31
- package/src/surfaces/grid-open.ts +10 -14
- package/src/surfaces/grid-panel.ts +13 -9
- package/src/surfaces/grid-replace.ts +13 -13
- package/src/surfaces/grid-views.ts +15 -18
- package/src/surfaces/inspector-dock.json +162 -14
- package/src/surfaces/inspector-dock.ts +59 -31
- package/src/surfaces/jump-bar.ts +5 -6
- package/src/surfaces/jxsuite-update.ts +16 -22
- package/src/surfaces/library-pane.json +670 -437
- package/src/surfaces/library-pane.ts +111 -77
- package/src/surfaces/logic-panel.ts +17 -30
- package/src/surfaces/logic-workspace.ts +13 -15
- package/src/surfaces/media-browser.json +62 -6
- package/src/surfaces/media-browser.ts +26 -11
- package/src/surfaces/media-field.json +1 -1
- package/src/surfaces/media-field.ts +4 -5
- package/src/surfaces/media-library.json +1586 -0
- package/src/surfaces/media-library.ts +282 -0
- package/src/surfaces/media-pane.ts +8 -7
- package/src/surfaces/media-pick-dialog.json +71 -0
- package/src/surfaces/media-pick-dialog.ts +132 -0
- package/src/surfaces/menu.ts +47 -23
- package/src/surfaces/navigator-dock.ts +11 -12
- package/src/surfaces/new-project.ts +27 -26
- package/src/surfaces/pane-context.json +144 -77
- package/src/surfaces/pane-context.ts +44 -25
- package/src/surfaces/pane-grid.ts +15 -10
- package/src/surfaces/panel-activity.ts +5 -6
- package/src/surfaces/panel-data.ts +3 -4
- package/src/surfaces/panel-deploy-checklist.json +12 -6
- package/src/surfaces/panel-deploy-checklist.ts +8 -7
- package/src/surfaces/panel-elements.ts +5 -7
- package/src/surfaces/panel-i18n.ts +5 -7
- package/src/surfaces/panel-imports.ts +6 -13
- package/src/surfaces/panel-outline.ts +25 -19
- package/src/surfaces/panel-page.json +72 -2
- package/src/surfaces/panel-page.ts +49 -25
- package/src/surfaces/panel-problems.ts +5 -7
- package/src/surfaces/panel-signals.json +1 -1
- package/src/surfaces/panel-signals.ts +25 -39
- package/src/surfaces/panel-stylebook-layers.json +71 -204
- package/src/surfaces/panel-stylebook-layers.ts +5 -29
- package/src/surfaces/preferences.json +120 -48
- package/src/surfaces/preferences.ts +29 -11
- package/src/surfaces/progress-modal.ts +15 -9
- package/src/surfaces/properties-panel.json +1 -1
- package/src/surfaces/properties-panel.ts +20 -42
- 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/publish.ts +18 -18
- package/src/surfaces/push-plan.ts +7 -8
- package/src/surfaces/rail.json +1 -159
- package/src/surfaces/rail.ts +22 -79
- package/src/surfaces/reference-field.ts +11 -15
- package/src/surfaces/schema-builder.ts +16 -22
- package/src/surfaces/schema-form.json +10 -2
- package/src/surfaces/schema-form.ts +17 -25
- package/src/surfaces/secret-field.ts +9 -6
- package/src/surfaces/seo.json +1 -1
- package/src/surfaces/seo.ts +21 -15
- package/src/surfaces/settings-contexts.ts +9 -7
- package/src/surfaces/settings-contributed.json +5 -5
- package/src/surfaces/settings-contributed.ts +32 -12
- package/src/surfaces/settings-css-vars.json +44 -36
- package/src/surfaces/settings-css-vars.ts +82 -16
- package/src/surfaces/settings-defs.json +2 -2
- package/src/surfaces/settings-defs.ts +17 -21
- package/src/surfaces/settings-deploy.json +2 -15
- package/src/surfaces/settings-deploy.ts +9 -16
- package/src/surfaces/settings-dialog.json +51 -0
- package/src/surfaces/settings-dialog.ts +169 -0
- package/src/surfaces/settings-extensions.ts +5 -6
- package/src/surfaces/settings-head.ts +33 -39
- package/src/surfaces/settings-locales.ts +8 -20
- package/src/surfaces/settings-overview.json +3 -3
- package/src/surfaces/settings-overview.ts +14 -8
- package/src/surfaces/settings-packages.ts +8 -7
- package/src/surfaces/settings-pane.json +269 -46
- package/src/surfaces/settings-pane.ts +88 -36
- package/src/surfaces/settings-rawjson.ts +5 -6
- package/src/surfaces/shell.json +2 -1
- package/src/surfaces/shell.ts +1 -2
- 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 +42 -9
- package/src/surfaces/statements.ts +13 -18
- package/src/surfaces/statusbar.ts +56 -29
- package/src/surfaces/style-panel.ts +18 -31
- package/src/surfaces/stylebook-chrome.ts +3 -5
- 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/tab-strip.ts +14 -13
- package/src/surfaces/target-line.ts +6 -7
- package/src/surfaces/welcome.ts +6 -8
- 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/patch-ops.ts +4 -3
- package/src/tabs/project-config.ts +32 -21
- package/src/tabs/selection.ts +36 -8
- package/src/tabs/tab.ts +99 -48
- package/src/tabs/transact.ts +98 -53
- package/src/types.ts +53 -43
- package/src/ui/ai-credentials-form.ts +10 -13
- package/src/ui/ai-model-picker.ts +3 -3
- package/src/ui/cf-account-picker.ts +7 -9
- package/src/ui/color-selector.ts +9 -5
- package/src/ui/dynamic-slot.ts +7 -8
- package/src/ui/expression-editor.ts +112 -105
- package/src/ui/form-controls.ts +104 -56
- package/src/ui/formula-catalog.ts +239 -230
- package/src/ui/formula-chips.ts +33 -18
- package/src/ui/layers.ts +57 -31
- package/src/ui/media-picker.ts +300 -47
- package/src/ui/popover-surface.ts +20 -12
- package/src/ui/progress-modal.ts +13 -8
- package/src/ui/regions.ts +14 -12
- package/src/ui/schema-form.ts +372 -97
- package/src/ui/surface.ts +71 -8
- package/src/ui/timing.ts +9 -0
- package/src/ui/value-source.ts +44 -31
- package/src/ui/virtual-window.ts +29 -3
- package/src/utils/base64.ts +6 -4
- package/src/utils/edit-display.ts +366 -111
- package/src/utils/element-selectors.ts +3 -2
- package/src/utils/google-fonts.ts +15 -14
- package/src/utils/inherited-style.ts +18 -20
- package/src/utils/link-target.ts +1 -1
- package/src/utils/local-day.ts +18 -0
- package/src/utils/preview-format.ts +25 -3
- package/src/utils/strip-events.ts +49 -22
- package/src/utils/studio-utils.ts +29 -14
- package/src/view.ts +2 -3
- package/src/workspace/pane-derive.ts +14 -16
- package/src/workspace/session.ts +81 -50
- 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 +165 -47
- package/styles/shell-frame.css +30 -7
- package/styles/shell-frame.json +15 -3
- package/dist/chunks/2020-va2wwra4.js +0 -4
- package/dist/chunks/adapter-f93pafd7.js +0 -4
- package/dist/chunks/cf-account-picker-af1h381v.js +0 -4
- package/dist/chunks/cf-settings-125tdm0w.js +0 -4
- package/dist/chunks/color-area-jqhna8zy.js +0 -4
- package/dist/chunks/color-field-embq5ctg.js +0 -4
- package/dist/chunks/color-fpezvnkt.js +0 -4
- package/dist/chunks/color-slider-1qmw7wqy.js +0 -4
- package/dist/chunks/combobox-773hdzyj.js +0 -4
- package/dist/chunks/components-ax2f1fnn.js +0 -4
- package/dist/chunks/content-source-108aqtk1.js +0 -4
- package/dist/chunks/convert-to-repeater-ry5rmppr.js +0 -4
- package/dist/chunks/dialog-mtm9fq5t.js +0 -4
- package/dist/chunks/editor-pfgdmkb0.js +0 -4
- package/dist/chunks/entry-commands-t0k1q0kx.js +0 -4
- package/dist/chunks/extension-sections-6mwh2hf8.js +0 -4
- package/dist/chunks/file-ops-meej5mqf.js +0 -4
- package/dist/chunks/files-44vp8a2s.js +0 -4
- package/dist/chunks/format-host-7q1petb9.js +0 -4
- package/dist/chunks/fs-events-h2mwnxaz.js +0 -4
- package/dist/chunks/git-panel-zngbymvd.js +0 -4
- package/dist/chunks/grid-view-sx9vynsp.js +0 -51
- package/dist/chunks/icons-p44xhjf0.js +0 -4
- package/dist/chunks/iframe-entry-f7mqjwpm.js +0 -5
- package/dist/chunks/iframe-entry-haw7jkvz.js +0 -5
- package/dist/chunks/iframe-entry-z6qrytv3.js +0 -5
- package/dist/chunks/iframe-host-qk3ycq87.js +0 -4
- package/dist/chunks/index-wkyfjeqz.js +0 -4
- package/dist/chunks/jsonMode-s8kt9dsj.js +0 -13
- package/dist/chunks/jx-validate-psgd7vsx.js +0 -4
- package/dist/chunks/layers-panel-1kq2jyfk.js +0 -4
- package/dist/chunks/layers-xd92fg8d.js +0 -4
- package/dist/chunks/listbox-s8fkmnq8.js +0 -4
- package/dist/chunks/lit-html-ac4cwwr4.js +0 -4
- package/dist/chunks/media-picker-0svzkdyb.js +0 -4
- package/dist/chunks/menu-37d07z9m.js +0 -4
- package/dist/chunks/menu-5c55ea3d.js +0 -4
- package/dist/chunks/monaco-binding-z6r70pdt.js +0 -4
- package/dist/chunks/monaco-buffer-fz6gq5y7.js +0 -4
- package/dist/chunks/monaco-setup-q4axkgj7.js +0 -4
- package/dist/chunks/notify-0xgtn8q1.js +0 -4
- package/dist/chunks/number-field-gw7sq8rn.js +0 -4
- package/dist/chunks/parse-897fvqah.js +0 -4
- package/dist/chunks/platform-v1qctbe6.js +0 -4
- package/dist/chunks/popover-8htra4er.js +0 -4
- package/dist/chunks/publish-panel-ym1fmgq5.js +0 -4
- package/dist/chunks/quick-search-a54e6t5n.js +0 -4
- package/dist/chunks/references-gmqp82fs.js +0 -4
- package/dist/chunks/right-panel-v7t9awbk.js +0 -4
- package/dist/chunks/section-registry-vhyf3zsr.js +0 -4
- package/dist/chunks/select-n3v7gwas.js +0 -4
- package/dist/chunks/split-pff8f3ze.js +0 -4
- package/dist/chunks/studio-0bhx5hz6.js +0 -5
- package/dist/chunks/studio-0k1ddnyy.js +0 -6
- package/dist/chunks/studio-1b04pd0j.js +0 -64
- package/dist/chunks/studio-4dwbj6gb.js +0 -5
- package/dist/chunks/studio-4tanh828.js +0 -5
- package/dist/chunks/studio-55h4mstj.js +0 -4
- package/dist/chunks/studio-5tnvjkz4.js +0 -5
- package/dist/chunks/studio-60dqgb0c.js +0 -5
- package/dist/chunks/studio-61dp09qg.js +0 -9
- package/dist/chunks/studio-63epcrj2.js +0 -5
- package/dist/chunks/studio-74k29dy9.js +0 -5
- package/dist/chunks/studio-7vg6jcd3.js +0 -5
- package/dist/chunks/studio-7zx31knw.js +0 -9
- package/dist/chunks/studio-8bxk97km.js +0 -5
- package/dist/chunks/studio-933e30n4.js +0 -5
- package/dist/chunks/studio-98wnwhbg.js +0 -5
- package/dist/chunks/studio-9mccjwjh.js +0 -14
- package/dist/chunks/studio-a2ke1e4t.js +0 -5
- package/dist/chunks/studio-e3a2a6v3.js +0 -4
- package/dist/chunks/studio-e81q1vb9.js +0 -5
- package/dist/chunks/studio-epgrmqqm.js +0 -5
- package/dist/chunks/studio-ery42sqr.js +0 -4
- package/dist/chunks/studio-g1shz58g.js +0 -5
- package/dist/chunks/studio-gf0fz03c.js +0 -5
- package/dist/chunks/studio-gjtpe62q.js +0 -6
- package/dist/chunks/studio-gkskhg7m.js +0 -5
- package/dist/chunks/studio-gx4dnjzd.js +0 -5
- package/dist/chunks/studio-j03zga3y.js +0 -9
- package/dist/chunks/studio-jhpxeckz.js +0 -5
- package/dist/chunks/studio-k01r7ykp.js +0 -5
- package/dist/chunks/studio-kk7h30ne.js +0 -7
- package/dist/chunks/studio-kt1zqney.js +0 -5
- package/dist/chunks/studio-m3cwhvye.js +0 -5
- package/dist/chunks/studio-mcayt6hz.js +0 -5
- package/dist/chunks/studio-myjcghv3.js +0 -5
- package/dist/chunks/studio-n8bxwc3y.js +0 -4
- package/dist/chunks/studio-ngm8gvpb.js +0 -5
- package/dist/chunks/studio-nj5a01jy.js +0 -928
- package/dist/chunks/studio-pcgz8heb.js +0 -5
- package/dist/chunks/studio-qcsq0t15.js +0 -5
- package/dist/chunks/studio-qkstvve9.js +0 -5
- package/dist/chunks/studio-qv1pwd3c.js +0 -5
- package/dist/chunks/studio-rc6dvxjb.js +0 -7
- package/dist/chunks/studio-s8dmmz66.js +0 -5
- package/dist/chunks/studio-sp5xzbj4.js +0 -6
- package/dist/chunks/studio-szsjbj0g.js +0 -5
- package/dist/chunks/studio-t1xzawhr.js +0 -16
- package/dist/chunks/studio-tdcprbqb.js +0 -5
- package/dist/chunks/studio-vrmcjf9g.js +0 -7
- package/dist/chunks/studio-vyc0b4he.js +0 -10
- package/dist/chunks/studio-w0cgf6jg.js +0 -5
- package/dist/chunks/studio-w3hskzqk.js +0 -5
- package/dist/chunks/studio-xgrgcdyh.js +0 -9
- package/dist/chunks/studio-xz2yxfcb.js +0 -5
- package/dist/chunks/studio-y6n42tgs.js +0 -5
- package/dist/chunks/studio-yb560vxr.js +0 -5
- package/dist/chunks/studio-yn9f8vtx.js +0 -5
- package/dist/chunks/studio-yw05w8f0.js +0 -7
- package/dist/chunks/studio-z16sgavc.js +0 -5
- package/dist/chunks/studio-zey7zsdw.js +0 -12
- package/dist/chunks/studio-zyqn40w4.js +0 -5
- package/dist/chunks/swatch-group-8qh9a3bm.js +0 -4
- package/dist/chunks/tabs-jwthg40w.js +0 -4
- package/dist/chunks/textfield-spy3hc77.js +0 -4
- package/dist/chunks/toast-4d35zd64.js +0 -4
- package/dist/chunks/toast-host-8sjmfed3.js +0 -4
- package/dist/chunks/tooltip-fd6g9xy3.js +0 -4
- package/dist/chunks/tree-h19v1jfz.js +0 -4
- package/dist/chunks/tsMode-2frah6h1.js +0 -14
- package/dist/chunks/workspace-zt81g1ms.js +0 -4
- package/dist/chunks/ws-client-n2bh4hkf.js +0 -4
- package/src/panels/frontmatter-panel.ts +0 -701
- package/src/panels/git-panel.ts +0 -1120
- package/src/surfaces/doc-header.json +0 -776
- package/src/surfaces/doc-header.ts +0 -231
- package/src/surfaces/git-panel.json +0 -1880
- package/src/surfaces/git-panel.ts +0 -272
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rules a new file's name is held to, with no dialog and no write attached.
|
|
3
|
+
*
|
|
4
|
+
* `createFileIn` (`files/files.ts`) used to keep these inside one closure over its prompt, which
|
|
5
|
+
* was right while the prompt was the only creation flow there was. Add new (`create/add-new.ts`,
|
|
6
|
+
* `specs/studio.md` §9.1.1) composes a name the same way and must refuse the same things — a blank
|
|
7
|
+
* name, a name that escapes the destination, a name that is already taken — and a second copy of
|
|
8
|
+
* those rules would disagree with the first the first time either was fixed. So the composing and
|
|
9
|
+
* the judging live here, DOM-free, and both flows call them; only the write, and what each flow
|
|
10
|
+
* does around it, differs.
|
|
11
|
+
*
|
|
12
|
+
* Nothing here lists a directory. The caller supplies the `taken` names it listed, because when
|
|
13
|
+
* that listing happens is the caller's concern: before the prompt opens for New File, and once per
|
|
14
|
+
* destination directory for Add new, whose destination moves as the reader types.
|
|
15
|
+
*
|
|
16
|
+
* @docs studio/interface
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The picker row meaning "I will type the whole name myself".
|
|
21
|
+
*
|
|
22
|
+
* It is what keeps the format picker from being a cage. `New File…` is the only generic
|
|
23
|
+
* file-creation affordance in Studio and both backends create intermediate directories on write, so
|
|
24
|
+
* without this row `styles/main.css`, `public/robots.txt`, `.gitignore` and a `credits.txt` beside
|
|
25
|
+
* a collection's images all become uncreatable — and the picker would have taken away more than it
|
|
26
|
+
* gave. The picker stays the authority on the extension; this is one of its answers.
|
|
27
|
+
*/
|
|
28
|
+
export const OTHER_FORMAT = "__other__";
|
|
29
|
+
|
|
30
|
+
/** How the extension of a new file is settled. */
|
|
31
|
+
export type FormatChoice =
|
|
32
|
+
/** Picker over every creatable format, plus Other…. The field is a NAME, taken verbatim. */
|
|
33
|
+
| { kind: "choose"; defaultExt?: string; docKind?: "page" | "component" | "content" }
|
|
34
|
+
/**
|
|
35
|
+
* Picker holding ONE format, plus Other… — the collection's own extension. The field is a NAME.
|
|
36
|
+
* `because` is the sentence a refusal quotes when Other… would smuggle a foreign document in.
|
|
37
|
+
*/
|
|
38
|
+
| { kind: "locked"; ext: string; because: string }
|
|
39
|
+
/** No picker. The field is a DISPLAY NAME, slugified, and `ext` is appended. */
|
|
40
|
+
| { kind: "fixed"; ext: string };
|
|
41
|
+
|
|
42
|
+
/** A display name, reduced to a file stem. Lowercase by design: this is a slug. */
|
|
43
|
+
export function slugify(input: string): string {
|
|
44
|
+
return input
|
|
45
|
+
.trim()
|
|
46
|
+
.toLowerCase()
|
|
47
|
+
.replaceAll(/\s+/g, "-")
|
|
48
|
+
.replaceAll(/[^a-z\d-]/g, "");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** The extension of a file name, including the dot, or `""`. */
|
|
52
|
+
export function extensionOfName(name: string): string {
|
|
53
|
+
const dot = name.lastIndexOf(".");
|
|
54
|
+
return dot > 0 ? name.slice(dot) : "";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The file name a field value and a pick compose to — three modes, not two.
|
|
59
|
+
*
|
|
60
|
+
* The verbatim STEM mode is not a convenience. The slugifier lowercases and strips everything
|
|
61
|
+
* outside `[a-z0-9-]`, which turns `[slug]` into `slug` — and eight shipped starters carry
|
|
62
|
+
* `pages/[slug].json`, `[sku].json`, `[...slug].json`. A picker that slugified would make a dynamic
|
|
63
|
+
* route uncreatable and would say nothing about it.
|
|
64
|
+
*
|
|
65
|
+
* @param input What the reader typed.
|
|
66
|
+
* @param picked The picker's value: an extension, {@link OTHER_FORMAT}, or `""` with no picker.
|
|
67
|
+
* @param choice How the extension is settled; absent takes the whole name verbatim.
|
|
68
|
+
*/
|
|
69
|
+
export function composeNewFileName(input: string, picked: string, choice?: FormatChoice): string {
|
|
70
|
+
const trimmed = input.trim();
|
|
71
|
+
if (choice === undefined || picked === OTHER_FORMAT) {
|
|
72
|
+
return trimmed;
|
|
73
|
+
}
|
|
74
|
+
if (choice.kind === "fixed") {
|
|
75
|
+
const slug = slugify(trimmed);
|
|
76
|
+
return slug === "" ? "" : `${slug}${choice.ext}`;
|
|
77
|
+
}
|
|
78
|
+
return trimmed.toLowerCase().endsWith(picked.toLowerCase()) ? trimmed : `${trimmed}${picked}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** What a name is judged against: where it is going, and what is already there. */
|
|
82
|
+
export interface NewFileNameCheck {
|
|
83
|
+
/** Destination directory, project-relative. `"."` is the project root. */
|
|
84
|
+
dir: string;
|
|
85
|
+
choice?: FormatChoice | undefined;
|
|
86
|
+
/** The destination's entries, LOWER-CASED: APFS and NTFS compare without case, and so does this. */
|
|
87
|
+
taken: ReadonlySet<string>;
|
|
88
|
+
/** Extensions that are documents, lower-cased (`knownDocumentExtensions()`). */
|
|
89
|
+
known?: ReadonlySet<string>;
|
|
90
|
+
/** The picker's rows, for naming a pick in a refusal. */
|
|
91
|
+
rows?: readonly { label: string; value: string }[] | null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The refusal a name earns, or `""` when it is usable.
|
|
96
|
+
*
|
|
97
|
+
* In order: blank, a display name that slugifies to nothing, a path that starts or ends with a
|
|
98
|
+
* slash, a `..` segment, a subfolder where the field is a name rather than a whole file name, an
|
|
99
|
+
* extension that contradicts the pick, a document smuggled through Other… past a collection's lock,
|
|
100
|
+
* and finally a name already taken in the destination. The first that applies is the sentence.
|
|
101
|
+
*
|
|
102
|
+
* @param value What the reader typed.
|
|
103
|
+
* @param picked The picker's value, as {@link composeNewFileName} takes it.
|
|
104
|
+
* @param check The destination and what it already holds.
|
|
105
|
+
*/
|
|
106
|
+
export function validateNewFileName(
|
|
107
|
+
value: string,
|
|
108
|
+
picked: string,
|
|
109
|
+
check: NewFileNameCheck,
|
|
110
|
+
): string {
|
|
111
|
+
const { choice, dir, known = new Set<string>(), rows = null, taken } = check;
|
|
112
|
+
const trimmed = value.trim();
|
|
113
|
+
if (!trimmed) {
|
|
114
|
+
return choice?.kind === "fixed" ? "Enter a name." : "Enter a file name.";
|
|
115
|
+
}
|
|
116
|
+
if (choice?.kind === "fixed" && slugify(trimmed) === "") {
|
|
117
|
+
return "Enter at least one letter or number.";
|
|
118
|
+
}
|
|
119
|
+
const normalized = trimmed.replaceAll("\\", "/");
|
|
120
|
+
if (normalized.startsWith("/") || normalized.endsWith("/")) {
|
|
121
|
+
return "Enter a name, not a path that starts or ends with a slash.";
|
|
122
|
+
}
|
|
123
|
+
if (normalized.split("/").includes("..")) {
|
|
124
|
+
return "A name cannot step outside the destination folder.";
|
|
125
|
+
}
|
|
126
|
+
// A slash is the tree's ONLY way to make a directory — both backends create them on write — so
|
|
127
|
+
// It is allowed exactly where the field is a whole file name, and refused where it is a name.
|
|
128
|
+
if (normalized.includes("/") && choice !== undefined && picked !== OTHER_FORMAT) {
|
|
129
|
+
return `Pick Other… to create a file in a subfolder of ${dir === "." ? "the project root" : dir}.`;
|
|
130
|
+
}
|
|
131
|
+
/* A name whose extension MATCHES the pick is fine and is composed once, never doubled;
|
|
132
|
+
refusing every trailing extension would reject this dialog's own `untitled.json` default.
|
|
133
|
+
A name ending in an extension no format claims is a stem that happens to have a dot. */
|
|
134
|
+
const typedExt = extensionOfName(normalized);
|
|
135
|
+
if (
|
|
136
|
+
picked !== "" &&
|
|
137
|
+
picked !== OTHER_FORMAT &&
|
|
138
|
+
typedExt !== "" &&
|
|
139
|
+
typedExt.toLowerCase() !== picked.toLowerCase() &&
|
|
140
|
+
known.has(typedExt.toLowerCase())
|
|
141
|
+
) {
|
|
142
|
+
const label = rows?.find((row) => row.value === picked)?.label ?? picked;
|
|
143
|
+
return `You picked ${label}; this name ends in ${typedExt}.`;
|
|
144
|
+
}
|
|
145
|
+
if (choice?.kind === "locked" && picked === OTHER_FORMAT) {
|
|
146
|
+
const foreign = typedExt !== "" && typedExt.toLowerCase() !== choice.ext.toLowerCase();
|
|
147
|
+
if (foreign && known.has(typedExt.toLowerCase())) {
|
|
148
|
+
return choice.because;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/* No emptiness check here: every way `composeNewFileName` can answer "" is already refused
|
|
152
|
+
above — a blank field by the first rule, a display name that slugifies to nothing by the
|
|
153
|
+
second. */
|
|
154
|
+
const candidate = composeNewFileName(value, picked, choice);
|
|
155
|
+
return taken.has(candidate.toLowerCase()) ? `${candidate} already exists in ${dir}/.` : "";
|
|
156
|
+
}
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
loadFormats,
|
|
21
21
|
} from "../format/format-host";
|
|
22
22
|
import { serializeJson } from "@jxsuite/schema/json-layout";
|
|
23
|
+
import { restoreBareRepeaters } from "../tabs/bare-repeaters";
|
|
23
24
|
import type { Tab } from "../tabs/tab";
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -40,15 +41,19 @@ export async function serializeDocument(tab: Tab): Promise<string> {
|
|
|
40
41
|
await loadFormats();
|
|
41
42
|
const sourceFormat = formatByName(tab.doc.sourceFormat);
|
|
42
43
|
if (sourceFormat?.capabilities.serialize) {
|
|
43
|
-
const fm = tab.doc.content
|
|
44
|
+
const fm = tab.doc.content.frontmatter;
|
|
44
45
|
const doc = tab.doc.document;
|
|
45
46
|
const fullDoc = { ...fm, ...doc, children: doc.children ?? [] };
|
|
46
|
-
|
|
47
|
+
// The text the file was read as, so what the author did not edit keeps its own spelling.
|
|
48
|
+
return formatSerialize(sourceFormat.name, fullDoc, {
|
|
49
|
+
mode: "roundtrip",
|
|
50
|
+
...(tab.doc.source !== null && { source: tab.doc.source }),
|
|
51
|
+
});
|
|
47
52
|
}
|
|
48
53
|
if (tab.doc.mode === "content") {
|
|
49
54
|
const format = defaultContentFormat();
|
|
50
55
|
if (format) {
|
|
51
|
-
const fm = tab.doc.content
|
|
56
|
+
const fm = tab.doc.content.frontmatter;
|
|
52
57
|
const hasFrontmatter = Object.keys(fm).length > 0;
|
|
53
58
|
const fullDoc = { ...fm, ...tab.doc.document };
|
|
54
59
|
return formatSerialize(format.name, fullDoc, {
|
|
@@ -62,6 +67,11 @@ export async function serializeDocument(tab: Tab): Promise<string> {
|
|
|
62
67
|
a blank line sits, which characters were escaped — and it is null for a document with no JSON
|
|
63
68
|
source, where every object expands and every array is laid by fit: the formatter's own answer
|
|
64
69
|
for stringified output. Either way the text ends with one newline, as every formatted file
|
|
65
|
-
does.
|
|
66
|
-
|
|
70
|
+
does. A repeater the file wrote as a node's whole `children` is written that way again
|
|
71
|
+
(`tabs/bare-repeaters.ts`), so the member form the editor works in never reaches the disk —
|
|
72
|
+
which is also what keeps the layout's pointers, taken from the file's own text, lined up. */
|
|
73
|
+
return serializeJson(
|
|
74
|
+
restoreBareRepeaters(tab.doc.document, tab.doc.bareRepeaters ?? null),
|
|
75
|
+
tab.doc.layout ?? null,
|
|
76
|
+
);
|
|
67
77
|
}
|
|
@@ -84,7 +84,7 @@ export function createNestingValidator(elements?: ElementsHints): NestingValidat
|
|
|
84
84
|
*
|
|
85
85
|
* Returned as a plain object because it crosses to the canvas frame with the render message.
|
|
86
86
|
*/
|
|
87
|
-
export function formatEditableVerdicts(elements?: ElementsHints)
|
|
87
|
+
export function formatEditableVerdicts(elements?: ElementsHints) {
|
|
88
88
|
const verdicts: Record<string, boolean> = {};
|
|
89
89
|
for (const [tag, rule] of Object.entries(elements?.nesting ?? {})) {
|
|
90
90
|
// "_root" is the document itself, not an element.
|
|
@@ -29,6 +29,10 @@ import {
|
|
|
29
29
|
import { convertTargetExtensions, convertTargets } from "./format-choices";
|
|
30
30
|
import { parseJsonDocument, serializeJson } from "@jxsuite/schema/json-layout";
|
|
31
31
|
import type { JsonLayout } from "@jxsuite/schema/json-layout";
|
|
32
|
+
import { isString } from "@jxsuite/schema/guards";
|
|
33
|
+
import type { JsonValue, JxMutableNode } from "@jxsuite/schema/types";
|
|
34
|
+
import type { CommandArgValues } from "../commands/command-args";
|
|
35
|
+
import type { FormatFrontmatter } from "./format-host";
|
|
32
36
|
import type { AnyCommand, CommandRegistry } from "../commands/registry";
|
|
33
37
|
|
|
34
38
|
/** What a conversion will do, resolved in full before the reader is asked to confirm it. */
|
|
@@ -72,14 +76,17 @@ function withExtension(path: string, ext: string): string {
|
|
|
72
76
|
async function readDocument(
|
|
73
77
|
path: string,
|
|
74
78
|
source: string,
|
|
75
|
-
): Promise<{ document:
|
|
79
|
+
): Promise<{ document: JxMutableNode; frontmatter: FormatFrontmatter }> {
|
|
76
80
|
const { formatForPath } = await import("./format-host");
|
|
77
81
|
if (formatForPath(path)) {
|
|
78
82
|
const { parseSourceForPath } = await import("../files/file-ops");
|
|
79
83
|
const parsed = await parseSourceForPath(path, source);
|
|
80
84
|
return { document: parsed.document, frontmatter: parsed.frontmatter };
|
|
81
85
|
}
|
|
82
|
-
|
|
86
|
+
/* SAFETY: a path no format claims is a `.json` document in pages/ or components/ (the only files
|
|
87
|
+
`convertTargets` offers a conversion for), and that file's schema requires a top-level object;
|
|
88
|
+
`JSON.parse` without a reviver yields a JSON value, so the object is a Jx document. */
|
|
89
|
+
return { document: JSON.parse(source) as JxMutableNode, frontmatter: {} };
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
/**
|
|
@@ -94,8 +101,8 @@ async function readDocument(
|
|
|
94
101
|
*/
|
|
95
102
|
async function writeDocument(
|
|
96
103
|
targetExt: string,
|
|
97
|
-
document:
|
|
98
|
-
frontmatter:
|
|
104
|
+
document: JxMutableNode,
|
|
105
|
+
frontmatter: FormatFrontmatter,
|
|
99
106
|
): Promise<string> {
|
|
100
107
|
const merged = { ...frontmatter, ...document };
|
|
101
108
|
if (targetExt === ".json") {
|
|
@@ -194,7 +201,9 @@ export async function buildPlan(
|
|
|
194
201
|
errors, which is indistinguishable from a valid document. Claiming validity on the strength of
|
|
195
202
|
a schema that was never compiled is the "never render 0" defect one layer down. */
|
|
196
203
|
const { validateDocOrNull } = await import("../services/jx-validate");
|
|
197
|
-
|
|
204
|
+
/* SAFETY: `text` is what `serializeJson` just wrote, and `JSON.parse` without a reviver yields
|
|
205
|
+
a JSON value. */
|
|
206
|
+
const errors = await validateDocOrNull(JSON.parse(text) as JsonValue);
|
|
198
207
|
if (errors === null) {
|
|
199
208
|
lines.push(
|
|
200
209
|
"The result could not be checked against the document schema — that is not the same as " +
|
|
@@ -292,7 +301,9 @@ export async function convertFile(path: string, targetExt?: string): Promise<str
|
|
|
292
301
|
|
|
293
302
|
let original: string;
|
|
294
303
|
try {
|
|
295
|
-
|
|
304
|
+
// The read crosses the PAL's wire, which can answer without a `content` field: read that as empty.
|
|
305
|
+
const read = await platform.readFile(path);
|
|
306
|
+
original = isString(read) ? read : "";
|
|
296
307
|
} catch (error) {
|
|
297
308
|
return fail(`Could not read ${path}.`, errorMessage(error));
|
|
298
309
|
}
|
|
@@ -373,8 +384,8 @@ async function rebuildTab(from: string, to: string, text: string): Promise<void>
|
|
|
373
384
|
const { formatForPath } = await import("./format-host");
|
|
374
385
|
renameTab(from, to, to);
|
|
375
386
|
const format = formatForPath(to);
|
|
376
|
-
let document:
|
|
377
|
-
let frontmatter:
|
|
387
|
+
let document: JxMutableNode;
|
|
388
|
+
let frontmatter: FormatFrontmatter | undefined;
|
|
378
389
|
// The layout of the JSON just written, as every other JSON reader records it (`files/files.ts`,
|
|
379
390
|
// `files/file-ops.ts`): the text is the serializer's own, so the record says what a layout-less
|
|
380
391
|
// Write would do anyway — but the tab reads its file rather than assuming it.
|
|
@@ -392,14 +403,17 @@ async function rebuildTab(from: string, to: string, text: string): Promise<void>
|
|
|
392
403
|
// Points at the file, and reopening it is one click.
|
|
393
404
|
return;
|
|
394
405
|
}
|
|
395
|
-
openTab
|
|
406
|
+
const options: Parameters<typeof openTab>[0] = {
|
|
396
407
|
document,
|
|
397
408
|
documentPath: to,
|
|
398
|
-
...(frontmatter === undefined ? {} : { frontmatter }),
|
|
399
409
|
id: to,
|
|
400
410
|
layout,
|
|
401
411
|
sourceFormat: format?.name ?? null,
|
|
402
|
-
}
|
|
412
|
+
};
|
|
413
|
+
if (frontmatter !== undefined) {
|
|
414
|
+
options.frontmatter = frontmatter;
|
|
415
|
+
}
|
|
416
|
+
openTab(options);
|
|
403
417
|
}
|
|
404
418
|
|
|
405
419
|
const CANNOT_CONVERT =
|
|
@@ -499,8 +513,8 @@ export function fileFormatCommands(): AnyCommand[] {
|
|
|
499
513
|
undo: "none",
|
|
500
514
|
/* No `aiTool`, by studio-ui-guidelines.md §12.4's second deletion rule: `run` awaits a
|
|
501
515
|
confirm dialog the person answers. */
|
|
502
|
-
run: async (_ctx, args:
|
|
503
|
-
const target =
|
|
516
|
+
run: async (_ctx, args: CommandArgValues) => {
|
|
517
|
+
const target = isString(args.format) ? args.format : undefined;
|
|
504
518
|
await convertFile(
|
|
505
519
|
stringArg("file.convertFormat", args, "source"),
|
|
506
520
|
...(target === undefined ? [] : [target]),
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* context menu is built during a render and cannot await anything.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { isString } from "@jxsuite/schema/guards";
|
|
14
15
|
import { collectionForFile } from "../content/collection-match";
|
|
15
16
|
import { documentExtensions, formatByExtension, formatForPath, getFormats } from "./format-host";
|
|
16
17
|
import { projectState } from "../store";
|
|
@@ -76,7 +77,7 @@ function declaresDocumentKind(kinds: readonly string[]): boolean {
|
|
|
76
77
|
*/
|
|
77
78
|
function isLayoutPath(path: string): boolean {
|
|
78
79
|
const declared = projectState?.projectConfig?.layout;
|
|
79
|
-
if (
|
|
80
|
+
if (!isString(declared)) {
|
|
80
81
|
return false;
|
|
81
82
|
}
|
|
82
83
|
return normalize(declared).toLowerCase() === normalize(path).toLowerCase();
|
|
@@ -9,8 +9,15 @@
|
|
|
9
9
|
|
|
10
10
|
import { setProjectSchemasForMonaco } from "../services/monaco-lazy";
|
|
11
11
|
import { getPlatform } from "../platform";
|
|
12
|
-
import type {
|
|
13
|
-
|
|
12
|
+
import type {
|
|
13
|
+
ExtensionCatalogEntry,
|
|
14
|
+
ExtensionsInfo,
|
|
15
|
+
FormatActionRequest,
|
|
16
|
+
ProjectSchemasResponse,
|
|
17
|
+
StudioPlatform,
|
|
18
|
+
} from "../types";
|
|
19
|
+
import { isString } from "@jxsuite/schema/guards";
|
|
20
|
+
import type { JxDocumentValue, JxMutableNode } from "@jxsuite/schema/types";
|
|
14
21
|
|
|
15
22
|
export interface StudioFormatHints {
|
|
16
23
|
icon?: string;
|
|
@@ -25,6 +32,10 @@ export interface StudioFormatHints {
|
|
|
25
32
|
inline?: string[];
|
|
26
33
|
void?: string[];
|
|
27
34
|
textOnly?: string[];
|
|
35
|
+
/**
|
|
36
|
+
* Per-parent child rules. The registry arrives over the wire unvalidated, so a rule may be a
|
|
37
|
+
* JSON `null`, which every reader treats as a tag the format leaves undeclared.
|
|
38
|
+
*/
|
|
28
39
|
nesting?: Record<
|
|
29
40
|
string,
|
|
30
41
|
{
|
|
@@ -32,10 +43,9 @@ export interface StudioFormatHints {
|
|
|
32
43
|
inline?: boolean;
|
|
33
44
|
directive?: boolean;
|
|
34
45
|
only?: string[];
|
|
35
|
-
}
|
|
46
|
+
} | null
|
|
36
47
|
>;
|
|
37
48
|
};
|
|
38
|
-
[key: string]: unknown;
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
export interface StudioFormat {
|
|
@@ -57,10 +67,11 @@ export function loadFormats(): Promise<StudioFormat[]> {
|
|
|
57
67
|
if (!_loaded) {
|
|
58
68
|
_loaded = (async () => {
|
|
59
69
|
try {
|
|
60
|
-
const platform = getPlatform()
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
70
|
+
const platform = getPlatform();
|
|
71
|
+
/* SAFETY: every backend's formats route serializes its format registry as the records this
|
|
72
|
+
Module declares (`StudioFormat`); the PAL types the payload `unknown[]` only because this
|
|
73
|
+
Module, not `types.ts`, owns that shape. */
|
|
74
|
+
_formats = ((await platform.listFormats?.()) as StudioFormat[] | undefined) ?? [];
|
|
64
75
|
} catch {
|
|
65
76
|
_formats = [];
|
|
66
77
|
}
|
|
@@ -100,9 +111,7 @@ export function loadExtensions(): Promise<ExtensionsInfo[]> {
|
|
|
100
111
|
if (!_extensionsLoaded) {
|
|
101
112
|
_extensionsLoaded = (async () => {
|
|
102
113
|
try {
|
|
103
|
-
const platform = getPlatform()
|
|
104
|
-
listExtensions?: () => Promise<ExtensionsInfo[]>;
|
|
105
|
-
};
|
|
114
|
+
const platform = getPlatform();
|
|
106
115
|
_extensions = (await platform.listExtensions?.()) ?? [];
|
|
107
116
|
} catch {
|
|
108
117
|
_extensions = [];
|
|
@@ -144,9 +153,7 @@ export function loadExtensionCatalog(): Promise<ExtensionCatalogEntry[]> {
|
|
|
144
153
|
if (!_catalogLoaded) {
|
|
145
154
|
_catalogLoaded = (async () => {
|
|
146
155
|
try {
|
|
147
|
-
const platform = getPlatform()
|
|
148
|
-
listExtensionCatalog?: () => Promise<ExtensionCatalogEntry[]>;
|
|
149
|
-
};
|
|
156
|
+
const platform = getPlatform();
|
|
150
157
|
_catalog = (await platform.listExtensionCatalog?.()) ?? [];
|
|
151
158
|
} catch {
|
|
152
159
|
_catalog = [];
|
|
@@ -180,10 +187,10 @@ export function setExtensionCatalog(catalog: ExtensionCatalogEntry[]) {
|
|
|
180
187
|
* @param {object} platform - The studio platform (only `fetchProjectSchemas` is consulted)
|
|
181
188
|
* @returns {Promise<void>} Resolves once both consumers have been updated
|
|
182
189
|
*/
|
|
183
|
-
async function shareProjectSchemas(
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
let schemas:
|
|
190
|
+
async function shareProjectSchemas(
|
|
191
|
+
platform: Pick<StudioPlatform, "fetchProjectSchemas">,
|
|
192
|
+
): Promise<void> {
|
|
193
|
+
let schemas: ProjectSchemasResponse | null = null;
|
|
187
194
|
try {
|
|
188
195
|
schemas = (await platform.fetchProjectSchemas?.()) ?? null;
|
|
189
196
|
} catch {
|
|
@@ -208,9 +215,7 @@ async function shareProjectSchemas(platform: {
|
|
|
208
215
|
*
|
|
209
216
|
* @param {object} platform - The studio platform (only `fetchProjectSchemas` is consulted here)
|
|
210
217
|
*/
|
|
211
|
-
export function refreshExtensionUi(platform: {
|
|
212
|
-
fetchProjectSchemas?: () => Promise<{ project?: unknown; document?: unknown }>;
|
|
213
|
-
}): void {
|
|
218
|
+
export function refreshExtensionUi(platform: Pick<StudioPlatform, "fetchProjectSchemas">): void {
|
|
214
219
|
void shareProjectSchemas(platform);
|
|
215
220
|
// `installed` and `bundled` are per-project facts, so an install or a project switch can move
|
|
216
221
|
// Them even when the shipped half has not changed.
|
|
@@ -279,15 +284,40 @@ export function documentExtensions(kind?: string): string[] {
|
|
|
279
284
|
return [...exts];
|
|
280
285
|
}
|
|
281
286
|
|
|
287
|
+
/**
|
|
288
|
+
* The extensions the build routes under `pages/`: `.json`, and every extension a page format claims
|
|
289
|
+
* (`discoverPages` in `@jxsuite/compiler` walks with the same set). Waits for the registry, so a
|
|
290
|
+
* Markdown page is never judged unroutable because the formats had not landed yet.
|
|
291
|
+
*/
|
|
292
|
+
export async function routedPageExtensions(): Promise<string[]> {
|
|
293
|
+
await loadFormats();
|
|
294
|
+
return [".json", ...documentExtensions("page")];
|
|
295
|
+
}
|
|
296
|
+
|
|
282
297
|
/** The default format for new content documents (first content-kind serializer). */
|
|
283
298
|
export function defaultContentFormat(): StudioFormat | undefined {
|
|
284
299
|
return _formats.find((f) => f.capabilities.serialize && f.documentKinds.includes("content"));
|
|
285
300
|
}
|
|
286
301
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
302
|
+
/** The options a format capability is invoked with. */
|
|
303
|
+
export interface FormatActionOptions {
|
|
304
|
+
/** `"roundtrip"` asks the format to preserve what it can of the source's own spelling. */
|
|
305
|
+
mode?: string;
|
|
306
|
+
/** Whether the serializer writes a frontmatter block. */
|
|
307
|
+
frontmatter?: boolean;
|
|
308
|
+
/**
|
|
309
|
+
* The text the document was read from: a roundtrip serializer keeps the spelling of whatever did
|
|
310
|
+
* not change (`@jxsuite/parser`'s `source` option).
|
|
311
|
+
*/
|
|
312
|
+
source?: string;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Send one request to the format route (POST /__studio/format, or the desktop RPC of the same
|
|
317
|
+
* name). A serialize request carries the caller's document through unchanged.
|
|
318
|
+
*/
|
|
319
|
+
async function formatAction(payload: FormatActionRequest): Promise<JxMutableNode | string> {
|
|
320
|
+
const platform = getPlatform();
|
|
291
321
|
if (!platform.formatAction) {
|
|
292
322
|
throw new Error("Platform does not support format actions");
|
|
293
323
|
}
|
|
@@ -298,8 +328,11 @@ async function formatAction(payload: Record<string, unknown>): Promise<unknown>
|
|
|
298
328
|
export async function formatParse(
|
|
299
329
|
name: string,
|
|
300
330
|
source: string,
|
|
301
|
-
options?:
|
|
331
|
+
options?: FormatActionOptions,
|
|
302
332
|
): Promise<JxMutableNode> {
|
|
333
|
+
/* SAFETY: a format class's `parse` capability returns the Jx document it built from the source —
|
|
334
|
+
The contract every format class implements (specs/extensions.md) — and nothing here inspects
|
|
335
|
+
It before `splitFormatDocument` reads its keys. */
|
|
303
336
|
return (await formatAction({
|
|
304
337
|
action: "parse",
|
|
305
338
|
format: name,
|
|
@@ -308,12 +341,19 @@ export async function formatParse(
|
|
|
308
341
|
})) as JxMutableNode;
|
|
309
342
|
}
|
|
310
343
|
|
|
311
|
-
/**
|
|
344
|
+
/**
|
|
345
|
+
* Serialize a Jx document to source text via the named format's serialize capability.
|
|
346
|
+
*
|
|
347
|
+
* `doc` is relayed to the format class verbatim: the document merged with its frontmatter, whose
|
|
348
|
+
* open keys are whatever the format's own parser produced.
|
|
349
|
+
*/
|
|
312
350
|
export async function formatSerialize(
|
|
313
351
|
name: string,
|
|
314
|
-
doc:
|
|
315
|
-
options?:
|
|
352
|
+
doc: JxMutableNode,
|
|
353
|
+
options?: FormatActionOptions,
|
|
316
354
|
): Promise<string> {
|
|
355
|
+
/* SAFETY: a format class's `serialize` capability returns the source text it wrote — the contract
|
|
356
|
+
Every format class implements (specs/extensions.md) — so the answer is a string. */
|
|
317
357
|
return (await formatAction({
|
|
318
358
|
action: "serialize",
|
|
319
359
|
doc,
|
|
@@ -322,42 +362,47 @@ export async function formatSerialize(
|
|
|
322
362
|
})) as string;
|
|
323
363
|
}
|
|
324
364
|
|
|
365
|
+
/** A document's frontmatter: the open keys of a Jx node, as a format's parser left them. */
|
|
366
|
+
export type FormatFrontmatter = Record<string, JxDocumentValue | undefined>;
|
|
367
|
+
|
|
368
|
+
/** What {@link splitFormatDocument} makes of a parsed format document. */
|
|
369
|
+
export interface SplitFormatDocument {
|
|
370
|
+
document: JxMutableNode;
|
|
371
|
+
frontmatter: FormatFrontmatter;
|
|
372
|
+
mode: "component" | "content";
|
|
373
|
+
}
|
|
374
|
+
|
|
325
375
|
/**
|
|
326
376
|
* Split a parsed format document into { document, frontmatter, mode } per the format's
|
|
327
377
|
* `$studio.documentMode` hints. Component documents (e.g. a frontmatter `tagName` with a hyphen)
|
|
328
378
|
* keep their full shape; content documents separate frontmatter metadata from the body children.
|
|
329
379
|
*/
|
|
330
|
-
export function splitFormatDocument(
|
|
380
|
+
export function splitFormatDocument(
|
|
381
|
+
format: StudioFormat | undefined,
|
|
382
|
+
doc: JxMutableNode,
|
|
383
|
+
): SplitFormatDocument {
|
|
331
384
|
const hints = format?.studio?.documentMode;
|
|
332
385
|
const componentWhen = hints?.componentWhen;
|
|
333
386
|
if (componentWhen?.frontmatterKey) {
|
|
334
387
|
const value = doc[componentWhen.frontmatterKey];
|
|
335
388
|
const pattern = componentWhen.matches ? new RegExp(componentWhen.matches) : /./;
|
|
336
|
-
if (
|
|
337
|
-
return {
|
|
338
|
-
document: doc,
|
|
339
|
-
frontmatter: {} as Record<string, unknown>,
|
|
340
|
-
mode: "component",
|
|
341
|
-
};
|
|
389
|
+
if (isString(value) && pattern.test(value)) {
|
|
390
|
+
return { document: doc, frontmatter: {}, mode: "component" };
|
|
342
391
|
}
|
|
343
392
|
}
|
|
344
393
|
if (hints?.default === "component") {
|
|
345
|
-
return {
|
|
346
|
-
document: doc,
|
|
347
|
-
frontmatter: {} as Record<string, unknown>,
|
|
348
|
-
mode: "component",
|
|
349
|
-
};
|
|
394
|
+
return { document: doc, frontmatter: {}, mode: "component" };
|
|
350
395
|
}
|
|
351
396
|
|
|
352
397
|
// Content document — children form the root-level body; other keys are frontmatter
|
|
353
|
-
const children =
|
|
354
|
-
if (children.length === 0) {
|
|
398
|
+
const children = doc.children ?? [];
|
|
399
|
+
if (Array.isArray(children) && children.length === 0) {
|
|
355
400
|
children.push({ children: [], tagName: "p" });
|
|
356
401
|
}
|
|
357
402
|
|
|
358
403
|
const documentKeys = new Set(["state", "imports"]);
|
|
359
|
-
const contentDoc:
|
|
360
|
-
const frontmatter:
|
|
404
|
+
const contentDoc: JxMutableNode = { children };
|
|
405
|
+
const frontmatter: FormatFrontmatter = {};
|
|
361
406
|
for (const [key, value] of Object.entries(doc)) {
|
|
362
407
|
if (key === "children") {
|
|
363
408
|
continue;
|
|
@@ -369,9 +414,5 @@ export function splitFormatDocument(format: StudioFormat | undefined, doc: JxMut
|
|
|
369
414
|
}
|
|
370
415
|
}
|
|
371
416
|
|
|
372
|
-
return {
|
|
373
|
-
document: contentDoc as JxMutableNode,
|
|
374
|
-
frontmatter,
|
|
375
|
-
mode: "content",
|
|
376
|
-
};
|
|
417
|
+
return { document: contentDoc, frontmatter, mode: "content" };
|
|
377
418
|
}
|
|
@@ -18,6 +18,14 @@
|
|
|
18
18
|
|
|
19
19
|
const STORAGE_KEY = "jx_github_token";
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* The GitHub OAuth app every Jx sign-in authorizes against: the browser's device flow in
|
|
23
|
+
* `github-auth.ts`, and the desktop launcher's RFC 8252 loopback redirect
|
|
24
|
+
* (`packages/desktop/src/github-signin.ts`). Here, in the module with no UI dependency, because the
|
|
25
|
+
* launcher's main process imports it too.
|
|
26
|
+
*/
|
|
27
|
+
export const GITHUB_OAUTH_CLIENT_ID = "Ov23liYVlMFpgjOEPXJH";
|
|
28
|
+
|
|
21
29
|
/**
|
|
22
30
|
* The desktop launcher's RFC 8252 sign-in, when this build is running inside one.
|
|
23
31
|
*
|
|
@@ -30,13 +38,29 @@ export interface NativeGithubAuth {
|
|
|
30
38
|
status: () => Promise<{ stored: boolean }>;
|
|
31
39
|
}
|
|
32
40
|
|
|
33
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Whether the registered platform object carries a launcher's `githubAuth` member.
|
|
43
|
+
*
|
|
44
|
+
* Presence is the whole test, as it always was: the member is written only by a launcher's adapter
|
|
45
|
+
* (`packages/desktop/src/platform.ts`), which installs a {@link NativeGithubAuth}, and a browser
|
|
46
|
+
* build has no member at all.
|
|
47
|
+
*/
|
|
48
|
+
function carriesNativeAuth(platform: unknown): platform is { githubAuth: NativeGithubAuth } {
|
|
34
49
|
return (
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
typeof platform === "object" &&
|
|
51
|
+
platform !== null &&
|
|
52
|
+
"githubAuth" in platform &&
|
|
53
|
+
platform.githubAuth != null
|
|
37
54
|
);
|
|
38
55
|
}
|
|
39
56
|
|
|
57
|
+
export function nativeAuth(): NativeGithubAuth | null {
|
|
58
|
+
// `globalThis` as a plain object: its declared type knows nothing of the launcher's slot.
|
|
59
|
+
const host: object = globalThis;
|
|
60
|
+
const platform = "__jxPlatform" in host ? host.__jxPlatform : undefined;
|
|
61
|
+
return carriesNativeAuth(platform) ? platform.githubAuth : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
40
64
|
/**
|
|
41
65
|
* The token the desktop store handed us this session.
|
|
42
66
|
*
|