@jxsuite/studio 2.0.1 → 2.1.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/canvas.html +21 -0
- package/dist/chunks/Overlay.dev-qfjzkms8.js +23 -0
- package/dist/chunks/{Overlay.dev-cszx7yn5.js.map → Overlay.dev-qfjzkms8.js.map} +1 -1
- package/dist/chunks/{adapter-z8fkmbmv.js → adapter-qfnesn5v.js} +2 -2
- package/dist/chunks/{cf-settings-mmyv436d.js → cf-settings-73sh5z4p.js} +3 -3
- package/dist/chunks/{components-799jpvhm.js → components-2bpqp4mq.js} +5 -7
- package/dist/chunks/{components-799jpvhm.js.map → components-2bpqp4mq.js.map} +1 -1
- package/dist/chunks/content-source-34gnkbnn.js +38 -0
- package/dist/chunks/content-source-34gnkbnn.js.map +9 -0
- package/dist/chunks/convert-to-repeater-t8kxre5s.js +258 -0
- package/dist/chunks/convert-to-repeater-t8kxre5s.js.map +10 -0
- package/dist/chunks/extension-sections-6mqydz35.js +301 -0
- package/dist/chunks/extension-sections-6mqydz35.js.map +11 -0
- package/dist/chunks/files-8kn321h1.js +70 -0
- package/dist/chunks/files-8kn321h1.js.map +9 -0
- package/dist/chunks/{format-host-8jxwts2p.js → format-host-mkwsmt8d.js} +3 -3
- package/dist/chunks/git-panel-tjbr191d.js +51 -0
- package/dist/chunks/git-panel-tjbr191d.js.map +9 -0
- package/dist/chunks/{index-baeg2x3y.js → index-7pxchcce.js} +4 -3
- package/dist/chunks/{index-baeg2x3y.js.map → index-7pxchcce.js.map} +3 -3
- package/dist/chunks/{jx-validate-qx8degj7.js → jx-validate-v6qgknrs.js} +3 -3
- package/dist/chunks/layers-panel-87pb25km.js +58 -0
- package/dist/chunks/layers-panel-87pb25km.js.map +9 -0
- package/dist/chunks/{monaco-setup-zjdp9zdw.js → monaco-setup-e0yh25j4.js} +4 -75
- package/dist/chunks/{monaco-setup-zjdp9zdw.js.map → monaco-setup-e0yh25j4.js.map} +3 -4
- package/dist/chunks/publish-panel-m0fcgdrt.js +21 -0
- package/dist/chunks/publish-panel-m0fcgdrt.js.map +9 -0
- package/dist/chunks/quick-search-rhs6cbsb.js +53 -0
- package/dist/chunks/quick-search-rhs6cbsb.js.map +9 -0
- package/dist/chunks/right-panel-zpdxd98t.js +37 -0
- package/dist/chunks/right-panel-zpdxd98t.js.map +9 -0
- package/dist/chunks/section-registry-zmckaxf4.js +30 -0
- package/dist/chunks/section-registry-zmckaxf4.js.map +9 -0
- package/dist/chunks/sp-overlay.dev-9ran0sfq.js +18 -0
- package/dist/chunks/sp-overlay.dev-9ran0sfq.js.map +9 -0
- package/dist/chunks/sp-popover.dev-rebmdncr.js +9 -0
- package/dist/chunks/sp-popover.dev-rebmdncr.js.map +9 -0
- package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js +15 -0
- package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js.map +9 -0
- package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js +15 -0
- package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js.map +9 -0
- package/dist/chunks/{sp-tray.dev-pb2fsd65.js → sp-tray.dev-ey59vfh7.js} +10 -8
- package/dist/chunks/{sp-tray.dev-pb2fsd65.js.map → sp-tray.dev-ey59vfh7.js.map} +2 -2
- package/dist/chunks/{studio-9qr6fq7p.js → studio-0rqqhrdy.js} +10 -378
- package/dist/chunks/{studio-9qr6fq7p.js.map → studio-0rqqhrdy.js.map} +4 -14
- package/dist/chunks/{studio-5x6tqqec.js → studio-2sj1jj3v.js} +10 -8
- package/dist/chunks/{studio-5x6tqqec.js.map → studio-2sj1jj3v.js.map} +2 -2
- package/dist/chunks/{studio-h3fw0wn8.js → studio-3fk1ac9z.js} +6 -4
- package/dist/chunks/{studio-h3fw0wn8.js.map → studio-3fk1ac9z.js.map} +2 -2
- package/dist/chunks/studio-41exgzhq.js +6690 -0
- package/dist/chunks/studio-41exgzhq.js.map +38 -0
- package/dist/chunks/{studio-7v2bpweq.js → studio-4mqb9a0j.js} +6 -4
- package/dist/chunks/{studio-7v2bpweq.js.map → studio-4mqb9a0j.js.map} +2 -2
- package/dist/chunks/studio-50k77cca.js +1787 -0
- package/dist/chunks/studio-50k77cca.js.map +18 -0
- package/dist/chunks/{studio-56v3zwk6.js → studio-6a0h7sne.js} +10 -8
- package/dist/chunks/{studio-56v3zwk6.js.map → studio-6a0h7sne.js.map} +2 -2
- package/dist/chunks/studio-6f0am8f2.js +53 -0
- package/dist/chunks/studio-6f0am8f2.js.map +10 -0
- package/dist/chunks/studio-6nmjc5qk.js +7002 -0
- package/dist/chunks/studio-6nmjc5qk.js.map +32 -0
- package/dist/chunks/studio-7afd21nw.js +67 -0
- package/dist/chunks/studio-7afd21nw.js.map +10 -0
- package/dist/chunks/{studio-xze4zrs1.js → studio-7bmdvzne.js} +2 -2
- package/dist/chunks/studio-7bmdvzne.js.map +10 -0
- package/dist/chunks/studio-8jpzqk5s.js +128 -0
- package/dist/chunks/studio-8jpzqk5s.js.map +10 -0
- package/dist/chunks/studio-94m77tgk.js +1482 -0
- package/dist/chunks/studio-94m77tgk.js.map +13 -0
- package/dist/chunks/studio-9je6w5sq.js +1000 -0
- package/dist/chunks/studio-9je6w5sq.js.map +16 -0
- package/dist/chunks/{studio-w7c6kvk7.js → studio-a7pzveq1.js} +2 -2
- package/dist/chunks/{studio-gs6kqdt1.js → studio-adn0z1k3.js} +14 -8
- package/dist/chunks/{studio-gs6kqdt1.js.map → studio-adn0z1k3.js.map} +3 -3
- package/dist/chunks/{studio-kr88ryr7.js → studio-bsxxx3x2.js} +17 -15
- package/dist/chunks/{studio-kr88ryr7.js.map → studio-bsxxx3x2.js.map} +7 -6
- package/dist/chunks/studio-bw5dz6km.js +608 -0
- package/dist/chunks/studio-bw5dz6km.js.map +21 -0
- package/dist/chunks/studio-c6s5getc.js +4232 -0
- package/dist/chunks/studio-c6s5getc.js.map +39 -0
- package/dist/chunks/studio-cjm7fdjf.js +451 -0
- package/dist/chunks/studio-cjm7fdjf.js.map +14 -0
- package/dist/chunks/{studio-cx91mnh8.js → studio-ckwz4d3x.js} +53 -53
- package/dist/chunks/{studio-cx91mnh8.js.map → studio-ckwz4d3x.js.map} +5 -5
- package/dist/chunks/studio-dg7zyt5x.js +28 -0
- package/dist/chunks/studio-dg7zyt5x.js.map +10 -0
- package/dist/chunks/studio-dzcyvvvz.js +166 -0
- package/dist/chunks/studio-dzcyvvvz.js.map +10 -0
- package/dist/chunks/{studio-7kqe41sa.js → studio-evkr5d3b.js} +15 -7
- package/dist/chunks/{studio-7kqe41sa.js.map → studio-evkr5d3b.js.map} +5 -4
- package/dist/chunks/studio-f12h6177.js +411 -0
- package/dist/chunks/studio-f12h6177.js.map +13 -0
- package/dist/chunks/studio-hbp1s8m9.js +1817 -0
- package/dist/chunks/studio-hbp1s8m9.js.map +14 -0
- package/dist/chunks/studio-k2sxjph5.js +15295 -0
- package/dist/chunks/studio-k2sxjph5.js.map +235 -0
- package/dist/chunks/studio-myje51t3.js +385 -0
- package/dist/chunks/studio-myje51t3.js.map +12 -0
- package/dist/chunks/studio-na6rwzhw.js +353 -0
- package/dist/chunks/studio-na6rwzhw.js.map +11 -0
- package/dist/chunks/{studio-4k0m4fmp.js → studio-p471gg5n.js} +126 -3
- package/dist/chunks/studio-p471gg5n.js.map +14 -0
- package/dist/chunks/{studio-9bjwf0d4.js → studio-rgwcpast.js} +5 -46
- package/dist/chunks/{studio-9bjwf0d4.js.map → studio-rgwcpast.js.map} +4 -5
- package/dist/chunks/{studio-esrewxap.js → studio-s519zc99.js} +3 -3
- package/dist/chunks/studio-sas7mt3k.js +75 -0
- package/dist/chunks/studio-sas7mt3k.js.map +10 -0
- package/dist/chunks/studio-t5wqwzdh.js +74 -0
- package/dist/chunks/studio-t5wqwzdh.js.map +10 -0
- package/dist/chunks/{studio-9qpspwfn.js → studio-temv5bw4.js} +5 -5
- package/dist/chunks/{studio-9qpspwfn.js.map → studio-temv5bw4.js.map} +1 -1
- package/dist/chunks/{studio-qkvt37c0.js → studio-tr20m8g8.js} +669 -18
- package/dist/chunks/studio-tr20m8g8.js.map +9 -0
- package/dist/chunks/{studio-v7jkjqng.js → studio-v96qfnww.js} +5514 -4291
- package/dist/chunks/studio-v96qfnww.js.map +54 -0
- package/dist/chunks/studio-vcetc91k.js +659 -0
- package/dist/chunks/studio-vcetc91k.js.map +10 -0
- package/dist/chunks/{studio-jm0z592w.js → studio-vmymjefp.js} +2972 -4937
- package/dist/chunks/studio-vmymjefp.js.map +98 -0
- package/dist/chunks/{studio-bghqfmea.js → studio-wwseqyw0.js} +2 -2
- package/dist/chunks/studio-x9hmy5kr.js +390 -0
- package/dist/chunks/studio-x9hmy5kr.js.map +10 -0
- package/dist/chunks/studio-xa0ce0n7.js +335 -0
- package/dist/chunks/studio-xa0ce0n7.js.map +10 -0
- package/dist/chunks/studio-xm3dbxvn.js +378 -0
- package/dist/chunks/studio-xm3dbxvn.js.map +19 -0
- package/dist/chunks/{studio-nqw433kv.js → studio-xmvbhg31.js} +2 -2
- package/dist/chunks/studio-zv37yrfa.js +1054 -0
- package/dist/chunks/studio-zv37yrfa.js.map +13 -0
- package/dist/chunks/{ws-client-q7hkz3j9.js → ws-client-654ts66j.js} +6 -4
- package/dist/chunks/ws-client-654ts66j.js.map +11 -0
- package/dist/iframe-entry.js +668 -230
- package/dist/studio.js +21080 -32585
- package/index.html +35 -4685
- package/package.json +17 -15
- package/src/about/about-modal.ts +42 -11
- package/src/browse/library-commands.ts +240 -0
- package/src/browse/library-layouts.ts +369 -0
- package/src/browse/library-model.ts +396 -0
- package/src/browse/library-pane.ts +1049 -0
- package/src/browse/library-preview.ts +252 -0
- package/src/browse/library-source.ts +150 -0
- package/src/canvas/canvas-helpers.ts +33 -9
- package/src/canvas/canvas-live-render.ts +45 -29
- package/src/canvas/canvas-patcher.ts +151 -31
- package/src/canvas/canvas-perf.ts +46 -7
- package/src/canvas/canvas-render.ts +1092 -220
- package/src/canvas/canvas-surface.ts +240 -0
- package/src/canvas/canvas-utils.ts +1166 -124
- package/src/canvas/iframe-drop.ts +2 -1
- package/src/canvas/iframe-entry.ts +241 -4
- package/src/canvas/iframe-host.ts +1226 -143
- package/src/canvas/iframe-inline-edit.ts +31 -0
- package/src/canvas/iframe-interaction.ts +76 -16
- package/src/canvas/iframe-keys.ts +107 -45
- package/src/canvas/iframe-overlay.ts +38 -1
- package/src/canvas/iframe-protocol.ts +137 -2
- package/src/canvas/iframe-render.ts +70 -2
- package/src/canvas/iframe-slash.ts +6 -0
- package/src/canvas/path-mapping.ts +56 -6
- package/src/canvas/surface-registry.ts +292 -0
- package/src/collab/collab-commands.ts +244 -0
- package/src/collab/collab-session.ts +198 -32
- package/src/collab/collab-state.ts +28 -2
- package/src/collab/presence-chips.ts +100 -9
- package/src/commands/active-registry.ts +42 -0
- package/src/commands/app-commands.ts +176 -0
- package/src/commands/budget.ts +158 -0
- package/src/commands/command-args.ts +253 -0
- package/src/commands/context.ts +387 -0
- package/src/commands/defaults.ts +580 -0
- package/src/commands/keymap.ts +571 -0
- package/src/commands/levels.ts +345 -0
- package/src/commands/live-context.ts +193 -0
- package/src/commands/reference.ts +227 -0
- package/src/commands/registry.ts +293 -0
- package/src/component-props.ts +3 -2
- package/src/content/draft-state.ts +106 -0
- package/src/content/entry-commands.ts +282 -0
- package/src/content/entry-editor.ts +327 -0
- package/src/content/entry-fields.ts +116 -0
- package/src/content/entry-model.ts +175 -0
- package/src/editor/canvas-context-menu.ts +17 -11
- package/src/editor/canvas-slash-bridge.ts +11 -4
- package/src/editor/context-menu.ts +804 -204
- package/src/editor/convert-to-component.ts +31 -10
- package/src/editor/convert-to-repeater.ts +3 -2
- package/src/editor/inline-edit-apply.ts +5 -5
- package/src/editor/inline-edit.ts +86 -50
- package/src/editor/insert-zone-action.ts +1 -1
- package/src/editor/shortcuts.ts +1043 -329
- package/src/editor/slash-menu.ts +1 -0
- package/src/files/elements.ts +166 -0
- package/src/files/file-ops.ts +178 -31
- package/src/files/files.ts +898 -268
- package/src/files/fs-events.ts +21 -0
- package/src/files/media-meta.ts +203 -0
- package/src/files/media-paths.ts +141 -0
- package/src/files/media-upload.ts +7 -3
- package/src/files/media-usage.ts +189 -0
- package/src/github/github-auth.ts +235 -33
- package/src/github/github-publish.ts +80 -21
- package/src/grid/grid-controller.ts +152 -12
- package/src/grid/grid-layout.ts +225 -20
- package/src/grid/grid-open.ts +120 -0
- package/src/grid/grid-panel.ts +554 -39
- package/src/grid/grid-source.ts +27 -1
- package/src/grid/redirects-grid.ts +599 -0
- package/src/grid/redirects.ts +398 -0
- package/src/grid/sources/content-source.ts +108 -2
- package/src/i18n/i18n-commands.ts +322 -0
- package/src/new-project/add-repo-modal.ts +8 -10
- package/src/new-project/import-tab.ts +7 -0
- package/src/new-project/location-fields.ts +9 -1
- package/src/new-project/new-project-modal.ts +221 -245
- package/src/packages/jxsuite-update.ts +97 -48
- package/src/packages/pull-package-sync.ts +18 -9
- package/src/page-params.ts +2 -1
- package/src/panels/activity-bar.ts +152 -91
- package/src/panels/activity-panel.ts +481 -0
- package/src/panels/ai-chat/chat-markdown.ts +8 -3
- package/src/panels/ai-chat/chat-view.ts +245 -18
- package/src/panels/ai-chat/composer.ts +31 -5
- package/src/panels/ai-chat/sessions-view.ts +17 -10
- package/src/panels/ai-panel.ts +279 -53
- package/src/panels/block-action-bar.ts +1115 -224
- package/src/panels/bottom-dock.ts +337 -0
- package/src/panels/canvas-dnd-bridge.ts +37 -8
- package/src/panels/chat-panel.ts +29 -12
- package/src/panels/data-explorer.ts +254 -112
- package/src/panels/data-grid.ts +10 -4
- package/src/panels/dnd.ts +28 -51
- package/src/panels/editors.ts +434 -107
- package/src/panels/elements-panel.ts +66 -2
- package/src/panels/empty-state.ts +112 -0
- package/src/panels/events-panel.ts +596 -82
- package/src/panels/formula-workspace.ts +408 -218
- package/src/panels/frontmatter-fields.ts +51 -10
- package/src/panels/frontmatter-panel.ts +357 -79
- package/src/panels/git-panel.ts +375 -164
- package/src/panels/head-panel.ts +791 -85
- package/src/panels/i18n-panel.ts +592 -0
- package/src/panels/imports-panel.ts +95 -85
- package/src/panels/jump-bar.ts +584 -0
- package/src/panels/layers-panel.ts +1106 -328
- package/src/panels/left-panel.ts +109 -204
- package/src/panels/navigator-panels.ts +131 -0
- package/src/panels/overlays.ts +7 -2
- package/src/panels/pane-context.ts +1477 -0
- package/src/panels/pane-grid.ts +406 -0
- package/src/panels/panel-registry.ts +340 -0
- package/src/panels/panel-scheduler.ts +75 -0
- package/src/panels/problems-panel.ts +234 -0
- package/src/panels/properties-panel.ts +946 -1139
- package/src/panels/provenance.ts +258 -0
- package/src/panels/quick-search.ts +791 -122
- package/src/panels/right-panel.ts +228 -102
- package/src/panels/seo-modal.ts +395 -0
- package/src/panels/settings-pane.ts +203 -0
- package/src/panels/signals-panel.ts +354 -137
- package/src/panels/statement-editor.ts +41 -25
- package/src/panels/statusbar.ts +342 -106
- package/src/panels/style-panel.ts +974 -463
- package/src/panels/style-utils.ts +3 -3
- package/src/panels/stylebook-doc.ts +4 -2
- package/src/panels/stylebook-layers-panel.ts +3 -2
- package/src/panels/stylebook-panel.ts +51 -26
- package/src/panels/tab-strip.ts +1058 -78
- package/src/panels/target-line.ts +274 -0
- package/src/panels/toolbar.ts +631 -484
- package/src/panels/welcome-screen.ts +301 -198
- package/src/platform-errors.ts +26 -3
- package/src/platform.ts +96 -2
- package/src/platforms/cloud.ts +71 -8
- package/src/platforms/devserver.ts +92 -29
- package/src/publish/deploy-checklist.ts +383 -0
- package/src/publish/pages-service.ts +10 -1
- package/src/publish/publish-commands.ts +218 -0
- package/src/publish/publish-panel.ts +110 -16
- package/src/recent-projects.ts +3 -2
- package/src/services/a11y-report.ts +505 -0
- package/src/services/ai-models.ts +30 -1
- package/src/services/ai-project-tools.ts +29 -10
- package/src/services/ai-system-prompt.ts +53 -14
- package/src/services/ai-tools.ts +39 -3
- package/src/services/ai-writes.ts +154 -0
- package/src/services/announce.ts +97 -0
- package/src/services/automation.ts +424 -175
- package/src/services/cem-export.ts +9 -4
- package/src/services/clock.ts +61 -0
- package/src/services/document-assistant.ts +36 -8
- package/src/services/idle.ts +179 -0
- package/src/services/import-client.ts +26 -0
- package/src/services/monaco-buffer.ts +627 -0
- package/src/services/monaco-lazy.ts +36 -0
- package/src/services/notify.ts +271 -0
- package/src/services/profile.ts +155 -0
- package/src/services/references.ts +0 -0
- package/src/services/token-lint.ts +3 -2
- package/src/services/tool-executor.ts +53 -7
- package/src/services/trusted-types.ts +123 -0
- package/src/settings/contexts-section.ts +427 -0
- package/src/settings/contributed-section.ts +149 -11
- package/src/settings/css-vars-editor.ts +306 -349
- package/src/settings/defs-editor.ts +93 -34
- package/src/settings/dependencies-editor.ts +7 -7
- package/src/settings/extension-sections.ts +37 -5
- package/src/settings/general-settings.ts +204 -121
- package/src/settings/locales-section.ts +347 -0
- package/src/settings/preferences-accounts.ts +140 -0
- package/src/settings/preferences-dialog.ts +620 -0
- package/src/settings/preferences-keymap.ts +270 -0
- package/src/settings/project-sections.ts +260 -0
- package/src/settings/section-registry.ts +169 -0
- package/src/settings/settings-document.ts +385 -0
- package/src/shell.ts +1549 -0
- package/src/site-context.ts +45 -20
- package/src/state.ts +14 -275
- package/src/store.ts +88 -43
- package/src/studio.ts +790 -346
- package/src/style/live-preview.ts +36 -0
- package/src/style/project-styles.ts +349 -0
- package/src/style/token-ref.ts +123 -0
- package/src/tabs/patch-ops.ts +16 -4
- package/src/tabs/project-config.ts +416 -0
- package/src/tabs/selection.ts +323 -0
- package/src/tabs/tab.ts +182 -47
- package/src/tabs/transact.ts +209 -28
- package/src/types.ts +68 -13
- package/src/ui/ai-credentials-form.ts +39 -35
- package/src/ui/ai-managed-connect.ts +7 -1
- package/src/ui/dynamic-slot.ts +224 -81
- package/src/ui/expression-editor.ts +71 -63
- package/src/ui/field-input.ts +4 -6
- package/src/ui/field-row.ts +215 -38
- package/src/ui/form-controls.ts +194 -7
- package/src/ui/layers.ts +492 -69
- package/src/ui/media-picker.ts +115 -25
- package/src/ui/panel-resize.ts +109 -91
- package/src/ui/progress-modal.ts +131 -50
- package/src/ui/regions.ts +462 -0
- package/src/ui/schema-form.ts +229 -45
- package/src/ui/spectrum.ts +24 -8
- package/src/ui/timing.ts +49 -0
- package/src/ui/value-selector.ts +20 -0
- package/src/ui/value-source.ts +348 -0
- package/src/ui/virtual-window.ts +400 -0
- package/src/utils/edit-display.ts +3 -2
- package/src/utils/inherited-style.ts +67 -37
- package/src/view.ts +64 -78
- package/src/workspace/pane-derive.ts +1258 -0
- package/src/workspace/session.ts +317 -0
- package/src/workspace/workspace.ts +1345 -23
- package/styles/canvas.css +653 -0
- package/styles/forced-colors.css +106 -0
- package/styles/inspector.css +1454 -0
- package/styles/overlays.css +2704 -0
- package/styles/panels.css +1701 -0
- package/styles/shell.css +1423 -0
- package/styles/tokens.css +151 -0
- package/dist/chunks/Overlay.dev-cszx7yn5.js +0 -22
- package/dist/chunks/extension-sections-qr3drkkw.js +0 -531
- package/dist/chunks/extension-sections-qr3drkkw.js.map +0 -12
- package/dist/chunks/files-j8a1t06k.js +0 -50
- package/dist/chunks/files-j8a1t06k.js.map +0 -9
- package/dist/chunks/git-panel-xtq5z81x.js +0 -28
- package/dist/chunks/git-panel-xtq5z81x.js.map +0 -9
- package/dist/chunks/layers-panel-ancn2wpq.js +0 -26
- package/dist/chunks/layers-panel-ancn2wpq.js.map +0 -9
- package/dist/chunks/settings-modal-17ttr96g.js +0 -29
- package/dist/chunks/settings-modal-17ttr96g.js.map +0 -9
- package/dist/chunks/sp-overlay.dev-earn5as8.js +0 -17
- package/dist/chunks/sp-overlay.dev-earn5as8.js.map +0 -9
- package/dist/chunks/sp-popover.dev-frxtfejn.js +0 -8
- package/dist/chunks/sp-popover.dev-frxtfejn.js.map +0 -9
- package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js +0 -14
- package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js.map +0 -9
- package/dist/chunks/sp-tooltip.dev-a9fywesm.js +0 -21
- package/dist/chunks/sp-tooltip.dev-a9fywesm.js.map +0 -10
- package/dist/chunks/studio-0xgs7xrs.js +0 -429
- package/dist/chunks/studio-0xgs7xrs.js.map +0 -13
- package/dist/chunks/studio-4d8hzcz5.js +0 -3427
- package/dist/chunks/studio-4d8hzcz5.js.map +0 -26
- package/dist/chunks/studio-4k0m4fmp.js.map +0 -13
- package/dist/chunks/studio-9smfzqhc.js +0 -19
- package/dist/chunks/studio-9smfzqhc.js.map +0 -10
- package/dist/chunks/studio-jm0z592w.js.map +0 -115
- package/dist/chunks/studio-m2t4mdg5.js +0 -1854
- package/dist/chunks/studio-m2t4mdg5.js.map +0 -18
- package/dist/chunks/studio-n4p11dfy.js +0 -853
- package/dist/chunks/studio-n4p11dfy.js.map +0 -12
- package/dist/chunks/studio-qkvt37c0.js.map +0 -9
- package/dist/chunks/studio-sjh1wt1a.js +0 -3744
- package/dist/chunks/studio-sjh1wt1a.js.map +0 -22
- package/dist/chunks/studio-v7jkjqng.js.map +0 -46
- package/dist/chunks/studio-xq0t7n5t.js +0 -140
- package/dist/chunks/studio-xq0t7n5t.js.map +0 -10
- package/dist/chunks/studio-xze4zrs1.js.map +0 -10
- package/dist/chunks/studio-yfcstdzy.js +0 -399
- package/dist/chunks/studio-yfcstdzy.js.map +0 -12
- package/dist/chunks/studio-zp7jncs8.js +0 -469
- package/dist/chunks/studio-zp7jncs8.js.map +0 -14
- package/dist/chunks/ws-client-q7hkz3j9.js.map +0 -11
- package/src/browse/browse-modal.ts +0 -69
- package/src/browse/browse.ts +0 -904
- package/src/canvas/canvas-diff.ts +0 -216
- package/src/canvas/nested-site-style.ts +0 -58
- package/src/new-project/design-fields.ts +0 -264
- package/src/new-project/templates.ts +0 -61
- package/src/panels/tab-bar.ts +0 -548
- package/src/settings/settings-modal.ts +0 -203
- /package/dist/chunks/{adapter-z8fkmbmv.js.map → adapter-qfnesn5v.js.map} +0 -0
- /package/dist/chunks/{cf-settings-mmyv436d.js.map → cf-settings-73sh5z4p.js.map} +0 -0
- /package/dist/chunks/{format-host-8jxwts2p.js.map → format-host-mkwsmt8d.js.map} +0 -0
- /package/dist/chunks/{jx-validate-qx8degj7.js.map → jx-validate-v6qgknrs.js.map} +0 -0
- /package/dist/chunks/{studio-w7c6kvk7.js.map → studio-a7pzveq1.js.map} +0 -0
- /package/dist/chunks/{studio-esrewxap.js.map → studio-s519zc99.js.map} +0 -0
- /package/dist/chunks/{studio-bghqfmea.js.map → studio-wwseqyw0.js.map} +0 -0
- /package/dist/chunks/{studio-nqw433kv.js.map → studio-xmvbhg31.js.map} +0 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command-args.ts — loud coercion for the arguments a command record declares.
|
|
3
|
+
*
|
|
4
|
+
* Every scriptable command carries an `args` JSON Schema (spec §13.1, plan §13.3): the palette
|
|
5
|
+
* prompts from it, the AI tool's parameters are it, and `scripts/check-shot-contract.ts` validates
|
|
6
|
+
* every manifest step against it in the `checks` job. This module is the RUNTIME half of that same
|
|
7
|
+
* declaration — the four lines inside `run` that turn `Record<string, unknown>` into the typed
|
|
8
|
+
* value the implementation wants.
|
|
9
|
+
*
|
|
10
|
+
* Two rules, and they are the reason this is a module rather than twelve copies of `String(x)`:
|
|
11
|
+
*
|
|
12
|
+
* 1. **Reject loudly, never clamp.** An unknown panel id, a mode the tab cannot enter, a selection
|
|
13
|
+
* path that is not in the document — each throws. A no-op would photograph the previous state
|
|
14
|
+
* and a docs build would accept it; a clamp would photograph a state the caller did not ask for.
|
|
15
|
+
* Plan §13.4 makes this a tested property.
|
|
16
|
+
* 2. **The failure names the fix.** `{@link enumArg}` prints the declared values, which is what makes
|
|
17
|
+
* §13.5's headline failure — _manifest shot "properties-bar" names panel "head"; the registry
|
|
18
|
+
* declares "page"_ — a sentence a reader can act on rather than a stack trace. Nothing else in
|
|
19
|
+
* this file matters as much as that message.
|
|
20
|
+
*
|
|
21
|
+
* `RangeError` (not a bespoke class) because `services/profile.ts` already throws it for exactly
|
|
22
|
+
* this — an id outside a declared set — and one refusal shape is easier to catch than two.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** Arguments as they arrive from a palette prompt, a manifest step or an AI tool call. */
|
|
26
|
+
export type CommandArgValues = Record<string, unknown>;
|
|
27
|
+
|
|
28
|
+
/** `command "<id>" argument "<key>": <problem>` — the one sentence shape every failure here uses. */
|
|
29
|
+
function refuse(commandId: string, key: string, problem: string): RangeError {
|
|
30
|
+
return new RangeError(`command "${commandId}" argument "${key}": ${problem}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** How a received value is quoted back to the caller. Strings keep their quotes; nothing is elided. */
|
|
34
|
+
function describe(value: unknown): string {
|
|
35
|
+
if (value === undefined) {
|
|
36
|
+
return "missing";
|
|
37
|
+
}
|
|
38
|
+
return typeof value === "string" ? `"${value}"` : `${typeof value} ${JSON.stringify(value)}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** A required string argument. */
|
|
42
|
+
export function stringArg(commandId: string, args: CommandArgValues, key: string): string {
|
|
43
|
+
const value = args[key];
|
|
44
|
+
if (typeof value !== "string" || value === "") {
|
|
45
|
+
throw refuse(commandId, key, `expected a non-empty string, got ${describe(value)}`);
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** An optional string argument — `undefined` when absent, never coerced from another type. */
|
|
51
|
+
export function optionalStringArg(
|
|
52
|
+
commandId: string,
|
|
53
|
+
args: CommandArgValues,
|
|
54
|
+
key: string,
|
|
55
|
+
): string | undefined {
|
|
56
|
+
if (args[key] === undefined) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
return stringArg(commandId, args, key);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** A required finite number argument. `NaN` and `Infinity` are refusals, not values. */
|
|
63
|
+
export function numberArg(commandId: string, args: CommandArgValues, key: string): number {
|
|
64
|
+
const value = args[key];
|
|
65
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
66
|
+
throw refuse(commandId, key, `expected a finite number, got ${describe(value)}`);
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A required number argument inside a closed interval.
|
|
73
|
+
*
|
|
74
|
+
* REJECTS out of range rather than clamping, which is the difference between a shot that fails and
|
|
75
|
+
* a shot that photographs 500% when it asked for 1000% — the second is a wrong picture the docs
|
|
76
|
+
* build accepts. The interactive controls still clamp: dragging a slider past its end is a gesture
|
|
77
|
+
* with an obvious meaning, whereas a caller naming a number outside the range is stating something
|
|
78
|
+
* untrue about the app.
|
|
79
|
+
*/
|
|
80
|
+
export function boundedNumberArg(
|
|
81
|
+
commandId: string,
|
|
82
|
+
args: CommandArgValues,
|
|
83
|
+
key: string,
|
|
84
|
+
minimum: number,
|
|
85
|
+
maximum: number,
|
|
86
|
+
): number {
|
|
87
|
+
const value = numberArg(commandId, args, key);
|
|
88
|
+
if (value < minimum || value > maximum) {
|
|
89
|
+
throw refuse(commandId, key, `${value} is outside the supported range ${minimum}–${maximum}`);
|
|
90
|
+
}
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** A required boolean argument. A setter's whole point is that `false` is a value, so no default. */
|
|
95
|
+
export function booleanArg(commandId: string, args: CommandArgValues, key: string): boolean {
|
|
96
|
+
const value = args[key];
|
|
97
|
+
if (typeof value !== "boolean") {
|
|
98
|
+
throw refuse(commandId, key, `expected a boolean, got ${describe(value)}`);
|
|
99
|
+
}
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* A required argument drawn from a declared set.
|
|
105
|
+
*
|
|
106
|
+
* The `declared:` clause is the point — see the module header. It is printed in full rather than
|
|
107
|
+
* truncated: these sets are panel ids and tab ids, never long enough to be a wall, and a reader
|
|
108
|
+
* comparing a stale id against the current list needs the whole list.
|
|
109
|
+
*/
|
|
110
|
+
export function enumArg<T extends string>(
|
|
111
|
+
commandId: string,
|
|
112
|
+
args: CommandArgValues,
|
|
113
|
+
key: string,
|
|
114
|
+
declared: readonly T[],
|
|
115
|
+
): T {
|
|
116
|
+
const value = args[key];
|
|
117
|
+
if (typeof value !== "string" || !(declared as readonly string[]).includes(value)) {
|
|
118
|
+
throw refuse(
|
|
119
|
+
commandId,
|
|
120
|
+
key,
|
|
121
|
+
`${describe(value)} is not declared — declared: ${declared.join(", ")}`,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return value as T;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* A document path (`JxPath`) argument: an array of strings and numbers.
|
|
129
|
+
*
|
|
130
|
+
* The empty array is the document root and therefore VALID — `selection.set { path: [] }` is how a
|
|
131
|
+
* shot selects the root element, and treating `[]` as "no path" would silently deselect instead.
|
|
132
|
+
*/
|
|
133
|
+
export function pathArg(
|
|
134
|
+
commandId: string,
|
|
135
|
+
args: CommandArgValues,
|
|
136
|
+
key: string,
|
|
137
|
+
): (string | number)[] {
|
|
138
|
+
const value = args[key];
|
|
139
|
+
if (
|
|
140
|
+
!Array.isArray(value) ||
|
|
141
|
+
!value.every((s) => typeof s === "string" || typeof s === "number")
|
|
142
|
+
) {
|
|
143
|
+
throw refuse(
|
|
144
|
+
commandId,
|
|
145
|
+
key,
|
|
146
|
+
`expected an array of path segments (strings or numbers), got ${describe(value)}`,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
return value as (string | number)[];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** A path argument that may be `null` — "select nothing" is a state a user can be in. */
|
|
153
|
+
export function nullablePathArg(
|
|
154
|
+
commandId: string,
|
|
155
|
+
args: CommandArgValues,
|
|
156
|
+
key: string,
|
|
157
|
+
): (string | number)[] | null {
|
|
158
|
+
if (args[key] === null) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
return pathArg(commandId, args, key);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* A LIST of document paths — the whole selection set (§6.5).
|
|
166
|
+
*
|
|
167
|
+
* Every element is validated as a path, so a caller that passes one bare path by mistake
|
|
168
|
+
* (`["children", 0]` rather than `[["children", 0]]`) is refused by name instead of selecting two
|
|
169
|
+
* nodes called "children" and "0". `[]` is legal and means "select nothing", which is the same
|
|
170
|
+
* thing `selection.set { path: null }` means.
|
|
171
|
+
*/
|
|
172
|
+
export function pathListArg(
|
|
173
|
+
commandId: string,
|
|
174
|
+
args: CommandArgValues,
|
|
175
|
+
key: string,
|
|
176
|
+
): (string | number)[][] {
|
|
177
|
+
const value = args[key];
|
|
178
|
+
if (!Array.isArray(value)) {
|
|
179
|
+
throw refuse(commandId, key, `expected an array of document paths, got ${describe(value)}`);
|
|
180
|
+
}
|
|
181
|
+
return value.map((entry, i) => {
|
|
182
|
+
if (
|
|
183
|
+
!Array.isArray(entry) ||
|
|
184
|
+
!entry.every((s) => typeof s === "string" || typeof s === "number")
|
|
185
|
+
) {
|
|
186
|
+
throw refuse(
|
|
187
|
+
commandId,
|
|
188
|
+
key,
|
|
189
|
+
`entry ${i} is not a document path — expected an array of segments, got ${describe(entry)}`,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
return entry as (string | number)[];
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ─── Schema fragments ─────────────────────────────────────────────────────────
|
|
197
|
+
// The declarations above are enforced at RUN time; these are the same facts in the form the palette
|
|
198
|
+
// Prompt, the AI tool's parameter list and Lane 1's static check read. Keeping the pair adjacent is
|
|
199
|
+
// Deliberate: a schema that disagrees with its coercion is the defect this module exists to prevent.
|
|
200
|
+
|
|
201
|
+
/** `{ type: "object", properties, required, additionalProperties: false }` in one call. */
|
|
202
|
+
export function argsSchema(
|
|
203
|
+
properties: Record<string, object>,
|
|
204
|
+
required: readonly string[] = Object.keys(properties),
|
|
205
|
+
): object {
|
|
206
|
+
return {
|
|
207
|
+
additionalProperties: false,
|
|
208
|
+
properties,
|
|
209
|
+
required: [...required],
|
|
210
|
+
type: "object",
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** A string property constrained to a declared set, with the human sentence the palette shows. */
|
|
215
|
+
export function enumProperty(declared: readonly string[], description: string): object {
|
|
216
|
+
return { description, enum: [...declared], type: "string" };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** A number property, optionally bounded — the bounds a control already enforces, written down. */
|
|
220
|
+
export function numberProperty(
|
|
221
|
+
description: string,
|
|
222
|
+
bounds: { minimum?: number; maximum?: number } = {},
|
|
223
|
+
): object {
|
|
224
|
+
return { description, type: "number", ...bounds };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** A boolean property. Named `open`/`enabled`/… by the caller; the description carries the sense. */
|
|
228
|
+
export function booleanProperty(description: string): object {
|
|
229
|
+
return { description, type: "boolean" };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** A `JxPath` property — an array of string keys and numeric indexes. */
|
|
233
|
+
export function pathProperty(description: string, nullable = false): object {
|
|
234
|
+
const path = {
|
|
235
|
+
items: { type: ["string", "number"] },
|
|
236
|
+
type: "array",
|
|
237
|
+
};
|
|
238
|
+
return nullable ? { description, oneOf: [path, { type: "null" }] } : { description, ...path };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** A property holding a LIST of `JxPath`s — an array of arrays of segments. */
|
|
242
|
+
export function pathListProperty(description: string): object {
|
|
243
|
+
return {
|
|
244
|
+
description,
|
|
245
|
+
items: { items: { type: ["string", "number"] }, type: "array" },
|
|
246
|
+
type: "array",
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** A free-form string property. */
|
|
251
|
+
export function stringProperty(description: string): object {
|
|
252
|
+
return { description, type: "string" };
|
|
253
|
+
}
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context.ts — the record every `when` / `enablement` predicate closes over.
|
|
3
|
+
*
|
|
4
|
+
* One flat-ish record of the facts a command can be gated on (UX-REDESIGN-PLAN §5.2). Predicates
|
|
5
|
+
* are plain closures over this record — the shape `services/gated-registry.ts` already ships for
|
|
6
|
+
* the AI's tools — not a serialisable string DSL. §13 rejects the DSL explicitly: a `"project.open
|
|
7
|
+
* && editor.kind == 'canvas'"` grammar needs a tokenizer, a parser and a reactive evaluator to buy
|
|
8
|
+
* serialisability nothing in Studio consumes.
|
|
9
|
+
*
|
|
10
|
+
* This module owns the SHAPE and a pure builder. It deliberately imports nothing from the state
|
|
11
|
+
* modules: the registry takes a `getContext()` thunk, so wiring the live sources (the reactive
|
|
12
|
+
* `shell` record, `workspace`, `activeTab`) is a next-wave change to one function, and every test
|
|
13
|
+
* here builds its own context with {@link makeContext} instead of standing up the app.
|
|
14
|
+
*
|
|
15
|
+
* `capability.*` mirrors the Platform Abstraction Layer so cloud / desktop / dev-server differences
|
|
16
|
+
* stop being `if (platform.x)` scattered across templates and become one `when` clause per
|
|
17
|
+
* command.
|
|
18
|
+
*
|
|
19
|
+
* {@link keyScopeStack} lives here for the same reason: it is a pure function of this record, so
|
|
20
|
+
* the keyboard's "where am I" question is answered by the same facts every `when` predicate reads,
|
|
21
|
+
* rather than by a second set of `if (canvasMode === …)` branches in the listener.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { KeyScope } from "./levels";
|
|
25
|
+
import type { JxPath } from "../state";
|
|
26
|
+
|
|
27
|
+
/** PAL-derived capability keys. One `when` clause replaces a scattered `if (platform.x)`. */
|
|
28
|
+
export const CAPABILITIES = [
|
|
29
|
+
"gitClone",
|
|
30
|
+
"importSite",
|
|
31
|
+
"openProjectInNewWindow",
|
|
32
|
+
"dataRows",
|
|
33
|
+
"windowControls",
|
|
34
|
+
"findReferences",
|
|
35
|
+
] as const;
|
|
36
|
+
|
|
37
|
+
export type Capability = (typeof CAPABILITIES)[number];
|
|
38
|
+
|
|
39
|
+
/** Which shell region owns focus. Drives the keyboard scope stack and F6 region cycling. */
|
|
40
|
+
export type FocusRegion =
|
|
41
|
+
| "rail"
|
|
42
|
+
| "navigator"
|
|
43
|
+
| "pane"
|
|
44
|
+
| "inspector"
|
|
45
|
+
| "dock"
|
|
46
|
+
| "status"
|
|
47
|
+
| "palette";
|
|
48
|
+
|
|
49
|
+
/** The editor kind the focused pane is rendering. */
|
|
50
|
+
export type EditorKind =
|
|
51
|
+
| "canvas"
|
|
52
|
+
| "grid"
|
|
53
|
+
| "code"
|
|
54
|
+
| "diff"
|
|
55
|
+
| "library"
|
|
56
|
+
| "config"
|
|
57
|
+
| "entry"
|
|
58
|
+
| "none";
|
|
59
|
+
|
|
60
|
+
/** The Canvas view axis — one control, three values (§4.2). */
|
|
61
|
+
export type CanvasView = "edit" | "design" | "preview";
|
|
62
|
+
|
|
63
|
+
/*
|
|
64
|
+
* ─── Modes → the two axes ────────────────────────────────────────────────────
|
|
65
|
+
*
|
|
66
|
+
* `canvasMode` is ONE string conflating two orthogonal questions: WHICH EDITOR is open, and (for
|
|
67
|
+
* the Canvas editor only) WHICH VIEW of it. Both maps live here, beside the types they produce and
|
|
68
|
+
* beside `keyScopeStack`, because they are the same fact and there must be exactly one place to add
|
|
69
|
+
* an entry to.
|
|
70
|
+
*
|
|
71
|
+
* They used to be two copies — one in `tabs/tab.ts` driving the pane model, one in
|
|
72
|
+
* `commands/live-context.ts` driving every `when` predicate and the keyboard — each falling through
|
|
73
|
+
* to its own `?? "canvas"` default. Neither learned about `settings` when Project Settings became a
|
|
74
|
+
* document, so a JSON configuration object resolved into the CANVAS key scope: ⌘V over Project
|
|
75
|
+
* Settings ran `edit.paste`, which inserted an element node at the root of `project.json`, through
|
|
76
|
+
* the transaction log, and saved it.
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
/** The `canvasMode` strings, mapped onto the editor each one actually names. */
|
|
80
|
+
const EDITOR_KIND_BY_MODE: Readonly<Record<string, EditorKind>> = {
|
|
81
|
+
design: "canvas",
|
|
82
|
+
edit: "canvas",
|
|
83
|
+
/* The Entry editor — `content/entry-editor.ts`'s ENTRY_MODE. A form over ONE content entry's
|
|
84
|
+
frontmatter, typed by its collection's schema; the same document the canvas edits the body of,
|
|
85
|
+
which is why it is a mode of that tab rather than a tab of its own. */
|
|
86
|
+
entry: "entry",
|
|
87
|
+
"git-diff": "diff",
|
|
88
|
+
grid: "grid",
|
|
89
|
+
manage: "library",
|
|
90
|
+
preview: "canvas",
|
|
91
|
+
/* Project Settings — `settings/settings-document.ts`'s SETTINGS_MODE. A form over `project.json`,
|
|
92
|
+
which is a configuration object and not a document tree; `stylebook` (Project Styles) is the
|
|
93
|
+
second editor over that same document and answers `config` for the same reason. */
|
|
94
|
+
settings: "config",
|
|
95
|
+
source: "code",
|
|
96
|
+
stylebook: "config",
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The editor kind a mode string names.
|
|
101
|
+
*
|
|
102
|
+
* An unknown mode reads as Canvas, and that default is only for modes STUDIO DOES NOT OWN: a format
|
|
103
|
+
* declares its own list (`format.studio.modes`) and every mode declared that way is a view of the
|
|
104
|
+
* artboard. A mode Studio itself introduces is not covered by it and belongs in the map above —
|
|
105
|
+
* which is what `settings` was not.
|
|
106
|
+
*/
|
|
107
|
+
/**
|
|
108
|
+
* Human names for the editor kinds, so no surface prints an enum at a reader — and so two surfaces
|
|
109
|
+
* cannot print DIFFERENT words for the same kind. The status bar and the pane context bar each had
|
|
110
|
+
* their own copy, and they disagreed: one said "Stylebook", the wire value, where the other said
|
|
111
|
+
* "Project Styles". §16.2 exists to stop the shell contradicting itself about what is on screen.
|
|
112
|
+
*/
|
|
113
|
+
export const EDITOR_KIND_LABELS: Readonly<Record<EditorKind, string>> = {
|
|
114
|
+
canvas: "Canvas",
|
|
115
|
+
code: "Code",
|
|
116
|
+
config: "Project Styles",
|
|
117
|
+
diff: "Diff",
|
|
118
|
+
entry: "Entry",
|
|
119
|
+
grid: "Grid",
|
|
120
|
+
library: "Library",
|
|
121
|
+
none: "None",
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export function editorKindForMode(mode: string): EditorKind {
|
|
125
|
+
return EDITOR_KIND_BY_MODE[mode] ?? "canvas";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Only the three canvas modes carry a view. */
|
|
129
|
+
const CANVAS_VIEW_BY_MODE: Readonly<Record<string, CanvasView>> = {
|
|
130
|
+
design: "design",
|
|
131
|
+
edit: "edit",
|
|
132
|
+
preview: "preview",
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The Canvas view a mode names — the neutral "design" for a mode that has none.
|
|
137
|
+
*
|
|
138
|
+
* Meaningful only where {@link editorKindForMode} answers `"canvas"`; every reader pairs the two.
|
|
139
|
+
*/
|
|
140
|
+
export function canvasViewForMode(mode: string): CanvasView {
|
|
141
|
+
return CANVAS_VIEW_BY_MODE[mode] ?? "design";
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Facts a command may be gated on. Every field is a plain value; predicates read, never write. */
|
|
145
|
+
export interface CommandContext {
|
|
146
|
+
project: {
|
|
147
|
+
open: boolean;
|
|
148
|
+
isSite: boolean;
|
|
149
|
+
isRepo: boolean;
|
|
150
|
+
/** The project declares two or more locales — the gate on every `i18n.*` verb and surface. */
|
|
151
|
+
isMultilingual: boolean;
|
|
152
|
+
};
|
|
153
|
+
git: {
|
|
154
|
+
ahead: number;
|
|
155
|
+
behind: number;
|
|
156
|
+
dirtyCount: number;
|
|
157
|
+
};
|
|
158
|
+
document: {
|
|
159
|
+
open: boolean;
|
|
160
|
+
dirty: boolean;
|
|
161
|
+
/** Format class of the open document — "json", "md", "css", … */
|
|
162
|
+
mode: string;
|
|
163
|
+
canUndo: boolean;
|
|
164
|
+
canRedo: boolean;
|
|
165
|
+
};
|
|
166
|
+
editor: {
|
|
167
|
+
kind: EditorKind;
|
|
168
|
+
};
|
|
169
|
+
canvas: {
|
|
170
|
+
view: CanvasView;
|
|
171
|
+
};
|
|
172
|
+
pane: {
|
|
173
|
+
count: number;
|
|
174
|
+
derived: boolean;
|
|
175
|
+
};
|
|
176
|
+
selection: {
|
|
177
|
+
count: number;
|
|
178
|
+
/**
|
|
179
|
+
* The selected document paths, in selection order — `[]` when nothing is selected (§6.5).
|
|
180
|
+
*
|
|
181
|
+
* The context is what `probe.state()` answers with, so this is how a script, a screenshot step
|
|
182
|
+
* or the assistant READS a multi-selection back. `count` is its length; the last entry is the
|
|
183
|
+
* primary, which every other fact in this group describes.
|
|
184
|
+
*/
|
|
185
|
+
paths: JxPath[];
|
|
186
|
+
/** Tag or node kind of the primary selection, "" when nothing is selected. */
|
|
187
|
+
kind: string;
|
|
188
|
+
isRoot: boolean;
|
|
189
|
+
isComponentInstance: boolean;
|
|
190
|
+
isLayoutNode: boolean;
|
|
191
|
+
/**
|
|
192
|
+
* Whether the selection IS a repeater — an `$prototype: "Array"` pseudo-element.
|
|
193
|
+
*
|
|
194
|
+
* A fact rather than a verb's precondition, in the idiom of `isComponentInstance`: a repeater
|
|
195
|
+
* has no child list (its content is the single `map` template), so several structural verbs
|
|
196
|
+
* mean nothing on one, and each of them used to re-derive it from the node it could not see.
|
|
197
|
+
*/
|
|
198
|
+
isRepeater: boolean;
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Whether a live text caret owns the keyboard. Derived host-side in `iframe-host.ts` from the
|
|
202
|
+
* editStart / selectionChanged / editEnd messages the canvas bridge already sends — this is the
|
|
203
|
+
* fact that stops ⌘C/⌘X/⌘V being stolen from a writer mid-sentence.
|
|
204
|
+
*/
|
|
205
|
+
caret: {
|
|
206
|
+
active: boolean;
|
|
207
|
+
/**
|
|
208
|
+
* Whether that caret is the CANVAS's, rather than a parent-realm text field's.
|
|
209
|
+
*
|
|
210
|
+
* `active` folds both together on purpose: for {@link keyScopeStack} they are the same fact —
|
|
211
|
+
* something is being typed into, so element-level chords must not fire. For a record they are
|
|
212
|
+
* not. `format.bold` acts on a run of text inside the selected node and is meaningless while
|
|
213
|
+
* the caret is in the Inspector's href field or the link popover's own URL box; a record gated
|
|
214
|
+
* on `active` alone would fire there, and `mod+k` would re-mount the popover the author is
|
|
215
|
+
* typing into.
|
|
216
|
+
*
|
|
217
|
+
* Sourced from the bridge alone (`isCaretActive()`), never from `isTextEntryFocused()`.
|
|
218
|
+
*/
|
|
219
|
+
inCanvas: boolean;
|
|
220
|
+
};
|
|
221
|
+
focus: {
|
|
222
|
+
region: FocusRegion;
|
|
223
|
+
};
|
|
224
|
+
modal: {
|
|
225
|
+
open: boolean;
|
|
226
|
+
};
|
|
227
|
+
collab: {
|
|
228
|
+
attached: boolean;
|
|
229
|
+
readOnly: boolean;
|
|
230
|
+
sourceCanonical: boolean;
|
|
231
|
+
};
|
|
232
|
+
ai: {
|
|
233
|
+
configured: boolean;
|
|
234
|
+
streaming: boolean;
|
|
235
|
+
};
|
|
236
|
+
capability: Record<Capability, boolean>;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** A partial context, one group at a time — what tests and call sites actually write. */
|
|
240
|
+
export type CommandContextPatch = {
|
|
241
|
+
[K in keyof CommandContext]?: Partial<CommandContext[K]>;
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* The zero context: no project, no document, no selection, no capabilities.
|
|
246
|
+
*
|
|
247
|
+
* This is the honest cold-start state, which makes it the right default for the `when` predicates
|
|
248
|
+
* to be tested against — a command that is enabled here has said so deliberately.
|
|
249
|
+
*/
|
|
250
|
+
/**
|
|
251
|
+
* The three selection predicates every node-editing verb is gated on.
|
|
252
|
+
*
|
|
253
|
+
* Here rather than in one verb module because two of them define records: `editor/shortcuts.ts`'s
|
|
254
|
+
* chord table and `editor/context-menu.ts`'s element family. Importing one from the other made a
|
|
255
|
+
* cycle (`shortcuts` already imports `copyNode`/`cutNode` from `context-menu`), and re-declaring
|
|
256
|
+
* them is how "an element selection" comes to mean two different things in two menus.
|
|
257
|
+
*/
|
|
258
|
+
|
|
259
|
+
/** The editor showing a document TREE. Every verb that addresses a node needs this. */
|
|
260
|
+
export const inCanvas = (ctx: CommandContext) => ctx.editor.kind === "canvas";
|
|
261
|
+
|
|
262
|
+
/** Something is selected — including the document root, which is a selection of one. */
|
|
263
|
+
export const hasSelection = (ctx: CommandContext) => inCanvas(ctx) && ctx.selection.count > 0;
|
|
264
|
+
|
|
265
|
+
/** A selection that is not the document element, i.e. a node with a parent to act relative to. */
|
|
266
|
+
export const hasElementSelection = (ctx: CommandContext) =>
|
|
267
|
+
hasSelection(ctx) && !ctx.selection.isRoot;
|
|
268
|
+
|
|
269
|
+
export function emptyContext(): CommandContext {
|
|
270
|
+
return {
|
|
271
|
+
project: { open: false, isSite: false, isRepo: false, isMultilingual: false },
|
|
272
|
+
git: { ahead: 0, behind: 0, dirtyCount: 0 },
|
|
273
|
+
document: { open: false, dirty: false, mode: "", canUndo: false, canRedo: false },
|
|
274
|
+
editor: { kind: "none" },
|
|
275
|
+
canvas: { view: "design" },
|
|
276
|
+
pane: { count: 1, derived: false },
|
|
277
|
+
selection: {
|
|
278
|
+
count: 0,
|
|
279
|
+
paths: [],
|
|
280
|
+
kind: "",
|
|
281
|
+
isRoot: false,
|
|
282
|
+
isComponentInstance: false,
|
|
283
|
+
isLayoutNode: false,
|
|
284
|
+
isRepeater: false,
|
|
285
|
+
},
|
|
286
|
+
caret: { active: false, inCanvas: false },
|
|
287
|
+
focus: { region: "pane" },
|
|
288
|
+
modal: { open: false },
|
|
289
|
+
collab: { attached: false, readOnly: false, sourceCanonical: false },
|
|
290
|
+
ai: { configured: false, streaming: false },
|
|
291
|
+
capability: {
|
|
292
|
+
gitClone: false,
|
|
293
|
+
importSite: false,
|
|
294
|
+
openProjectInNewWindow: false,
|
|
295
|
+
dataRows: false,
|
|
296
|
+
windowControls: false,
|
|
297
|
+
findReferences: false,
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Build a context by overriding groups of {@link emptyContext}.
|
|
304
|
+
*
|
|
305
|
+
* Group-level merge, not a deep one: `makeContext({ selection: { count: 1 } })` keeps the other
|
|
306
|
+
* selection fields at their empty defaults. One level is all the record has, and a general deep
|
|
307
|
+
* merge would be a second thing to get right.
|
|
308
|
+
*/
|
|
309
|
+
export function makeContext(patch: CommandContextPatch = {}): CommandContext {
|
|
310
|
+
const base = emptyContext();
|
|
311
|
+
for (const key of Object.keys(patch) as (keyof CommandContext)[]) {
|
|
312
|
+
const group = patch[key];
|
|
313
|
+
if (group) {
|
|
314
|
+
Object.assign(base[key], group);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return base;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* An overlay owns the keyboard outright.
|
|
322
|
+
*
|
|
323
|
+
* This is what replaces `shortcuts.ts`'s blanket `if (isModalOpen()) return`. Expressing it as a
|
|
324
|
+
* stack rather than a `!ctx.modal.open` term repeated in fourteen `when` predicates keeps the rule
|
|
325
|
+
* in ONE place — and a dialog that swallows every click has to swallow every chord too, including
|
|
326
|
+
* ones registered next week by a surface that never heard of dialogs. `palette` is the scope
|
|
327
|
+
* overlay-owned bindings register in; nothing outside it resolves while one is up.
|
|
328
|
+
*/
|
|
329
|
+
const MODAL_STACK: readonly KeyScope[] = ["palette"];
|
|
330
|
+
|
|
331
|
+
/** A live caret shadows the canvas: app-level chords still fire, element-level ones do not. */
|
|
332
|
+
const CARET_STACK: readonly KeyScope[] = ["caret", "global"];
|
|
333
|
+
|
|
334
|
+
/** Focus outside the pane grid. Dock-scoped bindings, then the app's. */
|
|
335
|
+
const DOCK_STACK: readonly KeyScope[] = ["dock", "global"];
|
|
336
|
+
|
|
337
|
+
const CANVAS_STACK: readonly KeyScope[] = ["canvas", "global"];
|
|
338
|
+
const GRID_STACK: readonly KeyScope[] = ["grid", "global"];
|
|
339
|
+
const CODE_STACK: readonly KeyScope[] = ["code", "global"];
|
|
340
|
+
|
|
341
|
+
/** No engine owns the keyboard — only app-level chords are live. */
|
|
342
|
+
const GLOBAL_STACK: readonly KeyScope[] = ["global"];
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* The scope stack for the current context, narrowest scope first.
|
|
346
|
+
*
|
|
347
|
+
* The ladder is `caret > grid/code engine > focused dock > global` (plan §5.3), and the whole point
|
|
348
|
+
* is that a scope which is not on the stack cannot fire AT ALL. Three hand-written guards collapse
|
|
349
|
+
* into it:
|
|
350
|
+
*
|
|
351
|
+
* - The blanket modal return → the `palette`-only stack;
|
|
352
|
+
* - The `canvasMode === "grid" && !["o","p","s","w","z","Z"].includes(key)` allow-list → the `grid`
|
|
353
|
+
* stack, whose six survivors are precisely the `global`-scoped commands;
|
|
354
|
+
* - The caret guard → the `caret` stack, which drops `canvas` so ⌘C/⌘X/⌘V/Delete stay off a sentence
|
|
355
|
+
* being typed while ⌘S still saves.
|
|
356
|
+
*
|
|
357
|
+
* Preview and the non-editing surfaces (the diff view, the media library, the stylebook) get the
|
|
358
|
+
* bare `global` stack. Preview draws no overlays and posts no hits, so a selection carried in from
|
|
359
|
+
* Design is invisible: every element-level chord there — destructive or not — acts on something the
|
|
360
|
+
* author cannot see. That is one rule where `shortcuts.ts` had two ad-hoc refusal sets which
|
|
361
|
+
* disagreed with each other (⌘X was refused, ⌘C was not).
|
|
362
|
+
*/
|
|
363
|
+
export function keyScopeStack(ctx: CommandContext): readonly KeyScope[] {
|
|
364
|
+
if (ctx.modal.open) {
|
|
365
|
+
return MODAL_STACK;
|
|
366
|
+
}
|
|
367
|
+
if (ctx.caret.active) {
|
|
368
|
+
return CARET_STACK;
|
|
369
|
+
}
|
|
370
|
+
if (ctx.focus.region !== "pane") {
|
|
371
|
+
return DOCK_STACK;
|
|
372
|
+
}
|
|
373
|
+
switch (ctx.editor.kind) {
|
|
374
|
+
case "grid": {
|
|
375
|
+
return GRID_STACK;
|
|
376
|
+
}
|
|
377
|
+
case "code": {
|
|
378
|
+
return CODE_STACK;
|
|
379
|
+
}
|
|
380
|
+
case "canvas": {
|
|
381
|
+
return ctx.canvas.view === "preview" ? GLOBAL_STACK : CANVAS_STACK;
|
|
382
|
+
}
|
|
383
|
+
default: {
|
|
384
|
+
return GLOBAL_STACK;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|