@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,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Levels.ts — the containment taxonomy and the level × placement matrix.
|
|
3
|
+
*
|
|
4
|
+
* Two vocabularies live here, and keeping them apart is the whole point (UX-REDESIGN-PLAN §5.1):
|
|
5
|
+
*
|
|
6
|
+
* - {@link Level} answers WHAT a record acts on. It governs placement and is CI-checked.
|
|
7
|
+
* - {@link KeyScope} answers WHERE a chord is live. It governs keyboard dispatch only.
|
|
8
|
+
*
|
|
9
|
+
* Conflating them is how "position encodes scope" degrades into unenforced prose. The clearest case
|
|
10
|
+
* is inline text formatting: Bold acts on a text range inside the selected node, so its `level` is
|
|
11
|
+
* `selection`, while its `keyScope` is `caret` — which is what makes the chord live only when a
|
|
12
|
+
* caret exists. There is deliberately no `range` level; a fifth level would demand a fifth region
|
|
13
|
+
* and there is none.
|
|
14
|
+
*
|
|
15
|
+
* {@link PLACEMENT_MATRIX} is the normative table: each placement declares the set of levels it
|
|
16
|
+
* admits. Mixed regions are mixed IN THE TABLE, never by prose exemption — the status bar is three
|
|
17
|
+
* separate single-level placements, not one "mixed" one. `scripts/check-command-levels.ts`
|
|
18
|
+
* validates every registered command's `menus` against this table, and `createCommandRegistry`
|
|
19
|
+
* applies the same check at registration so a violation cannot reach a running app either.
|
|
20
|
+
*
|
|
21
|
+
* Panel placements (the two rail groups, the navigator dock body, the bottom dock, the inspector
|
|
22
|
+
* dock) are part of the same matrix in `studio-ui-guidelines.md` §12 and live here too, in
|
|
23
|
+
* {@link PANEL_PLACEMENT_MATRIX}: `registerPanel()` (`panels/panel-registry.ts`) now exists, so a
|
|
24
|
+
* Panel record declares a `level` for the same reason a Command does and is checked the same way.
|
|
25
|
+
* The two tables are separate because their placement VOCABULARIES are disjoint — a command is
|
|
26
|
+
* never "in the rail's upper group", and a panel is never "in the block action bar".
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/** Containment level — WHAT a record acts on. Governs placement. CI-checked. */
|
|
30
|
+
export const LEVELS = ["application", "project", "document", "selection"] as const;
|
|
31
|
+
|
|
32
|
+
export type Level = (typeof LEVELS)[number];
|
|
33
|
+
|
|
34
|
+
/** Keyboard dispatch scope — WHERE a chord is live. Orthogonal to {@link Level}. */
|
|
35
|
+
export const KEY_SCOPES = ["global", "canvas", "caret", "grid", "code", "dock", "palette"] as const;
|
|
36
|
+
|
|
37
|
+
export type KeyScope = (typeof KEY_SCOPES)[number];
|
|
38
|
+
|
|
39
|
+
/** Command categories. The palette renders rows as "<Category>: <title>". */
|
|
40
|
+
export const CATEGORIES = [
|
|
41
|
+
"File",
|
|
42
|
+
"Edit",
|
|
43
|
+
"Selection",
|
|
44
|
+
"Insert",
|
|
45
|
+
"View",
|
|
46
|
+
"Document",
|
|
47
|
+
"Project",
|
|
48
|
+
"Source Control",
|
|
49
|
+
"Publish",
|
|
50
|
+
"Assistant",
|
|
51
|
+
"Collaborate",
|
|
52
|
+
"Help",
|
|
53
|
+
] as const;
|
|
54
|
+
|
|
55
|
+
export type Category = (typeof CATEGORIES)[number];
|
|
56
|
+
|
|
57
|
+
/** Every surface a command may declare itself into. `"never"` means keyboard/API only. */
|
|
58
|
+
export const PLACEMENTS = [
|
|
59
|
+
"commandbar/primary",
|
|
60
|
+
"commandbar/overflow",
|
|
61
|
+
"statusbar/project",
|
|
62
|
+
"statusbar/document",
|
|
63
|
+
"statusbar/selection",
|
|
64
|
+
"context/element",
|
|
65
|
+
"context/file",
|
|
66
|
+
"context/layer",
|
|
67
|
+
"context/tab",
|
|
68
|
+
"context/pane",
|
|
69
|
+
"blockbar",
|
|
70
|
+
/**
|
|
71
|
+
* The block action bar's SECOND cluster — inline formatting, shown while the caret is in text.
|
|
72
|
+
*
|
|
73
|
+
* A separate placement rather than a crowd inside `blockbar`, for the reason the status bar is
|
|
74
|
+
* three placements and not one "mixed" region: the bar's verb cluster is capped at five, and
|
|
75
|
+
* eight format verbs sharing that cap would push Bold into a `⋮` menu. Same level, same surface,
|
|
76
|
+
* different budget — which is a row in the table, not an exemption in prose.
|
|
77
|
+
*/
|
|
78
|
+
"blockbar/format",
|
|
79
|
+
"outline/row",
|
|
80
|
+
"palette",
|
|
81
|
+
"never",
|
|
82
|
+
] as const;
|
|
83
|
+
|
|
84
|
+
export type Placement = (typeof PLACEMENTS)[number];
|
|
85
|
+
|
|
86
|
+
/** One row of the matrix: the levels a placement admits, plus why it is drawn that way. */
|
|
87
|
+
export interface PlacementRule {
|
|
88
|
+
/** Levels this placement accepts. A command declaring any other level is a CI failure. */
|
|
89
|
+
admits: readonly Level[];
|
|
90
|
+
/** The reason, printed in the violation message so the fix is obvious from the failure. */
|
|
91
|
+
note: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The level × placement matrix — normative copy, mirrored into `studio-ui-guidelines.md` §12.
|
|
96
|
+
*
|
|
97
|
+
* Read a row as: "this region renders records of these levels, and nothing else."
|
|
98
|
+
*/
|
|
99
|
+
export const PLACEMENT_MATRIX: Readonly<Record<Placement, PlacementRule>> = {
|
|
100
|
+
"commandbar/primary": {
|
|
101
|
+
admits: ["application", "document"],
|
|
102
|
+
note: "document only for Save / Undo / Redo / Open in Browser, by frequency; ≤5 total",
|
|
103
|
+
},
|
|
104
|
+
"commandbar/overflow": {
|
|
105
|
+
admits: ["application", "project", "document"],
|
|
106
|
+
note: "never selection — the Command Bar is not a selection surface",
|
|
107
|
+
},
|
|
108
|
+
"statusbar/project": { admits: ["project"], note: "the status bar's left field" },
|
|
109
|
+
"statusbar/document": { admits: ["document"], note: "the status bar's centre field" },
|
|
110
|
+
"statusbar/selection": { admits: ["selection"], note: "the status bar's right field" },
|
|
111
|
+
"context/element": { admits: ["selection"], note: "the canvas element menu acts on a selection" },
|
|
112
|
+
"context/file": { admits: ["project"], note: "a file row addresses the project's file set" },
|
|
113
|
+
"context/layer": { admits: ["selection"], note: "an outline row IS a selection" },
|
|
114
|
+
"context/tab": { admits: ["document"], note: "a tab addresses one document" },
|
|
115
|
+
"context/pane": { admits: ["document"], note: "a pane hosts one document" },
|
|
116
|
+
blockbar: { admits: ["selection"], note: "the floating bar owns selection-scoped verbs" },
|
|
117
|
+
"blockbar/format": {
|
|
118
|
+
admits: ["selection"],
|
|
119
|
+
note: "the bar's inline-format cluster — a range inside the selection is still the selection",
|
|
120
|
+
},
|
|
121
|
+
"outline/row": { admits: ["selection"], note: "row actions act on the row's node" },
|
|
122
|
+
palette: {
|
|
123
|
+
admits: ["application", "project", "document", "selection"],
|
|
124
|
+
note: "the level-agnostic surface; it groups rows by level",
|
|
125
|
+
},
|
|
126
|
+
never: {
|
|
127
|
+
admits: ["application", "project", "document", "selection"],
|
|
128
|
+
note: "keyboard- and API-only; no rendered surface to be misplaced in",
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// ─── Panels ───────────────────────────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Every surface a Panel record may occupy.
|
|
136
|
+
*
|
|
137
|
+
* The rail's two level groups are SEPARATE placements, not one "mixed" region — principle 2's whole
|
|
138
|
+
* point. `navigator` is the dock body below them, which hosts whichever panel the rail selected and
|
|
139
|
+
* prints that panel's level in its header, so it is the one panel placement admitting two levels.
|
|
140
|
+
*/
|
|
141
|
+
export const PANEL_PLACEMENTS = [
|
|
142
|
+
"rail/project",
|
|
143
|
+
"rail/document",
|
|
144
|
+
"navigator",
|
|
145
|
+
"inspector",
|
|
146
|
+
"dock.bottom",
|
|
147
|
+
] as const;
|
|
148
|
+
|
|
149
|
+
export type PanelPlacement = (typeof PANEL_PLACEMENTS)[number];
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The level × placement matrix for Panel records — normative copy, mirrored into
|
|
153
|
+
* `studio-ui-guidelines.md` §12 beside {@link PLACEMENT_MATRIX}.
|
|
154
|
+
*
|
|
155
|
+
* The rail rows are what stop it re-accreting: a panel cannot be filed in the PROJECT group because
|
|
156
|
+
* "it feels project-ish", only because the state it WRITES is the project's (principle 3).
|
|
157
|
+
*/
|
|
158
|
+
export const PANEL_PLACEMENT_MATRIX: Readonly<Record<PanelPlacement, PlacementRule>> = {
|
|
159
|
+
"rail/project": {
|
|
160
|
+
admits: ["project"],
|
|
161
|
+
note: "the rail's upper group — Files, Search, Source Control, Problems",
|
|
162
|
+
},
|
|
163
|
+
"rail/document": {
|
|
164
|
+
admits: ["document"],
|
|
165
|
+
note: "the rail's lower group — Outline, Page, Data, Packages",
|
|
166
|
+
},
|
|
167
|
+
navigator: {
|
|
168
|
+
admits: ["project", "document"],
|
|
169
|
+
note: "whichever level the hosted panel declares; the panel header prints it",
|
|
170
|
+
},
|
|
171
|
+
inspector: { admits: ["selection"], note: "the Inspector's tabs are selection surfaces" },
|
|
172
|
+
"dock.bottom": { admits: ["project", "document"], note: "the panel header states which" },
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/** The subset of a Panel record this check reads. */
|
|
176
|
+
export interface PlaceablePanel {
|
|
177
|
+
id: string;
|
|
178
|
+
level: Level;
|
|
179
|
+
/** The dock hosting the panel's body. */
|
|
180
|
+
dock: "navigator" | "inspector" | "bottom";
|
|
181
|
+
/** `false` for a panel with no rail button — it declares no rail-group placement. */
|
|
182
|
+
rail?: boolean | undefined;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** One rejected (panel, placement) pair. Shaped like {@link PlacementViolation}, keyed by panel. */
|
|
186
|
+
export interface PanelPlacementViolation {
|
|
187
|
+
panelId: string;
|
|
188
|
+
placement: string;
|
|
189
|
+
level: string;
|
|
190
|
+
message: string;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** The dock body placement a panel's `dock` names. */
|
|
194
|
+
function dockPlacement(dock: PlaceablePanel["dock"]): PanelPlacement {
|
|
195
|
+
return dock === "bottom" ? "dock.bottom" : dock;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Every placement a Panel record occupies: its dock body, plus its rail group when it has a button.
|
|
200
|
+
*
|
|
201
|
+
* The rail group is DERIVED from the level rather than declared, which is what makes "grouped by
|
|
202
|
+
* level with a divider" (§3.2 ②) a property of the data instead of an ordering convention in the
|
|
203
|
+
* rail's template. A rail panel whose level has no group (`application`, `selection`) therefore
|
|
204
|
+
* names a placement the matrix does not contain, and is rejected below.
|
|
205
|
+
*/
|
|
206
|
+
export function panelPlacements(panel: PlaceablePanel): string[] {
|
|
207
|
+
const placements: string[] = [dockPlacement(panel.dock)];
|
|
208
|
+
if (panel.rail !== false) {
|
|
209
|
+
placements.push(`rail/${panel.level}`);
|
|
210
|
+
}
|
|
211
|
+
return placements;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Whether `value` is one of the {@link PANEL_PLACEMENTS}. */
|
|
215
|
+
export function isPanelPlacement(value: string): value is PanelPlacement {
|
|
216
|
+
return (PANEL_PLACEMENTS as readonly string[]).includes(value);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Check one Panel record against {@link PANEL_PLACEMENT_MATRIX}. */
|
|
220
|
+
export function checkPanelPlacement(panel: PlaceablePanel): PanelPlacementViolation[] {
|
|
221
|
+
const violations: PanelPlacementViolation[] = [];
|
|
222
|
+
if (!isLevel(panel.level)) {
|
|
223
|
+
violations.push({
|
|
224
|
+
panelId: panel.id,
|
|
225
|
+
placement: "—",
|
|
226
|
+
level: String(panel.level),
|
|
227
|
+
message:
|
|
228
|
+
`declares unknown level "${String(panel.level)}" ` +
|
|
229
|
+
`(expected one of: ${LEVELS.join(", ")})`,
|
|
230
|
+
});
|
|
231
|
+
return violations;
|
|
232
|
+
}
|
|
233
|
+
for (const placement of panelPlacements(panel)) {
|
|
234
|
+
if (!isPanelPlacement(placement)) {
|
|
235
|
+
violations.push({
|
|
236
|
+
panelId: panel.id,
|
|
237
|
+
placement,
|
|
238
|
+
level: panel.level,
|
|
239
|
+
message:
|
|
240
|
+
`is level "${panel.level}" and would render in "${placement}", which is not a panel ` +
|
|
241
|
+
`placement — the rail has a group for project and one for document, and nothing else`,
|
|
242
|
+
});
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const rule = PANEL_PLACEMENT_MATRIX[placement];
|
|
246
|
+
if (!rule.admits.includes(panel.level)) {
|
|
247
|
+
violations.push({
|
|
248
|
+
panelId: panel.id,
|
|
249
|
+
placement,
|
|
250
|
+
level: panel.level,
|
|
251
|
+
message:
|
|
252
|
+
`is level "${panel.level}" but "${placement}" admits only ` +
|
|
253
|
+
`${rule.admits.join(", ")} — ${rule.note}`,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return violations;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Check a whole panel set. Empty result = the set satisfies the matrix. */
|
|
261
|
+
export function checkPanelPlacements(panels: readonly PlaceablePanel[]): PanelPlacementViolation[] {
|
|
262
|
+
return panels.flatMap((panel) => checkPanelPlacement(panel));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ─── Commands ─────────────────────────────────────────────────────────────────
|
|
266
|
+
|
|
267
|
+
/** The subset of a command record the placement check needs. */
|
|
268
|
+
export interface PlaceableRecord {
|
|
269
|
+
id: string;
|
|
270
|
+
level: Level;
|
|
271
|
+
menus?: readonly Placement[] | undefined;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** One rejected (command, placement) pair. */
|
|
275
|
+
export interface PlacementViolation {
|
|
276
|
+
commandId: string;
|
|
277
|
+
placement: string;
|
|
278
|
+
level: string;
|
|
279
|
+
/** Ready-to-print explanation: what was declared, what the row admits, and why. */
|
|
280
|
+
message: string;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** Whether `placement` is one of the {@link PLACEMENTS}. */
|
|
284
|
+
export function isPlacement(value: string): value is Placement {
|
|
285
|
+
return (PLACEMENTS as readonly string[]).includes(value);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** Whether `level` is one of the {@link LEVELS}. */
|
|
289
|
+
export function isLevel(value: string): value is Level {
|
|
290
|
+
return (LEVELS as readonly string[]).includes(value);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** Whether the matrix lets a `level` record render in `placement`. */
|
|
294
|
+
export function placementAdmits(placement: Placement, level: Level): boolean {
|
|
295
|
+
return PLACEMENT_MATRIX[placement].admits.includes(level);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Check one record's declared placements against the matrix.
|
|
300
|
+
*
|
|
301
|
+
* A record with no `menus` defaults to `["palette"]`, which admits every level, so the default is
|
|
302
|
+
* always legal — a command has to opt IN to a region before it can be misplaced.
|
|
303
|
+
*/
|
|
304
|
+
export function checkRecordPlacements(record: PlaceableRecord): PlacementViolation[] {
|
|
305
|
+
const violations: PlacementViolation[] = [];
|
|
306
|
+
if (!isLevel(record.level)) {
|
|
307
|
+
violations.push({
|
|
308
|
+
commandId: record.id,
|
|
309
|
+
placement: "—",
|
|
310
|
+
level: String(record.level),
|
|
311
|
+
message:
|
|
312
|
+
`declares unknown level "${String(record.level)}" ` +
|
|
313
|
+
`(expected one of: ${LEVELS.join(", ")})`,
|
|
314
|
+
});
|
|
315
|
+
return violations;
|
|
316
|
+
}
|
|
317
|
+
for (const placement of record.menus ?? []) {
|
|
318
|
+
if (!isPlacement(placement)) {
|
|
319
|
+
violations.push({
|
|
320
|
+
commandId: record.id,
|
|
321
|
+
placement: String(placement),
|
|
322
|
+
level: record.level,
|
|
323
|
+
message: `declares unknown placement "${String(placement)}"`,
|
|
324
|
+
});
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const rule = PLACEMENT_MATRIX[placement];
|
|
328
|
+
if (!rule.admits.includes(record.level)) {
|
|
329
|
+
violations.push({
|
|
330
|
+
commandId: record.id,
|
|
331
|
+
placement,
|
|
332
|
+
level: record.level,
|
|
333
|
+
message:
|
|
334
|
+
`is level "${record.level}" but "${placement}" admits only ` +
|
|
335
|
+
`${rule.admits.join(", ")} — ${rule.note}`,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return violations;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** Check a whole command set. Empty result = the set satisfies the matrix. */
|
|
343
|
+
export function checkPlacements(records: readonly PlaceableRecord[]): PlacementViolation[] {
|
|
344
|
+
return records.flatMap((record) => checkRecordPlacements(record));
|
|
345
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
/**
|
|
3
|
+
* Live-context.ts — the running app's answer to `getContext()`.
|
|
4
|
+
*
|
|
5
|
+
* `context.ts` owns the SHAPE and refuses to import a state module, which is what lets the CI
|
|
6
|
+
* checks load the command set in a bare Bun process. This file is the other half: it reads the
|
|
7
|
+
* three places state actually lives — the reactive `shell` record, `workspace` / `activeTab`, and
|
|
8
|
+
* `projectState` — and projects them onto that shape, once per predicate evaluation.
|
|
9
|
+
*
|
|
10
|
+
* **A fresh snapshot per call, deliberately.** The registry calls `getContext()` inside
|
|
11
|
+
* `visible()`, `isEnabled()` and `run()`, so a surface that repaints from an effect reads
|
|
12
|
+
* `shell.focusRegion`, `activeTab.value` and the tab's session THROUGH this builder while that
|
|
13
|
+
* effect is collecting dependencies — the effect therefore tracks them and re-runs when they
|
|
14
|
+
* change. Caching the record would be the one change that breaks that, and there is nothing to
|
|
15
|
+
* cache: every field is a property read or a `typeof fn === "function"` test.
|
|
16
|
+
*
|
|
17
|
+
* The recorded pitfall for reactive state — build nested collections complete BEFORE inserting
|
|
18
|
+
* them, because writes through a raw ref skip effects — does not bite here for the same reason: the
|
|
19
|
+
* returned record is a plain, complete, throwaway object. It is never made reactive and never
|
|
20
|
+
* mutated after construction.
|
|
21
|
+
*
|
|
22
|
+
* Four facts arrive by injection rather than by import: the canvas mode (owned by `studio.ts`), the
|
|
23
|
+
* caret and modal flags (`canvas/iframe-host.ts`, `ui/layers.ts`) and the platform. Injecting them
|
|
24
|
+
* keeps this module free of the import cycle studio.ts → shortcuts.ts → live-context.ts →
|
|
25
|
+
* studio.ts, and lets a test state a context without standing up the app.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { resolveI18n } from "@jxsuite/schema/locale";
|
|
29
|
+
import { componentRegistry } from "../files/components";
|
|
30
|
+
import { getNodeAtPath, projectState } from "../store";
|
|
31
|
+
import { shell } from "../shell";
|
|
32
|
+
import { activeTab, workspace } from "../workspace/workspace";
|
|
33
|
+
import { primarySelection } from "../tabs/selection";
|
|
34
|
+
import { canRedo, canUndo } from "../tabs/transact";
|
|
35
|
+
import { collabState } from "../collab/collab-state";
|
|
36
|
+
|
|
37
|
+
import { canvasViewForMode, editorKindForMode, emptyContext } from "./context";
|
|
38
|
+
import type { CommandContext, FocusRegion } from "./context";
|
|
39
|
+
import type { StudioPlatform } from "../types";
|
|
40
|
+
|
|
41
|
+
/** Elements that own the keyboard while focused. Matches the old listener's target test exactly. */
|
|
42
|
+
const TEXT_ENTRY =
|
|
43
|
+
"input, textarea, select, sp-textfield, sp-search, sp-number-field, sp-picker, [contenteditable=true]";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Whether a parent-realm text control has focus.
|
|
47
|
+
*
|
|
48
|
+
* The old listener asked this of `e.target`; asking it of `document.activeElement` is the same
|
|
49
|
+
* answer — a keydown's target IS the focused element, and shadow-DOM retargeting maps the inner
|
|
50
|
+
* `<input>` of an `sp-textfield` onto the host in both cases — but it makes the fact part of the
|
|
51
|
+
* CONTEXT instead of part of the event, so a `when` predicate can read it too.
|
|
52
|
+
*/
|
|
53
|
+
export function isTextEntryFocused(doc: Document = document): boolean {
|
|
54
|
+
const el = doc.activeElement;
|
|
55
|
+
return el instanceof HTMLElement && el.matches(TEXT_ENTRY);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The facts this module cannot reach without an import cycle or a DOM/platform singleton. */
|
|
59
|
+
export interface LiveContextSources {
|
|
60
|
+
/** `studio.ts`'s `getCanvasMode()` — the per-tab preview toggle already composed in. */
|
|
61
|
+
canvasMode: () => string;
|
|
62
|
+
/** `canvas/iframe-host.ts`'s bridge-derived caret flag (P1). */
|
|
63
|
+
isCaretActive: () => boolean;
|
|
64
|
+
/** `ui/layers.ts`'s `isModalOpen()`. */
|
|
65
|
+
isModalOpen: () => boolean;
|
|
66
|
+
/** The registered platform, or null before one is registered. */
|
|
67
|
+
platform: () => StudioPlatform | null;
|
|
68
|
+
/** Whether the assistant has usable credentials (`services/ai-models`). */
|
|
69
|
+
aiConfigured: () => boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Whether the assistant is mid-stream. `panels/ai-panel.ts` keeps its `DocumentAssistant` module-
|
|
72
|
+
* private, so there is nothing to read yet; the caller passes a probe when one exists.
|
|
73
|
+
*/
|
|
74
|
+
aiStreaming?: () => boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** `capability.*` is presence-of-method on the PAL — the check every call site does by hand today. */
|
|
78
|
+
function capabilities(platform: StudioPlatform | null): CommandContext["capability"] {
|
|
79
|
+
const has = (key: keyof StudioPlatform) => typeof platform?.[key] === "function";
|
|
80
|
+
return {
|
|
81
|
+
dataRows: has("dataRows"),
|
|
82
|
+
// The real member now, not the `codeService` stand-in it was minted against: the usage query is
|
|
83
|
+
// Its own route over the rename refactor's walker, and cloud computes it server-side.
|
|
84
|
+
findReferences: has("findReferences"),
|
|
85
|
+
gitClone: has("gitClone"),
|
|
86
|
+
importSite: has("importSite"),
|
|
87
|
+
openProjectInNewWindow: has("openProjectInNewWindow"),
|
|
88
|
+
// Multi-window is a desktop-only family; `newWindow` is the one every member implies.
|
|
89
|
+
windowControls: has("newWindow"),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build the live `getContext` thunk.
|
|
95
|
+
*
|
|
96
|
+
* @returns A function the registry calls per predicate evaluation.
|
|
97
|
+
*/
|
|
98
|
+
export function createLiveContext(sources: LiveContextSources): () => CommandContext {
|
|
99
|
+
return () => {
|
|
100
|
+
const ctx = emptyContext();
|
|
101
|
+
const tab = activeTab.value ?? null;
|
|
102
|
+
const project = projectState;
|
|
103
|
+
const mode = sources.canvasMode();
|
|
104
|
+
|
|
105
|
+
ctx.project.open = project !== null;
|
|
106
|
+
ctx.project.isSite = project?.isSiteProject === true;
|
|
107
|
+
// A project is a repository once git has answered for it. Before the first refresh the honest
|
|
108
|
+
// Answer is "not yet", and Source Control's own commands are the ones that refresh it.
|
|
109
|
+
ctx.project.isRepo = shell.git.status?.isRepo === true;
|
|
110
|
+
/*
|
|
111
|
+
* Two locales, not two entries in the array: the shared resolver canonicalizes and drops what
|
|
112
|
+
* it cannot parse, so `["en", "en", "not_a_tag"]` is one locale and gates nothing on. Reading
|
|
113
|
+
* `.length` off the raw config would open every i18n surface on a project that has none.
|
|
114
|
+
*
|
|
115
|
+
* `@jxsuite/schema/locale` and not `site-context.ts`: everything reachable from
|
|
116
|
+
* `app-commands.ts` has to load in a bare Bun process with no DOM, and that module reaches the
|
|
117
|
+
* platform. This one is `Intl` and string math.
|
|
118
|
+
*/
|
|
119
|
+
ctx.project.isMultilingual =
|
|
120
|
+
(resolveI18n(project?.projectConfig ?? {}).i18n?.locales.length ?? 0) > 1;
|
|
121
|
+
ctx.git.ahead = shell.git.status?.ahead ?? 0;
|
|
122
|
+
ctx.git.behind = shell.git.status?.behind ?? 0;
|
|
123
|
+
ctx.git.dirtyCount = shell.git.status?.files.length ?? 0;
|
|
124
|
+
|
|
125
|
+
ctx.document.open = tab !== null;
|
|
126
|
+
ctx.document.dirty = tab?.doc.dirty === true;
|
|
127
|
+
ctx.document.mode = tab?.doc.mode ?? "";
|
|
128
|
+
ctx.document.canUndo = tab ? canUndo(tab) : false;
|
|
129
|
+
ctx.document.canRedo = tab ? canRedo(tab) : false;
|
|
130
|
+
|
|
131
|
+
ctx.editor.kind = tab ? editorKindForMode(mode) : "none";
|
|
132
|
+
ctx.canvas.view = canvasViewForMode(mode);
|
|
133
|
+
/* The grid, counted from the grid. This read spent two phases saying "panes land in P3; until
|
|
134
|
+
then the grid is one pane showing the focused tab" — through P3, which built the pane model,
|
|
135
|
+
and through P8, which gave every pane a live stage. It counted `workspace.tabOrder`, the
|
|
136
|
+
FOCUSED pane's strip, so it could never answer 2 and answered 0 whenever no tab was open,
|
|
137
|
+
while a pane with no tab is still a pane. `derived` was declared and never assigned at all.
|
|
138
|
+
`services/automation.ts` publishes this whole record as `probe.state()`, so the screenshot
|
|
139
|
+
pipeline and the assistant were both reading it.
|
|
140
|
+
|
|
141
|
+
`derived` is ASSIGNED now, and it means "the grid contains a derived pane" — the fact the two
|
|
142
|
+
predicates that need it actually ask. A third phase of a declared-never-assigned context key
|
|
143
|
+
is not caution; it is the `layoutSelection`-with-no-writer shape this codebase keeps
|
|
144
|
+
catching. */
|
|
145
|
+
ctx.pane.count = workspace.panes.length;
|
|
146
|
+
ctx.pane.derived = workspace.panes.some((pane) => pane.derived !== null);
|
|
147
|
+
|
|
148
|
+
const paths = tab?.session.selection ?? [];
|
|
149
|
+
const selection = primarySelection(paths);
|
|
150
|
+
if (selection) {
|
|
151
|
+
const node = getNodeAtPath(tab!.doc.document, selection);
|
|
152
|
+
ctx.selection.count = paths.length;
|
|
153
|
+
ctx.selection.paths = paths.map((path) => [...path]);
|
|
154
|
+
// Every remaining fact describes the PRIMARY — the one node a single-target verb addresses —
|
|
155
|
+
// Except `isRoot`, which is the batch's own gate: a batch containing the document element
|
|
156
|
+
// Cannot be deleted or duplicated any more than that element could be on its own.
|
|
157
|
+
ctx.selection.kind = typeof node?.tagName === "string" ? node.tagName : "";
|
|
158
|
+
// A path of fewer than two segments addresses the document element itself — the same test
|
|
159
|
+
// `shortcuts.ts` spelled as `selection.length >= 2` at four separate call sites.
|
|
160
|
+
ctx.selection.isRoot = paths.some((path) => path.length < 2);
|
|
161
|
+
ctx.selection.isComponentInstance = componentRegistry.some(
|
|
162
|
+
(c) => c.tagName === node?.tagName,
|
|
163
|
+
);
|
|
164
|
+
ctx.selection.isRepeater = node?.$prototype === "Array";
|
|
165
|
+
}
|
|
166
|
+
// Layout chrome is NOT a document selection: the node is not in the open page at all.
|
|
167
|
+
ctx.selection.isLayoutNode = shell.layoutSelection !== null;
|
|
168
|
+
|
|
169
|
+
// A parent-realm text field owns the keyboard exactly as a canvas caret does. Folding both into
|
|
170
|
+
// One key is what lets `keyScopeStack` stay a pure function of the context.
|
|
171
|
+
ctx.caret.active = sources.isCaretActive() || isTextEntryFocused();
|
|
172
|
+
/* …and folding them is wrong for a RECORD, which is why `inCanvas` is the bridge fact alone.
|
|
173
|
+
`format.bold` and `format.link` are `keyScope: "caret"`, so they are live wherever the caret
|
|
174
|
+
stack is — including the link popover's own URL field, where ⌘K would re-mount the popover
|
|
175
|
+
being typed into. The stack asks "is something being typed into"; a record asks "is the
|
|
176
|
+
thing being typed into the page". */
|
|
177
|
+
ctx.caret.inCanvas = sources.isCaretActive();
|
|
178
|
+
ctx.focus.region = shell.focusRegion as FocusRegion;
|
|
179
|
+
ctx.modal.open = sources.isModalOpen();
|
|
180
|
+
|
|
181
|
+
if (tab) {
|
|
182
|
+
const collab = collabState(tab);
|
|
183
|
+
ctx.collab.attached = collab.active;
|
|
184
|
+
ctx.collab.readOnly = collab.readOnly;
|
|
185
|
+
ctx.collab.sourceCanonical = collab.sourceCanonical;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
ctx.ai.configured = sources.aiConfigured();
|
|
189
|
+
ctx.ai.streaming = sources.aiStreaming?.() ?? false;
|
|
190
|
+
ctx.capability = capabilities(sources.platform());
|
|
191
|
+
return ctx;
|
|
192
|
+
};
|
|
193
|
+
}
|