@jxsuite/studio 5.2.0 → 5.4.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/data/css-meta.json +28 -27
- package/dist/chunks/{cf-account-picker-h4dab9bq.js → cf-account-picker-3f08rry6.js} +21 -18
- package/dist/chunks/{cf-settings-adg74qsk.js → cf-settings-m79r7p5y.js} +4 -4
- package/dist/chunks/{components-5y78e05d.js → components-xm826zjw.js} +3 -3
- package/dist/chunks/{content-source-sgg1sv6n.js → content-source-jt7rbcbc.js} +22 -22
- package/dist/chunks/{convert-to-repeater-mf1cxgfn.js → convert-to-repeater-vg38bq99.js} +22 -22
- package/dist/chunks/{editor-3qhbg51x.js → editor-ddy019sn.js} +2 -2
- package/dist/chunks/entry-commands-64nb2a9a.js +42 -0
- package/dist/chunks/{extension-sections-c6zwzbre.js → extension-sections-r73an5s6.js} +31 -30
- package/dist/chunks/{file-ops-vrpq1jjk.js → file-ops-3xan8mnj.js} +21 -21
- package/dist/chunks/{files-msaf95y6.js → files-egsb0ry8.js} +30 -29
- package/dist/chunks/{format-host-f64fnesp.js → format-host-mym8qnd0.js} +3 -3
- package/dist/chunks/{fs-events-3y6y0nrm.js → fs-events-z2gg8qjt.js} +4 -4
- package/dist/chunks/{git-panel-bv5e22j0.js → git-panel-a05atk6q.js} +20 -20
- package/dist/chunks/{grid-view-1p6hsyjx.css → grid-view-3g1h2re8.css} +956 -139
- package/dist/chunks/{grid-view-paj67nr5.js → grid-view-qaqer9kr.js} +24 -24
- package/dist/chunks/{icons-yy10wk9r.js → icons-drxq9q10.js} +8 -2
- package/dist/chunks/{iframe-host-dh5np8vk.js → iframe-host-psetcv20.js} +11 -11
- package/dist/chunks/{index-n4j7qxyr.js → index-9yjae3hz.js} +4 -4
- package/dist/chunks/{javascript-4abzjcy9.css → javascript-bc9z461d.css} +688 -54
- package/dist/chunks/{javascript-xvrfrpkb.js → javascript-hr5sg57n.js} +2 -2
- package/dist/chunks/{jsonMode-vvmpmk34.css → jsonMode-qqd9nm1t.css} +1120 -303
- package/dist/chunks/{jsonMode-tyx2kj6p.js → jsonMode-zs88v0wr.js} +4 -4
- package/dist/chunks/{jx-validate-j0wxk72w.js → jx-validate-epp2z3s3.js} +2 -2
- package/dist/chunks/layers-panel-83a16ytx.js +41 -0
- package/dist/chunks/{layers-1vcfdbet.js → layers-s6mc01ry.js} +3 -3
- package/dist/chunks/{listbox-6v2ac08j.js → listbox-b7jxnhq1.js} +19 -3
- package/dist/chunks/{media-picker-p49nhqhs.js → media-picker-nhn6rv4w.js} +11 -11
- package/dist/chunks/{menu-qht48vnc.js → menu-8j4qxjrp.js} +4 -4
- package/dist/chunks/{monaco-binding-sw6en4dw.js → monaco-binding-e8d9wzyk.js} +3 -3
- package/dist/chunks/{monaco-buffer-pykwbm92.js → monaco-buffer-jaq1t9zj.js} +2 -2
- package/dist/chunks/{monaco-setup-8vbyqnhr.css → monaco-setup-ey9cjcm3.css} +954 -137
- package/dist/chunks/{monaco-setup-8r62fj6y.js → monaco-setup-gsjdysze.js} +7 -7
- package/dist/chunks/{platform-ts9cg1a4.js → platform-fnchmhy7.js} +5 -3
- package/dist/chunks/publish-panel-ak6hxfma.js +27 -0
- package/dist/chunks/quick-search-c63mkt3b.js +47 -0
- package/dist/chunks/{references-h69xp9zz.js → references-f7bdv3z5.js} +3 -3
- package/dist/chunks/right-panel-t5n8wttg.js +45 -0
- package/dist/chunks/{split-g7nm1rgb.js → split-4cz4b6ef.js} +14 -3
- package/dist/chunks/{studio-sar8g54p.js → studio-01269reb.js} +8 -8
- package/dist/chunks/{studio-rnyf3f8e.js → studio-0a4gncx0.js} +3 -3
- package/dist/chunks/{studio-e1f24eff.js → studio-0d4p4wxj.js} +6778 -2993
- package/dist/chunks/{studio-4wbb9ct0.js → studio-1tejxvba.js} +3006 -1855
- package/dist/chunks/{studio-p6pmwkvh.js → studio-3nprwbd9.js} +147 -28
- package/dist/chunks/{studio-4c041r8y.js → studio-40z39dme.js} +3 -3
- package/dist/chunks/{studio-68fydsrj.js → studio-482eeazk.js} +20 -20
- package/dist/chunks/{studio-y4f9fdge.js → studio-4998spet.js} +10 -9
- package/dist/chunks/{studio-nq5yf5mp.js → studio-557bmn0x.js} +2 -2
- package/dist/chunks/{studio-hdc2jt3z.js → studio-5w26wza5.js} +3 -3
- package/dist/chunks/{studio-6nzaktvy.js → studio-6pwn9j8c.js} +6 -3
- package/dist/chunks/{studio-wnsyffg1.js → studio-6vg0x5fz.js} +3 -2
- package/dist/chunks/{studio-vn5hske5.js → studio-7d3c1m59.js} +12 -5
- package/dist/chunks/{studio-28vs5k2y.js → studio-9vsvz7w7.js} +7 -7
- package/dist/chunks/{studio-t3adrr56.js → studio-9wm0xzpf.js} +19 -19
- package/dist/chunks/{studio-38emantv.js → studio-a5h5j33a.js} +19 -5
- package/dist/chunks/{studio-ympe0n7a.js → studio-ag3z6shb.js} +10 -4
- package/dist/chunks/studio-aj9drheq.js +177 -0
- package/dist/chunks/{studio-vt0tj32j.js → studio-apv9x70c.js} +189 -130
- package/dist/chunks/{studio-c04nzbby.js → studio-b9q3znz8.js} +2 -2
- package/dist/chunks/{studio-2mdbbc76.js → studio-bh47w0cy.js} +9 -10
- package/dist/chunks/{studio-ez11dmdh.js → studio-dgdm6vvt.js} +264 -52
- package/dist/chunks/{studio-5nz00bhh.js → studio-dwdcr2e8.js} +4 -4
- package/dist/chunks/{studio-nqgawfks.js → studio-fbcyz234.js} +146 -139
- package/dist/chunks/{studio-kcgff4r0.js → studio-fgmfksz8.js} +2 -2
- package/dist/chunks/{studio-x72j58vw.js → studio-fqpkpq1t.js} +7 -4
- package/dist/chunks/{studio-15f3kxqe.js → studio-fy320dfc.js} +62 -68
- package/dist/chunks/{studio-zjjde71a.js → studio-gjj4bbfx.js} +2 -2
- package/dist/chunks/{studio-66arbpc2.js → studio-jfhxy8h3.js} +16 -15
- package/dist/chunks/{studio-d0300636.js → studio-ks2zzth2.js} +17 -2
- package/dist/chunks/{studio-c07s5ckd.js → studio-kwewmhhq.js} +4 -4
- package/dist/chunks/{studio-01jeck2y.js → studio-m58xn8at.js} +26 -25
- package/dist/chunks/{studio-mpzwze9x.js → studio-mfkh1q8d.js} +17 -17
- package/dist/chunks/{studio-etvx9s2g.js → studio-mrh0a92f.js} +17 -17
- package/dist/chunks/{studio-qa7fzxan.js → studio-mt79tn56.js} +3 -3
- package/dist/chunks/{studio-hhskq2mr.js → studio-ne593z1j.js} +39 -32
- package/dist/chunks/{studio-h4nvnjvz.js → studio-ptfcxkz5.js} +418 -232
- package/dist/chunks/{studio-4xmjcrg3.js → studio-qxye31h7.js} +7 -7
- package/dist/chunks/{studio-a1pswps3.js → studio-rav5bpmp.js} +81 -59
- package/dist/chunks/{studio-ncn9e189.js → studio-rwmmbrth.js} +6 -6
- package/dist/chunks/{studio-en82c639.js → studio-sceg8ws0.js} +8 -8
- package/dist/chunks/{studio-wjpgxywa.js → studio-sjas5b3a.js} +19 -13
- package/dist/chunks/{studio-4r14yzyy.js → studio-vd67b66d.js} +3 -3
- package/dist/chunks/{studio-14d9tq4d.js → studio-w475cbf9.js} +10 -10
- package/dist/chunks/{studio-ctm7bhx5.js → studio-w7gpcvfj.js} +9 -9
- package/dist/chunks/{studio-9j6sfeba.js → studio-xrbfdxgk.js} +7 -7
- package/dist/chunks/{studio-x10fp4tp.js → studio-y49b6mjv.js} +35 -35
- package/dist/chunks/{studio-5t7ena5z.js → studio-ygvr9e4q.js} +10 -10
- package/dist/chunks/{studio-z2smx5ce.js → studio-yxmmjy2a.js} +9 -6
- package/dist/chunks/{tsMode-jy4m86rv.js → tsMode-ay4c78ne.js} +4 -4
- package/dist/chunks/{tsMode-hnxxb73h.css → tsMode-c72wyz27.css} +954 -137
- package/dist/chunks/{workspace-bnw54cha.js → workspace-b9k2frks.js} +5 -5
- package/dist/chunks/{ws-client-ny4zc51r.js → ws-client-erze4czt.js} +356 -20
- package/dist/codicon.ttf +0 -0
- package/dist/iframe-entry.js +20 -18
- package/dist/studio.css +956 -139
- package/dist/studio.js +1511 -851
- package/dist/workers/editor.worker.js +196 -107
- package/dist/workers/json.worker.js +196 -107
- package/dist/workers/ts.worker.js +196 -107
- package/package.json +18 -17
- package/src/about/about-modal.ts +4 -3
- package/src/browse/library-commands.ts +15 -13
- package/src/browse/library-pane.ts +2 -2
- package/src/canvas/canvas-render.ts +97 -83
- package/src/canvas/canvas-surface.ts +2 -2
- package/src/canvas/canvas-utils.ts +42 -32
- package/src/canvas/diff-toolbar.ts +21 -2
- package/src/canvas/diff-view.ts +30 -6
- package/src/canvas/iframe-entry.ts +2 -2
- package/src/canvas/iframe-host.ts +5 -5
- package/src/canvas/iframe-interaction.ts +7 -6
- package/src/canvas/iframe-overlay.ts +1 -1
- package/src/canvas/iframe-protocol.ts +6 -5
- package/src/canvas/iframe-render.ts +3 -3
- package/src/canvas/popover-state.ts +1 -1
- package/src/canvas/stage-scrollbar.ts +112 -0
- package/src/canvas/surface-registry.ts +9 -2
- package/src/collab/collab-commands.ts +8 -6
- package/src/collab/collab-session.ts +4 -4
- package/src/collab/collab-state.ts +4 -4
- package/src/collab/presence-chips.ts +3 -3
- package/src/commands/app-commands.ts +7 -7
- package/src/commands/budget.ts +17 -16
- package/src/commands/command-args.ts +10 -9
- package/src/commands/context.ts +20 -10
- package/src/commands/defaults.ts +30 -29
- package/src/commands/levels.ts +8 -6
- package/src/commands/live-context.ts +12 -10
- package/src/commands/reference.ts +5 -5
- package/src/commands/registry.ts +2 -2
- package/src/content/draft-state.ts +9 -8
- package/src/content/entry-commands.ts +16 -14
- package/src/content/entry-editor.ts +2 -2
- package/src/content/entry-fields.ts +1 -1
- package/src/editor/context-menu.ts +4 -4
- package/src/editor/convert-to-component.ts +6 -5
- package/src/editor/inline-edit.ts +3 -3
- package/src/editor/shortcuts.ts +88 -20
- package/src/editor/slash-menu.ts +11 -10
- package/src/files/elements.ts +4 -5
- package/src/files/file-ops.ts +1 -1
- package/src/files/files.ts +21 -17
- package/src/files/serialize-document.ts +1 -1
- package/src/format/convert-file.ts +5 -5
- package/src/github/github-auth-status.ts +107 -0
- package/src/github/github-auth.ts +18 -82
- package/src/github/github-publish.ts +7 -6
- package/src/grid/cell-editors.ts +8 -7
- package/src/grid/grid-layout.ts +1 -1
- package/src/grid/grid-open.ts +6 -5
- package/src/grid/grid-panel.ts +13 -12
- package/src/grid/redirects-grid.ts +14 -11
- package/src/grid/redirects.ts +10 -5
- package/src/grid/sources/content-source.ts +10 -9
- package/src/hosting/document.ts +28 -1
- package/src/hosting/stage.ts +15 -4
- package/src/i18n/i18n-commands.ts +9 -7
- package/src/new-project/location-fields.ts +16 -3
- package/src/new-project/new-project-modal.ts +23 -7
- package/src/page-params.ts +6 -6
- package/src/panels/activity-panel.ts +11 -10
- package/src/panels/ai-chat/chat-view.ts +27 -14
- package/src/panels/ai-chat/composer.ts +10 -3
- package/src/panels/ai-panel.ts +87 -29
- package/src/panels/block-action-bar.ts +59 -33
- package/src/panels/bottom-dock.ts +22 -22
- package/src/panels/chat-panel.ts +40 -5
- package/src/panels/data-explorer.ts +9 -9
- package/src/panels/data-grid.ts +1 -1
- package/src/panels/editors.ts +8 -8
- package/src/panels/elements-panel.ts +7 -8
- package/src/panels/empty-state.ts +1 -1
- package/src/panels/events-panel.ts +13 -12
- package/src/panels/formula-workspace.ts +8 -5
- package/src/panels/frontmatter-fields.ts +4 -1
- package/src/panels/frontmatter-panel.ts +18 -18
- package/src/panels/git-panel.ts +19 -13
- package/src/panels/head-panel.ts +9 -10
- package/src/panels/i18n-panel.ts +6 -4
- package/src/panels/imports-panel.ts +1 -1
- package/src/panels/jump-bar.ts +20 -18
- package/src/panels/layers-panel.ts +19 -17
- package/src/panels/left-panel.ts +3 -3
- package/src/panels/navigator-panels.ts +8 -8
- package/src/panels/overlays.ts +1 -1
- package/src/panels/pane-context.ts +56 -28
- package/src/panels/pane-grid.ts +9 -8
- package/src/panels/panel-registry.ts +47 -19
- package/src/panels/problems-panel.ts +11 -12
- package/src/panels/properties-panel.ts +21 -20
- package/src/panels/provenance.ts +3 -3
- package/src/panels/quick-search.ts +15 -11
- package/src/panels/right-panel.ts +4 -3
- package/src/panels/seo-modal.ts +5 -4
- package/src/panels/settings-menu.ts +9 -9
- package/src/panels/settings-pane.ts +3 -3
- package/src/panels/signals-panel.ts +16 -9
- package/src/panels/statement-editor.ts +12 -9
- package/src/panels/style-panel.ts +45 -41
- package/src/panels/stylebook-panel.ts +0 -1
- package/src/panels/tab-strip.ts +26 -23
- package/src/platform-errors.ts +2 -2
- package/src/platform.ts +44 -4
- package/src/platforms/cloud.ts +11 -1
- package/src/platforms/default-platform.ts +141 -4
- package/src/platforms/devserver.ts +23 -3
- package/src/publish/deploy-checklist.ts +11 -10
- package/src/publish/publish-commands.ts +9 -8
- package/src/services/a11y-report.ts +1 -1
- package/src/services/ai-ask.ts +39 -16
- package/src/services/ai-command-tools.ts +28 -14
- package/src/services/ai-import-tools.ts +43 -29
- package/src/services/ai-project-tools.ts +20 -38
- package/src/services/ai-settings.ts +55 -2
- package/src/services/ai-tools.ts +69 -58
- package/src/services/ai-write-report.ts +4 -4
- package/src/services/ai-writes.ts +74 -55
- package/src/services/automation.ts +12 -12
- package/src/services/clock.ts +2 -1
- package/src/services/connection.ts +2 -2
- package/src/services/context-manager.ts +69 -6
- package/src/services/directory-picker.ts +93 -27
- package/src/services/document-assistant.ts +86 -17
- package/src/services/gated-registry.ts +4 -3
- package/src/services/idle.ts +5 -4
- package/src/services/live-surfaces.ts +2 -1
- package/src/services/module-preloads.ts +67 -0
- package/src/services/notify.ts +9 -7
- package/src/services/popover-report.ts +3 -3
- package/src/services/preview-eval.ts +1 -1
- package/src/services/project-adoption.ts +34 -1
- package/src/services/project-path.ts +53 -0
- package/src/services/settings/kernel.ts +13 -2
- package/src/services/tool-executor.ts +135 -30
- package/src/services/tool-outcomes.ts +143 -0
- package/src/settings/contexts-section.ts +8 -8
- package/src/settings/contributed-section.ts +6 -6
- package/src/settings/css-vars-editor.ts +6 -5
- package/src/settings/extension-commands.ts +19 -18
- package/src/settings/extension-sections.ts +7 -7
- package/src/settings/extensions-section.ts +3 -3
- package/src/settings/general-settings.ts +21 -18
- package/src/settings/preferences-accounts.ts +3 -3
- package/src/settings/preferences-dialog.ts +9 -8
- package/src/settings/preferences-sections.ts +1 -1
- package/src/settings/project-sections.ts +2 -2
- package/src/settings/section-registry.ts +7 -6
- package/src/settings/settings-document.ts +41 -43
- package/src/shell/tree.ts +29 -0
- package/src/shell.ts +63 -55
- package/src/site-context.ts +1 -1
- package/src/store.ts +2 -2
- package/src/studio.ts +52 -28
- package/src/style/live-preview.ts +8 -8
- package/src/style/project-styles.ts +8 -9
- package/src/style/token-ref.ts +2 -2
- package/src/surfaces/about.json +6 -2
- package/src/surfaces/add-repo.json +2 -2
- package/src/surfaces/add-repo.ts +2 -2
- package/src/surfaces/ai-chat.json +14 -6
- package/src/surfaces/ai-chat.ts +4 -2
- package/src/surfaces/ai-credentials-form.json +53 -49
- package/src/surfaces/ai-credentials-form.ts +18 -4
- package/src/surfaces/ai-managed-connect.json +4 -4
- package/src/surfaces/block-action-bar.json +4 -4
- package/src/surfaces/block-action-bar.ts +3 -3
- package/src/surfaces/boot-failure.json +228 -0
- package/src/surfaces/boot-failure.ts +156 -0
- package/src/surfaces/bottom-dock.json +1 -1
- package/src/surfaces/canvas-stage.json +9 -40
- package/src/surfaces/canvas-stage.ts +9 -9
- package/src/surfaces/cf-account-picker.json +11 -8
- package/src/surfaces/commandbar.ts +11 -10
- package/src/surfaces/convert-repeater.ts +3 -2
- package/src/surfaces/dialog.json +7 -1
- package/src/surfaces/dialog.ts +2 -2
- package/src/surfaces/doc-header.json +6 -13
- package/src/surfaces/doc-header.ts +4 -5
- package/src/surfaces/entry-editor.json +1 -1
- package/src/surfaces/entry-editor.ts +2 -1
- package/src/surfaces/expression-editor.json +5 -5
- package/src/surfaces/files-panel.json +4 -3
- package/src/surfaces/files-panel.ts +1 -1
- package/src/surfaces/formula-palette.json +10 -9
- package/src/surfaces/formula-palette.ts +11 -7
- package/src/surfaces/git-panel.json +167 -109
- package/src/surfaces/git-panel.ts +2 -2
- package/src/surfaces/github-auth.json +2 -1
- package/src/surfaces/github-publish.ts +3 -3
- package/src/surfaces/grid-open.json +1 -1
- package/src/surfaces/grid-panel.json +2 -2
- package/src/surfaces/grid-views.json +1 -1
- package/src/surfaces/inspector-dock.json +2 -2
- package/src/surfaces/jump-bar.json +1 -1
- package/src/surfaces/jump-bar.ts +1 -1
- package/src/surfaces/library-pane.ts +2 -1
- package/src/surfaces/logic-panel.json +7 -7
- package/src/surfaces/logic-panel.ts +6 -4
- package/src/surfaces/logic-workspace.json +2 -2
- package/src/surfaces/logic-workspace.ts +2 -2
- package/src/surfaces/media-browser.ts +3 -3
- package/src/surfaces/media-pane.ts +2 -1
- package/src/surfaces/menu.ts +5 -5
- package/src/surfaces/new-project.json +1 -1
- package/src/surfaces/palette.json +8 -7
- package/src/surfaces/pane-context.json +86 -57
- package/src/surfaces/pane-context.ts +2 -1
- package/src/surfaces/pane-grid.json +3 -3
- package/src/surfaces/panel-activity.json +2 -1
- package/src/surfaces/panel-deploy-checklist.json +1 -1
- package/src/surfaces/panel-elements.ts +2 -1
- package/src/surfaces/panel-i18n.ts +2 -1
- package/src/surfaces/panel-outline.json +1 -1
- package/src/surfaces/panel-outline.ts +4 -3
- package/src/surfaces/panel-page.json +2 -2
- package/src/surfaces/panel-page.ts +4 -1
- package/src/surfaces/panel-problems.json +4 -3
- package/src/surfaces/panel-problems.ts +2 -2
- package/src/surfaces/panel-signals.json +23 -8
- package/src/surfaces/panel-signals.ts +5 -1
- package/src/surfaces/panel-stylebook-layers.json +1 -1
- package/src/surfaces/panel-stylebook-layers.ts +2 -2
- package/src/surfaces/preferences.json +21 -8
- package/src/surfaces/preferences.ts +9 -1
- package/src/surfaces/properties-panel.json +4 -4
- package/src/surfaces/properties-panel.ts +2 -2
- package/src/surfaces/publish.json +1 -1
- package/src/surfaces/publish.ts +2 -1
- package/src/surfaces/push-plan.json +4 -3
- package/src/surfaces/rail.json +2 -2
- package/src/surfaces/rail.ts +10 -4
- package/src/surfaces/reference-field.json +1 -1
- package/src/surfaces/schema-form.json +2 -2
- package/src/surfaces/schema-form.ts +1 -1
- package/src/surfaces/seo.json +1 -1
- package/src/surfaces/seo.ts +2 -2
- package/src/surfaces/settings-contributed.ts +2 -1
- package/src/surfaces/settings-css-vars.json +4 -4
- package/src/surfaces/settings-deploy.json +4 -4
- package/src/surfaces/settings-deploy.ts +2 -1
- package/src/surfaces/settings-extensions.json +6 -6
- package/src/surfaces/settings-extensions.ts +6 -2
- package/src/surfaces/settings-locales.json +4 -4
- package/src/surfaces/settings-overview.ts +2 -1
- package/src/surfaces/settings-pane.ts +3 -2
- package/src/surfaces/settings-rawjson.ts +1 -1
- package/src/surfaces/slash-menu.ts +4 -4
- package/src/surfaces/statements.json +3 -3
- package/src/surfaces/statusbar.ts +23 -23
- package/src/surfaces/style-panel.json +9 -5
- package/src/surfaces/style-panel.ts +3 -3
- package/src/surfaces/tab-strip.json +2 -2
- package/src/surfaces/target-line.json +1 -1
- package/src/surfaces/target-line.ts +12 -10
- package/src/surfaces/welcome.json +7 -2
- package/src/tabs/doc-op-apply.ts +5 -5
- package/src/tabs/patch-ops.ts +3 -3
- package/src/tabs/project-config.ts +10 -10
- package/src/tabs/selection.ts +1 -1
- package/src/tabs/tab.ts +13 -14
- package/src/tabs/transact.ts +18 -24
- package/src/types.ts +27 -3
- package/src/ui/ai-credentials-form.ts +74 -16
- package/src/ui/ai-managed-connect.ts +68 -19
- package/src/ui/color-selector.ts +1 -1
- package/src/ui/expression-editor.ts +3 -3
- package/src/ui/form-controls.ts +6 -5
- package/src/ui/formula-catalog.ts +4 -4
- package/src/ui/formula-chips.ts +2 -2
- package/src/ui/layers.ts +20 -18
- package/src/ui/panel-resize.ts +8 -28
- package/src/ui/popover-surface.ts +2 -2
- package/src/ui/progress-modal.ts +3 -3
- package/src/ui/regions.ts +14 -9
- package/src/ui/schema-form.ts +10 -9
- package/src/ui/timing.ts +9 -7
- package/src/ui/value-source.ts +4 -4
- package/src/ui/virtual-window.ts +58 -5
- package/src/utils/base64.ts +20 -1
- package/src/utils/canvas-media.ts +1 -1
- package/src/utils/studio-utils.ts +0 -31
- package/src/view.ts +4 -3
- package/src/workspace/pane-derive.ts +14 -13
- package/src/workspace/session.ts +7 -6
- package/src/workspace/workspace.ts +49 -39
- package/styles/canvas.css +1 -27
- package/styles/inspector.css +4 -3
- package/styles/overlays.css +1 -1
- package/styles/tokens.css +17 -1
- package/styles/tokens.json +6 -1
- package/dist/chunks/entry-commands-w9qsxa6q.js +0 -42
- package/dist/chunks/layers-panel-fksbdg4q.js +0 -40
- package/dist/chunks/publish-panel-vz23wwmg.js +0 -27
- package/dist/chunks/quick-search-e9rp74m8.js +0 -46
- package/dist/chunks/right-panel-j97t2yvy.js +0 -45
- package/dist/chunks/studio-727xh0e3.js +0 -76
- package/src/files/json-layout.ts +0 -474
- package/src/services/ai-turn-signal.ts +0 -70
|
@@ -674,11 +674,17 @@ export function resetZoom(surface: CanvasSurface = activeCanvasSurface()) {
|
|
|
674
674
|
* (`iframe-host.ts`'s `pointForRect` composes the frame offset and the empirical scale before it
|
|
675
675
|
* hands a rect back), so one arithmetic serves a rect measured from a parent DOM element and a rect
|
|
676
676
|
* measured inside a canvas iframe alike.
|
|
677
|
+
*
|
|
678
|
+
* **The viewport is the box that SCROLLS, not the stage.** Edit docks the Document Header card
|
|
679
|
+
* above its scroller, inside the same stage cell, so there the stage is the band plus the page and
|
|
680
|
+
* its centre is half a band too high: a reveal would land the node that far above the middle of
|
|
681
|
+
* what the author can actually see. {@link revealScroller} is that box wherever there is one, and
|
|
682
|
+
* where there is none (Design, Stylebook, a comparison) the stage is still the viewport.
|
|
677
683
|
*/
|
|
678
684
|
function centeringOffset(surface: CanvasSurface, rect: { top: number; height: number }): number {
|
|
679
|
-
const
|
|
680
|
-
const elCenterY = rect.top + rect.height / 2 -
|
|
681
|
-
return
|
|
685
|
+
const viewport = rectOf(revealScroller(surface) ?? surface.wrap);
|
|
686
|
+
const elCenterY = rect.top + rect.height / 2 - viewport.top;
|
|
687
|
+
return viewport.height / 2 - elCenterY;
|
|
682
688
|
}
|
|
683
689
|
|
|
684
690
|
/**
|
|
@@ -789,7 +795,7 @@ export type CanvasMode = (typeof CANVAS_MODES)[number];
|
|
|
789
795
|
/** The base modes the preview flag composes with — the two the Canvas view axis shows it beside. */
|
|
790
796
|
const PREVIEWABLE_BASE_MODES = new Set(["edit", "design"]);
|
|
791
797
|
|
|
792
|
-
// ─── §4.2
|
|
798
|
+
// ─── The Canvas view (§4.2) ──────────────────────────────────────────────────
|
|
793
799
|
// `Edit │ Design` is a radio and `Preview` is a TOGGLE beside it, because that is what the storage
|
|
794
800
|
// Has always been: `preview` is not a base mode, it is a flag that composes with one.
|
|
795
801
|
//
|
|
@@ -826,8 +832,9 @@ interface ViewableTab {
|
|
|
826
832
|
/**
|
|
827
833
|
* The views this document supports, in axis order — the control's entries.
|
|
828
834
|
*
|
|
829
|
-
* Filtered by `capabilities.modes` for the same reason the
|
|
830
|
-
* permanently dead segment is a question the app refuses to
|
|
835
|
+
* Filtered by `capabilities.modes` for the same reason the Editor control is (the pane context bar,
|
|
836
|
+
* `docs/studio/interface/tabs.md`): a permanently dead segment is a question the app refuses to
|
|
837
|
+
* answer.
|
|
831
838
|
*/
|
|
832
839
|
export function canvasViewsFor(tab: ViewableTab): CanvasView[] {
|
|
833
840
|
return CANVAS_VIEWS.filter((value) => tab.capabilities.modes.includes(value));
|
|
@@ -937,16 +944,16 @@ const PANZOOM_MODES = new Set(["design", "stylebook", "git-diff"]);
|
|
|
937
944
|
|
|
938
945
|
const documentOpen = (ctx: { document: { open: boolean } }) => ctx.document.open;
|
|
939
946
|
|
|
940
|
-
/** The three values `session.ui.previewColorScheme` may hold (spec §9.5). */
|
|
947
|
+
/** The three values `session.ui.previewColorScheme` may hold (spec.md §9.5). */
|
|
941
948
|
const COLOR_SCHEMES = ["auto", "light", "dark"] as const;
|
|
942
949
|
|
|
943
950
|
/**
|
|
944
951
|
* The canvas view verbs — `setMode`, `setZoom`, `setEditZoom`.
|
|
945
952
|
*
|
|
946
|
-
* **These are the setters that retire `canvas.togglePreview`** (
|
|
947
|
-
* cannot say which state it ends in, so six screenshots taken through it photographed
|
|
948
|
-
* the default happened to point that week; `canvas.setMode { mode: "preview" }` means
|
|
949
|
-
* thing twice in a row and from any starting state.
|
|
953
|
+
* **These are the setters that retire `canvas.togglePreview`** (the idempotence rule, §13.5). A
|
|
954
|
+
* toggle cannot say which state it ends in, so six screenshots taken through it photographed
|
|
955
|
+
* whichever way the default happened to point that week; `canvas.setMode { mode: "preview" }` means
|
|
956
|
+
* the same thing twice in a row and from any starting state.
|
|
950
957
|
*
|
|
951
958
|
* Three refusals, each a wrong picture that used to be accepted silently:
|
|
952
959
|
*
|
|
@@ -1025,13 +1032,14 @@ export function canvasViewCommands(deps: CanvasCommandDeps): AnyCommand[] {
|
|
|
1025
1032
|
requires: "an open document",
|
|
1026
1033
|
when: documentOpen,
|
|
1027
1034
|
/*
|
|
1028
|
-
* Projected, and it was deleted once under §12.4's first rule
|
|
1029
|
-
* verb whose WHOLE effect is what the person is looking at, and
|
|
1030
|
-
* tools are gated on `editor.kind`, which is derived from the
|
|
1031
|
-
* a document the person left in Code — or the model itself
|
|
1032
|
-
* with "requires an open document whose element tree the
|
|
1033
|
-
* verb the model had no way back to a state it can
|
|
1034
|
-
* the model reads the result: `after.editor.kind` is
|
|
1035
|
+
* Projected, and it was deleted once under studio-ui-guidelines.md §12.4's first rule
|
|
1036
|
+
* (chrome). The rule is about a verb whose WHOLE effect is what the person is looking at, and
|
|
1037
|
+
* this one's is not: the tree tools are gated on `editor.kind`, which is derived from the
|
|
1038
|
+
* mode (`editorKindForMode`), so a document the person left in Code — or the model itself
|
|
1039
|
+
* moved — refuses every tree write with "requires an open document whose element tree the
|
|
1040
|
+
* canvas is editing", and without this verb the model had no way back to a state it can
|
|
1041
|
+
* read. `run` acts on the active tab and the model reads the result: `after.editor.kind` is
|
|
1042
|
+
* the same fact the gate reads.
|
|
1035
1043
|
*/
|
|
1036
1044
|
aiTool: {
|
|
1037
1045
|
description:
|
|
@@ -1210,7 +1218,7 @@ export function canvasViewCommands(deps: CanvasCommandDeps): AnyCommand[] {
|
|
|
1210
1218
|
title: "Set Edit Width",
|
|
1211
1219
|
},
|
|
1212
1220
|
/*
|
|
1213
|
-
* ── The rendering context (§
|
|
1221
|
+
* ── The rendering context (§6.2) ──────────────────────────────────────────────────────────
|
|
1214
1222
|
*
|
|
1215
1223
|
* Three setters for the three axes the Context popover offers. The popover wrote
|
|
1216
1224
|
* `session.ui.activeMedia` / `previewColorScheme` / `showLayout` through `updateUi` directly, so
|
|
@@ -1218,11 +1226,11 @@ export function canvasViewCommands(deps: CanvasCommandDeps): AnyCommand[] {
|
|
|
1218
1226
|
* assistant, and not bindable — in a design whose first principle is that a capability exists
|
|
1219
1227
|
* as a `Command` record and every surface projects it.
|
|
1220
1228
|
*
|
|
1221
|
-
* SETTERS, not cycles.
|
|
1222
|
-
*
|
|
1223
|
-
*
|
|
1224
|
-
* state you end in works from every surface; the chords are
|
|
1225
|
-
* made here.
|
|
1229
|
+
* SETTERS, not cycles. The cycle chords once proposed for the size and scheme axes
|
|
1230
|
+
* (`⌘⌥↑`/`⌘⌥↓`, `⌘⌥⇧S`) carry no argument, so they would need `next`/`prev` records of their
|
|
1231
|
+
* own — a delta each, which the shot contract's R1 (`scripts/screenshots/README.md`) forbids a
|
|
1232
|
+
* screenshot from naming. Naming the state you end in works from every surface; the chords are
|
|
1233
|
+
* a separate decision and are not made here (§6.2).
|
|
1226
1234
|
*/
|
|
1227
1235
|
{
|
|
1228
1236
|
args: {
|
|
@@ -1302,12 +1310,13 @@ export function canvasViewCommands(deps: CanvasCommandDeps): AnyCommand[] {
|
|
|
1302
1310
|
* The language a pane RENDERS AS — and only that.
|
|
1303
1311
|
*
|
|
1304
1312
|
* Jx has no message catalogue: a translation is a different file in a different directory
|
|
1305
|
-
* (site-architecture §13.3), so "show this page in French" is a navigation and the locale
|
|
1313
|
+
* (site-architecture.md §13.3), so "show this page in French" is a navigation and the locale
|
|
1306
1314
|
* preset owns it. What is left over is genuinely a rendering context, and it is the half of
|
|
1307
|
-
* §13.4 the canvas can show today: the artboard root's `lang` and `dir`.
|
|
1308
|
-
* verb and not a label — an RTL locale mirrors the layout on screen.
|
|
1309
|
-
* `$page.locale` in the rendered state, is injected by the
|
|
1310
|
-
* `substitutePreviewParams`; previewing it is a separate
|
|
1315
|
+
* site-architecture.md §13.4 the canvas can show today: the artboard root's `lang` and `dir`.
|
|
1316
|
+
* That is why this is a verb and not a label — an RTL locale mirrors the layout on screen.
|
|
1317
|
+
* (That section's other half, `$page.locale` in the rendered state, is injected by the
|
|
1318
|
+
* compiler and not by the canvas's `substitutePreviewParams`; previewing it is a separate
|
|
1319
|
+
* change to the render path.)
|
|
1311
1320
|
*
|
|
1312
1321
|
* It lives in this file rather than in `i18n/i18n-commands.ts` because `contextTab`, `paneArg`
|
|
1313
1322
|
* and `repaint` are local closures here, and every rendering-context verb needs all three: the
|
|
@@ -1514,9 +1523,10 @@ export function canvasViewCommands(deps: CanvasCommandDeps): AnyCommand[] {
|
|
|
1514
1523
|
when: documentOpen,
|
|
1515
1524
|
/*
|
|
1516
1525
|
* The route params and component test props live in a popover now, and a transient surface
|
|
1517
|
-
* opens by COMMAND rather than by clicking (
|
|
1518
|
-
*
|
|
1519
|
-
* The pointer and the camera use the same
|
|
1526
|
+
* opens by COMMAND rather than by clicking (§6.2) — otherwise the one shot that types a test
|
|
1527
|
+
* value would need a CSS selector to reach it, which the shot contract
|
|
1528
|
+
* (`scripts/screenshots/README.md`) forbids outright. The pointer and the camera use the same
|
|
1529
|
+
* door.
|
|
1520
1530
|
*/
|
|
1521
1531
|
run: (_commandCtx, args) => {
|
|
1522
1532
|
// Resolve the tab first, so a pane with no document refuses before anything opens.
|
|
@@ -79,9 +79,27 @@ export function setDiffRepaint(repaint: (paneId: string) => void): void {
|
|
|
79
79
|
_repaint = repaint;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* How this module asks the pane CHROME to redraw, injected by `studio.ts`.
|
|
84
|
+
*
|
|
85
|
+
* The zoom pod is not drawn over the Code half (`panels/pane-context.ts`, `podFor`), and the half
|
|
86
|
+
* lives in `diff-view.ts`, which is deliberately not reactive — so nothing would tell the pod the
|
|
87
|
+
* half had changed, and it would go on floating over Monaco until something unrelated repainted the
|
|
88
|
+
* bar. Injected rather than imported for {@link setDiffRepaint}'s reason: the chrome module is far
|
|
89
|
+
* outside this one's graph, and `studio.ts` is the one place that already holds both. Null until
|
|
90
|
+
* then, which is only ever a test that drives the toolbar without the shell.
|
|
91
|
+
*/
|
|
92
|
+
let _chromeRepaint: (() => void) | null = null;
|
|
93
|
+
|
|
94
|
+
/** Register the chrome repaint. Called once, from `studio.ts`. */
|
|
95
|
+
export function setDiffChromeRepaint(repaint: () => void): void {
|
|
96
|
+
_chromeRepaint = repaint;
|
|
97
|
+
}
|
|
98
|
+
|
|
82
99
|
/**
|
|
83
100
|
* Rebuild the whole stage, because Visual and Code are different renders rather than different
|
|
84
|
-
* styling: one draws two artboards on a pan/zoom surface, the other one Monaco.
|
|
101
|
+
* styling: one draws two artboards on a pan/zoom surface, the other one Monaco. And redraw the
|
|
102
|
+
* pane's chrome after it, because whether the zoom pod is drawn at all depends on the half.
|
|
85
103
|
*
|
|
86
104
|
* `prevCanvasMode` is nulled first — the documented "this stage's structure is stale" signal, the
|
|
87
105
|
* same one `resetCanvasView` ends on. Without it the repaint sees `modeChanged === false` (the
|
|
@@ -90,6 +108,7 @@ export function setDiffRepaint(repaint: (paneId: string) => void): void {
|
|
|
90
108
|
function repaintDiffStage(paneId: string): void {
|
|
91
109
|
surfaceForPane(paneId).prevCanvasMode = null;
|
|
92
110
|
_repaint(paneId);
|
|
111
|
+
_chromeRepaint?.();
|
|
93
112
|
}
|
|
94
113
|
|
|
95
114
|
/** Redraw one pane's toolbar in place, without touching its artboards. */
|
|
@@ -332,7 +351,7 @@ export function diffCommands(): AnyCommand[] {
|
|
|
332
351
|
title: "Set Diff View",
|
|
333
352
|
undo: "none",
|
|
334
353
|
when: (ctx) => ctx.editor.kind === "diff",
|
|
335
|
-
/* No `aiTool`, by §12.4's first deletion rule: chrome. */
|
|
354
|
+
/* No `aiTool`, by studio-ui-guidelines.md §12.4's first deletion rule: chrome. */
|
|
336
355
|
run: (_ctx, args) => {
|
|
337
356
|
// An idempotent SETTER, never a toggle: the screenshot contract refuses a `toggle*` id, and
|
|
338
357
|
// A verb whose result depends on the state it is called in cannot be photographed honestly.
|
package/src/canvas/diff-view.ts
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* **The store is a module-local `Map` keyed by pane**, the shape `_editWidths` in `edit-width.ts`
|
|
5
5
|
* and `_fits` in `canvas-utils.ts` already use. The value is transient — it is rebuilt by the
|
|
6
|
-
* render pass that computes the comparison — and its only readers are that pass, the stepper
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* render pass that computes the comparison — and its only readers are that pass, the stepper, the
|
|
7
|
+
* diff header and the zoom pod (which asks only which half is showing), so a field on
|
|
8
|
+
* `CanvasSurface` would buy nothing and a field on `shell` would be an app-level slot for a
|
|
9
|
+
* per-pane fact, which is exactly the defect the Diff LENS was built to avoid
|
|
10
|
+
* (`workspace/pane-derive.ts`, finding 4: one app-level `diffState` meant two panes drew one
|
|
10
11
|
* comparison).
|
|
11
12
|
*
|
|
12
13
|
* **Keyed by PANE, because two panes can compare two different files at once** — the Source Control
|
|
@@ -14,8 +15,8 @@
|
|
|
14
15
|
* source tab's `session.ui`. `canvasModeOfPane` and `derivationOfPane` are keyed the same way for
|
|
15
16
|
* the same reason.
|
|
16
17
|
*
|
|
17
|
-
* **This file is a LEAF.** It imports one type and nothing else, so the render pass, the stepper
|
|
18
|
-
* and the
|
|
18
|
+
* **This file is a LEAF.** It imports one type and nothing else, so the render pass, the stepper,
|
|
19
|
+
* the header and the pane chrome can all reach it without any of them reaching each other.
|
|
19
20
|
*/
|
|
20
21
|
|
|
21
22
|
import type { ChangeMap } from "./diff-marks";
|
|
@@ -102,11 +103,34 @@ export function diffViewOf(paneId: string): DiffView {
|
|
|
102
103
|
return _records.get(paneId)?.view ?? "visual";
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Whether this pane draws its comparison as TEXT: the file has no visual half, or the author chose
|
|
108
|
+
* Code.
|
|
109
|
+
*
|
|
110
|
+
* One definition, because three callers must never disagree about it. `canvas-render.ts`'s branch
|
|
111
|
+
* chooses which half to build and its mount's post-await guard re-asks whether that half is still
|
|
112
|
+
* wanted; they were two spellings — the branch said "not renderable OR the author chose Code", the
|
|
113
|
+
* guard said only "the author chose Code" — so for a file with no visual half the branch built the
|
|
114
|
+
* Code container and the guard then refused to mount into it. An empty stage, no error, and a
|
|
115
|
+
* toolbar correctly describing an editor that was never there. The third is the zoom pod
|
|
116
|
+
* (`panels/pane-context.ts`), which is not drawn over the Code half: nothing there zooms, and it
|
|
117
|
+
* sat on Monaco's own scrollbar.
|
|
118
|
+
*
|
|
119
|
+
* **Renderability is an ARGUMENT** — `canRenderComparison(path)` from `panels/git-diff-open.ts` —
|
|
120
|
+
* so this file stays the leaf its header promises. Taking the path would have pulled the opener
|
|
121
|
+
* (and the media pane behind it) into everything that reads a pane's review state.
|
|
122
|
+
*/
|
|
123
|
+
export function diffShowsCode(paneId: string, renderable: boolean): boolean {
|
|
124
|
+
return !renderable || diffViewOf(paneId) === "code";
|
|
125
|
+
}
|
|
126
|
+
|
|
105
127
|
/**
|
|
106
128
|
* Show the visual comparison or the code one.
|
|
107
129
|
*
|
|
108
130
|
* The caller pairs this with a repaint: the store is deliberately not reactive (its writers are a
|
|
109
131
|
* render pass and a toolbar click, both of which already know to redraw), so nothing observes it.
|
|
132
|
+
* That repaint is the stage AND the pane chrome, because the zoom pod reads this through
|
|
133
|
+
* {@link diffShowsCode} — see `repaintDiffStage` in `diff-toolbar.ts`.
|
|
110
134
|
*/
|
|
111
135
|
export function setDiffView(paneId: string, view: DiffView): void {
|
|
112
136
|
recordFor(paneId).view = view;
|
|
@@ -209,7 +209,7 @@ export function patchDisturbsActiveEdit(forwardOps: JxDocOp[]): boolean {
|
|
|
209
209
|
return false;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
/** Consecutive quiet animation frames before the frame declares itself settled (§13.
|
|
212
|
+
/** Consecutive quiet animation frames before the frame declares itself settled (§13.5). */
|
|
213
213
|
export const IDLE_QUIET_FRAMES = 2;
|
|
214
214
|
|
|
215
215
|
/**
|
|
@@ -466,7 +466,7 @@ export function startCanvasIframe(opts: {
|
|
|
466
466
|
const heightObserver = ResizeObs ? new ResizeObs(() => postContentHeight()) : null;
|
|
467
467
|
|
|
468
468
|
// ─── Cross-realm quiescence ─────────────────────────────────────────────────
|
|
469
|
-
// The frame answers "have I settled?" instead of being polled (§13.
|
|
469
|
+
// The frame answers "have I settled?" instead of being polled (§13.5). Nothing in the
|
|
470
470
|
// Parent realm can look inside a cross-origin frame, so the alternative was `wait: {ms}` — and 115
|
|
471
471
|
// Of those were 115 places a slow subsystem got answered with +500 ms and the wrong picture was
|
|
472
472
|
// Accepted. Sampling lives HERE, next to the render that changes the answer.
|
|
@@ -203,7 +203,7 @@ interface HostState {
|
|
|
203
203
|
* The frame's own last quiescence report (`{kind: "idle"}`), or null before the first one.
|
|
204
204
|
*
|
|
205
205
|
* Held PER HOST from the start. `shot.ts`'s "Studio's only child frame" is a coin flip the moment
|
|
206
|
-
*
|
|
206
|
+
* a second pane exists, and a global would have to be unpicked exactly then.
|
|
207
207
|
*/
|
|
208
208
|
idle: Omit<Extract<IframeToParent, { kind: "idle" }>, "kind"> | null;
|
|
209
209
|
/**
|
|
@@ -607,7 +607,7 @@ function hostLabel(host: HostState): string {
|
|
|
607
607
|
* Empty means every live frame's DOM reflects what this host told it, its fonts have loaded, no
|
|
608
608
|
* animation is running and no image retry is in flight. Naming the blockers is the whole point:
|
|
609
609
|
* `probe.idle()` rejects with this list, so a slow subsystem identifies itself instead of being
|
|
610
|
-
* answered with `+500 ms` and a wrong capture (
|
|
610
|
+
* answered with `+500 ms` and a wrong capture ("Determinism", `scripts/screenshots/README.md`).
|
|
611
611
|
*/
|
|
612
612
|
export function canvasIdleBlockers(): string[] {
|
|
613
613
|
const blockers: string[] = [];
|
|
@@ -660,8 +660,8 @@ export function canvasIdleBlockers(): string[] {
|
|
|
660
660
|
*
|
|
661
661
|
* The caller gets a point it can act on directly — click it, scroll to it, anchor a popover to it —
|
|
662
662
|
* without knowing that a canvas iframe, a panzoom transform and an edit-zoom scale sit between the
|
|
663
|
-
* document and the screen.
|
|
664
|
-
*
|
|
663
|
+
* document and the screen. The find-references jump, the jump bar and collab follow-peer all want
|
|
664
|
+
* exactly this, and so does anything driving Studio from outside.
|
|
665
665
|
*/
|
|
666
666
|
export interface CanvasPoint {
|
|
667
667
|
x: number;
|
|
@@ -1551,7 +1551,7 @@ function requestPresence(host: HostState): void {
|
|
|
1551
1551
|
if (!structuralSelection || peer.state.focusedPath !== tab?.documentPath) {
|
|
1552
1552
|
continue;
|
|
1553
1553
|
}
|
|
1554
|
-
// A peer publishes their whole selection SET (§6.
|
|
1554
|
+
// A peer publishes their whole selection SET (§6.7). Every path gets its own box under the
|
|
1555
1555
|
// Same name and colour — the meta map is keyed by path, so a peer selecting six nodes draws
|
|
1556
1556
|
// Six boxes and a peer selecting one draws exactly the one box it always did.
|
|
1557
1557
|
for (const path of structuralSelection) {
|
|
@@ -294,9 +294,9 @@ export function startInteraction(
|
|
|
294
294
|
}
|
|
295
295
|
const hit = nearestHit(e.target);
|
|
296
296
|
if (hit) {
|
|
297
|
-
// Ctrl/Cmd is the accumulate gesture (§
|
|
298
|
-
//
|
|
299
|
-
//
|
|
297
|
+
// Ctrl/Cmd is the accumulate gesture (studio-ui-guidelines.md §8.1). The iframe reports the
|
|
298
|
+
// Modifier and nothing else — it holds no selection state, so what a modified click MEANS is
|
|
299
|
+
// The parent's to decide, exactly as the unmodified click already was.
|
|
300
300
|
const mouse = e as MouseEvent;
|
|
301
301
|
channel.post({
|
|
302
302
|
additive: mouse.ctrlKey === true || mouse.metaKey === true,
|
|
@@ -427,9 +427,10 @@ export function startInteraction(
|
|
|
427
427
|
*
|
|
428
428
|
* This called `preventDefault()` before looking, "legacy parity" with a handler that did the
|
|
429
429
|
* same. The parent then posts `path: null`, `showContextMenu` returns early on it, and the
|
|
430
|
-
* result is a right-click that suppresses the browser menu and shows nothing in its place
|
|
431
|
-
*
|
|
432
|
-
*
|
|
430
|
+
* result is a right-click that suppresses the browser menu and shows nothing in its place, a
|
|
431
|
+
* dead zone where the docs promise the browser's own menu
|
|
432
|
+
* (`docs/studio/interface/canvas.md#the-right-click-context-menu`). The margin around the
|
|
433
|
+
* artboard is exactly where a reader reaches for View Source or Inspect.
|
|
433
434
|
*/
|
|
434
435
|
if (!hit) {
|
|
435
436
|
return;
|
|
@@ -71,7 +71,7 @@ export interface OverlayLayer {
|
|
|
71
71
|
*/
|
|
72
72
|
setPresence: (items: { placement: OverlayPlacement; color: string; label: string }[]) => void;
|
|
73
73
|
/**
|
|
74
|
-
* Draw the NON-primary members of a multi-selection (§6.
|
|
74
|
+
* Draw the NON-primary members of a multi-selection (§6.7). Replaces the whole set each call;
|
|
75
75
|
* pass [] to clear, which is what a selection of one always does — one selected node draws the
|
|
76
76
|
* selection box and nothing else, exactly as it did before the selection became a list.
|
|
77
77
|
*/
|
|
@@ -122,7 +122,7 @@ export type ParentToIframe =
|
|
|
122
122
|
assets?: AssetContext | null;
|
|
123
123
|
mapperCtx: WireMapperCtx;
|
|
124
124
|
siteStyle: Record<string, unknown> | null;
|
|
125
|
-
// Forced color-scheme preview (spec §9.5): "light"/"dark" sets data-color-scheme on the
|
|
125
|
+
// Forced color-scheme preview (spec.md §9.5): "light"/"dark" sets data-color-scheme on the
|
|
126
126
|
// Iframe root, null clears it (auto — follow the OS).
|
|
127
127
|
colorScheme: "light" | "dark" | null;
|
|
128
128
|
/**
|
|
@@ -478,13 +478,13 @@ export type IframeToParent =
|
|
|
478
478
|
// Not the parent, now resolves the scope). `gen` lets the parent drop a snapshot from a superseded
|
|
479
479
|
// Render. Posted right after `renderComplete`; values are JSON-safe deep clones (see serialize-scope).
|
|
480
480
|
| { kind: "dataScope"; gen: number; scope: Record<string, unknown> }
|
|
481
|
-
// The frame's own quiescence, ANSWERED rather than polled (
|
|
481
|
+
// The frame's own quiescence, ANSWERED rather than polled (studio.md §13.5).
|
|
482
482
|
// Nothing in the parent realm can see inside a cross-origin frame, so a parent that wanted to
|
|
483
483
|
// Know whether the canvas had settled could only sleep. The frame samples itself at its own rAF
|
|
484
484
|
// And posts whenever the tuple changes, ending with the quiet one; the host holds the latest.
|
|
485
485
|
//
|
|
486
486
|
// `fonts` is `document.fonts.status === "loaded"` and is NOT sufficient alone — in a blank canvas
|
|
487
|
-
// Frame it reports "loaded" against an EMPTY font set (measured
|
|
487
|
+
// Frame it reports "loaded" against an EMPTY font set (measured: `hero` drifted 0.150 RMSE
|
|
488
488
|
// Because the first capture measured fallback metrics while Plus Jakarta Sans was still in
|
|
489
489
|
// Flight). It is honest only together with `gen` (a frame that has not acked a render has not
|
|
490
490
|
// Loaded its fonts either) and the runner's per-frame network count.
|
|
@@ -496,8 +496,9 @@ export type IframeToParent =
|
|
|
496
496
|
// Pending retry is the one image fact only the app knows.
|
|
497
497
|
| { kind: "idle"; gen: number; fonts: boolean; animations: number; images: number }
|
|
498
498
|
// `additive` is Ctrl/Cmd being held at the moment of the click — the ACCUMULATE gesture, which
|
|
499
|
-
// The parent turns into a toggle against `session.selection` (studio §
|
|
500
|
-
//
|
|
499
|
+
// The parent turns into a toggle against `session.selection` (studio-ui-guidelines.md §8.1).
|
|
500
|
+
// Absent/false is a plain replace, which is what every canvas click was before the selection
|
|
501
|
+
// Became a set.
|
|
501
502
|
| { kind: "hit"; hit: NodeHit; additive?: boolean }
|
|
502
503
|
// A click on layout chrome (see {@link LayoutHit}). Distinct from `hit` because the target is not
|
|
503
504
|
// In the page document at all: the parent adopts it as `view.layoutSelection` (which shows the
|
|
@@ -573,7 +573,7 @@ export function syncEditableRoot(container: HTMLElement, mode: CanvasMode): void
|
|
|
573
573
|
* infer it from.
|
|
574
574
|
*
|
|
575
575
|
* Scoped deliberately: this describes the editing REGION. Per-block landmarks and a
|
|
576
|
-
* keyboard-reachable block action bar are still missing (see specs/studio.md §4.
|
|
576
|
+
* keyboard-reachable block action bar are still missing (see specs/studio.md §4.4).
|
|
577
577
|
*/
|
|
578
578
|
container.setAttribute("role", "textbox");
|
|
579
579
|
container.setAttribute("aria-multiline", "true");
|
|
@@ -693,7 +693,7 @@ export function syncStylebookCss(doc: Document, mode: CanvasMode): void {
|
|
|
693
693
|
* Apply the project's site style as a real stylesheet (replace-in-place): custom properties on
|
|
694
694
|
* `:root`, plain properties on `body`, conditional blocks dual-emitted per the forced-scheme
|
|
695
695
|
* contract. A stylesheet — not inline root properties — so `:root[data-color-scheme]` override
|
|
696
|
-
* selectors can win (spec §9.5), and so removed tokens can't linger on reused iframes.
|
|
696
|
+
* selectors can win (spec.md §9.5), and so removed tokens can't linger on reused iframes.
|
|
697
697
|
*/
|
|
698
698
|
export function applySiteStyle(
|
|
699
699
|
siteStyle: Record<string, unknown> | null | undefined,
|
|
@@ -719,7 +719,7 @@ export function applySiteStyle(
|
|
|
719
719
|
|
|
720
720
|
/**
|
|
721
721
|
* Force or clear the color-scheme preview on the iframe's root element (the platform's
|
|
722
|
-
* data-color-scheme contract, spec §9.5). Survives re-renders and patches — renders only replace
|
|
722
|
+
* data-color-scheme contract, spec.md §9.5). Survives re-renders and patches — renders only replace
|
|
723
723
|
* the container's children, never the root element.
|
|
724
724
|
*/
|
|
725
725
|
export function applyPreviewColorScheme(doc: Document, scheme: "light" | "dark" | null): void {
|
|
@@ -117,7 +117,7 @@ export function ensurePopoverRevealWatch(): () => void {
|
|
|
117
117
|
const tab = activeTab.value;
|
|
118
118
|
/* TRACKED: the selection, and the document its paths resolve against. Those are what the
|
|
119
119
|
rule depends on, and a selection move is the only thing that should fire it. The whole
|
|
120
|
-
SET is read joined, for the reason §6.
|
|
120
|
+
SET is read joined, for the reason §6.7 gives: a bare property read would not re-trigger
|
|
121
121
|
when the selection changes within the array. */
|
|
122
122
|
void tab?.session.selection.map((path) => path.join("/")).join("|");
|
|
123
123
|
void tab?.doc.document;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
/**
|
|
3
|
+
* Stage scrollbar — how wide the stage's own scrollbar is, published on the pane's cell.
|
|
4
|
+
*
|
|
5
|
+
* The zoom pod floats in the pane's chrome layer (`surfaces/pane-grid.json`, `[part="chrome"]`),
|
|
6
|
+
* and that layer spans the WHOLE lower cell, scrollbar gutter included. Edit's scroller is the
|
|
7
|
+
* stage document's `[part="edit-canvas"]`, which sits in the same cell with its native vertical
|
|
8
|
+
* scrollbar flush against the same edge. The pod was placed a fixed `--jx-space-4` (12px) in from
|
|
9
|
+
* that edge, and a classic scrollbar is 15px in Chromium on Linux, so the pod painted 3px into the
|
|
10
|
+
* track (more with its shadow) and swallowed clicks on it.
|
|
11
|
+
*
|
|
12
|
+
* **CSS cannot measure a scrollbar, and the pod's document cannot see the scroller.** The scroller
|
|
13
|
+
* belongs to `surfaces/canvas-stage.json`; the pod to `surfaces/pane-context.json`. So the
|
|
14
|
+
* renderer, which is the one module that knows which element scrolls in which mode, hands the
|
|
15
|
+
* scroller here, and this module writes its width onto the cell as {@link STAGE_SCROLLBAR_VAR}. The
|
|
16
|
+
* pod's margin adds it by cascade. It is the seam `--jump-bar-h` and `--pane-context-h` already
|
|
17
|
+
* use, run the other way: there each bar owns its answer and the stage reads it; here the stage
|
|
18
|
+
* owns its scroller and the chrome reads the answer.
|
|
19
|
+
*
|
|
20
|
+
* **On the CELL, never on `:root`**, for the reason `panels/jump-bar.ts` gives at the same seam:
|
|
21
|
+
* two panes have two stages, and one document-level number would push the other pane's pod in by a
|
|
22
|
+
* scrollbar it does not have. A stage outside any cell writes the root, which only happens in a
|
|
23
|
+
* test fixture.
|
|
24
|
+
*
|
|
25
|
+
* **Edit only, and 0 everywhere else by construction.** `canvas-render.ts` tracks the scroller from
|
|
26
|
+
* the Edit branch and releases it at every teardown (a mode transition, a full reset, the pane's
|
|
27
|
+
* own teardown), and a release writes `0px`. Design, Stylebook and a comparison clip their stage
|
|
28
|
+
* rather than scroll it, so the pod goes back to its plain inset there.
|
|
29
|
+
*
|
|
30
|
+
* The observer writes a custom property on the cell. That moves the pod, which is in the chrome
|
|
31
|
+
* layer, and never the observed scroller, so there is no resize loop.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import { PANE_SELECTOR } from "../surfaces/pane-grid";
|
|
35
|
+
import type { CanvasSurface } from "./surface-registry";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The custom property the cell carries: the stage scroller's scrollbar width, in px.
|
|
39
|
+
*
|
|
40
|
+
* `surfaces/pane-context.json`'s `[part="pod"]` margin reads it, and
|
|
41
|
+
* `tests/stage-scrollbar.test.ts` joins the two names so renaming either side goes red.
|
|
42
|
+
*/
|
|
43
|
+
export const STAGE_SCROLLBAR_VAR = "--stage-scrollbar-w";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* How much of `scroller`'s width its vertical scrollbar takes.
|
|
47
|
+
*
|
|
48
|
+
* `offsetWidth` is the border box and `clientWidth` the padding box minus the scrollbar, so the
|
|
49
|
+
* difference is exactly the scrollbar for a box that draws no border — which `[part="edit-canvas"]`
|
|
50
|
+
* does not (`surfaces/canvas-stage.json`). Give it one and this would count the border too.
|
|
51
|
+
*
|
|
52
|
+
* An OVERLAY scrollbar (macOS, GTK's overlay mode) reserves no width and correctly answers 0, so
|
|
53
|
+
* those platforms keep the pod at its plain inset. So does a DOM with no layout engine.
|
|
54
|
+
*
|
|
55
|
+
* @param {HTMLElement} scroller
|
|
56
|
+
* @returns {number}
|
|
57
|
+
*/
|
|
58
|
+
export function scrollbarWidthOf(scroller: HTMLElement): number {
|
|
59
|
+
return Math.max(0, scroller.offsetWidth - scroller.clientWidth);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** The cell a stage belongs to, or the document root when it is outside every cell. */
|
|
63
|
+
function cellOf(surface: CanvasSurface): HTMLElement {
|
|
64
|
+
return surface.wrap?.closest<HTMLElement>(PANE_SELECTOR) ?? document.documentElement;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Publish `scroller`'s scrollbar width on this stage's cell, and keep it current.
|
|
69
|
+
*
|
|
70
|
+
* **Observed, not measured once.** The scroller's content box shrinks when a scrollbar appears and
|
|
71
|
+
* grows when it goes, so one `ResizeObserver` hears the page growing past the pane and shrinking
|
|
72
|
+
* back as well as every window, dock and split resize.
|
|
73
|
+
*
|
|
74
|
+
* **Idempotent for the scroller it already holds**, because Edit's `.then` runs on every
|
|
75
|
+
* content-only repaint and the stage document keeps its scroller across them: re-observing it each
|
|
76
|
+
* time would churn an observer for nothing. A DIFFERENT scroller (the stage changed hands) releases
|
|
77
|
+
* the old one first, and `null` only releases.
|
|
78
|
+
*
|
|
79
|
+
* @param {CanvasSurface} surface
|
|
80
|
+
* @param {HTMLElement | null} scroller The element that scrolls this stage, or null for none.
|
|
81
|
+
*/
|
|
82
|
+
export function trackStageScrollbar(surface: CanvasSurface, scroller: HTMLElement | null): void {
|
|
83
|
+
if (surface.stageScrollbar?.scroller === scroller) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
releaseStageScrollbar(surface);
|
|
87
|
+
if (!scroller) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const cell = cellOf(surface);
|
|
91
|
+
const write = () => {
|
|
92
|
+
cell.style.setProperty(STAGE_SCROLLBAR_VAR, `${scrollbarWidthOf(scroller)}px`);
|
|
93
|
+
};
|
|
94
|
+
const observer = new ResizeObserver(write);
|
|
95
|
+
observer.observe(scroller);
|
|
96
|
+
surface.stageScrollbar = { observer, scroller };
|
|
97
|
+
write();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Stop observing this stage's scroller and put the cell's offset back to 0. Idempotent.
|
|
102
|
+
*
|
|
103
|
+
* Safe while the surface has no host yet, because a surface is addressable before its stage exists
|
|
104
|
+
* and the teardown paths run for it all the same.
|
|
105
|
+
*
|
|
106
|
+
* @param {CanvasSurface} surface
|
|
107
|
+
*/
|
|
108
|
+
export function releaseStageScrollbar(surface: CanvasSurface): void {
|
|
109
|
+
surface.stageScrollbar?.observer.disconnect();
|
|
110
|
+
surface.stageScrollbar = null;
|
|
111
|
+
cellOf(surface).style.setProperty(STAGE_SCROLLBAR_VAR, "0px");
|
|
112
|
+
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* The surface REGISTRY — one stage record per pane, and nothing that knows what a pane contains.
|
|
4
4
|
*
|
|
5
|
-
* A pane is the unit of split, focus and zoom (§
|
|
6
|
-
*
|
|
5
|
+
* A pane is the unit of split, focus and zoom (§18), so it is also the unit of RENDER. The panels a
|
|
6
|
+
* pass mounted, the host element they mounted into, the mode that pass was drawn in and the full
|
|
7
7
|
* render a failed patch escalates to are all facts about ONE pane; they were app-level singletons
|
|
8
8
|
* only because the shell had a single stage.
|
|
9
9
|
*
|
|
@@ -67,6 +67,12 @@ export interface CanvasSurface {
|
|
|
67
67
|
stage: CanvasStageHandle | null;
|
|
68
68
|
/** The observer that re-centres this stage until the author pans it. One per stage. */
|
|
69
69
|
centerObserver: ResizeObserver | null;
|
|
70
|
+
/**
|
|
71
|
+
* The observer publishing this stage's scroller's scrollbar width to its cell, and the scroller
|
|
72
|
+
* it watches. Edit only: it is the one mode whose stage scrolls under the zoom pod. See
|
|
73
|
+
* `canvas/stage-scrollbar.ts`.
|
|
74
|
+
*/
|
|
75
|
+
stageScrollbar: { observer: ResizeObserver; scroller: HTMLElement } | null;
|
|
70
76
|
/** Whether this stage still wants re-centring — cleared by the first deliberate pan. */
|
|
71
77
|
needsCenter: boolean;
|
|
72
78
|
panX: number;
|
|
@@ -164,6 +170,7 @@ function freshSurface(paneId: string): CanvasSurface {
|
|
|
164
170
|
prevStylebookFilter: null,
|
|
165
171
|
renderGeneration: 0,
|
|
166
172
|
sourceCollabCleanup: null,
|
|
173
|
+
stageScrollbar: null,
|
|
167
174
|
wrap: null as unknown as HTMLElement,
|
|
168
175
|
};
|
|
169
176
|
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `Collaborate:` command family — the entry point and the exit co-editing has never had
|
|
2
|
+
* The `Collaborate:` command family — the entry point and the exit co-editing has never had
|
|
3
|
+
* (collab.md §4).
|
|
3
4
|
*
|
|
4
5
|
* Collaboration in Studio has been a thing that HAPPENS to a document: a session attaches because
|
|
5
6
|
* the platform said the file was shared, presence chips appear in the toolbar, and there is no verb
|
|
6
7
|
* anywhere in the app for starting it, inviting anybody, following someone, or stopping. The
|
|
7
|
-
* palette had no `Collaborate:` rows at all, and `CATEGORIES` has carried the name since
|
|
8
|
-
* nothing filed under it.
|
|
8
|
+
* palette had no `Collaborate:` rows at all, and `CATEGORIES` has carried the name since the
|
|
9
|
+
* command registry landed with nothing filed under it.
|
|
9
10
|
*
|
|
10
|
-
* Five records, each registered here — in the module that owns the machinery,
|
|
11
|
-
* composed by `commands/app-commands.ts`. Every one is idempotent or refuses with a `requires`
|
|
11
|
+
* Five records, each registered here — in the module that owns the machinery, as §13.1 requires —
|
|
12
|
+
* and composed by `commands/app-commands.ts`. Every one is idempotent or refuses with a `requires`
|
|
12
13
|
* sentence, and none of them is a `toggle*` without a `set*` beside it: `collab.setEnabled` is the
|
|
13
14
|
* setter, `collab.stop` is the same setter's `false` and exists because "stop sharing" is what a
|
|
14
15
|
* person actually looks for in a palette.
|
|
@@ -216,7 +217,8 @@ export function collabCommands(): AnyCommand[] {
|
|
|
216
217
|
}
|
|
217
218
|
const state = collabState(active);
|
|
218
219
|
/* This is the honesty command: every one of these states used to be either invisible or
|
|
219
|
-
spelled "detached", and the freeze in particular looked exactly like a bug
|
|
220
|
+
spelled "detached", and the freeze in particular looked exactly like a bug
|
|
221
|
+
(collab.md §4). */
|
|
220
222
|
const lines = [
|
|
221
223
|
`Status: ${state.status}${state.attachError ? ` — ${state.attachError}` : ""}`,
|
|
222
224
|
`People here: ${state.peers.length === 0 ? "just you" : state.peers.map((p) => p.state.user.login).join(", ")}`,
|