@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
package/dist/studio.js
CHANGED
|
@@ -22,6 +22,19 @@ import {
|
|
|
22
22
|
hasElementSelection,
|
|
23
23
|
emptyContext,
|
|
24
24
|
keyScopeStack,
|
|
25
|
+
toolSuccess,
|
|
26
|
+
toolError,
|
|
27
|
+
createToolDefinition,
|
|
28
|
+
createSessionFacts,
|
|
29
|
+
createToolContext,
|
|
30
|
+
linkCallSignal,
|
|
31
|
+
createToolRegistry,
|
|
32
|
+
openTurnLedger,
|
|
33
|
+
fileTurn,
|
|
34
|
+
writesForTurn,
|
|
35
|
+
summarizeWrites,
|
|
36
|
+
resetAiWrites,
|
|
37
|
+
turnAnchor,
|
|
25
38
|
PROJECT_CONFIG_PATH,
|
|
26
39
|
editorKindOf,
|
|
27
40
|
editorKindsOf,
|
|
@@ -46,6 +59,8 @@ import {
|
|
|
46
59
|
mutateUpdateProperty,
|
|
47
60
|
mutateUpdateStyle,
|
|
48
61
|
mutateUpdateAttribute,
|
|
62
|
+
mutateAddDef,
|
|
63
|
+
mutateRemoveDef,
|
|
49
64
|
mutateUpdateProp,
|
|
50
65
|
mutateUpdateFrontmatter,
|
|
51
66
|
collabState,
|
|
@@ -97,7 +112,7 @@ import {
|
|
|
97
112
|
activateTab2,
|
|
98
113
|
moveTabToPane2,
|
|
99
114
|
registerTabCommands2
|
|
100
|
-
} from "./chunks/studio-
|
|
115
|
+
} from "./chunks/studio-dgdm6vvt.js";
|
|
101
116
|
import {
|
|
102
117
|
LEVELS,
|
|
103
118
|
CATEGORIES,
|
|
@@ -108,7 +123,7 @@ import {
|
|
|
108
123
|
isPanelVisible,
|
|
109
124
|
railGroups,
|
|
110
125
|
railPanelSet
|
|
111
|
-
} from "./chunks/studio-
|
|
126
|
+
} from "./chunks/studio-fqpkpq1t.js";
|
|
112
127
|
import {
|
|
113
128
|
LIVE_PREVIEW,
|
|
114
129
|
getNodeAtPath2,
|
|
@@ -141,17 +156,19 @@ import {
|
|
|
141
156
|
clearSettings,
|
|
142
157
|
hydrateSettings,
|
|
143
158
|
watchRemoteSettings
|
|
144
|
-
} from "./chunks/studio-
|
|
159
|
+
} from "./chunks/studio-yxmmjy2a.js";
|
|
145
160
|
import {
|
|
146
161
|
registerPlatform2,
|
|
162
|
+
launcherSignal2,
|
|
147
163
|
getPlatform2,
|
|
148
164
|
hasPlatform2
|
|
149
|
-
} from "./chunks/studio-
|
|
165
|
+
} from "./chunks/studio-6pwn9j8c.js";
|
|
150
166
|
import {
|
|
151
167
|
effectScope,
|
|
152
168
|
effect,
|
|
153
169
|
reactive,
|
|
154
170
|
toRaw,
|
|
171
|
+
shallowRef,
|
|
155
172
|
announce,
|
|
156
173
|
now,
|
|
157
174
|
problems2,
|
|
@@ -178,7 +195,7 @@ import {
|
|
|
178
195
|
bufferMovedOn2,
|
|
179
196
|
commitTabBuffers2,
|
|
180
197
|
tabBufferUnsaved2
|
|
181
|
-
} from "./chunks/studio-
|
|
198
|
+
} from "./chunks/studio-6vg0x5fz.js";
|
|
182
199
|
import {
|
|
183
200
|
loadMonaco,
|
|
184
201
|
loadedMonaco,
|
|
@@ -192,7 +209,7 @@ import {
|
|
|
192
209
|
formatByName2,
|
|
193
210
|
formatForPath2,
|
|
194
211
|
formatSerialize2
|
|
195
|
-
} from "./chunks/studio-
|
|
212
|
+
} from "./chunks/studio-9vsvz7w7.js";
|
|
196
213
|
import {
|
|
197
214
|
parseJsonDocument,
|
|
198
215
|
serializeJson,
|
|
@@ -234,14 +251,14 @@ import {
|
|
|
234
251
|
toggleActivityTab,
|
|
235
252
|
resetProjectShell,
|
|
236
253
|
registerShellViewCommands
|
|
237
|
-
} from "./chunks/studio-
|
|
254
|
+
} from "./chunks/studio-mfkh1q8d.js";
|
|
238
255
|
import {
|
|
239
256
|
componentMetaFrom,
|
|
240
257
|
componentRegistry2,
|
|
241
258
|
loadComponentRegistry2,
|
|
242
259
|
noteComponentSaved2,
|
|
243
260
|
buildComponentInstance2
|
|
244
|
-
} from "./chunks/studio-
|
|
261
|
+
} from "./chunks/studio-gjj4bbfx.js";
|
|
245
262
|
import {
|
|
246
263
|
tabOfPane,
|
|
247
264
|
derivationOfPane,
|
|
@@ -352,7 +369,7 @@ import {
|
|
|
352
369
|
postApplyFormat2,
|
|
353
370
|
postOpenSlash2,
|
|
354
371
|
commitActiveEditSession2
|
|
355
|
-
} from "./chunks/studio-
|
|
372
|
+
} from "./chunks/studio-mrh0a92f.js";
|
|
356
373
|
import {
|
|
357
374
|
PUBLIC_DIR,
|
|
358
375
|
setSkipServerFunctions,
|
|
@@ -366,13 +383,14 @@ import {
|
|
|
366
383
|
showModal,
|
|
367
384
|
close2,
|
|
368
385
|
splitModifiersOf,
|
|
386
|
+
trackOf,
|
|
369
387
|
attrs,
|
|
370
388
|
tagOf,
|
|
371
389
|
labelOf,
|
|
372
390
|
KIT_BASE,
|
|
373
391
|
setActiveRegistry,
|
|
374
392
|
activeRegistry
|
|
375
|
-
} from "./chunks/studio-
|
|
393
|
+
} from "./chunks/studio-3nprwbd9.js";
|
|
376
394
|
import {
|
|
377
395
|
DERIVE_PRESETS,
|
|
378
396
|
PRESET_LABELS,
|
|
@@ -402,7 +420,7 @@ import {
|
|
|
402
420
|
defBadgeLabel,
|
|
403
421
|
mountSignalsPanel,
|
|
404
422
|
registerSignalsCommands
|
|
405
|
-
} from "./chunks/studio-
|
|
423
|
+
} from "./chunks/studio-rav5bpmp.js";
|
|
406
424
|
import {
|
|
407
425
|
mountsUsing,
|
|
408
426
|
mountsOf,
|
|
@@ -428,7 +446,7 @@ import {
|
|
|
428
446
|
showConfirmDialog2,
|
|
429
447
|
showSaveDiscardDialog2,
|
|
430
448
|
showPromptDialog2
|
|
431
|
-
} from "./chunks/studio-
|
|
449
|
+
} from "./chunks/studio-ag3z6shb.js";
|
|
432
450
|
import {
|
|
433
451
|
html3,
|
|
434
452
|
nothing2,
|
|
@@ -444,11 +462,11 @@ import {
|
|
|
444
462
|
uploadAssets,
|
|
445
463
|
mediaSiteUrl,
|
|
446
464
|
previewFileSrc
|
|
447
|
-
} from "./chunks/studio-
|
|
448
|
-
import"./chunks/studio-
|
|
465
|
+
} from "./chunks/studio-sceg8ws0.js";
|
|
466
|
+
import"./chunks/studio-dwdcr2e8.js";
|
|
449
467
|
import {
|
|
450
468
|
invalidateUsages2
|
|
451
|
-
} from "./chunks/studio-
|
|
469
|
+
} from "./chunks/studio-557bmn0x.js";
|
|
452
470
|
import {
|
|
453
471
|
hydrateProjectList,
|
|
454
472
|
getProjectList,
|
|
@@ -470,11 +488,7 @@ import {
|
|
|
470
488
|
registerSourceControlCommands2,
|
|
471
489
|
cleanupGitPanel2,
|
|
472
490
|
registerGitPanel2
|
|
473
|
-
} from "./chunks/studio-
|
|
474
|
-
import"./chunks/studio-4r14yzyy.js";
|
|
475
|
-
import {
|
|
476
|
-
openMenu2
|
|
477
|
-
} from "./chunks/studio-qa7fzxan.js";
|
|
491
|
+
} from "./chunks/studio-apv9x70c.js";
|
|
478
492
|
import {
|
|
479
493
|
parseGridTabId,
|
|
480
494
|
gridTabLabel,
|
|
@@ -500,22 +514,26 @@ import {
|
|
|
500
514
|
exportFile2,
|
|
501
515
|
confirmFileDelete2,
|
|
502
516
|
renamePromptMessage2
|
|
503
|
-
} from "./chunks/studio-
|
|
517
|
+
} from "./chunks/studio-y49b6mjv.js";
|
|
518
|
+
import"./chunks/studio-vd67b66d.js";
|
|
519
|
+
import {
|
|
520
|
+
openMenu2
|
|
521
|
+
} from "./chunks/studio-mt79tn56.js";
|
|
504
522
|
import {
|
|
505
523
|
platformSupportsPublish,
|
|
506
524
|
latestDeployment,
|
|
507
525
|
currentDeploy,
|
|
508
526
|
noteDeployment
|
|
509
|
-
} from "./chunks/studio-
|
|
527
|
+
} from "./chunks/studio-xrbfdxgk.js";
|
|
510
528
|
import {
|
|
511
529
|
resetIgnoreCache,
|
|
512
530
|
startFsSync2
|
|
513
|
-
} from "./chunks/studio-
|
|
531
|
+
} from "./chunks/studio-40z39dme.js";
|
|
514
532
|
import {
|
|
515
533
|
beginActivity,
|
|
516
534
|
registerActivityPanel,
|
|
517
535
|
showProgressModal
|
|
518
|
-
} from "./chunks/studio-
|
|
536
|
+
} from "./chunks/studio-4998spet.js";
|
|
519
537
|
import {
|
|
520
538
|
_defineProperty,
|
|
521
539
|
combine,
|
|
@@ -544,12 +562,15 @@ import {
|
|
|
544
562
|
createLibrarySource,
|
|
545
563
|
openLibraryTab,
|
|
546
564
|
registerGridCommands
|
|
547
|
-
} from "./chunks/studio-
|
|
565
|
+
} from "./chunks/studio-9wm0xzpf.js";
|
|
548
566
|
import {
|
|
549
567
|
DRAFT_FIELD,
|
|
550
568
|
isDraftEntry
|
|
551
|
-
} from "./chunks/studio-
|
|
552
|
-
import"./chunks/studio-
|
|
569
|
+
} from "./chunks/studio-ygvr9e4q.js";
|
|
570
|
+
import"./chunks/studio-sjas5b3a.js";
|
|
571
|
+
import {
|
|
572
|
+
computeWindow
|
|
573
|
+
} from "./chunks/studio-a5h5j33a.js";
|
|
553
574
|
import {
|
|
554
575
|
ensureDependenciesInstalled,
|
|
555
576
|
convertTargets,
|
|
@@ -573,11 +594,11 @@ import {
|
|
|
573
594
|
reloadCleanTab2,
|
|
574
595
|
reloadFileInTab2,
|
|
575
596
|
registerFilesPanel2
|
|
576
|
-
} from "./chunks/studio-
|
|
597
|
+
} from "./chunks/studio-m58xn8at.js";
|
|
577
598
|
import {
|
|
578
599
|
REDIRECT_STATUSES,
|
|
579
600
|
referenceTarget
|
|
580
|
-
} from "./chunks/studio-
|
|
601
|
+
} from "./chunks/studio-w7gpcvfj.js";
|
|
581
602
|
import"./chunks/studio-tjjk7jvk.js";
|
|
582
603
|
import {
|
|
583
604
|
FIELD_TYPES,
|
|
@@ -593,7 +614,7 @@ import {
|
|
|
593
614
|
renderEntryMode,
|
|
594
615
|
createEntry2,
|
|
595
616
|
registerContentCommands2
|
|
596
|
-
} from "./chunks/studio-
|
|
617
|
+
} from "./chunks/studio-482eeazk.js";
|
|
597
618
|
import {
|
|
598
619
|
notifySettingsDocument2,
|
|
599
620
|
onSettingsDocumentChanged2,
|
|
@@ -623,11 +644,11 @@ import {
|
|
|
623
644
|
renderStylebookMode,
|
|
624
645
|
selectStylebookTag,
|
|
625
646
|
defaultCommands
|
|
626
|
-
} from "./chunks/studio-
|
|
647
|
+
} from "./chunks/studio-bh47w0cy.js";
|
|
627
648
|
import {
|
|
628
649
|
openPopoverSurface,
|
|
629
650
|
invalidateMediaCache2
|
|
630
|
-
} from "./chunks/studio-
|
|
651
|
+
} from "./chunks/studio-rwmmbrth.js";
|
|
631
652
|
import {
|
|
632
653
|
findEnclosingRepeater,
|
|
633
654
|
convertToComponent,
|
|
@@ -670,11 +691,11 @@ import {
|
|
|
670
691
|
liveElementCommands,
|
|
671
692
|
dismissContextMenu,
|
|
672
693
|
showContextMenu
|
|
673
|
-
} from "./chunks/studio-
|
|
694
|
+
} from "./chunks/studio-ne593z1j.js";
|
|
674
695
|
import {
|
|
675
696
|
initQuickSearch2,
|
|
676
697
|
openQuickSearch2
|
|
677
|
-
} from "./chunks/studio-
|
|
698
|
+
} from "./chunks/studio-jfhxy8h3.js";
|
|
678
699
|
import {
|
|
679
700
|
initCssData,
|
|
680
701
|
registerStyleCommands,
|
|
@@ -682,19 +703,20 @@ import {
|
|
|
682
703
|
render2,
|
|
683
704
|
inspectorTab2,
|
|
684
705
|
setInspectorTab2
|
|
685
|
-
} from "./chunks/studio-
|
|
706
|
+
} from "./chunks/studio-fy320dfc.js";
|
|
686
707
|
import {
|
|
687
708
|
validateDoc2,
|
|
688
709
|
validateProjectConfig2
|
|
689
|
-
} from "./chunks/studio-
|
|
710
|
+
} from "./chunks/studio-fgmfksz8.js";
|
|
690
711
|
import {
|
|
691
712
|
getOpenAiKey,
|
|
692
713
|
setOpenAiKey,
|
|
693
|
-
hasOpenAiKey,
|
|
694
714
|
getBaseUrl,
|
|
695
715
|
storedModel,
|
|
696
716
|
setModel,
|
|
697
717
|
saveAiProvider,
|
|
718
|
+
aiProviderDiffers,
|
|
719
|
+
storedEquals,
|
|
698
720
|
aiConnection,
|
|
699
721
|
resetModelCache,
|
|
700
722
|
cachedModels,
|
|
@@ -708,8 +730,8 @@ import {
|
|
|
708
730
|
proxyModelsErrorMessage,
|
|
709
731
|
preferredModel,
|
|
710
732
|
fetchAvailableModels
|
|
711
|
-
} from "./chunks/studio-
|
|
712
|
-
import"./chunks/studio-
|
|
733
|
+
} from "./chunks/studio-7d3c1m59.js";
|
|
734
|
+
import"./chunks/studio-b9q3znz8.js";
|
|
713
735
|
import {
|
|
714
736
|
platformBrokersCf,
|
|
715
737
|
refreshCfConnection,
|
|
@@ -719,13 +741,13 @@ import {
|
|
|
719
741
|
revokeAccount,
|
|
720
742
|
onCredentialsChanged,
|
|
721
743
|
notifyCredentialsChanged
|
|
722
|
-
} from "./chunks/studio-
|
|
744
|
+
} from "./chunks/studio-qxye31h7.js";
|
|
723
745
|
import {
|
|
724
746
|
seedPublishConnected2
|
|
725
|
-
} from "./chunks/studio-
|
|
747
|
+
} from "./chunks/studio-w475cbf9.js";
|
|
726
748
|
import {
|
|
727
749
|
selectContributedEntry
|
|
728
|
-
} from "./chunks/studio-
|
|
750
|
+
} from "./chunks/studio-01269reb.js";
|
|
729
751
|
import {
|
|
730
752
|
deepEqual2,
|
|
731
753
|
matchChildren
|
|
@@ -744,13 +766,10 @@ import {
|
|
|
744
766
|
applySavedView,
|
|
745
767
|
deleteSavedView
|
|
746
768
|
} from "./chunks/studio-1tbjfqv4.js";
|
|
747
|
-
import {
|
|
748
|
-
computeWindow
|
|
749
|
-
} from "./chunks/studio-38emantv.js";
|
|
750
769
|
import {
|
|
751
770
|
entryCollections,
|
|
752
771
|
collectionOfPath
|
|
753
|
-
} from "./chunks/studio-
|
|
772
|
+
} from "./chunks/studio-5w26wza5.js";
|
|
754
773
|
import"./chunks/studio-q0a3yk4n.js";
|
|
755
774
|
import {
|
|
756
775
|
resolveAssetRef,
|
|
@@ -758,7 +777,7 @@ import {
|
|
|
758
777
|
assetContextFor,
|
|
759
778
|
previewAssetSrc,
|
|
760
779
|
hostAssetDeclarations
|
|
761
|
-
} from "./chunks/studio-
|
|
780
|
+
} from "./chunks/studio-kwewmhhq.js";
|
|
762
781
|
import {
|
|
763
782
|
rectOf,
|
|
764
783
|
elementAtPoint
|
|
@@ -773,7 +792,7 @@ import {
|
|
|
773
792
|
__preload,
|
|
774
793
|
__chunks
|
|
775
794
|
} from "./chunks/studio-5te5tqng.js";
|
|
776
|
-
__chunks(import.meta.url,["
|
|
795
|
+
__chunks(import.meta.url,["eq7zkzt2","z2gg8qjt","ak6hxfma","reg9eevt","f7bdv3z5","42gk8ha1","b9k2frks","jaq1t9zj","fnchmhy7","s6mc01ry","m244w9nh","e8d9wzyk","xm826zjw","3f08rry6","erze4czt","m79r7p5y","5pq32t0q","3xan8mnj","psetcv20","qaqer9kr","nhn6rv4w","epp2z3s3","mym8qnd0","egsb0ry8","r73an5s6","3ntvq6cg","yxa81f45","dgdm6vvt","fqpkpq1t","m1p9b6c3","yxmmjy2a","6pwn9j8c","d8z5bajd","6vg0x5fz","9vsvz7w7","mfkh1q8d","gjj4bbfx","mrh0a92f","3nprwbd9","rav5bpmp","ag3z6shb","1k325pwf","sceg8ws0","dwdcr2e8","557bmn0x","apv9x70c","y49b6mjv","vd67b66d","mt79tn56","xrbfdxgk","40z39dme","4998spet","esrxgwh6","t171hxhe","9wm0xzpf","ygvr9e4q","sjas5b3a","a5h5j33a","m58xn8at","w7gpcvfj","tjjk7jvk","482eeazk","0hqbpbd0","vhv5tgxm","bh47w0cy","rwmmbrth","ne593z1j","jfhxy8h3","fy320dfc","fgmfksz8","7d3c1m59","b9q3znz8","qxye31h7","w475cbf9","01269reb","wqpngpsk","98b3z07n","1tbjfqv4","5w26wza5","q0a3yk4n","kwewmhhq","rs76k67r","r6xmmtfh","5te5tqng","0a4gncx0","ptfcxkz5","9yjae3hz","aj9drheq","ddy019sn","gsjdysze","c63mkt3b","nj7tnjfn","t5n8wttg","a05atk6q","8j4qxjrp","64nb2a9a","jt7rbcbc","vg38bq99","83a16ytx","g79n01v9","mdmyx6s2","ks2zzth2","0d4p4wxj","zs88v0wr","hr5sg57n","1tejxvba","7e11jpa9","fbcyz234","ay4c78ne"],[["./studio.js",25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83],["./chunks/fs-events-z2gg8qjt.js",50,44,29,31,26,25],["./chunks/publish-panel-ak6hxfma.js",73,70,71,49,35,30,27,33,29,34,31,40,41,38,26,25,32],["./chunks/parse-reg9eevt.js",26,25],["./chunks/references-f7bdv3z5.js",44,31,26,25],["./chunks/notify-42gk8ha1.js",32],["./chunks/workspace-b9k2frks.js",27,33,29,34,31,25,32],["./chunks/monaco-buffer-jaq1t9zj.js",33,32],["./chunks/platform-fnchmhy7.js",31],["./chunks/layers-s6mc01ry.js",40,41,38,26,25,32],["./chunks/lit-html-m244w9nh.js",41],["./chunks/monaco-binding-e8d9wzyk.js",84,85],["./chunks/components-xm826zjw.js",36,29,31,25],["./chunks/cf-account-picker-3f08rry6.js",31,25,26,32,41,40,38,56,30,71,70,72],["./chunks/ws-client-erze4czt.js",85],["./chunks/cf-settings-m79r7p5y.js",71,30,31,32],["./chunks/section-registry-5pq32t0q.js",62],["./chunks/file-ops-3xan8mnj.js",46,50,79,47,51,49,28,44,37,35,36,30,42,80,27,33,29,52,34,31,48,40,41,38,26,25,32],["./chunks/iframe-host-psetcv20.js",37,35,36,30,80,27,33,29,52,34,31,38,26,25,32],["./chunks/grid-view-qaqer9kr.js",31,34,25,29,27,41,40,38,32,26,65,33,42,43,55,50,44,49,35,30,51,28,46,47,48,37,36,52,53,77,79,80,81],["./chunks/media-picker-nhn6rv4w.js",65,43,42,80,27,33,29,34,31,40,41,38,26,25,32],["./chunks/jx-validate-epp2z3s3.js",69],["./chunks/format-host-mym8qnd0.js",34,31],["./chunks/files-egsb0ry8.js",58,57,45,56,53,43,78,54,55,46,50,79,47,51,49,28,44,37,35,36,30,42,80,27,33,29,52,34,31,48,40,41,38,26,25,32],["./chunks/extension-sections-r73an5s6.js",31,34,62,25,26,32,33,27,29,38,40,59,41,48,60,69,74,54,50,44,49,35,30,51,28,46,55,42,47,37,36,52,79,80],["./chunks/studio-3ntvq6cg.js"],["./chunks/studio-yxa81f45.js"],["./chunks/studio-dgdm6vvt.js",32,33,31,34,25,29,87,83],["./chunks/studio-fqpkpq1t.js",27,29,35],["./chunks/studio-m1p9b6c3.js",25],["./chunks/studio-yxmmjy2a.js",31,32],["./chunks/studio-6pwn9j8c.js"],["./chunks/studio-d8z5bajd.js"],["./chunks/studio-6vg0x5fz.js",32],["./chunks/studio-9vsvz7w7.js",31,83],["./chunks/studio-mfkh1q8d.js",32,27,31,34,29,30,26,83],["./chunks/studio-gjj4bbfx.js",31,29],["./chunks/studio-mrh0a92f.js",31,25,29,32,33,27,34,35,36,38,52,80,81,82],["./chunks/studio-3nprwbd9.js",25,32,83],["./chunks/studio-rav5bpmp.js",31,25,29,36,37,32,33,27,34,38,35,41,40,28,47,59,52,48,65,44,87,81,83],["./chunks/studio-ag3z6shb.js",41,38,32,26],["./chunks/studio-1k325pwf.js"],["./chunks/studio-sceg8ws0.js",26,31,32,27,29,38,80],["./chunks/studio-dwdcr2e8.js",31,38,42],["./chunks/studio-557bmn0x.js",31,26],["./chunks/studio-apv9x70c.js",41,26,31,34,29,27,32,42,40,38,43,35,28,56,51,37,53,81,83],["./chunks/studio-y49b6mjv.js",31,25,26,44,32,27,34,29,42,38,40,48,35,49,37,50,51,79],["./chunks/studio-vd67b66d.js",31,25,29,34],["./chunks/studio-mt79tn56.js",38,40,32,81],["./chunks/studio-xrbfdxgk.js",31,29,32,35,38,40],["./chunks/studio-40z39dme.js",31,44,29],["./chunks/studio-4998spet.js",41,38,49,32,35,40,28,44],["./chunks/studio-esrxgwh6.js",83],["./chunks/studio-t171hxhe.js"],["./chunks/studio-9wm0xzpf.js",32,27,31,34,29,40,26,50,46,55,42],["./chunks/studio-ygvr9e4q.js",31,29,32,27,34,50,46,83],["./chunks/studio-sjas5b3a.js",26,32,40,38],["./chunks/studio-a5h5j33a.js",81],["./chunks/studio-m58xn8at.js",41,26,27,40,38,32,29,31,34,50,44,35,30,51,28,46,36,52,45,42,54,48,57,78,83],["./chunks/studio-w7gpcvfj.js",38,27,25,32,40,48,87],["./chunks/studio-tjjk7jvk.js"],["./chunks/studio-482eeazk.js",34,32,27,29,26,38,40,59,55,35,46,78,87,83],["./chunks/studio-0hqbpbd0.js"],["./chunks/studio-vhv5tgxm.js"],["./chunks/studio-bh47w0cy.js",29,38,40,32,27,35,36,37,83],["./chunks/studio-rwmmbrth.js",31,29,40,32,42,43,80,81],["./chunks/studio-ne593z1j.js",41,25,29,32,33,27,31,35,28,36,38,40,64,26,37,52,39,48,44,57,79,81,83],["./chunks/studio-jfhxy8h3.js",31,29,34,27,40,38,32,58],["./chunks/studio-fy320dfc.js",25,29,32,27,38,39,40,59,37,48,35,44,64,87],["./chunks/studio-fgmfksz8.js",101,83],["./chunks/studio-7d3c1m59.js",31,30],["./chunks/studio-b9q3znz8.js",30],["./chunks/studio-qxye31h7.js",56,31,71,70,83],["./chunks/studio-w475cbf9.js",38,49,31,29,32,40,71,70,83],["./chunks/studio-01269reb.js",26,31,32,29,40,59,69,54,37,35],["./chunks/studio-wqpngpsk.js"],["./chunks/studio-98b3z07n.js"],["./chunks/studio-1tbjfqv4.js"],["./chunks/studio-5w26wza5.js",29,27,55],["./chunks/studio-q0a3yk4n.js"],["./chunks/studio-kwewmhhq.js",31,27,38,29],["./chunks/studio-rs76k67r.js"],["./chunks/studio-r6xmmtfh.js"],["./chunks/studio-5te5tqng.js"],["./chunks/studio-0a4gncx0.js",85],["./chunks/studio-ptfcxkz5.js"],["./chunks/index-9yjae3hz.js",84,85,75,87],["./chunks/studio-aj9drheq.js"],["./chunks/editor-ddy019sn.js",102],["./chunks/monaco-setup-gsjdysze.js",102,105,60,63,101,82,83],["./chunks/quick-search-c63mkt3b.js",67,58,57,45,56,53,43,78,54,55,46,50,79,47,51,49,28,44,37,35,36,30,42,80,27,33,29,52,34,31,48,40,41,38,26,25,32],["./chunks/adapter-nj7tnjfn.js",52],["./chunks/right-panel-t5n8wttg.js",68,64,39,65,43,59,47,28,44,37,35,36,30,42,80,27,33,29,52,34,31,48,40,41,38,26,25,32,60],["./chunks/git-panel-a05atk6q.js",45,56,53,43,51,49,28,44,37,35,36,30,42,80,27,33,29,52,34,31,40,41,38,26,25,32],["./chunks/menu-8j4qxjrp.js",48,40,41,38,26,25,32],["./chunks/entry-commands-64nb2a9a.js",61,78,59,55,46,50,79,47,51,49,28,44,37,35,36,30,42,80,27,33,29,52,34,31,48,40,41,38,26,25,32,60],["./chunks/content-source-jt7rbcbc.js",55,46,50,79,47,51,49,28,44,37,35,36,30,42,80,27,33,29,52,34,31,48,40,41,38,26,25,32],["./chunks/convert-to-repeater-vg38bq99.js",25,29,32,33,27,31,34,41,40,38,26,37,39,59,60,28,30,35,36,52,47,48,65,42,43,44,80],["./chunks/layers-panel-83a16ytx.js",66,64,39,65,57,43,59,79,47,28,44,37,35,36,30,42,80,27,33,29,52,34,31,48,40,41,38,26,25,32,60],["./chunks/2020-g79n01v9.js",106,83],["./chunks/index-mdmyx6s2.js",106,83],["./chunks/studio-ks2zzth2.js"],["./chunks/studio-0d4p4wxj.js"],["./chunks/jsonMode-zs88v0wr.js",102,107,105],["./chunks/javascript-hr5sg57n.js",102],["./chunks/studio-1tejxvba.js",102,83],["./chunks/studio-7e11jpa9.js",83],["./chunks/studio-fbcyz234.js",102,105],["./chunks/tsMode-ay4c78ne.js",102,107,105]],0);
|
|
777
796
|
|
|
778
797
|
// ../../node_modules/ms/index.js
|
|
779
798
|
var require_ms = __commonJS(function(exports, module) {
|
|
@@ -1307,6 +1326,41 @@ var require_extend = __commonJS(function(exports, module) {
|
|
|
1307
1326
|
};
|
|
1308
1327
|
});
|
|
1309
1328
|
|
|
1329
|
+
// src/services/module-preloads.ts
|
|
1330
|
+
var QUEUED = [];
|
|
1331
|
+
var frame = null;
|
|
1332
|
+
var originalAppend = null;
|
|
1333
|
+
function flush() {
|
|
1334
|
+
frame = null;
|
|
1335
|
+
const singleOrigin = originalAppend;
|
|
1336
|
+
if (!singleOrigin) {
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
for (const link of QUEUED.splice(0)) {
|
|
1340
|
+
singleOrigin.call(document.head, link);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
function appendChildPatched(node) {
|
|
1344
|
+
if (node instanceof HTMLLinkElement && node.rel === "modulepreload") {
|
|
1345
|
+
QUEUED.push(node);
|
|
1346
|
+
if (frame === null) {
|
|
1347
|
+
frame = requestAnimationFrame(flush);
|
|
1348
|
+
}
|
|
1349
|
+
return node;
|
|
1350
|
+
}
|
|
1351
|
+
return originalAppend ? originalAppend(node) : node;
|
|
1352
|
+
}
|
|
1353
|
+
function installModulePreloadBatcher() {
|
|
1354
|
+
const { head } = document;
|
|
1355
|
+
if (!head || head.__modulePreloadsBatched) {
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
const patched = head;
|
|
1359
|
+
originalAppend = Node.prototype.appendChild.bind(head);
|
|
1360
|
+
patched.__modulePreloadsBatched = true;
|
|
1361
|
+
head.appendChild = appendChildPatched;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1310
1364
|
// src/canvas/diff-view.ts
|
|
1311
1365
|
var _records = new Map;
|
|
1312
1366
|
function recordFor(paneId) {
|
|
@@ -1349,6 +1403,9 @@ function stepDiff(paneId, delta) {
|
|
|
1349
1403
|
function diffViewOf(paneId) {
|
|
1350
1404
|
return _records.get(paneId)?.view ?? "visual";
|
|
1351
1405
|
}
|
|
1406
|
+
function diffShowsCode(paneId, renderable) {
|
|
1407
|
+
return !renderable || diffViewOf(paneId) === "code";
|
|
1408
|
+
}
|
|
1352
1409
|
function setDiffView(paneId, view) {
|
|
1353
1410
|
recordFor(paneId).view = view;
|
|
1354
1411
|
}
|
|
@@ -1693,9 +1750,14 @@ var _repaint = () => {};
|
|
|
1693
1750
|
function setDiffRepaint(repaint) {
|
|
1694
1751
|
_repaint = repaint;
|
|
1695
1752
|
}
|
|
1753
|
+
var _chromeRepaint = null;
|
|
1754
|
+
function setDiffChromeRepaint(repaint) {
|
|
1755
|
+
_chromeRepaint = repaint;
|
|
1756
|
+
}
|
|
1696
1757
|
function repaintDiffStage(paneId) {
|
|
1697
1758
|
surfaceForPane(paneId).prevCanvasMode = null;
|
|
1698
1759
|
_repaint(paneId);
|
|
1760
|
+
_chromeRepaint?.();
|
|
1699
1761
|
}
|
|
1700
1762
|
function renderDiffToolbar(paneId) {
|
|
1701
1763
|
const bar = _bars.get(paneId);
|
|
@@ -1743,7 +1805,7 @@ async function stepDiffAndReveal(paneId, delta) {
|
|
|
1743
1805
|
if (!step) {
|
|
1744
1806
|
return;
|
|
1745
1807
|
}
|
|
1746
|
-
const { hostForCanvas, measureInCanvas, revealCanvasPathIn } = await (__preload("
|
|
1808
|
+
const { hostForCanvas, measureInCanvas, revealCanvasPathIn } = await (__preload("psetcv20"), import("./chunks/iframe-host-psetcv20.js"));
|
|
1747
1809
|
const measured = await Promise.all([
|
|
1748
1810
|
step.originalPath && original ? measureInCanvas(original, step.originalPath) : null,
|
|
1749
1811
|
step.currentPath && current ? measureInCanvas(current, step.currentPath) : null
|
|
@@ -1961,6 +2023,37 @@ function buildChangeMap(a, b, opts = {}) {
|
|
|
1961
2023
|
};
|
|
1962
2024
|
}
|
|
1963
2025
|
|
|
2026
|
+
// src/canvas/stage-scrollbar.ts
|
|
2027
|
+
var STAGE_SCROLLBAR_VAR = "--stage-scrollbar-w";
|
|
2028
|
+
function scrollbarWidthOf(scroller) {
|
|
2029
|
+
return Math.max(0, scroller.offsetWidth - scroller.clientWidth);
|
|
2030
|
+
}
|
|
2031
|
+
function cellOf(surface) {
|
|
2032
|
+
return surface.wrap?.closest(PANE_SELECTOR) ?? document.documentElement;
|
|
2033
|
+
}
|
|
2034
|
+
function trackStageScrollbar(surface, scroller) {
|
|
2035
|
+
if (surface.stageScrollbar?.scroller === scroller) {
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
releaseStageScrollbar(surface);
|
|
2039
|
+
if (!scroller) {
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
const cell = cellOf(surface);
|
|
2043
|
+
const write = () => {
|
|
2044
|
+
cell.style.setProperty(STAGE_SCROLLBAR_VAR, `${scrollbarWidthOf(scroller)}px`);
|
|
2045
|
+
};
|
|
2046
|
+
const observer = new ResizeObserver(write);
|
|
2047
|
+
observer.observe(scroller);
|
|
2048
|
+
surface.stageScrollbar = { observer, scroller };
|
|
2049
|
+
write();
|
|
2050
|
+
}
|
|
2051
|
+
function releaseStageScrollbar(surface) {
|
|
2052
|
+
surface.stageScrollbar?.observer.disconnect();
|
|
2053
|
+
surface.stageScrollbar = null;
|
|
2054
|
+
cellOf(surface).style.setProperty(STAGE_SCROLLBAR_VAR, "0px");
|
|
2055
|
+
}
|
|
2056
|
+
|
|
1964
2057
|
// src/collab/monaco-cursors.ts
|
|
1965
2058
|
function cssString(value) {
|
|
1966
2059
|
return `"${value.replaceAll("\\", String.raw`\\`).replaceAll('"', String.raw`\"`)}"`;
|
|
@@ -2011,7 +2104,7 @@ function loadGridEngine() {
|
|
|
2011
2104
|
if (_engine) {
|
|
2012
2105
|
return Promise.resolve(_engine);
|
|
2013
2106
|
}
|
|
2014
|
-
_loading ??= (__preload("
|
|
2107
|
+
_loading ??= (__preload("qaqer9kr"), import("./chunks/grid-view-qaqer9kr.js")).then((engine) => {
|
|
2015
2108
|
_engine = engine;
|
|
2016
2109
|
return engine;
|
|
2017
2110
|
});
|
|
@@ -2030,7 +2123,7 @@ var grid_panel_default = {
|
|
|
2030
2123
|
part: "grid"
|
|
2031
2124
|
},
|
|
2032
2125
|
style: {
|
|
2033
|
-
$description: "The frame's own layout and the engine's theme, and nothing that was a class. The toolbar is a single row that never wraps: the chrome budget (§12.2) caps what may stand in it, and the count is the only thing pinned to the far end.",
|
|
2126
|
+
$description: "The frame's own layout and the engine's theme, and nothing that was a class. The toolbar is a single row that never wraps: the chrome budget (studio-ui-guidelines.md §12.2) caps what may stand in it, and the count is the only thing pinned to the far end.",
|
|
2034
2127
|
display: "flex",
|
|
2035
2128
|
flexDirection: "column",
|
|
2036
2129
|
height: "100%",
|
|
@@ -2482,7 +2575,7 @@ var grid_panel_default = {
|
|
|
2482
2575
|
textContent: "${state.viewLabel}"
|
|
2483
2576
|
}
|
|
2484
2577
|
],
|
|
2485
|
-
$description: "One control for the four facets a saved view is made of, because they are only ever adjusted together (§12.2). Its label is the applied view's name, so the toolbar says what is on screen before the panel is opened."
|
|
2578
|
+
$description: "One control for the four facets a saved view is made of, because they are only ever adjusted together (studio-ui-guidelines.md §12.2). Its label is the applied view's name, so the toolbar says what is on screen before the panel is opened."
|
|
2486
2579
|
},
|
|
2487
2580
|
{
|
|
2488
2581
|
tagName: "jx-textfield",
|
|
@@ -2818,7 +2911,7 @@ function mountGridPanelSurface(host, view, actions) {
|
|
|
2818
2911
|
var grid_views_default = {
|
|
2819
2912
|
$schema: "https://jxsuite.com/schema/v1",
|
|
2820
2913
|
$id: "StudioGridViews",
|
|
2821
|
-
$description: "The grid's View panel: the saved views, and the four facets one of them is made of — columns, sort, grouping, and the filter the toolbar box holds. ONE control rather than four, because the chrome budget caps what may stand in a toolbar (§12.2) and these four are only ever adjusted together. There is no Apply button: every edit writes the working layout and applies immediately, so the panel is never showing something the grid is not already doing.",
|
|
2914
|
+
$description: "The grid's View panel: the saved views, and the four facets one of them is made of — columns, sort, grouping, and the filter the toolbar box holds. ONE control rather than four, because the chrome budget caps what may stand in a toolbar (studio-ui-guidelines.md §12.2) and these four are only ever adjusted together. There is no Apply button: every edit writes the working layout and applies immediately, so the panel is never showing something the grid is not already doing.",
|
|
2822
2915
|
tagName: "jx-popover",
|
|
2823
2916
|
attributes: {
|
|
2824
2917
|
part: "views-panel"
|
|
@@ -6985,10 +7078,10 @@ var add_repo_default = {
|
|
|
6985
7078
|
color: "var(--danger)"
|
|
6986
7079
|
},
|
|
6987
7080
|
'& [part="access"]': {
|
|
6988
|
-
$description: "The listing is bounded by what the Jx Suite GitHub App was granted, so the dialog always offers a way out of that boundary rather than only saying the list is short.",
|
|
7081
|
+
$description: "The listing is bounded by what the Jx Suite GitHub App was granted, so the dialog always offers a way out of that boundary rather than only saying the list is short. `center`, not `baseline`: the `Refresh` beside the note is a small kit button, and a small kit control is layout-contained so its outset hit area adds nothing to a scrolling ancestor's overflow, which leaves it with no baseline to export; a baseline row would synthesise one from its bottom edge and drop the note and the links onto it. surface-baseline-rows.test.ts holds the rule for every surface.",
|
|
6989
7082
|
display: "flex",
|
|
6990
7083
|
flexWrap: "wrap",
|
|
6991
|
-
alignItems: "
|
|
7084
|
+
alignItems: "center",
|
|
6992
7085
|
gap: "var(--jx-space-2)",
|
|
6993
7086
|
paddingTop: "var(--jx-space-3)",
|
|
6994
7087
|
borderTop: "1px solid var(--border)",
|
|
@@ -7642,10 +7735,15 @@ var welcome_default = {
|
|
|
7642
7735
|
letterSpacing: "0.3px"
|
|
7643
7736
|
},
|
|
7644
7737
|
'& [part="section-header"]': {
|
|
7738
|
+
$description: "`center`, not `baseline`: `Clear all` is a small kit button, and a small kit control is layout-contained so its outset hit area adds nothing to a scrolling ancestor's overflow, which leaves it with no baseline to export. A baseline row synthesised one from the button's bottom edge, which stood the header 33px tall with the title 10px under the button's label. The gap under the header is the HEADER's, because centring aligns MARGIN boxes: while the h2 kept its own 8px block-end margin its margin box was 26px, exactly the header's height, so `center` had nothing to move and left the h2 flush at the top with the button centred 3.6px below its baseline — a fix that measured right and still looked wrong. With the margin moved out, the two boxes are the only things in the line and they centre on each other.",
|
|
7645
7739
|
display: "flex",
|
|
7646
|
-
alignItems: "
|
|
7740
|
+
alignItems: "center",
|
|
7647
7741
|
justifyContent: "space-between",
|
|
7648
|
-
gap: "var(--jx-space-4)"
|
|
7742
|
+
gap: "var(--jx-space-4)",
|
|
7743
|
+
marginBottom: "var(--jx-space-3)"
|
|
7744
|
+
},
|
|
7745
|
+
'& [part="section-header"] [part="section-title"]': {
|
|
7746
|
+
marginBlockEnd: "0"
|
|
7649
7747
|
},
|
|
7650
7748
|
'& [part="actions"]': {
|
|
7651
7749
|
display: "flex",
|
|
@@ -8824,7 +8922,7 @@ var tab_strip_default = {
|
|
|
8824
8922
|
lineHeight: "1"
|
|
8825
8923
|
},
|
|
8826
8924
|
'& [part="pill"]': {
|
|
8827
|
-
$description: '`.entry-pill` — the draft pill (§7.6), in `jx-tab`\'s status slot. It is ON THE CHIP and not only inside the entry editor because the mistake it prevents, publishing something you believed was private, is made while glancing at a row of tabs. "Published" is deliberately as visible as "Draft": a state you can only see one half of is a state you cannot check. The two Spectrum colours it used to name are `--jx-signal` against `--jx-bg`, which flips with the theme where a fixed yellow and a fixed black did not.',
|
|
8925
|
+
$description: '`.entry-pill` — the draft pill (site-architecture.md §7.6), in `jx-tab`\'s status slot. It is ON THE CHIP and not only inside the entry editor because the mistake it prevents, publishing something you believed was private, is made while glancing at a row of tabs. "Published" is deliberately as visible as "Draft": a state you can only see one half of is a state you cannot check. The two Spectrum colours it used to name are `--jx-signal` against `--jx-bg`, which flips with the theme where a fixed yellow and a fixed black did not.',
|
|
8828
8926
|
padding: "0 5px",
|
|
8829
8927
|
borderRadius: "var(--radius)",
|
|
8830
8928
|
background: "var(--bg)",
|
|
@@ -9057,7 +9155,7 @@ var tab_strip_default = {
|
|
|
9057
9155
|
textContent: "${$map.item.pill}"
|
|
9058
9156
|
},
|
|
9059
9157
|
{
|
|
9060
|
-
$description: "The pin, in the actions slot. `title` is the whole accessible name (§10): the glyph is hidden, so a matching `aria-label` would announce it twice. `tabindex` follows the tab's own selectedness, exactly as the close button's does — the strip is ONE stop in the tab order, and a control inside every chip that took the platform's default would make fifteen.",
|
|
9158
|
+
$description: "The pin, in the actions slot. `title` is the whole accessible name (studio-ui-guidelines.md §10): the glyph is hidden, so a matching `aria-label` would announce it twice. `tabindex` follows the tab's own selectedness, exactly as the close button's does — the strip is ONE stop in the tab order, and a control inside every chip that took the platform's default would make fifteen.",
|
|
9061
9159
|
tagName: "button",
|
|
9062
9160
|
attributes: {
|
|
9063
9161
|
slot: "actions",
|
|
@@ -10217,7 +10315,7 @@ var panel_page_default = {
|
|
|
10217
10315
|
padding: "0 var(--jx-space-2)"
|
|
10218
10316
|
},
|
|
10219
10317
|
'& [part="row"]': {
|
|
10220
|
-
$description: "§4.1's standard form row, stacked: the label sits above a control that takes the whole width. `min-width: 0` is what keeps it honest in a 240px dock (§4.6) — a field refuses to shrink below its own min-content without it.",
|
|
10318
|
+
$description: "studio-ui-guidelines.md §4.1's standard form row, stacked: the label sits above a control that takes the whole width. `min-width: 0` is what keeps it honest in a 240px dock (studio-ui-guidelines.md §4.6) — a field refuses to shrink below its own min-content without it.",
|
|
10221
10319
|
display: "flex",
|
|
10222
10320
|
flexDirection: "column",
|
|
10223
10321
|
alignItems: "stretch",
|
|
@@ -10233,7 +10331,7 @@ var panel_page_default = {
|
|
|
10233
10331
|
color: "var(--fg-dim)"
|
|
10234
10332
|
},
|
|
10235
10333
|
'& [part="chip"]': {
|
|
10236
|
-
$description: "§4.2's set dot: a 6px accent dot, drawn only where the value is set on this document, that clears it when pressed. A real `button` — it is pressable, and it is the row's only keyboard route to clearing — stripped of the chrome a button brings so the row still reads as a label with a dot beside it.",
|
|
10334
|
+
$description: "studio-ui-guidelines.md §4.2's set dot: a 6px accent dot, drawn only where the value is set on this document, that clears it when pressed. A real `button` — it is pressable, and it is the row's only keyboard route to clearing — stripped of the chrome a button brings so the row still reads as a label with a dot beside it.",
|
|
10237
10335
|
width: "6px",
|
|
10238
10336
|
height: "6px",
|
|
10239
10337
|
padding: "0",
|
|
@@ -11901,7 +11999,7 @@ function removeEntry(key) {
|
|
|
11901
11999
|
}
|
|
11902
12000
|
var _mediaPicker = null;
|
|
11903
12001
|
function mediaPicker() {
|
|
11904
|
-
_mediaPicker ??= (__preload("
|
|
12002
|
+
_mediaPicker ??= (__preload("nhn6rv4w"), import("./chunks/media-picker-nhn6rv4w.js"));
|
|
11905
12003
|
return _mediaPicker;
|
|
11906
12004
|
}
|
|
11907
12005
|
var ACTIONS = {
|
|
@@ -12004,28 +12102,21 @@ function registerPagePanel() {
|
|
|
12004
12102
|
var doc_header_default = {
|
|
12005
12103
|
$schema: "https://jxsuite.com/schema/v1",
|
|
12006
12104
|
$id: "StudioDocHeader",
|
|
12007
|
-
$description: "The Document Header card (
|
|
12105
|
+
$description: "The Document Header card (`docs/studio/editing/frontmatter.md`) — the artefact's own header, drawn IN the stage, docked above Edit's page, once per pane. A bar naming the collection and the page's route, a column of rows, the door to Search appearance and a disclosure listing the raw head tags. ONE row vocabulary: Title, the Layout picker and every schema-driven frontmatter field are the same `row`, discriminated by `kind`, because they differ in what they COMMIT rather than in what they look like — and what they commit is `panels/frontmatter-panel.ts`'s decision, reached by the row's `key`. Nothing here knows what a content collection is, which document it is drawing, or that a page has a route: the flow hands over rows whose every field is already a string and this draws them.",
|
|
12008
12106
|
tagName: "section",
|
|
12009
12107
|
attributes: {
|
|
12010
12108
|
part: "card",
|
|
12011
12109
|
"aria-label": "Document header",
|
|
12012
|
-
"data-jx-region": "${state.region}"
|
|
12013
|
-
"data-placement": "${state.placement}"
|
|
12110
|
+
"data-jx-region": "${state.region}"
|
|
12014
12111
|
},
|
|
12015
12112
|
style: {
|
|
12016
|
-
$description: "The twelve `.doc-header*` rules from `styles/canvas.css` plus the row vocabulary the card used to borrow from `styles/inspector.css` (`.style-row`, `.style-row-label`, `.set-dot`) and `styles/overlays.css` (`.media-picker`, `.media-picker-thumb`). The borrowed ones are TRANSCRIBED rather than shared: the card is a document now and a class it does not emit cannot style it, and the three lit panels that still draw a `.style-row` go on owning those rules until they convert. Every `--spectrum-font-size-*` reference is re-pointed at the kit's scale — a document mounted in a pane's stage is outside `<sp-theme>`, so each of them would silently have taken its hard-coded fallback and stopped following the theme. And the
|
|
12113
|
+
$description: "The twelve `.doc-header*` rules from `styles/canvas.css` plus the row vocabulary the card used to borrow from `styles/inspector.css` (`.style-row`, `.style-row-label`, `.set-dot`) and `styles/overlays.css` (`.media-picker`, `.media-picker-thumb`). The borrowed ones are TRANSCRIBED rather than shared: the card is a document now and a class it does not emit cannot style it, and the three lit panels that still draw a `.style-row` go on owning those rules until they convert. Every `--spectrum-font-size-*` reference is re-pointed at the kit's scale — a document mounted in a pane's stage is outside `<sp-theme>`, so each of them would silently have taken its hard-coded fallback and stopped following the theme. And the card has ONE shape, because it has one place: Edit docks it above the page's scroller as a band across the pane, so it is a band rather than a box. It keeps only the bottom edge that separates it from the page, because a box's border and radius would draw a card edge across a full-width strip. It used to have two placements, arriving as a `data-placement` the card keyed on, and the band look was the Design one; the attribute was read before the runtime had written it, so the band never drew.",
|
|
12017
12114
|
display: "flex",
|
|
12018
12115
|
flexDirection: "column",
|
|
12019
12116
|
padding: "8px 12px 10px",
|
|
12020
12117
|
background: "var(--bg-panel)",
|
|
12021
|
-
|
|
12022
|
-
borderRadius: "var(--radius)",
|
|
12118
|
+
borderBottom: "1px solid var(--border)",
|
|
12023
12119
|
fontFamily: "var(--jx-font-sans)",
|
|
12024
|
-
'&[data-placement="pinned"]': {
|
|
12025
|
-
$description: "Design draws the artboards under a pan/zoom transform, so the card is pinned above the surface at 1:1 rather than sitting in the document's own column. Pinned it is a band, not a box: it spans the stage, so the box's border and radius would draw a card edge across a full-width strip.",
|
|
12026
|
-
borderWidth: "0 0 1px",
|
|
12027
|
-
borderRadius: "0"
|
|
12028
|
-
},
|
|
12029
12120
|
'& [part="bar"]': {
|
|
12030
12121
|
display: "flex",
|
|
12031
12122
|
alignItems: "baseline",
|
|
@@ -12051,7 +12142,7 @@ var doc_header_default = {
|
|
|
12051
12142
|
gap: "2px"
|
|
12052
12143
|
},
|
|
12053
12144
|
'& [part="row"]': {
|
|
12054
|
-
$description: "§4.1's standard form row, stacked: the label sits above a control that takes the whole width. `min-width: 0` is what keeps it honest at a narrow stage (§4.6) — a field refuses to shrink below its own min-content without it, and the card is drawn inside a pane that can be half a window wide.",
|
|
12145
|
+
$description: "studio-ui-guidelines.md §4.1's standard form row, stacked: the label sits above a control that takes the whole width. `min-width: 0` is what keeps it honest at a narrow stage (studio-ui-guidelines.md §4.6) — a field refuses to shrink below its own min-content without it, and the card is drawn inside a pane that can be half a window wide.",
|
|
12055
12146
|
display: "flex",
|
|
12056
12147
|
flexDirection: "column",
|
|
12057
12148
|
alignItems: "stretch",
|
|
@@ -12067,7 +12158,7 @@ var doc_header_default = {
|
|
|
12067
12158
|
color: "var(--fg-dim)"
|
|
12068
12159
|
},
|
|
12069
12160
|
'& [part="chip"]': {
|
|
12070
|
-
$description: "§4.2's set dot: a 6px accent dot, drawn only where the value is set here, that clears the value when it is pressed. It is a real `button` — it is pressable, and it is the row's only keyboard route to clearing — stripped of the chrome a button brings with it so the row still reads as a label with a dot beside it.",
|
|
12161
|
+
$description: "studio-ui-guidelines.md §4.2's set dot: a 6px accent dot, drawn only where the value is set here, that clears the value when it is pressed. It is a real `button` — it is pressable, and it is the row's only keyboard route to clearing — stripped of the chrome a button brings with it so the row still reads as a label with a dot beside it.",
|
|
12071
12162
|
width: "6px",
|
|
12072
12163
|
height: "6px",
|
|
12073
12164
|
padding: "0",
|
|
@@ -12745,7 +12836,6 @@ var doc_header_default = {
|
|
|
12745
12836
|
registerSurface("doc-header", doc_header_default);
|
|
12746
12837
|
function project9(scope, view) {
|
|
12747
12838
|
scope.region = view.region;
|
|
12748
|
-
scope.placement = view.placement;
|
|
12749
12839
|
scope.collection = view.collection;
|
|
12750
12840
|
scope.route = view.route;
|
|
12751
12841
|
scope.hasRoute = view.hasRoute;
|
|
@@ -12759,7 +12849,6 @@ function mountDocHeaderSurface(host, view, actions) {
|
|
|
12759
12849
|
...actions,
|
|
12760
12850
|
collection: "Document",
|
|
12761
12851
|
hasRoute: false,
|
|
12762
|
-
placement: "in-column",
|
|
12763
12852
|
rawEntries: [],
|
|
12764
12853
|
rawOpen: false,
|
|
12765
12854
|
rawState: "empty",
|
|
@@ -12914,7 +13003,6 @@ function viewFor(tab, paneId, card) {
|
|
|
12914
13003
|
return {
|
|
12915
13004
|
collection: collection ? collection.name : "Document",
|
|
12916
13005
|
hasRoute: route !== null,
|
|
12917
|
-
placement: card.el.dataset["placement"] === "pinned" ? "pinned" : "in-column",
|
|
12918
13006
|
rawEntries: raw,
|
|
12919
13007
|
rawOpen: _rawOpen.has(tab.id),
|
|
12920
13008
|
rawState: raw.length === 0 ? "empty" : "list",
|
|
@@ -13090,7 +13178,7 @@ function makeActions(card) {
|
|
|
13090
13178
|
}
|
|
13091
13179
|
var _mediaPicker2 = null;
|
|
13092
13180
|
function mediaPicker2() {
|
|
13093
|
-
_mediaPicker2 ??= (__preload("
|
|
13181
|
+
_mediaPicker2 ??= (__preload("nhn6rv4w"), import("./chunks/media-picker-nhn6rv4w.js"));
|
|
13094
13182
|
return _mediaPicker2;
|
|
13095
13183
|
}
|
|
13096
13184
|
// src/surfaces/canvas-stage.json
|
|
@@ -13103,7 +13191,7 @@ var canvas_stage_default = {
|
|
|
13103
13191
|
part: "stage"
|
|
13104
13192
|
},
|
|
13105
13193
|
style: {
|
|
13106
|
-
$description: "`styles/canvas.css`'s `.panzoom-wrap`, `.canvas-panel*`, `.source-*`, `.
|
|
13194
|
+
$description: "`styles/canvas.css`'s `.panzoom-wrap`, `.canvas-panel*`, `.source-*`, `.content-edit-*`, `.edit-width-handle`, `.preview-stage` and `.diff-code-*` rules, keyed on `part`, plus the Document Header card's host. One selector reaches FOREIGN DOM and says so where it stands: `.is-resizing` is written by `canvas/edit-width-drag.ts` onto the column this document draws — the same shape `grid-panel.json` uses for Tabulator's theme. The width handles are `jx-split` elements, which paint their own hover, drag and focus states; this sheet says only where they stand and how wide they are.",
|
|
13107
13195
|
display: "contents",
|
|
13108
13196
|
'& [part="panzoom"]': {
|
|
13109
13197
|
$description: "The transformed surface. `applyTransform` writes `transform` on it imperatively, so only the origin is declared here. `--boards-inset` is the Project Styles bar's clearance: that bar floats over the stage, so the boards start below it.",
|
|
@@ -13193,21 +13281,12 @@ var canvas_stage_default = {
|
|
|
13193
13281
|
left: "50%",
|
|
13194
13282
|
transform: "translateX(-50%)"
|
|
13195
13283
|
},
|
|
13196
|
-
'& [part="doc-header"]
|
|
13197
|
-
$description: "Edit: the card is a block of the
|
|
13198
|
-
display: "block",
|
|
13199
|
-
paddingBottom: "16px"
|
|
13200
|
-
},
|
|
13201
|
-
'& [part="doc-header"][data-placement="pinned"]': {
|
|
13202
|
-
$description: "Design: the artboards are drawn under a pan/zoom transform, and a form drawn at 40% is a picture of a form — so the card sits above the surface at 1:1 and keeps its own scroll. This is the one placement that still needs a cap.",
|
|
13284
|
+
'& [part="doc-header"]': {
|
|
13285
|
+
$description: "Edit only: the card is docked above the page's scroller as a band across the pane at 1:1, so its width is the pane's rather than the breakpoint's, and the page scrolls under it rather than taking it along. It used to be a block of Edit's column, which made it exactly as wide as the column's breakpoint or dragged width and scrolled it off with the first screen of the page. `canvas-render.ts` stacks the stage cell when it draws one — and closes its gap, so the band's own bottom border is where the scroller clips; `flex-shrink: 0` keeps the band whole while the scroller below takes the squeeze. THE CAP IS TWO CAPS, because the band has two ways to eat the page: `40vh` is the tall-pane answer, and `50%` is the short-pane one — a percentage `max-height` resolves against this flex container, the stage cell, whose height is definite (row 2 of the pane's `auto minmax(0, 1fr)` grid), so the page always keeps half the pane however little of it the dock and the bars have left. `40vh` alone is a share of the WINDOW: with the bottom dock open in a 650px window it is most of a 280px stage, and the page was a sliver. No `z-index`: the one it had was for Design, where the band's sibling was a pan/zoom surface whose boards could paint over it. Edit's sibling is the scroller, which clips its own content, and Design draws no card at all. The shadow is the same one-edge idiom as `panel-outline.json`'s verb cluster — it reaches over the scroller's top padding, so the page reads as passing under a docked band rather than ending at a rule. No `display`, so `hidden` on the host still hides it.",
|
|
13203
13286
|
flexShrink: "0",
|
|
13204
|
-
maxHeight: "40vh",
|
|
13205
|
-
overflowY: "auto"
|
|
13206
|
-
|
|
13207
|
-
'& [part="doc-header"][data-placement="pinned"] .doc-header': {
|
|
13208
|
-
$description: "FOREIGN DOM: `.doc-header` is the frontmatter card's own root, mounted into this host by `panels/frontmatter-panel.ts`. Pinned, the card is a band across the stage rather than a card on it, so it loses its own rounding and keeps only the edge that separates it from the boards.",
|
|
13209
|
-
borderWidth: "0 0 1px",
|
|
13210
|
-
borderRadius: "0"
|
|
13287
|
+
maxHeight: "min(40vh, 50%)",
|
|
13288
|
+
overflowY: "auto",
|
|
13289
|
+
boxShadow: "0 8px 8px -8px rgb(0 0 0 / 0.35)"
|
|
13211
13290
|
},
|
|
13212
13291
|
'& [part="edit-canvas"]': {
|
|
13213
13292
|
$description: "The side gutter is LOAD-BEARING, not breathing room: the resize handles hang outside the column's own box — flex siblings with a negative margin, so they take no width from it — and this container clips horizontally, so without it the handles would be unreachable at exactly the width where dragging inward matters most. It must stay at least as wide as `EDIT_CANVAS_GUTTER` in `canvas/edit-width.ts`, which is what the drag's own ceiling subtracts. This box is also the TRACK the two `jx-split`s measure: the first ancestor of a handle that generates a box, which is why the handles are drawn here beside the column rather than inside it — inside, the column would have been the track, and the track cannot be the thing being resized.",
|
|
@@ -13294,7 +13373,7 @@ var canvas_stage_default = {
|
|
|
13294
13373
|
},
|
|
13295
13374
|
children: [
|
|
13296
13375
|
{
|
|
13297
|
-
$description: "What stands over the frame, and only ever one of the three: the Document Header card above
|
|
13376
|
+
$description: "What stands over the frame, and only ever one of the three: the Document Header card docked above Edit's scrolling column, the Compare bar over a comparison, or the Project Styles bar over the catalogue. Each is an empty host — the surface that fills it is mounted by the module that owns it.",
|
|
13298
13377
|
tagName: "div",
|
|
13299
13378
|
attributes: {
|
|
13300
13379
|
part: "lead-slot"
|
|
@@ -13307,10 +13386,10 @@ var canvas_stage_default = {
|
|
|
13307
13386
|
},
|
|
13308
13387
|
cases: {
|
|
13309
13388
|
header: {
|
|
13389
|
+
$description: "The card has ONE place, so the host says nothing about where it is. It used to carry `data-placement`, one of two, and the card read it back as the host was announced — which is before the runtime writes a node's attributes, so the card read no attribute at all and drew `in-column` even where the host said `pinned`.",
|
|
13310
13390
|
tagName: "div",
|
|
13311
13391
|
attributes: {
|
|
13312
|
-
part: "doc-header"
|
|
13313
|
-
"data-placement": "pinned"
|
|
13392
|
+
part: "doc-header"
|
|
13314
13393
|
}
|
|
13315
13394
|
},
|
|
13316
13395
|
toolbar: {
|
|
@@ -13391,28 +13470,6 @@ var canvas_stage_default = {
|
|
|
13391
13470
|
}
|
|
13392
13471
|
},
|
|
13393
13472
|
children: [
|
|
13394
|
-
{
|
|
13395
|
-
$description: "Edit draws the Document Header card INSIDE the document's column, as its first block — the column's first child by contract. The width handles are not in the column at all (they stand beside it, see the frame), so nothing here can get in front of it.",
|
|
13396
|
-
tagName: "div",
|
|
13397
|
-
attributes: {
|
|
13398
|
-
part: "column-header-slot"
|
|
13399
|
-
},
|
|
13400
|
-
style: {
|
|
13401
|
-
display: "contents"
|
|
13402
|
-
},
|
|
13403
|
-
$switch: {
|
|
13404
|
-
$ref: "#/state/columnHeader"
|
|
13405
|
-
},
|
|
13406
|
-
cases: {
|
|
13407
|
-
shown: {
|
|
13408
|
-
tagName: "div",
|
|
13409
|
-
attributes: {
|
|
13410
|
-
part: "doc-header",
|
|
13411
|
-
"data-placement": "in-column"
|
|
13412
|
-
}
|
|
13413
|
-
}
|
|
13414
|
-
}
|
|
13415
|
-
},
|
|
13416
13473
|
{
|
|
13417
13474
|
$prototype: "Array",
|
|
13418
13475
|
items: { $ref: "#/state/panels" },
|
|
@@ -13566,7 +13623,6 @@ function project10(scope, view) {
|
|
|
13566
13623
|
scope.frameVars = view.frameVars;
|
|
13567
13624
|
scope.hug = view.hug;
|
|
13568
13625
|
scope.lead = view.lead;
|
|
13569
|
-
scope.columnHeader = view.columnHeader;
|
|
13570
13626
|
scope.handles = view.handles;
|
|
13571
13627
|
scope.panels = view.panels;
|
|
13572
13628
|
}
|
|
@@ -13574,7 +13630,6 @@ function mountCanvasStage(host, view, actions) {
|
|
|
13574
13630
|
host.replaceChildren();
|
|
13575
13631
|
const boards = new Map;
|
|
13576
13632
|
const scope = reactive({
|
|
13577
|
-
columnHeader: "hidden",
|
|
13578
13633
|
frame: "boards",
|
|
13579
13634
|
framePart: "panzoom",
|
|
13580
13635
|
frameVars: "",
|
|
@@ -13596,7 +13651,7 @@ function mountCanvasStage(host, view, actions) {
|
|
|
13596
13651
|
} else if (part && element instanceof HTMLElement) {
|
|
13597
13652
|
const slot = attrOf(def, "data-slot");
|
|
13598
13653
|
const named = slot === "frame" ? scope.framePart : slot === "inner" ? scope.innerPart : part;
|
|
13599
|
-
actions.host(named, element, attrOf(def, "data-side")
|
|
13654
|
+
actions.host(named, element, attrOf(def, "data-side"));
|
|
13600
13655
|
}
|
|
13601
13656
|
}
|
|
13602
13657
|
}).then((surface) => {
|
|
@@ -13645,19 +13700,6 @@ function resolveValue(target, wanted, modifiers) {
|
|
|
13645
13700
|
}
|
|
13646
13701
|
return bound(target.snap(bounded, modifiers));
|
|
13647
13702
|
}
|
|
13648
|
-
function trackOf(handle, axis) {
|
|
13649
|
-
for (let node = handle.parentElement;node; node = node.parentElement) {
|
|
13650
|
-
if (globalThis.getComputedStyle(node).display === "contents") {
|
|
13651
|
-
continue;
|
|
13652
|
-
}
|
|
13653
|
-
const box = rectOf(node);
|
|
13654
|
-
const length = axis === "x" ? box.width : box.height;
|
|
13655
|
-
if (length > 0) {
|
|
13656
|
-
return length;
|
|
13657
|
-
}
|
|
13658
|
-
}
|
|
13659
|
-
return 0;
|
|
13660
|
-
}
|
|
13661
13703
|
function shareOf(target, size, track) {
|
|
13662
13704
|
const scale = target.scale();
|
|
13663
13705
|
const lead = target.lead?.(track) ?? 0;
|
|
@@ -13672,7 +13714,7 @@ function sizeOf(target, share, track) {
|
|
|
13672
13714
|
}
|
|
13673
13715
|
function bindSplit(handle, target) {
|
|
13674
13716
|
const sync = () => {
|
|
13675
|
-
const track = trackOf(handle, target.axis);
|
|
13717
|
+
const track = trackOf(handle, target.axis === "x");
|
|
13676
13718
|
if (track <= 0) {
|
|
13677
13719
|
return;
|
|
13678
13720
|
}
|
|
@@ -13684,7 +13726,7 @@ function bindSplit(handle, target) {
|
|
|
13684
13726
|
handle.value = shareOf(target, target.read(), track);
|
|
13685
13727
|
};
|
|
13686
13728
|
const onInput = (event) => {
|
|
13687
|
-
const track = trackOf(handle, target.axis);
|
|
13729
|
+
const track = trackOf(handle, target.axis === "x");
|
|
13688
13730
|
if (track <= 0) {
|
|
13689
13731
|
return;
|
|
13690
13732
|
}
|
|
@@ -13953,7 +13995,6 @@ function drawStage(surface, view, entries) {
|
|
|
13953
13995
|
}
|
|
13954
13996
|
function editorStageView(frame) {
|
|
13955
13997
|
return {
|
|
13956
|
-
columnHeader: "hidden",
|
|
13957
13998
|
frame,
|
|
13958
13999
|
framePart: "panzoom",
|
|
13959
14000
|
frameVars: "",
|
|
@@ -13980,7 +14021,7 @@ function disposeSourceCollab(surface) {
|
|
|
13980
14021
|
surface.sourceCollabCleanup = null;
|
|
13981
14022
|
}
|
|
13982
14023
|
async function createSourceCollabBinding(model, editor, ctx) {
|
|
13983
|
-
const { bindMonacoToYText } = await (__preload("
|
|
14024
|
+
const { bindMonacoToYText } = await (__preload("e8d9wzyk"), import("./chunks/monaco-binding-e8d9wzyk.js"));
|
|
13984
14025
|
const unbind = bindMonacoToYText({
|
|
13985
14026
|
awareness: ctx.awareness,
|
|
13986
14027
|
editors: [editor],
|
|
@@ -14023,9 +14064,6 @@ function sideMarks(marks, modified) {
|
|
|
14023
14064
|
path: mark.path
|
|
14024
14065
|
})) ?? null;
|
|
14025
14066
|
}
|
|
14026
|
-
function diffShowsCode(paneId, filePath) {
|
|
14027
|
-
return !canRenderComparison(filePath) || diffViewOf(paneId) === "code";
|
|
14028
|
-
}
|
|
14029
14067
|
async function mountDiffEditor(surface, container, state) {
|
|
14030
14068
|
const { paneId } = surface;
|
|
14031
14069
|
const key = state.filePath;
|
|
@@ -14033,7 +14071,7 @@ async function mountDiffEditor(surface, container, state) {
|
|
|
14033
14071
|
disposeDiffEditor(surface);
|
|
14034
14072
|
}
|
|
14035
14073
|
const monaco = await loadMonaco();
|
|
14036
|
-
if (!mountStillWanted(container, surface.monacoDiffEditor, () => canvasModeOfPane(paneId) === "git-diff" && diffShowsCode(paneId, key))) {
|
|
14074
|
+
if (!mountStillWanted(container, surface.monacoDiffEditor, () => canvasModeOfPane(paneId) === "git-diff" && diffShowsCode(paneId, canRenderComparison(key)))) {
|
|
14037
14075
|
return;
|
|
14038
14076
|
}
|
|
14039
14077
|
const uris = diffModelUrisFor(paneId, key || "document.json");
|
|
@@ -14070,6 +14108,7 @@ function resetCanvasView(surface) {
|
|
|
14070
14108
|
surface.centerObserver.disconnect();
|
|
14071
14109
|
surface.centerObserver = null;
|
|
14072
14110
|
}
|
|
14111
|
+
releaseStageScrollbar(surface);
|
|
14073
14112
|
for (const p of surface.panels) {
|
|
14074
14113
|
p.renderScope?.stop();
|
|
14075
14114
|
p.renderScope = null;
|
|
@@ -14080,6 +14119,7 @@ function resetCanvasView(surface) {
|
|
|
14080
14119
|
canvasWrap.style.padding = "";
|
|
14081
14120
|
canvasWrap.style.alignItems = "";
|
|
14082
14121
|
canvasWrap.style.flexDirection = "";
|
|
14122
|
+
canvasWrap.style.gap = "";
|
|
14083
14123
|
canvasWrap.style.display = "";
|
|
14084
14124
|
canvasWrap.style.overflow = "";
|
|
14085
14125
|
dismissBlockActionBar();
|
|
@@ -14251,7 +14291,7 @@ function renderCanvasImpl(surface) {
|
|
|
14251
14291
|
ui: tab.session.ui
|
|
14252
14292
|
};
|
|
14253
14293
|
const canvasMode = canvasModeOfPane(surface.paneId);
|
|
14254
|
-
const wantsDocHeader =
|
|
14294
|
+
const wantsDocHeader = canvasMode === "edit" && derivationOfPane(surface.paneId)?.kind !== "lens" && hasDocumentHeader(tab);
|
|
14255
14295
|
if (!wantsDocHeader) {
|
|
14256
14296
|
attachDocumentHeaderHost(surface.paneId, null);
|
|
14257
14297
|
}
|
|
@@ -14324,6 +14364,7 @@ function renderCanvasImpl(surface) {
|
|
|
14324
14364
|
surface.centerObserver.disconnect();
|
|
14325
14365
|
surface.centerObserver = null;
|
|
14326
14366
|
}
|
|
14367
|
+
releaseStageScrollbar(surface);
|
|
14327
14368
|
detachGridPanel(surface.paneId);
|
|
14328
14369
|
detachLibraryPane(surface.paneId);
|
|
14329
14370
|
detachEntryPane(surface.paneId);
|
|
@@ -14338,9 +14379,9 @@ function renderCanvasImpl(surface) {
|
|
|
14338
14379
|
canvasWrap.style.padding = "";
|
|
14339
14380
|
canvasWrap.style.alignItems = "";
|
|
14340
14381
|
canvasWrap.style.flexDirection = "";
|
|
14382
|
+
canvasWrap.style.gap = "";
|
|
14341
14383
|
canvasWrap.style.display = "";
|
|
14342
14384
|
canvasWrap.style.overflow = "";
|
|
14343
|
-
canvasWrap.style.overflow = "";
|
|
14344
14385
|
dismissBlockActionBar();
|
|
14345
14386
|
dismissLinkPopover();
|
|
14346
14387
|
dismissContextMenu();
|
|
@@ -14376,7 +14417,6 @@ function renderCanvasImpl(surface) {
|
|
|
14376
14417
|
canvasPanels.push(entry.panel);
|
|
14377
14418
|
const { featureToggles: previewToggles } = S.ui;
|
|
14378
14419
|
drawStage(surface, {
|
|
14379
|
-
columnHeader: "hidden",
|
|
14380
14420
|
frame: "boards",
|
|
14381
14421
|
framePart: "preview-stage",
|
|
14382
14422
|
frameVars: `--preview-w:${previewWidth}px`,
|
|
@@ -14457,7 +14497,7 @@ function renderCanvasImpl(surface) {
|
|
|
14457
14497
|
canvasWrap.style.overflow = "hidden";
|
|
14458
14498
|
}
|
|
14459
14499
|
const renderable = canRenderComparison(gitDiffState.filePath ?? "");
|
|
14460
|
-
if (diffShowsCode(surface.paneId,
|
|
14500
|
+
if (diffShowsCode(surface.paneId, renderable)) {
|
|
14461
14501
|
if (!renderable) {
|
|
14462
14502
|
setDiffChangeMap(surface.paneId, null);
|
|
14463
14503
|
}
|
|
@@ -14479,7 +14519,6 @@ function renderCanvasImpl(surface) {
|
|
|
14479
14519
|
const origEntry = canvasPanelEntry("git-diff-original", "Original", false, panelWidth);
|
|
14480
14520
|
const currEntry = canvasPanelEntry("git-diff-current", "Current", false, panelWidth);
|
|
14481
14521
|
const diffStage = drawStage(surface, {
|
|
14482
|
-
columnHeader: "hidden",
|
|
14483
14522
|
frame: "boards",
|
|
14484
14523
|
framePart: "panzoom",
|
|
14485
14524
|
frameVars: "",
|
|
@@ -14549,22 +14588,26 @@ function renderCanvasImpl(surface) {
|
|
|
14549
14588
|
const rootTag = S.document.tagName;
|
|
14550
14589
|
const isComponentDoc = typeof rootTag === "string" && rootTag.includes("-");
|
|
14551
14590
|
const { featureToggles: editToggles } = S.ui;
|
|
14591
|
+
canvasWrap.style.flexDirection = wantsDocHeader ? "column" : "";
|
|
14592
|
+
canvasWrap.style.alignItems = wantsDocHeader ? "stretch" : "";
|
|
14593
|
+
canvasWrap.style.gap = wantsDocHeader ? "0px" : "";
|
|
14552
14594
|
drawStage(surface, {
|
|
14553
|
-
columnHeader: wantsDocHeader ? "shown" : "hidden",
|
|
14554
14595
|
frame: "boards",
|
|
14555
14596
|
framePart: "edit-canvas",
|
|
14556
14597
|
frameVars: "",
|
|
14557
14598
|
handles: "shown",
|
|
14558
14599
|
hug: isComponentDoc,
|
|
14559
14600
|
innerPart: "edit-column",
|
|
14560
|
-
lead: "none",
|
|
14601
|
+
lead: wantsDocHeader ? "header" : "none",
|
|
14561
14602
|
panels: [entry.item]
|
|
14562
14603
|
}, [entry]).then((stage) => {
|
|
14563
14604
|
if (!stage) {
|
|
14564
14605
|
return;
|
|
14565
14606
|
}
|
|
14566
14607
|
const editColumn = stageHost(surface, "edit-column");
|
|
14567
|
-
|
|
14608
|
+
const scroller = stageHost(surface, "edit-canvas");
|
|
14609
|
+
entry.panel.scrollContainer = scroller;
|
|
14610
|
+
trackStageScrollbar(surface, scroller);
|
|
14568
14611
|
if (editColumn) {
|
|
14569
14612
|
editColumn.style.maxWidth = `${Math.round(columnWidth)}px`;
|
|
14570
14613
|
mountEditWidthHandle(surface, stageHost(surface, "edit-handle:start") ?? undefined, editColumn, -1);
|
|
@@ -14580,9 +14623,6 @@ function renderCanvasImpl(surface) {
|
|
|
14580
14623
|
canvasWrap.style.padding = "0";
|
|
14581
14624
|
canvasWrap.style.overflow = "hidden";
|
|
14582
14625
|
}
|
|
14583
|
-
canvasWrap.style.flexDirection = wantsDocHeader ? "column" : "";
|
|
14584
|
-
canvasWrap.style.alignItems = wantsDocHeader ? "stretch" : "";
|
|
14585
|
-
const designLead = wantsDocHeader ? "header" : "none";
|
|
14586
14626
|
const {
|
|
14587
14627
|
sizeBreakpoints,
|
|
14588
14628
|
featureQueries: _featureQueries,
|
|
@@ -14596,14 +14636,13 @@ function renderCanvasImpl(surface) {
|
|
|
14596
14636
|
const label = hasBaseWidth ? `${mediaDisplayName("--")} (${baseWidth}px)` : null;
|
|
14597
14637
|
const entry = canvasPanelEntry(hasBaseWidth ? "base" : null, label, !hasBaseWidth, hasBaseWidth ? baseWidth : undefined);
|
|
14598
14638
|
drawStage(surface, {
|
|
14599
|
-
columnHeader: "hidden",
|
|
14600
14639
|
frame: "boards",
|
|
14601
14640
|
framePart: "panzoom",
|
|
14602
14641
|
frameVars: "",
|
|
14603
14642
|
handles: "hidden",
|
|
14604
14643
|
hug: false,
|
|
14605
14644
|
innerPart: "boards",
|
|
14606
|
-
lead:
|
|
14645
|
+
lead: "none",
|
|
14607
14646
|
panels: [entry.item]
|
|
14608
14647
|
}, [entry]).then((stage) => {
|
|
14609
14648
|
if (!stage) {
|
|
@@ -14639,14 +14678,13 @@ function renderCanvasImpl(surface) {
|
|
|
14639
14678
|
const panelDefs = chosen.length > 0 ? chosen : allPanelDefs;
|
|
14640
14679
|
const panelEntries = panelDefs.map((def) => canvasPanelEntry(def.name, `${def.displayName} (${def.width}px)`, false, def.width));
|
|
14641
14680
|
const stageDrawn = drawStage(surface, {
|
|
14642
|
-
columnHeader: "hidden",
|
|
14643
14681
|
frame: "boards",
|
|
14644
14682
|
framePart: "panzoom",
|
|
14645
14683
|
frameVars: "",
|
|
14646
14684
|
handles: "hidden",
|
|
14647
14685
|
hug: false,
|
|
14648
14686
|
innerPart: "boards",
|
|
14649
|
-
lead:
|
|
14687
|
+
lead: "none",
|
|
14650
14688
|
panels: panelEntries.map((e) => e.item)
|
|
14651
14689
|
}, panelEntries);
|
|
14652
14690
|
const passGen = surface.renderGeneration;
|
|
@@ -14710,6 +14748,7 @@ setSurfaceTeardown((surface) => {
|
|
|
14710
14748
|
disposeSourceEditor(surface);
|
|
14711
14749
|
surface.centerObserver?.disconnect();
|
|
14712
14750
|
surface.centerObserver = null;
|
|
14751
|
+
releaseStageScrollbar(surface);
|
|
14713
14752
|
surface.panzoomWrap = null;
|
|
14714
14753
|
detachCanvasStage(surface);
|
|
14715
14754
|
});
|
|
@@ -17497,7 +17536,7 @@ async function onAddPackage() {
|
|
|
17497
17536
|
sync2();
|
|
17498
17537
|
try {
|
|
17499
17538
|
await getPlatform2().addPackage(name);
|
|
17500
|
-
const { loadComponentRegistry } = await (__preload("
|
|
17539
|
+
const { loadComponentRegistry } = await (__preload("xm826zjw"), import("./chunks/components-xm826zjw.js"));
|
|
17501
17540
|
await loadComponentRegistry();
|
|
17502
17541
|
context()?.renderLeftPanel();
|
|
17503
17542
|
} catch (error) {
|
|
@@ -17515,7 +17554,7 @@ async function onRemovePackage(pkg) {
|
|
|
17515
17554
|
try {
|
|
17516
17555
|
await getPlatform2().removePackage(pkg);
|
|
17517
17556
|
const updated = removePackageElements(siteElements(), pkg);
|
|
17518
|
-
const { loadComponentRegistry } = await (__preload("
|
|
17557
|
+
const { loadComponentRegistry } = await (__preload("xm826zjw"), import("./chunks/components-xm826zjw.js"));
|
|
17519
17558
|
await loadComponentRegistry();
|
|
17520
17559
|
await updateSiteConfig({ $elements: updated });
|
|
17521
17560
|
context()?.renderLeftPanel();
|
|
@@ -17755,7 +17794,7 @@ var panel_problems_default = {
|
|
|
17755
17794
|
overflowWrap: "anywhere"
|
|
17756
17795
|
},
|
|
17757
17796
|
'& [part="file-path"]': {
|
|
17758
|
-
$description: 'The path is a control, not a label: it opens the file the problem is about, which is the click-through §
|
|
17797
|
+
$description: 'The path is a control, not a label: it opens the file the problem is about, which is the click-through §16.1 asks for. Underlined on hover only — a permanently underlined path in a dense list reads as a paragraph of links. A plain button rather than a `jx-button`, for the reason the status bar\'s items are: what it has to look like is a line of text, and every fill, border and control height the kit draws would have to be taken back off again. Named `file-path` and not `path` because `jx-icon` puts `part="path"` on the `<path>` of every glyph it draws, so `[part="path"]` inside a row selects this button and both SVG paths beside it — a styling rule that reaches into the kit\'s own drawing, and a test or a devtools query that cannot tell them apart.',
|
|
17759
17798
|
alignSelf: "flex-start",
|
|
17760
17799
|
maxWidth: "100%",
|
|
17761
17800
|
overflow: "hidden",
|
|
@@ -17781,7 +17820,8 @@ var panel_problems_default = {
|
|
|
17781
17820
|
}
|
|
17782
17821
|
},
|
|
17783
17822
|
'& [part="detail"]': {
|
|
17784
|
-
$description: "The captured log the progress modal used to own. It scrolls inside the row rather than growing it, so one 400-line `bun install` failure does not become the whole panel.",
|
|
17823
|
+
$description: "The captured log the progress modal used to own. It scrolls inside the row rather than growing it, so one 400-line `bun install` failure does not become the whole panel. It is the one part of a problem row that is selectable against the root's `user-select: none`: the log is what a reader pastes into a bug report, and the row offers no copy action.",
|
|
17824
|
+
userSelect: "text",
|
|
17785
17825
|
maxHeight: "180px",
|
|
17786
17826
|
margin: "var(--jx-space-1) 0 0",
|
|
17787
17827
|
padding: "6px",
|
|
@@ -17813,7 +17853,7 @@ var panel_problems_default = {
|
|
|
17813
17853
|
flex: "none"
|
|
17814
17854
|
},
|
|
17815
17855
|
'& [part="empty"]': {
|
|
17816
|
-
$description: "The teaching empty state, drawn here rather than borrowed from `panels/empty-state.ts`: that helper is a lit template, and a document cannot call one. What is kept is the pattern's rules (§11) — one sentence saying what the region is FOR, then where its content comes from. It offers no action, because the action that fills this region is a failure and nothing here should invite one.",
|
|
17856
|
+
$description: "The teaching empty state, drawn here rather than borrowed from `panels/empty-state.ts`: that helper is a lit template, and a document cannot call one. What is kept is the pattern's rules (studio-ui-guidelines.md §11) — one sentence saying what the region is FOR, then where its content comes from. It offers no action, because the action that fills this region is a failure and nothing here should invite one.",
|
|
17817
17857
|
display: "flex",
|
|
17818
17858
|
flexDirection: "column",
|
|
17819
17859
|
alignItems: "center",
|
|
@@ -18200,7 +18240,7 @@ function groupProblems(records = problems2) {
|
|
|
18200
18240
|
return groups;
|
|
18201
18241
|
}
|
|
18202
18242
|
function openProblemPath(path) {
|
|
18203
|
-
(__preload("
|
|
18243
|
+
(__preload("egsb0ry8"), import("./chunks/files-egsb0ry8.js")).then((m) => m.openFileInTab(path));
|
|
18204
18244
|
}
|
|
18205
18245
|
function projectRow2(record) {
|
|
18206
18246
|
const id = record.action;
|
|
@@ -18304,7 +18344,7 @@ function registerProblemsPanel() {
|
|
|
18304
18344
|
var bottom_dock_default = {
|
|
18305
18345
|
$schema: "https://jxsuite.com/schema/v1",
|
|
18306
18346
|
$id: "StudioBottomDock",
|
|
18307
|
-
$description: "
|
|
18347
|
+
$description: "The Bottom dock's chrome (studio.md §16.3) as a Jx document over the kit: a real `tablist` of the dock's tabs, the button that shuts it, and ONE `tabpanel` for whichever tab is showing. Every tab names that one panel — the dock draws the selected tab's body and no other, so there is exactly one panel element and its `aria-labelledby` follows the selection, which is the same shape `surfaces/git-panel.json` uses for its two sub-tabs. It draws no tab BODY: `panels/bottom-dock.ts` keeps the panel registry, the `when` filter, the badge in each tab's label, the Logic reveal and every tab's `afterRender`, and renders the active record into the one empty `[part=\"dock-body\"]` island this document leaves for it (studio-ui-guidelines.md §9.4).",
|
|
18308
18348
|
tagName: "div",
|
|
18309
18349
|
attributes: {
|
|
18310
18350
|
part: "bottom-dock"
|
|
@@ -19159,6 +19199,28 @@ var SELF_SCROLLING_MODES = new Set([
|
|
|
19159
19199
|
"preview",
|
|
19160
19200
|
"settings"
|
|
19161
19201
|
]);
|
|
19202
|
+
function wheelBelongsInside(e, stage, pageScroller) {
|
|
19203
|
+
const { target } = e;
|
|
19204
|
+
let el = target instanceof Element ? target : target?.parentElement ?? null;
|
|
19205
|
+
while (el && el !== stage) {
|
|
19206
|
+
if (el instanceof HTMLElement && el !== pageScroller && canScrollBy(el, e.deltaX, e.deltaY)) {
|
|
19207
|
+
return true;
|
|
19208
|
+
}
|
|
19209
|
+
el = el.parentElement;
|
|
19210
|
+
}
|
|
19211
|
+
return false;
|
|
19212
|
+
}
|
|
19213
|
+
function canScrollBy(el, dx, dy) {
|
|
19214
|
+
const downRoom = dy > 0 && el.scrollTop < el.scrollHeight - el.clientHeight - 1;
|
|
19215
|
+
const upRoom = dy < 0 && el.scrollTop > 0;
|
|
19216
|
+
const acrossRoom = dx > 0 && el.scrollLeft < el.scrollWidth - el.clientWidth - 1 || dx < 0 && el.scrollLeft > 0;
|
|
19217
|
+
if (!downRoom && !upRoom && !acrossRoom) {
|
|
19218
|
+
return false;
|
|
19219
|
+
}
|
|
19220
|
+
const style = getComputedStyle(el);
|
|
19221
|
+
const scrolls = (overflow) => overflow === "auto" || overflow === "scroll" || overflow === "overlay";
|
|
19222
|
+
return (downRoom || upRoom ? scrolls(style.overflowY) : false) || (acrossRoom ? scrolls(style.overflowX) : false);
|
|
19223
|
+
}
|
|
19162
19224
|
var _stageContext = null;
|
|
19163
19225
|
function installStageGestures(surface) {
|
|
19164
19226
|
const canvasWrap = surface.wrap;
|
|
@@ -19176,7 +19238,7 @@ function installStageGestures(surface) {
|
|
|
19176
19238
|
return;
|
|
19177
19239
|
}
|
|
19178
19240
|
const sc = canvasWrap.querySelector('[part="edit-canvas"]');
|
|
19179
|
-
if (sc) {
|
|
19241
|
+
if (sc && !wheelBelongsInside(e, canvasWrap, sc)) {
|
|
19180
19242
|
e.preventDefault();
|
|
19181
19243
|
sc.scrollTop += e.deltaY;
|
|
19182
19244
|
sc.scrollLeft += e.deltaX;
|
|
@@ -19262,7 +19324,7 @@ function stageless() {
|
|
|
19262
19324
|
var pane_context_default = {
|
|
19263
19325
|
$schema: "https://jxsuite.com/schema/v1",
|
|
19264
19326
|
$id: "StudioPaneContext",
|
|
19265
|
-
$description: "The pane's own chrome —
|
|
19327
|
+
$description: "The pane's own chrome — the pane context bar and the floating zoom pod, one mount per pane. It replaces `#tab-bar`, a 28px band styled identically to the tab strip above it that held five unrelated axes with no labels on any of them; the three axes here each say what they are, and the label is what stops the band re-accreting into an anonymous strip of chips. `panels/pane-context.ts` keeps every decision — which editor kinds a document declares, which canvas views it has, whether preview is a flag over them, what a lens may not write, which pane's stage a zoom verb lands on, and what a route param's candidate values are — and hands this document one projection of words, booleans and keyed rows. The bar and the banner share `[part=\"band\"]` so the adapter can MEASURE what the stage must clear: a banner's height is its wrapped text, and only layout knows how many lines that is.",
|
|
19266
19328
|
tagName: "div",
|
|
19267
19329
|
attributes: {
|
|
19268
19330
|
part: "chrome"
|
|
@@ -19320,10 +19382,14 @@ var pane_context_default = {
|
|
|
19320
19382
|
flexShrink: "0"
|
|
19321
19383
|
},
|
|
19322
19384
|
'& [part="preview-toggle"]': {
|
|
19323
|
-
$description: "Not a third radio, and it must not read as one: it says the canvas is inert. The margin separates the two axes the eye is being asked to read as separate; the accent fill is the kit's `emphasized`, which is what deleted the four Spectrum custom properties this rule used to carry.",
|
|
19385
|
+
$description: "Not a third radio, and it must not read as one: it says the canvas is inert. The margin separates the two axes the eye is being asked to read as separate; the accent fill is the kit's `emphasized`, which is what deleted the four Spectrum custom properties this rule used to carry. The toggle is `quiet: false`, so it rests as a framed button of its own beside the framed View switch, as the Spectrum button did; quiet, it read as a fourth loose word once the switch beside it drew its frame.",
|
|
19324
19386
|
flexShrink: "0",
|
|
19325
19387
|
marginInlineStart: "6px"
|
|
19326
19388
|
},
|
|
19389
|
+
'& jx-action-button[part="preview-toggle"] > [part="control"]': {
|
|
19390
|
+
$description: "The text padding the View switch's segments get from their group, which this toggle stands outside: the small size's 2px is for a glyph, and 6px is what the group gives a text segment. (0,3,1) outranks the kit's small padding at (0,3,0).",
|
|
19391
|
+
paddingInline: "6px"
|
|
19392
|
+
},
|
|
19327
19393
|
'& [part="context-trigger"]': {
|
|
19328
19394
|
fontVariantNumeric: "tabular-nums"
|
|
19329
19395
|
},
|
|
@@ -19349,29 +19415,47 @@ var pane_context_default = {
|
|
|
19349
19415
|
fontSize: "var(--jx-text-md)"
|
|
19350
19416
|
},
|
|
19351
19417
|
'& [part="pod"]': {
|
|
19352
|
-
$description: "Bottom-right of the pane, over the stage — a viewport control, which is not a document axis and so never belonged in a band beside one.",
|
|
19418
|
+
$description: "Bottom-right of the pane, over the stage — a viewport control, which is not a document axis and so never belonged in a band beside one. It is a ROW of two controls and draws no box of its own: a 1px border and a 6px radius round 2px/4px of padding framed the zoom group's own frame and the Fit select's own frame, a frame inside a frame and busier than any other group in the chrome. Each control draws itself and takes `--pod-lift` to stand off the document under it, which is what the pod's fill and border used to do for both at once, and the gap between them says they are two controls rather than one strip with a seam. The right inset ADDS `--stage-scrollbar-w` because the chrome layer spans the whole cell, scrollbar gutter included: a fixed 12px put the pod 3px into Edit's 15px track, painting over it and swallowing its clicks. CSS cannot measure a scrollbar and this document cannot see the scroller, so `canvas/stage-scrollbar.ts` measures the stage's own and writes the width on the cell, where this reads it by cascade. It is 0 wherever the stage does not scroll or the scrollbar is an overlay. Physical right on purpose: Studio's chrome is never mirrored.",
|
|
19419
|
+
"--pod-lift": "0 2px 8px rgb(0 0 0 / 25%)",
|
|
19353
19420
|
alignSelf: "flex-end",
|
|
19421
|
+
display: "flex",
|
|
19422
|
+
alignItems: "center",
|
|
19423
|
+
gap: "var(--jx-space-2)",
|
|
19354
19424
|
pointerEvents: "auto",
|
|
19355
|
-
margin: "0 var(--jx-space-4) var(--jx-space-4) 0"
|
|
19356
|
-
|
|
19357
|
-
|
|
19358
|
-
|
|
19359
|
-
|
|
19360
|
-
boxShadow: "
|
|
19425
|
+
margin: "0 calc(var(--jx-space-4) + var(--stage-scrollbar-w, 0px)) var(--jx-space-4) 0"
|
|
19426
|
+
},
|
|
19427
|
+
'& [part="zoom-group"]': {
|
|
19428
|
+
$description: "The segmented frame floats on its own now that the pod draws no box around it. The radius is the one the first and last segments already draw, so the shadow follows the shape of the strip instead of a rectangle around it — the host's box IS that strip, since the group has no padding and each member's -1px pulls its own host in by a pixel.",
|
|
19429
|
+
borderRadius: "var(--jx-radius-sm)",
|
|
19430
|
+
boxShadow: "var(--pod-lift)"
|
|
19431
|
+
},
|
|
19432
|
+
'& [part="pod"] > [part="zoom-group"][data-compact] > jx-action-button > [part="control"]': {
|
|
19433
|
+
$description: "One resting ink across the pod. A compact group rests its unselected members on `--jx-fg-dim` and brightens the chosen one, which is how a segmented control says which segment is on — and nothing in this pod is ever chosen: the three are a viewport verb, a readout and a verb. So the dim ink said nothing here and made `100%` read as disabled beside the Fit select's value, which is the same kind of readout at `--jx-fg`. (0,5,1) over the group's own resting rule at (0,4,1); the group's hover is (0,6,1) and still wins, which now leaves it only the wash.",
|
|
19434
|
+
color: "var(--fg)"
|
|
19361
19435
|
},
|
|
19362
19436
|
'& [part="zoom-label"]': {
|
|
19363
|
-
|
|
19364
|
-
|
|
19437
|
+
$description: "A floor under the percentage, so the pod does not shift as the value changes length. The width is the host's and the face is the control's, below: the control sets its own font and sits inside the host at its content width, so this rule used to carry a mono face nothing inherited and to open a 6px hole in the segmented frame before the `+` segment (measured: a 48px host round a 41.8px control, drawn in Inter).",
|
|
19438
|
+
minWidth: "48px"
|
|
19439
|
+
},
|
|
19440
|
+
'& [part="zoom-label"] > [part="control"]': {
|
|
19441
|
+
$description: "The control fills its 48px host, closing the frame, and draws the percentage in the mono face with tabular digits so a changing value does not jitter. (0,3,0) outranks the kit control's own face at (0,2,0).",
|
|
19442
|
+
flex: "1",
|
|
19443
|
+
fontFamily: "var(--font-mono)",
|
|
19444
|
+
fontVariantNumeric: "tabular-nums"
|
|
19365
19445
|
},
|
|
19366
19446
|
'& [part="fit"]': {
|
|
19367
19447
|
maxWidth: "120px"
|
|
19368
19448
|
},
|
|
19449
|
+
'& [part="fit"] > [part="control"]': {
|
|
19450
|
+
$description: "The pod's second control lifts off the document the way the first does, on the frame the kit's select already draws. On the control rather than the host, because the host is a column whose error region and help slot are the select's neighbours: the shadow has to follow the box that is painted.",
|
|
19451
|
+
boxShadow: "var(--pod-lift)"
|
|
19452
|
+
},
|
|
19369
19453
|
'& [part="popover"]': {
|
|
19370
19454
|
$description: "Both panels take the pointer that `.pane-chrome` withholds. A popover is in the top layer while it shows, which changes where it PAINTS and not where it is in the tree, so it inherits `pointer-events: none` from the chrome layer exactly as the band does.",
|
|
19371
19455
|
pointerEvents: "auto"
|
|
19372
19456
|
},
|
|
19373
19457
|
'& [part="ctx"]': {
|
|
19374
|
-
$description: "One column of labelled groups over a footer that leaves for the definition site. This control only SELECTS (§2
|
|
19458
|
+
$description: "One column of labelled groups over a footer that leaves for the definition site. This control only SELECTS; the definitions live in Project Settings › Contexts (§6.2), and nothing in it defines a breakpoint, a scheme or a feature query.",
|
|
19375
19459
|
display: "flex",
|
|
19376
19460
|
flexDirection: "column",
|
|
19377
19461
|
gap: "10px",
|
|
@@ -19390,11 +19474,15 @@ var pane_context_default = {
|
|
|
19390
19474
|
letterSpacing: "0.04em",
|
|
19391
19475
|
color: "var(--fg-dim)"
|
|
19392
19476
|
},
|
|
19393
|
-
'& [part="sizes"]': {
|
|
19394
|
-
|
|
19477
|
+
'& [part="sizes"], & [part="features"]': {
|
|
19478
|
+
$description: "The two groups that WRAP, and the reason neither is a segmented control. A breakpoint or feature list is as long as the project makes it and this popover is a 240–340px column, so both fold onto a second line in any project with more than a few; a compact group cannot draw that — its seam is written in tree order, no selector can find the ends of a flex line, and it therefore refuses to wrap and would overflow the column instead (ui.md §5.4). Chips instead: each member draws its own rounded frame (`quiet: false`, since the group no longer frames it) and the chosen one takes the accent edge below. The gap is --jx-space-2 rather than the group's own 2px, which puts every small chip's hit area at the 24px floor on both axes instead of the 22px two overlapping extensions leave (§4.3).",
|
|
19479
|
+
flexWrap: "wrap",
|
|
19480
|
+
gap: "var(--jx-space-2)"
|
|
19395
19481
|
},
|
|
19396
|
-
'& [part="features"]': {
|
|
19397
|
-
|
|
19482
|
+
'& [part="sizes-item"][data-selected] > [part="control"], & [part="features-item"][data-selected] > [part="control"]': {
|
|
19483
|
+
$description: "A chosen chip reads the way a chosen segment does: the accent tint with an accent EDGE. A member's own selected state is a 15% tint over its neutral border, which among framed neighbours reads as a faint wash rather than as the one that is on — the measurement that gave the compact group its selected rule, restated here at the one place the group does not draw it. (0,4,0) over the member's own (0,3,0).",
|
|
19484
|
+
borderColor: "var(--jx-accent)",
|
|
19485
|
+
background: "var(--jx-accent-20)"
|
|
19398
19486
|
},
|
|
19399
19487
|
'& [part="fields"]': {
|
|
19400
19488
|
$description: "The fields, stacked. They were a row on the bar capped at 140px; in the popover each takes the full column so a route value or a JSON test prop is readable while you type it.",
|
|
@@ -19722,6 +19810,7 @@ var pane_context_default = {
|
|
|
19722
19810
|
},
|
|
19723
19811
|
$props: {
|
|
19724
19812
|
size: "sm",
|
|
19813
|
+
quiet: false,
|
|
19725
19814
|
toggles: true,
|
|
19726
19815
|
emphasized: true,
|
|
19727
19816
|
selected: {
|
|
@@ -20060,55 +20149,55 @@ var pane_context_default = {
|
|
|
20060
20149
|
}
|
|
20061
20150
|
]
|
|
20062
20151
|
}
|
|
20063
|
-
}
|
|
20064
|
-
|
|
20065
|
-
|
|
20152
|
+
}
|
|
20153
|
+
]
|
|
20154
|
+
},
|
|
20155
|
+
{
|
|
20156
|
+
tagName: "div",
|
|
20157
|
+
attributes: {
|
|
20158
|
+
part: "fit-slot",
|
|
20159
|
+
role: "none"
|
|
20160
|
+
},
|
|
20161
|
+
style: {
|
|
20162
|
+
display: "contents"
|
|
20163
|
+
},
|
|
20164
|
+
$switch: {
|
|
20165
|
+
$ref: "#/state/fitState"
|
|
20166
|
+
},
|
|
20167
|
+
cases: {
|
|
20168
|
+
shown: {
|
|
20169
|
+
tagName: "jx-select",
|
|
20066
20170
|
attributes: {
|
|
20067
|
-
part: "fit
|
|
20068
|
-
role: "none"
|
|
20069
|
-
},
|
|
20070
|
-
style: {
|
|
20071
|
-
display: "contents"
|
|
20171
|
+
part: "fit"
|
|
20072
20172
|
},
|
|
20073
|
-
$
|
|
20074
|
-
|
|
20173
|
+
$props: {
|
|
20174
|
+
label: "Fit",
|
|
20175
|
+
size: "sm",
|
|
20176
|
+
options: {
|
|
20177
|
+
$ref: "#/state/fitOptions"
|
|
20178
|
+
},
|
|
20179
|
+
value: {
|
|
20180
|
+
$ref: "#/state/fitValue"
|
|
20181
|
+
}
|
|
20075
20182
|
},
|
|
20076
|
-
|
|
20077
|
-
|
|
20078
|
-
|
|
20079
|
-
|
|
20080
|
-
|
|
20081
|
-
|
|
20082
|
-
|
|
20083
|
-
label: "Fit",
|
|
20084
|
-
size: "sm",
|
|
20085
|
-
options: {
|
|
20086
|
-
$ref: "#/state/fitOptions"
|
|
20183
|
+
onchange: {
|
|
20184
|
+
$prototype: "Function",
|
|
20185
|
+
body: [
|
|
20186
|
+
{
|
|
20187
|
+
operator: "call",
|
|
20188
|
+
target: {
|
|
20189
|
+
$ref: "#/state/chooseFit"
|
|
20087
20190
|
},
|
|
20088
|
-
value:
|
|
20089
|
-
$ref: "#/state/fitValue"
|
|
20090
|
-
}
|
|
20091
|
-
},
|
|
20092
|
-
onchange: {
|
|
20093
|
-
$prototype: "Function",
|
|
20094
|
-
body: [
|
|
20191
|
+
value: [
|
|
20095
20192
|
{
|
|
20096
|
-
|
|
20097
|
-
target: {
|
|
20098
|
-
$ref: "#/state/chooseFit"
|
|
20099
|
-
},
|
|
20100
|
-
value: [
|
|
20101
|
-
{
|
|
20102
|
-
$ref: "event#/target/value"
|
|
20103
|
-
}
|
|
20104
|
-
]
|
|
20193
|
+
$ref: "event#/target/value"
|
|
20105
20194
|
}
|
|
20106
20195
|
]
|
|
20107
20196
|
}
|
|
20108
|
-
|
|
20197
|
+
]
|
|
20109
20198
|
}
|
|
20110
20199
|
}
|
|
20111
|
-
|
|
20200
|
+
}
|
|
20112
20201
|
}
|
|
20113
20202
|
]
|
|
20114
20203
|
}
|
|
@@ -20159,7 +20248,7 @@ var pane_context_default = {
|
|
|
20159
20248
|
},
|
|
20160
20249
|
$props: {
|
|
20161
20250
|
selects: "single",
|
|
20162
|
-
compact:
|
|
20251
|
+
compact: false,
|
|
20163
20252
|
label: "Size"
|
|
20164
20253
|
},
|
|
20165
20254
|
children: [
|
|
@@ -20179,6 +20268,7 @@ var pane_context_default = {
|
|
|
20179
20268
|
},
|
|
20180
20269
|
$props: {
|
|
20181
20270
|
size: "sm",
|
|
20271
|
+
quiet: false,
|
|
20182
20272
|
checked: {
|
|
20183
20273
|
$ref: "$map/item/checked"
|
|
20184
20274
|
},
|
|
@@ -20431,7 +20521,7 @@ var pane_context_default = {
|
|
|
20431
20521
|
},
|
|
20432
20522
|
$props: {
|
|
20433
20523
|
selects: "multiple",
|
|
20434
|
-
compact:
|
|
20524
|
+
compact: false,
|
|
20435
20525
|
label: "Features"
|
|
20436
20526
|
},
|
|
20437
20527
|
children: [
|
|
@@ -20451,6 +20541,7 @@ var pane_context_default = {
|
|
|
20451
20541
|
},
|
|
20452
20542
|
$props: {
|
|
20453
20543
|
size: "sm",
|
|
20544
|
+
quiet: false,
|
|
20454
20545
|
toggles: true,
|
|
20455
20546
|
selected: {
|
|
20456
20547
|
$ref: "$map/item/selected"
|
|
@@ -21383,6 +21474,11 @@ function fitChoiceValue(fit) {
|
|
|
21383
21474
|
return fit;
|
|
21384
21475
|
}
|
|
21385
21476
|
var STAGE_ZOOM_MODES = new Set(["design", "stylebook", "git-diff"]);
|
|
21477
|
+
function comparisonPathOf(paneId) {
|
|
21478
|
+
const derived = derivationOfPane(paneId);
|
|
21479
|
+
const state = derived?.kind === "lens" && derived.preset === "diff" ? derived.diff : shell.git.diffState;
|
|
21480
|
+
return state?.filePath ?? "";
|
|
21481
|
+
}
|
|
21386
21482
|
function podFor(tab, paneId) {
|
|
21387
21483
|
const mode = canvasModeOfPane(paneId);
|
|
21388
21484
|
const surface = surfaceForPane(paneId);
|
|
@@ -21393,6 +21489,9 @@ function podFor(tab, paneId) {
|
|
|
21393
21489
|
if (!STAGE_ZOOM_MODES.has(mode)) {
|
|
21394
21490
|
return { podState: "hidden" };
|
|
21395
21491
|
}
|
|
21492
|
+
if (mode === "git-diff" && diffShowsCode(paneId, canRenderComparison(comparisonPathOf(paneId)))) {
|
|
21493
|
+
return { podState: "hidden" };
|
|
21494
|
+
}
|
|
21396
21495
|
const zoom = stageZoom(surface);
|
|
21397
21496
|
return {
|
|
21398
21497
|
fitOptions: FIT_CHOICES.map(({ label, value }) => ({ label, value })),
|
|
@@ -21989,130 +22088,15 @@ async function applyFileDrop(tab, files, hit, preview) {
|
|
|
21989
22088
|
}
|
|
21990
22089
|
}
|
|
21991
22090
|
}
|
|
21992
|
-
|
|
21993
|
-
// ../ai/src/tools.ts
|
|
21994
|
-
function toolSuccess(data, summary) {
|
|
21995
|
-
return { success: true, data, summary };
|
|
21996
|
-
}
|
|
21997
|
-
function toolError(error) {
|
|
21998
|
-
return { success: false, error };
|
|
21999
|
-
}
|
|
22000
|
-
function createToolDefinition({
|
|
22001
|
-
name,
|
|
22002
|
-
description,
|
|
22003
|
-
parameters,
|
|
22004
|
-
strict = true,
|
|
22005
|
-
llmStrict = false,
|
|
22006
|
-
execute
|
|
22007
|
-
}) {
|
|
22008
|
-
return { name, description, parameters, strict, llmStrict, execute };
|
|
22009
|
-
}
|
|
22010
|
-
function createToolRegistry() {
|
|
22011
|
-
const _tools = new Map;
|
|
22012
|
-
return {
|
|
22013
|
-
register(tool) {
|
|
22014
|
-
if (_tools.has(tool.name)) {
|
|
22015
|
-
console.warn(`Tool "${tool.name}" is being re-registered.`);
|
|
22016
|
-
}
|
|
22017
|
-
_tools.set(tool.name, tool);
|
|
22018
|
-
},
|
|
22019
|
-
list() {
|
|
22020
|
-
return [..._tools.values()];
|
|
22021
|
-
},
|
|
22022
|
-
listForLLM() {
|
|
22023
|
-
return [..._tools.values()].map((t) => ({
|
|
22024
|
-
type: "function",
|
|
22025
|
-
function: {
|
|
22026
|
-
name: t.name,
|
|
22027
|
-
description: t.description,
|
|
22028
|
-
parameters: t.parameters,
|
|
22029
|
-
...t.llmStrict === true ? { strict: true } : {}
|
|
22030
|
-
}
|
|
22031
|
-
}));
|
|
22032
|
-
},
|
|
22033
|
-
getDefinition(toolName) {
|
|
22034
|
-
return _tools.get(toolName);
|
|
22035
|
-
},
|
|
22036
|
-
validate(toolName, args) {
|
|
22037
|
-
const tool = _tools.get(toolName);
|
|
22038
|
-
if (!tool) {
|
|
22039
|
-
return { valid: false, errors: [`Unknown tool: "${toolName}"`] };
|
|
22040
|
-
}
|
|
22041
|
-
if (!tool.strict) {
|
|
22042
|
-
return { valid: true };
|
|
22043
|
-
}
|
|
22044
|
-
const schema = tool.parameters;
|
|
22045
|
-
if (!schema || !schema.properties) {
|
|
22046
|
-
return { valid: true };
|
|
22047
|
-
}
|
|
22048
|
-
const errors = [];
|
|
22049
|
-
const props = schema.properties;
|
|
22050
|
-
const required = schema.required || [];
|
|
22051
|
-
const argRecord = args;
|
|
22052
|
-
for (const key of required) {
|
|
22053
|
-
if (!(key in args) || argRecord[key] === undefined || argRecord[key] === null) {
|
|
22054
|
-
errors.push(`Missing required argument: "${key}"`);
|
|
22055
|
-
}
|
|
22056
|
-
}
|
|
22057
|
-
for (const [key, value] of Object.entries(argRecord)) {
|
|
22058
|
-
const propSchema = props[key];
|
|
22059
|
-
if (!propSchema) {
|
|
22060
|
-
continue;
|
|
22061
|
-
}
|
|
22062
|
-
const expectedType = propSchema.type;
|
|
22063
|
-
if (!expectedType) {
|
|
22064
|
-
continue;
|
|
22065
|
-
}
|
|
22066
|
-
const actualType = Array.isArray(value) ? "array" : typeof value;
|
|
22067
|
-
if (expectedType === "number" || expectedType === "integer") {
|
|
22068
|
-
if (actualType === "number") {
|
|
22069
|
-
continue;
|
|
22070
|
-
}
|
|
22071
|
-
if (actualType === "string") {
|
|
22072
|
-
if (!Number.isNaN(Number(value))) {
|
|
22073
|
-
continue;
|
|
22074
|
-
}
|
|
22075
|
-
errors.push(`Argument "${key}" should be a number, got non-numeric string "${value}"`);
|
|
22076
|
-
continue;
|
|
22077
|
-
}
|
|
22078
|
-
errors.push(`Argument "${key}" should be a number, got ${actualType}`);
|
|
22079
|
-
continue;
|
|
22080
|
-
}
|
|
22081
|
-
if (expectedType === "string" && actualType !== "string") {
|
|
22082
|
-
errors.push(`Argument "${key}" should be a string, got ${actualType}`);
|
|
22083
|
-
} else if (expectedType === "boolean" && actualType !== "boolean") {
|
|
22084
|
-
errors.push(`Argument "${key}" should be a boolean, got ${actualType}`);
|
|
22085
|
-
} else if (expectedType === "array" && actualType !== "array") {
|
|
22086
|
-
errors.push(`Argument "${key}" should be an array, got ${actualType}`);
|
|
22087
|
-
} else if (expectedType === "object" && actualType !== "object") {
|
|
22088
|
-
errors.push(`Argument "${key}" should be an object, got ${actualType}`);
|
|
22089
|
-
}
|
|
22090
|
-
}
|
|
22091
|
-
return errors.length > 0 ? { valid: false, errors } : { valid: true };
|
|
22092
|
-
},
|
|
22093
|
-
async execute(toolName, args) {
|
|
22094
|
-
const tool = _tools.get(toolName);
|
|
22095
|
-
if (!tool) {
|
|
22096
|
-
return toolError(`Unknown tool: "${toolName}"`);
|
|
22097
|
-
}
|
|
22098
|
-
const validation = this.validate(toolName, args);
|
|
22099
|
-
if (!validation.valid) {
|
|
22100
|
-
return toolError(`Validation failed: ${(validation.errors || []).join("; ")}`);
|
|
22101
|
-
}
|
|
22102
|
-
try {
|
|
22103
|
-
return await tool.execute(args);
|
|
22104
|
-
} catch (error) {
|
|
22105
|
-
return toolError(`Tool "${toolName}" execution error: ${error.message}`);
|
|
22106
|
-
}
|
|
22107
|
-
}
|
|
22108
|
-
};
|
|
22109
|
-
}
|
|
22110
22091
|
// ../ai/src/chat-state.ts
|
|
22111
22092
|
var _idCounter = 0;
|
|
22112
22093
|
function uid() {
|
|
22113
22094
|
_idCounter += 1;
|
|
22114
22095
|
return `msg_${Date.now()}_${_idCounter}`;
|
|
22115
22096
|
}
|
|
22097
|
+
function isCount(value) {
|
|
22098
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
22099
|
+
}
|
|
22116
22100
|
function createChatState(opts = {}) {
|
|
22117
22101
|
const model = opts.model || "gpt-4o";
|
|
22118
22102
|
const store = reactive({
|
|
@@ -22123,7 +22107,8 @@ function createChatState(opts = {}) {
|
|
|
22123
22107
|
error: null,
|
|
22124
22108
|
model,
|
|
22125
22109
|
tokenCount: 0,
|
|
22126
|
-
contextWarning: false
|
|
22110
|
+
contextWarning: false,
|
|
22111
|
+
usage: null
|
|
22127
22112
|
});
|
|
22128
22113
|
let _streamingMessage = null;
|
|
22129
22114
|
function sendMessage(text) {
|
|
@@ -22204,15 +22189,14 @@ function createChatState(opts = {}) {
|
|
|
22204
22189
|
}
|
|
22205
22190
|
function appendToolCallEnd(_id) {}
|
|
22206
22191
|
function appendToolResult(id, result) {
|
|
22207
|
-
|
|
22208
|
-
|
|
22209
|
-
|
|
22192
|
+
let index = store.messages.length - 1;
|
|
22193
|
+
while (index >= 0 && store.messages[index].role === "tool") {
|
|
22194
|
+
index -= 1;
|
|
22210
22195
|
}
|
|
22211
|
-
|
|
22212
|
-
|
|
22213
|
-
|
|
22214
|
-
|
|
22215
|
-
}
|
|
22196
|
+
const request = store.messages[index];
|
|
22197
|
+
const record = request?.role === "assistant" ? request.toolCalls?.find((t) => t.id === id) : null;
|
|
22198
|
+
if (record) {
|
|
22199
|
+
record.result = result;
|
|
22216
22200
|
}
|
|
22217
22201
|
}
|
|
22218
22202
|
function finishStream(_stopReason) {
|
|
@@ -22225,6 +22209,7 @@ function createChatState(opts = {}) {
|
|
|
22225
22209
|
store.status = "error";
|
|
22226
22210
|
store.error = message;
|
|
22227
22211
|
store.streamingContent = "";
|
|
22212
|
+
store.pendingToolCalls = [];
|
|
22228
22213
|
if (_streamingMessage) {
|
|
22229
22214
|
const idx = store.messages.lastIndexOf(_streamingMessage);
|
|
22230
22215
|
if (idx !== -1) {
|
|
@@ -22253,6 +22238,7 @@ function createChatState(opts = {}) {
|
|
|
22253
22238
|
store.pendingToolCalls = [];
|
|
22254
22239
|
store.error = null;
|
|
22255
22240
|
store.contextWarning = false;
|
|
22241
|
+
store.usage = null;
|
|
22256
22242
|
_streamingMessage = null;
|
|
22257
22243
|
}
|
|
22258
22244
|
function retryLast() {
|
|
@@ -22271,6 +22257,27 @@ function createChatState(opts = {}) {
|
|
|
22271
22257
|
function setTokenCount(count) {
|
|
22272
22258
|
store.tokenCount = count;
|
|
22273
22259
|
}
|
|
22260
|
+
function recordUsage(usage, extra = {}) {
|
|
22261
|
+
if (!isCount(usage.inputTokens) || !isCount(usage.outputTokens)) {
|
|
22262
|
+
return;
|
|
22263
|
+
}
|
|
22264
|
+
const { type: _type, ...counts } = usage;
|
|
22265
|
+
const counted = _streamingMessage ?? store.messages.at(-1);
|
|
22266
|
+
const replayed = Boolean(counted?.reasoningContent && (counted.content || (counted.toolCalls?.length ?? 0) > 0));
|
|
22267
|
+
const unreplayed = replayed || !isCount(usage.reasoningTokens) ? 0 : usage.reasoningTokens;
|
|
22268
|
+
const contextTokens = Math.max(0, usage.inputTokens + usage.outputTokens - unreplayed);
|
|
22269
|
+
store.usage = {
|
|
22270
|
+
...counts,
|
|
22271
|
+
contextTokens,
|
|
22272
|
+
lastMessageId: store.messages.at(-1)?.id ?? "",
|
|
22273
|
+
messageCount: store.messages.length,
|
|
22274
|
+
...extra.systemTokens === undefined ? {} : { systemTokens: extra.systemTokens }
|
|
22275
|
+
};
|
|
22276
|
+
store.tokenCount = contextTokens;
|
|
22277
|
+
}
|
|
22278
|
+
function clearUsage() {
|
|
22279
|
+
store.usage = null;
|
|
22280
|
+
}
|
|
22274
22281
|
function setContextWarning(warning) {
|
|
22275
22282
|
store.contextWarning = warning;
|
|
22276
22283
|
}
|
|
@@ -22332,6 +22339,8 @@ function createChatState(opts = {}) {
|
|
|
22332
22339
|
retryLast,
|
|
22333
22340
|
setModel,
|
|
22334
22341
|
setTokenCount,
|
|
22342
|
+
recordUsage,
|
|
22343
|
+
clearUsage,
|
|
22335
22344
|
setContextWarning,
|
|
22336
22345
|
toMessagesArray
|
|
22337
22346
|
});
|
|
@@ -22368,6 +22377,7 @@ function createProxyStreamingClient({
|
|
|
22368
22377
|
apiKey,
|
|
22369
22378
|
baseUrl
|
|
22370
22379
|
}) {
|
|
22380
|
+
let resolvedUrl = null;
|
|
22371
22381
|
async function* streamChat(messages, tools, systemPrompt, signal) {
|
|
22372
22382
|
const headers = { "Content-Type": "application/json" };
|
|
22373
22383
|
if (apiKey) {
|
|
@@ -22376,9 +22386,24 @@ function createProxyStreamingClient({
|
|
|
22376
22386
|
if (baseUrl) {
|
|
22377
22387
|
headers["X-Api-Base-URL"] = baseUrl;
|
|
22378
22388
|
}
|
|
22389
|
+
resolvedUrl ??= Promise.resolve(typeof chatUrl === "function" ? chatUrl() : chatUrl);
|
|
22390
|
+
let url;
|
|
22391
|
+
try {
|
|
22392
|
+
url = await resolvedUrl;
|
|
22393
|
+
} catch (error) {
|
|
22394
|
+
if (signal?.aborted) {
|
|
22395
|
+
yield { type: "done", stopReason: "cancelled" };
|
|
22396
|
+
return;
|
|
22397
|
+
}
|
|
22398
|
+
throw error;
|
|
22399
|
+
}
|
|
22400
|
+
if (signal?.aborted) {
|
|
22401
|
+
yield { type: "done", stopReason: "cancelled" };
|
|
22402
|
+
return;
|
|
22403
|
+
}
|
|
22379
22404
|
let response;
|
|
22380
22405
|
try {
|
|
22381
|
-
response = await fetch(
|
|
22406
|
+
response = await fetch(url, {
|
|
22382
22407
|
method: "POST",
|
|
22383
22408
|
headers,
|
|
22384
22409
|
body: JSON.stringify({ messages, tools, systemPrompt, model }),
|
|
@@ -22464,54 +22489,28 @@ function createProxyStreamingClient({
|
|
|
22464
22489
|
}
|
|
22465
22490
|
return { streamChat };
|
|
22466
22491
|
}
|
|
22467
|
-
// src/services/
|
|
22468
|
-
|
|
22469
|
-
|
|
22470
|
-
|
|
22471
|
-
function beginTurn(id) {
|
|
22472
|
-
if (open?.id === id) {
|
|
22473
|
-
return;
|
|
22492
|
+
// src/services/project-path.ts
|
|
22493
|
+
function normalizeRelPath(path) {
|
|
22494
|
+
if (typeof path !== "string" || !path.trim()) {
|
|
22495
|
+
return null;
|
|
22474
22496
|
}
|
|
22475
|
-
|
|
22476
|
-
|
|
22477
|
-
|
|
22478
|
-
open?.writes.push(write);
|
|
22479
|
-
}
|
|
22480
|
-
function endTurn(id) {
|
|
22481
|
-
if (!open) {
|
|
22482
|
-
return [];
|
|
22497
|
+
let p = path.trim().replaceAll("\\", "/");
|
|
22498
|
+
while (p.startsWith("./")) {
|
|
22499
|
+
p = p.slice(2);
|
|
22483
22500
|
}
|
|
22484
|
-
|
|
22485
|
-
|
|
22486
|
-
if (turn.writes.length === 0) {
|
|
22487
|
-
return [];
|
|
22501
|
+
if (p.startsWith("/") || p.startsWith("~") || /^[A-Za-z]:/.test(p)) {
|
|
22502
|
+
return null;
|
|
22488
22503
|
}
|
|
22489
|
-
|
|
22490
|
-
|
|
22491
|
-
turns.shift();
|
|
22504
|
+
if (p.split("/").includes("..")) {
|
|
22505
|
+
return null;
|
|
22492
22506
|
}
|
|
22493
|
-
return
|
|
22494
|
-
}
|
|
22495
|
-
function writesForTurn(id) {
|
|
22496
|
-
return turns.find((turn) => turn.id === id)?.writes ?? [];
|
|
22507
|
+
return p;
|
|
22497
22508
|
}
|
|
22498
|
-
function
|
|
22499
|
-
|
|
22500
|
-
|
|
22501
|
-
|
|
22502
|
-
|
|
22503
|
-
return failed > 0 ? `${failed} change${failed === 1 ? "" : "s"} failed` : "";
|
|
22504
|
-
}
|
|
22505
|
-
const files = `Changed ${paths.size} file${paths.size === 1 ? "" : "s"}`;
|
|
22506
|
-
const diskPaths = new Set(applied.filter((w) => w.disk).map((w) => w.path));
|
|
22507
|
-
const parts = [files];
|
|
22508
|
-
if (diskPaths.size > 0) {
|
|
22509
|
-
parts.push(`${diskPaths.size} written to disk — undo cannot reach ${diskPaths.size === 1 ? "it" : "them"}`);
|
|
22510
|
-
}
|
|
22511
|
-
if (failed > 0) {
|
|
22512
|
-
parts.push(`${failed} failed`);
|
|
22513
|
-
}
|
|
22514
|
-
return parts.join(" · ");
|
|
22509
|
+
function invalidPathResult(path) {
|
|
22510
|
+
return {
|
|
22511
|
+
success: false,
|
|
22512
|
+
error: `Invalid path ${JSON.stringify(path)} — use a path relative to the project root (no leading "/", no "..").`
|
|
22513
|
+
};
|
|
22515
22514
|
}
|
|
22516
22515
|
|
|
22517
22516
|
// src/services/token-lint.ts
|
|
@@ -22681,7 +22680,7 @@ function noDocError() {
|
|
|
22681
22680
|
error: "No document is open — use open_document(path) to open one on the canvas, or " + "read_file/write_file to work with project files directly."
|
|
22682
22681
|
};
|
|
22683
22682
|
}
|
|
22684
|
-
async function applyAndValidate(tab, mutationFn, summary, validate, renderCheck, projectStyle) {
|
|
22683
|
+
async function applyAndValidate(ledger, tab, mutationFn, summary, validate, renderCheck, projectStyle) {
|
|
22685
22684
|
const deps = { getProjectStyle: () => projectStyle, renderCheck, validate };
|
|
22686
22685
|
const before = await snapshotBeforeWrite(tab, deps);
|
|
22687
22686
|
if (!transactDoc(tab, mutationFn)) {
|
|
@@ -22690,7 +22689,7 @@ async function applyAndValidate(tab, mutationFn, summary, validate, renderCheck,
|
|
|
22690
22689
|
error: "The document is frozen: a collaborator is editing its source, so structural edits are " + "paused. Nothing was changed. Try again once source editing ends."
|
|
22691
22690
|
};
|
|
22692
22691
|
}
|
|
22693
|
-
|
|
22692
|
+
ledger.record({ disk: false, ok: true, path: tab.documentPath ?? "(untitled)", tool: summary });
|
|
22694
22693
|
return reportDocumentWrite(tab, before, summary, deps);
|
|
22695
22694
|
}
|
|
22696
22695
|
function registerAiTools(registry, {
|
|
@@ -22769,7 +22768,7 @@ function registerAiTools(registry, {
|
|
|
22769
22768
|
},
|
|
22770
22769
|
required: ["path", "key"]
|
|
22771
22770
|
},
|
|
22772
|
-
async execute(args) {
|
|
22771
|
+
async execute(args, ctx) {
|
|
22773
22772
|
const tab = getTab();
|
|
22774
22773
|
if (!tab) {
|
|
22775
22774
|
return noDocError();
|
|
@@ -22779,7 +22778,7 @@ function registerAiTools(registry, {
|
|
|
22779
22778
|
if (node === undefined) {
|
|
22780
22779
|
return { success: false, error: `No node exists at path ${JSON.stringify(path)}.` };
|
|
22781
22780
|
}
|
|
22782
|
-
return applyAndValidate(tab, (t) => mutateUpdateProperty(t, path, key, value ?? undefined), `Set "${key}" at ${JSON.stringify(path)}.`, validate, renderCheck, styleOf());
|
|
22781
|
+
return applyAndValidate(ctx.ledger, tab, (t) => mutateUpdateProperty(t, path, key, value ?? undefined), `Set "${key}" at ${JSON.stringify(path)}.`, validate, renderCheck, styleOf());
|
|
22783
22782
|
}
|
|
22784
22783
|
}));
|
|
22785
22784
|
registry.register(createToolDefinition({
|
|
@@ -22801,7 +22800,7 @@ function registerAiTools(registry, {
|
|
|
22801
22800
|
},
|
|
22802
22801
|
required: ["parentPath", "index", "node"]
|
|
22803
22802
|
},
|
|
22804
|
-
async execute(args) {
|
|
22803
|
+
async execute(args, ctx) {
|
|
22805
22804
|
const tab = getTab();
|
|
22806
22805
|
if (!tab) {
|
|
22807
22806
|
return noDocError();
|
|
@@ -22827,7 +22826,7 @@ function registerAiTools(registry, {
|
|
|
22827
22826
|
error: "Cannot insert into mapped-array children; edit the map template instead."
|
|
22828
22827
|
};
|
|
22829
22828
|
}
|
|
22830
|
-
return applyAndValidate(tab, (t) => mutateInsertNode(t, parentPath, index, childNode), `Inserted node at ${JSON.stringify([...parentPath, "children", index])}.`, validate, renderCheck, styleOf());
|
|
22829
|
+
return applyAndValidate(ctx.ledger, tab, (t) => mutateInsertNode(t, parentPath, index, childNode), `Inserted node at ${JSON.stringify([...parentPath, "children", index])}.`, validate, renderCheck, styleOf());
|
|
22831
22830
|
}
|
|
22832
22831
|
}));
|
|
22833
22832
|
registry.register(createToolDefinition({
|
|
@@ -22851,7 +22850,7 @@ function registerAiTools(registry, {
|
|
|
22851
22850
|
},
|
|
22852
22851
|
required: ["path", "property"]
|
|
22853
22852
|
},
|
|
22854
|
-
async execute(args) {
|
|
22853
|
+
async execute(args, ctx) {
|
|
22855
22854
|
const tab = getTab();
|
|
22856
22855
|
if (!tab) {
|
|
22857
22856
|
return noDocError();
|
|
@@ -22862,7 +22861,7 @@ function registerAiTools(registry, {
|
|
|
22862
22861
|
}
|
|
22863
22862
|
const prop = property;
|
|
22864
22863
|
const val = value == null ? undefined : String(value);
|
|
22865
|
-
return applyAndValidate(tab, (t) => mutateUpdateStyle(t, path, prop, val), `Set style "${prop}" at ${JSON.stringify(path)}.`, validate, renderCheck, styleOf());
|
|
22864
|
+
return applyAndValidate(ctx.ledger, tab, (t) => mutateUpdateStyle(t, path, prop, val), `Set style "${prop}" at ${JSON.stringify(path)}.`, validate, renderCheck, styleOf());
|
|
22866
22865
|
}
|
|
22867
22866
|
}));
|
|
22868
22867
|
registry.register(createToolDefinition({
|
|
@@ -22880,7 +22879,7 @@ function registerAiTools(registry, {
|
|
|
22880
22879
|
},
|
|
22881
22880
|
required: ["path", "value"]
|
|
22882
22881
|
},
|
|
22883
|
-
async execute(args) {
|
|
22882
|
+
async execute(args, ctx) {
|
|
22884
22883
|
const tab = getTab();
|
|
22885
22884
|
if (!tab) {
|
|
22886
22885
|
return noDocError();
|
|
@@ -22889,10 +22888,9 @@ function registerAiTools(registry, {
|
|
|
22889
22888
|
if (getNodeAtPath2(tab.doc.document, path) === undefined) {
|
|
22890
22889
|
return { success: false, error: `No node exists at path ${JSON.stringify(path)}.` };
|
|
22891
22890
|
}
|
|
22892
|
-
return applyAndValidate(tab, (t) => {
|
|
22893
|
-
|
|
22894
|
-
|
|
22895
|
-
node.children = [value];
|
|
22891
|
+
return applyAndValidate(ctx.ledger, tab, (t) => {
|
|
22892
|
+
mutateUpdateProperty(t, path, "textContent");
|
|
22893
|
+
mutateUpdateProperty(t, path, "children", [value]);
|
|
22896
22894
|
}, `Set text at ${JSON.stringify(path)}.`, validate, renderCheck, styleOf());
|
|
22897
22895
|
}
|
|
22898
22896
|
}));
|
|
@@ -22909,7 +22907,7 @@ function registerAiTools(registry, {
|
|
|
22909
22907
|
},
|
|
22910
22908
|
required: ["key", "value"]
|
|
22911
22909
|
},
|
|
22912
|
-
async execute(args) {
|
|
22910
|
+
async execute(args, ctx) {
|
|
22913
22911
|
const tab = getTab();
|
|
22914
22912
|
if (!tab) {
|
|
22915
22913
|
return noDocError();
|
|
@@ -22921,12 +22919,7 @@ function registerAiTools(registry, {
|
|
|
22921
22919
|
error: `State key "${key}" already exists. Use update_state to change it, or remove it first.`
|
|
22922
22920
|
};
|
|
22923
22921
|
}
|
|
22924
|
-
return applyAndValidate(tab, (t) => {
|
|
22925
|
-
if (!t.doc.document.state) {
|
|
22926
|
-
t.doc.document.state = {};
|
|
22927
|
-
}
|
|
22928
|
-
t.doc.document.state[key] = value;
|
|
22929
|
-
}, `Added state "${key}".`, validate, renderCheck, styleOf());
|
|
22922
|
+
return applyAndValidate(ctx.ledger, tab, (t) => mutateAddDef(t, key, value), `Added state "${key}".`, validate, renderCheck, styleOf());
|
|
22930
22923
|
}
|
|
22931
22924
|
}));
|
|
22932
22925
|
registry.register(createToolDefinition({
|
|
@@ -22942,7 +22935,7 @@ function registerAiTools(registry, {
|
|
|
22942
22935
|
},
|
|
22943
22936
|
required: ["key"]
|
|
22944
22937
|
},
|
|
22945
|
-
async execute(args) {
|
|
22938
|
+
async execute(args, ctx) {
|
|
22946
22939
|
const tab = getTab();
|
|
22947
22940
|
if (!tab) {
|
|
22948
22941
|
return noDocError();
|
|
@@ -22954,15 +22947,11 @@ function registerAiTools(registry, {
|
|
|
22954
22947
|
error: `State key "${key}" does not exist. Use add_state to create it, or check the name. Current state keys: ${Object.keys(tab.doc.document.state || {}).join(", ") || "(none)"}`
|
|
22955
22948
|
};
|
|
22956
22949
|
}
|
|
22957
|
-
return applyAndValidate(tab, (t) => {
|
|
22958
|
-
const { state } = t.doc.document;
|
|
22959
|
-
if (!state) {
|
|
22960
|
-
return;
|
|
22961
|
-
}
|
|
22950
|
+
return applyAndValidate(ctx.ledger, tab, (t) => {
|
|
22962
22951
|
if (value == null) {
|
|
22963
|
-
|
|
22952
|
+
mutateRemoveDef(t, key);
|
|
22964
22953
|
} else {
|
|
22965
|
-
|
|
22954
|
+
mutateAddDef(t, key, value);
|
|
22966
22955
|
}
|
|
22967
22956
|
}, value == null ? `Removed state "${key}".` : `Updated state "${key}".`, validate, renderCheck, styleOf());
|
|
22968
22957
|
}
|
|
@@ -22990,7 +22979,7 @@ function registerAiTools(registry, {
|
|
|
22990
22979
|
},
|
|
22991
22980
|
required: ["fromPath", "toParentPath", "toIndex"]
|
|
22992
22981
|
},
|
|
22993
|
-
async execute(args) {
|
|
22982
|
+
async execute(args, ctx) {
|
|
22994
22983
|
const tab = getTab();
|
|
22995
22984
|
if (!tab) {
|
|
22996
22985
|
return noDocError();
|
|
@@ -23011,7 +23000,7 @@ function registerAiTools(registry, {
|
|
|
23011
23000
|
error: `No node exists at toParentPath ${JSON.stringify(toParentPath)}.`
|
|
23012
23001
|
};
|
|
23013
23002
|
}
|
|
23014
|
-
return applyAndValidate(tab, (t) => mutateMoveNode(t, fromPath, toParentPath, toIndex), `Moved node from ${JSON.stringify(fromPath)} to ${JSON.stringify([...toParentPath, "children", toIndex])}.`, validate, renderCheck, styleOf());
|
|
23003
|
+
return applyAndValidate(ctx.ledger, tab, (t) => mutateMoveNode(t, fromPath, toParentPath, toIndex), `Moved node from ${JSON.stringify(fromPath)} to ${JSON.stringify([...toParentPath, "children", toIndex])}.`, validate, renderCheck, styleOf());
|
|
23015
23004
|
}
|
|
23016
23005
|
}));
|
|
23017
23006
|
registry.register(createToolDefinition({
|
|
@@ -23031,14 +23020,18 @@ function registerAiTools(registry, {
|
|
|
23031
23020
|
},
|
|
23032
23021
|
required: ["path", "content"]
|
|
23033
23022
|
},
|
|
23034
|
-
async execute(args) {
|
|
23023
|
+
async execute(args, ctx) {
|
|
23035
23024
|
if (!saveFile) {
|
|
23036
23025
|
return {
|
|
23037
23026
|
success: false,
|
|
23038
23027
|
error: "File operations are not available in this environment."
|
|
23039
23028
|
};
|
|
23040
23029
|
}
|
|
23041
|
-
const { path
|
|
23030
|
+
const { path, content } = args;
|
|
23031
|
+
const relPath = normalizeRelPath(path);
|
|
23032
|
+
if (relPath === null) {
|
|
23033
|
+
return invalidPathResult(path);
|
|
23034
|
+
}
|
|
23042
23035
|
const dirtyError = dirtyTabError(relPath);
|
|
23043
23036
|
if (dirtyError) {
|
|
23044
23037
|
return dirtyError;
|
|
@@ -23065,14 +23058,14 @@ ${formatted}`
|
|
|
23065
23058
|
}
|
|
23066
23059
|
try {
|
|
23067
23060
|
await saveFile(relPath, serializeJson(content, null));
|
|
23068
|
-
|
|
23061
|
+
ctx.ledger.record({ disk: true, ok: true, path: relPath, tool: "create_component" });
|
|
23069
23062
|
return {
|
|
23070
23063
|
success: true,
|
|
23071
23064
|
summary: await reconcileAfterWrite(relPath, `Created component at "${relPath}".`)
|
|
23072
23065
|
};
|
|
23073
23066
|
} catch (error) {
|
|
23074
23067
|
const message = error instanceof Error ? error.message : String(error);
|
|
23075
|
-
|
|
23068
|
+
ctx.ledger.record({
|
|
23076
23069
|
disk: true,
|
|
23077
23070
|
error: message,
|
|
23078
23071
|
ok: false,
|
|
@@ -23103,14 +23096,18 @@ ${formatted}`
|
|
|
23103
23096
|
},
|
|
23104
23097
|
required: ["path", "content"]
|
|
23105
23098
|
},
|
|
23106
|
-
async execute(args) {
|
|
23099
|
+
async execute(args, ctx) {
|
|
23107
23100
|
if (!saveFile) {
|
|
23108
23101
|
return {
|
|
23109
23102
|
success: false,
|
|
23110
23103
|
error: "File operations are not available in this environment."
|
|
23111
23104
|
};
|
|
23112
23105
|
}
|
|
23113
|
-
const { path
|
|
23106
|
+
const { path, content } = args;
|
|
23107
|
+
const relPath = normalizeRelPath(path);
|
|
23108
|
+
if (relPath === null) {
|
|
23109
|
+
return invalidPathResult(path);
|
|
23110
|
+
}
|
|
23114
23111
|
const dirtyError = dirtyTabError(relPath);
|
|
23115
23112
|
if (dirtyError) {
|
|
23116
23113
|
return dirtyError;
|
|
@@ -23137,14 +23134,14 @@ ${formatted}`
|
|
|
23137
23134
|
}
|
|
23138
23135
|
try {
|
|
23139
23136
|
await saveFile(relPath, serializeJson(content, null));
|
|
23140
|
-
|
|
23137
|
+
ctx.ledger.record({ disk: true, ok: true, path: relPath, tool: "create_page" });
|
|
23141
23138
|
return {
|
|
23142
23139
|
success: true,
|
|
23143
23140
|
summary: await reconcileAfterWrite(relPath, `Created page at "${relPath}".`)
|
|
23144
23141
|
};
|
|
23145
23142
|
} catch (error) {
|
|
23146
23143
|
const message = error instanceof Error ? error.message : String(error);
|
|
23147
|
-
|
|
23144
|
+
ctx.ledger.record({
|
|
23148
23145
|
disk: true,
|
|
23149
23146
|
error: message,
|
|
23150
23147
|
ok: false,
|
|
@@ -23160,26 +23157,6 @@ ${formatted}`
|
|
|
23160
23157
|
}));
|
|
23161
23158
|
}
|
|
23162
23159
|
|
|
23163
|
-
// src/services/ai-turn-signal.ts
|
|
23164
|
-
var _signal;
|
|
23165
|
-
function beginTurnSignal(signal) {
|
|
23166
|
-
_signal = signal;
|
|
23167
|
-
}
|
|
23168
|
-
function turnSignal() {
|
|
23169
|
-
return _signal;
|
|
23170
|
-
}
|
|
23171
|
-
function endTurnSignal() {
|
|
23172
|
-
_signal = undefined;
|
|
23173
|
-
_toolCallId = "";
|
|
23174
|
-
}
|
|
23175
|
-
var _toolCallId = "";
|
|
23176
|
-
function beginToolCall(id) {
|
|
23177
|
-
_toolCallId = id;
|
|
23178
|
-
}
|
|
23179
|
-
function currentToolCallId() {
|
|
23180
|
-
return _toolCallId;
|
|
23181
|
-
}
|
|
23182
|
-
|
|
23183
23160
|
// src/services/ai-ask.ts
|
|
23184
23161
|
var MAX_OPTIONS = 6;
|
|
23185
23162
|
var state2 = reactive({ pending: null });
|
|
@@ -23197,11 +23174,10 @@ function pendingAsk() {
|
|
|
23197
23174
|
function isAwaitingAnswer() {
|
|
23198
23175
|
return state2.pending !== null;
|
|
23199
23176
|
}
|
|
23200
|
-
function askUser(ask) {
|
|
23177
|
+
function askUser(ask, signal) {
|
|
23201
23178
|
return new Promise((resolve) => {
|
|
23202
23179
|
state2.pending = ask;
|
|
23203
23180
|
settle2 = resolve;
|
|
23204
|
-
const signal = turnSignal();
|
|
23205
23181
|
if (signal?.aborted) {
|
|
23206
23182
|
clear();
|
|
23207
23183
|
resolve({ answer: null, skipped: false });
|
|
@@ -23246,9 +23222,10 @@ function resetAsk() {
|
|
|
23246
23222
|
cancelAsk();
|
|
23247
23223
|
clear();
|
|
23248
23224
|
}
|
|
23249
|
-
function registerAskTool(registry) {
|
|
23225
|
+
function registerAskTool(registry, { onPending } = {}) {
|
|
23250
23226
|
registry.register(createToolDefinition({
|
|
23251
23227
|
name: "ask_user",
|
|
23228
|
+
interactive: true,
|
|
23252
23229
|
description: "Pause and put a question to the user; the turn waits for their reply and resumes with " + "it. Use it ONLY for a judgement that is genuinely theirs — which pages matter, whether to " + "keep the original design, whether a low-fidelity page is acceptable. Never ask something " + "you can decide yourself or discover with another tool, and never ask about an option you " + "cannot act on. One question at a time; offer options when the sensible answers are a " + "short list, and the user may always reply in their own words instead.",
|
|
23253
23230
|
parameters: {
|
|
23254
23231
|
type: "object",
|
|
@@ -23269,7 +23246,7 @@ function registerAskTool(registry) {
|
|
|
23269
23246
|
},
|
|
23270
23247
|
required: ["question"]
|
|
23271
23248
|
},
|
|
23272
|
-
execute: async (args) => {
|
|
23249
|
+
execute: async (args, ctx) => {
|
|
23273
23250
|
const { question, options, context } = args;
|
|
23274
23251
|
if (typeof question !== "string" || !question.trim()) {
|
|
23275
23252
|
return toolError('Pass "question" — the question to put to the user.');
|
|
@@ -23278,12 +23255,16 @@ function registerAskTool(registry) {
|
|
|
23278
23255
|
return toolError("A question is already waiting for the user.");
|
|
23279
23256
|
}
|
|
23280
23257
|
const choices = Array.isArray(options) ? options.filter((o) => typeof o === "string" && o.trim().length > 0).slice(0, MAX_OPTIONS) : [];
|
|
23281
|
-
const
|
|
23258
|
+
const answering = askUser({
|
|
23282
23259
|
context: typeof context === "string" ? context.trim() : "",
|
|
23283
|
-
id:
|
|
23260
|
+
id: ctx.callId,
|
|
23284
23261
|
options: choices,
|
|
23285
23262
|
question: question.trim()
|
|
23286
|
-
});
|
|
23263
|
+
}, ctx.signal);
|
|
23264
|
+
if (isAwaitingAnswer()) {
|
|
23265
|
+
onPending?.();
|
|
23266
|
+
}
|
|
23267
|
+
const { answer, skipped } = await answering;
|
|
23287
23268
|
if (skipped) {
|
|
23288
23269
|
return toolSuccess({ answer: null, skipped: true }, "The user declined to answer; proceed with your best judgment.");
|
|
23289
23270
|
}
|
|
@@ -23350,7 +23331,7 @@ function definitionOf(registry, command, execute) {
|
|
|
23350
23331
|
parameters,
|
|
23351
23332
|
strict: false,
|
|
23352
23333
|
llmStrict: false,
|
|
23353
|
-
execute: (args) => execute(tool.name, args)
|
|
23334
|
+
execute: (args, ctx) => execute(tool.name, args, ctx)
|
|
23354
23335
|
};
|
|
23355
23336
|
}
|
|
23356
23337
|
function advertisedCommandTools(registry, execute = async (name) => toolError(`Tool "${name}" was listed without an executor.`)) {
|
|
@@ -23393,20 +23374,20 @@ function defaultPaths(command, tab) {
|
|
|
23393
23374
|
}
|
|
23394
23375
|
return ["(unknown file)"];
|
|
23395
23376
|
}
|
|
23396
|
-
function fileLedger(command, tab, wrote) {
|
|
23377
|
+
function fileLedger(ledger, command, tab, wrote) {
|
|
23397
23378
|
if (command.undo === undefined) {
|
|
23398
23379
|
return;
|
|
23399
23380
|
}
|
|
23400
23381
|
const disk = command.undo === "none";
|
|
23401
23382
|
for (const path of wrote ?? defaultPaths(command, tab)) {
|
|
23402
|
-
|
|
23383
|
+
ledger.record({ disk, ok: true, path, tool: command.title });
|
|
23403
23384
|
}
|
|
23404
23385
|
}
|
|
23405
23386
|
function createCommandToolRegistry(deps) {
|
|
23406
23387
|
function commandFor(name) {
|
|
23407
23388
|
return activeRegistry()?.list().find((command) => command.aiTool?.name === name);
|
|
23408
23389
|
}
|
|
23409
|
-
async function execute(name, received) {
|
|
23390
|
+
async function execute(name, received, ctx = createToolContext()) {
|
|
23410
23391
|
const registry = activeRegistry();
|
|
23411
23392
|
const command = commandFor(name);
|
|
23412
23393
|
if (!registry || !command) {
|
|
@@ -23448,7 +23429,7 @@ function createCommandToolRegistry(deps) {
|
|
|
23448
23429
|
try {
|
|
23449
23430
|
report = normaliseReport(tool.report({ after, args, before }));
|
|
23450
23431
|
} catch (error) {
|
|
23451
|
-
fileLedger(command, tab, projectUnchanged ? [] : undefined);
|
|
23432
|
+
fileLedger(ctx.ledger, command, tab, projectUnchanged ? [] : undefined);
|
|
23452
23433
|
const message = error instanceof Error ? error.message : String(error);
|
|
23453
23434
|
return { error: `${command.title} ran, but its report failed: ${message}`, success: false };
|
|
23454
23435
|
}
|
|
@@ -23459,7 +23440,7 @@ function createCommandToolRegistry(deps) {
|
|
|
23459
23440
|
summary = `${command.title} changed nothing: project.json is exactly as it was.`;
|
|
23460
23441
|
}
|
|
23461
23442
|
}
|
|
23462
|
-
fileLedger(command, tab, wrote);
|
|
23443
|
+
fileLedger(ctx.ledger, command, tab, wrote);
|
|
23463
23444
|
if (tab && snapshot) {
|
|
23464
23445
|
const verdict = await reportDocumentWrite(tab, snapshot, summary, deps);
|
|
23465
23446
|
if (!verdict.success) {
|
|
@@ -23526,9 +23507,9 @@ function composeToolRegistries(hand, commands) {
|
|
|
23526
23507
|
const side = sideOf(name);
|
|
23527
23508
|
return side ? side.validate(name, args) : { valid: false, errors: [`Unknown tool: "${name}"`] };
|
|
23528
23509
|
},
|
|
23529
|
-
async execute(name, args) {
|
|
23510
|
+
async execute(name, args, ctx) {
|
|
23530
23511
|
const side = sideOf(name);
|
|
23531
|
-
return side ? side.execute(name, args) : toolError(`Unknown tool: "${name}"`);
|
|
23512
|
+
return side ? side.execute(name, args, ctx) : toolError(`Unknown tool: "${name}"`);
|
|
23532
23513
|
}
|
|
23533
23514
|
};
|
|
23534
23515
|
}
|
|
@@ -23546,6 +23527,14 @@ async function adoptProject(root) {
|
|
|
23546
23527
|
}
|
|
23547
23528
|
async function adoptCreatedProject(root, { getTab, adopt = adoptProject, initRepo = initProjectRepo2 }) {
|
|
23548
23529
|
await initRepo(root);
|
|
23530
|
+
const platform = hasPlatform2() ? getPlatform2() : null;
|
|
23531
|
+
const shownRoot = workspace2.projectRoot;
|
|
23532
|
+
const openedElsewhere = Boolean(platform && shownRoot && shownRoot !== root && platform.openProjectInNewWindow);
|
|
23533
|
+
if (openedElsewhere && shownRoot && platform?.setWindowProject) {
|
|
23534
|
+
try {
|
|
23535
|
+
await platform.setWindowProject(shownRoot);
|
|
23536
|
+
} catch {}
|
|
23537
|
+
}
|
|
23549
23538
|
const wasBatching = isBatching();
|
|
23550
23539
|
if (wasBatching) {
|
|
23551
23540
|
endBatch();
|
|
@@ -23559,7 +23548,7 @@ async function adoptCreatedProject(root, { getTab, adopt = adoptProject, initRep
|
|
|
23559
23548
|
if (wasBatching) {
|
|
23560
23549
|
beginBatch(getTab());
|
|
23561
23550
|
}
|
|
23562
|
-
return { adopted: workspace2.projectRoot === root, error: adoptionError };
|
|
23551
|
+
return { adopted: workspace2.projectRoot === root, error: adoptionError, openedElsewhere };
|
|
23563
23552
|
}
|
|
23564
23553
|
|
|
23565
23554
|
// src/services/ai-project-tools.ts
|
|
@@ -23569,28 +23558,6 @@ var SEARCH_CAP = 100;
|
|
|
23569
23558
|
var READ_CAP_BYTES = 48 * 1024;
|
|
23570
23559
|
var WRITE_CAP_BYTES = 256 * 1024;
|
|
23571
23560
|
var NOT_UNDOABLE = "(saved to disk; not undoable with Cmd+Z)";
|
|
23572
|
-
function normalizeRelPath(path) {
|
|
23573
|
-
if (typeof path !== "string" || !path.trim()) {
|
|
23574
|
-
return null;
|
|
23575
|
-
}
|
|
23576
|
-
let p = path.trim().replaceAll("\\", "/");
|
|
23577
|
-
while (p.startsWith("./")) {
|
|
23578
|
-
p = p.slice(2);
|
|
23579
|
-
}
|
|
23580
|
-
if (p.startsWith("/") || p.startsWith("~") || /^[A-Za-z]:/.test(p)) {
|
|
23581
|
-
return null;
|
|
23582
|
-
}
|
|
23583
|
-
if (p.split("/").includes("..")) {
|
|
23584
|
-
return null;
|
|
23585
|
-
}
|
|
23586
|
-
return p;
|
|
23587
|
-
}
|
|
23588
|
-
function pathError(path) {
|
|
23589
|
-
return {
|
|
23590
|
-
success: false,
|
|
23591
|
-
error: `Invalid path ${JSON.stringify(path)} — use a path relative to the project root (no leading "/", no "..").`
|
|
23592
|
-
};
|
|
23593
|
-
}
|
|
23594
23561
|
function isJxDocPath(path) {
|
|
23595
23562
|
return /^(pages|layouts|components|elements)\//.test(path) && path.endsWith(".json");
|
|
23596
23563
|
}
|
|
@@ -23626,7 +23593,7 @@ function registerProjectTools(registry, {
|
|
|
23626
23593
|
const { dir } = args;
|
|
23627
23594
|
const start = dir === undefined || dir === "" ? "." : normalizeRelPath(dir);
|
|
23628
23595
|
if (start === null) {
|
|
23629
|
-
return
|
|
23596
|
+
return invalidPathResult(dir);
|
|
23630
23597
|
}
|
|
23631
23598
|
const platform = getPlatform2();
|
|
23632
23599
|
const entries = [];
|
|
@@ -23683,7 +23650,7 @@ function registerProjectTools(registry, {
|
|
|
23683
23650
|
async execute(args) {
|
|
23684
23651
|
const relPath = normalizeRelPath(args.path);
|
|
23685
23652
|
if (relPath === null) {
|
|
23686
|
-
return
|
|
23653
|
+
return invalidPathResult(args.path);
|
|
23687
23654
|
}
|
|
23688
23655
|
try {
|
|
23689
23656
|
const content = await getPlatform2().readFile(relPath);
|
|
@@ -23723,11 +23690,11 @@ function registerProjectTools(registry, {
|
|
|
23723
23690
|
},
|
|
23724
23691
|
required: ["path", "content"]
|
|
23725
23692
|
},
|
|
23726
|
-
async execute(args) {
|
|
23693
|
+
async execute(args, ctx) {
|
|
23727
23694
|
const { path, content } = args;
|
|
23728
23695
|
const relPath = normalizeRelPath(path);
|
|
23729
23696
|
if (relPath === null) {
|
|
23730
|
-
return
|
|
23697
|
+
return invalidPathResult(path);
|
|
23731
23698
|
}
|
|
23732
23699
|
if (content.length > WRITE_CAP_BYTES) {
|
|
23733
23700
|
return {
|
|
@@ -23808,10 +23775,16 @@ ${formatted}`
|
|
|
23808
23775
|
}
|
|
23809
23776
|
try {
|
|
23810
23777
|
await getPlatform2().writeFile(relPath, content);
|
|
23811
|
-
|
|
23778
|
+
ctx.ledger.record({ disk: true, ok: true, path: relPath, tool: "write_file" });
|
|
23812
23779
|
} catch (error) {
|
|
23813
23780
|
const message = error instanceof Error ? error.message : String(error);
|
|
23814
|
-
|
|
23781
|
+
ctx.ledger.record({
|
|
23782
|
+
disk: true,
|
|
23783
|
+
error: message,
|
|
23784
|
+
ok: false,
|
|
23785
|
+
path: relPath,
|
|
23786
|
+
tool: "write_file"
|
|
23787
|
+
});
|
|
23815
23788
|
return {
|
|
23816
23789
|
success: false,
|
|
23817
23790
|
error: `Failed to write "${relPath}": ${message}`
|
|
@@ -23898,7 +23871,7 @@ ${tokenHints}` : summary };
|
|
|
23898
23871
|
},
|
|
23899
23872
|
required: ["name"]
|
|
23900
23873
|
},
|
|
23901
|
-
async execute(args) {
|
|
23874
|
+
async execute(args, ctx) {
|
|
23902
23875
|
const {
|
|
23903
23876
|
name,
|
|
23904
23877
|
description,
|
|
@@ -23962,6 +23935,7 @@ ${tokenHints}` : summary };
|
|
|
23962
23935
|
error: `Failed to create project: ${error instanceof Error ? error.message : String(error)}`
|
|
23963
23936
|
};
|
|
23964
23937
|
}
|
|
23938
|
+
ctx.ledger.record({ disk: true, ok: true, path: result.root, tool: "create_project" });
|
|
23965
23939
|
const { adopted, error: adoptionError } = await adoptCreatedProject(result.root, {
|
|
23966
23940
|
adopt: adoptProject,
|
|
23967
23941
|
getTab
|
|
@@ -24124,10 +24098,7 @@ function destinationProblem(destination) {
|
|
|
24124
24098
|
}
|
|
24125
24099
|
return null;
|
|
24126
24100
|
}
|
|
24127
|
-
var
|
|
24128
|
-
function resetImportGuard() {
|
|
24129
|
-
imported = false;
|
|
24130
|
-
}
|
|
24101
|
+
var IMPORT_DONE = "import.done";
|
|
24131
24102
|
function describeRun(id, root, summary) {
|
|
24132
24103
|
const record = importRun(id);
|
|
24133
24104
|
const warnings = [...record?.warnings ?? [], ...summary?.warnings ?? []];
|
|
@@ -24157,7 +24128,12 @@ function describeRun(id, root, summary) {
|
|
|
24157
24128
|
lines.push("The file and document tools are available now. Read the emitted pages, layouts and " + "components before proposing changes — then ask the user about anything the import had to " + "guess at (pages it skipped, components it may have split wrongly, a layout it did not find, " + "a page that did not render faithfully).");
|
|
24158
24129
|
return lines.join(" ");
|
|
24159
24130
|
}
|
|
24160
|
-
function registerImportTools(registry, {
|
|
24131
|
+
function registerImportTools(registry, {
|
|
24132
|
+
getTab,
|
|
24133
|
+
adoptProject,
|
|
24134
|
+
onProjectAdopted,
|
|
24135
|
+
brief: briefStore = { clear: clearPendingImportBrief, read: pendingImportBrief }
|
|
24136
|
+
}) {
|
|
24161
24137
|
registry.register(createToolDefinition({
|
|
24162
24138
|
name: "import_site",
|
|
24163
24139
|
description: "Clone a live website into a new Jx project and open it in the studio: pages, styles, " + "assets, a shared layout and recurring components. Only available while no project is " + "open. Progress streams into the conversation as it runs. When the user reached you " + "through the New Project Import form, the URL, crawl options and destination are already " + "settled — call this with the url alone and the rest is filled in. After it succeeds the " + "file and document tools become available in the next round.",
|
|
@@ -24203,7 +24179,7 @@ function registerImportTools(registry, { getTab, adoptProject, onProjectAdopted
|
|
|
24203
24179
|
},
|
|
24204
24180
|
required: ["url"]
|
|
24205
24181
|
},
|
|
24206
|
-
async execute(args) {
|
|
24182
|
+
async execute(args, ctx) {
|
|
24207
24183
|
const {
|
|
24208
24184
|
url,
|
|
24209
24185
|
directory,
|
|
@@ -24219,7 +24195,7 @@ function registerImportTools(registry, { getTab, adoptProject, onProjectAdopted
|
|
|
24219
24195
|
if (workspace2.projectRoot) {
|
|
24220
24196
|
return toolError("A project is already open in this window — import_site is only for bootstrapping.");
|
|
24221
24197
|
}
|
|
24222
|
-
if (
|
|
24198
|
+
if (ctx.session.get(IMPORT_DONE) === true) {
|
|
24223
24199
|
return toolError("A site has already been imported in this conversation. Start a new chat to import " + "another, or use the file tools on the project that was created.");
|
|
24224
24200
|
}
|
|
24225
24201
|
const platform = getPlatform2();
|
|
@@ -24235,7 +24211,7 @@ function registerImportTools(registry, { getTab, adoptProject, onProjectAdopted
|
|
|
24235
24211
|
if (target.protocol !== "http:" && target.protocol !== "https:") {
|
|
24236
24212
|
return toolError("The url must start with http:// or https://.");
|
|
24237
24213
|
}
|
|
24238
|
-
const brief =
|
|
24214
|
+
const brief = briefStore.read();
|
|
24239
24215
|
const requested = typeof directory === "string" ? directory.trim() : "";
|
|
24240
24216
|
if (requested && brief && requested !== brief.directory) {
|
|
24241
24217
|
return toolError(`The destination is already set to ${brief.directory} — call import_site without ` + '"directory", or ask the user before changing it.');
|
|
@@ -24248,12 +24224,14 @@ function registerImportTools(registry, { getTab, adoptProject, onProjectAdopted
|
|
|
24248
24224
|
if (problem) {
|
|
24249
24225
|
return toolError(problem);
|
|
24250
24226
|
}
|
|
24251
|
-
const id =
|
|
24227
|
+
const id = ctx.callId;
|
|
24252
24228
|
const signal = beginImportRun(id, { directory: destination, url: target.href });
|
|
24253
|
-
|
|
24229
|
+
const callSignal = ctx.signal;
|
|
24230
|
+
const stopRun = () => {
|
|
24254
24231
|
abortImportRun();
|
|
24255
24232
|
finishImportRun(id, { status: "stopped" });
|
|
24256
|
-
}
|
|
24233
|
+
};
|
|
24234
|
+
callSignal.addEventListener("abort", stopRun, { once: true });
|
|
24257
24235
|
const apiKey = getOpenAiKey();
|
|
24258
24236
|
const baseUrl = getBaseUrl();
|
|
24259
24237
|
const model = brief?.model || preferredModel();
|
|
@@ -24280,8 +24258,11 @@ function registerImportTools(registry, { getTab, adoptProject, onProjectAdopted
|
|
|
24280
24258
|
...apiKey ? { apiKey } : {},
|
|
24281
24259
|
...baseUrl ? { baseUrl } : {},
|
|
24282
24260
|
...model ? { model } : {}
|
|
24283
|
-
}, (evt) =>
|
|
24261
|
+
}, (evt) => {
|
|
24262
|
+
ctx.progress(evt);
|
|
24263
|
+
}, signal, onReady);
|
|
24284
24264
|
} catch (error) {
|
|
24265
|
+
callSignal.removeEventListener("abort", stopRun);
|
|
24285
24266
|
const message = error instanceof Error ? error.message : String(error);
|
|
24286
24267
|
if (signal.aborted) {
|
|
24287
24268
|
finishImportRun(id, { error: message, status: "stopped" });
|
|
@@ -24295,9 +24276,11 @@ function registerImportTools(registry, { getTab, adoptProject, onProjectAdopted
|
|
|
24295
24276
|
Last steps:
|
|
24296
24277
|
${tail}` : ""}`);
|
|
24297
24278
|
}
|
|
24279
|
+
callSignal.removeEventListener("abort", stopRun);
|
|
24298
24280
|
finishImportRun(id, { status: "done" });
|
|
24299
|
-
|
|
24300
|
-
|
|
24281
|
+
ctx.ledger.record({ disk: true, ok: true, path: result.root, tool: "import_site" });
|
|
24282
|
+
ctx.session.set(IMPORT_DONE, true);
|
|
24283
|
+
briefStore.clear();
|
|
24301
24284
|
const { adopted, error: adoptionError } = await (adoption ?? adoptCreatedProject(result.root, {
|
|
24302
24285
|
getTab,
|
|
24303
24286
|
...adoptProject ? { adopt: adoptProject } : {}
|
|
@@ -24334,7 +24317,7 @@ function createGatedToolRegistry(inner, availability) {
|
|
|
24334
24317
|
validate(toolName, args) {
|
|
24335
24318
|
return inner.validate(toolName, args);
|
|
24336
24319
|
},
|
|
24337
|
-
async execute(toolName, args) {
|
|
24320
|
+
async execute(toolName, args, ctx) {
|
|
24338
24321
|
if (inner.getDefinition(toolName) && !isAvailable(toolName)) {
|
|
24339
24322
|
const gate = availability.get(toolName);
|
|
24340
24323
|
return {
|
|
@@ -24342,28 +24325,207 @@ function createGatedToolRegistry(inner, availability) {
|
|
|
24342
24325
|
error: `Tool "${toolName}" is not available right now — it requires ${gate?.requires ?? "a different studio state"}.`
|
|
24343
24326
|
};
|
|
24344
24327
|
}
|
|
24345
|
-
return inner.execute(toolName, args);
|
|
24328
|
+
return inner.execute(toolName, args, ctx);
|
|
24346
24329
|
}
|
|
24347
24330
|
};
|
|
24348
24331
|
}
|
|
24349
24332
|
|
|
24333
|
+
// src/services/context-manager.ts
|
|
24334
|
+
var CHARS_PER_TOKEN = 4;
|
|
24335
|
+
var MODEL_CONTEXT_WINDOWS = [
|
|
24336
|
+
["gpt-4o", 128000],
|
|
24337
|
+
["gpt-4.1", 1e6],
|
|
24338
|
+
["gpt-4-turbo", 128000],
|
|
24339
|
+
["gpt-4", 8192],
|
|
24340
|
+
["gpt-3.5", 16385],
|
|
24341
|
+
["o1", 200000],
|
|
24342
|
+
["o3", 200000],
|
|
24343
|
+
["claude", 200000]
|
|
24344
|
+
];
|
|
24345
|
+
var DEFAULT_CONTEXT_WINDOW = 32000;
|
|
24346
|
+
var BUDGET_FRACTION = 0.8;
|
|
24347
|
+
var WARN_FRACTION = 0.5;
|
|
24348
|
+
function contextWindowFor(model) {
|
|
24349
|
+
if (!model) {
|
|
24350
|
+
return DEFAULT_CONTEXT_WINDOW;
|
|
24351
|
+
}
|
|
24352
|
+
const reported = modelContextWindow(model);
|
|
24353
|
+
if (reported !== undefined && reported > 0) {
|
|
24354
|
+
return reported;
|
|
24355
|
+
}
|
|
24356
|
+
const id = model.toLowerCase();
|
|
24357
|
+
let best = 0;
|
|
24358
|
+
let window2 = DEFAULT_CONTEXT_WINDOW;
|
|
24359
|
+
for (const [prefix, size] of MODEL_CONTEXT_WINDOWS) {
|
|
24360
|
+
if (id.startsWith(prefix) && prefix.length > best) {
|
|
24361
|
+
best = prefix.length;
|
|
24362
|
+
window2 = size;
|
|
24363
|
+
}
|
|
24364
|
+
}
|
|
24365
|
+
return window2;
|
|
24366
|
+
}
|
|
24367
|
+
var KEEP_RECENT = 20;
|
|
24368
|
+
var MIN_USER_TURNS = 3;
|
|
24369
|
+
function estimateTokens(input) {
|
|
24370
|
+
if (typeof input === "string") {
|
|
24371
|
+
return Math.ceil(input.length / CHARS_PER_TOKEN);
|
|
24372
|
+
}
|
|
24373
|
+
let total = 0;
|
|
24374
|
+
for (const msg of input) {
|
|
24375
|
+
total += estimateTokens(msg.content || "");
|
|
24376
|
+
if (msg.tool_calls) {
|
|
24377
|
+
for (const tc of msg.tool_calls) {
|
|
24378
|
+
total += estimateTokens(tc.function?.name || "") + estimateTokens(tc.function?.arguments || "") + 4;
|
|
24379
|
+
}
|
|
24380
|
+
}
|
|
24381
|
+
total += 4;
|
|
24382
|
+
}
|
|
24383
|
+
return total;
|
|
24384
|
+
}
|
|
24385
|
+
function wireEntry(msg) {
|
|
24386
|
+
const entry = { content: msg.content, role: msg.role };
|
|
24387
|
+
if (msg.toolCalls) {
|
|
24388
|
+
entry.tool_calls = msg.toolCalls.map((tc) => ({
|
|
24389
|
+
function: { arguments: tc.arguments, name: tc.name }
|
|
24390
|
+
}));
|
|
24391
|
+
}
|
|
24392
|
+
return entry;
|
|
24393
|
+
}
|
|
24394
|
+
function contextTokens(chatState, systemTokens) {
|
|
24395
|
+
const { usage, messages } = chatState;
|
|
24396
|
+
const boundary = usage ? messages[usage.messageCount - 1] : undefined;
|
|
24397
|
+
if (usage && boundary && boundary.id === usage.lastMessageId) {
|
|
24398
|
+
const since = messages.slice(usage.messageCount).filter((msg) => msg.role !== "assistant" || msg.content || (msg.toolCalls?.length ?? 0) > 0).map((msg) => wireEntry(msg));
|
|
24399
|
+
const promptDrift = usage.systemTokens === undefined ? 0 : systemTokens - usage.systemTokens;
|
|
24400
|
+
const reused = Math.max(0, usage.contextTokens + promptDrift) + estimateTokens(since);
|
|
24401
|
+
if (Number.isFinite(reused)) {
|
|
24402
|
+
return reused;
|
|
24403
|
+
}
|
|
24404
|
+
}
|
|
24405
|
+
return systemTokens + estimateTokens(chatState.toMessagesArray());
|
|
24406
|
+
}
|
|
24407
|
+
function estimatePromptTokens(systemPrompt) {
|
|
24408
|
+
return estimateTokens(systemPrompt);
|
|
24409
|
+
}
|
|
24410
|
+
function trimContext(chatState, systemPrompt) {
|
|
24411
|
+
const window2 = contextWindowFor(chatState.model);
|
|
24412
|
+
const maxTokens = Math.floor(window2 * BUDGET_FRACTION);
|
|
24413
|
+
const warnTokens = Math.floor(window2 * WARN_FRACTION);
|
|
24414
|
+
const systemTokens = estimateTokens(systemPrompt);
|
|
24415
|
+
const allMessages = chatState.messages;
|
|
24416
|
+
const total = contextTokens(chatState, systemTokens);
|
|
24417
|
+
if (total <= maxTokens) {
|
|
24418
|
+
chatState.setTokenCount(total);
|
|
24419
|
+
chatState.setContextWarning(total >= warnTokens);
|
|
24420
|
+
return { estimatedTokens: total, droppedCount: 0 };
|
|
24421
|
+
}
|
|
24422
|
+
let keepFrom = Math.max(0, allMessages.length - KEEP_RECENT);
|
|
24423
|
+
let preservedTurns = 0;
|
|
24424
|
+
for (let i = allMessages.length - 1;i >= keepFrom; i--) {
|
|
24425
|
+
const { role } = allMessages[i];
|
|
24426
|
+
if (role === "user" || role === "tool") {
|
|
24427
|
+
preservedTurns += 1;
|
|
24428
|
+
}
|
|
24429
|
+
}
|
|
24430
|
+
if (preservedTurns < MIN_USER_TURNS) {
|
|
24431
|
+
for (let i = keepFrom - 1;i >= 0 && preservedTurns < MIN_USER_TURNS; i--) {
|
|
24432
|
+
keepFrom = i;
|
|
24433
|
+
const { role } = allMessages[i];
|
|
24434
|
+
if (role === "user" || role === "tool") {
|
|
24435
|
+
preservedTurns += 1;
|
|
24436
|
+
}
|
|
24437
|
+
}
|
|
24438
|
+
}
|
|
24439
|
+
if (keepFrom <= 0) {
|
|
24440
|
+
chatState.setTokenCount(total);
|
|
24441
|
+
chatState.setContextWarning(true);
|
|
24442
|
+
return { estimatedTokens: total, droppedCount: 0 };
|
|
24443
|
+
}
|
|
24444
|
+
const droppedCount = keepFrom;
|
|
24445
|
+
allMessages.splice(0, keepFrom);
|
|
24446
|
+
chatState.clearUsage();
|
|
24447
|
+
allMessages.unshift({
|
|
24448
|
+
id: `ctx_summary_${Date.now()}`,
|
|
24449
|
+
role: "user",
|
|
24450
|
+
content: `[Earlier conversation truncated. ${droppedCount} messages dropped to stay within token budget. The most recent ${allMessages.length - 1} messages are preserved.]`,
|
|
24451
|
+
timestamp: Date.now()
|
|
24452
|
+
});
|
|
24453
|
+
const newTotal = systemTokens + estimateTokens(chatState.toMessagesArray());
|
|
24454
|
+
chatState.setTokenCount(newTotal);
|
|
24455
|
+
chatState.setContextWarning(true);
|
|
24456
|
+
return { estimatedTokens: newTotal, droppedCount };
|
|
24457
|
+
}
|
|
24458
|
+
var UNANSWERED_TOOL_RESULT = JSON.stringify({
|
|
24459
|
+
success: false,
|
|
24460
|
+
error: "This tool call was never completed — the session was reloaded or the history was trimmed."
|
|
24461
|
+
});
|
|
24462
|
+
function pruneOrphanToolMessages(chatState) {
|
|
24463
|
+
const { messages } = chatState;
|
|
24464
|
+
const requested = new Set;
|
|
24465
|
+
const answered = new Set;
|
|
24466
|
+
for (const msg of messages) {
|
|
24467
|
+
if (msg.role === "assistant" && msg.toolCalls) {
|
|
24468
|
+
for (const call of msg.toolCalls) {
|
|
24469
|
+
requested.add(call.id);
|
|
24470
|
+
}
|
|
24471
|
+
} else if (msg.role === "tool" && msg.toolCallId) {
|
|
24472
|
+
answered.add(msg.toolCallId);
|
|
24473
|
+
}
|
|
24474
|
+
}
|
|
24475
|
+
let dropped = 0;
|
|
24476
|
+
for (let i = messages.length - 1;i >= 0; i--) {
|
|
24477
|
+
const msg = messages[i];
|
|
24478
|
+
if (msg.role === "tool" && (!msg.toolCallId || !requested.has(msg.toolCallId))) {
|
|
24479
|
+
messages.splice(i, 1);
|
|
24480
|
+
dropped += 1;
|
|
24481
|
+
}
|
|
24482
|
+
}
|
|
24483
|
+
let sealed = 0;
|
|
24484
|
+
for (let i = messages.length - 1;i >= 0; i--) {
|
|
24485
|
+
const msg = messages[i];
|
|
24486
|
+
if (msg.role !== "assistant" || !msg.toolCalls) {
|
|
24487
|
+
continue;
|
|
24488
|
+
}
|
|
24489
|
+
const missing = msg.toolCalls.filter((call) => !answered.has(call.id));
|
|
24490
|
+
if (missing.length === 0) {
|
|
24491
|
+
continue;
|
|
24492
|
+
}
|
|
24493
|
+
messages.splice(i + 1, 0, ...missing.map((call, n) => ({
|
|
24494
|
+
content: UNANSWERED_TOOL_RESULT,
|
|
24495
|
+
id: `sealed_${call.id}_${n}`,
|
|
24496
|
+
role: "tool",
|
|
24497
|
+
timestamp: msg.timestamp,
|
|
24498
|
+
toolCallId: call.id
|
|
24499
|
+
})));
|
|
24500
|
+
sealed += missing.length;
|
|
24501
|
+
}
|
|
24502
|
+
return { dropped, sealed };
|
|
24503
|
+
}
|
|
24504
|
+
|
|
24350
24505
|
// src/services/tool-executor.ts
|
|
24351
24506
|
var MAX_ROUNDS = 5;
|
|
24507
|
+
var EMPTY_TURN_TEXT = "The model sent back an empty reply.";
|
|
24352
24508
|
var MAX_TOTAL_ROUNDS = 25;
|
|
24353
|
-
var INTERACTIVE_TOOLS = new Set(["ask_user"]);
|
|
24354
24509
|
async function runAgentLoop({
|
|
24355
24510
|
chatState,
|
|
24356
24511
|
streamingClient,
|
|
24357
24512
|
toolRegistry,
|
|
24358
24513
|
systemPrompt,
|
|
24359
24514
|
signal,
|
|
24360
|
-
getTab
|
|
24515
|
+
getTab,
|
|
24516
|
+
session = createSessionFacts()
|
|
24361
24517
|
}) {
|
|
24362
24518
|
const allErrors = [];
|
|
24363
24519
|
const appliedSummaries = [];
|
|
24364
|
-
const
|
|
24365
|
-
|
|
24366
|
-
|
|
24520
|
+
const turnUserId = chatState.messages.findLast((m) => m.role === "user")?.id;
|
|
24521
|
+
const ledger = openTurnLedger(`turn:${chatState.messages.length}`);
|
|
24522
|
+
const actor = {
|
|
24523
|
+
id: `assistant:${session.sessionId ?? "local"}:${ledger.turnId}`,
|
|
24524
|
+
kind: "assistant",
|
|
24525
|
+
model: chatState.model,
|
|
24526
|
+
sessionId: session.sessionId,
|
|
24527
|
+
turnId: ledger.turnId
|
|
24528
|
+
};
|
|
24367
24529
|
if (getTab) {
|
|
24368
24530
|
beginBatch(getTab());
|
|
24369
24531
|
}
|
|
@@ -24413,6 +24575,10 @@ async function runAgentLoop({
|
|
|
24413
24575
|
chatState.appendToolCallEnd(event.id);
|
|
24414
24576
|
break;
|
|
24415
24577
|
}
|
|
24578
|
+
case "usage": {
|
|
24579
|
+
chatState.recordUsage(event, { systemTokens: estimatePromptTokens(systemPrompt) });
|
|
24580
|
+
break;
|
|
24581
|
+
}
|
|
24416
24582
|
case "done": {
|
|
24417
24583
|
({ stopReason } = event);
|
|
24418
24584
|
break;
|
|
@@ -24427,7 +24593,6 @@ async function runAgentLoop({
|
|
|
24427
24593
|
}
|
|
24428
24594
|
}
|
|
24429
24595
|
}
|
|
24430
|
-
chatState.finishStream(stopReason);
|
|
24431
24596
|
if (streamError) {
|
|
24432
24597
|
if (streamErrorCode === "cf_reconnect_required") {
|
|
24433
24598
|
resetModelCache();
|
|
@@ -24436,30 +24601,55 @@ async function runAgentLoop({
|
|
|
24436
24601
|
chatState.setError(streamError);
|
|
24437
24602
|
return;
|
|
24438
24603
|
}
|
|
24439
|
-
|
|
24604
|
+
const stopped = stopReason === "cancelled" || signal?.aborted === true;
|
|
24605
|
+
if (toolCalls.size === 0 && !stopped && turnAnchor(chatState.messages, turnUserId) === null) {
|
|
24606
|
+
chatState.setError(EMPTY_TURN_TEXT);
|
|
24607
|
+
return;
|
|
24608
|
+
}
|
|
24609
|
+
chatState.finishStream(stopReason);
|
|
24610
|
+
if (toolCalls.size === 0 || stopReason === "cancelled" || signal?.aborted) {
|
|
24440
24611
|
return;
|
|
24441
24612
|
}
|
|
24442
24613
|
let didWork = false;
|
|
24443
24614
|
for (const [id, call] of toolCalls) {
|
|
24615
|
+
if (signal?.aborted) {
|
|
24616
|
+
return;
|
|
24617
|
+
}
|
|
24444
24618
|
let result;
|
|
24445
|
-
|
|
24619
|
+
const writesBefore = ledger.writes.length;
|
|
24620
|
+
const link = signal ? linkCallSignal(signal) : null;
|
|
24621
|
+
const ctx = createToolContext({
|
|
24622
|
+
actor,
|
|
24623
|
+
callId: id,
|
|
24624
|
+
ledger,
|
|
24625
|
+
progress: (event) => {
|
|
24626
|
+
recordImportProgress(id, event);
|
|
24627
|
+
},
|
|
24628
|
+
session,
|
|
24629
|
+
...link ? { signal: link.signal } : {}
|
|
24630
|
+
});
|
|
24446
24631
|
try {
|
|
24447
|
-
|
|
24448
|
-
result = await toolRegistry.execute(call.name, args);
|
|
24632
|
+
result = await toolRegistry.execute(call.name, parseToolArguments(call.arguments), ctx);
|
|
24449
24633
|
} catch (error) {
|
|
24450
24634
|
result = {
|
|
24451
24635
|
success: false,
|
|
24452
24636
|
error: `Failed to parse arguments: ${error.message}`
|
|
24453
24637
|
};
|
|
24638
|
+
} finally {
|
|
24639
|
+
link?.release();
|
|
24454
24640
|
}
|
|
24455
|
-
if (!
|
|
24641
|
+
if (turnUserId !== undefined && !chatState.messages.some((m) => m.id === turnUserId)) {
|
|
24642
|
+
return;
|
|
24643
|
+
}
|
|
24644
|
+
if (toolRegistry.getDefinition(call.name)?.interactive !== true) {
|
|
24456
24645
|
didWork = true;
|
|
24457
24646
|
}
|
|
24458
24647
|
reanchorBatch();
|
|
24459
24648
|
if (!result.success && result.error) {
|
|
24460
24649
|
allErrors.push(result.error);
|
|
24461
24650
|
}
|
|
24462
|
-
|
|
24651
|
+
const wrote = ledger.writes.slice(writesBefore).some((write) => write.ok);
|
|
24652
|
+
if (result.success && result.summary && wrote) {
|
|
24463
24653
|
appliedSummaries.push(result.summary);
|
|
24464
24654
|
}
|
|
24465
24655
|
chatState.appendToolResult(id, result);
|
|
@@ -24468,6 +24658,9 @@ async function runAgentLoop({
|
|
|
24468
24658
|
if (didWork) {
|
|
24469
24659
|
workRounds += 1;
|
|
24470
24660
|
}
|
|
24661
|
+
if (signal?.aborted) {
|
|
24662
|
+
return;
|
|
24663
|
+
}
|
|
24471
24664
|
if (workRounds < MAX_ROUNDS && round < MAX_TOTAL_ROUNDS) {
|
|
24472
24665
|
chatState.beginAssistantTurn();
|
|
24473
24666
|
}
|
|
@@ -24494,10 +24687,20 @@ You can continue by sending another message, or try a more specific request.`;
|
|
|
24494
24687
|
}
|
|
24495
24688
|
chatState.setError(tail);
|
|
24496
24689
|
} finally {
|
|
24497
|
-
endTurnSignal();
|
|
24498
24690
|
endBatch();
|
|
24499
|
-
|
|
24691
|
+
fileTurn(turnAnchor(chatState.messages, turnUserId) ?? "", ledger.writes);
|
|
24692
|
+
}
|
|
24693
|
+
}
|
|
24694
|
+
function parseToolArguments(text) {
|
|
24695
|
+
if (!text) {
|
|
24696
|
+
return {};
|
|
24697
|
+
}
|
|
24698
|
+
const parsed = JSON.parse(text);
|
|
24699
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
24700
|
+
const type = parsed === null ? "null" : Array.isArray(parsed) ? "array" : typeof parsed;
|
|
24701
|
+
throw new TypeError(`arguments must be a JSON object, got ${type}`);
|
|
24500
24702
|
}
|
|
24703
|
+
return parsed;
|
|
24501
24704
|
}
|
|
24502
24705
|
|
|
24503
24706
|
// src/services/ai-system-prompt.ts
|
|
@@ -25209,153 +25412,6 @@ function buildProjectSummary({
|
|
|
25209
25412
|
`);
|
|
25210
25413
|
}
|
|
25211
25414
|
|
|
25212
|
-
// src/services/context-manager.ts
|
|
25213
|
-
var CHARS_PER_TOKEN = 4;
|
|
25214
|
-
var MODEL_CONTEXT_WINDOWS = [
|
|
25215
|
-
["gpt-4o", 128000],
|
|
25216
|
-
["gpt-4.1", 1e6],
|
|
25217
|
-
["gpt-4-turbo", 128000],
|
|
25218
|
-
["gpt-4", 8192],
|
|
25219
|
-
["gpt-3.5", 16385],
|
|
25220
|
-
["o1", 200000],
|
|
25221
|
-
["o3", 200000],
|
|
25222
|
-
["claude", 200000]
|
|
25223
|
-
];
|
|
25224
|
-
var DEFAULT_CONTEXT_WINDOW = 32000;
|
|
25225
|
-
var BUDGET_FRACTION = 0.8;
|
|
25226
|
-
var WARN_FRACTION = 0.5;
|
|
25227
|
-
function contextWindowFor(model) {
|
|
25228
|
-
if (!model) {
|
|
25229
|
-
return DEFAULT_CONTEXT_WINDOW;
|
|
25230
|
-
}
|
|
25231
|
-
const reported = modelContextWindow(model);
|
|
25232
|
-
if (reported !== undefined && reported > 0) {
|
|
25233
|
-
return reported;
|
|
25234
|
-
}
|
|
25235
|
-
const id = model.toLowerCase();
|
|
25236
|
-
let best = 0;
|
|
25237
|
-
let window2 = DEFAULT_CONTEXT_WINDOW;
|
|
25238
|
-
for (const [prefix, size] of MODEL_CONTEXT_WINDOWS) {
|
|
25239
|
-
if (id.startsWith(prefix) && prefix.length > best) {
|
|
25240
|
-
best = prefix.length;
|
|
25241
|
-
window2 = size;
|
|
25242
|
-
}
|
|
25243
|
-
}
|
|
25244
|
-
return window2;
|
|
25245
|
-
}
|
|
25246
|
-
var KEEP_RECENT = 20;
|
|
25247
|
-
var MIN_USER_TURNS = 3;
|
|
25248
|
-
function estimateTokens(input) {
|
|
25249
|
-
if (typeof input === "string") {
|
|
25250
|
-
return Math.ceil(input.length / CHARS_PER_TOKEN);
|
|
25251
|
-
}
|
|
25252
|
-
let total = 0;
|
|
25253
|
-
for (const msg of input) {
|
|
25254
|
-
total += estimateTokens(msg.content || "");
|
|
25255
|
-
if (msg.tool_calls) {
|
|
25256
|
-
for (const tc of msg.tool_calls) {
|
|
25257
|
-
total += estimateTokens(tc.function?.name || "") + estimateTokens(tc.function?.arguments || "") + 4;
|
|
25258
|
-
}
|
|
25259
|
-
}
|
|
25260
|
-
total += 4;
|
|
25261
|
-
}
|
|
25262
|
-
return total;
|
|
25263
|
-
}
|
|
25264
|
-
function trimContext(chatState, systemPrompt) {
|
|
25265
|
-
const window2 = contextWindowFor(chatState.model);
|
|
25266
|
-
const maxTokens = Math.floor(window2 * BUDGET_FRACTION);
|
|
25267
|
-
const warnTokens = Math.floor(window2 * WARN_FRACTION);
|
|
25268
|
-
const systemTokens = estimateTokens(systemPrompt);
|
|
25269
|
-
const allMessages = chatState.messages;
|
|
25270
|
-
const messageTokens = estimateTokens(chatState.toMessagesArray());
|
|
25271
|
-
const total = systemTokens + messageTokens;
|
|
25272
|
-
if (total <= maxTokens) {
|
|
25273
|
-
chatState.setTokenCount(total);
|
|
25274
|
-
chatState.setContextWarning(total >= warnTokens);
|
|
25275
|
-
return { estimatedTokens: total, droppedCount: 0 };
|
|
25276
|
-
}
|
|
25277
|
-
let keepFrom = Math.max(0, allMessages.length - KEEP_RECENT);
|
|
25278
|
-
let preservedTurns = 0;
|
|
25279
|
-
for (let i = allMessages.length - 1;i >= keepFrom; i--) {
|
|
25280
|
-
const { role } = allMessages[i];
|
|
25281
|
-
if (role === "user" || role === "tool") {
|
|
25282
|
-
preservedTurns += 1;
|
|
25283
|
-
}
|
|
25284
|
-
}
|
|
25285
|
-
if (preservedTurns < MIN_USER_TURNS) {
|
|
25286
|
-
for (let i = keepFrom - 1;i >= 0 && preservedTurns < MIN_USER_TURNS; i--) {
|
|
25287
|
-
keepFrom = i;
|
|
25288
|
-
const { role } = allMessages[i];
|
|
25289
|
-
if (role === "user" || role === "tool") {
|
|
25290
|
-
preservedTurns += 1;
|
|
25291
|
-
}
|
|
25292
|
-
}
|
|
25293
|
-
}
|
|
25294
|
-
if (keepFrom <= 0) {
|
|
25295
|
-
chatState.setTokenCount(total);
|
|
25296
|
-
chatState.setContextWarning(true);
|
|
25297
|
-
return { estimatedTokens: total, droppedCount: 0 };
|
|
25298
|
-
}
|
|
25299
|
-
const droppedCount = keepFrom;
|
|
25300
|
-
allMessages.splice(0, keepFrom);
|
|
25301
|
-
allMessages.unshift({
|
|
25302
|
-
id: `ctx_summary_${Date.now()}`,
|
|
25303
|
-
role: "user",
|
|
25304
|
-
content: `[Earlier conversation truncated. ${droppedCount} messages dropped to stay within token budget. The most recent ${allMessages.length - 1} messages are preserved.]`,
|
|
25305
|
-
timestamp: Date.now()
|
|
25306
|
-
});
|
|
25307
|
-
const newTotal = systemTokens + estimateTokens(chatState.toMessagesArray());
|
|
25308
|
-
chatState.setTokenCount(newTotal);
|
|
25309
|
-
chatState.setContextWarning(true);
|
|
25310
|
-
return { estimatedTokens: newTotal, droppedCount };
|
|
25311
|
-
}
|
|
25312
|
-
var UNANSWERED_TOOL_RESULT = JSON.stringify({
|
|
25313
|
-
success: false,
|
|
25314
|
-
error: "This tool call was never completed — the session was reloaded or the history was trimmed."
|
|
25315
|
-
});
|
|
25316
|
-
function pruneOrphanToolMessages(chatState) {
|
|
25317
|
-
const { messages } = chatState;
|
|
25318
|
-
const requested = new Set;
|
|
25319
|
-
const answered = new Set;
|
|
25320
|
-
for (const msg of messages) {
|
|
25321
|
-
if (msg.role === "assistant" && msg.toolCalls) {
|
|
25322
|
-
for (const call of msg.toolCalls) {
|
|
25323
|
-
requested.add(call.id);
|
|
25324
|
-
}
|
|
25325
|
-
} else if (msg.role === "tool" && msg.toolCallId) {
|
|
25326
|
-
answered.add(msg.toolCallId);
|
|
25327
|
-
}
|
|
25328
|
-
}
|
|
25329
|
-
let dropped = 0;
|
|
25330
|
-
for (let i = messages.length - 1;i >= 0; i--) {
|
|
25331
|
-
const msg = messages[i];
|
|
25332
|
-
if (msg.role === "tool" && (!msg.toolCallId || !requested.has(msg.toolCallId))) {
|
|
25333
|
-
messages.splice(i, 1);
|
|
25334
|
-
dropped += 1;
|
|
25335
|
-
}
|
|
25336
|
-
}
|
|
25337
|
-
let sealed = 0;
|
|
25338
|
-
for (let i = messages.length - 1;i >= 0; i--) {
|
|
25339
|
-
const msg = messages[i];
|
|
25340
|
-
if (msg.role !== "assistant" || !msg.toolCalls) {
|
|
25341
|
-
continue;
|
|
25342
|
-
}
|
|
25343
|
-
const missing = msg.toolCalls.filter((call) => !answered.has(call.id));
|
|
25344
|
-
if (missing.length === 0) {
|
|
25345
|
-
continue;
|
|
25346
|
-
}
|
|
25347
|
-
messages.splice(i + 1, 0, ...missing.map((call, n) => ({
|
|
25348
|
-
content: UNANSWERED_TOOL_RESULT,
|
|
25349
|
-
id: `sealed_${call.id}_${n}`,
|
|
25350
|
-
role: "tool",
|
|
25351
|
-
timestamp: msg.timestamp,
|
|
25352
|
-
toolCallId: call.id
|
|
25353
|
-
})));
|
|
25354
|
-
sealed += missing.length;
|
|
25355
|
-
}
|
|
25356
|
-
return { dropped, sealed };
|
|
25357
|
-
}
|
|
25358
|
-
|
|
25359
25415
|
// src/services/render-critic.ts
|
|
25360
25416
|
function translateRenderError(err) {
|
|
25361
25417
|
const msg = err.message || String(err);
|
|
@@ -25558,6 +25614,76 @@ function moveSession(fromRoot, toRoot, id) {
|
|
|
25558
25614
|
writeIndex(toRoot, toIndex);
|
|
25559
25615
|
}
|
|
25560
25616
|
|
|
25617
|
+
// src/services/tool-outcomes.ts
|
|
25618
|
+
var ASK_TOOL = "ask_user";
|
|
25619
|
+
var NEVER_COMPLETED = JSON.parse(UNANSWERED_TOOL_RESULT);
|
|
25620
|
+
function stripToolResults(messages) {
|
|
25621
|
+
return messages.map((message) => {
|
|
25622
|
+
if (!Array.isArray(message.toolCalls)) {
|
|
25623
|
+
return message;
|
|
25624
|
+
}
|
|
25625
|
+
return {
|
|
25626
|
+
...message,
|
|
25627
|
+
toolCalls: message.toolCalls.map((call) => {
|
|
25628
|
+
if (!call || typeof call !== "object") {
|
|
25629
|
+
return call;
|
|
25630
|
+
}
|
|
25631
|
+
const { result: _result, ...rest } = call;
|
|
25632
|
+
return rest;
|
|
25633
|
+
})
|
|
25634
|
+
};
|
|
25635
|
+
});
|
|
25636
|
+
}
|
|
25637
|
+
function parseToolResult(content) {
|
|
25638
|
+
if (typeof content !== "string") {
|
|
25639
|
+
return null;
|
|
25640
|
+
}
|
|
25641
|
+
try {
|
|
25642
|
+
const parsed = JSON.parse(content);
|
|
25643
|
+
return parsed && typeof parsed === "object" && typeof parsed.success === "boolean" ? parsed : null;
|
|
25644
|
+
} catch {
|
|
25645
|
+
return null;
|
|
25646
|
+
}
|
|
25647
|
+
}
|
|
25648
|
+
function backfillToolResults(messages) {
|
|
25649
|
+
return messages.map((message, index) => {
|
|
25650
|
+
if (message.role !== "assistant" || !Array.isArray(message.toolCalls)) {
|
|
25651
|
+
return message;
|
|
25652
|
+
}
|
|
25653
|
+
const replies = new Map;
|
|
25654
|
+
const sealed = new Set;
|
|
25655
|
+
for (let next = index + 1;next < messages.length; next++) {
|
|
25656
|
+
const reply = messages[next];
|
|
25657
|
+
if (reply.role !== "tool") {
|
|
25658
|
+
break;
|
|
25659
|
+
}
|
|
25660
|
+
const result = parseToolResult(reply.content);
|
|
25661
|
+
if (typeof reply.toolCallId === "string" && result && !replies.has(reply.toolCallId)) {
|
|
25662
|
+
replies.set(reply.toolCallId, result);
|
|
25663
|
+
if (reply.content === UNANSWERED_TOOL_RESULT) {
|
|
25664
|
+
sealed.add(reply.toolCallId);
|
|
25665
|
+
}
|
|
25666
|
+
}
|
|
25667
|
+
}
|
|
25668
|
+
return {
|
|
25669
|
+
...message,
|
|
25670
|
+
toolCalls: message.toolCalls.map((call) => {
|
|
25671
|
+
if (!call || typeof call !== "object") {
|
|
25672
|
+
return call;
|
|
25673
|
+
}
|
|
25674
|
+
const record = call;
|
|
25675
|
+
const id = typeof record.id === "string" ? record.id : undefined;
|
|
25676
|
+
const answered = id === undefined ? undefined : replies.get(id);
|
|
25677
|
+
if (record.name === ASK_TOOL) {
|
|
25678
|
+
const reply = answered && id !== undefined && !sealed.has(id) ? answered : undefined;
|
|
25679
|
+
return { ...record, result: reply ?? record.result ?? null };
|
|
25680
|
+
}
|
|
25681
|
+
return { ...record, result: answered ?? record.result ?? NEVER_COMPLETED };
|
|
25682
|
+
})
|
|
25683
|
+
};
|
|
25684
|
+
});
|
|
25685
|
+
}
|
|
25686
|
+
|
|
25561
25687
|
// src/services/document-assistant.ts
|
|
25562
25688
|
function projectRoot() {
|
|
25563
25689
|
return workspace2.projectRoot || "";
|
|
@@ -25565,6 +25691,7 @@ function projectRoot() {
|
|
|
25565
25691
|
function createDocumentAssistant() {
|
|
25566
25692
|
const chatState = createChatState({ model: preferredModel() });
|
|
25567
25693
|
let sessionId = null;
|
|
25694
|
+
let sessionFacts = createSessionFacts();
|
|
25568
25695
|
const getProjectStyle = () => workspace2.projectConfig?.style;
|
|
25569
25696
|
const findOpenTab = (path) => {
|
|
25570
25697
|
for (const tab of workspace2.tabs.values()) {
|
|
@@ -25575,7 +25702,7 @@ function createDocumentAssistant() {
|
|
|
25575
25702
|
return null;
|
|
25576
25703
|
};
|
|
25577
25704
|
const innerRegistry = createToolRegistry();
|
|
25578
|
-
registerAskTool(innerRegistry);
|
|
25705
|
+
registerAskTool(innerRegistry, { onPending: () => persistChat() });
|
|
25579
25706
|
registerAiTools(innerRegistry, {
|
|
25580
25707
|
getTab: () => activeTab2.value,
|
|
25581
25708
|
saveFile: async (relPath, content) => {
|
|
@@ -25644,6 +25771,8 @@ function createDocumentAssistant() {
|
|
|
25644
25771
|
});
|
|
25645
25772
|
const toolRegistry = composeToolRegistries(createGatedToolRegistry(innerRegistry, availability), commandTools);
|
|
25646
25773
|
let controller = null;
|
|
25774
|
+
const turnActive = shallowRef(false);
|
|
25775
|
+
let turnEnded = Promise.resolve();
|
|
25647
25776
|
function buildPrompt() {
|
|
25648
25777
|
const tab = activeTab2.value;
|
|
25649
25778
|
const inventory = projectState ? [...projectState.dirs.values()].flat().filter((e) => e.type === "file").map((e) => e.path) : undefined;
|
|
@@ -25670,7 +25799,7 @@ function createDocumentAssistant() {
|
|
|
25670
25799
|
});
|
|
25671
25800
|
}
|
|
25672
25801
|
async function sendMessage(text) {
|
|
25673
|
-
if (!text.trim() || chatState.status === "streaming") {
|
|
25802
|
+
if (!text.trim() || turnActive.value || chatState.status === "streaming") {
|
|
25674
25803
|
return;
|
|
25675
25804
|
}
|
|
25676
25805
|
if (!sessionId) {
|
|
@@ -25683,32 +25812,49 @@ function createDocumentAssistant() {
|
|
|
25683
25812
|
}
|
|
25684
25813
|
pruneOrphanToolMessages(chatState);
|
|
25685
25814
|
persistChat();
|
|
25815
|
+
controller = new AbortController;
|
|
25816
|
+
const { signal } = controller;
|
|
25817
|
+
turnActive.value = true;
|
|
25818
|
+
let settleTurnEnded;
|
|
25819
|
+
turnEnded = new Promise((settle) => {
|
|
25820
|
+
settleTurnEnded = settle;
|
|
25821
|
+
});
|
|
25686
25822
|
try {
|
|
25687
25823
|
const plat = getPlatform2();
|
|
25688
|
-
const chatUrl = await Promise.resolve(plat.aiChatUrl());
|
|
25689
25824
|
chatState.setModel(preferredModel());
|
|
25690
25825
|
const streamingClient = createProxyStreamingClient({
|
|
25691
|
-
chatUrl,
|
|
25826
|
+
chatUrl: () => plat.aiChatUrl(),
|
|
25692
25827
|
model: chatState.model,
|
|
25693
25828
|
apiKey: getOpenAiKey() || undefined,
|
|
25694
25829
|
baseUrl: getBaseUrl() || undefined
|
|
25695
25830
|
});
|
|
25696
|
-
|
|
25831
|
+
if (sessionFacts.sessionId !== sessionId) {
|
|
25832
|
+
sessionFacts = createSessionFacts(sessionId, sessionFacts.toJSON());
|
|
25833
|
+
}
|
|
25697
25834
|
await runAgentLoop({
|
|
25698
25835
|
chatState,
|
|
25699
25836
|
streamingClient,
|
|
25700
25837
|
toolRegistry,
|
|
25701
25838
|
systemPrompt: buildPrompt(),
|
|
25702
|
-
signal
|
|
25839
|
+
signal,
|
|
25840
|
+
session: sessionFacts,
|
|
25703
25841
|
getTab: () => activeTab2.value
|
|
25704
25842
|
});
|
|
25705
25843
|
} catch (error) {
|
|
25706
25844
|
chatState.setError(error instanceof Error ? error.message : String(error));
|
|
25707
25845
|
} finally {
|
|
25708
25846
|
controller = null;
|
|
25847
|
+
turnActive.value = false;
|
|
25709
25848
|
persistChat();
|
|
25849
|
+
settleTurnEnded();
|
|
25710
25850
|
}
|
|
25711
25851
|
}
|
|
25852
|
+
function isTurnActive() {
|
|
25853
|
+
return turnActive.value;
|
|
25854
|
+
}
|
|
25855
|
+
function whenTurnEnds() {
|
|
25856
|
+
return turnEnded;
|
|
25857
|
+
}
|
|
25712
25858
|
function stop() {
|
|
25713
25859
|
controller?.abort();
|
|
25714
25860
|
cancelAsk();
|
|
@@ -25719,7 +25865,8 @@ function createDocumentAssistant() {
|
|
|
25719
25865
|
stop();
|
|
25720
25866
|
resetAsk();
|
|
25721
25867
|
resetImportRuns();
|
|
25722
|
-
|
|
25868
|
+
resetAiWrites();
|
|
25869
|
+
sessionFacts = createSessionFacts();
|
|
25723
25870
|
chatState.clearChat();
|
|
25724
25871
|
sessionId = null;
|
|
25725
25872
|
setActiveSession(projectRoot(), null);
|
|
@@ -25756,10 +25903,10 @@ function createDocumentAssistant() {
|
|
|
25756
25903
|
return;
|
|
25757
25904
|
}
|
|
25758
25905
|
const msgs = chatState.messages.filter((m) => m.role !== "assistant" || m.content || (m.toolCalls?.length ?? 0) > 0);
|
|
25759
|
-
saveSession(projectRoot(), sessionId, msgs);
|
|
25906
|
+
saveSession(projectRoot(), sessionId, stripToolResults(msgs));
|
|
25760
25907
|
}
|
|
25761
25908
|
function pushRestoredMessages(msgs) {
|
|
25762
|
-
for (const m of msgs) {
|
|
25909
|
+
for (const m of backfillToolResults(msgs)) {
|
|
25763
25910
|
chatState.messages.push({
|
|
25764
25911
|
...m,
|
|
25765
25912
|
id: m.id || `restored_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`
|
|
@@ -25782,7 +25929,9 @@ function createDocumentAssistant() {
|
|
|
25782
25929
|
restoreChat();
|
|
25783
25930
|
return {
|
|
25784
25931
|
chatState,
|
|
25932
|
+
isTurnActive,
|
|
25785
25933
|
sendMessage,
|
|
25934
|
+
whenTurnEnds,
|
|
25786
25935
|
stop,
|
|
25787
25936
|
newChat,
|
|
25788
25937
|
listSessions: listSessions2,
|
|
@@ -25795,7 +25944,7 @@ function createDocumentAssistant() {
|
|
|
25795
25944
|
var ai_credentials_form_default = {
|
|
25796
25945
|
$schema: "https://jxsuite.com/schema/v1",
|
|
25797
25946
|
$id: "StudioAiCredentialsForm",
|
|
25798
|
-
$description: "The AI provider credentials form every gate embeds below the keyless offer: a key, a model, an endpoint, and the two buttons that keep or abandon them. Everything here is a projection — `ui/ai-credentials-form.ts` keeps the drafts, decides what a fetch is sent with, what Save actually persists and whether
|
|
25947
|
+
$description: "The AI provider credentials form every gate embeds below the keyless offer: a key, a model, an endpoint, and the two buttons that keep or abandon them. Everything here is a projection — `ui/ai-credentials-form.ts` keeps the drafts, decides what a fetch is sent with, what Save actually persists and whether there is anything to Save or Cancel at all. This document never reads a setting and never calls the proxy.",
|
|
25799
25948
|
tagName: "div",
|
|
25800
25949
|
attributes: {
|
|
25801
25950
|
part: "ai-creds-form",
|
|
@@ -25811,8 +25960,7 @@ var ai_credentials_form_default = {
|
|
|
25811
25960
|
maxWidth: "320px",
|
|
25812
25961
|
textAlign: "left",
|
|
25813
25962
|
'& [part="title"]': {
|
|
25814
|
-
$description: "
|
|
25815
|
-
alignSelf: "center",
|
|
25963
|
+
$description: "Weight alone, and no `align-self`. It was centred over the column on the grounds that the form is a titled block rather than a list of labelled rows, and in Preferences it is neither: the sheet's own heading, the blurb, this note, the `Model` label and all three fields start at the column's inline edge, so a centred line 102px wide in a 320px column read as a stray, not as a title. A heading belongs over the first character of the thing it names.",
|
|
25816
25964
|
fontWeight: "600"
|
|
25817
25965
|
},
|
|
25818
25966
|
'& [part="note"]': {
|
|
@@ -25832,17 +25980,22 @@ var ai_credentials_form_default = {
|
|
|
25832
25980
|
width: "100%"
|
|
25833
25981
|
},
|
|
25834
25982
|
'& [part="models"]': {
|
|
25983
|
+
$description: "A wrapping row that holds the button and, when there is one, the reason the listing did not finish — and the reason always takes a line of its own, which is what `flex-basis: 100%` on it (below) is for. Beside the button it began 96px into a 320px column, so a six-line upstream body read as a paragraph indented past the control it answers for, while the button floated at that block's vertical middle with about 40px of dead space around it. Under the button, at the column's own inline edge, the reason starts where every other line in the form starts. `flex-start` rather than `center` so that a line which does hold two boxes lines up their first lines and not their middles.",
|
|
25835
25984
|
display: "flex",
|
|
25836
|
-
|
|
25985
|
+
flexWrap: "wrap",
|
|
25986
|
+
alignItems: "flex-start",
|
|
25837
25987
|
gap: "var(--jx-space-3)"
|
|
25838
25988
|
},
|
|
25839
25989
|
'& [part="models-error"]': {
|
|
25840
|
-
$description: 'Why the listing did not finish,
|
|
25990
|
+
$description: 'Why the listing did not finish, said under the button that would try it again. Named `models-error` rather than `error` because the kit\'s own fields render a permanent `[part="error"]` region of their own, and a surface that reused the name would be addressing three empty paragraphs before it reached its own. `flexBasis: 100%` is what puts it under the button rather than beside it: the row wraps, and a basis of the whole line cannot share one, so however short the reason is it always reads as a line of the form (`[part="models"]`). `minWidth: 0` and `overflowWrap: anywhere` because the reason is often an upstream body passed through whole — a 401\'s JSON is one unbroken word — and a flex item\'s automatic minimum is its min-content width: one measured 853px inside the 320px column, and scrolled the whole Preferences section sideways over the buttons below it.',
|
|
25841
25991
|
color: "var(--danger)",
|
|
25842
|
-
fontSize: "var(--jx-text-sm)"
|
|
25992
|
+
fontSize: "var(--jx-text-sm)",
|
|
25993
|
+
flexBasis: "100%",
|
|
25994
|
+
minWidth: "0",
|
|
25995
|
+
overflowWrap: "anywhere"
|
|
25843
25996
|
},
|
|
25844
25997
|
'& [part="actions"]': {
|
|
25845
|
-
$description:
|
|
25998
|
+
$description: 'Buttons at the end of the column, in the order a dismissal precedes a commitment. Still a flex item of the column although it is drawn inside `[part="actions-slot"]`, because that slot is `display: contents` — which is what keeps `alignSelf` meaning the column\'s end.',
|
|
25846
25999
|
display: "flex",
|
|
25847
26000
|
alignSelf: "flex-end",
|
|
25848
26001
|
gap: "var(--jx-space-3)"
|
|
@@ -26044,26 +26197,26 @@ var ai_credentials_form_default = {
|
|
|
26044
26197
|
}
|
|
26045
26198
|
},
|
|
26046
26199
|
{
|
|
26200
|
+
$description: "The commit pair exists only while there is something to commit: `dirty` is the flow's answer to whether Save would change what is stored, compared after the store's own normalisation, so a pasted key's whitespace is not an edit. At rest neither button is drawn. The rule this replaced offered Cancel whenever a key was stored and Save always, so Preferences › Assistant showed a Save that would re-store what was there and a Cancel that abandoned nothing, beside the dialog's own Close. Absent rather than disabled, against the house rule for a control that cannot act, because the pair IS the pending edit's affordance: a disabled pair at rest would still be the two buttons the reader reported seeing with nothing to save, each needing a reason to give for being there. Answering either takes away the button that was pressed, so `surfaces/ai-credentials-form.ts` lands focus on the key field rather than letting it fall out of the sheet.",
|
|
26047
26201
|
tagName: "div",
|
|
26048
26202
|
attributes: {
|
|
26049
|
-
part: "actions"
|
|
26203
|
+
part: "actions-slot",
|
|
26204
|
+
role: "none"
|
|
26050
26205
|
},
|
|
26051
|
-
|
|
26052
|
-
|
|
26206
|
+
style: {
|
|
26207
|
+
display: "contents"
|
|
26208
|
+
},
|
|
26209
|
+
$switch: {
|
|
26210
|
+
$ref: "#/state/dirty"
|
|
26211
|
+
},
|
|
26212
|
+
cases: {
|
|
26213
|
+
true: {
|
|
26053
26214
|
tagName: "div",
|
|
26054
26215
|
attributes: {
|
|
26055
|
-
part: "
|
|
26056
|
-
role: "none"
|
|
26057
|
-
},
|
|
26058
|
-
style: {
|
|
26059
|
-
display: "contents"
|
|
26060
|
-
},
|
|
26061
|
-
$switch: {
|
|
26062
|
-
$ref: "#/state/haveKey"
|
|
26216
|
+
part: "actions"
|
|
26063
26217
|
},
|
|
26064
|
-
|
|
26065
|
-
|
|
26066
|
-
$description: "Only offered once a key exists: with nothing stored there is nothing to go back to, and a Cancel that abandons an empty form to an empty form is a button that does nothing.",
|
|
26218
|
+
children: [
|
|
26219
|
+
{
|
|
26067
26220
|
tagName: "jx-button",
|
|
26068
26221
|
attributes: {
|
|
26069
26222
|
part: "cancel"
|
|
@@ -26089,38 +26242,38 @@ var ai_credentials_form_default = {
|
|
|
26089
26242
|
textContent: "Cancel"
|
|
26090
26243
|
}
|
|
26091
26244
|
]
|
|
26092
|
-
}
|
|
26093
|
-
}
|
|
26094
|
-
},
|
|
26095
|
-
{
|
|
26096
|
-
tagName: "jx-button",
|
|
26097
|
-
attributes: {
|
|
26098
|
-
part: "save"
|
|
26099
|
-
},
|
|
26100
|
-
$props: {
|
|
26101
|
-
size: "sm",
|
|
26102
|
-
variant: "accent"
|
|
26103
|
-
},
|
|
26104
|
-
onclick: {
|
|
26105
|
-
$prototype: "Function",
|
|
26106
|
-
body: [
|
|
26107
|
-
{
|
|
26108
|
-
operator: "call",
|
|
26109
|
-
target: {
|
|
26110
|
-
$ref: "#/state/save"
|
|
26111
|
-
},
|
|
26112
|
-
value: []
|
|
26113
|
-
}
|
|
26114
|
-
]
|
|
26115
|
-
},
|
|
26116
|
-
children: [
|
|
26245
|
+
},
|
|
26117
26246
|
{
|
|
26118
|
-
tagName: "
|
|
26119
|
-
|
|
26247
|
+
tagName: "jx-button",
|
|
26248
|
+
attributes: {
|
|
26249
|
+
part: "save"
|
|
26250
|
+
},
|
|
26251
|
+
$props: {
|
|
26252
|
+
size: "sm",
|
|
26253
|
+
variant: "accent"
|
|
26254
|
+
},
|
|
26255
|
+
onclick: {
|
|
26256
|
+
$prototype: "Function",
|
|
26257
|
+
body: [
|
|
26258
|
+
{
|
|
26259
|
+
operator: "call",
|
|
26260
|
+
target: {
|
|
26261
|
+
$ref: "#/state/save"
|
|
26262
|
+
},
|
|
26263
|
+
value: []
|
|
26264
|
+
}
|
|
26265
|
+
]
|
|
26266
|
+
},
|
|
26267
|
+
children: [
|
|
26268
|
+
{
|
|
26269
|
+
tagName: "span",
|
|
26270
|
+
textContent: "Save"
|
|
26271
|
+
}
|
|
26272
|
+
]
|
|
26120
26273
|
}
|
|
26121
26274
|
]
|
|
26122
26275
|
}
|
|
26123
|
-
|
|
26276
|
+
}
|
|
26124
26277
|
}
|
|
26125
26278
|
]
|
|
26126
26279
|
};
|
|
@@ -26151,7 +26304,11 @@ function createAiCredentialsSurface(view, actions) {
|
|
|
26151
26304
|
return {
|
|
26152
26305
|
host,
|
|
26153
26306
|
update(next) {
|
|
26307
|
+
const answering = !next.dirty && host.querySelector('[part="actions"]')?.contains(document.activeElement);
|
|
26154
26308
|
Object.assign(scope, derive3(next));
|
|
26309
|
+
if (answering && !host.contains(document.activeElement)) {
|
|
26310
|
+
host.querySelector('[part="key"] [part="input"]')?.focus();
|
|
26311
|
+
}
|
|
26155
26312
|
if (mounting || handle !== null && host.contains(handle.root)) {
|
|
26156
26313
|
return;
|
|
26157
26314
|
}
|
|
@@ -26172,11 +26329,23 @@ function createAiCredentialsForm(opts) {
|
|
|
26172
26329
|
let availableModels = [];
|
|
26173
26330
|
let modelsLoading = false;
|
|
26174
26331
|
let modelsError = "";
|
|
26332
|
+
let basis = { apiKey: "", baseUrl: "", model: "" };
|
|
26175
26333
|
let surface = null;
|
|
26334
|
+
function stored() {
|
|
26335
|
+
return { apiKey: getOpenAiKey(), baseUrl: getBaseUrl(), model: storedModel() };
|
|
26336
|
+
}
|
|
26176
26337
|
function loadDrafts() {
|
|
26177
|
-
|
|
26178
|
-
|
|
26179
|
-
|
|
26338
|
+
basis = stored();
|
|
26339
|
+
keyDraft = basis.apiKey;
|
|
26340
|
+
baseUrlDraft = basis.baseUrl;
|
|
26341
|
+
modelDraft = basis.model;
|
|
26342
|
+
}
|
|
26343
|
+
function followStore() {
|
|
26344
|
+
const now = stored();
|
|
26345
|
+
keyDraft = storedEquals(SETTINGS.aiOpenAiKey, keyDraft, basis.apiKey) ? now.apiKey : keyDraft;
|
|
26346
|
+
baseUrlDraft = storedEquals(SETTINGS.aiBaseUrl, baseUrlDraft, basis.baseUrl) ? now.baseUrl : baseUrlDraft;
|
|
26347
|
+
modelDraft = storedEquals(SETTINGS.aiModel, modelDraft, basis.model) ? now.model : modelDraft;
|
|
26348
|
+
basis = now;
|
|
26180
26349
|
}
|
|
26181
26350
|
function startEdit() {
|
|
26182
26351
|
loadDrafts();
|
|
@@ -26227,8 +26396,8 @@ function createAiCredentialsForm(opts) {
|
|
|
26227
26396
|
function view() {
|
|
26228
26397
|
return {
|
|
26229
26398
|
baseUrlDraft,
|
|
26399
|
+
dirty: aiProviderDiffers({ apiKey: keyDraft, baseUrl: baseUrlDraft, model: modelDraft }),
|
|
26230
26400
|
fetchLabel: modelsLoading ? "Fetching…" : availableModels.length > 0 ? "Refresh models" : "Fetch models",
|
|
26231
|
-
haveKey: hasOpenAiKey(),
|
|
26232
26401
|
intro: opts.intro ?? DEFAULT_INTRO,
|
|
26233
26402
|
keyDraft,
|
|
26234
26403
|
modelDraft,
|
|
@@ -26250,6 +26419,7 @@ function createAiCredentialsForm(opts) {
|
|
|
26250
26419
|
surface?.update(view());
|
|
26251
26420
|
}
|
|
26252
26421
|
function render() {
|
|
26422
|
+
followStore();
|
|
26253
26423
|
if (surface) {
|
|
26254
26424
|
surface.update(view());
|
|
26255
26425
|
} else {
|
|
@@ -26293,8 +26463,8 @@ var ai_managed_connect_default = {
|
|
|
26293
26463
|
maxWidth: "280px",
|
|
26294
26464
|
textAlign: "center"
|
|
26295
26465
|
},
|
|
26296
|
-
'& [part="
|
|
26297
|
-
$description:
|
|
26466
|
+
'& [part="connect-failure"]': {
|
|
26467
|
+
$description: 'Why the last attempt did not finish, said under the button that would try it again. Narrower than the lede: a refusal is a sentence to read once, not the thing the surface is about. The name is qualified, and BOTH halves of it are load-bearing. Not `error`, because every kit field draws a permanent, empty `[part="error"]` of its own and the kit is light DOM, so a rule on that name would reach any field this surface ever holds. Not the bare `failure` every panel banner uses either, because this document is MOUNTED inside another one: `new-project.json` puts it in `[part="managed-island"]` and styles its own backend refusal with a descendant `[part="failure"]`, which would draw this sentence with that dialog\'s footer border and padding. A guest surface picks a name its host does not write, and `tests/surface-part-names.test.ts` holds the two island hosts to it.',
|
|
26298
26468
|
margin: "0",
|
|
26299
26469
|
color: "var(--danger)",
|
|
26300
26470
|
fontSize: "var(--jx-text-sm)",
|
|
@@ -26349,7 +26519,7 @@ var ai_managed_connect_default = {
|
|
|
26349
26519
|
{
|
|
26350
26520
|
tagName: "div",
|
|
26351
26521
|
attributes: {
|
|
26352
|
-
part: "
|
|
26522
|
+
part: "connect-failure-slot",
|
|
26353
26523
|
role: "none"
|
|
26354
26524
|
},
|
|
26355
26525
|
style: {
|
|
@@ -26362,7 +26532,7 @@ var ai_managed_connect_default = {
|
|
|
26362
26532
|
true: {
|
|
26363
26533
|
tagName: "p",
|
|
26364
26534
|
attributes: {
|
|
26365
|
-
part: "
|
|
26535
|
+
part: "connect-failure"
|
|
26366
26536
|
},
|
|
26367
26537
|
textContent: "${state.error}"
|
|
26368
26538
|
}
|
|
@@ -26415,6 +26585,7 @@ function createManagedConnectSurface(view, actions) {
|
|
|
26415
26585
|
// src/ui/ai-managed-connect.ts
|
|
26416
26586
|
var LEDE_FRESH = "Recommended — run the assistant on Workers AI in your own Cloudflare account. No API key to create, copy or rotate.";
|
|
26417
26587
|
var LEDE_LAPSED = "Your Cloudflare connection has expired. Reconnect to keep using the assistant.";
|
|
26588
|
+
var LEDE_ACCOUNT = "Cloudflare is connected, but no account is chosen yet. Pick the account the assistant should run on.";
|
|
26418
26589
|
function createManagedConnect(opts) {
|
|
26419
26590
|
let busy = false;
|
|
26420
26591
|
let connectError = "";
|
|
@@ -26425,6 +26596,26 @@ function createManagedConnect(opts) {
|
|
|
26425
26596
|
}
|
|
26426
26597
|
return !isProxyConfigured() || proxyStateCode() === "cf_reconnect_required";
|
|
26427
26598
|
}
|
|
26599
|
+
const needsAccount = () => proxyStateCode() === "cf_account_required";
|
|
26600
|
+
async function verifyConnection() {
|
|
26601
|
+
resetModelCache();
|
|
26602
|
+
await fetchAvailableModels({ force: true });
|
|
26603
|
+
if (proxyStateCode() === "cf_account_required") {
|
|
26604
|
+
connectError = "Cloudflare still reports no account chosen for the assistant. Pick one again, or use your own API key below.";
|
|
26605
|
+
} else if (!isProxyConfigured() || proxyStateCode() === "cf_reconnect_required") {
|
|
26606
|
+
connectError = "Cloudflare connected, but the assistant backend still reports the connection as unusable. " + "Try reconnecting, or use your own API key below.";
|
|
26607
|
+
}
|
|
26608
|
+
}
|
|
26609
|
+
async function chooseAccount() {
|
|
26610
|
+
const { openCfAccountPicker } = await (__preload("3f08rry6"), import("./chunks/cf-account-picker-3f08rry6.js"));
|
|
26611
|
+
if (!await openCfAccountPicker()) {
|
|
26612
|
+
resetModelCache();
|
|
26613
|
+
await fetchAvailableModels({ force: true });
|
|
26614
|
+
connectError = "Cloudflare is connected, but no account is chosen yet — pick one to finish setting up the assistant.";
|
|
26615
|
+
return;
|
|
26616
|
+
}
|
|
26617
|
+
await verifyConnection();
|
|
26618
|
+
}
|
|
26428
26619
|
function ensureProbe() {
|
|
26429
26620
|
ensureProxyProbe(opts.requestRender);
|
|
26430
26621
|
}
|
|
@@ -26437,17 +26628,10 @@ function createManagedConnect(opts) {
|
|
|
26437
26628
|
return;
|
|
26438
26629
|
}
|
|
26439
26630
|
if (!outcome.connection.accountId) {
|
|
26440
|
-
|
|
26441
|
-
|
|
26442
|
-
connectError = "Cloudflare is connected, but no account is chosen yet — pick one to finish setting up the assistant.";
|
|
26443
|
-
return;
|
|
26444
|
-
}
|
|
26445
|
-
}
|
|
26446
|
-
resetModelCache();
|
|
26447
|
-
await fetchAvailableModels({ force: true });
|
|
26448
|
-
if (!isProxyConfigured() || proxyStateCode() === "cf_reconnect_required") {
|
|
26449
|
-
connectError = "Cloudflare connected, but the assistant backend still reports the connection as unusable. Try reconnecting, or use your own API key below.";
|
|
26631
|
+
await chooseAccount();
|
|
26632
|
+
return;
|
|
26450
26633
|
}
|
|
26634
|
+
await verifyConnection();
|
|
26451
26635
|
}
|
|
26452
26636
|
async function connect() {
|
|
26453
26637
|
if (busy) {
|
|
@@ -26457,7 +26641,7 @@ function createManagedConnect(opts) {
|
|
|
26457
26641
|
connectError = "";
|
|
26458
26642
|
opts.requestRender();
|
|
26459
26643
|
try {
|
|
26460
|
-
await settle(await getPlatform2().cfConnect?.() ?? null);
|
|
26644
|
+
await (needsAccount() ? chooseAccount() : settle(await getPlatform2().cfConnect?.() ?? null));
|
|
26461
26645
|
} catch (error) {
|
|
26462
26646
|
connectError = error instanceof Error ? error.message : String(error);
|
|
26463
26647
|
}
|
|
@@ -26465,6 +26649,14 @@ function createManagedConnect(opts) {
|
|
|
26465
26649
|
opts.requestRender();
|
|
26466
26650
|
}
|
|
26467
26651
|
function view() {
|
|
26652
|
+
if (needsAccount()) {
|
|
26653
|
+
return {
|
|
26654
|
+
buttonLabel: busy ? "Choosing…" : "Choose Cloudflare account",
|
|
26655
|
+
busy,
|
|
26656
|
+
error: connectError,
|
|
26657
|
+
intro: LEDE_ACCOUNT
|
|
26658
|
+
};
|
|
26659
|
+
}
|
|
26468
26660
|
const lapsed = proxyStateCode() === "cf_reconnect_required";
|
|
26469
26661
|
const idleLabel = lapsed ? "Reconnect Cloudflare" : "Connect Cloudflare";
|
|
26470
26662
|
return {
|
|
@@ -26758,7 +26950,8 @@ var preferences_default = {
|
|
|
26758
26950
|
maxHeight: "60vh",
|
|
26759
26951
|
overflowY: "auto"
|
|
26760
26952
|
},
|
|
26761
|
-
'& [part="title"]': {
|
|
26953
|
+
'& [part="section"] > [part="title"]': {
|
|
26954
|
+
$description: 'The sheet\'s own heading, and a CHILD selector because the Assistant section mounts other documents into this one\'s light DOM: the credentials form draws a `[part="title"]` of its own ("AI provider key"), and as a bare descendant this rule set the section heading\'s size on it. A host styles the boxes it draws, never a name its guest also uses — `tests/surface-part-names.test.ts` holds the island hosts to that.',
|
|
26762
26955
|
margin: "0",
|
|
26763
26956
|
fontSize: "var(--jx-text-lg)",
|
|
26764
26957
|
color: "var(--fg)"
|
|
@@ -26794,8 +26987,10 @@ var preferences_default = {
|
|
|
26794
26987
|
gap: "var(--jx-space-2)"
|
|
26795
26988
|
},
|
|
26796
26989
|
'& [part="account"]': {
|
|
26990
|
+
$description: "A wrapping row (studio-ui-guidelines.md §4.6): the sentence and the actions share one line while the sentence keeps its basis, and the actions drop under it when they would not. A brokered Cloudflare row carries two actions (Reconnect or Choose account, beside Disconnect), and on one line they squeezed the sentence into a column about 114px wide at the default width. Every single-action row measures exactly what it did before the wrap existed. `align-items: flex-start`, not `center`, because the sentence's height is not the row's: the AI row's endpoint wraps to five lines, and centred that put Disconnect level with the fourth line of a URL while the heading it acts on sat at the top, so the card read as two unrelated things. Pinned to the start, the button is always beside its label.",
|
|
26797
26991
|
display: "flex",
|
|
26798
|
-
|
|
26992
|
+
flexWrap: "wrap",
|
|
26993
|
+
alignItems: "flex-start",
|
|
26799
26994
|
justifyContent: "space-between",
|
|
26800
26995
|
gap: "var(--jx-space-4)",
|
|
26801
26996
|
padding: "var(--jx-space-3)",
|
|
@@ -26803,8 +26998,9 @@ var preferences_default = {
|
|
|
26803
26998
|
borderRadius: "var(--radius)"
|
|
26804
26999
|
},
|
|
26805
27000
|
'& [part="account-text"]': {
|
|
26806
|
-
$description: "`min-width: 0`
|
|
27001
|
+
$description: "The wrap threshold is this basis, not a width: 120px of sentence is the least a row keeps before its actions take a line of their own. Keep it well under 200px, or a single-action row at the default sheet width wraps at rest. `min-width: 0` lets the column shrink below its content, and what then fits the detail into it is the detail's own `overflow-wrap`. Nothing here ellipsizes: the detail is the only place the row names its endpoint or account, so a truncated one would hide exactly the part that tells two of them apart.",
|
|
26807
27002
|
display: "flex",
|
|
27003
|
+
flex: "1 1 120px",
|
|
26808
27004
|
flexDirection: "column",
|
|
26809
27005
|
gap: "var(--jx-space-1)",
|
|
26810
27006
|
minWidth: "0"
|
|
@@ -26813,19 +27009,25 @@ var preferences_default = {
|
|
|
26813
27009
|
color: "var(--fg)"
|
|
26814
27010
|
},
|
|
26815
27011
|
'& [part="account-detail"]': {
|
|
27012
|
+
$description: "`overflow-wrap: anywhere` because the detail carries author-supplied strings with no break opportunity in them: the AI row's endpoint URL (a Cloudflare AI Gateway URL is a 32-hex account id between slashes) and the local Cloudflare row's account id. Without it one of them measured 419px in a 223px column, ran under Disconnect, past the card's border, and scrolled the whole section sideways. `anywhere` rather than `break-word`, because only `anywhere` lowers the min-content the row is sized from; not `word-break: break-all`, which would split every ordinary word in the sentences around it. It sits here rather than on the column because the label is a fixed service name and only the detail is unbounded.",
|
|
26816
27013
|
fontSize: "var(--jx-text-sm)",
|
|
26817
|
-
color: "var(--fg-dim)"
|
|
27014
|
+
color: "var(--fg-dim)",
|
|
27015
|
+
overflowWrap: "anywhere"
|
|
26818
27016
|
},
|
|
26819
27017
|
'& [part="account-actions"]': {
|
|
27018
|
+
$description: "`margin-inline-start: auto` keeps the actions at the row's end when the wrap puts them on a line of their own; on a shared line `space-between` already puts them there, so the margin changes nothing.",
|
|
26820
27019
|
display: "flex",
|
|
26821
27020
|
alignItems: "center",
|
|
26822
27021
|
gap: "var(--jx-space-3)",
|
|
26823
|
-
flexShrink: "0"
|
|
27022
|
+
flexShrink: "0",
|
|
27023
|
+
marginInlineStart: "auto"
|
|
26824
27024
|
},
|
|
26825
27025
|
'& [part="keys"]': {
|
|
27026
|
+
$description: "The inline-end padding is for the scrollbar, not for rhythm. A row whose title wraps pushes its `Change` button to the very end of the line, and the sheet always scrolls (64 bindings), so that button's right edge landed exactly on the scroller's content edge and read as touching the scrollbar track.",
|
|
26826
27027
|
display: "flex",
|
|
26827
27028
|
flexDirection: "column",
|
|
26828
|
-
gap: "var(--jx-space-1)"
|
|
27029
|
+
gap: "var(--jx-space-1)",
|
|
27030
|
+
paddingInlineEnd: "var(--jx-space-2)"
|
|
26829
27031
|
},
|
|
26830
27032
|
'& [part="refusal"]': {
|
|
26831
27033
|
display: "flex",
|
|
@@ -26850,9 +27052,10 @@ var preferences_default = {
|
|
|
26850
27052
|
textTransform: "uppercase",
|
|
26851
27053
|
color: "var(--fg-dim)"
|
|
26852
27054
|
},
|
|
26853
|
-
'& [part="key"]': {
|
|
27055
|
+
'& [part="key"][data-command]': {
|
|
27056
|
+
$description: "Qualified by `data-command`, the attribute a binding row carries, because `key` is also the credentials form's API-key FIELD and that form is mounted into this sheet's own light DOM: as a bare descendant this rule laid a 16px flex row over a `jx-textfield`, which drew it 24px tall beside the 20px endpoint field under it. `center`, not `baseline`: the `Change` and `Reset` buttons are small kit action buttons, and a small kit control is layout-contained so its outset hit area adds nothing to this sheet's scrollable overflow, which leaves it with no baseline to export. A baseline row synthesises one from the button's bottom edge instead, and every row grew from 24px to 29px with the chord and title dropped onto it. Centred, every single-line row measures exactly what the baseline layout did (24px, with the chord, the title and the button label on one baseline); a row whose title wraps centres its chord and buttons between the title's lines. surface-baseline-rows.test.ts holds the rule for every surface.",
|
|
26854
27057
|
display: "flex",
|
|
26855
|
-
alignItems: "
|
|
27058
|
+
alignItems: "center",
|
|
26856
27059
|
gap: "var(--jx-space-4)",
|
|
26857
27060
|
padding: "var(--jx-space-1) 0"
|
|
26858
27061
|
},
|
|
@@ -26864,6 +27067,8 @@ var preferences_default = {
|
|
|
26864
27067
|
color: "var(--fg)"
|
|
26865
27068
|
},
|
|
26866
27069
|
'& [part="key-title"]': {
|
|
27070
|
+
$description: "`flex: 1` for the same reason the chord beside it is a fixed 96px column: down 64 rows, an action that starts where the last title ended is not scannable. The title took its content's width, so `Change` landed at fourteen different offsets in the first twenty rows, and at the line's very end on a row whose title wrapped. Growing the title instead pins the actions to that end, where a reader finds them by scanning one edge. `minWidth: 0` is what still lets a long title wrap rather than widen the sheet.",
|
|
27071
|
+
flex: "1",
|
|
26867
27072
|
color: "var(--fg-dim)",
|
|
26868
27073
|
minWidth: "0"
|
|
26869
27074
|
}
|
|
@@ -27608,6 +27813,7 @@ function openPreferencesSurface(options) {
|
|
|
27608
27813
|
const box = element.querySelector('dialog[part="dialog"]') ?? element;
|
|
27609
27814
|
box.setAttribute(REGION_ATTR, overlayRegion("dialog", "preferences"));
|
|
27610
27815
|
showModal(element);
|
|
27816
|
+
element.querySelector('[part="nav-item"][aria-current="true"]')?.focus();
|
|
27611
27817
|
return element;
|
|
27612
27818
|
});
|
|
27613
27819
|
return {
|
|
@@ -38895,6 +39101,9 @@ function formatErrorAdvice(error) {
|
|
|
38895
39101
|
if (lower.includes("500") || lower.includes("internal")) {
|
|
38896
39102
|
return "The upstream API returned a server error. Try again in a moment.";
|
|
38897
39103
|
}
|
|
39104
|
+
if (lower.includes("empty reply")) {
|
|
39105
|
+
return "Try again, or pick another model: some send back nothing for a request they cannot handle.";
|
|
39106
|
+
}
|
|
38898
39107
|
return "";
|
|
38899
39108
|
}
|
|
38900
39109
|
function projectCommand(id, opts = {}, registry = activeRegistry()) {
|
|
@@ -38924,7 +39133,7 @@ function tokenLabel2(tokens) {
|
|
|
38924
39133
|
function tokenHint(tokens, overBudget) {
|
|
38925
39134
|
return overBudget ? `About ${tokens.toLocaleString()} tokens — past half the model's context. ` + "The oldest turns are dropped as this grows; start a new chat to keep them." : `About ${tokens.toLocaleString()} tokens of the model's context in use`;
|
|
38926
39135
|
}
|
|
38927
|
-
var
|
|
39136
|
+
var ASK_TOOL2 = "ask_user";
|
|
38928
39137
|
var IMPORT_TOOL = "import_site";
|
|
38929
39138
|
function parseAsk(tc) {
|
|
38930
39139
|
let parsed;
|
|
@@ -38944,7 +39153,7 @@ function parseAsk(tc) {
|
|
|
38944
39153
|
}
|
|
38945
39154
|
function toolOutcome(tc, live = true) {
|
|
38946
39155
|
if (!tc.result) {
|
|
38947
|
-
return tc.name ===
|
|
39156
|
+
return tc.name === ASK_TOOL2 && !live ? "unanswered" : "pending";
|
|
38948
39157
|
}
|
|
38949
39158
|
return tc.result.success ? "ok" : "failed";
|
|
38950
39159
|
}
|
|
@@ -38953,7 +39162,12 @@ function toolOutcomeText(tc) {
|
|
|
38953
39162
|
if (!result) {
|
|
38954
39163
|
return "";
|
|
38955
39164
|
}
|
|
38956
|
-
|
|
39165
|
+
const text = (result.success ? result.summary : result.error) ?? "";
|
|
39166
|
+
if (tc.name === IMPORT_TOOL && result.success) {
|
|
39167
|
+
const end = /\.(?:\s|$)/.exec(text);
|
|
39168
|
+
return end ? text.slice(0, end.index + 1) : text;
|
|
39169
|
+
}
|
|
39170
|
+
return text;
|
|
38957
39171
|
}
|
|
38958
39172
|
function noImport() {
|
|
38959
39173
|
return {
|
|
@@ -38992,7 +39206,7 @@ function projectChip(tc, handlers = {}) {
|
|
|
38992
39206
|
const outcome = toolOutcome(tc, tc.id === handlers.pendingId || Boolean(tc.result));
|
|
38993
39207
|
const text = toolOutcomeText(tc);
|
|
38994
39208
|
const run = tc.name === IMPORT_TOOL ? handlers.importRun?.(tc.id) ?? null : null;
|
|
38995
|
-
const ask = tc.name ===
|
|
39209
|
+
const ask = tc.name === ASK_TOOL2 ? parseAsk(tc) : null;
|
|
38996
39210
|
const answered = tc.result?.success ? tc.result.data ?? null : null;
|
|
38997
39211
|
const base = {
|
|
38998
39212
|
answer: answered ? answered.skipped ? "You decide" : answered.answer ?? "" : "",
|
|
@@ -39441,7 +39655,7 @@ function createComposer(opts) {
|
|
|
39441
39655
|
}
|
|
39442
39656
|
}
|
|
39443
39657
|
function send() {
|
|
39444
|
-
if (!draft.trim() || opts.isStreaming()) {
|
|
39658
|
+
if (!draft.trim() || opts.isStreaming() && !(opts.isAwaiting?.() ?? false)) {
|
|
39445
39659
|
return;
|
|
39446
39660
|
}
|
|
39447
39661
|
const text = buildMessageWithContext(draft, chips);
|
|
@@ -39474,7 +39688,7 @@ function createComposer(opts) {
|
|
|
39474
39688
|
placeholder: awaiting ? "Answer the assistant… (Enter to reply)" : "Ask the assistant… (Enter to send)",
|
|
39475
39689
|
sendDisabled: draft.trim().length === 0,
|
|
39476
39690
|
sendLabel: awaiting ? "Answer" : "Send",
|
|
39477
|
-
sendState: streaming ? "stop" : "send"
|
|
39691
|
+
sendState: !awaiting && streaming ? "stop" : "send"
|
|
39478
39692
|
};
|
|
39479
39693
|
}
|
|
39480
39694
|
return {
|
|
@@ -39635,12 +39849,15 @@ var ai_chat_default = {
|
|
|
39635
39849
|
color: "var(--fg-dim)"
|
|
39636
39850
|
},
|
|
39637
39851
|
'& [part="user-body"]': {
|
|
39852
|
+
$description: "The transcript is content, not interface, and the panel offers no copy action, so each message body opts back in to selection against the root's `user-select: none`: this bubble, the model's `md`, the `streaming` text and the `import-log`. The header, the chips and the composer's buttons around them stay unselectable.",
|
|
39638
39853
|
whiteSpace: "pre-wrap",
|
|
39639
|
-
overflowWrap: "anywhere"
|
|
39854
|
+
overflowWrap: "anywhere",
|
|
39855
|
+
userSelect: "text"
|
|
39640
39856
|
},
|
|
39641
39857
|
'& [part="md"]': {
|
|
39642
|
-
$description: "The island `panels/ai-chat/chat-markdown.ts` fills — the app's ONE injection sink, and the markup reaching it has been through `markdownToHtml` and the Trusted Types policy that asserts it did. The document renders the host node and nothing inside it (specs/studio-ui-guidelines.md §9.4).",
|
|
39643
|
-
overflowWrap: "anywhere"
|
|
39858
|
+
$description: "The island `panels/ai-chat/chat-markdown.ts` fills — the app's ONE injection sink, and the markup reaching it has been through `markdownToHtml` and the Trusted Types policy that asserts it did. The document renders the host node and nothing inside it (specs/studio-ui-guidelines.md §9.4). A reply is the text a reader most often copies out of this panel, code blocks included, so it is selectable.",
|
|
39859
|
+
overflowWrap: "anywhere",
|
|
39860
|
+
userSelect: "text"
|
|
39644
39861
|
},
|
|
39645
39862
|
'& [part="md"] > *:first-child': {
|
|
39646
39863
|
marginTop: "0"
|
|
@@ -39675,8 +39892,10 @@ var ai_chat_default = {
|
|
|
39675
39892
|
padding: "var(--jx-space-1) 6px"
|
|
39676
39893
|
},
|
|
39677
39894
|
'& [part="streaming"]': {
|
|
39895
|
+
$description: 'The reply while it arrives, selectable for the reason `[part="md"]` is. The cursor is generated content, which no selection ever includes.',
|
|
39678
39896
|
whiteSpace: "pre-wrap",
|
|
39679
39897
|
overflowWrap: "anywhere",
|
|
39898
|
+
userSelect: "text",
|
|
39680
39899
|
"::after": {
|
|
39681
39900
|
content: '"▋"',
|
|
39682
39901
|
color: "var(--accent)",
|
|
@@ -39852,6 +40071,8 @@ var ai_chat_default = {
|
|
|
39852
40071
|
marginRight: "var(--jx-space-2)"
|
|
39853
40072
|
},
|
|
39854
40073
|
'& [part="import-log"]': {
|
|
40074
|
+
$description: "A captured log, kept selectable against the root's `user-select: none`: its lines are what a reader pastes into a bug report, and nothing here offers to copy them.",
|
|
40075
|
+
userSelect: "text",
|
|
39855
40076
|
marginTop: "var(--jx-space-2)",
|
|
39856
40077
|
maxHeight: "40vh",
|
|
39857
40078
|
overflowY: "auto",
|
|
@@ -39907,7 +40128,8 @@ var ai_chat_default = {
|
|
|
39907
40128
|
color: "var(--warning)",
|
|
39908
40129
|
fontStyle: "normal"
|
|
39909
40130
|
},
|
|
39910
|
-
'& [part="error"]': {
|
|
40131
|
+
'& [part="error-card"]': {
|
|
40132
|
+
$description: "The failed turn's card. It is `error-card` rather than `error` because the model picker mounted into the composer is a `jx-select`, and every kit field draws a permanent, empty `[part=\"error\"]` of its own in this same light-DOM tree: under that name this rule, padding and wash drew an 18×12 red bar over the picker's top-left corner that took the click. Its children keep the `error-` prefix, which the kit does not use.",
|
|
39911
40133
|
borderLeft: "2px solid var(--danger)",
|
|
39912
40134
|
borderRadius: "var(--jx-radius-sm)",
|
|
39913
40135
|
background: "var(--hover-bg)",
|
|
@@ -40090,7 +40312,7 @@ var ai_chat_default = {
|
|
|
40090
40312
|
'& [part="tool-chip"][data-outcome="failed"]': {
|
|
40091
40313
|
borderLeftColor: "LinkText"
|
|
40092
40314
|
},
|
|
40093
|
-
'& [part="error"]': {
|
|
40315
|
+
'& [part="error-card"]': {
|
|
40094
40316
|
borderLeftColor: "LinkText"
|
|
40095
40317
|
}
|
|
40096
40318
|
},
|
|
@@ -41049,7 +41271,7 @@ var ai_chat_default = {
|
|
|
41049
41271
|
shown: {
|
|
41050
41272
|
tagName: "div",
|
|
41051
41273
|
attributes: {
|
|
41052
|
-
part: "error",
|
|
41274
|
+
part: "error-card",
|
|
41053
41275
|
role: "alert"
|
|
41054
41276
|
},
|
|
41055
41277
|
children: [
|
|
@@ -41995,8 +42217,13 @@ function watchAssistant() {
|
|
|
41995
42217
|
const last = cs.messages.at(-1);
|
|
41996
42218
|
last?.content;
|
|
41997
42219
|
last?.toolCalls?.length;
|
|
42220
|
+
for (const tc of latestRequest(cs.messages)?.toolCalls ?? []) {
|
|
42221
|
+
tc.result;
|
|
42222
|
+
}
|
|
41998
42223
|
cs.status;
|
|
41999
42224
|
cs.error;
|
|
42225
|
+
assistant.isTurnActive();
|
|
42226
|
+
cs.tokenCount;
|
|
42000
42227
|
activeRegistry();
|
|
42001
42228
|
pendingAsk();
|
|
42002
42229
|
const run = activeImportRun();
|
|
@@ -42007,6 +42234,14 @@ function watchAssistant() {
|
|
|
42007
42234
|
});
|
|
42008
42235
|
});
|
|
42009
42236
|
}
|
|
42237
|
+
function latestRequest(messages) {
|
|
42238
|
+
for (let index = messages.length - 1;index >= 0; index--) {
|
|
42239
|
+
if (messages[index].role !== "tool") {
|
|
42240
|
+
return messages[index];
|
|
42241
|
+
}
|
|
42242
|
+
}
|
|
42243
|
+
return;
|
|
42244
|
+
}
|
|
42010
42245
|
function mountAiPanel() {
|
|
42011
42246
|
if (mounted) {
|
|
42012
42247
|
return;
|
|
@@ -42015,7 +42250,7 @@ function mountAiPanel() {
|
|
|
42015
42250
|
onCredentialsChanged(renderAiPanel);
|
|
42016
42251
|
}
|
|
42017
42252
|
async function handleAssistantSend(text) {
|
|
42018
|
-
if (!text.trim()
|
|
42253
|
+
if (!text.trim()) {
|
|
42019
42254
|
return;
|
|
42020
42255
|
}
|
|
42021
42256
|
if (answerAsk(text.trim())) {
|
|
@@ -42023,12 +42258,17 @@ async function handleAssistantSend(text) {
|
|
|
42023
42258
|
renderAiPanel();
|
|
42024
42259
|
return;
|
|
42025
42260
|
}
|
|
42261
|
+
if (isAssistantStreaming()) {
|
|
42262
|
+
return;
|
|
42263
|
+
}
|
|
42026
42264
|
view5 = "chat";
|
|
42027
42265
|
surface?.pin();
|
|
42028
42266
|
renderAiPanel();
|
|
42029
42267
|
try {
|
|
42030
42268
|
await assistant.sendMessage(text);
|
|
42031
|
-
} catch {}
|
|
42269
|
+
} catch {} finally {
|
|
42270
|
+
renderAiPanel();
|
|
42271
|
+
}
|
|
42032
42272
|
}
|
|
42033
42273
|
async function handleRetry() {
|
|
42034
42274
|
const cs = assistant.chatState;
|
|
@@ -42062,6 +42302,9 @@ function handleRestore(messageId) {
|
|
|
42062
42302
|
renderAiPanel();
|
|
42063
42303
|
}
|
|
42064
42304
|
async function seedAssistantPrompt(text) {
|
|
42305
|
+
if (assistant.isTurnActive()) {
|
|
42306
|
+
await assistant.whenTurnEnds();
|
|
42307
|
+
}
|
|
42065
42308
|
await handleAssistantSend(text);
|
|
42066
42309
|
}
|
|
42067
42310
|
async function revealImportHandoff(brief) {
|
|
@@ -42120,7 +42363,7 @@ function isAssistantWaiting() {
|
|
|
42120
42363
|
return isAwaitingAnswer();
|
|
42121
42364
|
}
|
|
42122
42365
|
function isAssistantStreaming() {
|
|
42123
|
-
return assistant.
|
|
42366
|
+
return assistant.isTurnActive();
|
|
42124
42367
|
}
|
|
42125
42368
|
function stop() {
|
|
42126
42369
|
assistant.stop();
|
|
@@ -42293,6 +42536,9 @@ function assistantCommands() {
|
|
|
42293
42536
|
];
|
|
42294
42537
|
}
|
|
42295
42538
|
|
|
42539
|
+
// src/hosting/document.ts
|
|
42540
|
+
var BOOT_META = { name: "jx-boot", content: "launcher" };
|
|
42541
|
+
|
|
42296
42542
|
// src/services/import-client.ts
|
|
42297
42543
|
var IMPORT_WARNING_PHASE = "warning";
|
|
42298
42544
|
async function streamImport(endpoint, opts, onProgress, signal, onReady) {
|
|
@@ -42466,7 +42712,7 @@ var STUDIO_ROUTES = {
|
|
|
42466
42712
|
resolveSite: route("GET", "/__studio/resolve-site", "Resolve the owning site of a file path {sitePath, projectConfig?, fileRelPath?}"),
|
|
42467
42713
|
sites: route("GET", "/__studio/sites", "Enumerate site projects [{config, path}] (backs listProjects)", "The welcome screen's Projects catalogue stays hidden."),
|
|
42468
42714
|
findProject: route("GET", "/__studio/find-project", "Locate a project directory by name outside the root", "openProject falls back to config-matching only."),
|
|
42469
|
-
locateDirectory: route("GET", "/__studio/locate-directory", `Resolve the absolute path of a showDirectoryPicker() folder by the id in its ${LOCATION_ID_FILE}`, "
|
|
42715
|
+
locateDirectory: route("GET", "/__studio/locate-directory", `Resolve the absolute path of a showDirectoryPicker() folder by the id in its ${LOCATION_ID_FILE}`, "Browse… reports that the folder could not be placed; the Location is typed by hand."),
|
|
42470
42716
|
createProject: route("POST", "/__studio/create-project", "Scaffold a project at a caller-chosen destination → {root, config}"),
|
|
42471
42717
|
starters: route("GET", "/__studio/starters", "Starter templates (StarterInfo[])", "The New Project picker offers only blank/templates."),
|
|
42472
42718
|
importSite: route("POST", "/__studio/import-site", "Clone a live website into a project; streams NDJSON progress", "The New Project Import tab is unavailable."),
|
|
@@ -42475,6 +42721,7 @@ var STUDIO_ROUTES = {
|
|
|
42475
42721
|
fileWrite: route("PUT", "/__studio/file", "Write a file's text content"),
|
|
42476
42722
|
fileDelete: route("DELETE", "/__studio/file", "Delete a file"),
|
|
42477
42723
|
fileUpload: route("POST", "/__studio/file/upload", "Upload binary content to a path → UploadResult {path, size?}. `path` is the answer, not an " + "echo: a store that de-duplicates, suffixes, or normalizes names reports what it really wrote"),
|
|
42724
|
+
fileBytes: route("GET", "/__studio/file/bytes", "A project file's raw bytes, undecoded — the read side of `fileUpload`, for a caller that must " + "not go through UTF-8", "Studio can only read a project file as text, so any surface that must decode one stays hidden."),
|
|
42478
42725
|
documentRaw: route("GET", "/__studio/raw/", "A project file as its own bytes, at its own URL — the mount a canvas renders against when " + "the editor origin does not serve the site's URL space. The project-relative path is " + "appended to this prefix", "The canvas cannot address project files by URL, so a host whose origin does not serve the " + "site URL space renders no component $refs and no images."),
|
|
42479
42726
|
fileRename: route("POST", "/__studio/file/rename", "Rename/move (+ refactor report). `from`/`to` are server-root-relative; every path in the " + "report is relative to the active project (specs/server.md §4.1)"),
|
|
42480
42727
|
references: route("GET", "/__studio/references", "Where a file or a component tag is used (?path=&tag=, at least one) → ReferencesResult " + "{files, filesReferencing, refsTotal} — the read side of the rename refactor's own walker, " + "cached until the backend's watcher sees the tree move (backs findReferences). `?path=` is " + "server-root-relative and every path in the answer is relative to the active project; a " + "target outside that project is a 400, never a zero-result 200 (specs/server.md §4.1)", 'Usage counts are hidden: no "Used on N pages" in the inspector, no Selection: Find Usages, ' + "and delete/rename confirmations state no reference count."),
|
|
@@ -42775,6 +43022,13 @@ function createCloudPlatform(project) {
|
|
|
42775
43022
|
const data = await okJson(await api(`/file?path=${encodeURIComponent(path)}`), `Failed to read file: ${path}`);
|
|
42776
43023
|
return data.content;
|
|
42777
43024
|
},
|
|
43025
|
+
async readFileBytes(path) {
|
|
43026
|
+
const res = await api(`/file/bytes?path=${encodeURIComponent(path)}`);
|
|
43027
|
+
if (!res.ok) {
|
|
43028
|
+
throw new Error(await errorMessage(res, `Failed to read file bytes: ${path}`));
|
|
43029
|
+
}
|
|
43030
|
+
return res.arrayBuffer();
|
|
43031
|
+
},
|
|
42778
43032
|
async writeFile(path, content) {
|
|
42779
43033
|
const res = await api("/file", {
|
|
42780
43034
|
method: "PUT",
|
|
@@ -42825,7 +43079,7 @@ function createCloudPlatform(project) {
|
|
|
42825
43079
|
}
|
|
42826
43080
|
const { offer } = negotiated;
|
|
42827
43081
|
collabConnection ??= (async () => {
|
|
42828
|
-
const { createWsCollabConnection } = await (__preload("
|
|
43082
|
+
const { createWsCollabConnection } = await (__preload("erze4czt"), import("./chunks/ws-client-erze4czt.js"));
|
|
42829
43083
|
const scheme = location.protocol === "https:" ? "wss" : "ws";
|
|
42830
43084
|
return createWsCollabConnection({
|
|
42831
43085
|
hydratePath: async (path) => {
|
|
@@ -43325,32 +43579,50 @@ function newLocationId() {
|
|
|
43325
43579
|
globalThis.crypto.getRandomValues(bytes);
|
|
43326
43580
|
return [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
43327
43581
|
}
|
|
43582
|
+
function nameOf(error) {
|
|
43583
|
+
return typeof error === "object" && error !== null && "name" in error && typeof error.name === "string" ? error.name : "";
|
|
43584
|
+
}
|
|
43328
43585
|
async function pickDirectoryPath(locate) {
|
|
43329
43586
|
const show = globalThis.showDirectoryPicker;
|
|
43330
43587
|
if (typeof show !== "function") {
|
|
43331
|
-
|
|
43588
|
+
throw new TypeError("This browser has no folder chooser. Type the folder's path into Location instead.");
|
|
43332
43589
|
}
|
|
43333
43590
|
let handle;
|
|
43334
43591
|
try {
|
|
43335
43592
|
handle = await show({ id: "jx-new-project-location", mode: "readwrite", startIn: "documents" });
|
|
43336
|
-
} catch {
|
|
43337
|
-
|
|
43593
|
+
} catch (error) {
|
|
43594
|
+
if (nameOf(error) === "AbortError") {
|
|
43595
|
+
return null;
|
|
43596
|
+
}
|
|
43597
|
+
throw new Error(`The folder chooser could not open (${errorMessage2(error)}). Type the folder's path into Location instead.`, { cause: error });
|
|
43338
43598
|
}
|
|
43599
|
+
const { name } = handle;
|
|
43339
43600
|
const id = newLocationId();
|
|
43601
|
+
let tagged = false;
|
|
43340
43602
|
try {
|
|
43341
|
-
|
|
43342
|
-
|
|
43343
|
-
|
|
43344
|
-
|
|
43345
|
-
|
|
43346
|
-
|
|
43347
|
-
|
|
43348
|
-
|
|
43349
|
-
|
|
43350
|
-
|
|
43351
|
-
|
|
43603
|
+
try {
|
|
43604
|
+
const file = await handle.getFileHandle(LOCATION_ID_FILE, { create: true });
|
|
43605
|
+
tagged = true;
|
|
43606
|
+
const writable = await file.createWritable();
|
|
43607
|
+
await writable.write(id);
|
|
43608
|
+
await writable.close();
|
|
43609
|
+
} catch (error) {
|
|
43610
|
+
throw new Error(`Jx Studio can't write to "${name}" (${errorMessage2(error)}), so it can't create a project there. Choose a folder you can write to.`, { cause: error });
|
|
43611
|
+
}
|
|
43612
|
+
let path;
|
|
43613
|
+
try {
|
|
43614
|
+
path = await locate({ id, name });
|
|
43615
|
+
} catch (error) {
|
|
43616
|
+
throw new Error(`Jx Studio could not look up where "${name}" is: ${errorMessage2(error)}. Type the folder's path into Location instead.`, { cause: error });
|
|
43617
|
+
}
|
|
43618
|
+
if (!path) {
|
|
43619
|
+
throw new Error(`Jx Studio could not find where "${name}" is. From a browser it can only find folders inside your home folder. Type its full path into Location instead.`);
|
|
43620
|
+
}
|
|
43621
|
+
return path;
|
|
43352
43622
|
} finally {
|
|
43353
|
-
|
|
43623
|
+
if (tagged) {
|
|
43624
|
+
await handle.removeEntry(LOCATION_ID_FILE).catch(() => {});
|
|
43625
|
+
}
|
|
43354
43626
|
}
|
|
43355
43627
|
}
|
|
43356
43628
|
|
|
@@ -43486,7 +43758,8 @@ function createDevServerPlatform() {
|
|
|
43486
43758
|
return pickDirectoryPath(async ({ id, name }) => {
|
|
43487
43759
|
const res = await fetch(`/__studio/locate-directory?name=${encodeURIComponent(name)}&id=${encodeURIComponent(id)}`);
|
|
43488
43760
|
if (!res.ok) {
|
|
43489
|
-
|
|
43761
|
+
const body = await res.json().catch(() => null);
|
|
43762
|
+
throw new Error(problemMessage(body, res.status));
|
|
43490
43763
|
}
|
|
43491
43764
|
const found = await readJson2(res);
|
|
43492
43765
|
return found.path ?? null;
|
|
@@ -43522,6 +43795,13 @@ function createDevServerPlatform() {
|
|
|
43522
43795
|
const data = await readJson2(res);
|
|
43523
43796
|
return data.content;
|
|
43524
43797
|
},
|
|
43798
|
+
async readFileBytes(path) {
|
|
43799
|
+
const res = await fetch(`/__studio/file/bytes?path=${encodeURIComponent(serverPath(path))}`);
|
|
43800
|
+
if (!res.ok) {
|
|
43801
|
+
throw new Error(`Failed to read file bytes: ${path}`);
|
|
43802
|
+
}
|
|
43803
|
+
return res.arrayBuffer();
|
|
43804
|
+
},
|
|
43525
43805
|
async writeFile(path, content) {
|
|
43526
43806
|
const res = await fetch(`/__studio/file?path=${encodeURIComponent(serverPath(path))}`, {
|
|
43527
43807
|
body: content,
|
|
@@ -43621,7 +43901,7 @@ function createDevServerPlatform() {
|
|
|
43621
43901
|
}
|
|
43622
43902
|
const { offer } = negotiation;
|
|
43623
43903
|
_collabConnection ??= (async () => {
|
|
43624
|
-
const { createWsCollabConnection } = await (__preload("
|
|
43904
|
+
const { createWsCollabConnection } = await (__preload("erze4czt"), import("./chunks/ws-client-erze4czt.js"));
|
|
43625
43905
|
const scheme = location.protocol === "https:" ? "wss" : "ws";
|
|
43626
43906
|
return createWsCollabConnection({
|
|
43627
43907
|
protocols: offer,
|
|
@@ -44143,7 +44423,7 @@ function createDevServerPlatform() {
|
|
|
44143
44423
|
}
|
|
44144
44424
|
},
|
|
44145
44425
|
async cfApi(apiPath, init) {
|
|
44146
|
-
const { getCfToken } = await (__preload("
|
|
44426
|
+
const { getCfToken } = await (__preload("m79r7p5y"), import("./chunks/cf-settings-m79r7p5y.js"));
|
|
44147
44427
|
const token = getCfToken();
|
|
44148
44428
|
if (!token) {
|
|
44149
44429
|
throw new Error("No Cloudflare API token configured");
|
|
@@ -44161,7 +44441,7 @@ function createDevServerPlatform() {
|
|
|
44161
44441
|
return envelope.result ?? envelope;
|
|
44162
44442
|
},
|
|
44163
44443
|
async cfConnection() {
|
|
44164
|
-
const { getCfAccountId, getCfToken, setCfAccountId } = await (__preload("
|
|
44444
|
+
const { getCfAccountId, getCfToken, setCfAccountId } = await (__preload("m79r7p5y"), import("./chunks/cf-settings-m79r7p5y.js"));
|
|
44165
44445
|
if (!getCfToken()) {
|
|
44166
44446
|
return null;
|
|
44167
44447
|
}
|
|
@@ -44199,7 +44479,59 @@ function createDevServerPlatform() {
|
|
|
44199
44479
|
}
|
|
44200
44480
|
|
|
44201
44481
|
// src/platforms/default-platform.ts
|
|
44482
|
+
var BACKENDLESS_SCHEMES = new Set(["views:", "file:"]);
|
|
44483
|
+
function bootEnvironment() {
|
|
44484
|
+
const doc = globalThis.document;
|
|
44485
|
+
const meta = `meta[name="${BOOT_META.name}"][content="${BOOT_META.content}"]`;
|
|
44486
|
+
return {
|
|
44487
|
+
cloud: globalThis.__jxCloud != null,
|
|
44488
|
+
declared: doc?.querySelector(meta) != null,
|
|
44489
|
+
launcher: launcherSignal2(),
|
|
44490
|
+
protocol: globalThis.location?.protocol ?? ""
|
|
44491
|
+
};
|
|
44492
|
+
}
|
|
44493
|
+
function bootRefusal(env = bootEnvironment()) {
|
|
44494
|
+
if (env.launcher) {
|
|
44495
|
+
return { kind: "launcher", launcher: env.launcher.launcher };
|
|
44496
|
+
}
|
|
44497
|
+
if (env.cloud) {
|
|
44498
|
+
return null;
|
|
44499
|
+
}
|
|
44500
|
+
if (env.declared) {
|
|
44501
|
+
return { kind: "declared" };
|
|
44502
|
+
}
|
|
44503
|
+
if (BACKENDLESS_SCHEMES.has(env.protocol)) {
|
|
44504
|
+
return { kind: "scheme", protocol: env.protocol };
|
|
44505
|
+
}
|
|
44506
|
+
return null;
|
|
44507
|
+
}
|
|
44508
|
+
function refusalMessage(refusal) {
|
|
44509
|
+
switch (refusal.kind) {
|
|
44510
|
+
case "launcher": {
|
|
44511
|
+
return `Jx Studio's launcher (${refusal.launcher ?? "unnamed"}) announced itself but registered no platform adapter.`;
|
|
44512
|
+
}
|
|
44513
|
+
case "declared": {
|
|
44514
|
+
return "Jx Studio's page declares a boot module, but nothing registered a platform adapter.";
|
|
44515
|
+
}
|
|
44516
|
+
default: {
|
|
44517
|
+
return `Jx Studio was opened from a ${refusal.protocol} URL, which has no backend for the dev-server adapter.`;
|
|
44518
|
+
}
|
|
44519
|
+
}
|
|
44520
|
+
}
|
|
44521
|
+
|
|
44522
|
+
class PlatformUnavailableError extends Error {
|
|
44523
|
+
refusal;
|
|
44524
|
+
constructor(refusal) {
|
|
44525
|
+
super(refusalMessage(refusal));
|
|
44526
|
+
this.name = "PlatformUnavailableError";
|
|
44527
|
+
this.refusal = refusal;
|
|
44528
|
+
}
|
|
44529
|
+
}
|
|
44202
44530
|
function resolveDefaultPlatform() {
|
|
44531
|
+
const refusal = bootRefusal();
|
|
44532
|
+
if (refusal) {
|
|
44533
|
+
throw new PlatformUnavailableError(refusal);
|
|
44534
|
+
}
|
|
44203
44535
|
const cloud = globalThis.__jxCloud;
|
|
44204
44536
|
return cloud ? createCloudPlatform(cloud.project) : createDevServerPlatform();
|
|
44205
44537
|
}
|
|
@@ -45911,6 +46243,7 @@ function capabilities(platform) {
|
|
|
45911
46243
|
gitClone: has("gitClone"),
|
|
45912
46244
|
importSite: has("importSite"),
|
|
45913
46245
|
openProjectInNewWindow: has("openProjectInNewWindow"),
|
|
46246
|
+
readFileBytes: has("readFileBytes"),
|
|
45914
46247
|
windowControls: has("newWindow")
|
|
45915
46248
|
};
|
|
45916
46249
|
}
|
|
@@ -46080,7 +46413,7 @@ function openSettingsMenu(anchor, opts = {}) {
|
|
|
46080
46413
|
var rail_default = {
|
|
46081
46414
|
$schema: "https://jxsuite.com/schema/v1",
|
|
46082
46415
|
$id: "StudioRail",
|
|
46083
|
-
$description: "The Navigator rail: a rendering of the panel registry. The adapter hands in `groups`, every rail-able panel grouped by level with its selected state and badge, and `settingsVisible`/`settingsOpen` for the foot's gear. Nothing here is a list of panels.",
|
|
46416
|
+
$description: "The Navigator rail: a rendering of the panel registry. The adapter hands in `groups`, every rail-able panel grouped by level with its selected state and badge, and `settingsVisible`/`settingsOpen` for the foot's gear. Nothing here is a list of panels. Each button prints its record's short `railLabel` under the glyph, falling back to `title`, and carries the full `title` as its accessible name and tooltip: the visible words lead the name a reader hears (WCAG 2.5.3), and the tooltip restores what a short label leaves out.",
|
|
46084
46417
|
tagName: "div",
|
|
46085
46418
|
attributes: {
|
|
46086
46419
|
part: "rail"
|
|
@@ -46224,7 +46557,7 @@ var rail_default = {
|
|
|
46224
46557
|
children: [
|
|
46225
46558
|
{
|
|
46226
46559
|
tagName: "span",
|
|
46227
|
-
textContent: "${$map.item.
|
|
46560
|
+
textContent: "${$map.item.railLabel}"
|
|
46228
46561
|
}
|
|
46229
46562
|
]
|
|
46230
46563
|
}
|
|
@@ -46355,6 +46688,7 @@ function project17() {
|
|
|
46355
46688
|
badge: String(panel.badge?.(ctx) ?? ""),
|
|
46356
46689
|
icon: panel.icon,
|
|
46357
46690
|
id: panel.id,
|
|
46691
|
+
railLabel: panel.railLabel ?? panel.title,
|
|
46358
46692
|
selected: isRailPanelShowing(panel),
|
|
46359
46693
|
title: panel.title
|
|
46360
46694
|
}))
|
|
@@ -48014,6 +48348,14 @@ var ASSISTANT_REGION = "inspector.assistant";
|
|
|
48014
48348
|
var _host5 = null;
|
|
48015
48349
|
var _container = null;
|
|
48016
48350
|
var _scope9 = null;
|
|
48351
|
+
var _surfaceBound = false;
|
|
48352
|
+
function ensureAssistantSurface() {
|
|
48353
|
+
if (_surfaceBound || !_container) {
|
|
48354
|
+
return;
|
|
48355
|
+
}
|
|
48356
|
+
_surfaceBound = true;
|
|
48357
|
+
bindAiPanelHost(_container);
|
|
48358
|
+
}
|
|
48017
48359
|
function mount9(host) {
|
|
48018
48360
|
if (!host || _host5 === host && _container) {
|
|
48019
48361
|
return;
|
|
@@ -48025,10 +48367,15 @@ function mount9(host) {
|
|
|
48025
48367
|
host.textContent = "";
|
|
48026
48368
|
host.append(_container);
|
|
48027
48369
|
mountAiPanel();
|
|
48028
|
-
|
|
48370
|
+
_surfaceBound = false;
|
|
48029
48371
|
_scope9?.stop();
|
|
48030
48372
|
_scope9 = effectScope();
|
|
48031
48373
|
_scope9.run(() => {
|
|
48374
|
+
effect(() => {
|
|
48375
|
+
if (inspectorTab2() === "assistant") {
|
|
48376
|
+
ensureAssistantSurface();
|
|
48377
|
+
}
|
|
48378
|
+
});
|
|
48032
48379
|
effect(() => {
|
|
48033
48380
|
const root = workspace2.projectRoot;
|
|
48034
48381
|
if (!root || !hasPendingAgentPrompt(root)) {
|
|
@@ -48563,7 +48910,7 @@ function installUrlOf(error) {
|
|
|
48563
48910
|
var new_project_default = {
|
|
48564
48911
|
$schema: "https://jxsuite.com/schema/v1",
|
|
48565
48912
|
$id: "StudioNewProject",
|
|
48566
|
-
$description: "The New Project wizard: two steps in one dialog. Step 1 asks where the site comes from — a starter gallery that ends with one Start from scratch card, an Import of a site that already exists, or an Agent brief — and step 2 asks what it is called and where it is written. A `jx-dialog`, so the platform owns modality, Escape, focus restoration and the backdrop, which is what retired the fixed `.new-project-modal` card, the `<sp-underlay>` beside it, the `z-index: 1000` that card needed to climb back out from under its own scrim, and the hand-drawn header with its own close button. The footer is the dialog's too: Cancel, Back and the primary are `cancelLabel`, `secondaryLabel` and `confirmLabel`, so the wizard says which answers exist by naming them rather than by drawing three buttons per step. Nothing here knows what a starter is, which platform writes to a repository rather than a folder, what makes an AI gate open, or how a breakpoint policy is spelled — `new-project/new-project-modal.ts` keeps the flow, `new-project/import-tab.ts` the brief and `new-project/location-fields.ts` the destination, and all three hand over strings, flags and one discriminant per branch. The three AI controllers are islands: their own mounted documents, placed by the adapter into boxes this document renders empty (§9.4).",
|
|
48913
|
+
$description: "The New Project wizard: two steps in one dialog. Step 1 asks where the site comes from — a starter gallery that ends with one Start from scratch card, an Import of a site that already exists, or an Agent brief — and step 2 asks what it is called and where it is written. A `jx-dialog`, so the platform owns modality, Escape, focus restoration and the backdrop, which is what retired the fixed `.new-project-modal` card, the `<sp-underlay>` beside it, the `z-index: 1000` that card needed to climb back out from under its own scrim, and the hand-drawn header with its own close button. The footer is the dialog's too: Cancel, Back and the primary are `cancelLabel`, `secondaryLabel` and `confirmLabel`, so the wizard says which answers exist by naming them rather than by drawing three buttons per step. Nothing here knows what a starter is, which platform writes to a repository rather than a folder, what makes an AI gate open, or how a breakpoint policy is spelled — `new-project/new-project-modal.ts` keeps the flow, `new-project/import-tab.ts` the brief and `new-project/location-fields.ts` the destination, and all three hand over strings, flags and one discriminant per branch. The three AI controllers are islands: their own mounted documents, placed by the adapter into boxes this document renders empty (studio-ui-guidelines.md §9.4).",
|
|
48567
48914
|
tagName: "jx-dialog",
|
|
48568
48915
|
$props: {
|
|
48569
48916
|
headline: {
|
|
@@ -50468,6 +50815,8 @@ async function browseLocation(rerender) {
|
|
|
50468
50815
|
_parent = picked;
|
|
50469
50816
|
_error2 = "";
|
|
50470
50817
|
}
|
|
50818
|
+
} catch (error) {
|
|
50819
|
+
_error2 = errorMessage2(error) || "The folder could not be chosen. Type its path into Location instead.";
|
|
50471
50820
|
} finally {
|
|
50472
50821
|
_browsing = false;
|
|
50473
50822
|
rerender();
|
|
@@ -50848,7 +51197,15 @@ function finish(result) {
|
|
|
50848
51197
|
settle3(result);
|
|
50849
51198
|
}
|
|
50850
51199
|
async function finishCreated(result) {
|
|
50851
|
-
await
|
|
51200
|
+
const { adopted, error, openedElsewhere } = await adoptCreatedProject(result.root, {
|
|
51201
|
+
getTab: () => null
|
|
51202
|
+
});
|
|
51203
|
+
if (!adopted && !openedElsewhere) {
|
|
51204
|
+
notify2.warn("Created the project, but could not open it here.", {
|
|
51205
|
+
...error === null ? {} : { detail: error },
|
|
51206
|
+
source: "New Project"
|
|
51207
|
+
});
|
|
51208
|
+
}
|
|
50852
51209
|
finish(result);
|
|
50853
51210
|
}
|
|
50854
51211
|
function deriveSlug(name) {
|
|
@@ -51318,7 +51675,7 @@ var seo_default = {
|
|
|
51318
51675
|
}
|
|
51319
51676
|
},
|
|
51320
51677
|
'& [part="chip"]': {
|
|
51321
|
-
$description: "The provenance vocabulary of `panels/provenance.ts` §6.
|
|
51678
|
+
$description: "The provenance vocabulary of `panels/provenance.ts` (§6.7), drawn by this document rather than borrowed as a class: `set` keeps the accent dot it has always been, `inherited` is an amber pill naming its donor, and a field nothing supplies gets no chip at all — because a ghost dot on every unset row says less than silence. The two chips that can go somewhere are `<button>`s; the ones with nowhere to go are not, which is §6.7's rule that a control which looks pressable and does nothing is a defect.",
|
|
51322
51679
|
flexShrink: "0"
|
|
51323
51680
|
},
|
|
51324
51681
|
'& jx-dot[part="chip"][data-state="set"]': {
|
|
@@ -54251,8 +54608,8 @@ var settings_css_vars_default = {
|
|
|
54251
54608
|
fontWeight: "600",
|
|
54252
54609
|
color: "var(--fg)"
|
|
54253
54610
|
},
|
|
54254
|
-
'& [part="error"]': {
|
|
54255
|
-
$description:
|
|
54611
|
+
'& [part="section-error"]': {
|
|
54612
|
+
$description: 'A write that failed, said under the section title rather than pinned to a control. This section saves on change and the predecessor dropped every rejection on the floor, so a read-only file made each edit look as though it had been taken and then quietly forgotten. It is `section-error` rather than `error` because every text field and select in the section draws a permanent `[part="error"]` of its own in this same light-DOM tree, and a rule on that name would give their refusal sentences this margin and size too.',
|
|
54256
54613
|
margin: "0 0 12px",
|
|
54257
54614
|
fontSize: "var(--jx-text-md)",
|
|
54258
54615
|
color: "var(--danger)"
|
|
@@ -54408,7 +54765,7 @@ var settings_css_vars_default = {
|
|
|
54408
54765
|
{
|
|
54409
54766
|
tagName: "div",
|
|
54410
54767
|
attributes: {
|
|
54411
|
-
part: "error-slot",
|
|
54768
|
+
part: "section-error-slot",
|
|
54412
54769
|
role: "none"
|
|
54413
54770
|
},
|
|
54414
54771
|
$switch: {
|
|
@@ -54418,7 +54775,7 @@ var settings_css_vars_default = {
|
|
|
54418
54775
|
true: {
|
|
54419
54776
|
tagName: "p",
|
|
54420
54777
|
attributes: {
|
|
54421
|
-
part: "error",
|
|
54778
|
+
part: "section-error",
|
|
54422
54779
|
role: "alert"
|
|
54423
54780
|
},
|
|
54424
54781
|
textContent: "${state.error}"
|
|
@@ -57898,8 +58255,8 @@ var settings_extensions_default = {
|
|
|
57898
58255
|
fontSize: "var(--jx-text-md)",
|
|
57899
58256
|
color: "var(--fg-dim)"
|
|
57900
58257
|
},
|
|
57901
|
-
'& [part="error"]': {
|
|
57902
|
-
$description: 'The last operation that failed, said under the section title. Not a second announcement: `updateSiteConfig` already files a Problem for a rejected write and `activity.fail` files one for a rejected install. §13.1 makes INLINE the tier for "the value is on screen, and nothing else is the right place" — the Problem is about the file, and this line is about the switch the reader is looking at.',
|
|
58258
|
+
'& [part="section-error"]': {
|
|
58259
|
+
$description: 'The last operation that failed, said under the section title. Not a second announcement: `updateSiteConfig` already files a Problem for a rejected write and `activity.fail` files one for a rejected install. studio-ui-guidelines.md §13.1 makes INLINE the tier for "the value is on screen, and nothing else is the right place" — the Problem is about the file, and this line is about the switch the reader is looking at. It is `section-error` rather than `error`, the name every settings section uses, because every kit field draws a permanent `[part="error"]` of its own and the kit is light DOM: a rule on that name here would reach any field the section ever holds.',
|
|
57903
58260
|
margin: "6px 0 0",
|
|
57904
58261
|
fontSize: "var(--jx-text-md)",
|
|
57905
58262
|
color: "var(--danger)"
|
|
@@ -57916,7 +58273,7 @@ var settings_extensions_default = {
|
|
|
57916
58273
|
color: "var(--fg)"
|
|
57917
58274
|
},
|
|
57918
58275
|
'& [part="row"]': {
|
|
57919
|
-
$description: "A wrapping row, never an overflowing one (guidelines §4.6): the package name is author-supplied and unbounded, and Project Settings renders at two very different widths.",
|
|
58276
|
+
$description: "A wrapping row, never an overflowing one (studio-ui-guidelines.md §4.6): the package name is author-supplied and unbounded, and Project Settings renders at two very different widths.",
|
|
57920
58277
|
display: "flex",
|
|
57921
58278
|
flexWrap: "wrap",
|
|
57922
58279
|
alignItems: "flex-start",
|
|
@@ -57992,7 +58349,7 @@ var settings_extensions_default = {
|
|
|
57992
58349
|
flexShrink: "0"
|
|
57993
58350
|
},
|
|
57994
58351
|
'& [part="empty"]': {
|
|
57995
|
-
$description: 'The teaching empty state, drawn here rather than borrowed from `panels/empty-state.ts`: that helper is a lit template, and a document cannot call one. What is kept is the pattern\'s rules (§11) — one sentence saying what the region is FOR, never "no extensions", then where its content comes from — in the compact shape, which reads as a note inside an otherwise populated pane rather than as a whole empty region. It offers no action because there is nothing here to act on: a backend that lists no catalogue is not a project waiting to be filled in.',
|
|
58352
|
+
$description: 'The teaching empty state, drawn here rather than borrowed from `panels/empty-state.ts`: that helper is a lit template, and a document cannot call one. What is kept is the pattern\'s rules (studio-ui-guidelines.md §11) — one sentence saying what the region is FOR, never "no extensions", then where its content comes from — in the compact shape, which reads as a note inside an otherwise populated pane rather than as a whole empty region. It offers no action because there is nothing here to act on: a backend that lists no catalogue is not a project waiting to be filled in.',
|
|
57996
58353
|
display: "flex",
|
|
57997
58354
|
flexDirection: "column",
|
|
57998
58355
|
alignItems: "flex-start",
|
|
@@ -58029,7 +58386,7 @@ var settings_extensions_default = {
|
|
|
58029
58386
|
{
|
|
58030
58387
|
tagName: "div",
|
|
58031
58388
|
attributes: {
|
|
58032
|
-
part: "error-slot",
|
|
58389
|
+
part: "section-error-slot",
|
|
58033
58390
|
role: "none"
|
|
58034
58391
|
},
|
|
58035
58392
|
style: {
|
|
@@ -58042,7 +58399,7 @@ var settings_extensions_default = {
|
|
|
58042
58399
|
true: {
|
|
58043
58400
|
tagName: "p",
|
|
58044
58401
|
attributes: {
|
|
58045
|
-
part: "error",
|
|
58402
|
+
part: "section-error",
|
|
58046
58403
|
role: "alert"
|
|
58047
58404
|
},
|
|
58048
58405
|
textContent: "${state.error}"
|
|
@@ -58584,8 +58941,8 @@ var settings_deploy_default = {
|
|
|
58584
58941
|
fontWeight: "600",
|
|
58585
58942
|
color: "var(--fg)"
|
|
58586
58943
|
},
|
|
58587
|
-
'& [part="error"]': {
|
|
58588
|
-
$description:
|
|
58944
|
+
'& [part="section-error"]': {
|
|
58945
|
+
$description: 'A write that failed, said under the section title rather than pinned to the picker: settings save on change, so a silent rejection would read as "my edit just vanished", and what failed was the whole file rather than the value. Its size read `--spectrum-font-size-75` with a 12px fallback, which resolved only inside `<sp-theme>` — a document is mounted in the settings pane, where that name is not in scope and the fallback is all it would ever have taken — so the size is the kit\'s scale and the colour is the studio layer\'s. It is `section-error` rather than `error` because the adapter picker is a `jx-select`, which draws a permanent `[part="error"]` of its own in this same light-DOM tree: a rule on that name would give the picker\'s own refusal sentence this 16px margin too.',
|
|
58589
58946
|
margin: "0 0 16px",
|
|
58590
58947
|
fontSize: "var(--jx-text-md)",
|
|
58591
58948
|
color: "var(--danger)"
|
|
@@ -58606,7 +58963,7 @@ var settings_deploy_default = {
|
|
|
58606
58963
|
{
|
|
58607
58964
|
tagName: "div",
|
|
58608
58965
|
attributes: {
|
|
58609
|
-
part: "error-slot",
|
|
58966
|
+
part: "section-error-slot",
|
|
58610
58967
|
role: "none"
|
|
58611
58968
|
},
|
|
58612
58969
|
style: {
|
|
@@ -58619,7 +58976,7 @@ var settings_deploy_default = {
|
|
|
58619
58976
|
true: {
|
|
58620
58977
|
tagName: "p",
|
|
58621
58978
|
attributes: {
|
|
58622
|
-
part: "error",
|
|
58979
|
+
part: "section-error",
|
|
58623
58980
|
role: "alert"
|
|
58624
58981
|
},
|
|
58625
58982
|
textContent: "${state.error}"
|
|
@@ -60580,8 +60937,8 @@ var settings_locales_default = {
|
|
|
60580
60937
|
fontSize: "var(--jx-text-md)",
|
|
60581
60938
|
color: "var(--fg-dim)"
|
|
60582
60939
|
},
|
|
60583
|
-
'& [part="error"]': {
|
|
60584
|
-
$description:
|
|
60940
|
+
'& [part="section-error"]': {
|
|
60941
|
+
$description: "A write that failed, said under the section title rather than pinned to a control: settings save on change, so a silent rejection would read as \"my edit just vanished\", and the whole file failed rather than one field. It is `section-error` rather than `error` because the tag field's refusal IS the kit's own `[part=\"error\"]` sentence, in this same light-DOM tree: a rule on that name gave the tag field's refusal this margin and size, and reached the two pickers' empty regions as well.",
|
|
60585
60942
|
margin: "6px 0 0",
|
|
60586
60943
|
fontSize: "var(--jx-text-md)",
|
|
60587
60944
|
color: "var(--danger)"
|
|
@@ -60655,7 +61012,7 @@ var settings_locales_default = {
|
|
|
60655
61012
|
{
|
|
60656
61013
|
tagName: "div",
|
|
60657
61014
|
attributes: {
|
|
60658
|
-
part: "error-slot",
|
|
61015
|
+
part: "section-error-slot",
|
|
60659
61016
|
role: "none"
|
|
60660
61017
|
},
|
|
60661
61018
|
$switch: {
|
|
@@ -60665,7 +61022,7 @@ var settings_locales_default = {
|
|
|
60665
61022
|
true: {
|
|
60666
61023
|
tagName: "p",
|
|
60667
61024
|
attributes: {
|
|
60668
|
-
part: "error",
|
|
61025
|
+
part: "section-error",
|
|
60669
61026
|
role: "alert"
|
|
60670
61027
|
},
|
|
60671
61028
|
textContent: "${state.error}"
|
|
@@ -61261,7 +61618,7 @@ async function openProjectSettings(section) {
|
|
|
61261
61618
|
showSettingsDocument();
|
|
61262
61619
|
notifySettingsDocument2();
|
|
61263
61620
|
try {
|
|
61264
|
-
const { syncExtensionSettingsSections } = await (__preload("
|
|
61621
|
+
const { syncExtensionSettingsSections } = await (__preload("r73an5s6"), import("./chunks/extension-sections-r73an5s6.js"));
|
|
61265
61622
|
await syncExtensionSettingsSections();
|
|
61266
61623
|
} catch (error) {
|
|
61267
61624
|
notify2.warn(`Could not load extension settings sections — ${errorMessage2(error)}`, {
|
|
@@ -61398,9 +61755,11 @@ var about_default = {
|
|
|
61398
61755
|
color: "var(--fg-dim)"
|
|
61399
61756
|
},
|
|
61400
61757
|
'& [part="meta-value"]': {
|
|
61758
|
+
$description: "One of the two places in the chrome that opt back in to selection (`STYLING.md` §Text selection). The root sets `user-select: none` and `styles/tokens.json` restates it on every modal `dialog`, but this dialog's body IS its message: the version, the build date, the commit and the channel exist to be pasted into a bug report, and Studio offers no copy action for them.",
|
|
61401
61759
|
margin: "0",
|
|
61402
61760
|
color: "var(--fg)",
|
|
61403
|
-
wordBreak: "break-word"
|
|
61761
|
+
wordBreak: "break-word",
|
|
61762
|
+
userSelect: "text"
|
|
61404
61763
|
},
|
|
61405
61764
|
'& [part="links"]': {
|
|
61406
61765
|
display: "flex",
|
|
@@ -61438,10 +61797,12 @@ var about_default = {
|
|
|
61438
61797
|
gap: "4px"
|
|
61439
61798
|
},
|
|
61440
61799
|
'& [part="package-row"]': {
|
|
61800
|
+
$description: "Selectable for the same reason `meta-value` is, and on the ROW rather than on the version alone: what a reader pastes is the pair, `@jxsuite/ai 0.37.0`, so a drag has to be able to cross from the name to the number and on down the list.",
|
|
61441
61801
|
display: "flex",
|
|
61442
61802
|
justifyContent: "space-between",
|
|
61443
61803
|
gap: "12px",
|
|
61444
|
-
fontSize: "var(--jx-text-md)"
|
|
61804
|
+
fontSize: "var(--jx-text-md)",
|
|
61805
|
+
userSelect: "text"
|
|
61445
61806
|
},
|
|
61446
61807
|
'& [part="package-name"]': {
|
|
61447
61808
|
color: "var(--fg-dim)"
|
|
@@ -61671,9 +62032,9 @@ function openAboutSurface(options) {
|
|
|
61671
62032
|
|
|
61672
62033
|
// src/version.ts
|
|
61673
62034
|
var APP_NAME = "Jx Studio";
|
|
61674
|
-
var VERSION = "5.
|
|
61675
|
-
var BUILD_DATE = "2026-09-
|
|
61676
|
-
var GIT_COMMIT = "
|
|
62035
|
+
var VERSION = "5.4.0";
|
|
62036
|
+
var BUILD_DATE = "2026-09-29T19:41:26.105Z";
|
|
62037
|
+
var GIT_COMMIT = "fd18dbe";
|
|
61677
62038
|
var LINKS = {
|
|
61678
62039
|
github: "https://github.com/jxsuite/jx",
|
|
61679
62040
|
docs: "https://github.com/jxsuite/jx#readme",
|
|
@@ -62081,9 +62442,9 @@ function withExtension(path, ext) {
|
|
|
62081
62442
|
return current === "" ? `${path}${ext}` : `${path.slice(0, path.length - current.length)}${ext}`;
|
|
62082
62443
|
}
|
|
62083
62444
|
async function readDocument(path, source) {
|
|
62084
|
-
const { formatForPath } = await (__preload("
|
|
62445
|
+
const { formatForPath } = await (__preload("mym8qnd0"), import("./chunks/format-host-mym8qnd0.js"));
|
|
62085
62446
|
if (formatForPath(path)) {
|
|
62086
|
-
const { parseSourceForPath } = await (__preload("
|
|
62447
|
+
const { parseSourceForPath } = await (__preload("3xan8mnj"), import("./chunks/file-ops-3xan8mnj.js"));
|
|
62087
62448
|
const parsed = await parseSourceForPath(path, source);
|
|
62088
62449
|
return { document: parsed.document, frontmatter: parsed.frontmatter };
|
|
62089
62450
|
}
|
|
@@ -62094,7 +62455,7 @@ async function writeDocument(targetExt, document2, frontmatter) {
|
|
|
62094
62455
|
if (targetExt === ".json") {
|
|
62095
62456
|
return serializeJson(merged, null);
|
|
62096
62457
|
}
|
|
62097
|
-
const { formatByExtension, formatSerialize } = await (__preload("
|
|
62458
|
+
const { formatByExtension, formatSerialize } = await (__preload("mym8qnd0"), import("./chunks/format-host-mym8qnd0.js"));
|
|
62098
62459
|
const format = formatByExtension(targetExt, "serialize");
|
|
62099
62460
|
if (!format) {
|
|
62100
62461
|
throw new Error(`No installed format writes ${targetExt} files.`);
|
|
@@ -62131,7 +62492,7 @@ async function buildPlan(path, source, targetExt) {
|
|
|
62131
62492
|
};
|
|
62132
62493
|
}
|
|
62133
62494
|
if (targetExt === ".json") {
|
|
62134
|
-
const { validateDocOrNull } = await (__preload("
|
|
62495
|
+
const { validateDocOrNull } = await (__preload("epp2z3s3"), import("./chunks/jx-validate-epp2z3s3.js"));
|
|
62135
62496
|
const errors = await validateDocOrNull(JSON.parse(text));
|
|
62136
62497
|
if (errors === null) {
|
|
62137
62498
|
lines.push("The result could not be checked against the document schema — that is not the same as " + "it being valid.");
|
|
@@ -62149,7 +62510,7 @@ async function buildPlan(path, source, targetExt) {
|
|
|
62149
62510
|
if (!await readsBackIdentically(targetPath, text)) {
|
|
62150
62511
|
lines.push("This file will not read back identically after conversion.");
|
|
62151
62512
|
}
|
|
62152
|
-
const { loadUsages, usageWarning } = await (__preload("
|
|
62513
|
+
const { loadUsages, usageWarning } = await (__preload("f7bdv3z5"), import("./chunks/references-f7bdv3z5.js"));
|
|
62153
62514
|
const sentence = usageWarning(await loadUsages({ path }), "convert");
|
|
62154
62515
|
if (sentence !== null) {
|
|
62155
62516
|
lines.push(sentence);
|
|
@@ -62164,14 +62525,14 @@ async function convertFile(path, targetExt) {
|
|
|
62164
62525
|
notify.error(message, { detail, path, source: "Convert" });
|
|
62165
62526
|
return null;
|
|
62166
62527
|
};
|
|
62167
|
-
const { loadFormats } = await (__preload("
|
|
62528
|
+
const { loadFormats } = await (__preload("mym8qnd0"), import("./chunks/format-host-mym8qnd0.js"));
|
|
62168
62529
|
await loadFormats();
|
|
62169
62530
|
const targets = convertTargets(path);
|
|
62170
62531
|
if (targets.length === 0) {
|
|
62171
62532
|
return fail(`${path} cannot be converted to another format.`, CANNOT_CONVERT);
|
|
62172
62533
|
}
|
|
62173
|
-
const { workspace } = await (__preload("
|
|
62174
|
-
const { commitTabBuffers } = await (__preload("
|
|
62534
|
+
const { workspace } = await (__preload("b9k2frks"), import("./chunks/workspace-b9k2frks.js"));
|
|
62535
|
+
const { commitTabBuffers } = await (__preload("jaq1t9zj"), import("./chunks/monaco-buffer-jaq1t9zj.js"));
|
|
62175
62536
|
const openTabEntry = [...workspace.tabs.values()].find((tab) => tab.documentPath === path);
|
|
62176
62537
|
if (openTabEntry) {
|
|
62177
62538
|
await commitTabBuffers(openTabEntry);
|
|
@@ -62186,7 +62547,7 @@ async function convertFile(path, targetExt) {
|
|
|
62186
62547
|
if (!targets.some((target) => target.ext === chosen)) {
|
|
62187
62548
|
return fail(`${path} cannot be converted to ${chosen}.`, CANNOT_CONVERT);
|
|
62188
62549
|
}
|
|
62189
|
-
const { getPlatform } = await (__preload("
|
|
62550
|
+
const { getPlatform } = await (__preload("fnchmhy7"), import("./chunks/platform-fnchmhy7.js"));
|
|
62190
62551
|
const platform = getPlatform();
|
|
62191
62552
|
const targetPath = withExtension(path, chosen);
|
|
62192
62553
|
try {
|
|
@@ -62208,7 +62569,7 @@ async function convertFile(path, targetExt) {
|
|
|
62208
62569
|
if (plan.blocker !== null) {
|
|
62209
62570
|
return fail(`${path} cannot be converted to ${chosen}.`, plan.blocker);
|
|
62210
62571
|
}
|
|
62211
|
-
const { showConfirmDialog } = await (__preload("
|
|
62572
|
+
const { showConfirmDialog } = await (__preload("s6mc01ry"), import("./chunks/layers-s6mc01ry.js"));
|
|
62212
62573
|
const { html } = await (__preload("m244w9nh"), import("./chunks/lit-html-m244w9nh.js"));
|
|
62213
62574
|
const confirmed = await showConfirmDialog("Convert Format", html`<span
|
|
62214
62575
|
>Convert <strong>${path.split("/").pop()}</strong> to
|
|
@@ -62217,8 +62578,8 @@ async function convertFile(path, targetExt) {
|
|
|
62217
62578
|
if (!confirmed) {
|
|
62218
62579
|
return null;
|
|
62219
62580
|
}
|
|
62220
|
-
const { markLocalMutation } = await (__preload("
|
|
62221
|
-
const { notifyMoveOutcome, settleRename } = await (__preload("
|
|
62581
|
+
const { markLocalMutation } = await (__preload("z2gg8qjt"), import("./chunks/fs-events-z2gg8qjt.js"));
|
|
62582
|
+
const { notifyMoveOutcome, settleRename } = await (__preload("egsb0ry8"), import("./chunks/files-egsb0ry8.js"));
|
|
62222
62583
|
markLocalMutation(path, plan.targetPath);
|
|
62223
62584
|
try {
|
|
62224
62585
|
await platform.writeFile(path, plan.text);
|
|
@@ -62243,8 +62604,8 @@ async function convertFile(path, targetExt) {
|
|
|
62243
62604
|
return plan.targetPath;
|
|
62244
62605
|
}
|
|
62245
62606
|
async function rebuildTab(from, to, text) {
|
|
62246
|
-
const { openTab, renameTab } = await (__preload("
|
|
62247
|
-
const { formatForPath } = await (__preload("
|
|
62607
|
+
const { openTab, renameTab } = await (__preload("b9k2frks"), import("./chunks/workspace-b9k2frks.js"));
|
|
62608
|
+
const { formatForPath } = await (__preload("mym8qnd0"), import("./chunks/format-host-mym8qnd0.js"));
|
|
62248
62609
|
renameTab(from, to, to);
|
|
62249
62610
|
const format = formatForPath(to);
|
|
62250
62611
|
let document2;
|
|
@@ -62252,7 +62613,7 @@ async function rebuildTab(from, to, text) {
|
|
|
62252
62613
|
let layout = null;
|
|
62253
62614
|
try {
|
|
62254
62615
|
if (format) {
|
|
62255
|
-
const { parseSourceForPath } = await (__preload("
|
|
62616
|
+
const { parseSourceForPath } = await (__preload("3xan8mnj"), import("./chunks/file-ops-3xan8mnj.js"));
|
|
62256
62617
|
({ document: document2, frontmatter } = await parseSourceForPath(to, text));
|
|
62257
62618
|
} else {
|
|
62258
62619
|
({ document: document2, layout } = parseJsonDocument(text));
|
|
@@ -62281,7 +62642,7 @@ async function pickTarget(path, targets) {
|
|
|
62281
62642
|
if (targets.length === 1) {
|
|
62282
62643
|
return targets[0].ext;
|
|
62283
62644
|
}
|
|
62284
|
-
const { showPromptDialog } = await (__preload("
|
|
62645
|
+
const { showPromptDialog } = await (__preload("s6mc01ry"), import("./chunks/layers-s6mc01ry.js"));
|
|
62285
62646
|
let picked = targets[0].ext;
|
|
62286
62647
|
const answer = await showPromptDialog("Convert Format", {
|
|
62287
62648
|
choice: {
|
|
@@ -62406,7 +62767,7 @@ function publishCommands() {
|
|
|
62406
62767
|
when: (ctx) => ctx.project.open,
|
|
62407
62768
|
enablement: () => platformSupportsPublish(),
|
|
62408
62769
|
run: async () => {
|
|
62409
|
-
const { openPublishPanel } = await (__preload("
|
|
62770
|
+
const { openPublishPanel } = await (__preload("ak6hxfma"), import("./chunks/publish-panel-ak6hxfma.js"));
|
|
62410
62771
|
openPublishPanel();
|
|
62411
62772
|
},
|
|
62412
62773
|
title: "Set Up Publishing"
|
|
@@ -63107,7 +63468,7 @@ function i18nCommands() {
|
|
|
63107
63468
|
});
|
|
63108
63469
|
return;
|
|
63109
63470
|
}
|
|
63110
|
-
const { openFileInTab } = await (__preload("
|
|
63471
|
+
const { openFileInTab } = await (__preload("egsb0ry8"), import("./chunks/files-egsb0ry8.js"));
|
|
63111
63472
|
await openFileInTab(path);
|
|
63112
63473
|
},
|
|
63113
63474
|
title: "Open Translation"
|
|
@@ -63152,7 +63513,7 @@ function i18nCommands() {
|
|
|
63152
63513
|
return;
|
|
63153
63514
|
}
|
|
63154
63515
|
const cut = path.lastIndexOf("/");
|
|
63155
|
-
const { createFileIn, openFileInTab } = await (__preload("
|
|
63516
|
+
const { createFileIn, openFileInTab } = await (__preload("egsb0ry8"), import("./chunks/files-egsb0ry8.js"));
|
|
63156
63517
|
const created = await createFileIn({
|
|
63157
63518
|
content,
|
|
63158
63519
|
dir: cut === -1 ? "." : path.slice(0, cut),
|
|
@@ -63323,6 +63684,293 @@ registerSurface("shell", shell_default);
|
|
|
63323
63684
|
function mountShellSurface(host) {
|
|
63324
63685
|
return mountSurface("shell", {}, host);
|
|
63325
63686
|
}
|
|
63687
|
+
// src/surfaces/boot-failure.json
|
|
63688
|
+
var boot_failure_default = {
|
|
63689
|
+
$schema: "https://jxsuite.com/schema/v1",
|
|
63690
|
+
$id: "StudioBootFailure",
|
|
63691
|
+
$description: 'What Studio draws INSTEAD of the application frame when a launcher was supposed to register a platform adapter and did not (`platforms/default-platform.ts`\'s `bootRefusal`). It replaces a failure that drew everything: desktop 5.0.0 to 5.1.3 fell back to the dev-server adapter inside a `views://` window, so every panel rendered and the first click said "Failed to fetch". This says so up front instead: what failed, the error the launcher recorded, and the facts a support thread needs, with a Reload for a transient failure and a Copy for everything else. The adapter decides every sentence; the document only draws them. It is the whole page, so it carries a window drag strip of its own — the desktop window hides its title bar, and with no Command Bar mounted there is otherwise nothing to move the window by.',
|
|
63692
|
+
tagName: "div",
|
|
63693
|
+
attributes: {
|
|
63694
|
+
part: "screen",
|
|
63695
|
+
role: "alert"
|
|
63696
|
+
},
|
|
63697
|
+
style: {
|
|
63698
|
+
$description: "Tokens throughout: the screen is mounted before anything but the kit's theme has run, so it can lean on nothing the frame's stylesheets declare. The top padding clears the macOS traffic lights, which sit over the page because the window's title bar is hidden.",
|
|
63699
|
+
position: "fixed",
|
|
63700
|
+
inset: "0",
|
|
63701
|
+
display: "flex",
|
|
63702
|
+
alignItems: "flex-start",
|
|
63703
|
+
justifyContent: "center",
|
|
63704
|
+
paddingTop: "max(10vh, calc(var(--jx-control-h) * 2))",
|
|
63705
|
+
overflowY: "auto",
|
|
63706
|
+
boxSizing: "border-box",
|
|
63707
|
+
background: "var(--jx-bg)",
|
|
63708
|
+
fontFamily: "var(--jx-font-sans)",
|
|
63709
|
+
color: "var(--jx-fg)",
|
|
63710
|
+
'& > [part="drag-strip"]': {
|
|
63711
|
+
$description: "The window's handle. Electrobun's preload recognises the class and supplies the drag behaviour itself, so the rule here only sizes the strip: as tall as the hidden title bar would have been.",
|
|
63712
|
+
position: "absolute",
|
|
63713
|
+
top: "0",
|
|
63714
|
+
left: "0",
|
|
63715
|
+
right: "0",
|
|
63716
|
+
height: "calc(var(--jx-control-h) + var(--jx-space-3))"
|
|
63717
|
+
},
|
|
63718
|
+
'& > [part="content"]': {
|
|
63719
|
+
position: "relative",
|
|
63720
|
+
maxWidth: "40rem",
|
|
63721
|
+
width: "100%",
|
|
63722
|
+
padding: "0 var(--jx-space-6) var(--jx-space-7)",
|
|
63723
|
+
boxSizing: "border-box",
|
|
63724
|
+
display: "flex",
|
|
63725
|
+
flexDirection: "column",
|
|
63726
|
+
gap: "var(--jx-space-4)"
|
|
63727
|
+
},
|
|
63728
|
+
'& > [part="content"] > [part="icon"]': {
|
|
63729
|
+
$description: 'The child, not any descendant: `jx-button` renders its own `<span part="icon">` into light DOM unconditionally, so a bare `[part="icon"]` reached the Reload and Copy buttons too. `--jx-icon-size` is a custom property and `color` inherits, so both passed through that span\'s `display: contents` and drew the Copy button\'s clipboard glyph at 32px in warning amber, stretching the actions row with it. This is the collision `[part="error"]` taught: a part name is shared with the kit unless a selector says otherwise.',
|
|
63730
|
+
"--jx-icon-size": "2rem",
|
|
63731
|
+
color: "var(--jx-warning)"
|
|
63732
|
+
},
|
|
63733
|
+
'& [part="title"]': {
|
|
63734
|
+
margin: "0",
|
|
63735
|
+
fontSize: "1.5rem",
|
|
63736
|
+
fontWeight: "400",
|
|
63737
|
+
color: "var(--jx-fg)"
|
|
63738
|
+
},
|
|
63739
|
+
'& [part="lead"]': {
|
|
63740
|
+
margin: "0",
|
|
63741
|
+
fontSize: "var(--jx-text-lg)",
|
|
63742
|
+
lineHeight: "1.5",
|
|
63743
|
+
color: "var(--jx-fg)"
|
|
63744
|
+
},
|
|
63745
|
+
'& [part="detail"]': {
|
|
63746
|
+
$description: "The launcher's own error, verbatim. Selectable, because the point of printing it is that someone pastes it into an issue, and wrapped, because a stack line is wider than any window it is likely to be read in.",
|
|
63747
|
+
margin: "0",
|
|
63748
|
+
padding: "var(--jx-space-3) var(--jx-space-4)",
|
|
63749
|
+
maxHeight: "40vh",
|
|
63750
|
+
overflow: "auto",
|
|
63751
|
+
background: "var(--jx-bg-panel)",
|
|
63752
|
+
border: "1px solid var(--jx-border)",
|
|
63753
|
+
borderRadius: "var(--jx-radius-sm)",
|
|
63754
|
+
fontFamily: "var(--jx-font-mono)",
|
|
63755
|
+
fontSize: "var(--jx-text-sm)",
|
|
63756
|
+
whiteSpace: "pre-wrap",
|
|
63757
|
+
wordBreak: "break-word",
|
|
63758
|
+
userSelect: "text"
|
|
63759
|
+
},
|
|
63760
|
+
'& [part="facts"]': {
|
|
63761
|
+
margin: "0",
|
|
63762
|
+
fontSize: "var(--jx-text-sm)",
|
|
63763
|
+
color: "var(--jx-fg-dim)",
|
|
63764
|
+
wordBreak: "break-all",
|
|
63765
|
+
userSelect: "text"
|
|
63766
|
+
},
|
|
63767
|
+
'& [part="actions"]': {
|
|
63768
|
+
display: "flex",
|
|
63769
|
+
flexWrap: "wrap",
|
|
63770
|
+
gap: "var(--jx-space-3)"
|
|
63771
|
+
}
|
|
63772
|
+
},
|
|
63773
|
+
children: [
|
|
63774
|
+
{
|
|
63775
|
+
tagName: "div",
|
|
63776
|
+
attributes: {
|
|
63777
|
+
part: "drag-strip",
|
|
63778
|
+
class: "electrobun-webkit-app-region-drag",
|
|
63779
|
+
"aria-hidden": "true"
|
|
63780
|
+
}
|
|
63781
|
+
},
|
|
63782
|
+
{
|
|
63783
|
+
tagName: "div",
|
|
63784
|
+
attributes: {
|
|
63785
|
+
part: "content"
|
|
63786
|
+
},
|
|
63787
|
+
children: [
|
|
63788
|
+
{
|
|
63789
|
+
tagName: "jx-icon",
|
|
63790
|
+
attributes: {
|
|
63791
|
+
part: "icon"
|
|
63792
|
+
},
|
|
63793
|
+
$props: {
|
|
63794
|
+
name: "warning",
|
|
63795
|
+
weight: "fill"
|
|
63796
|
+
}
|
|
63797
|
+
},
|
|
63798
|
+
{
|
|
63799
|
+
tagName: "h1",
|
|
63800
|
+
attributes: {
|
|
63801
|
+
part: "title"
|
|
63802
|
+
},
|
|
63803
|
+
textContent: "${state.title}"
|
|
63804
|
+
},
|
|
63805
|
+
{
|
|
63806
|
+
tagName: "p",
|
|
63807
|
+
attributes: {
|
|
63808
|
+
part: "lead"
|
|
63809
|
+
},
|
|
63810
|
+
textContent: "${state.lead}"
|
|
63811
|
+
},
|
|
63812
|
+
{
|
|
63813
|
+
tagName: "div",
|
|
63814
|
+
attributes: {
|
|
63815
|
+
part: "detail-slot",
|
|
63816
|
+
role: "none"
|
|
63817
|
+
},
|
|
63818
|
+
style: {
|
|
63819
|
+
display: "contents"
|
|
63820
|
+
},
|
|
63821
|
+
$switch: {
|
|
63822
|
+
$ref: "#/state/hasDetail"
|
|
63823
|
+
},
|
|
63824
|
+
cases: {
|
|
63825
|
+
true: {
|
|
63826
|
+
tagName: "pre",
|
|
63827
|
+
attributes: {
|
|
63828
|
+
part: "detail"
|
|
63829
|
+
},
|
|
63830
|
+
textContent: "${state.detail}"
|
|
63831
|
+
}
|
|
63832
|
+
}
|
|
63833
|
+
},
|
|
63834
|
+
{
|
|
63835
|
+
tagName: "p",
|
|
63836
|
+
attributes: {
|
|
63837
|
+
part: "facts"
|
|
63838
|
+
},
|
|
63839
|
+
textContent: "${state.facts}"
|
|
63840
|
+
},
|
|
63841
|
+
{
|
|
63842
|
+
tagName: "div",
|
|
63843
|
+
attributes: {
|
|
63844
|
+
part: "actions"
|
|
63845
|
+
},
|
|
63846
|
+
children: [
|
|
63847
|
+
{
|
|
63848
|
+
tagName: "jx-button",
|
|
63849
|
+
attributes: {
|
|
63850
|
+
part: "reload"
|
|
63851
|
+
},
|
|
63852
|
+
$props: {
|
|
63853
|
+
variant: "accent"
|
|
63854
|
+
},
|
|
63855
|
+
onclick: {
|
|
63856
|
+
$prototype: "Function",
|
|
63857
|
+
body: [
|
|
63858
|
+
{
|
|
63859
|
+
operator: "call",
|
|
63860
|
+
target: {
|
|
63861
|
+
$ref: "#/state/reload"
|
|
63862
|
+
},
|
|
63863
|
+
value: []
|
|
63864
|
+
}
|
|
63865
|
+
]
|
|
63866
|
+
},
|
|
63867
|
+
children: [
|
|
63868
|
+
{
|
|
63869
|
+
tagName: "span",
|
|
63870
|
+
textContent: "Reload"
|
|
63871
|
+
}
|
|
63872
|
+
]
|
|
63873
|
+
},
|
|
63874
|
+
{
|
|
63875
|
+
tagName: "jx-button",
|
|
63876
|
+
attributes: {
|
|
63877
|
+
part: "copy"
|
|
63878
|
+
},
|
|
63879
|
+
$props: {
|
|
63880
|
+
quiet: true
|
|
63881
|
+
},
|
|
63882
|
+
onclick: {
|
|
63883
|
+
$prototype: "Function",
|
|
63884
|
+
body: [
|
|
63885
|
+
{
|
|
63886
|
+
operator: "call",
|
|
63887
|
+
target: {
|
|
63888
|
+
$ref: "#/state/copy"
|
|
63889
|
+
},
|
|
63890
|
+
value: []
|
|
63891
|
+
}
|
|
63892
|
+
]
|
|
63893
|
+
},
|
|
63894
|
+
children: [
|
|
63895
|
+
{
|
|
63896
|
+
tagName: "jx-icon",
|
|
63897
|
+
attributes: {
|
|
63898
|
+
slot: "icon"
|
|
63899
|
+
},
|
|
63900
|
+
$props: {
|
|
63901
|
+
name: "copy"
|
|
63902
|
+
}
|
|
63903
|
+
},
|
|
63904
|
+
{
|
|
63905
|
+
tagName: "span",
|
|
63906
|
+
textContent: "${state.copied ? 'Copied' : 'Copy details'}"
|
|
63907
|
+
}
|
|
63908
|
+
]
|
|
63909
|
+
}
|
|
63910
|
+
]
|
|
63911
|
+
}
|
|
63912
|
+
]
|
|
63913
|
+
}
|
|
63914
|
+
]
|
|
63915
|
+
};
|
|
63916
|
+
|
|
63917
|
+
// src/surfaces/boot-failure.ts
|
|
63918
|
+
registerSurface("boot-failure", boot_failure_default);
|
|
63919
|
+
function leadFor(refusal) {
|
|
63920
|
+
if (refusal.kind === "launcher") {
|
|
63921
|
+
return "The desktop app's bridge to its backend failed to start, so nothing can be opened, saved or created; reinstalling the latest release from the download page fixes this.";
|
|
63922
|
+
}
|
|
63923
|
+
if (refusal.kind === "declared") {
|
|
63924
|
+
return "This page's startup script never ran, so Studio has no backend to open, save or create projects with.";
|
|
63925
|
+
}
|
|
63926
|
+
return `This page was opened from a ${refusal.protocol} URL, which has no Studio backend behind it; open Studio through the desktop app or a Jx dev server instead.`;
|
|
63927
|
+
}
|
|
63928
|
+
function detailFor() {
|
|
63929
|
+
const error = launcherSignal2()?.error;
|
|
63930
|
+
if (!error) {
|
|
63931
|
+
return "";
|
|
63932
|
+
}
|
|
63933
|
+
const { message, stack } = error;
|
|
63934
|
+
if (!stack) {
|
|
63935
|
+
return message;
|
|
63936
|
+
}
|
|
63937
|
+
return stack.includes(message) ? stack : `${message}
|
|
63938
|
+
${stack}`;
|
|
63939
|
+
}
|
|
63940
|
+
function bootFailureView(refusal, page = location) {
|
|
63941
|
+
const detail = detailFor();
|
|
63942
|
+
const launcher = (refusal.kind === "launcher" ? refusal.launcher : undefined) ?? launcherSignal2()?.launcher ?? "none";
|
|
63943
|
+
const [where] = page.href.split(/[?#]/u);
|
|
63944
|
+
return {
|
|
63945
|
+
detail,
|
|
63946
|
+
facts: `${APP_NAME} bundle ${VERSION} (${GIT_COMMIT}) · launcher ${launcher} · ${where}`,
|
|
63947
|
+
hasDetail: detail !== "",
|
|
63948
|
+
lead: leadFor(refusal),
|
|
63949
|
+
title: "Jx Studio couldn't connect to its backend"
|
|
63950
|
+
};
|
|
63951
|
+
}
|
|
63952
|
+
function bootFailureReport(view) {
|
|
63953
|
+
return [view.title, view.lead, view.detail, view.facts].filter(Boolean).join(`
|
|
63954
|
+
|
|
63955
|
+
`);
|
|
63956
|
+
}
|
|
63957
|
+
function mountBootFailureSurface(host, refusal, page = location) {
|
|
63958
|
+
const view = bootFailureView(refusal, page);
|
|
63959
|
+
const scope = reactive({
|
|
63960
|
+
...view,
|
|
63961
|
+
copied: false,
|
|
63962
|
+
async copy() {
|
|
63963
|
+
try {
|
|
63964
|
+
await navigator.clipboard.writeText(bootFailureReport(view));
|
|
63965
|
+
scope.copied = true;
|
|
63966
|
+
} catch {}
|
|
63967
|
+
},
|
|
63968
|
+
reload() {
|
|
63969
|
+
location.reload();
|
|
63970
|
+
}
|
|
63971
|
+
});
|
|
63972
|
+
return mountSurface("boot-failure", scope, host);
|
|
63973
|
+
}
|
|
63326
63974
|
|
|
63327
63975
|
// src/shell/tree.ts
|
|
63328
63976
|
function overlayLayers() {
|
|
@@ -63357,6 +64005,12 @@ async function mountShellTree(host = document.body) {
|
|
|
63357
64005
|
await mountShellSurface(root);
|
|
63358
64006
|
render4(overlayLayers(), root);
|
|
63359
64007
|
}
|
|
64008
|
+
async function mountBootFailureTree(refusal, host = document.body) {
|
|
64009
|
+
const root = shellRoot(host);
|
|
64010
|
+
root.textContent = "";
|
|
64011
|
+
delete root["_$litPart$"];
|
|
64012
|
+
await mountBootFailureSurface(root, refusal);
|
|
64013
|
+
}
|
|
63360
64014
|
function shellRoot(host) {
|
|
63361
64015
|
const existing = host.querySelector?.("#shell-root");
|
|
63362
64016
|
if (existing) {
|
|
@@ -63369,6 +64023,7 @@ function shellRoot(host) {
|
|
|
63369
64023
|
}
|
|
63370
64024
|
|
|
63371
64025
|
// src/studio.ts
|
|
64026
|
+
installModulePreloadBatcher();
|
|
63372
64027
|
setBundleBase(import.meta.url);
|
|
63373
64028
|
var derivationDeps = {
|
|
63374
64029
|
fileExists: async (path) => {
|
|
@@ -63407,7 +64062,14 @@ async function navigateToComponent(componentPath) {
|
|
|
63407
64062
|
}
|
|
63408
64063
|
initCssData(webdata_default);
|
|
63409
64064
|
if (!hasPlatform2()) {
|
|
63410
|
-
|
|
64065
|
+
try {
|
|
64066
|
+
registerPlatform2(resolveDefaultPlatform());
|
|
64067
|
+
} catch (error) {
|
|
64068
|
+
if (error instanceof PlatformUnavailableError) {
|
|
64069
|
+
await mountBootFailureTree(error.refusal);
|
|
64070
|
+
}
|
|
64071
|
+
throw error;
|
|
64072
|
+
}
|
|
63411
64073
|
}
|
|
63412
64074
|
mountResizeEdges();
|
|
63413
64075
|
registerKit();
|
|
@@ -63434,6 +64096,7 @@ mount5(primaryCell?.chrome ?? document.createElement("div"), {
|
|
|
63434
64096
|
parseMediaEntries,
|
|
63435
64097
|
setCanvasMode
|
|
63436
64098
|
});
|
|
64099
|
+
setDiffChromeRepaint(render7);
|
|
63437
64100
|
mount4({
|
|
63438
64101
|
isEditing,
|
|
63439
64102
|
renderBlockActionBar
|
|
@@ -63515,11 +64178,8 @@ initWelcome({
|
|
|
63515
64178
|
}
|
|
63516
64179
|
},
|
|
63517
64180
|
cloneRepository: () => cloneRepository2({ openRecentProject }),
|
|
63518
|
-
openNewProject:
|
|
63519
|
-
|
|
63520
|
-
if (result) {
|
|
63521
|
-
openRecentProject(result.root);
|
|
63522
|
-
}
|
|
64181
|
+
openNewProject: (options) => {
|
|
64182
|
+
openNewProjectModal(options);
|
|
63523
64183
|
},
|
|
63524
64184
|
openProject: () => openProject(),
|
|
63525
64185
|
openRecentProject: (root) => openRecentProject(root)
|
|
@@ -64047,5 +64707,5 @@ export {
|
|
|
64047
64707
|
hasUnsavedTabs
|
|
64048
64708
|
};
|
|
64049
64709
|
|
|
64050
|
-
//# debugId=
|
|
64710
|
+
//# debugId=17BBB037A446E70F64756E2164756E21
|
|
64051
64711
|
//# sourceMappingURL=studio.js.map
|