@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
package/src/files/files.ts
CHANGED
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
dropTargetForElements,
|
|
60
60
|
monitorForElements,
|
|
61
61
|
} from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
|
|
62
|
+
import type { ElementDragPayload } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
|
|
62
63
|
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
|
|
63
64
|
import {
|
|
64
65
|
PRIMARY_PANE,
|
|
@@ -79,6 +80,7 @@ import { runActiveReported, runReported } from "../commands/run-reported";
|
|
|
79
80
|
import { collectionOfPath } from "../content/entry-model";
|
|
80
81
|
import { confirmFileDelete, parseSourceForPath, renamePromptMessage } from "./file-ops";
|
|
81
82
|
import { parseJsonDocument } from "@jxsuite/schema/json-layout";
|
|
83
|
+
import { normalizeDocument } from "../tabs/bare-repeaters";
|
|
82
84
|
// The pure half of the differ: order-insensitive equality with no Yjs behind it.
|
|
83
85
|
import { deepEqual } from "@jxsuite/collab/diff-core";
|
|
84
86
|
import type { JsonLayout } from "@jxsuite/schema/json-layout";
|
|
@@ -93,15 +95,31 @@ import {
|
|
|
93
95
|
refreshFormats,
|
|
94
96
|
} from "../format/format-host";
|
|
95
97
|
import { convertTargets, creationFormats, knownDocumentExtensions } from "../format/format-choices";
|
|
98
|
+
import {
|
|
99
|
+
composeNewFileName,
|
|
100
|
+
extensionOfName,
|
|
101
|
+
OTHER_FORMAT,
|
|
102
|
+
validateNewFileName,
|
|
103
|
+
} from "./new-file-name";
|
|
104
|
+
import type { FormatChoice, NewFileNameCheck } from "./new-file-name";
|
|
96
105
|
import { collectionForDirectory } from "../content/collection-match";
|
|
97
|
-
import {
|
|
106
|
+
import {
|
|
107
|
+
markSessionRestored,
|
|
108
|
+
persistedSession,
|
|
109
|
+
persistedWorkspace,
|
|
110
|
+
resetProjectShell,
|
|
111
|
+
} from "../shell";
|
|
112
|
+
import type { WorkspaceId } from "../workspace/workspace-ids";
|
|
98
113
|
import { restoreSession } from "../workspace/session";
|
|
99
|
-
import {
|
|
114
|
+
import { cleanupSourceControl } from "../publish/source-control";
|
|
100
115
|
import { addRecentProject, trackRecentFile } from "../recent-projects";
|
|
101
116
|
import { listWindow, revealListRow, watchListWindow } from "../ui/virtual-window";
|
|
102
|
-
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
117
|
+
import type { JsonObject, JxMutableNode } from "@jxsuite/schema/types";
|
|
118
|
+
import { isString } from "@jxsuite/schema/guards";
|
|
119
|
+
import type { CommandArgValues } from "../commands/command-args";
|
|
120
|
+
import type { FormatFrontmatter } from "../format/format-host";
|
|
103
121
|
import type { ResolvedI18n } from "@jxsuite/schema/locale";
|
|
104
|
-
import type { ChoiceOption } from "../ui/layers";
|
|
122
|
+
import type { ChoiceOption, PromptDialogOptions } from "../ui/layers";
|
|
105
123
|
import { emptyFilesPanelValues, mountFilesPanelSurface } from "../surfaces/files-panel";
|
|
106
124
|
import { openMenu } from "../surfaces/menu";
|
|
107
125
|
import type {
|
|
@@ -136,6 +154,65 @@ export async function loadDirectory(dirPath: string) {
|
|
|
136
154
|
}
|
|
137
155
|
}
|
|
138
156
|
|
|
157
|
+
/** The project directories the Library scans and the tree opens expanded, when present. */
|
|
158
|
+
const CONVENTIONAL_DIRS = new Set([
|
|
159
|
+
"pages",
|
|
160
|
+
"layouts",
|
|
161
|
+
"components",
|
|
162
|
+
"content",
|
|
163
|
+
"data",
|
|
164
|
+
"public",
|
|
165
|
+
"styles",
|
|
166
|
+
]);
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Load the project root, expand and load each conventional directory it holds, and record them as
|
|
170
|
+
* `projectDirs` — the list the Library scans.
|
|
171
|
+
*
|
|
172
|
+
* One function because there are four doors into a project (the folder picker, a recent project,
|
|
173
|
+
* the `?project=` boot and the root probe a fresh window runs), and each used to carry its own copy
|
|
174
|
+
* of this walk. Two of the copies expanded the directories without recording them, so on desktop
|
|
175
|
+
* the file tree was full and the Library said the project had no files.
|
|
176
|
+
*/
|
|
177
|
+
export async function loadProjectDirectories(): Promise<void> {
|
|
178
|
+
if (!projectState) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
await loadDirectory(".");
|
|
182
|
+
const found: string[] = [];
|
|
183
|
+
for (const entry of projectState.dirs.get(".") ?? []) {
|
|
184
|
+
if (entry.type === "directory" && CONVENTIONAL_DIRS.has(entry.name)) {
|
|
185
|
+
found.push(entry.name);
|
|
186
|
+
projectState.expanded.add(entry.path || entry.name);
|
|
187
|
+
await loadDirectory(entry.path || entry.name);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
projectState.projectDirs = found;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Re-read the project's directories when a write created a conventional one the list lacks.
|
|
195
|
+
*
|
|
196
|
+
* The list is read as the project opens, so a project that had no `pages/` or `content/` then does
|
|
197
|
+
* not grow one in the list when Add new writes its first page or collection, and the Content scan
|
|
198
|
+
* walks only what the list names. Resolves `true` when the list was re-read, so the caller knows a
|
|
199
|
+
* repaint is due; `false` (and no listing at all) when every written path was already covered.
|
|
200
|
+
*
|
|
201
|
+
* @param paths Project-relative paths just written.
|
|
202
|
+
*/
|
|
203
|
+
export async function refreshProjectDirectoriesFor(paths: readonly string[]): Promise<boolean> {
|
|
204
|
+
const known = new Set(projectState?.projectDirs);
|
|
205
|
+
const missing = paths.some((path) => {
|
|
206
|
+
const top = path.replaceAll("\\", "/").replace(/^\.\//, "").split("/")[0] ?? "";
|
|
207
|
+
return path.includes("/") && CONVENTIONAL_DIRS.has(top) && !known.has(top);
|
|
208
|
+
});
|
|
209
|
+
if (!missing) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
await loadProjectDirectories();
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
|
|
139
216
|
/**
|
|
140
217
|
* Whether the tree currently draws the files `.gitignore` masks.
|
|
141
218
|
*
|
|
@@ -196,7 +273,7 @@ export async function loadProject() {
|
|
|
196
273
|
await loadFormats();
|
|
197
274
|
refreshExtensionUi(platform);
|
|
198
275
|
}
|
|
199
|
-
await
|
|
276
|
+
await loadProjectDirectories();
|
|
200
277
|
await loadComponentRegistry();
|
|
201
278
|
await openLastSessionOrHome();
|
|
202
279
|
void maybePromptJxsuiteUpdate(meta.root);
|
|
@@ -216,13 +293,19 @@ export async function loadProject() {
|
|
|
216
293
|
* the window on the project it already had, and the caller announces the outcome — "Opening the
|
|
217
294
|
* project…" over a dialog the user just dismissed is a report of something that did not happen.
|
|
218
295
|
*
|
|
219
|
-
* @param
|
|
296
|
+
* @param ctx.renderLeftPanel Repaint the Navigator.
|
|
297
|
+
* @param ctx.resetProjectCaches Drop the caches panels above this module keep for the project being
|
|
298
|
+
* left (the Site panel's index, the media listing). Injected for the reason `fs-events.ts` takes
|
|
299
|
+
* `invalidateDerivedCaches`: those panels import from the file layer, so reaching up is a cycle,
|
|
300
|
+
* and `studio.ts`'s `openRecentProject` runs the same reset.
|
|
220
301
|
* @returns {Promise<boolean>} Whether the window changed project.
|
|
221
302
|
*/
|
|
222
303
|
export async function openProject({
|
|
223
304
|
renderLeftPanel,
|
|
305
|
+
resetProjectCaches,
|
|
224
306
|
}: {
|
|
225
307
|
renderLeftPanel: () => void;
|
|
308
|
+
resetProjectCaches?: () => void;
|
|
226
309
|
}): Promise<boolean> {
|
|
227
310
|
try {
|
|
228
311
|
const platform = getPlatform();
|
|
@@ -268,35 +351,17 @@ export async function openProject({
|
|
|
268
351
|
await loadFormats();
|
|
269
352
|
refreshExtensionUi(platform);
|
|
270
353
|
}
|
|
271
|
-
await
|
|
354
|
+
await loadProjectDirectories();
|
|
272
355
|
await loadComponentRegistry();
|
|
273
356
|
|
|
274
|
-
// Auto-expand key directories and populate projectDirs for Browse view
|
|
275
|
-
const conventionalDirs = new Set([
|
|
276
|
-
"pages",
|
|
277
|
-
"layouts",
|
|
278
|
-
"components",
|
|
279
|
-
"content",
|
|
280
|
-
"data",
|
|
281
|
-
"public",
|
|
282
|
-
"styles",
|
|
283
|
-
]);
|
|
284
|
-
const entries = requireProjectState().dirs.get(".") || [];
|
|
285
|
-
const foundDirs = [];
|
|
286
|
-
for (const e of entries) {
|
|
287
|
-
if (e.type === "directory" && conventionalDirs.has(e.name)) {
|
|
288
|
-
foundDirs.push(e.name);
|
|
289
|
-
requireProjectState().expanded.add(e.path || e.name);
|
|
290
|
-
await loadDirectory(e.path || e.name);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
requireProjectState().projectDirs = foundDirs;
|
|
294
|
-
|
|
295
357
|
// Source control, the stylebook selection and the settings tab describe the project being
|
|
296
|
-
// Left behind. The poll timer goes with them; the
|
|
297
|
-
|
|
358
|
+
// Left behind. The poll timer goes with them; the Publish workspace re-arms it on its next mount.
|
|
359
|
+
cleanupSourceControl();
|
|
298
360
|
resetProjectShell();
|
|
299
|
-
|
|
361
|
+
resetProjectCaches?.();
|
|
362
|
+
/* The Navigator's panel is the workspace frame's, applied as the project opened (§22.3): Design
|
|
363
|
+
opens on the Site panel. Forcing Files here put a developer panel with no rail button on
|
|
364
|
+
screen and re-opened a Navigator the frame had hidden. */
|
|
300
365
|
addRecentProject(requireProjectState().name, requireProjectState().projectRoot);
|
|
301
366
|
renderLeftPanel();
|
|
302
367
|
// The project's name is permanent state in the status bar's PROJECT field now.
|
|
@@ -405,6 +470,31 @@ export async function openLastSessionOrHome(): Promise<boolean> {
|
|
|
405
470
|
// `projectState.projectRoot`, the recent-list entry. One reader of one field cannot disagree with
|
|
406
471
|
// The writer; three callers passing three spellings silently restore nothing.
|
|
407
472
|
const session = persistedSession(workspace.projectRoot);
|
|
473
|
+
/* The workspace it was left in, read BEFORE anything opens: every tab the restore activates
|
|
474
|
+
enters its own workspace, and that entry is persisted over the stored one. */
|
|
475
|
+
const left = persistedWorkspace(workspace.projectRoot);
|
|
476
|
+
const restored = await restoreSessionOrHome(session);
|
|
477
|
+
_showWorkspace?.(left);
|
|
478
|
+
return restored;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* How a project open shows the workspace it was left in, once its documents are open. Installed by
|
|
483
|
+
* the bootstrap (`workspace/workspace-switch.ts`'s `switchWorkspace`), because the switch opens
|
|
484
|
+
* homes this module must not import; unset, the restore leaves the workspace wherever its tabs
|
|
485
|
+
* did.
|
|
486
|
+
*/
|
|
487
|
+
let _showWorkspace: ((id: WorkspaceId) => void) | null = null;
|
|
488
|
+
|
|
489
|
+
/** Install (or with `null`, remove) what {@link openLastSessionOrHome} shows the workspace with. */
|
|
490
|
+
export function setSessionWorkspaceShow(show: ((id: WorkspaceId) => void) | null): void {
|
|
491
|
+
_showWorkspace = show;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/** The body of {@link openLastSessionOrHome}: the session, or the home page. */
|
|
495
|
+
async function restoreSessionOrHome(
|
|
496
|
+
session: ReturnType<typeof persistedSession>,
|
|
497
|
+
): Promise<boolean> {
|
|
408
498
|
// Read first, THEN allow writes: the persist effect fires the moment `workspace.projectRoot` is
|
|
409
499
|
// Set, and an empty workspace captured at that instant would overwrite the very record this line
|
|
410
500
|
// Reads. See `markSessionRestored`.
|
|
@@ -460,35 +550,6 @@ function fileIconName(name: string, type: string, expanded: boolean): string {
|
|
|
460
550
|
}
|
|
461
551
|
}
|
|
462
552
|
|
|
463
|
-
/**
|
|
464
|
-
* The Navigator no longer draws this panel with lit.
|
|
465
|
-
*
|
|
466
|
-
* The record below returns `nothing` and mounts `surfaces/files-panel.json` in `afterRender`, so
|
|
467
|
-
* this is a stub: it survives only because `NavigatorPanelDeps` still declares the injection and
|
|
468
|
-
* `studio.ts` still passes it. Both go in the change that deletes this.
|
|
469
|
-
*
|
|
470
|
-
* @deprecated The panel is `surfaces/files-panel.json`; call {@link mountFilesPanel}.
|
|
471
|
-
*/
|
|
472
|
-
export function renderFilesTemplate(): typeof nothing {
|
|
473
|
-
return nothing;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* Drag-and-drop is registered against the row elements the DOCUMENT creates, as they are created.
|
|
478
|
-
*
|
|
479
|
-
* The pass this replaced ran on a `requestAnimationFrame` after every repaint, found the tree and
|
|
480
|
-
* its rows by class, and re-registered every one of them — which is exactly the acquisition
|
|
481
|
-
* `studio-ui-guidelines.md` §9.4 objects to, and which a WINDOWED list makes wrong rather than
|
|
482
|
-
* merely wasteful: the rows are re-used for different files as the window slides, so a pass that
|
|
483
|
-
* arrives one frame late hands a file's drag source to whichever file is standing in its place.
|
|
484
|
-
* {@link mountFilesPanel} adopts each row through `onNodeCreated` instead.
|
|
485
|
-
*
|
|
486
|
-
* @deprecated Registration is `onNodeCreated`; this exists for the deps entry alone.
|
|
487
|
-
*/
|
|
488
|
-
export function registerFileTreeDnD(_ctx: { renderLeftPanel: () => void }): void {
|
|
489
|
-
// Intentionally nothing: see the note above.
|
|
490
|
-
}
|
|
491
|
-
|
|
492
553
|
// ─── The row model, and the window onto it ───────────────────────────────────
|
|
493
554
|
|
|
494
555
|
/**
|
|
@@ -1183,10 +1244,9 @@ export function canMoveInto(srcPath: string, targetDir: string): boolean {
|
|
|
1183
1244
|
}
|
|
1184
1245
|
|
|
1185
1246
|
/** {@link canMoveInto} for a ROW: only a directory has an inside to move something into. */
|
|
1186
|
-
function acceptsDrop(
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
);
|
|
1247
|
+
function acceptsDrop(source: ElementDragPayload, targetPath: string, targetType: string): boolean {
|
|
1248
|
+
const srcPath = source.data.path;
|
|
1249
|
+
return targetType === "directory" && isString(srcPath) && canMoveInto(srcPath, targetPath);
|
|
1190
1250
|
}
|
|
1191
1251
|
|
|
1192
1252
|
/**
|
|
@@ -1310,15 +1370,15 @@ function adoptFileRow(element: HTMLElement, path: string, type: string): void {
|
|
|
1310
1370
|
},
|
|
1311
1371
|
element,
|
|
1312
1372
|
getData({ source }) {
|
|
1313
|
-
return acceptsDrop(source
|
|
1373
|
+
return acceptsDrop(source, path, type)
|
|
1314
1374
|
? { targetDir: path, type: "file-tree-target" }
|
|
1315
1375
|
: { type: "file-tree-refused" };
|
|
1316
1376
|
},
|
|
1317
1377
|
onDrag({ source }) {
|
|
1318
|
-
setRowUnderDrag(path, acceptsDrop(source
|
|
1378
|
+
setRowUnderDrag(path, acceptsDrop(source, path, type));
|
|
1319
1379
|
},
|
|
1320
1380
|
onDragEnter({ source }) {
|
|
1321
|
-
setRowUnderDrag(path, acceptsDrop(source
|
|
1381
|
+
setRowUnderDrag(path, acceptsDrop(source, path, type));
|
|
1322
1382
|
},
|
|
1323
1383
|
onDragLeave() {
|
|
1324
1384
|
setRowUnderDrag("", false);
|
|
@@ -1361,7 +1421,8 @@ function adoptFileTreeElement(element: HTMLElement): void {
|
|
|
1361
1421
|
pointer — {@link syncRootDrop} — because the row is what the drop will reach. */
|
|
1362
1422
|
const rootCleanup = dropTargetForElements({
|
|
1363
1423
|
canDrop({ source }) {
|
|
1364
|
-
|
|
1424
|
+
const srcPath = source.data.path;
|
|
1425
|
+
return source.data.type === "file-tree" && isString(srcPath) && canMoveInto(srcPath, ".");
|
|
1365
1426
|
},
|
|
1366
1427
|
element,
|
|
1367
1428
|
getData() {
|
|
@@ -1393,9 +1454,11 @@ function adoptFileTreeElement(element: HTMLElement): void {
|
|
|
1393
1454
|
return;
|
|
1394
1455
|
}
|
|
1395
1456
|
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1457
|
+
// A file-tree row's drag source always carries its string `path`, and an accepting row's
|
|
1458
|
+
// Target its string `targetDir` (see `adoptFileRow`); anything else is not a move.
|
|
1459
|
+
const srcPath = source.data.path;
|
|
1460
|
+
const targetDirPath = target.data.targetDir;
|
|
1461
|
+
if (!isString(srcPath) || !isString(targetDirPath) || !canMoveInto(srcPath, targetDirPath)) {
|
|
1399
1462
|
return;
|
|
1400
1463
|
}
|
|
1401
1464
|
const fileName = srcPath.split("/").pop();
|
|
@@ -1520,8 +1583,8 @@ function dismissFileContextMenu(): void {
|
|
|
1520
1583
|
* Entry Form" is not offered on it. A command whose required arguments this row cannot answer is
|
|
1521
1584
|
* skipped — never rendered into a refusal the author cannot act on.
|
|
1522
1585
|
*/
|
|
1523
|
-
function fileRowFacts(entry: { path: string; type: string }):
|
|
1524
|
-
const facts:
|
|
1586
|
+
function fileRowFacts(entry: { path: string; type: string }): CommandArgValues {
|
|
1587
|
+
const facts: CommandArgValues = {};
|
|
1525
1588
|
if (entry.type === "directory") {
|
|
1526
1589
|
// The collection's own SOURCE ROOT, not any directory beneath it: "Edit Collection in Grid"
|
|
1527
1590
|
// Opens the whole collection, so offering it on `content/posts/2026` would be a row whose label
|
|
@@ -1579,13 +1642,11 @@ function placedFileRows(entry: { path: string; type: string }): MenuRowProjectio
|
|
|
1579
1642
|
const facts = fileRowFacts(entry);
|
|
1580
1643
|
const rows: MenuRowProjection[] = [];
|
|
1581
1644
|
for (const command of registry.forPlacement("context/file")) {
|
|
1582
|
-
const schema = command.args
|
|
1583
|
-
| { properties?: Record<string, unknown>; required?: readonly string[] }
|
|
1584
|
-
| undefined;
|
|
1645
|
+
const schema = command.args;
|
|
1585
1646
|
if (!(schema?.required ?? []).every((key) => key in facts)) {
|
|
1586
1647
|
continue;
|
|
1587
1648
|
}
|
|
1588
|
-
const args:
|
|
1649
|
+
const args: CommandArgValues = {};
|
|
1589
1650
|
for (const key of Object.keys(schema?.properties ?? {})) {
|
|
1590
1651
|
if (key in facts) {
|
|
1591
1652
|
args[key] = facts[key];
|
|
@@ -1708,29 +1769,6 @@ const BLANK_DOCUMENT = JSON.stringify(
|
|
|
1708
1769
|
2,
|
|
1709
1770
|
);
|
|
1710
1771
|
|
|
1711
|
-
/**
|
|
1712
|
-
* The picker row meaning "I will type the whole name myself".
|
|
1713
|
-
*
|
|
1714
|
-
* It is what keeps the format picker from being a cage. `New File…` is the only generic
|
|
1715
|
-
* file-creation affordance in Studio and both backends create intermediate directories on write, so
|
|
1716
|
-
* without this row `styles/main.css`, `public/robots.txt`, `.gitignore` and a `credits.txt` beside
|
|
1717
|
-
* a collection's images all become uncreatable — and the picker would have taken away more than it
|
|
1718
|
-
* gave. The picker stays the authority on the extension; this is one of its answers.
|
|
1719
|
-
*/
|
|
1720
|
-
export const OTHER_FORMAT = "__other__";
|
|
1721
|
-
|
|
1722
|
-
/** How the extension of a new file is settled. */
|
|
1723
|
-
export type FormatChoice =
|
|
1724
|
-
/** Picker over every creatable format, plus Other…. The field is a NAME, taken verbatim. */
|
|
1725
|
-
| { kind: "choose"; defaultExt?: string; docKind?: "page" | "component" | "content" }
|
|
1726
|
-
/**
|
|
1727
|
-
* Picker holding ONE format, plus Other… — the collection's own extension. The field is a NAME.
|
|
1728
|
-
* `because` is the sentence a refusal quotes when Other… would smuggle a foreign document in.
|
|
1729
|
-
*/
|
|
1730
|
-
| { kind: "locked"; ext: string; because: string }
|
|
1731
|
-
/** No picker. The field is a DISPLAY NAME, slugified, and `ext` is appended. */
|
|
1732
|
-
| { kind: "fixed"; ext: string };
|
|
1733
|
-
|
|
1734
1772
|
/**
|
|
1735
1773
|
* One creation, named.
|
|
1736
1774
|
*
|
|
@@ -1752,34 +1790,12 @@ export interface NewFileRequest {
|
|
|
1752
1790
|
* (`i18n.createTranslation` composes `about.json` from the file it is translating).
|
|
1753
1791
|
*/
|
|
1754
1792
|
format?: FormatChoice;
|
|
1755
|
-
/**
|
|
1756
|
-
|
|
1757
|
-
* file name, which is the only way a caller can seed a body whose FORMAT — or whose own tag name
|
|
1758
|
-
* — it does not know until the dialog resolves. Defaults to the resolved format's
|
|
1759
|
-
* `newFileTemplate`.
|
|
1760
|
-
*/
|
|
1761
|
-
content?:
|
|
1762
|
-
| string
|
|
1763
|
-
| ((ext: string, fileName: string) => string | undefined | Promise<string | undefined>);
|
|
1793
|
+
/** Body to write. Defaults to the resolved format's `newFileTemplate`. */
|
|
1794
|
+
content?: string;
|
|
1764
1795
|
/** Who is creating, for the Problem's `source` line. Defaults to "Files". */
|
|
1765
1796
|
source?: string;
|
|
1766
1797
|
}
|
|
1767
1798
|
|
|
1768
|
-
/** A display name, reduced to a file stem. Lowercase by design: this is a slug. */
|
|
1769
|
-
function slugify(input: string): string {
|
|
1770
|
-
return input
|
|
1771
|
-
.trim()
|
|
1772
|
-
.toLowerCase()
|
|
1773
|
-
.replaceAll(/\s+/g, "-")
|
|
1774
|
-
.replaceAll(/[^a-z\d-]/g, "");
|
|
1775
|
-
}
|
|
1776
|
-
|
|
1777
|
-
/** The extension of a file name, including the dot, or `""`. */
|
|
1778
|
-
function extensionOfName(name: string): string {
|
|
1779
|
-
const dot = name.lastIndexOf(".");
|
|
1780
|
-
return dot > 0 ? name.slice(dot) : "";
|
|
1781
|
-
}
|
|
1782
|
-
|
|
1783
1799
|
/** The two picker modes — the ones where the field asks for a NAME and the picker owns the suffix. */
|
|
1784
1800
|
type PickerChoice = Extract<FormatChoice, { kind: "choose" } | { kind: "locked" }>;
|
|
1785
1801
|
|
|
@@ -1841,14 +1857,7 @@ export async function createFileIn(request: NewFileRequest): Promise<string | nu
|
|
|
1841
1857
|
await loadFormats();
|
|
1842
1858
|
|
|
1843
1859
|
// One listing, before the field opens: the names it must refuse are known while typing.
|
|
1844
|
-
|
|
1845
|
-
try {
|
|
1846
|
-
const listing = await getPlatform().listDirectory(dir);
|
|
1847
|
-
taken = new Set(listing.map((entry) => entry.name.toLowerCase()));
|
|
1848
|
-
} catch {
|
|
1849
|
-
// A directory that cannot be listed is usually one that does not exist yet — the write below
|
|
1850
|
-
// Is the authority on whether that is a problem, and it reports with the real reason.
|
|
1851
|
-
}
|
|
1860
|
+
const taken = await listTakenNames(dir);
|
|
1852
1861
|
|
|
1853
1862
|
const picker = choice !== undefined && choice.kind !== "fixed" ? choice : null;
|
|
1854
1863
|
const rows = picker === null ? null : formatRowsFor(picker);
|
|
@@ -1857,108 +1866,40 @@ export async function createFileIn(request: NewFileRequest): Promise<string | nu
|
|
|
1857
1866
|
? ""
|
|
1858
1867
|
: initialPickFor(picker, rows, request.suggestedName ?? "");
|
|
1859
1868
|
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
*
|
|
1863
|
-
* The verbatim STEM mode is not a convenience. The slugifier lowercases and strips everything
|
|
1864
|
-
* outside `[a-z0-9-]`, which turns `[slug]` into `slug` — and eight shipped starters carry
|
|
1865
|
-
* `pages/[slug].json`, `[sku].json`, `[...slug].json`. A picker that slugified would make a
|
|
1866
|
-
* dynamic route uncreatable and would say nothing about it.
|
|
1867
|
-
*/
|
|
1868
|
-
const fileNameFor = (input: string, picked: string) => {
|
|
1869
|
-
const trimmed = input.trim();
|
|
1870
|
-
if (choice === undefined || picked === OTHER_FORMAT) {
|
|
1871
|
-
return trimmed;
|
|
1872
|
-
}
|
|
1873
|
-
if (choice.kind === "fixed") {
|
|
1874
|
-
const slug = slugify(trimmed);
|
|
1875
|
-
return slug === "" ? "" : `${slug}${choice.ext}`;
|
|
1876
|
-
}
|
|
1877
|
-
return trimmed.toLowerCase().endsWith(picked.toLowerCase()) ? trimmed : `${trimmed}${picked}`;
|
|
1878
|
-
};
|
|
1879
|
-
|
|
1880
|
-
const known = knownDocumentExtensions();
|
|
1881
|
-
|
|
1882
|
-
const validate = (value: string, picked: string) => {
|
|
1883
|
-
const trimmed = value.trim();
|
|
1884
|
-
if (!trimmed) {
|
|
1885
|
-
return choice?.kind === "fixed" ? "Enter a name." : "Enter a file name.";
|
|
1886
|
-
}
|
|
1887
|
-
if (choice?.kind === "fixed" && slugify(trimmed) === "") {
|
|
1888
|
-
return "Enter at least one letter or number.";
|
|
1889
|
-
}
|
|
1890
|
-
const normalized = trimmed.replaceAll("\\", "/");
|
|
1891
|
-
if (normalized.startsWith("/") || normalized.endsWith("/")) {
|
|
1892
|
-
return "Enter a name, not a path that starts or ends with a slash.";
|
|
1893
|
-
}
|
|
1894
|
-
if (normalized.split("/").includes("..")) {
|
|
1895
|
-
return "A name cannot step outside the destination folder.";
|
|
1896
|
-
}
|
|
1897
|
-
// A slash is the tree's ONLY way to make a directory — both backends create them on write — so
|
|
1898
|
-
// It is allowed exactly where the field is a whole file name, and refused where it is a name.
|
|
1899
|
-
if (normalized.includes("/") && choice !== undefined && picked !== OTHER_FORMAT) {
|
|
1900
|
-
return `Pick Other… to create a file in a subfolder of ${dir === "." ? "the project root" : dir}.`;
|
|
1901
|
-
}
|
|
1902
|
-
/* A name whose extension MATCHES the pick is fine and is composed once, never doubled;
|
|
1903
|
-
refusing every trailing extension would reject this dialog's own `untitled.json` default.
|
|
1904
|
-
A name ending in an extension no format claims is a stem that happens to have a dot. */
|
|
1905
|
-
const typedExt = extensionOfName(normalized);
|
|
1906
|
-
if (
|
|
1907
|
-
picked !== "" &&
|
|
1908
|
-
picked !== OTHER_FORMAT &&
|
|
1909
|
-
typedExt !== "" &&
|
|
1910
|
-
typedExt.toLowerCase() !== picked.toLowerCase() &&
|
|
1911
|
-
known.has(typedExt.toLowerCase())
|
|
1912
|
-
) {
|
|
1913
|
-
const label = rows?.find((row) => row.value === picked)?.label ?? picked;
|
|
1914
|
-
return `You picked ${label}; this name ends in ${typedExt}.`;
|
|
1915
|
-
}
|
|
1916
|
-
if (choice?.kind === "locked" && picked === OTHER_FORMAT) {
|
|
1917
|
-
const foreign = typedExt !== "" && typedExt.toLowerCase() !== choice.ext.toLowerCase();
|
|
1918
|
-
if (foreign && known.has(typedExt.toLowerCase())) {
|
|
1919
|
-
return choice.because;
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
/* No emptiness check here: every way `fileNameFor` can answer "" is already refused above — a
|
|
1923
|
-
blank field by the first rule, a display name that slugifies to nothing by the second. */
|
|
1924
|
-
const candidate = fileNameFor(value, picked);
|
|
1925
|
-
return taken.has(candidate.toLowerCase()) ? `${candidate} already exists in ${dir}/.` : "";
|
|
1926
|
-
};
|
|
1869
|
+
const check: NewFileNameCheck = { choice, dir, known: knownDocumentExtensions(), rows, taken };
|
|
1870
|
+
const validate = (value: string, picked: string) => validateNewFileName(value, picked, check);
|
|
1927
1871
|
|
|
1928
1872
|
let picked = initialPick;
|
|
1929
|
-
const
|
|
1930
|
-
...(rows === null
|
|
1931
|
-
? {}
|
|
1932
|
-
: {
|
|
1933
|
-
choice: {
|
|
1934
|
-
initial: initialPick,
|
|
1935
|
-
label: "Format",
|
|
1936
|
-
onChange: (next: string) => {
|
|
1937
|
-
picked = next;
|
|
1938
|
-
},
|
|
1939
|
-
options: () => rows,
|
|
1940
|
-
},
|
|
1941
|
-
}),
|
|
1873
|
+
const promptOptions: PromptDialogOptions = {
|
|
1942
1874
|
confirmLabel: "Create",
|
|
1943
1875
|
message: dir === "." ? "Creating in the project root." : `Creating in ${dir}/`,
|
|
1944
|
-
...(request.suggestedName === undefined ? {} : { placeholder: request.suggestedName }),
|
|
1945
1876
|
// With a picker there is no extension in the field to preserve, so the whole name is the stem.
|
|
1946
1877
|
select: rows === null ? "stem" : "all",
|
|
1947
1878
|
validate,
|
|
1948
1879
|
value: request.suggestedName ?? "untitled.json",
|
|
1949
|
-
}
|
|
1880
|
+
};
|
|
1881
|
+
if (rows !== null) {
|
|
1882
|
+
promptOptions.choice = {
|
|
1883
|
+
initial: initialPick,
|
|
1884
|
+
label: "Format",
|
|
1885
|
+
onChange: (next: string) => {
|
|
1886
|
+
picked = next;
|
|
1887
|
+
},
|
|
1888
|
+
options: () => rows,
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1891
|
+
if (request.suggestedName !== undefined) {
|
|
1892
|
+
promptOptions.placeholder = request.suggestedName;
|
|
1893
|
+
}
|
|
1894
|
+
const entered = await showPromptDialog(request.title ?? "New File", promptOptions);
|
|
1950
1895
|
if (!entered) {
|
|
1951
1896
|
return null;
|
|
1952
1897
|
}
|
|
1953
1898
|
|
|
1954
|
-
const fileName =
|
|
1899
|
+
const fileName = composeNewFileName(entered, picked, choice);
|
|
1955
1900
|
const path = dir === "." ? fileName : `${dir}/${fileName}`;
|
|
1956
|
-
markLocalMutation(path);
|
|
1957
1901
|
const format = formatForPath(fileName);
|
|
1958
|
-
const suppliedBody =
|
|
1959
|
-
typeof request.content === "function"
|
|
1960
|
-
? await request.content(extensionOfName(fileName), fileName)
|
|
1961
|
-
: request.content;
|
|
1902
|
+
const suppliedBody = request.content;
|
|
1962
1903
|
/*
|
|
1963
1904
|
* What a new file starts as, in precedence order.
|
|
1964
1905
|
*
|
|
@@ -1978,16 +1919,59 @@ export async function createFileIn(request: NewFileRequest): Promise<string | nu
|
|
|
1978
1919
|
? BLANK_DOCUMENT
|
|
1979
1920
|
: "{}\n"
|
|
1980
1921
|
: "");
|
|
1922
|
+
return writeNewFile(path, content, { dir, source });
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
/**
|
|
1926
|
+
* The names already in `dir`, lower-cased — what a new name there must not collide with.
|
|
1927
|
+
*
|
|
1928
|
+
* A directory that cannot be listed answers empty: it is usually one that does not exist yet, and
|
|
1929
|
+
* the write that follows is the authority on whether that is a problem, reporting with the real
|
|
1930
|
+
* reason.
|
|
1931
|
+
*/
|
|
1932
|
+
export async function listTakenNames(dir: string): Promise<Set<string>> {
|
|
1933
|
+
try {
|
|
1934
|
+
const listing = await getPlatform().listDirectory(dir);
|
|
1935
|
+
return new Set(listing.map((entry) => entry.name.toLowerCase()));
|
|
1936
|
+
} catch {
|
|
1937
|
+
return new Set();
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
/**
|
|
1942
|
+
* Write one new file: the second half of every creation flow, after its name has been judged.
|
|
1943
|
+
*
|
|
1944
|
+
* The local mutation is marked first, so the watcher's echo of this write is not mistaken for an
|
|
1945
|
+
* edit made outside Studio. The destination is re-listed so the tree shows the file, the success is
|
|
1946
|
+
* a toast, and a failure is a Problem carrying the path, since the thing the author must do next is
|
|
1947
|
+
* about that path. It does NOT open the file: what a new file opens in is the caller's to say.
|
|
1948
|
+
*
|
|
1949
|
+
* @param path Project-relative path of the file to create.
|
|
1950
|
+
* @param content Its first bytes.
|
|
1951
|
+
* @param opts `dir` is re-listed afterwards (defaults to the path's parent); `source` names the
|
|
1952
|
+
* flow in the Problem; `quiet` skips the success toast for a write that is one step of a larger
|
|
1953
|
+
* creation that reports once for all of them.
|
|
1954
|
+
* @returns The path, or `null` when the write failed.
|
|
1955
|
+
*/
|
|
1956
|
+
export async function writeNewFile(
|
|
1957
|
+
path: string,
|
|
1958
|
+
content: string,
|
|
1959
|
+
opts: { dir?: string; source?: string; quiet?: boolean } = {},
|
|
1960
|
+
): Promise<string | null> {
|
|
1961
|
+
const dir = opts.dir ?? (path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : ".");
|
|
1962
|
+
markLocalMutation(path);
|
|
1981
1963
|
try {
|
|
1982
1964
|
await getPlatform().writeFile(path, content);
|
|
1983
1965
|
await loadDirectory(dir);
|
|
1984
|
-
|
|
1966
|
+
if (opts.quiet !== true) {
|
|
1967
|
+
notify.success(`Created ${path}`);
|
|
1968
|
+
}
|
|
1985
1969
|
return path;
|
|
1986
1970
|
} catch (error) {
|
|
1987
1971
|
notify.error(`Could not create ${path}.`, {
|
|
1988
1972
|
detail: errorMessage(error),
|
|
1989
1973
|
path,
|
|
1990
|
-
source,
|
|
1974
|
+
source: opts.source ?? "Files",
|
|
1991
1975
|
});
|
|
1992
1976
|
return null;
|
|
1993
1977
|
}
|
|
@@ -2069,13 +2053,16 @@ async function createNewFile(dirPath: string, renderLeftPanel: () => void) {
|
|
|
2069
2053
|
*/
|
|
2070
2054
|
async function showRenameFileDialog(currentName: string, path: string): Promise<string | null> {
|
|
2071
2055
|
const message = await renamePromptMessage(path);
|
|
2072
|
-
|
|
2056
|
+
const promptOptions: PromptDialogOptions = {
|
|
2073
2057
|
confirmLabel: "Rename",
|
|
2074
|
-
...(message === undefined ? {} : { message }),
|
|
2075
2058
|
select: "stem",
|
|
2076
2059
|
validate: (v) => (v.trim() ? "" : "Enter a file name."),
|
|
2077
2060
|
value: currentName,
|
|
2078
|
-
}
|
|
2061
|
+
};
|
|
2062
|
+
if (message !== undefined) {
|
|
2063
|
+
promptOptions.message = message;
|
|
2064
|
+
}
|
|
2065
|
+
return showPromptDialog("Rename", promptOptions);
|
|
2079
2066
|
}
|
|
2080
2067
|
|
|
2081
2068
|
/** Build the status-bar message for a rename, summarising any reference/tag rewrites. */
|
|
@@ -2248,6 +2235,13 @@ export interface OpenFileOpts {
|
|
|
2248
2235
|
preview?: boolean;
|
|
2249
2236
|
/** False leaves the keyboard where it is. Defaults to true. */
|
|
2250
2237
|
focus?: boolean;
|
|
2238
|
+
/**
|
|
2239
|
+
* The canvas mode the tab is in when it becomes active, for an open that is not an edit: a
|
|
2240
|
+
* comparison opens straight into `git-diff`. Set BEFORE activation, because a tab's mode decides
|
|
2241
|
+
* which workspace it belongs to (`workspace/tab-workspace.ts`) and activation enters that
|
|
2242
|
+
* workspace; a mode set afterwards would visit the edit mode's workspace on the way.
|
|
2243
|
+
*/
|
|
2244
|
+
canvasMode?: string;
|
|
2251
2245
|
}
|
|
2252
2246
|
|
|
2253
2247
|
/**
|
|
@@ -2300,6 +2294,9 @@ export async function openFileInTab(path: string, opts: OpenFileOpts = {}) {
|
|
|
2300
2294
|
const follows = opts.focus !== false;
|
|
2301
2295
|
for (const [id, tab] of workspace.tabs.entries()) {
|
|
2302
2296
|
if (tab.documentPath === path) {
|
|
2297
|
+
if (opts.canvasMode !== undefined) {
|
|
2298
|
+
tab.session.ui.canvasMode = opts.canvasMode;
|
|
2299
|
+
}
|
|
2303
2300
|
revealOpenTab(id, opts);
|
|
2304
2301
|
// The tree's cursor answers "where is the author", so a side-open that deliberately left the
|
|
2305
2302
|
// Keyboard behind must not move it.
|
|
@@ -2376,8 +2373,8 @@ export async function openFileInTab(path: string, opts: OpenFileOpts = {}) {
|
|
|
2376
2373
|
}
|
|
2377
2374
|
|
|
2378
2375
|
await loadFormats();
|
|
2379
|
-
let document:
|
|
2380
|
-
let frontmatter:
|
|
2376
|
+
let document: JxMutableNode | JsonObject;
|
|
2377
|
+
let frontmatter: FormatFrontmatter | undefined;
|
|
2381
2378
|
// The layout the JSON was written in, so a save reproduces it (`json-layout.ts`, issue 308). A
|
|
2382
2379
|
// Format-class file has none: its serializer owns its layout.
|
|
2383
2380
|
let layout: JsonLayout | null = null;
|
|
@@ -2399,10 +2396,12 @@ export async function openFileInTab(path: string, opts: OpenFileOpts = {}) {
|
|
|
2399
2396
|
document,
|
|
2400
2397
|
...(frontmatter != null && { frontmatter }),
|
|
2401
2398
|
layout,
|
|
2399
|
+
source: format ? content : null,
|
|
2402
2400
|
sourceFormat: format?.name ?? null,
|
|
2403
2401
|
...(opts.paneId !== undefined && { paneId: opts.paneId }),
|
|
2404
2402
|
...(opts.preview === true && { preview: true }),
|
|
2405
2403
|
...(opts.focus === false && { focus: false }),
|
|
2404
|
+
...(opts.canvasMode !== undefined && { canvasMode: opts.canvasMode }),
|
|
2406
2405
|
});
|
|
2407
2406
|
if (follows) {
|
|
2408
2407
|
requireProjectState().selectedPath = path;
|
|
@@ -2515,14 +2514,21 @@ export async function reloadFileInTab(path: string, opts: { onlyIfChanged?: bool
|
|
|
2515
2514
|
}
|
|
2516
2515
|
tab.doc.document = document;
|
|
2517
2516
|
tab.doc.content.frontmatter = frontmatter;
|
|
2517
|
+
tab.doc.source = content;
|
|
2518
2518
|
} else if (path.endsWith(".json")) {
|
|
2519
2519
|
// The file changed under the tab, so its layout is whatever the new text says.
|
|
2520
2520
|
const parsed = parseJsonDocument(content);
|
|
2521
2521
|
tab.doc.layout = parsed.layout;
|
|
2522
|
-
|
|
2522
|
+
/* SAFETY: this tab was opened on this `.json` path as a Jx document, and the file's schema
|
|
2523
|
+
Makes its top-level object a node — the object `parseJsonDocument` states it returns. */
|
|
2524
|
+
const document = parsed.document as JxMutableNode;
|
|
2525
|
+
// The same member form the tab was opened in, so the comparison and every edit after it
|
|
2526
|
+
// Read one shape, and the record of what the file wrote is the new text's.
|
|
2527
|
+
tab.doc.bareRepeaters = normalizeDocument(document);
|
|
2528
|
+
if (opts.onlyIfChanged && deepEqual(document, tab.doc.document)) {
|
|
2523
2529
|
return;
|
|
2524
2530
|
}
|
|
2525
|
-
tab.doc.document =
|
|
2531
|
+
tab.doc.document = document;
|
|
2526
2532
|
}
|
|
2527
2533
|
tab.doc.dirty = false;
|
|
2528
2534
|
} catch (error) {
|
|
@@ -2553,6 +2559,10 @@ export function registerFilesPanel(): void {
|
|
|
2553
2559
|
level: "project",
|
|
2554
2560
|
dock: "navigator",
|
|
2555
2561
|
icon: "folder",
|
|
2562
|
+
/* A developer panel (§5.1): the Site panel lists the project by what its files ARE, so the tree
|
|
2563
|
+
of where they are stored is on the Design rail only with **Show developer panels** on. With it
|
|
2564
|
+
off the panel is still `view.setActivity { tab: "files" }` and a palette row away. */
|
|
2565
|
+
rail: "developer",
|
|
2556
2566
|
// The body is a document, so lit draws nothing and the mount happens against the painted DOM.
|
|
2557
2567
|
// `afterRender` runs on every repaint; {@link mountFilesPanel} is idempotent.
|
|
2558
2568
|
render: () => nothing,
|
|
@@ -2608,16 +2618,15 @@ let _lastValues: FilesPanelValues | null = null;
|
|
|
2608
2618
|
* Draw the panel — mounting the document the first time, and re-projecting into it every time
|
|
2609
2619
|
* after.
|
|
2610
2620
|
*
|
|
2611
|
-
* The document goes into `.panel-content`, not into the `.panel-body` this is handed,
|
|
2612
|
-
*
|
|
2613
|
-
*
|
|
2614
|
-
* next.
|
|
2621
|
+
* The document goes into `.panel-content`, not into the `.panel-body` this is handed, because only
|
|
2622
|
+
* one of them is the node lit renders this panel's body into, and appending to the other would
|
|
2623
|
+
* leave the panel drawn under whatever the Navigator paints next.
|
|
2615
2624
|
*
|
|
2616
|
-
* The repaint stays the NAVIGATOR'S.
|
|
2617
|
-
* reactive `shell.git` record; the tree's inputs are a `Map` of directory listings, a `Set`
|
|
2618
|
-
* expanded paths and a roaming setting, none of which is reactive — so `ctx.rerender` is what
|
|
2619
|
-
* surface has always been redrawn by, and it stays that way rather than growing a second
|
|
2620
|
-
* that could disagree with it.
|
|
2625
|
+
* The repaint stays the NAVIGATOR'S. The Publish workspace owns an `effect()` because its inputs
|
|
2626
|
+
* are the reactive `shell.git` record; the tree's inputs are a `Map` of directory listings, a `Set`
|
|
2627
|
+
* of expanded paths and a roaming setting, none of which is reactive — so `ctx.rerender` is what
|
|
2628
|
+
* this surface has always been redrawn by, and it stays that way rather than growing a second
|
|
2629
|
+
* scheduler that could disagree with it.
|
|
2621
2630
|
*
|
|
2622
2631
|
* @param host - The painted `.panel-body`
|
|
2623
2632
|
* @param rerender - The Navigator's repaint
|
|
@@ -2652,7 +2661,7 @@ export function mountFilesPanel(host: HTMLElement, rerender: () => void): void {
|
|
|
2652
2661
|
* Take the panel down: the document, the drag registrations, the scroll watch and the row handles.
|
|
2653
2662
|
*
|
|
2654
2663
|
* Exported because a project close has to reach it — the tree it is showing belongs to the project
|
|
2655
|
-
* being left behind, exactly as `
|
|
2664
|
+
* being left behind, exactly as `cleanupSourceControl` is for the working tree.
|
|
2656
2665
|
*/
|
|
2657
2666
|
export function unmountFilesPanel(): void {
|
|
2658
2667
|
dismissFileContextMenu();
|