@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
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* scheme layer, nested selector)`. It has always computed that tuple — it is the per-field key and
|
|
13
13
|
* the five-branch if/else below — and it used to hide it behind three disconnected widgets on two
|
|
14
14
|
* different bars. `target-line.ts` renders it as one sentence instead, and the three widgets are
|
|
15
|
-
* gone: the breakpoint and scheme axes are selected on the pane context bar
|
|
16
|
-
*
|
|
15
|
+
* gone: the breakpoint and scheme axes are selected on the pane context bar, and the selector is
|
|
16
|
+
* the one axis this tab owns.
|
|
17
17
|
*
|
|
18
18
|
* Every row then says where its value came from. `provenance.ts` supplies the vocabulary;
|
|
19
19
|
* `computeInheritedSources()` supplies the donor breakpoint the cascade walk always knew and this
|
|
@@ -28,17 +28,18 @@
|
|
|
28
28
|
* bound property, and an equal write is skipped. `renderOnly("rightPanel")` is gone from this file
|
|
29
29
|
* with it.
|
|
30
30
|
*
|
|
31
|
-
* **Two controls are lists, and the list is the KIT MENU** (`surfaces/menu.ts`,
|
|
32
|
-
* picker and the keyword suggestions were each an
|
|
33
|
-
* Source picker was a third. All three are
|
|
34
|
-
* typeahead and Escape arrive here without a line
|
|
31
|
+
* **Two controls are lists, and the list is the KIT MENU** (`surfaces/menu.ts`,
|
|
32
|
+
* `studio-ui-guidelines.md` §12.5). The unit picker and the keyword suggestions were each an
|
|
33
|
+
* `sp-overlay` + `sp-menu` of their own; the Value Source picker was a third. All three are
|
|
34
|
+
* `openMenu()` now, which is also how roving focus, typeahead and Escape arrive here without a line
|
|
35
|
+
* of keyboard code.
|
|
35
36
|
*
|
|
36
37
|
* **Nothing here is an island any more.** One control was: `specs/ui.md` §5.6 (Colour) was Pending,
|
|
37
38
|
* so a colour row drew an announced `[part="control-host"]` and a lit `paintColorControl` filled
|
|
38
|
-
* it, and that was the last Spectrum on this surface. §5.6 landed. The row is a
|
|
39
|
-
* the document beside every other control, its palette is a `jx-swatch-group`
|
|
40
|
-
* picker, and `ui/color-selector.ts` is what remains: which colours THIS PROJECT
|
|
41
|
-
* is a question no kit element can answer.
|
|
39
|
+
* it, and that was the last Spectrum on this surface. `ui.md` §5.6 landed. The row is a
|
|
40
|
+
* `jx-color-field` in the document beside every other control, its palette is a `jx-swatch-group`
|
|
41
|
+
* slotted into the picker, and `ui/color-selector.ts` is what remains: which colours THIS PROJECT
|
|
42
|
+
* has named, which is a question no kit element can answer.
|
|
42
43
|
*
|
|
43
44
|
* @docs studio/design/style-inspector
|
|
44
45
|
*/
|
|
@@ -68,7 +69,6 @@ import {
|
|
|
68
69
|
transactDoc,
|
|
69
70
|
} from "../tabs/transact";
|
|
70
71
|
import {
|
|
71
|
-
abbreviateValue,
|
|
72
72
|
friendlyNameToVar,
|
|
73
73
|
inferInputType,
|
|
74
74
|
kebabToLabel,
|
|
@@ -337,7 +337,7 @@ export function resetAffectedDisclosure(): void {
|
|
|
337
337
|
*
|
|
338
338
|
* The breakpoint and the colour scheme are DEFINED in Project Settings › Contexts and SELECTED on
|
|
339
339
|
* the pane context bar. A Target Line segment that opened its own list would be the third selector
|
|
340
|
-
* §6.
|
|
340
|
+
* §6.2 forbids, so it routes to the definition site through the same command the context bar's
|
|
341
341
|
* "Manage contexts…" footer runs.
|
|
342
342
|
*/
|
|
343
343
|
function runCommand(id: string, args?: Record<string, unknown>): void {
|
|
@@ -543,7 +543,7 @@ interface ProvenanceCtx {
|
|
|
543
543
|
signals: string[];
|
|
544
544
|
clear: (prop: string) => void;
|
|
545
545
|
/**
|
|
546
|
-
* Properties the selected elements DISAGREE about, and how many elements are selected (§6.
|
|
546
|
+
* Properties the selected elements DISAGREE about, and how many elements are selected (§6.7).
|
|
547
547
|
*
|
|
548
548
|
* Empty for a selection of one — which is what keeps every other branch of `provenanceOf`
|
|
549
549
|
* reachable exactly as often as it was before the selection became a set.
|
|
@@ -727,7 +727,7 @@ interface RowActions {
|
|
|
727
727
|
choices?: StyleChoice[];
|
|
728
728
|
choicesLabel?: string;
|
|
729
729
|
choose?: (value: string) => void;
|
|
730
|
-
/** The Value Source ladder for this position (§6.
|
|
730
|
+
/** The Value Source ladder for this position (§6.6). */
|
|
731
731
|
source?: {
|
|
732
732
|
mode: SlotMode;
|
|
733
733
|
caps: SlotMode[];
|
|
@@ -771,27 +771,30 @@ function isNumericValue(value: string): boolean {
|
|
|
771
771
|
}
|
|
772
772
|
|
|
773
773
|
/**
|
|
774
|
-
*
|
|
774
|
+
* The buttons a button-group row draws, and the overflow list it hands to the kit menu.
|
|
775
775
|
*
|
|
776
|
-
*
|
|
777
|
-
*
|
|
778
|
-
*
|
|
779
|
-
*
|
|
780
|
-
*
|
|
781
|
-
*
|
|
776
|
+
* A value's glyph is its css-meta `$icons` entry VERBATIM: those values are kit manifest names
|
|
777
|
+
* (ui.md §8, one key space), so there is nothing to translate. A value with no entry — `nowrap`,
|
|
778
|
+
* `wrap-reverse`, `normal`, `auto` — draws its own CSS KEYWORD instead, in full and in lower case,
|
|
779
|
+
* and its button is the only kind that carries any text.
|
|
780
|
+
*
|
|
781
|
+
* The keyword is written out rather than abbreviated. It used to go through an `abbreviateValue`
|
|
782
|
+
* table in `utils/studio-utils.ts`, and four segments of a row that is otherwise pictures then read
|
|
783
|
+
* `no-wr`, `wr-rev`, `norm` and `auto` — a reader cannot decode three of them. `wr-rev` is as
|
|
784
|
+
* opaque as the glyph-less Display row this pass set out to fix, and it sat beside a wrap arrow
|
|
785
|
+
* that a reader COULD decode, so the row taught them the buttons were unreadable. A word takes
|
|
786
|
+
* about 30px more than a glyph; the group's text segments pay 6px of inline padding for it
|
|
787
|
+
* (`jx-action-group`), and no row draws more than two. That table had no other caller, so it went
|
|
788
|
+
* with the abbreviations rather than staying behind as a spelling nothing spells.
|
|
789
|
+
*
|
|
790
|
+
* There used to be a table here from css-meta's own semantic names (`display-flex`,
|
|
791
|
+
* `justify-start`) to the eight of them the kit happened to ship verbatim. It was a second spelling
|
|
792
|
+
* of glyph names beside the manifest, it had drifted from the translations the kit had already
|
|
793
|
+
* reasoned out, and every name it lacked resolved to `""` in silence, which is how the whole
|
|
794
|
+
* Display row shipped as abbreviations. So there is no runtime `hasIcon` guard either: a name the
|
|
795
|
+
* manifest lacks is a red `tests/metadata.test.ts`, which is where a miss is caught before it ships
|
|
796
|
+
* rather than degraded after.
|
|
782
797
|
*/
|
|
783
|
-
const BUTTON_GLYPHS: Readonly<Record<string, string>> = {
|
|
784
|
-
"arrow-down": "arrow-down",
|
|
785
|
-
"arrow-left": "arrow-left",
|
|
786
|
-
"arrow-right": "arrow-right",
|
|
787
|
-
"arrow-up": "arrow-up",
|
|
788
|
-
"text-align-center": "text-align-center",
|
|
789
|
-
"text-align-justify": "text-align-justify",
|
|
790
|
-
"text-align-left": "text-align-left",
|
|
791
|
-
"text-align-right": "text-align-right",
|
|
792
|
-
};
|
|
793
|
-
|
|
794
|
-
/** The buttons a button-group row draws, and the overflow list it hands to the kit menu. */
|
|
795
798
|
function buttonGroup(
|
|
796
799
|
entry: CssPropertyEntry,
|
|
797
800
|
rowKey: string,
|
|
@@ -806,12 +809,12 @@ function buttonGroup(
|
|
|
806
809
|
? enumValues.filter((v) => !buttonValues.includes(v))
|
|
807
810
|
: [];
|
|
808
811
|
const buttons = values.map((v) => {
|
|
809
|
-
const icon =
|
|
812
|
+
const icon = iconMap[v] ?? "";
|
|
810
813
|
return {
|
|
811
814
|
icon,
|
|
812
815
|
row: rowKey,
|
|
813
816
|
selected: v === value,
|
|
814
|
-
text: icon === "" ?
|
|
817
|
+
text: icon === "" ? v : "",
|
|
815
818
|
title: v,
|
|
816
819
|
value: v,
|
|
817
820
|
};
|
|
@@ -1060,7 +1063,7 @@ function fieldRow(
|
|
|
1060
1063
|
row.placeholder = placeholder;
|
|
1061
1064
|
applyChip(row, actions, opts.provenance);
|
|
1062
1065
|
|
|
1063
|
-
// The Value Source ladder (§6.
|
|
1066
|
+
// The Value Source ladder (§6.6), and only where the position has one to offer. A longhand child
|
|
1064
1067
|
// Is not a document position of its own — its value is a slice of the shorthand above it — so it
|
|
1065
1068
|
// Carries no chip, which is what it has always done.
|
|
1066
1069
|
if (!opts.child) {
|
|
@@ -1395,7 +1398,7 @@ function buildView(canvasMode: () => string): StylePanelView {
|
|
|
1395
1398
|
return emptyView("Open a page to style what you click.", true);
|
|
1396
1399
|
}
|
|
1397
1400
|
// No separate `Styling: <h1>` header. It said the same thing the Target Line's scope chip says,
|
|
1398
|
-
// Except that it said it as a caption, after the fact, and without the blast radius (§6.
|
|
1401
|
+
// Except that it said it as a caption, after the fact, and without the blast radius (§6.2).
|
|
1399
1402
|
return buildEditor(tab, node, {
|
|
1400
1403
|
effectiveStyle: getEffectiveStyle(node.style),
|
|
1401
1404
|
stylebookSelector: shell.stylebook.selection,
|
|
@@ -1445,7 +1448,7 @@ function buildEditor(
|
|
|
1445
1448
|
const mediaTab = tab.session.ui.activeMedia || null;
|
|
1446
1449
|
const { activeSelector } = tab.session.ui;
|
|
1447
1450
|
|
|
1448
|
-
// ── Scheme-layer routing (spec §9.5)
|
|
1451
|
+
// ── Scheme-layer routing (spec.md §9.5) ─────────────────────────────────────
|
|
1449
1452
|
// With the tab-bar scheme control forcing a scheme that has a matching declared scheme query,
|
|
1450
1453
|
// Base-context edits target that scheme's `@--name` block — no extra sidebar tabs. Breakpoint
|
|
1451
1454
|
// Tabs stay breakpoint-scoped (scheme × breakpoint compound blocks are unsupported).
|
|
@@ -1476,7 +1479,7 @@ function buildEditor(
|
|
|
1476
1479
|
]),
|
|
1477
1480
|
];
|
|
1478
1481
|
|
|
1479
|
-
// ── The Target Line (§6.
|
|
1482
|
+
// ── The Target Line (§6.2) ─────────────────────────────────────────────────
|
|
1480
1483
|
const stylebookTag = stylebookTagOf(stylebookSelector);
|
|
1481
1484
|
const elementLabel =
|
|
1482
1485
|
stylebookTag ?? (typeof node.tagName === "string" ? node.tagName : "element");
|
|
@@ -1510,7 +1513,8 @@ function buildEditor(
|
|
|
1510
1513
|
},
|
|
1511
1514
|
onAddCustom: () => {
|
|
1512
1515
|
// One flow, through `ui/layers.ts` — the imperative `<input>` this replaces was appended to
|
|
1513
|
-
// A bar lit-html owns, bypassing both the renderer and the overlay stack (§
|
|
1516
|
+
// A bar lit-html owns, bypassing both the renderer and the overlay stack (§6.2, "Nested
|
|
1517
|
+
// Selector Context").
|
|
1514
1518
|
void showPromptDialog("Add Selector", {
|
|
1515
1519
|
confirmLabel: "Use",
|
|
1516
1520
|
message: "A state or nested rule to edit under this element.",
|
|
@@ -1537,7 +1541,7 @@ function buildEditor(
|
|
|
1537
1541
|
style === ownRootStyle
|
|
1538
1542
|
? activeStyle
|
|
1539
1543
|
: resolveContextStyle(ownRootStyle, activeSelector, editMedia);
|
|
1540
|
-
// ── The selection, and what it means to commit to it (§6.
|
|
1544
|
+
// ── The selection, and what it means to commit to it (§6.7) ────────────────
|
|
1541
1545
|
// Every selected element gets the same write, inside ONE `transactDoc` — so setting padding on
|
|
1542
1546
|
// Six cards is one decision and one undo step. With one element selected this loop runs once
|
|
1543
1547
|
// Against `sel` and is indistinguishable from the single-target commit it replaces.
|
package/src/panels/tab-strip.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Tab strip — one strip PER PANE, above each pane's editor.
|
|
4
4
|
*
|
|
5
|
-
* A tab belongs to a pane and the pane is what splits (§4
|
|
5
|
+
* A tab belongs to a pane and the pane is what splits (§14.4), so the strip is a rendering of
|
|
6
6
|
* `Pane.tabOrder` / `Pane.activeTabId`, not of a workspace-wide list. The host for each pane is
|
|
7
7
|
* addressed by REGION id — `pane.primary/tabs`, `pane.secondary/tabs` — rather than by element id,
|
|
8
8
|
* so the shell can move or rename the divs without touching this file.
|
|
@@ -185,7 +185,8 @@ export function mount(host: HTMLElement) {
|
|
|
185
185
|
source.data.type === "tab"
|
|
186
186
|
? { kind: "tab", tabId: source.data.tabId as string }
|
|
187
187
|
: { kind: "file", path: source.data.path as string };
|
|
188
|
-
// The tail target does not exist until a pane with no tabs is asked to grow one (§
|
|
188
|
+
// The tail target does not exist until a pane with no tabs is asked to grow one (§14.4's
|
|
189
|
+
// "Between panes" row; `project` draws it).
|
|
189
190
|
render();
|
|
190
191
|
// `render()` alone does not write `dragKey` — only `syncDrag()` does — so without this the
|
|
191
192
|
// Source chip's `data-dragging` mark would not appear until the pointer's first move.
|
|
@@ -566,8 +567,8 @@ function project(pane: Pane): TabStripValues {
|
|
|
566
567
|
tabs: pane.tabOrder.flatMap((id, index) => chipView(pane, id, index, labels)),
|
|
567
568
|
trailing: _overflowing.get(pane.id) === true,
|
|
568
569
|
trailingGlyph: "⌄",
|
|
569
|
-
// ONE accessible name (§10): the glyph is hidden, so `title` is both
|
|
570
|
-
// — `title` + a matching `aria-label` would announce it twice.
|
|
570
|
+
// ONE accessible name (`studio-ui-guidelines.md` §10): the glyph is hidden, so `title` is both
|
|
571
|
+
// The tooltip and the name — `title` + a matching `aria-label` would announce it twice.
|
|
571
572
|
trailingTitle: "Show hidden tabs",
|
|
572
573
|
};
|
|
573
574
|
}
|
|
@@ -594,9 +595,10 @@ function chipView(
|
|
|
594
595
|
if (!tab) {
|
|
595
596
|
return [];
|
|
596
597
|
}
|
|
597
|
-
/* The draft pill (§7.6). On the CHIP, not only inside the entry editor:
|
|
598
|
-
prevents — publishing something you believed was private — is made while
|
|
599
|
-
tabs, and it is made about a document that may not even be the active
|
|
598
|
+
/* The draft pill (`site-architecture.md` §7.6). On the CHIP, not only inside the entry editor:
|
|
599
|
+
the mistake this prevents — publishing something you believed was private — is made while
|
|
600
|
+
glancing at a row of tabs, and it is made about a document that may not even be the active
|
|
601
|
+
one. */
|
|
600
602
|
const pill = entryDraftPill(tab);
|
|
601
603
|
return [
|
|
602
604
|
{
|
|
@@ -678,8 +680,9 @@ function adoptChip(host: HTMLElement, element: HTMLElement, id: string): void {
|
|
|
678
680
|
|
|
679
681
|
/**
|
|
680
682
|
* Make a strip's OWN element a drop target — the empty tail after the last chip, and, for an empty
|
|
681
|
-
* pane growing one during a drag (§
|
|
682
|
-
* answers first; this only ever resolves a drop that landed on the strip
|
|
683
|
+
* pane growing one during a drag (§14.4, "Between panes"), the whole of it. Innermost wins, so a
|
|
684
|
+
* chip under the pointer answers first; this only ever resolves a drop that landed on the strip
|
|
685
|
+
* itself.
|
|
683
686
|
*/
|
|
684
687
|
function adoptStrip(host: HTMLElement, element: HTMLElement): void {
|
|
685
688
|
_stripDnd.get(host)?.();
|
|
@@ -848,11 +851,11 @@ export function dismissOverflowMenu() {
|
|
|
848
851
|
* List the off-screen tabs. Falls back to every tab when nothing measures as hidden, because an
|
|
849
852
|
* empty menu is a dead control and happy-dom (plus any zero-height layout) measures everything at 0.
|
|
850
853
|
*
|
|
851
|
-
* **The kit menu, not a popover of this strip's own** (guidelines §8.4).
|
|
852
|
-
* the panel, the roving caret, the typeahead, Escape and the light
|
|
853
|
-
* panel into the viewport once it has been laid out — so the
|
|
854
|
-
* to sit in a style attribute here is one `place` callback,
|
|
855
|
-
* instead of guessed from the chevron's.
|
|
854
|
+
* **The kit menu, not a popover of this strip's own** (`studio-ui-guidelines.md` §8.4).
|
|
855
|
+
* `surfaces/menu.ts` owns the panel, the roving caret, the typeahead, Escape and the light
|
|
856
|
+
* dismissal, and it clamps the panel into the viewport once it has been laid out — so the
|
|
857
|
+
* `right:`/`top:` arithmetic that used to sit in a style attribute here is one `place` callback,
|
|
858
|
+
* measured against the panel's real box instead of guessed from the chevron's.
|
|
856
859
|
*
|
|
857
860
|
* A row states whether it is the tab the pane is already showing. The strip's own chips carry that
|
|
858
861
|
* as `aria-selected`, and a menu row cannot: `checked` on every row is the shape this shell already
|
|
@@ -1038,12 +1041,12 @@ function placedTabItems(tab: Tab): MenuRowProjection[] {
|
|
|
1038
1041
|
* beside the gesture that needs them (`surfaces/tab-strip.json`), so what arrives here is the tab
|
|
1039
1042
|
* and the point — which is everything a menu is a function of.
|
|
1040
1043
|
*
|
|
1041
|
-
* **The rows go to the kit menu** (`surfaces/menu.ts`, guidelines §12.5), which is
|
|
1042
|
-
* row template in this file any more. Three things that were written here went with
|
|
1043
|
-
* that kept a right-click near the screen edge readable — `onMenuToggle` clamps every
|
|
1044
|
-
* has been laid out, against the panel's real box rather than a guessed one — the
|
|
1045
|
-
* disabled row prints, and the refusal to dismiss when a disabled row is clicked,
|
|
1046
|
-
* `jx-menu-item`'s own `onClick` guard.
|
|
1044
|
+
* **The rows go to the kit menu** (`surfaces/menu.ts`, `studio-ui-guidelines.md` §12.5), which is
|
|
1045
|
+
* why there is no row template in this file any more. Three things that were written here went with
|
|
1046
|
+
* it: the clamp that kept a right-click near the screen edge readable — `onMenuToggle` clamps every
|
|
1047
|
+
* panel once it has been laid out, against the panel's real box rather than a guessed one — the
|
|
1048
|
+
* `Needs …` line a disabled row prints, and the refusal to dismiss when a disabled row is clicked,
|
|
1049
|
+
* which is `jx-menu-item`'s own `onClick` guard.
|
|
1047
1050
|
*/
|
|
1048
1051
|
function openTabContextMenu(tab: Tab, clientX: number, clientY: number) {
|
|
1049
1052
|
dismissTabContextMenu();
|
|
@@ -1340,9 +1343,9 @@ async function confirmClose(tab: Tab): Promise<boolean> {
|
|
|
1340
1343
|
*
|
|
1341
1344
|
* **Three ways out, not two.** This asked "Close without saving?" over a two-way confirm, so the
|
|
1342
1345
|
* only buttons were the one that threw the work away and the one that did nothing — a dialog that
|
|
1343
|
-
* cannot do the thing the user most likely wants. §8.7's table assigns
|
|
1344
|
-
* `showSaveDiscardDialog`, and this is that decision — except when a
|
|
1345
|
-
* out at all, which is {@link confirmClose}'s job to tell apart.
|
|
1346
|
+
* cannot do the thing the user most likely wants. `studio-ui-guidelines.md` §8.7's table assigns
|
|
1347
|
+
* "Unsaved-work decisions" to `showSaveDiscardDialog`, and this is that decision — except when a
|
|
1348
|
+
* save is not one of the ways out at all, which is {@link confirmClose}'s job to tell apart.
|
|
1346
1349
|
*
|
|
1347
1350
|
* **A failed save must not close the tab.** `saveFile` reports its own failures and returns whether
|
|
1348
1351
|
* the bytes landed; treating "Save" as "save and then close regardless" would turn a write error
|
package/src/platform-errors.ts
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* **RFC 9457 reconciles cleanly with this, because a problem's `type` IS the code.** A backend
|
|
9
9
|
* answering `type: "https://jxsuite.com/problems/needs-installation-access"` is saying exactly what
|
|
10
10
|
* `code: "needs_installation_access"` said; `problemSlug` derives the one from the other, and
|
|
11
|
-
* `installUrl` is the extension member (§3.2) that type documents. So this module reads
|
|
12
|
-
* every surface above it keeps branching on `code` without knowing which shape arrived.
|
|
11
|
+
* `installUrl` is the extension member (RFC 9457 §3.2) that type documents. So this module reads
|
|
12
|
+
* both, and every surface above it keeps branching on `code` without knowing which shape arrived.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { problemSlug } from "@jxsuite/protocol";
|
package/src/platform.ts
CHANGED
|
@@ -13,20 +13,60 @@
|
|
|
13
13
|
*
|
|
14
14
|
* **Every call is counted.** `getPlatform()` hands out a counting proxy, not the raw adapter, so
|
|
15
15
|
* "is Studio waiting on I/O?" is one number rather than thirty hand-instrumented methods
|
|
16
|
-
* (`probe.idle()` condition
|
|
16
|
+
* (`probe.idle()`'s in-flight I/O condition, §13.5). Wrapping at the READ point rather than at
|
|
17
17
|
* `registerPlatform` is deliberate: desktop pre-registers its adapter on `window.__jxPlatform`
|
|
18
18
|
* without ever calling the registrar, and this is the seam both paths share.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import type { StudioPlatform } from "./types";
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
/** What a launcher's boot module recorded when its adapter could not be constructed. */
|
|
24
|
+
export interface LauncherBootError {
|
|
25
|
+
message: string;
|
|
26
|
+
stack?: string | undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A launcher's announcement that it owns this window's adapter (spec/desktop.md §3.3).
|
|
31
|
+
*
|
|
32
|
+
* Published by the launcher boot module's FIRST import, before any other module in its bundle
|
|
33
|
+
* evaluates, so it exists even when a later import throws. Present with no adapter registered, it
|
|
34
|
+
* means the launcher failed, and Studio must say so rather than fall back to the dev server. That
|
|
35
|
+
* fallback is what desktop 5.0.0-5.1.3 did for months: its init bundle threw on import, and every
|
|
36
|
+
* window ran the dev-server adapter against a `views://` origin with nothing behind it.
|
|
37
|
+
*/
|
|
38
|
+
export interface LauncherSignal {
|
|
39
|
+
/** Which launcher announced (`"electrobun"`, `"chromium"`), once its boot module's body ran. */
|
|
40
|
+
launcher?: string | undefined;
|
|
41
|
+
/** The first error the page raised during boot, or the adapter factory's own throw. */
|
|
42
|
+
error?: LauncherBootError | undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const g = globalThis as unknown as {
|
|
46
|
+
__jxPlatform?: StudioPlatform;
|
|
47
|
+
__jxLauncher?: LauncherSignal;
|
|
48
|
+
};
|
|
24
49
|
|
|
25
50
|
/** @param {StudioPlatform} platform */
|
|
26
51
|
export function registerPlatform(platform: StudioPlatform) {
|
|
27
52
|
g.__jxPlatform = platform;
|
|
28
53
|
}
|
|
29
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Publish the launcher signal on `globalThis.__jxLauncher`, or return the one already published.
|
|
57
|
+
*
|
|
58
|
+
* Idempotent, so an error recorded by the first announcer survives a second call.
|
|
59
|
+
*/
|
|
60
|
+
export function announceLauncher(): LauncherSignal {
|
|
61
|
+
g.__jxLauncher ??= {};
|
|
62
|
+
return g.__jxLauncher;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The launcher signal, when a launcher's boot module announced itself in this window. */
|
|
66
|
+
export function launcherSignal(): LauncherSignal | undefined {
|
|
67
|
+
return g.__jxLauncher;
|
|
68
|
+
}
|
|
69
|
+
|
|
30
70
|
// ─── In-flight accounting ─────────────────────────────────────────────────────
|
|
31
71
|
|
|
32
72
|
/** Method name → how many of its calls have not settled. Entries are deleted at zero. */
|
|
@@ -36,8 +76,8 @@ const inFlight = new Map<string, number>();
|
|
|
36
76
|
* One entry per platform call whose promise has not settled, named by its method.
|
|
37
77
|
*
|
|
38
78
|
* Repeats are meaningful: two concurrent `gitStatus` reads are two reasons the shell is not
|
|
39
|
-
* settled, and the rejection message that names them is the whole point of
|
|
40
|
-
* 115 places that could not fail.
|
|
79
|
+
* settled, and the rejection message that names them is the whole point of "Determinism" in
|
|
80
|
+
* `scripts/screenshots/README.md` — 115 sleeps were 115 places that could not fail.
|
|
41
81
|
*/
|
|
42
82
|
export function platformInFlight(): string[] {
|
|
43
83
|
const names: string[] = [];
|
package/src/platforms/cloud.ts
CHANGED
|
@@ -496,6 +496,16 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
496
496
|
return data.content;
|
|
497
497
|
},
|
|
498
498
|
|
|
499
|
+
/* Raw bytes. The session origin serves this route, so no CORS question arises — the shell and
|
|
500
|
+
the API are the same origin on cloud, which is exactly what `assetSpace: "repo"` is about. */
|
|
501
|
+
async readFileBytes(path: string) {
|
|
502
|
+
const res = await api(`/file/bytes?path=${encodeURIComponent(path)}`);
|
|
503
|
+
if (!res.ok) {
|
|
504
|
+
throw new Error(await errorMessage(res, `Failed to read file bytes: ${path}`));
|
|
505
|
+
}
|
|
506
|
+
return res.arrayBuffer();
|
|
507
|
+
},
|
|
508
|
+
|
|
499
509
|
async writeFile(path: string, content: string) {
|
|
500
510
|
const res = await api("/file", {
|
|
501
511
|
method: "PUT",
|
|
@@ -724,7 +734,7 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
724
734
|
*
|
|
725
735
|
* Degrades to `{}` rather than throwing: a backend too old to serve the route, or a project
|
|
726
736
|
* whose extensions the session cannot compose, keeps the bundled core schemas. That fallback
|
|
727
|
-
* under-suggests extension extras but never reports false errors (§5.3).
|
|
737
|
+
* under-suggests extension extras but never reports false errors (extensions.md §5.3).
|
|
728
738
|
*/
|
|
729
739
|
async fetchProjectSchemas(): Promise<ProjectSchemasResponse> {
|
|
730
740
|
try {
|
|
@@ -1,15 +1,33 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
/**
|
|
3
|
-
* Chooses the default PAL adapter when no platform was pre-registered on `window.__jxPlatform
|
|
3
|
+
* Chooses the default PAL adapter when no platform was pre-registered on `window.__jxPlatform` — or
|
|
4
|
+
* refuses to, when a launcher was supposed to have registered one.
|
|
4
5
|
*
|
|
5
6
|
* The cloud shell (the platform repo's `web/edit-init.ts`) publishes a `window.__jxCloud` signal
|
|
6
7
|
* _instead of_ constructing the adapter itself, so the cloud adapter — and its collab WebSocket
|
|
7
8
|
* client, which owns the shared `Y.Doc` — is created HERE, inside the studio bundle, sharing
|
|
8
9
|
* studio's single `yjs` instance. Creating it in the shell bundle loads a SECOND `yjs`, which
|
|
9
10
|
* breaks collab's cross-module `instanceof` checks (the Y↔JxDocOp conversion silently stops
|
|
10
|
-
* reconciling). The dev server sends no signal and
|
|
11
|
-
*
|
|
11
|
+
* reconciling). The dev server sends no signal and gets the dev-server adapter.
|
|
12
|
+
*
|
|
13
|
+
* **This resolver DOES run on desktop — whenever desktop failed.** A launcher registers its own
|
|
14
|
+
* adapter on `__jxPlatform` from a boot module that evaluates before this bundle, and this header
|
|
15
|
+
* used to conclude that the resolver never ran there. That held only while the boot module worked.
|
|
16
|
+
* Desktop 5.0.0 to 5.1.3 shipped an init bundle that threw on import, nothing registered, and this
|
|
17
|
+
* resolver answered with the dev-server adapter inside a `views://` window: every panel drew,
|
|
18
|
+
* "Create Project" said "Failed to fetch", and Browse did nothing at all. A half-working app is the
|
|
19
|
+
* worst failure a launcher can have, because it reads as a bug in whatever the user clicked last.
|
|
20
|
+
*
|
|
21
|
+
* So the fallback is now conditional on there being nobody it could be standing in for.
|
|
22
|
+
* {@link bootRefusal} is that decision, and {@link resolveDefaultPlatform} throws
|
|
23
|
+
* {@link PlatformUnavailableError} rather than guess; `studio.ts` catches it and draws the
|
|
24
|
+
* boot-failure screen (`surfaces/boot-failure.ts`) in place of the frame.
|
|
25
|
+
*
|
|
26
|
+
* @docs extending/embedding/platform-adapter
|
|
12
27
|
*/
|
|
28
|
+
import { BOOT_META } from "../hosting/document";
|
|
29
|
+
import { launcherSignal } from "../platform";
|
|
30
|
+
import type { LauncherSignal } from "../platform";
|
|
13
31
|
import type { StudioPlatform } from "../types";
|
|
14
32
|
import type { CloudProject } from "./cloud";
|
|
15
33
|
import { createCloudPlatform } from "./cloud";
|
|
@@ -20,8 +38,127 @@ export interface CloudSignal {
|
|
|
20
38
|
project: CloudProject | null;
|
|
21
39
|
}
|
|
22
40
|
|
|
23
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Why Studio will not fall back to the dev-server adapter, most specific evidence first.
|
|
43
|
+
*
|
|
44
|
+
* - `launcher` — a launcher's boot module announced itself (`globalThis.__jxLauncher`) and still no
|
|
45
|
+
* adapter was registered. The strongest evidence there is: the launcher ran, failed, and may have
|
|
46
|
+
* recorded why. The error is deliberately NOT copied here — the failure view reads
|
|
47
|
+
* `launcherSignal()?.error` when it renders, so an error recorded after this was decided still
|
|
48
|
+
* reaches the screen.
|
|
49
|
+
* - `declared` — the document carries {@link BOOT_META}, so its host declared a boot module, and
|
|
50
|
+
* nothing announced. The boot script never ran: a 404, a syntax error, a blocked load.
|
|
51
|
+
* - `scheme` — neither, but the page was opened from `views:` or `file:`. The dev-server adapter is
|
|
52
|
+
* relative `fetch`, `EventSource` and a WebSocket against the page's own origin, and those two
|
|
53
|
+
* schemes have no HTTP server behind them by construction. This covers a staged `index.html` old
|
|
54
|
+
* enough to predate the meta.
|
|
55
|
+
*/
|
|
56
|
+
export type BootRefusal =
|
|
57
|
+
| { kind: "launcher"; launcher: string | undefined }
|
|
58
|
+
| { kind: "declared" }
|
|
59
|
+
| { kind: "scheme"; protocol: string };
|
|
60
|
+
|
|
61
|
+
/** The four facts {@link bootRefusal} decides from, read once so the rule itself stays pure. */
|
|
62
|
+
export interface BootEnvironment {
|
|
63
|
+
/** The launcher signal, when a launcher's boot module announced itself. */
|
|
64
|
+
launcher: LauncherSignal | undefined;
|
|
65
|
+
/** Whether the cloud shell published its `__jxCloud` signal. */
|
|
66
|
+
cloud: boolean;
|
|
67
|
+
/** Whether the document carries the {@link BOOT_META} marker. */
|
|
68
|
+
declared: boolean;
|
|
69
|
+
/** `location.protocol`, colon included. */
|
|
70
|
+
protocol: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Schemes the dev-server adapter can never work from.
|
|
75
|
+
*
|
|
76
|
+
* ONLY these two, and the list is short on purpose. The embedding docs promise that a host serving
|
|
77
|
+
* the HTTP protocol reuses the stock dev-server adapter unchanged, and a third-party shell may well
|
|
78
|
+
* serve it through a custom scheme — `tauri://localhost` proxies to a real server. Refusing every
|
|
79
|
+
* scheme that is not `http:` would break that host to cover a case the meta and the launcher signal
|
|
80
|
+
* already cover for every host this repository ships. `views:` is Electrobun's own bundle scheme
|
|
81
|
+
* and `file:` is a document opened from disk; neither can be proxying anything. happy-dom's
|
|
82
|
+
* `about:` is absent for the same reason, which is also what lets a test import the entry without
|
|
83
|
+
* inventing a URL.
|
|
84
|
+
*/
|
|
85
|
+
const BACKENDLESS_SCHEMES: ReadonlySet<string> = new Set(["views:", "file:"]);
|
|
86
|
+
|
|
87
|
+
/** Read the boot environment from the live globals and document. */
|
|
88
|
+
export function bootEnvironment(): BootEnvironment {
|
|
89
|
+
const doc = (globalThis as { document?: Document }).document;
|
|
90
|
+
const meta = `meta[name="${BOOT_META.name}"][content="${BOOT_META.content}"]`;
|
|
91
|
+
return {
|
|
92
|
+
cloud: (globalThis as unknown as { __jxCloud?: CloudSignal }).__jxCloud != null,
|
|
93
|
+
declared: doc?.querySelector(meta) != null,
|
|
94
|
+
launcher: launcherSignal(),
|
|
95
|
+
protocol: (globalThis as { location?: Location }).location?.protocol ?? "",
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Whether the dev-server fallback must be refused, and why — `null` when it may run.
|
|
101
|
+
*
|
|
102
|
+
* The order is the argument. A launcher signal beats the cloud signal because it is direct evidence
|
|
103
|
+
* of a launcher that failed, and a window is not both. The cloud signal beats the meta because the
|
|
104
|
+
* cloud shell declares a boot module too — `edit-init.js` IS its boot module — and publishing the
|
|
105
|
+
* signal is that module succeeding.
|
|
106
|
+
*
|
|
107
|
+
* @param env The environment to judge; the live one by default.
|
|
108
|
+
*/
|
|
109
|
+
export function bootRefusal(env: BootEnvironment = bootEnvironment()): BootRefusal | null {
|
|
110
|
+
if (env.launcher) {
|
|
111
|
+
return { kind: "launcher", launcher: env.launcher.launcher };
|
|
112
|
+
}
|
|
113
|
+
if (env.cloud) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
if (env.declared) {
|
|
117
|
+
return { kind: "declared" };
|
|
118
|
+
}
|
|
119
|
+
if (BACKENDLESS_SCHEMES.has(env.protocol)) {
|
|
120
|
+
return { kind: "scheme", protocol: env.protocol };
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** The console's one-line account of a refusal; the screen says it at more length. */
|
|
126
|
+
function refusalMessage(refusal: BootRefusal): string {
|
|
127
|
+
switch (refusal.kind) {
|
|
128
|
+
case "launcher": {
|
|
129
|
+
return `Jx Studio's launcher (${refusal.launcher ?? "unnamed"}) announced itself but registered no platform adapter.`;
|
|
130
|
+
}
|
|
131
|
+
case "declared": {
|
|
132
|
+
return "Jx Studio's page declares a boot module, but nothing registered a platform adapter.";
|
|
133
|
+
}
|
|
134
|
+
default: {
|
|
135
|
+
return `Jx Studio was opened from a ${refusal.protocol} URL, which has no backend for the dev-server adapter.`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Thrown by {@link resolveDefaultPlatform} when {@link bootRefusal} refuses the fallback. */
|
|
141
|
+
export class PlatformUnavailableError extends Error {
|
|
142
|
+
readonly refusal: BootRefusal;
|
|
143
|
+
|
|
144
|
+
constructor(refusal: BootRefusal) {
|
|
145
|
+
super(refusalMessage(refusal));
|
|
146
|
+
this.name = "PlatformUnavailableError";
|
|
147
|
+
this.refusal = refusal;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The cloud adapter when the shell signalled cloud, else the dev-server adapter.
|
|
153
|
+
*
|
|
154
|
+
* @throws {PlatformUnavailableError} When a launcher was declared or announced and registered
|
|
155
|
+
* nothing, or the page's scheme has no server behind it — see {@link bootRefusal}.
|
|
156
|
+
*/
|
|
24
157
|
export function resolveDefaultPlatform(): StudioPlatform {
|
|
158
|
+
const refusal = bootRefusal();
|
|
159
|
+
if (refusal) {
|
|
160
|
+
throw new PlatformUnavailableError(refusal);
|
|
161
|
+
}
|
|
25
162
|
const cloud = (globalThis as unknown as { __jxCloud?: CloudSignal }).__jxCloud;
|
|
26
163
|
return cloud ? createCloudPlatform(cloud.project) : createDevServerPlatform();
|
|
27
164
|
}
|
|
@@ -320,7 +320,12 @@ export function createDevServerPlatform() {
|
|
|
320
320
|
* undefined and the modal falls back to a typed Location field.
|
|
321
321
|
*
|
|
322
322
|
* The picked handle carries no filesystem path, so it is resolved by the id the picker tags the
|
|
323
|
-
* folder with (see `@jxsuite/studio/directory-picker`).
|
|
323
|
+
* folder with (see `@jxsuite/studio/directory-picker`). Resolves `null` only when the user
|
|
324
|
+
* dismisses the chooser; every other failure rejects with a sentence the modal shows under
|
|
325
|
+
* Location. So `locate` keeps the helper's contract: `null` is "no folder holds this id" (the
|
|
326
|
+
* route's 200 `{ path: null }`), and a non-ok answer throws the problem the server sent rather
|
|
327
|
+
* than posing as a miss. A network failure (`TypeError: Failed to fetch`, e.g. a page that is
|
|
328
|
+
* not being served by the dev server at all) propagates as is, and the picker names it.
|
|
324
329
|
*/
|
|
325
330
|
...(canPickDirectory()
|
|
326
331
|
? {
|
|
@@ -330,9 +335,11 @@ export function createDevServerPlatform() {
|
|
|
330
335
|
`/__studio/locate-directory?name=${encodeURIComponent(name)}&id=${encodeURIComponent(id)}`,
|
|
331
336
|
);
|
|
332
337
|
if (!res.ok) {
|
|
333
|
-
|
|
338
|
+
// A proxy, or a `views://` host with no dev server behind it, may answer in HTML.
|
|
339
|
+
const body: unknown = await res.json().catch(() => null);
|
|
340
|
+
throw new Error(problemMessage(body, res.status));
|
|
334
341
|
}
|
|
335
|
-
const found = await readJson<{ path?: string }>(res);
|
|
342
|
+
const found = await readJson<{ path?: string | null }>(res);
|
|
336
343
|
return found.path ?? null;
|
|
337
344
|
});
|
|
338
345
|
},
|
|
@@ -383,6 +390,19 @@ export function createDevServerPlatform() {
|
|
|
383
390
|
return data.content;
|
|
384
391
|
},
|
|
385
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Raw bytes, undecoded. Same-origin here, so the response body is simply the file.
|
|
395
|
+
*
|
|
396
|
+
* @param {string} path
|
|
397
|
+
*/
|
|
398
|
+
async readFileBytes(path: string) {
|
|
399
|
+
const res = await fetch(`/__studio/file/bytes?path=${encodeURIComponent(serverPath(path))}`);
|
|
400
|
+
if (!res.ok) {
|
|
401
|
+
throw new Error(`Failed to read file bytes: ${path}`);
|
|
402
|
+
}
|
|
403
|
+
return res.arrayBuffer();
|
|
404
|
+
},
|
|
405
|
+
|
|
386
406
|
/**
|
|
387
407
|
* @param {string} path
|
|
388
408
|
* @param {string} content
|