@jxsuite/studio 2.0.0 → 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-0hzentwq.js → components-2bpqp4mq.js} +5 -7
- package/dist/chunks/{components-0hzentwq.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-2c188n41.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-97qcgvdn.js → studio-adn0z1k3.js} +14 -8
- package/dist/chunks/{studio-97qcgvdn.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-hhj92gcr.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-ygzav7z5.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-n9zmpxjg.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 +23711 -35185
- 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 +34 -9
- package/src/new-project/location-fields.ts +9 -1
- package/src/new-project/new-project-modal.ts +270 -262
- 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 -120
- 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 +75 -2
- 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 +113 -0
- 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-qct0gt8f.js +0 -531
- package/dist/chunks/extension-sections-qct0gt8f.js.map +0 -12
- package/dist/chunks/files-02zn5y24.js +0 -50
- package/dist/chunks/files-02zn5y24.js.map +0 -9
- package/dist/chunks/git-panel-mbb0y0hz.js +0 -28
- package/dist/chunks/git-panel-mbb0y0hz.js.map +0 -9
- package/dist/chunks/layers-panel-9qekmqk1.js +0 -26
- package/dist/chunks/layers-panel-9qekmqk1.js.map +0 -9
- package/dist/chunks/settings-modal-gj0hrah4.js +0 -29
- package/dist/chunks/settings-modal-gj0hrah4.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-4k0m4fmp.js.map +0 -13
- package/dist/chunks/studio-568nx4t5.js +0 -469
- package/dist/chunks/studio-568nx4t5.js.map +0 -14
- package/dist/chunks/studio-9smfzqhc.js +0 -19
- package/dist/chunks/studio-9smfzqhc.js.map +0 -10
- package/dist/chunks/studio-asvf0j9e.js +0 -429
- package/dist/chunks/studio-asvf0j9e.js.map +0 -13
- package/dist/chunks/studio-d77qm3pk.js +0 -3427
- package/dist/chunks/studio-d77qm3pk.js.map +0 -26
- package/dist/chunks/studio-j6gqyjf4.js +0 -1854
- package/dist/chunks/studio-j6gqyjf4.js.map +0 -18
- package/dist/chunks/studio-n9zmpxjg.js.map +0 -115
- package/dist/chunks/studio-qkvt37c0.js.map +0 -9
- package/dist/chunks/studio-rq0vxb19.js +0 -3744
- package/dist/chunks/studio-rq0vxb19.js.map +0 -22
- package/dist/chunks/studio-wd6ewdvd.js +0 -853
- package/dist/chunks/studio-wd6ewdvd.js.map +0 -12
- 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-ygzav7z5.js.map +0 -46
- package/dist/chunks/studio-zmy7241e.js +0 -399
- package/dist/chunks/studio-zmy7241e.js.map +0 -12
- 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-2c188n41.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-hhj92gcr.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,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference.ts — the registry, projected into the two generated documentation pages.
|
|
3
|
+
*
|
|
4
|
+
* Plan §12 P3 lists `docs/studio/interface/shortcuts.md` and a new
|
|
5
|
+
* `docs/studio/interface/commands.md` as **generated from the registry and CI-diffed**, which is
|
|
6
|
+
* the last of §5.5's seven derivations: with the chrome, the keymap, the palette, the menus and the
|
|
7
|
+
* automation surface all rendering the same records, a hand-maintained shortcut sheet is the only
|
|
8
|
+
* place left for the app's keyboard and its documentation to disagree.
|
|
9
|
+
*
|
|
10
|
+
* This module is the projection, not the generator. It takes a command set — `appCommandSet()`, the
|
|
11
|
+
* one bare-Bun-importable export the three CI checks already load — and returns ROWS plus the
|
|
12
|
+
* markdown table each page's body is made of. `scripts/docs/generate-reference.ts` supplies the
|
|
13
|
+
* frontmatter, the prose and the file write; keeping the two apart is what lets the projection be
|
|
14
|
+
* tested in this package, next to the records it reads, without a docs harness.
|
|
15
|
+
*
|
|
16
|
+
* Everything here is pure and sorted deterministically: a generated page that reorders itself
|
|
17
|
+
* between runs is a CI diff nobody can review.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { formatChord, normalizeChord } from "./keymap";
|
|
21
|
+
import { CATEGORIES } from "./levels";
|
|
22
|
+
import type { KeybindingOverrides } from "./keymap";
|
|
23
|
+
import type { Category, KeyScope, Level, Placement } from "./levels";
|
|
24
|
+
import type { AnyCommand } from "./registry";
|
|
25
|
+
|
|
26
|
+
/** One command, as `docs/studio/interface/commands.md` prints it. */
|
|
27
|
+
export interface CommandRow {
|
|
28
|
+
id: string;
|
|
29
|
+
title: string;
|
|
30
|
+
category: Category;
|
|
31
|
+
level: Level;
|
|
32
|
+
/** Canonical chords, in declaration order. Empty when the command is palette-only. */
|
|
33
|
+
chords: readonly string[];
|
|
34
|
+
/** Chords as a mac reader sees them. */
|
|
35
|
+
mac: readonly string[];
|
|
36
|
+
/** Chords as a Windows/Linux reader sees them. */
|
|
37
|
+
pc: readonly string[];
|
|
38
|
+
/** The `requires` sentence — the same string the disabled tooltip and the agent's refusal print. */
|
|
39
|
+
requires: string;
|
|
40
|
+
/** Declared placements, sorted. `["palette"]` when the record declares none. */
|
|
41
|
+
menus: readonly Placement[];
|
|
42
|
+
/** The assistant tool name this record projects to, or `""` when it does not. */
|
|
43
|
+
aiTool: string;
|
|
44
|
+
destructive: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** One binding, as `docs/studio/interface/shortcuts.md` prints it. */
|
|
48
|
+
export interface ShortcutRow {
|
|
49
|
+
/** Canonical chord — `"mod+shift+p"`. The sort key, so the sheet is stable across platforms. */
|
|
50
|
+
chord: string;
|
|
51
|
+
mac: string;
|
|
52
|
+
pc: string;
|
|
53
|
+
commandId: string;
|
|
54
|
+
title: string;
|
|
55
|
+
category: Category;
|
|
56
|
+
/** Where the chord is live. The column that explains why ⌘D does nothing while you are typing. */
|
|
57
|
+
scope: KeyScope;
|
|
58
|
+
/** Whether this chord came from the user's layer rather than the record. Always false in docs. */
|
|
59
|
+
overridden: boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Human wording for each {@link KeyScope}, so the sheet does not print an enum at a reader. */
|
|
63
|
+
export const SCOPE_LABELS: Readonly<Record<KeyScope, string>> = {
|
|
64
|
+
global: "Anywhere",
|
|
65
|
+
canvas: "Canvas selection",
|
|
66
|
+
caret: "Text caret",
|
|
67
|
+
grid: "Data grid",
|
|
68
|
+
code: "Code editor",
|
|
69
|
+
dock: "Focused dock",
|
|
70
|
+
palette: "Palette",
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/** Category order — declaration order in `levels.ts`, which is task order, not alphabetical. */
|
|
74
|
+
const CATEGORY_RANK = new Map(CATEGORIES.map((category, index) => [category, index]));
|
|
75
|
+
|
|
76
|
+
function categoryRank(category: Category): number {
|
|
77
|
+
return CATEGORY_RANK.get(category) ?? CATEGORIES.length;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** A record's chords in canonical form, in declaration order. */
|
|
81
|
+
function chordsOf(command: AnyCommand): string[] {
|
|
82
|
+
const raw = command.keybinding;
|
|
83
|
+
if (!raw) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
return (typeof raw === "string" ? [raw] : [...raw]).map((chord) => normalizeChord(chord));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Every command, as documentation rows.
|
|
91
|
+
*
|
|
92
|
+
* Sorted by category then title — the palette's own grouping, so a reader who learned the app
|
|
93
|
+
* through the palette finds the page laid out the way they already think.
|
|
94
|
+
*/
|
|
95
|
+
export function commandReference(commands: readonly AnyCommand[]): CommandRow[] {
|
|
96
|
+
return commands
|
|
97
|
+
.map((command) => {
|
|
98
|
+
const chords = chordsOf(command);
|
|
99
|
+
const row: CommandRow = {
|
|
100
|
+
id: command.id,
|
|
101
|
+
title: command.title,
|
|
102
|
+
category: command.category,
|
|
103
|
+
level: command.level,
|
|
104
|
+
chords,
|
|
105
|
+
mac: chords.map((chord) => formatChord(chord, true)),
|
|
106
|
+
pc: chords.map((chord) => formatChord(chord, false)),
|
|
107
|
+
requires: command.requires ?? "",
|
|
108
|
+
menus: [...(command.menus ?? ["palette"])].toSorted(),
|
|
109
|
+
aiTool: command.aiTool?.name ?? "",
|
|
110
|
+
destructive: command.destructive === true,
|
|
111
|
+
};
|
|
112
|
+
return row;
|
|
113
|
+
})
|
|
114
|
+
.toSorted(
|
|
115
|
+
(a, b) =>
|
|
116
|
+
categoryRank(a.category) - categoryRank(b.category) || a.title.localeCompare(b.title),
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Every BINDING, as documentation rows — one row per chord, not per command.
|
|
122
|
+
*
|
|
123
|
+
* `edit.redo` declares two chords and appears twice, because a reader looking up ⌘Y needs to find
|
|
124
|
+
* it. Sorted by scope then chord so the sheet reads as "here is what this key does, and here is
|
|
125
|
+
* where it is live".
|
|
126
|
+
*
|
|
127
|
+
* `overrides` is the user's layer (`specs/studio.md` §15) and is how ONE projection serves both
|
|
128
|
+
* readers: the generated page passes none and prints what the app ships with, while the in-app
|
|
129
|
+
* Keyboard sheet passes the live layer and prints what this author's keyboard actually does. A
|
|
130
|
+
* command the user unbound has an entry with no chords and contributes no row — the same rule as a
|
|
131
|
+
* command that never declared one, because in both cases there is nothing to press.
|
|
132
|
+
*/
|
|
133
|
+
export function shortcutReference(
|
|
134
|
+
commands: readonly AnyCommand[],
|
|
135
|
+
overrides?: KeybindingOverrides,
|
|
136
|
+
): ShortcutRow[] {
|
|
137
|
+
const rows: ShortcutRow[] = [];
|
|
138
|
+
for (const command of commands) {
|
|
139
|
+
const scope: KeyScope = command.keyScope ?? "global";
|
|
140
|
+
const override = overrides?.get(command.id);
|
|
141
|
+
const chords =
|
|
142
|
+
override === undefined ? chordsOf(command) : override.map((chord) => normalizeChord(chord));
|
|
143
|
+
for (const chord of chords) {
|
|
144
|
+
rows.push({
|
|
145
|
+
chord,
|
|
146
|
+
mac: formatChord(chord, true),
|
|
147
|
+
pc: formatChord(chord, false),
|
|
148
|
+
commandId: command.id,
|
|
149
|
+
title: command.title,
|
|
150
|
+
category: command.category,
|
|
151
|
+
scope,
|
|
152
|
+
overridden: override !== undefined,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return rows.toSorted((a, b) => a.scope.localeCompare(b.scope) || a.chord.localeCompare(b.chord));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Escape the one character that would break out of a markdown table cell. */
|
|
160
|
+
function cell(value: string): string {
|
|
161
|
+
return value.replaceAll("|", String.raw`\|`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function table(header: readonly string[], rows: readonly (readonly string[])[]): string {
|
|
165
|
+
const lines = [
|
|
166
|
+
`| ${header.join(" | ")} |`,
|
|
167
|
+
`| ${header.map(() => "---").join(" | ")} |`,
|
|
168
|
+
...rows.map((row) => `| ${row.map((value) => cell(value)).join(" | ")} |`),
|
|
169
|
+
];
|
|
170
|
+
return lines.join("\n");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** A chord list as one cell: `` `⌘⇧Z` or `⌘Y` ``, or an em dash when the command has none. */
|
|
174
|
+
function chordCell(chords: readonly string[]): string {
|
|
175
|
+
return chords.length === 0 ? "—" : chords.map((chord) => `\`${chord}\``).join(" or ");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* The body of `docs/studio/interface/shortcuts.md` — one `##` section per scope.
|
|
180
|
+
*
|
|
181
|
+
* Both platforms print, side by side, because the alternative is the page telling half its readers
|
|
182
|
+
* a chord they do not have — which is the documentation version of the bug `formatChord` fixed in
|
|
183
|
+
* the toolbar.
|
|
184
|
+
*/
|
|
185
|
+
export function shortcutsMarkdown(rows: readonly ShortcutRow[]): string {
|
|
186
|
+
const sections: string[] = [];
|
|
187
|
+
const scopes = [...new Set(rows.map((row) => row.scope))];
|
|
188
|
+
for (const scope of scopes) {
|
|
189
|
+
const inScope = rows.filter((row) => row.scope === scope);
|
|
190
|
+
sections.push(
|
|
191
|
+
`## ${SCOPE_LABELS[scope]}\n`,
|
|
192
|
+
table(
|
|
193
|
+
["macOS", "Windows / Linux", "Command", "Id"],
|
|
194
|
+
inScope.map((row) => [
|
|
195
|
+
`\`${row.mac}\``,
|
|
196
|
+
`\`${row.pc}\``,
|
|
197
|
+
row.title,
|
|
198
|
+
`\`${row.commandId}\``,
|
|
199
|
+
]),
|
|
200
|
+
),
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
return sections.join("\n\n");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** The body of `docs/studio/interface/commands.md` — one `##` section per category. */
|
|
207
|
+
export function commandsMarkdown(rows: readonly CommandRow[]): string {
|
|
208
|
+
const sections: string[] = [];
|
|
209
|
+
const categories = [...new Set(rows.map((row) => row.category))];
|
|
210
|
+
for (const category of categories) {
|
|
211
|
+
const inCategory = rows.filter((row) => row.category === category);
|
|
212
|
+
sections.push(
|
|
213
|
+
`## ${category}\n`,
|
|
214
|
+
table(
|
|
215
|
+
["Command", "Id", "Shortcut", "Level", "Requires"],
|
|
216
|
+
inCategory.map((row) => [
|
|
217
|
+
row.destructive ? `${row.title} (destructive)` : row.title,
|
|
218
|
+
`\`${row.id}\``,
|
|
219
|
+
chordCell(row.mac),
|
|
220
|
+
row.level,
|
|
221
|
+
row.requires || "—",
|
|
222
|
+
]),
|
|
223
|
+
),
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
return sections.join("\n\n");
|
|
227
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry.ts — one definition site per action.
|
|
3
|
+
*
|
|
4
|
+
* A {@link Command} is the whole record of a capability: its name, its containment level, the
|
|
5
|
+
* predicate that decides whether it is available, the one sentence that says why it is not, its
|
|
6
|
+
* default chord, and the surfaces it belongs in. Every surface — Command Bar, palette, rail,
|
|
7
|
+
* context menus, the block action bar, the keymap, `__jxAutomation` and the assistant's tools —
|
|
8
|
+
* becomes a RENDERING of these records. A surface may choose whether to show a command; it may
|
|
9
|
+
* never decide what it is called, when it is available, or what it does (plan §2, principle 1).
|
|
10
|
+
*
|
|
11
|
+
* Three things fail loudly at registration rather than degrading into a surface disagreement:
|
|
12
|
+
*
|
|
13
|
+
* - A duplicate id — the second definition site the whole design exists to prevent;
|
|
14
|
+
* - A chord already claimed in the same `keyScope` (see `keymap.ts`) — this is exactly how ⌘W came to
|
|
15
|
+
* disagree with the tab strip's × button;
|
|
16
|
+
* - A `menus` placement the level × placement matrix does not admit (see `levels.ts`), so a
|
|
17
|
+
* selection-level verb cannot appear in the Command Bar even in a hand-written test app.
|
|
18
|
+
*
|
|
19
|
+
* `when` / `enablement` are `(ctx) => boolean` closures, modelled on the shipped
|
|
20
|
+
* `services/gated-registry.ts` `ToolAvailability`, and `requires` is ONE string with three
|
|
21
|
+
* consumers: the disabled control's tooltip, the palette row's grey subtitle and the agent's
|
|
22
|
+
* refusal message. Plan §13 rejects a serialisable string DSL; do not add one.
|
|
23
|
+
*
|
|
24
|
+
* The registry takes its context by injection (`getContext`), so nothing here imports a state
|
|
25
|
+
* module. That is what lets the CI checks import the command set in a bare Bun process and lets
|
|
26
|
+
* every test build the exact state it wants to assert against.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { createKeymap } from "./keymap";
|
|
30
|
+
import type { KeyChordEvent, Keymap, KeymapMatch } from "./keymap";
|
|
31
|
+
import { checkRecordPlacements } from "./levels";
|
|
32
|
+
import type { Category, KeyScope, Level, Placement } from "./levels";
|
|
33
|
+
import type { CommandContext } from "./context";
|
|
34
|
+
|
|
35
|
+
/** Arguments as they arrive from a palette prompt, an automation manifest step or an AI tool call. */
|
|
36
|
+
export type CommandArgs = Record<string, unknown>;
|
|
37
|
+
|
|
38
|
+
/** How an action's effect is undone — surfaced to the user before an agent runs it. */
|
|
39
|
+
export type UndoScope = "document" | "project" | "none";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* One capability, defined once.
|
|
43
|
+
*
|
|
44
|
+
* @template A The argument record `run` receives. Defaults to `void` for the (common) case of a
|
|
45
|
+
* command that takes none.
|
|
46
|
+
*/
|
|
47
|
+
export interface Command<A = void> {
|
|
48
|
+
/** `"<category>.<verb>"` — the same namespace `scripts/screenshots/manifest.json` addresses. */
|
|
49
|
+
id: string;
|
|
50
|
+
/** Imperative human name. The ONLY place this action is named. */
|
|
51
|
+
title: string;
|
|
52
|
+
category: Category;
|
|
53
|
+
/** REQUIRED containment level, checked against every declared placement. */
|
|
54
|
+
level: Level;
|
|
55
|
+
/** Keyboard dispatch scope. Deliberately separate from {@link Command.level}. */
|
|
56
|
+
keyScope?: KeyScope;
|
|
57
|
+
/** Icon key, resolved through the activity bar's existing icon map. */
|
|
58
|
+
icon?: string;
|
|
59
|
+
/** Hide entirely. Default: always visible. */
|
|
60
|
+
when?: (ctx: CommandContext) => boolean;
|
|
61
|
+
/** Show but disable. Defaults to {@link Command.when}. */
|
|
62
|
+
enablement?: (ctx: CommandContext) => boolean;
|
|
63
|
+
/** The human reason, e.g. "an element selection". Tooltip, palette subtitle, agent refusal. */
|
|
64
|
+
requires?: string;
|
|
65
|
+
/** Default chord(s), e.g. `"mod+shift+p"`. User overrides layer on top. */
|
|
66
|
+
keybinding?: string | readonly string[];
|
|
67
|
+
/** JSON Schema for {@link Command.run}'s args — the palette's prompt AND the AI tool's params. */
|
|
68
|
+
args?: object;
|
|
69
|
+
/** Surfaces this command renders in. Defaults to `["palette"]`. */
|
|
70
|
+
menus?: readonly Placement[];
|
|
71
|
+
/** Menu ordering key: "1_clipboard", "3_structure", "9_danger". */
|
|
72
|
+
group?: string;
|
|
73
|
+
undo?: UndoScope;
|
|
74
|
+
destructive?: boolean;
|
|
75
|
+
/** Opt-in projection to the assistant. The human's gate and the agent's gate stay one predicate. */
|
|
76
|
+
aiTool?: { name: string; description: string };
|
|
77
|
+
run: (ctx: CommandContext, args: A) => void | Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* A command of unknown argument type, as stored.
|
|
82
|
+
*
|
|
83
|
+
* `never` (not `unknown`) is what makes an arbitrary `Command<A>` assignable: `run` is
|
|
84
|
+
* contravariant in its argument, so a handler taking `{ path }` satisfies one taking `never`.
|
|
85
|
+
*/
|
|
86
|
+
export type AnyCommand = Command<never>;
|
|
87
|
+
|
|
88
|
+
/** `<namespace>.<verb>`, lowercase namespace, at least two dot-separated segments. */
|
|
89
|
+
const ID_PATTERN = /^[a-z][a-z\d]*(\.[a-zA-Z\d]+)+$/;
|
|
90
|
+
|
|
91
|
+
/** Thrown when a command is invoked while its own predicate refuses it. */
|
|
92
|
+
export class CommandUnavailableError extends Error {
|
|
93
|
+
readonly commandId: string;
|
|
94
|
+
/** The `requires` sentence, or a generic fallback when the record did not supply one. */
|
|
95
|
+
readonly requires: string;
|
|
96
|
+
|
|
97
|
+
constructor(commandId: string, requires: string) {
|
|
98
|
+
super(refusalSentence(commandId, requires));
|
|
99
|
+
this.name = "CommandUnavailableError";
|
|
100
|
+
this.commandId = commandId;
|
|
101
|
+
this.requires = requires;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Wording shared by the disabled tooltip, the palette subtitle and the agent's refusal. */
|
|
106
|
+
const GENERIC_REQUIREMENT = "a different studio state";
|
|
107
|
+
|
|
108
|
+
/** The one refusal sentence. `CommandUnavailableError` and `refusalMessage` must not diverge. */
|
|
109
|
+
function refusalSentence(commandId: string, requires: string): string {
|
|
110
|
+
return `Command "${commandId}" is not available right now — it requires ${requires}.`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface CommandRegistry {
|
|
114
|
+
/** Define a command. Throws on a duplicate id, a chord conflict or a misplacement. */
|
|
115
|
+
register: <A>(command: Command<A>) => void;
|
|
116
|
+
/** Define many, in order. Not transactional: the throwing record is the one that failed. */
|
|
117
|
+
registerAll: (commands: readonly AnyCommand[]) => void;
|
|
118
|
+
get: (id: string) => AnyCommand | undefined;
|
|
119
|
+
/** Every command, in registration order. */
|
|
120
|
+
list: () => readonly AnyCommand[];
|
|
121
|
+
/** Commands whose `when` holds right now — what a surface iterates. */
|
|
122
|
+
visible: () => readonly AnyCommand[];
|
|
123
|
+
/** Commands declaring `placement`, visible right now, sorted by `group` then title. */
|
|
124
|
+
forPlacement: (placement: Placement) => readonly AnyCommand[];
|
|
125
|
+
isVisible: (id: string) => boolean;
|
|
126
|
+
isEnabled: (id: string) => boolean;
|
|
127
|
+
/** The `requires` sentence when a visible command is disabled; `undefined` when it is usable. */
|
|
128
|
+
disabledReason: (id: string) => string | undefined;
|
|
129
|
+
/** The full refusal sentence, for an agent or a tooltip. `undefined` when the command is usable. */
|
|
130
|
+
refusalMessage: (id: string) => string | undefined;
|
|
131
|
+
/** Run a command. Throws on an unknown id, or {@link CommandUnavailableError} when refused. */
|
|
132
|
+
run: (id: string, args?: CommandArgs) => void | Promise<void>;
|
|
133
|
+
/** The chord index, conflict-checked at registration. */
|
|
134
|
+
keymap: Keymap;
|
|
135
|
+
/** Resolve a keyboard event through a scope stack and run what it hits. */
|
|
136
|
+
handleKeyEvent: (event: KeyChordEvent, scopeStack: readonly KeyScope[]) => string | undefined;
|
|
137
|
+
/** The context the predicates are currently reading — exposed for surfaces that need a snapshot. */
|
|
138
|
+
context: () => CommandContext;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface CommandRegistryOptions {
|
|
142
|
+
/** The live context. Called per predicate evaluation; a reactive record makes this free. */
|
|
143
|
+
getContext: () => CommandContext;
|
|
144
|
+
/** Format chords with mac glyphs. Defaults to platform detection inside the keymap. */
|
|
145
|
+
mac?: boolean;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Build an empty registry.
|
|
150
|
+
*
|
|
151
|
+
* There is deliberately no module-level singleton here: the app creates one in its bootstrap and
|
|
152
|
+
* passes it down, so tests, the CI checks and a future second window each get their own.
|
|
153
|
+
*/
|
|
154
|
+
export function createCommandRegistry(options: CommandRegistryOptions): CommandRegistry {
|
|
155
|
+
const commands = new Map<string, AnyCommand>();
|
|
156
|
+
const keymap = createKeymap(options.mac === undefined ? {} : { mac: options.mac });
|
|
157
|
+
|
|
158
|
+
function mustGet(id: string): AnyCommand {
|
|
159
|
+
const command = commands.get(id);
|
|
160
|
+
if (!command) {
|
|
161
|
+
throw new Error(`unknown command "${id}"`);
|
|
162
|
+
}
|
|
163
|
+
return command;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function visibleWith(command: AnyCommand, ctx: CommandContext): boolean {
|
|
167
|
+
return command.when?.(ctx) ?? true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function enabledWith(command: AnyCommand, ctx: CommandContext): boolean {
|
|
171
|
+
if (!visibleWith(command, ctx)) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
// `enablement` defaults to `when`: a command that only declared `when` is enabled exactly when
|
|
175
|
+
// It is shown, which is the behaviour every existing hand-wired control already has.
|
|
176
|
+
return command.enablement?.(ctx) ?? true;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const registry: CommandRegistry = {
|
|
180
|
+
register(command) {
|
|
181
|
+
if (!ID_PATTERN.test(command.id)) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`invalid command id "${command.id}" — ids are "<category>.<verb>", ` +
|
|
184
|
+
`e.g. "selection.duplicate" or "view.toggleDock.right"`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (commands.has(command.id)) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
`duplicate command id "${command.id}" — a capability has exactly one definition site`,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
if (!command.title) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`command "${command.id}" has no title — the record is where actions are named`,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
const violations = checkRecordPlacements(command);
|
|
198
|
+
if (violations.length > 0) {
|
|
199
|
+
throw new Error(`command "${command.id}" ${violations[0]!.message}`);
|
|
200
|
+
}
|
|
201
|
+
// Chord conflicts throw from the keymap; do this LAST so a rejected record leaves the
|
|
202
|
+
// Registry untouched in every failure mode.
|
|
203
|
+
keymap.add(command);
|
|
204
|
+
commands.set(command.id, command as AnyCommand);
|
|
205
|
+
},
|
|
206
|
+
registerAll(list) {
|
|
207
|
+
for (const command of list) {
|
|
208
|
+
registry.register(command);
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
get(id) {
|
|
212
|
+
return commands.get(id);
|
|
213
|
+
},
|
|
214
|
+
list() {
|
|
215
|
+
return [...commands.values()];
|
|
216
|
+
},
|
|
217
|
+
visible() {
|
|
218
|
+
const ctx = options.getContext();
|
|
219
|
+
return [...commands.values()].filter((command) => visibleWith(command, ctx));
|
|
220
|
+
},
|
|
221
|
+
forPlacement(placement) {
|
|
222
|
+
const ctx = options.getContext();
|
|
223
|
+
return [...commands.values()]
|
|
224
|
+
.filter(
|
|
225
|
+
(command) =>
|
|
226
|
+
(command.menus ?? ["palette"]).includes(placement) && visibleWith(command, ctx),
|
|
227
|
+
)
|
|
228
|
+
.toSorted(
|
|
229
|
+
(a, b) => (a.group ?? "").localeCompare(b.group ?? "") || a.title.localeCompare(b.title),
|
|
230
|
+
);
|
|
231
|
+
},
|
|
232
|
+
isVisible(id) {
|
|
233
|
+
return visibleWith(mustGet(id), options.getContext());
|
|
234
|
+
},
|
|
235
|
+
isEnabled(id) {
|
|
236
|
+
return enabledWith(mustGet(id), options.getContext());
|
|
237
|
+
},
|
|
238
|
+
disabledReason(id) {
|
|
239
|
+
const command = mustGet(id);
|
|
240
|
+
const ctx = options.getContext();
|
|
241
|
+
return enabledWith(command, ctx) ? undefined : (command.requires ?? GENERIC_REQUIREMENT);
|
|
242
|
+
},
|
|
243
|
+
refusalMessage(id) {
|
|
244
|
+
const reason = registry.disabledReason(id);
|
|
245
|
+
return reason === undefined ? undefined : refusalSentence(id, reason);
|
|
246
|
+
},
|
|
247
|
+
run(id, args = {}) {
|
|
248
|
+
const command = mustGet(id);
|
|
249
|
+
const ctx = options.getContext();
|
|
250
|
+
if (!enabledWith(command, ctx)) {
|
|
251
|
+
throw new CommandUnavailableError(id, command.requires ?? GENERIC_REQUIREMENT);
|
|
252
|
+
}
|
|
253
|
+
return command.run(ctx, args as never);
|
|
254
|
+
},
|
|
255
|
+
keymap,
|
|
256
|
+
handleKeyEvent(event, scopeStack) {
|
|
257
|
+
/* NARROWEST AVAILABLE wins, not narrowest.
|
|
258
|
+
A chord bound to a command whose `when` is false is not a hit — the key falls through to
|
|
259
|
+
the browser rather than being swallowed by an action that is not there. It used to fall
|
|
260
|
+
through to the browser and NOWHERE ELSE: `resolveEvent` answers with the first scope that
|
|
261
|
+
binds the chord, so a hidden narrow binding also hid every wider one behind it. That is a
|
|
262
|
+
defect the caret scope was about to make real. `format.link` binds ⌘K at `caret`, and the
|
|
263
|
+
caret stack is live in every parent-realm text field too (`caret.active` folds both, by
|
|
264
|
+
design — see `commands/context.ts`); its `when` is false there, so ⌘K would have resolved
|
|
265
|
+
to a hidden record and stopped reaching `palette.open`, and the omnibox would have gone
|
|
266
|
+
quiet in every panel field with nothing to show for it.
|
|
267
|
+
Walking the stack one scope at a time and taking the first VISIBLE claimant is what
|
|
268
|
+
"shadowing" has to mean: a command shadows a wider one while it is THERE. */
|
|
269
|
+
let hit: KeymapMatch | undefined;
|
|
270
|
+
for (const scope of scopeStack) {
|
|
271
|
+
const match = keymap.resolveEvent(event, [scope]);
|
|
272
|
+
if (match && registry.isVisible(match.commandId)) {
|
|
273
|
+
hit = match;
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (!hit) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
// Visible but disabled — ⌘Z with no history, Delete on the root. The chord IS claimed (the
|
|
281
|
+
// Action exists here, it just cannot act right now, and letting the browser have it would be
|
|
282
|
+
// Worse), but running it would throw CommandUnavailableError straight out of the keydown
|
|
283
|
+
// Listener. Claim it silently instead of making every caller wrap dispatch in a try/catch.
|
|
284
|
+
if (!registry.isEnabled(hit.commandId)) {
|
|
285
|
+
return hit.commandId;
|
|
286
|
+
}
|
|
287
|
+
void registry.run(hit.commandId);
|
|
288
|
+
return hit.commandId;
|
|
289
|
+
},
|
|
290
|
+
context: options.getContext,
|
|
291
|
+
};
|
|
292
|
+
return registry;
|
|
293
|
+
}
|
package/src/component-props.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import type { JsonValue } from "./types";
|
|
14
14
|
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
15
|
+
import { isPrivateStateKey } from "@jxsuite/schema/guards";
|
|
15
16
|
|
|
16
17
|
/** One editable prop row: the state key and its current default value (undefined = none). */
|
|
17
18
|
export interface ComponentPropEntry {
|
|
@@ -60,8 +61,8 @@ export function componentPropEntries(doc: JxMutableNode | null | undefined): Com
|
|
|
60
61
|
const state = (doc?.state ?? {}) as Record<string, unknown>;
|
|
61
62
|
const out: ComponentPropEntry[] = [];
|
|
62
63
|
for (const [name, def] of Object.entries(state)) {
|
|
63
|
-
if (name
|
|
64
|
-
continue; // Private entries are not props (
|
|
64
|
+
if (isPrivateStateKey(name)) {
|
|
65
|
+
continue; // Private entries are not props (spec.md §5.6).
|
|
65
66
|
}
|
|
66
67
|
const shape = propShape(def);
|
|
67
68
|
if (!shape) {
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The draft axis (§7.6) — what a draft IS, and whether drafts are listed. One place, every list.
|
|
3
|
+
*
|
|
4
|
+
* It is deliberately its own module rather than a field on a pane's state and rather than a section
|
|
5
|
+
* of `entry-model.ts`. Two reasons, and the second is why the definitions live here beside the
|
|
6
|
+
* flag:
|
|
7
|
+
*
|
|
8
|
+
* 1. **The perspective is a fact about the AUTHOR, not the surface.** A draft filter that belonged to
|
|
9
|
+
* the Library would answer for the Library only, and the same author would then be shown their
|
|
10
|
+
* draft in the collection grid, in the pages grid and in Quick Open a second later.
|
|
11
|
+
* 2. **Every list that must obey it has to be able to import it.** `entry-model.ts` asks
|
|
12
|
+
* `grid/sources/content-source.ts` where a collection's entries live, so the grid source cannot
|
|
13
|
+
* ask `entry-model.ts` back what a draft is — `import/no-cycle` is an error in this repo, and
|
|
14
|
+
* the cycle would be real. This module has ONE runtime dependency (`reactivity`), so the flag
|
|
15
|
+
* and the predicate are importable from a data module, a panel and a command alike. That is the
|
|
16
|
+
* property that lets the surfaces actually share them, instead of each carrying its own `draft
|
|
17
|
+
* === true`.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { reactive } from "../reactivity";
|
|
21
|
+
import type { ContentTypeSchema } from "@jxsuite/schema/types";
|
|
22
|
+
|
|
23
|
+
/** The conventional boolean frontmatter field that marks an entry a draft (§7.6). */
|
|
24
|
+
export const DRAFT_FIELD = "draft";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* What a draft IS, in one sentence, for every surface that shows the pill.
|
|
28
|
+
*
|
|
29
|
+
* Deliberately does not say "excluded from production builds" — the spec's §7.6 bullet describes an
|
|
30
|
+
* intent the build does not implement yet (`packages/compiler`'s `site/site-build.ts` says so
|
|
31
|
+
* beside `$sitemap`), and a badge that promises a file will not ship when it will is the exact
|
|
32
|
+
* class of confident-wrong statement §16 exists to end. When the compiler learns to drop drafts,
|
|
33
|
+
* this constant changes and every surface changes with it.
|
|
34
|
+
*/
|
|
35
|
+
export const DRAFT_MEANING =
|
|
36
|
+
"Marked a draft. Studio filters drafts out of its own lists; the build does not exclude them yet.";
|
|
37
|
+
|
|
38
|
+
// ─── What a draft is ─────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Whether this record is marked a draft. Only the literal `true` counts — never a truthy string.
|
|
42
|
+
*
|
|
43
|
+
* Takes the entry's FIELDS, whichever record they live in — `content/entry-fields.ts` answers that,
|
|
44
|
+
* because a JSON collection's entry has no frontmatter and its `draft` key is on the document.
|
|
45
|
+
*/
|
|
46
|
+
export function isDraftEntry(fields: Record<string, unknown> | null | undefined): boolean {
|
|
47
|
+
return fields?.[DRAFT_FIELD] === true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Whether this collection's schema declares the draft field as a boolean. */
|
|
51
|
+
export function schemaDeclaresDraft(schema: ContentTypeSchema | null | undefined): boolean {
|
|
52
|
+
return schema?.properties?.[DRAFT_FIELD]?.type === "boolean";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Whether a surface should show a draft affordance for this entry at all.
|
|
57
|
+
*
|
|
58
|
+
* A collection that does not declare `draft` has no draft workflow, and painting a "Published" pill
|
|
59
|
+
* on its entries — or a Draft column over its grid — would be inventing a state the project never
|
|
60
|
+
* defined. An entry that carries `draft: true` anyway — because someone typed it — DOES show,
|
|
61
|
+
* because the fact is on disk.
|
|
62
|
+
*/
|
|
63
|
+
export function hasDraftAxis(
|
|
64
|
+
schema: ContentTypeSchema | null | undefined,
|
|
65
|
+
fields?: Record<string, unknown> | null,
|
|
66
|
+
): boolean {
|
|
67
|
+
return schemaDeclaresDraft(schema) || isDraftEntry(fields);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ─── The perspective ─────────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
/** The perspective. `includeDrafts` false — the default — hides entries marked `draft: true`. */
|
|
73
|
+
export const draftView = reactive({ includeDrafts: false });
|
|
74
|
+
|
|
75
|
+
/** Whether draft entries are currently listed. */
|
|
76
|
+
export function includingDrafts(): boolean {
|
|
77
|
+
return draftView.includeDrafts;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Set the perspective.
|
|
82
|
+
*
|
|
83
|
+
* A setter, never a toggle: `content.setIncludeDrafts` names the STATE it reaches, so a manifest
|
|
84
|
+
* step, an assistant call and a checkbox all land on the same value however many times they run
|
|
85
|
+
* (plan §13.1, and the rule `tests/app-commands.test.ts` enforces).
|
|
86
|
+
*
|
|
87
|
+
* Setting the flag is all this does. Repainting the lists that read it belongs to the command — see
|
|
88
|
+
* `content/entry-commands.ts` — because a data module may not reach into the workspace.
|
|
89
|
+
*/
|
|
90
|
+
export function setIncludeDrafts(include: boolean): void {
|
|
91
|
+
draftView.includeDrafts = include;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Apply the "including drafts" perspective to a list.
|
|
96
|
+
*
|
|
97
|
+
* `includeDrafts` true returns the input array unchanged — identity, not a copy — so a caller can
|
|
98
|
+
* cheaply tell that nothing was filtered.
|
|
99
|
+
*/
|
|
100
|
+
export function applyDraftFilter<T>(
|
|
101
|
+
items: readonly T[],
|
|
102
|
+
fieldsOf: (item: T) => Record<string, unknown> | null | undefined,
|
|
103
|
+
includeDrafts: boolean,
|
|
104
|
+
): readonly T[] {
|
|
105
|
+
return includeDrafts ? items : items.filter((item) => !isDraftEntry(fieldsOf(item)));
|
|
106
|
+
}
|