@jxsuite/studio 5.3.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-nwnwdm6p.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-4rzvmp0m.js → content-source-jt7rbcbc.js} +22 -22
- package/dist/chunks/{convert-to-repeater-jxsv62v7.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-2mrptyek.js → extension-sections-r73an5s6.js} +30 -29
- package/dist/chunks/{file-ops-knfqshrm.js → file-ops-3xan8mnj.js} +21 -21
- package/dist/chunks/{files-y6fr4he9.js → files-egsb0ry8.js} +28 -28
- package/dist/chunks/{format-host-1k4qrcqd.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-2rkrsjva.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-a7b87n2c.js → grid-view-qaqer9kr.js} +24 -24
- package/dist/chunks/{icons-yy10wk9r.js → icons-drxq9q10.js} +8 -2
- package/dist/chunks/{iframe-host-scpw01h8.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/layers-panel-83a16ytx.js +41 -0
- package/dist/chunks/{layers-jnvdbqva.js → layers-s6mc01ry.js} +3 -3
- package/dist/chunks/{media-picker-f66nav16.js → media-picker-nhn6rv4w.js} +11 -11
- package/dist/chunks/{menu-1dfhry4s.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-km6abc6q.js → monaco-setup-gsjdysze.js} +6 -6
- 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/{studio-ze46gzjh.js → studio-01269reb.js} +7 -7
- 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-d1n2bykn.js → studio-3nprwbd9.js} +117 -25
- package/dist/chunks/{studio-4c041r8y.js → studio-40z39dme.js} +3 -3
- package/dist/chunks/{studio-151w0xnh.js → studio-482eeazk.js} +20 -20
- package/dist/chunks/{studio-cgprcdxn.js → studio-4998spet.js} +10 -9
- package/dist/chunks/{studio-nq5yf5mp.js → studio-557bmn0x.js} +2 -2
- package/dist/chunks/{studio-rtskkm43.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-6gryj0sg.js → studio-9vsvz7w7.js} +6 -6
- package/dist/chunks/{studio-50wfwy44.js → studio-9wm0xzpf.js} +19 -19
- package/dist/chunks/{studio-0j7r7s9g.js → studio-ag3z6shb.js} +10 -4
- package/dist/chunks/studio-aj9drheq.js +177 -0
- package/dist/chunks/{studio-xajk70w0.js → studio-apv9x70c.js} +189 -130
- package/dist/chunks/{studio-c04nzbby.js → studio-b9q3znz8.js} +2 -2
- package/dist/chunks/{studio-nfx354t1.js → studio-bh47w0cy.js} +9 -10
- package/dist/chunks/{studio-gk2twyh4.js → studio-dgdm6vvt.js} +264 -52
- package/dist/chunks/{studio-gtfq1dkz.js → studio-dwdcr2e8.js} +4 -4
- package/dist/chunks/{studio-nqgawfks.js → studio-fbcyz234.js} +146 -139
- package/dist/chunks/{studio-27q131j3.js → studio-fqpkpq1t.js} +7 -4
- package/dist/chunks/{studio-c0qgc3tn.js → studio-fy320dfc.js} +62 -68
- package/dist/chunks/{studio-zjjde71a.js → studio-gjj4bbfx.js} +2 -2
- package/dist/chunks/{studio-g33v4fe9.js → studio-jfhxy8h3.js} +7 -7
- package/dist/chunks/{studio-ab87phj1.js → studio-kwewmhhq.js} +4 -4
- package/dist/chunks/{studio-7rrb1esn.js → studio-m58xn8at.js} +25 -24
- package/dist/chunks/{studio-fzjx96t2.js → studio-mfkh1q8d.js} +17 -17
- package/dist/chunks/{studio-drg8qj9k.js → studio-mrh0a92f.js} +17 -17
- package/dist/chunks/{studio-jt27ta6x.js → studio-mt79tn56.js} +3 -3
- package/dist/chunks/{studio-b66rsh78.js → studio-ne593z1j.js} +38 -31
- package/dist/chunks/{studio-h4nvnjvz.js → studio-ptfcxkz5.js} +418 -232
- package/dist/chunks/{studio-dd1qqkry.js → studio-qxye31h7.js} +7 -7
- package/dist/chunks/{studio-ye9drbk8.js → studio-rav5bpmp.js} +73 -52
- package/dist/chunks/{studio-6kvgrkrp.js → studio-rwmmbrth.js} +6 -6
- package/dist/chunks/{studio-b7xacrwe.js → studio-sceg8ws0.js} +8 -8
- package/dist/chunks/{studio-msc1ee1m.js → studio-sjas5b3a.js} +19 -13
- package/dist/chunks/{studio-a65w20ex.js → studio-vd67b66d.js} +3 -3
- package/dist/chunks/{studio-7144epdr.js → studio-w475cbf9.js} +10 -10
- package/dist/chunks/{studio-4f3psrk1.js → studio-w7gpcvfj.js} +9 -9
- package/dist/chunks/{studio-nsgw7dbk.js → studio-xrbfdxgk.js} +7 -7
- package/dist/chunks/{studio-ke09tn9k.js → studio-y49b6mjv.js} +35 -35
- package/dist/chunks/{studio-j0w60gft.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-np10mayv.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 +18 -16
- package/dist/studio.css +956 -139
- package/dist/studio.js +1453 -830
- 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 +15 -14
- 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/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/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 +46 -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 +2 -2
- package/src/surfaces/formula-palette.ts +1 -1
- 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/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 +5 -5
- 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 +1 -1
- 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-1wnsqn5d.js +0 -42
- package/dist/chunks/layers-panel-73gfkcga.js +0 -41
- package/dist/chunks/publish-panel-z1zd2da1.js +0 -27
- package/dist/chunks/quick-search-zdv7h8ht.js +0 -47
- package/dist/chunks/right-panel-889vbnm5.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,
|
|
@@ -373,7 +390,7 @@ import {
|
|
|
373
390
|
KIT_BASE,
|
|
374
391
|
setActiveRegistry,
|
|
375
392
|
activeRegistry
|
|
376
|
-
} from "./chunks/studio-
|
|
393
|
+
} from "./chunks/studio-3nprwbd9.js";
|
|
377
394
|
import {
|
|
378
395
|
DERIVE_PRESETS,
|
|
379
396
|
PRESET_LABELS,
|
|
@@ -403,7 +420,7 @@ import {
|
|
|
403
420
|
defBadgeLabel,
|
|
404
421
|
mountSignalsPanel,
|
|
405
422
|
registerSignalsCommands
|
|
406
|
-
} from "./chunks/studio-
|
|
423
|
+
} from "./chunks/studio-rav5bpmp.js";
|
|
407
424
|
import {
|
|
408
425
|
mountsUsing,
|
|
409
426
|
mountsOf,
|
|
@@ -429,7 +446,7 @@ import {
|
|
|
429
446
|
showConfirmDialog2,
|
|
430
447
|
showSaveDiscardDialog2,
|
|
431
448
|
showPromptDialog2
|
|
432
|
-
} from "./chunks/studio-
|
|
449
|
+
} from "./chunks/studio-ag3z6shb.js";
|
|
433
450
|
import {
|
|
434
451
|
html3,
|
|
435
452
|
nothing2,
|
|
@@ -445,11 +462,11 @@ import {
|
|
|
445
462
|
uploadAssets,
|
|
446
463
|
mediaSiteUrl,
|
|
447
464
|
previewFileSrc
|
|
448
|
-
} from "./chunks/studio-
|
|
449
|
-
import"./chunks/studio-
|
|
465
|
+
} from "./chunks/studio-sceg8ws0.js";
|
|
466
|
+
import"./chunks/studio-dwdcr2e8.js";
|
|
450
467
|
import {
|
|
451
468
|
invalidateUsages2
|
|
452
|
-
} from "./chunks/studio-
|
|
469
|
+
} from "./chunks/studio-557bmn0x.js";
|
|
453
470
|
import {
|
|
454
471
|
hydrateProjectList,
|
|
455
472
|
getProjectList,
|
|
@@ -471,11 +488,7 @@ import {
|
|
|
471
488
|
registerSourceControlCommands2,
|
|
472
489
|
cleanupGitPanel2,
|
|
473
490
|
registerGitPanel2
|
|
474
|
-
} from "./chunks/studio-
|
|
475
|
-
import"./chunks/studio-a65w20ex.js";
|
|
476
|
-
import {
|
|
477
|
-
openMenu2
|
|
478
|
-
} from "./chunks/studio-jt27ta6x.js";
|
|
491
|
+
} from "./chunks/studio-apv9x70c.js";
|
|
479
492
|
import {
|
|
480
493
|
parseGridTabId,
|
|
481
494
|
gridTabLabel,
|
|
@@ -501,22 +514,26 @@ import {
|
|
|
501
514
|
exportFile2,
|
|
502
515
|
confirmFileDelete2,
|
|
503
516
|
renamePromptMessage2
|
|
504
|
-
} 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";
|
|
505
522
|
import {
|
|
506
523
|
platformSupportsPublish,
|
|
507
524
|
latestDeployment,
|
|
508
525
|
currentDeploy,
|
|
509
526
|
noteDeployment
|
|
510
|
-
} from "./chunks/studio-
|
|
527
|
+
} from "./chunks/studio-xrbfdxgk.js";
|
|
511
528
|
import {
|
|
512
529
|
resetIgnoreCache,
|
|
513
530
|
startFsSync2
|
|
514
|
-
} from "./chunks/studio-
|
|
531
|
+
} from "./chunks/studio-40z39dme.js";
|
|
515
532
|
import {
|
|
516
533
|
beginActivity,
|
|
517
534
|
registerActivityPanel,
|
|
518
535
|
showProgressModal
|
|
519
|
-
} from "./chunks/studio-
|
|
536
|
+
} from "./chunks/studio-4998spet.js";
|
|
520
537
|
import {
|
|
521
538
|
_defineProperty,
|
|
522
539
|
combine,
|
|
@@ -545,12 +562,12 @@ import {
|
|
|
545
562
|
createLibrarySource,
|
|
546
563
|
openLibraryTab,
|
|
547
564
|
registerGridCommands
|
|
548
|
-
} from "./chunks/studio-
|
|
565
|
+
} from "./chunks/studio-9wm0xzpf.js";
|
|
549
566
|
import {
|
|
550
567
|
DRAFT_FIELD,
|
|
551
568
|
isDraftEntry
|
|
552
|
-
} from "./chunks/studio-
|
|
553
|
-
import"./chunks/studio-
|
|
569
|
+
} from "./chunks/studio-ygvr9e4q.js";
|
|
570
|
+
import"./chunks/studio-sjas5b3a.js";
|
|
554
571
|
import {
|
|
555
572
|
computeWindow
|
|
556
573
|
} from "./chunks/studio-a5h5j33a.js";
|
|
@@ -577,11 +594,11 @@ import {
|
|
|
577
594
|
reloadCleanTab2,
|
|
578
595
|
reloadFileInTab2,
|
|
579
596
|
registerFilesPanel2
|
|
580
|
-
} from "./chunks/studio-
|
|
597
|
+
} from "./chunks/studio-m58xn8at.js";
|
|
581
598
|
import {
|
|
582
599
|
REDIRECT_STATUSES,
|
|
583
600
|
referenceTarget
|
|
584
|
-
} from "./chunks/studio-
|
|
601
|
+
} from "./chunks/studio-w7gpcvfj.js";
|
|
585
602
|
import"./chunks/studio-tjjk7jvk.js";
|
|
586
603
|
import {
|
|
587
604
|
FIELD_TYPES,
|
|
@@ -597,7 +614,7 @@ import {
|
|
|
597
614
|
renderEntryMode,
|
|
598
615
|
createEntry2,
|
|
599
616
|
registerContentCommands2
|
|
600
|
-
} from "./chunks/studio-
|
|
617
|
+
} from "./chunks/studio-482eeazk.js";
|
|
601
618
|
import {
|
|
602
619
|
notifySettingsDocument2,
|
|
603
620
|
onSettingsDocumentChanged2,
|
|
@@ -627,11 +644,11 @@ import {
|
|
|
627
644
|
renderStylebookMode,
|
|
628
645
|
selectStylebookTag,
|
|
629
646
|
defaultCommands
|
|
630
|
-
} from "./chunks/studio-
|
|
647
|
+
} from "./chunks/studio-bh47w0cy.js";
|
|
631
648
|
import {
|
|
632
649
|
openPopoverSurface,
|
|
633
650
|
invalidateMediaCache2
|
|
634
|
-
} from "./chunks/studio-
|
|
651
|
+
} from "./chunks/studio-rwmmbrth.js";
|
|
635
652
|
import {
|
|
636
653
|
findEnclosingRepeater,
|
|
637
654
|
convertToComponent,
|
|
@@ -674,11 +691,11 @@ import {
|
|
|
674
691
|
liveElementCommands,
|
|
675
692
|
dismissContextMenu,
|
|
676
693
|
showContextMenu
|
|
677
|
-
} from "./chunks/studio-
|
|
694
|
+
} from "./chunks/studio-ne593z1j.js";
|
|
678
695
|
import {
|
|
679
696
|
initQuickSearch2,
|
|
680
697
|
openQuickSearch2
|
|
681
|
-
} from "./chunks/studio-
|
|
698
|
+
} from "./chunks/studio-jfhxy8h3.js";
|
|
682
699
|
import {
|
|
683
700
|
initCssData,
|
|
684
701
|
registerStyleCommands,
|
|
@@ -686,7 +703,7 @@ import {
|
|
|
686
703
|
render2,
|
|
687
704
|
inspectorTab2,
|
|
688
705
|
setInspectorTab2
|
|
689
|
-
} from "./chunks/studio-
|
|
706
|
+
} from "./chunks/studio-fy320dfc.js";
|
|
690
707
|
import {
|
|
691
708
|
validateDoc2,
|
|
692
709
|
validateProjectConfig2
|
|
@@ -694,11 +711,12 @@ import {
|
|
|
694
711
|
import {
|
|
695
712
|
getOpenAiKey,
|
|
696
713
|
setOpenAiKey,
|
|
697
|
-
hasOpenAiKey,
|
|
698
714
|
getBaseUrl,
|
|
699
715
|
storedModel,
|
|
700
716
|
setModel,
|
|
701
717
|
saveAiProvider,
|
|
718
|
+
aiProviderDiffers,
|
|
719
|
+
storedEquals,
|
|
702
720
|
aiConnection,
|
|
703
721
|
resetModelCache,
|
|
704
722
|
cachedModels,
|
|
@@ -712,8 +730,8 @@ import {
|
|
|
712
730
|
proxyModelsErrorMessage,
|
|
713
731
|
preferredModel,
|
|
714
732
|
fetchAvailableModels
|
|
715
|
-
} from "./chunks/studio-
|
|
716
|
-
import"./chunks/studio-
|
|
733
|
+
} from "./chunks/studio-7d3c1m59.js";
|
|
734
|
+
import"./chunks/studio-b9q3znz8.js";
|
|
717
735
|
import {
|
|
718
736
|
platformBrokersCf,
|
|
719
737
|
refreshCfConnection,
|
|
@@ -723,13 +741,13 @@ import {
|
|
|
723
741
|
revokeAccount,
|
|
724
742
|
onCredentialsChanged,
|
|
725
743
|
notifyCredentialsChanged
|
|
726
|
-
} from "./chunks/studio-
|
|
744
|
+
} from "./chunks/studio-qxye31h7.js";
|
|
727
745
|
import {
|
|
728
746
|
seedPublishConnected2
|
|
729
|
-
} from "./chunks/studio-
|
|
747
|
+
} from "./chunks/studio-w475cbf9.js";
|
|
730
748
|
import {
|
|
731
749
|
selectContributedEntry
|
|
732
|
-
} from "./chunks/studio-
|
|
750
|
+
} from "./chunks/studio-01269reb.js";
|
|
733
751
|
import {
|
|
734
752
|
deepEqual2,
|
|
735
753
|
matchChildren
|
|
@@ -751,7 +769,7 @@ import {
|
|
|
751
769
|
import {
|
|
752
770
|
entryCollections,
|
|
753
771
|
collectionOfPath
|
|
754
|
-
} from "./chunks/studio-
|
|
772
|
+
} from "./chunks/studio-5w26wza5.js";
|
|
755
773
|
import"./chunks/studio-q0a3yk4n.js";
|
|
756
774
|
import {
|
|
757
775
|
resolveAssetRef,
|
|
@@ -759,7 +777,7 @@ import {
|
|
|
759
777
|
assetContextFor,
|
|
760
778
|
previewAssetSrc,
|
|
761
779
|
hostAssetDeclarations
|
|
762
|
-
} from "./chunks/studio-
|
|
780
|
+
} from "./chunks/studio-kwewmhhq.js";
|
|
763
781
|
import {
|
|
764
782
|
rectOf,
|
|
765
783
|
elementAtPoint
|
|
@@ -774,7 +792,7 @@ import {
|
|
|
774
792
|
__preload,
|
|
775
793
|
__chunks
|
|
776
794
|
} from "./chunks/studio-5te5tqng.js";
|
|
777
|
-
__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);
|
|
778
796
|
|
|
779
797
|
// ../../node_modules/ms/index.js
|
|
780
798
|
var require_ms = __commonJS(function(exports, module) {
|
|
@@ -1385,6 +1403,9 @@ function stepDiff(paneId, delta) {
|
|
|
1385
1403
|
function diffViewOf(paneId) {
|
|
1386
1404
|
return _records.get(paneId)?.view ?? "visual";
|
|
1387
1405
|
}
|
|
1406
|
+
function diffShowsCode(paneId, renderable) {
|
|
1407
|
+
return !renderable || diffViewOf(paneId) === "code";
|
|
1408
|
+
}
|
|
1388
1409
|
function setDiffView(paneId, view) {
|
|
1389
1410
|
recordFor(paneId).view = view;
|
|
1390
1411
|
}
|
|
@@ -1729,9 +1750,14 @@ var _repaint = () => {};
|
|
|
1729
1750
|
function setDiffRepaint(repaint) {
|
|
1730
1751
|
_repaint = repaint;
|
|
1731
1752
|
}
|
|
1753
|
+
var _chromeRepaint = null;
|
|
1754
|
+
function setDiffChromeRepaint(repaint) {
|
|
1755
|
+
_chromeRepaint = repaint;
|
|
1756
|
+
}
|
|
1732
1757
|
function repaintDiffStage(paneId) {
|
|
1733
1758
|
surfaceForPane(paneId).prevCanvasMode = null;
|
|
1734
1759
|
_repaint(paneId);
|
|
1760
|
+
_chromeRepaint?.();
|
|
1735
1761
|
}
|
|
1736
1762
|
function renderDiffToolbar(paneId) {
|
|
1737
1763
|
const bar = _bars.get(paneId);
|
|
@@ -1779,7 +1805,7 @@ async function stepDiffAndReveal(paneId, delta) {
|
|
|
1779
1805
|
if (!step) {
|
|
1780
1806
|
return;
|
|
1781
1807
|
}
|
|
1782
|
-
const { hostForCanvas, measureInCanvas, revealCanvasPathIn } = await (__preload("
|
|
1808
|
+
const { hostForCanvas, measureInCanvas, revealCanvasPathIn } = await (__preload("psetcv20"), import("./chunks/iframe-host-psetcv20.js"));
|
|
1783
1809
|
const measured = await Promise.all([
|
|
1784
1810
|
step.originalPath && original ? measureInCanvas(original, step.originalPath) : null,
|
|
1785
1811
|
step.currentPath && current ? measureInCanvas(current, step.currentPath) : null
|
|
@@ -1997,6 +2023,37 @@ function buildChangeMap(a, b, opts = {}) {
|
|
|
1997
2023
|
};
|
|
1998
2024
|
}
|
|
1999
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
|
+
|
|
2000
2057
|
// src/collab/monaco-cursors.ts
|
|
2001
2058
|
function cssString(value) {
|
|
2002
2059
|
return `"${value.replaceAll("\\", String.raw`\\`).replaceAll('"', String.raw`\"`)}"`;
|
|
@@ -2047,7 +2104,7 @@ function loadGridEngine() {
|
|
|
2047
2104
|
if (_engine) {
|
|
2048
2105
|
return Promise.resolve(_engine);
|
|
2049
2106
|
}
|
|
2050
|
-
_loading ??= (__preload("
|
|
2107
|
+
_loading ??= (__preload("qaqer9kr"), import("./chunks/grid-view-qaqer9kr.js")).then((engine) => {
|
|
2051
2108
|
_engine = engine;
|
|
2052
2109
|
return engine;
|
|
2053
2110
|
});
|
|
@@ -2066,7 +2123,7 @@ var grid_panel_default = {
|
|
|
2066
2123
|
part: "grid"
|
|
2067
2124
|
},
|
|
2068
2125
|
style: {
|
|
2069
|
-
$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.",
|
|
2070
2127
|
display: "flex",
|
|
2071
2128
|
flexDirection: "column",
|
|
2072
2129
|
height: "100%",
|
|
@@ -2518,7 +2575,7 @@ var grid_panel_default = {
|
|
|
2518
2575
|
textContent: "${state.viewLabel}"
|
|
2519
2576
|
}
|
|
2520
2577
|
],
|
|
2521
|
-
$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."
|
|
2522
2579
|
},
|
|
2523
2580
|
{
|
|
2524
2581
|
tagName: "jx-textfield",
|
|
@@ -2854,7 +2911,7 @@ function mountGridPanelSurface(host, view, actions) {
|
|
|
2854
2911
|
var grid_views_default = {
|
|
2855
2912
|
$schema: "https://jxsuite.com/schema/v1",
|
|
2856
2913
|
$id: "StudioGridViews",
|
|
2857
|
-
$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.",
|
|
2858
2915
|
tagName: "jx-popover",
|
|
2859
2916
|
attributes: {
|
|
2860
2917
|
part: "views-panel"
|
|
@@ -7021,10 +7078,10 @@ var add_repo_default = {
|
|
|
7021
7078
|
color: "var(--danger)"
|
|
7022
7079
|
},
|
|
7023
7080
|
'& [part="access"]': {
|
|
7024
|
-
$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.",
|
|
7025
7082
|
display: "flex",
|
|
7026
7083
|
flexWrap: "wrap",
|
|
7027
|
-
alignItems: "
|
|
7084
|
+
alignItems: "center",
|
|
7028
7085
|
gap: "var(--jx-space-2)",
|
|
7029
7086
|
paddingTop: "var(--jx-space-3)",
|
|
7030
7087
|
borderTop: "1px solid var(--border)",
|
|
@@ -7678,10 +7735,15 @@ var welcome_default = {
|
|
|
7678
7735
|
letterSpacing: "0.3px"
|
|
7679
7736
|
},
|
|
7680
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.",
|
|
7681
7739
|
display: "flex",
|
|
7682
|
-
alignItems: "
|
|
7740
|
+
alignItems: "center",
|
|
7683
7741
|
justifyContent: "space-between",
|
|
7684
|
-
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"
|
|
7685
7747
|
},
|
|
7686
7748
|
'& [part="actions"]': {
|
|
7687
7749
|
display: "flex",
|
|
@@ -8860,7 +8922,7 @@ var tab_strip_default = {
|
|
|
8860
8922
|
lineHeight: "1"
|
|
8861
8923
|
},
|
|
8862
8924
|
'& [part="pill"]': {
|
|
8863
|
-
$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.',
|
|
8864
8926
|
padding: "0 5px",
|
|
8865
8927
|
borderRadius: "var(--radius)",
|
|
8866
8928
|
background: "var(--bg)",
|
|
@@ -9093,7 +9155,7 @@ var tab_strip_default = {
|
|
|
9093
9155
|
textContent: "${$map.item.pill}"
|
|
9094
9156
|
},
|
|
9095
9157
|
{
|
|
9096
|
-
$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.",
|
|
9097
9159
|
tagName: "button",
|
|
9098
9160
|
attributes: {
|
|
9099
9161
|
slot: "actions",
|
|
@@ -10253,7 +10315,7 @@ var panel_page_default = {
|
|
|
10253
10315
|
padding: "0 var(--jx-space-2)"
|
|
10254
10316
|
},
|
|
10255
10317
|
'& [part="row"]': {
|
|
10256
|
-
$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.",
|
|
10257
10319
|
display: "flex",
|
|
10258
10320
|
flexDirection: "column",
|
|
10259
10321
|
alignItems: "stretch",
|
|
@@ -10269,7 +10331,7 @@ var panel_page_default = {
|
|
|
10269
10331
|
color: "var(--fg-dim)"
|
|
10270
10332
|
},
|
|
10271
10333
|
'& [part="chip"]': {
|
|
10272
|
-
$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.",
|
|
10273
10335
|
width: "6px",
|
|
10274
10336
|
height: "6px",
|
|
10275
10337
|
padding: "0",
|
|
@@ -11937,7 +11999,7 @@ function removeEntry(key) {
|
|
|
11937
11999
|
}
|
|
11938
12000
|
var _mediaPicker = null;
|
|
11939
12001
|
function mediaPicker() {
|
|
11940
|
-
_mediaPicker ??= (__preload("
|
|
12002
|
+
_mediaPicker ??= (__preload("nhn6rv4w"), import("./chunks/media-picker-nhn6rv4w.js"));
|
|
11941
12003
|
return _mediaPicker;
|
|
11942
12004
|
}
|
|
11943
12005
|
var ACTIONS = {
|
|
@@ -12040,28 +12102,21 @@ function registerPagePanel() {
|
|
|
12040
12102
|
var doc_header_default = {
|
|
12041
12103
|
$schema: "https://jxsuite.com/schema/v1",
|
|
12042
12104
|
$id: "StudioDocHeader",
|
|
12043
|
-
$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.",
|
|
12044
12106
|
tagName: "section",
|
|
12045
12107
|
attributes: {
|
|
12046
12108
|
part: "card",
|
|
12047
12109
|
"aria-label": "Document header",
|
|
12048
|
-
"data-jx-region": "${state.region}"
|
|
12049
|
-
"data-placement": "${state.placement}"
|
|
12110
|
+
"data-jx-region": "${state.region}"
|
|
12050
12111
|
},
|
|
12051
12112
|
style: {
|
|
12052
|
-
$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.",
|
|
12053
12114
|
display: "flex",
|
|
12054
12115
|
flexDirection: "column",
|
|
12055
12116
|
padding: "8px 12px 10px",
|
|
12056
12117
|
background: "var(--bg-panel)",
|
|
12057
|
-
|
|
12058
|
-
borderRadius: "var(--radius)",
|
|
12118
|
+
borderBottom: "1px solid var(--border)",
|
|
12059
12119
|
fontFamily: "var(--jx-font-sans)",
|
|
12060
|
-
'&[data-placement="pinned"]': {
|
|
12061
|
-
$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.",
|
|
12062
|
-
borderWidth: "0 0 1px",
|
|
12063
|
-
borderRadius: "0"
|
|
12064
|
-
},
|
|
12065
12120
|
'& [part="bar"]': {
|
|
12066
12121
|
display: "flex",
|
|
12067
12122
|
alignItems: "baseline",
|
|
@@ -12087,7 +12142,7 @@ var doc_header_default = {
|
|
|
12087
12142
|
gap: "2px"
|
|
12088
12143
|
},
|
|
12089
12144
|
'& [part="row"]': {
|
|
12090
|
-
$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.",
|
|
12091
12146
|
display: "flex",
|
|
12092
12147
|
flexDirection: "column",
|
|
12093
12148
|
alignItems: "stretch",
|
|
@@ -12103,7 +12158,7 @@ var doc_header_default = {
|
|
|
12103
12158
|
color: "var(--fg-dim)"
|
|
12104
12159
|
},
|
|
12105
12160
|
'& [part="chip"]': {
|
|
12106
|
-
$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.",
|
|
12107
12162
|
width: "6px",
|
|
12108
12163
|
height: "6px",
|
|
12109
12164
|
padding: "0",
|
|
@@ -12781,7 +12836,6 @@ var doc_header_default = {
|
|
|
12781
12836
|
registerSurface("doc-header", doc_header_default);
|
|
12782
12837
|
function project9(scope, view) {
|
|
12783
12838
|
scope.region = view.region;
|
|
12784
|
-
scope.placement = view.placement;
|
|
12785
12839
|
scope.collection = view.collection;
|
|
12786
12840
|
scope.route = view.route;
|
|
12787
12841
|
scope.hasRoute = view.hasRoute;
|
|
@@ -12795,7 +12849,6 @@ function mountDocHeaderSurface(host, view, actions) {
|
|
|
12795
12849
|
...actions,
|
|
12796
12850
|
collection: "Document",
|
|
12797
12851
|
hasRoute: false,
|
|
12798
|
-
placement: "in-column",
|
|
12799
12852
|
rawEntries: [],
|
|
12800
12853
|
rawOpen: false,
|
|
12801
12854
|
rawState: "empty",
|
|
@@ -12950,7 +13003,6 @@ function viewFor(tab, paneId, card) {
|
|
|
12950
13003
|
return {
|
|
12951
13004
|
collection: collection ? collection.name : "Document",
|
|
12952
13005
|
hasRoute: route !== null,
|
|
12953
|
-
placement: card.el.dataset["placement"] === "pinned" ? "pinned" : "in-column",
|
|
12954
13006
|
rawEntries: raw,
|
|
12955
13007
|
rawOpen: _rawOpen.has(tab.id),
|
|
12956
13008
|
rawState: raw.length === 0 ? "empty" : "list",
|
|
@@ -13126,7 +13178,7 @@ function makeActions(card) {
|
|
|
13126
13178
|
}
|
|
13127
13179
|
var _mediaPicker2 = null;
|
|
13128
13180
|
function mediaPicker2() {
|
|
13129
|
-
_mediaPicker2 ??= (__preload("
|
|
13181
|
+
_mediaPicker2 ??= (__preload("nhn6rv4w"), import("./chunks/media-picker-nhn6rv4w.js"));
|
|
13130
13182
|
return _mediaPicker2;
|
|
13131
13183
|
}
|
|
13132
13184
|
// src/surfaces/canvas-stage.json
|
|
@@ -13139,7 +13191,7 @@ var canvas_stage_default = {
|
|
|
13139
13191
|
part: "stage"
|
|
13140
13192
|
},
|
|
13141
13193
|
style: {
|
|
13142
|
-
$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.",
|
|
13143
13195
|
display: "contents",
|
|
13144
13196
|
'& [part="panzoom"]': {
|
|
13145
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.",
|
|
@@ -13229,21 +13281,12 @@ var canvas_stage_default = {
|
|
|
13229
13281
|
left: "50%",
|
|
13230
13282
|
transform: "translateX(-50%)"
|
|
13231
13283
|
},
|
|
13232
|
-
'& [part="doc-header"]
|
|
13233
|
-
$description: "Edit: the card is a block of the
|
|
13234
|
-
display: "block",
|
|
13235
|
-
paddingBottom: "16px"
|
|
13236
|
-
},
|
|
13237
|
-
'& [part="doc-header"][data-placement="pinned"]': {
|
|
13238
|
-
$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.",
|
|
13239
13286
|
flexShrink: "0",
|
|
13240
|
-
maxHeight: "40vh",
|
|
13241
|
-
overflowY: "auto"
|
|
13242
|
-
|
|
13243
|
-
'& [part="doc-header"][data-placement="pinned"] .doc-header': {
|
|
13244
|
-
$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.",
|
|
13245
|
-
borderWidth: "0 0 1px",
|
|
13246
|
-
borderRadius: "0"
|
|
13287
|
+
maxHeight: "min(40vh, 50%)",
|
|
13288
|
+
overflowY: "auto",
|
|
13289
|
+
boxShadow: "0 8px 8px -8px rgb(0 0 0 / 0.35)"
|
|
13247
13290
|
},
|
|
13248
13291
|
'& [part="edit-canvas"]': {
|
|
13249
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.",
|
|
@@ -13330,7 +13373,7 @@ var canvas_stage_default = {
|
|
|
13330
13373
|
},
|
|
13331
13374
|
children: [
|
|
13332
13375
|
{
|
|
13333
|
-
$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.",
|
|
13334
13377
|
tagName: "div",
|
|
13335
13378
|
attributes: {
|
|
13336
13379
|
part: "lead-slot"
|
|
@@ -13343,10 +13386,10 @@ var canvas_stage_default = {
|
|
|
13343
13386
|
},
|
|
13344
13387
|
cases: {
|
|
13345
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`.",
|
|
13346
13390
|
tagName: "div",
|
|
13347
13391
|
attributes: {
|
|
13348
|
-
part: "doc-header"
|
|
13349
|
-
"data-placement": "pinned"
|
|
13392
|
+
part: "doc-header"
|
|
13350
13393
|
}
|
|
13351
13394
|
},
|
|
13352
13395
|
toolbar: {
|
|
@@ -13427,28 +13470,6 @@ var canvas_stage_default = {
|
|
|
13427
13470
|
}
|
|
13428
13471
|
},
|
|
13429
13472
|
children: [
|
|
13430
|
-
{
|
|
13431
|
-
$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.",
|
|
13432
|
-
tagName: "div",
|
|
13433
|
-
attributes: {
|
|
13434
|
-
part: "column-header-slot"
|
|
13435
|
-
},
|
|
13436
|
-
style: {
|
|
13437
|
-
display: "contents"
|
|
13438
|
-
},
|
|
13439
|
-
$switch: {
|
|
13440
|
-
$ref: "#/state/columnHeader"
|
|
13441
|
-
},
|
|
13442
|
-
cases: {
|
|
13443
|
-
shown: {
|
|
13444
|
-
tagName: "div",
|
|
13445
|
-
attributes: {
|
|
13446
|
-
part: "doc-header",
|
|
13447
|
-
"data-placement": "in-column"
|
|
13448
|
-
}
|
|
13449
|
-
}
|
|
13450
|
-
}
|
|
13451
|
-
},
|
|
13452
13473
|
{
|
|
13453
13474
|
$prototype: "Array",
|
|
13454
13475
|
items: { $ref: "#/state/panels" },
|
|
@@ -13602,7 +13623,6 @@ function project10(scope, view) {
|
|
|
13602
13623
|
scope.frameVars = view.frameVars;
|
|
13603
13624
|
scope.hug = view.hug;
|
|
13604
13625
|
scope.lead = view.lead;
|
|
13605
|
-
scope.columnHeader = view.columnHeader;
|
|
13606
13626
|
scope.handles = view.handles;
|
|
13607
13627
|
scope.panels = view.panels;
|
|
13608
13628
|
}
|
|
@@ -13610,7 +13630,6 @@ function mountCanvasStage(host, view, actions) {
|
|
|
13610
13630
|
host.replaceChildren();
|
|
13611
13631
|
const boards = new Map;
|
|
13612
13632
|
const scope = reactive({
|
|
13613
|
-
columnHeader: "hidden",
|
|
13614
13633
|
frame: "boards",
|
|
13615
13634
|
framePart: "panzoom",
|
|
13616
13635
|
frameVars: "",
|
|
@@ -13632,7 +13651,7 @@ function mountCanvasStage(host, view, actions) {
|
|
|
13632
13651
|
} else if (part && element instanceof HTMLElement) {
|
|
13633
13652
|
const slot = attrOf(def, "data-slot");
|
|
13634
13653
|
const named = slot === "frame" ? scope.framePart : slot === "inner" ? scope.innerPart : part;
|
|
13635
|
-
actions.host(named, element, attrOf(def, "data-side")
|
|
13654
|
+
actions.host(named, element, attrOf(def, "data-side"));
|
|
13636
13655
|
}
|
|
13637
13656
|
}
|
|
13638
13657
|
}).then((surface) => {
|
|
@@ -13976,7 +13995,6 @@ function drawStage(surface, view, entries) {
|
|
|
13976
13995
|
}
|
|
13977
13996
|
function editorStageView(frame) {
|
|
13978
13997
|
return {
|
|
13979
|
-
columnHeader: "hidden",
|
|
13980
13998
|
frame,
|
|
13981
13999
|
framePart: "panzoom",
|
|
13982
14000
|
frameVars: "",
|
|
@@ -14003,7 +14021,7 @@ function disposeSourceCollab(surface) {
|
|
|
14003
14021
|
surface.sourceCollabCleanup = null;
|
|
14004
14022
|
}
|
|
14005
14023
|
async function createSourceCollabBinding(model, editor, ctx) {
|
|
14006
|
-
const { bindMonacoToYText } = await (__preload("
|
|
14024
|
+
const { bindMonacoToYText } = await (__preload("e8d9wzyk"), import("./chunks/monaco-binding-e8d9wzyk.js"));
|
|
14007
14025
|
const unbind = bindMonacoToYText({
|
|
14008
14026
|
awareness: ctx.awareness,
|
|
14009
14027
|
editors: [editor],
|
|
@@ -14046,9 +14064,6 @@ function sideMarks(marks, modified) {
|
|
|
14046
14064
|
path: mark.path
|
|
14047
14065
|
})) ?? null;
|
|
14048
14066
|
}
|
|
14049
|
-
function diffShowsCode(paneId, filePath) {
|
|
14050
|
-
return !canRenderComparison(filePath) || diffViewOf(paneId) === "code";
|
|
14051
|
-
}
|
|
14052
14067
|
async function mountDiffEditor(surface, container, state) {
|
|
14053
14068
|
const { paneId } = surface;
|
|
14054
14069
|
const key = state.filePath;
|
|
@@ -14056,7 +14071,7 @@ async function mountDiffEditor(surface, container, state) {
|
|
|
14056
14071
|
disposeDiffEditor(surface);
|
|
14057
14072
|
}
|
|
14058
14073
|
const monaco = await loadMonaco();
|
|
14059
|
-
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)))) {
|
|
14060
14075
|
return;
|
|
14061
14076
|
}
|
|
14062
14077
|
const uris = diffModelUrisFor(paneId, key || "document.json");
|
|
@@ -14093,6 +14108,7 @@ function resetCanvasView(surface) {
|
|
|
14093
14108
|
surface.centerObserver.disconnect();
|
|
14094
14109
|
surface.centerObserver = null;
|
|
14095
14110
|
}
|
|
14111
|
+
releaseStageScrollbar(surface);
|
|
14096
14112
|
for (const p of surface.panels) {
|
|
14097
14113
|
p.renderScope?.stop();
|
|
14098
14114
|
p.renderScope = null;
|
|
@@ -14103,6 +14119,7 @@ function resetCanvasView(surface) {
|
|
|
14103
14119
|
canvasWrap.style.padding = "";
|
|
14104
14120
|
canvasWrap.style.alignItems = "";
|
|
14105
14121
|
canvasWrap.style.flexDirection = "";
|
|
14122
|
+
canvasWrap.style.gap = "";
|
|
14106
14123
|
canvasWrap.style.display = "";
|
|
14107
14124
|
canvasWrap.style.overflow = "";
|
|
14108
14125
|
dismissBlockActionBar();
|
|
@@ -14274,7 +14291,7 @@ function renderCanvasImpl(surface) {
|
|
|
14274
14291
|
ui: tab.session.ui
|
|
14275
14292
|
};
|
|
14276
14293
|
const canvasMode = canvasModeOfPane(surface.paneId);
|
|
14277
|
-
const wantsDocHeader =
|
|
14294
|
+
const wantsDocHeader = canvasMode === "edit" && derivationOfPane(surface.paneId)?.kind !== "lens" && hasDocumentHeader(tab);
|
|
14278
14295
|
if (!wantsDocHeader) {
|
|
14279
14296
|
attachDocumentHeaderHost(surface.paneId, null);
|
|
14280
14297
|
}
|
|
@@ -14347,6 +14364,7 @@ function renderCanvasImpl(surface) {
|
|
|
14347
14364
|
surface.centerObserver.disconnect();
|
|
14348
14365
|
surface.centerObserver = null;
|
|
14349
14366
|
}
|
|
14367
|
+
releaseStageScrollbar(surface);
|
|
14350
14368
|
detachGridPanel(surface.paneId);
|
|
14351
14369
|
detachLibraryPane(surface.paneId);
|
|
14352
14370
|
detachEntryPane(surface.paneId);
|
|
@@ -14361,9 +14379,9 @@ function renderCanvasImpl(surface) {
|
|
|
14361
14379
|
canvasWrap.style.padding = "";
|
|
14362
14380
|
canvasWrap.style.alignItems = "";
|
|
14363
14381
|
canvasWrap.style.flexDirection = "";
|
|
14382
|
+
canvasWrap.style.gap = "";
|
|
14364
14383
|
canvasWrap.style.display = "";
|
|
14365
14384
|
canvasWrap.style.overflow = "";
|
|
14366
|
-
canvasWrap.style.overflow = "";
|
|
14367
14385
|
dismissBlockActionBar();
|
|
14368
14386
|
dismissLinkPopover();
|
|
14369
14387
|
dismissContextMenu();
|
|
@@ -14399,7 +14417,6 @@ function renderCanvasImpl(surface) {
|
|
|
14399
14417
|
canvasPanels.push(entry.panel);
|
|
14400
14418
|
const { featureToggles: previewToggles } = S.ui;
|
|
14401
14419
|
drawStage(surface, {
|
|
14402
|
-
columnHeader: "hidden",
|
|
14403
14420
|
frame: "boards",
|
|
14404
14421
|
framePart: "preview-stage",
|
|
14405
14422
|
frameVars: `--preview-w:${previewWidth}px`,
|
|
@@ -14480,7 +14497,7 @@ function renderCanvasImpl(surface) {
|
|
|
14480
14497
|
canvasWrap.style.overflow = "hidden";
|
|
14481
14498
|
}
|
|
14482
14499
|
const renderable = canRenderComparison(gitDiffState.filePath ?? "");
|
|
14483
|
-
if (diffShowsCode(surface.paneId,
|
|
14500
|
+
if (diffShowsCode(surface.paneId, renderable)) {
|
|
14484
14501
|
if (!renderable) {
|
|
14485
14502
|
setDiffChangeMap(surface.paneId, null);
|
|
14486
14503
|
}
|
|
@@ -14502,7 +14519,6 @@ function renderCanvasImpl(surface) {
|
|
|
14502
14519
|
const origEntry = canvasPanelEntry("git-diff-original", "Original", false, panelWidth);
|
|
14503
14520
|
const currEntry = canvasPanelEntry("git-diff-current", "Current", false, panelWidth);
|
|
14504
14521
|
const diffStage = drawStage(surface, {
|
|
14505
|
-
columnHeader: "hidden",
|
|
14506
14522
|
frame: "boards",
|
|
14507
14523
|
framePart: "panzoom",
|
|
14508
14524
|
frameVars: "",
|
|
@@ -14572,22 +14588,26 @@ function renderCanvasImpl(surface) {
|
|
|
14572
14588
|
const rootTag = S.document.tagName;
|
|
14573
14589
|
const isComponentDoc = typeof rootTag === "string" && rootTag.includes("-");
|
|
14574
14590
|
const { featureToggles: editToggles } = S.ui;
|
|
14591
|
+
canvasWrap.style.flexDirection = wantsDocHeader ? "column" : "";
|
|
14592
|
+
canvasWrap.style.alignItems = wantsDocHeader ? "stretch" : "";
|
|
14593
|
+
canvasWrap.style.gap = wantsDocHeader ? "0px" : "";
|
|
14575
14594
|
drawStage(surface, {
|
|
14576
|
-
columnHeader: wantsDocHeader ? "shown" : "hidden",
|
|
14577
14595
|
frame: "boards",
|
|
14578
14596
|
framePart: "edit-canvas",
|
|
14579
14597
|
frameVars: "",
|
|
14580
14598
|
handles: "shown",
|
|
14581
14599
|
hug: isComponentDoc,
|
|
14582
14600
|
innerPart: "edit-column",
|
|
14583
|
-
lead: "none",
|
|
14601
|
+
lead: wantsDocHeader ? "header" : "none",
|
|
14584
14602
|
panels: [entry.item]
|
|
14585
14603
|
}, [entry]).then((stage) => {
|
|
14586
14604
|
if (!stage) {
|
|
14587
14605
|
return;
|
|
14588
14606
|
}
|
|
14589
14607
|
const editColumn = stageHost(surface, "edit-column");
|
|
14590
|
-
|
|
14608
|
+
const scroller = stageHost(surface, "edit-canvas");
|
|
14609
|
+
entry.panel.scrollContainer = scroller;
|
|
14610
|
+
trackStageScrollbar(surface, scroller);
|
|
14591
14611
|
if (editColumn) {
|
|
14592
14612
|
editColumn.style.maxWidth = `${Math.round(columnWidth)}px`;
|
|
14593
14613
|
mountEditWidthHandle(surface, stageHost(surface, "edit-handle:start") ?? undefined, editColumn, -1);
|
|
@@ -14603,9 +14623,6 @@ function renderCanvasImpl(surface) {
|
|
|
14603
14623
|
canvasWrap.style.padding = "0";
|
|
14604
14624
|
canvasWrap.style.overflow = "hidden";
|
|
14605
14625
|
}
|
|
14606
|
-
canvasWrap.style.flexDirection = wantsDocHeader ? "column" : "";
|
|
14607
|
-
canvasWrap.style.alignItems = wantsDocHeader ? "stretch" : "";
|
|
14608
|
-
const designLead = wantsDocHeader ? "header" : "none";
|
|
14609
14626
|
const {
|
|
14610
14627
|
sizeBreakpoints,
|
|
14611
14628
|
featureQueries: _featureQueries,
|
|
@@ -14619,14 +14636,13 @@ function renderCanvasImpl(surface) {
|
|
|
14619
14636
|
const label = hasBaseWidth ? `${mediaDisplayName("--")} (${baseWidth}px)` : null;
|
|
14620
14637
|
const entry = canvasPanelEntry(hasBaseWidth ? "base" : null, label, !hasBaseWidth, hasBaseWidth ? baseWidth : undefined);
|
|
14621
14638
|
drawStage(surface, {
|
|
14622
|
-
columnHeader: "hidden",
|
|
14623
14639
|
frame: "boards",
|
|
14624
14640
|
framePart: "panzoom",
|
|
14625
14641
|
frameVars: "",
|
|
14626
14642
|
handles: "hidden",
|
|
14627
14643
|
hug: false,
|
|
14628
14644
|
innerPart: "boards",
|
|
14629
|
-
lead:
|
|
14645
|
+
lead: "none",
|
|
14630
14646
|
panels: [entry.item]
|
|
14631
14647
|
}, [entry]).then((stage) => {
|
|
14632
14648
|
if (!stage) {
|
|
@@ -14662,14 +14678,13 @@ function renderCanvasImpl(surface) {
|
|
|
14662
14678
|
const panelDefs = chosen.length > 0 ? chosen : allPanelDefs;
|
|
14663
14679
|
const panelEntries = panelDefs.map((def) => canvasPanelEntry(def.name, `${def.displayName} (${def.width}px)`, false, def.width));
|
|
14664
14680
|
const stageDrawn = drawStage(surface, {
|
|
14665
|
-
columnHeader: "hidden",
|
|
14666
14681
|
frame: "boards",
|
|
14667
14682
|
framePart: "panzoom",
|
|
14668
14683
|
frameVars: "",
|
|
14669
14684
|
handles: "hidden",
|
|
14670
14685
|
hug: false,
|
|
14671
14686
|
innerPart: "boards",
|
|
14672
|
-
lead:
|
|
14687
|
+
lead: "none",
|
|
14673
14688
|
panels: panelEntries.map((e) => e.item)
|
|
14674
14689
|
}, panelEntries);
|
|
14675
14690
|
const passGen = surface.renderGeneration;
|
|
@@ -14733,6 +14748,7 @@ setSurfaceTeardown((surface) => {
|
|
|
14733
14748
|
disposeSourceEditor(surface);
|
|
14734
14749
|
surface.centerObserver?.disconnect();
|
|
14735
14750
|
surface.centerObserver = null;
|
|
14751
|
+
releaseStageScrollbar(surface);
|
|
14736
14752
|
surface.panzoomWrap = null;
|
|
14737
14753
|
detachCanvasStage(surface);
|
|
14738
14754
|
});
|
|
@@ -17520,7 +17536,7 @@ async function onAddPackage() {
|
|
|
17520
17536
|
sync2();
|
|
17521
17537
|
try {
|
|
17522
17538
|
await getPlatform2().addPackage(name);
|
|
17523
|
-
const { loadComponentRegistry } = await (__preload("
|
|
17539
|
+
const { loadComponentRegistry } = await (__preload("xm826zjw"), import("./chunks/components-xm826zjw.js"));
|
|
17524
17540
|
await loadComponentRegistry();
|
|
17525
17541
|
context()?.renderLeftPanel();
|
|
17526
17542
|
} catch (error) {
|
|
@@ -17538,7 +17554,7 @@ async function onRemovePackage(pkg) {
|
|
|
17538
17554
|
try {
|
|
17539
17555
|
await getPlatform2().removePackage(pkg);
|
|
17540
17556
|
const updated = removePackageElements(siteElements(), pkg);
|
|
17541
|
-
const { loadComponentRegistry } = await (__preload("
|
|
17557
|
+
const { loadComponentRegistry } = await (__preload("xm826zjw"), import("./chunks/components-xm826zjw.js"));
|
|
17542
17558
|
await loadComponentRegistry();
|
|
17543
17559
|
await updateSiteConfig({ $elements: updated });
|
|
17544
17560
|
context()?.renderLeftPanel();
|
|
@@ -17778,7 +17794,7 @@ var panel_problems_default = {
|
|
|
17778
17794
|
overflowWrap: "anywhere"
|
|
17779
17795
|
},
|
|
17780
17796
|
'& [part="file-path"]': {
|
|
17781
|
-
$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.',
|
|
17782
17798
|
alignSelf: "flex-start",
|
|
17783
17799
|
maxWidth: "100%",
|
|
17784
17800
|
overflow: "hidden",
|
|
@@ -17804,7 +17820,8 @@ var panel_problems_default = {
|
|
|
17804
17820
|
}
|
|
17805
17821
|
},
|
|
17806
17822
|
'& [part="detail"]': {
|
|
17807
|
-
$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",
|
|
17808
17825
|
maxHeight: "180px",
|
|
17809
17826
|
margin: "var(--jx-space-1) 0 0",
|
|
17810
17827
|
padding: "6px",
|
|
@@ -17836,7 +17853,7 @@ var panel_problems_default = {
|
|
|
17836
17853
|
flex: "none"
|
|
17837
17854
|
},
|
|
17838
17855
|
'& [part="empty"]': {
|
|
17839
|
-
$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.",
|
|
17840
17857
|
display: "flex",
|
|
17841
17858
|
flexDirection: "column",
|
|
17842
17859
|
alignItems: "center",
|
|
@@ -18223,7 +18240,7 @@ function groupProblems(records = problems2) {
|
|
|
18223
18240
|
return groups;
|
|
18224
18241
|
}
|
|
18225
18242
|
function openProblemPath(path) {
|
|
18226
|
-
(__preload("
|
|
18243
|
+
(__preload("egsb0ry8"), import("./chunks/files-egsb0ry8.js")).then((m) => m.openFileInTab(path));
|
|
18227
18244
|
}
|
|
18228
18245
|
function projectRow2(record) {
|
|
18229
18246
|
const id = record.action;
|
|
@@ -18327,7 +18344,7 @@ function registerProblemsPanel() {
|
|
|
18327
18344
|
var bottom_dock_default = {
|
|
18328
18345
|
$schema: "https://jxsuite.com/schema/v1",
|
|
18329
18346
|
$id: "StudioBottomDock",
|
|
18330
|
-
$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).",
|
|
18331
18348
|
tagName: "div",
|
|
18332
18349
|
attributes: {
|
|
18333
18350
|
part: "bottom-dock"
|
|
@@ -19182,6 +19199,28 @@ var SELF_SCROLLING_MODES = new Set([
|
|
|
19182
19199
|
"preview",
|
|
19183
19200
|
"settings"
|
|
19184
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
|
+
}
|
|
19185
19224
|
var _stageContext = null;
|
|
19186
19225
|
function installStageGestures(surface) {
|
|
19187
19226
|
const canvasWrap = surface.wrap;
|
|
@@ -19199,7 +19238,7 @@ function installStageGestures(surface) {
|
|
|
19199
19238
|
return;
|
|
19200
19239
|
}
|
|
19201
19240
|
const sc = canvasWrap.querySelector('[part="edit-canvas"]');
|
|
19202
|
-
if (sc) {
|
|
19241
|
+
if (sc && !wheelBelongsInside(e, canvasWrap, sc)) {
|
|
19203
19242
|
e.preventDefault();
|
|
19204
19243
|
sc.scrollTop += e.deltaY;
|
|
19205
19244
|
sc.scrollLeft += e.deltaX;
|
|
@@ -19285,7 +19324,7 @@ function stageless() {
|
|
|
19285
19324
|
var pane_context_default = {
|
|
19286
19325
|
$schema: "https://jxsuite.com/schema/v1",
|
|
19287
19326
|
$id: "StudioPaneContext",
|
|
19288
|
-
$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.",
|
|
19289
19328
|
tagName: "div",
|
|
19290
19329
|
attributes: {
|
|
19291
19330
|
part: "chrome"
|
|
@@ -19343,10 +19382,14 @@ var pane_context_default = {
|
|
|
19343
19382
|
flexShrink: "0"
|
|
19344
19383
|
},
|
|
19345
19384
|
'& [part="preview-toggle"]': {
|
|
19346
|
-
$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.",
|
|
19347
19386
|
flexShrink: "0",
|
|
19348
19387
|
marginInlineStart: "6px"
|
|
19349
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
|
+
},
|
|
19350
19393
|
'& [part="context-trigger"]': {
|
|
19351
19394
|
fontVariantNumeric: "tabular-nums"
|
|
19352
19395
|
},
|
|
@@ -19372,29 +19415,47 @@ var pane_context_default = {
|
|
|
19372
19415
|
fontSize: "var(--jx-text-md)"
|
|
19373
19416
|
},
|
|
19374
19417
|
'& [part="pod"]': {
|
|
19375
|
-
$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%)",
|
|
19376
19420
|
alignSelf: "flex-end",
|
|
19421
|
+
display: "flex",
|
|
19422
|
+
alignItems: "center",
|
|
19423
|
+
gap: "var(--jx-space-2)",
|
|
19377
19424
|
pointerEvents: "auto",
|
|
19378
|
-
margin: "0 var(--jx-space-4) var(--jx-space-4) 0"
|
|
19379
|
-
|
|
19380
|
-
|
|
19381
|
-
|
|
19382
|
-
|
|
19383
|
-
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)"
|
|
19384
19435
|
},
|
|
19385
19436
|
'& [part="zoom-label"]': {
|
|
19386
|
-
|
|
19387
|
-
|
|
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"
|
|
19388
19445
|
},
|
|
19389
19446
|
'& [part="fit"]': {
|
|
19390
19447
|
maxWidth: "120px"
|
|
19391
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
|
+
},
|
|
19392
19453
|
'& [part="popover"]': {
|
|
19393
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.",
|
|
19394
19455
|
pointerEvents: "auto"
|
|
19395
19456
|
},
|
|
19396
19457
|
'& [part="ctx"]': {
|
|
19397
|
-
$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.",
|
|
19398
19459
|
display: "flex",
|
|
19399
19460
|
flexDirection: "column",
|
|
19400
19461
|
gap: "10px",
|
|
@@ -19413,11 +19474,15 @@ var pane_context_default = {
|
|
|
19413
19474
|
letterSpacing: "0.04em",
|
|
19414
19475
|
color: "var(--fg-dim)"
|
|
19415
19476
|
},
|
|
19416
|
-
'& [part="sizes"]': {
|
|
19417
|
-
|
|
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)"
|
|
19418
19481
|
},
|
|
19419
|
-
'& [part="features"]': {
|
|
19420
|
-
|
|
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)"
|
|
19421
19486
|
},
|
|
19422
19487
|
'& [part="fields"]': {
|
|
19423
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.",
|
|
@@ -19745,6 +19810,7 @@ var pane_context_default = {
|
|
|
19745
19810
|
},
|
|
19746
19811
|
$props: {
|
|
19747
19812
|
size: "sm",
|
|
19813
|
+
quiet: false,
|
|
19748
19814
|
toggles: true,
|
|
19749
19815
|
emphasized: true,
|
|
19750
19816
|
selected: {
|
|
@@ -20083,55 +20149,55 @@ var pane_context_default = {
|
|
|
20083
20149
|
}
|
|
20084
20150
|
]
|
|
20085
20151
|
}
|
|
20086
|
-
}
|
|
20087
|
-
|
|
20088
|
-
|
|
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",
|
|
20089
20170
|
attributes: {
|
|
20090
|
-
part: "fit
|
|
20091
|
-
role: "none"
|
|
20092
|
-
},
|
|
20093
|
-
style: {
|
|
20094
|
-
display: "contents"
|
|
20171
|
+
part: "fit"
|
|
20095
20172
|
},
|
|
20096
|
-
$
|
|
20097
|
-
|
|
20173
|
+
$props: {
|
|
20174
|
+
label: "Fit",
|
|
20175
|
+
size: "sm",
|
|
20176
|
+
options: {
|
|
20177
|
+
$ref: "#/state/fitOptions"
|
|
20178
|
+
},
|
|
20179
|
+
value: {
|
|
20180
|
+
$ref: "#/state/fitValue"
|
|
20181
|
+
}
|
|
20098
20182
|
},
|
|
20099
|
-
|
|
20100
|
-
|
|
20101
|
-
|
|
20102
|
-
|
|
20103
|
-
|
|
20104
|
-
|
|
20105
|
-
|
|
20106
|
-
label: "Fit",
|
|
20107
|
-
size: "sm",
|
|
20108
|
-
options: {
|
|
20109
|
-
$ref: "#/state/fitOptions"
|
|
20183
|
+
onchange: {
|
|
20184
|
+
$prototype: "Function",
|
|
20185
|
+
body: [
|
|
20186
|
+
{
|
|
20187
|
+
operator: "call",
|
|
20188
|
+
target: {
|
|
20189
|
+
$ref: "#/state/chooseFit"
|
|
20110
20190
|
},
|
|
20111
|
-
value:
|
|
20112
|
-
$ref: "#/state/fitValue"
|
|
20113
|
-
}
|
|
20114
|
-
},
|
|
20115
|
-
onchange: {
|
|
20116
|
-
$prototype: "Function",
|
|
20117
|
-
body: [
|
|
20191
|
+
value: [
|
|
20118
20192
|
{
|
|
20119
|
-
|
|
20120
|
-
target: {
|
|
20121
|
-
$ref: "#/state/chooseFit"
|
|
20122
|
-
},
|
|
20123
|
-
value: [
|
|
20124
|
-
{
|
|
20125
|
-
$ref: "event#/target/value"
|
|
20126
|
-
}
|
|
20127
|
-
]
|
|
20193
|
+
$ref: "event#/target/value"
|
|
20128
20194
|
}
|
|
20129
20195
|
]
|
|
20130
20196
|
}
|
|
20131
|
-
|
|
20197
|
+
]
|
|
20132
20198
|
}
|
|
20133
20199
|
}
|
|
20134
|
-
|
|
20200
|
+
}
|
|
20135
20201
|
}
|
|
20136
20202
|
]
|
|
20137
20203
|
}
|
|
@@ -20182,7 +20248,7 @@ var pane_context_default = {
|
|
|
20182
20248
|
},
|
|
20183
20249
|
$props: {
|
|
20184
20250
|
selects: "single",
|
|
20185
|
-
compact:
|
|
20251
|
+
compact: false,
|
|
20186
20252
|
label: "Size"
|
|
20187
20253
|
},
|
|
20188
20254
|
children: [
|
|
@@ -20202,6 +20268,7 @@ var pane_context_default = {
|
|
|
20202
20268
|
},
|
|
20203
20269
|
$props: {
|
|
20204
20270
|
size: "sm",
|
|
20271
|
+
quiet: false,
|
|
20205
20272
|
checked: {
|
|
20206
20273
|
$ref: "$map/item/checked"
|
|
20207
20274
|
},
|
|
@@ -20454,7 +20521,7 @@ var pane_context_default = {
|
|
|
20454
20521
|
},
|
|
20455
20522
|
$props: {
|
|
20456
20523
|
selects: "multiple",
|
|
20457
|
-
compact:
|
|
20524
|
+
compact: false,
|
|
20458
20525
|
label: "Features"
|
|
20459
20526
|
},
|
|
20460
20527
|
children: [
|
|
@@ -20474,6 +20541,7 @@ var pane_context_default = {
|
|
|
20474
20541
|
},
|
|
20475
20542
|
$props: {
|
|
20476
20543
|
size: "sm",
|
|
20544
|
+
quiet: false,
|
|
20477
20545
|
toggles: true,
|
|
20478
20546
|
selected: {
|
|
20479
20547
|
$ref: "$map/item/selected"
|
|
@@ -21406,6 +21474,11 @@ function fitChoiceValue(fit) {
|
|
|
21406
21474
|
return fit;
|
|
21407
21475
|
}
|
|
21408
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
|
+
}
|
|
21409
21482
|
function podFor(tab, paneId) {
|
|
21410
21483
|
const mode = canvasModeOfPane(paneId);
|
|
21411
21484
|
const surface = surfaceForPane(paneId);
|
|
@@ -21416,6 +21489,9 @@ function podFor(tab, paneId) {
|
|
|
21416
21489
|
if (!STAGE_ZOOM_MODES.has(mode)) {
|
|
21417
21490
|
return { podState: "hidden" };
|
|
21418
21491
|
}
|
|
21492
|
+
if (mode === "git-diff" && diffShowsCode(paneId, canRenderComparison(comparisonPathOf(paneId)))) {
|
|
21493
|
+
return { podState: "hidden" };
|
|
21494
|
+
}
|
|
21419
21495
|
const zoom = stageZoom(surface);
|
|
21420
21496
|
return {
|
|
21421
21497
|
fitOptions: FIT_CHOICES.map(({ label, value }) => ({ label, value })),
|
|
@@ -22012,130 +22088,15 @@ async function applyFileDrop(tab, files, hit, preview) {
|
|
|
22012
22088
|
}
|
|
22013
22089
|
}
|
|
22014
22090
|
}
|
|
22015
|
-
|
|
22016
|
-
// ../ai/src/tools.ts
|
|
22017
|
-
function toolSuccess(data, summary) {
|
|
22018
|
-
return { success: true, data, summary };
|
|
22019
|
-
}
|
|
22020
|
-
function toolError(error) {
|
|
22021
|
-
return { success: false, error };
|
|
22022
|
-
}
|
|
22023
|
-
function createToolDefinition({
|
|
22024
|
-
name,
|
|
22025
|
-
description,
|
|
22026
|
-
parameters,
|
|
22027
|
-
strict = true,
|
|
22028
|
-
llmStrict = false,
|
|
22029
|
-
execute
|
|
22030
|
-
}) {
|
|
22031
|
-
return { name, description, parameters, strict, llmStrict, execute };
|
|
22032
|
-
}
|
|
22033
|
-
function createToolRegistry() {
|
|
22034
|
-
const _tools = new Map;
|
|
22035
|
-
return {
|
|
22036
|
-
register(tool) {
|
|
22037
|
-
if (_tools.has(tool.name)) {
|
|
22038
|
-
console.warn(`Tool "${tool.name}" is being re-registered.`);
|
|
22039
|
-
}
|
|
22040
|
-
_tools.set(tool.name, tool);
|
|
22041
|
-
},
|
|
22042
|
-
list() {
|
|
22043
|
-
return [..._tools.values()];
|
|
22044
|
-
},
|
|
22045
|
-
listForLLM() {
|
|
22046
|
-
return [..._tools.values()].map((t) => ({
|
|
22047
|
-
type: "function",
|
|
22048
|
-
function: {
|
|
22049
|
-
name: t.name,
|
|
22050
|
-
description: t.description,
|
|
22051
|
-
parameters: t.parameters,
|
|
22052
|
-
...t.llmStrict === true ? { strict: true } : {}
|
|
22053
|
-
}
|
|
22054
|
-
}));
|
|
22055
|
-
},
|
|
22056
|
-
getDefinition(toolName) {
|
|
22057
|
-
return _tools.get(toolName);
|
|
22058
|
-
},
|
|
22059
|
-
validate(toolName, args) {
|
|
22060
|
-
const tool = _tools.get(toolName);
|
|
22061
|
-
if (!tool) {
|
|
22062
|
-
return { valid: false, errors: [`Unknown tool: "${toolName}"`] };
|
|
22063
|
-
}
|
|
22064
|
-
if (!tool.strict) {
|
|
22065
|
-
return { valid: true };
|
|
22066
|
-
}
|
|
22067
|
-
const schema = tool.parameters;
|
|
22068
|
-
if (!schema || !schema.properties) {
|
|
22069
|
-
return { valid: true };
|
|
22070
|
-
}
|
|
22071
|
-
const errors = [];
|
|
22072
|
-
const props = schema.properties;
|
|
22073
|
-
const required = schema.required || [];
|
|
22074
|
-
const argRecord = args;
|
|
22075
|
-
for (const key of required) {
|
|
22076
|
-
if (!(key in args) || argRecord[key] === undefined || argRecord[key] === null) {
|
|
22077
|
-
errors.push(`Missing required argument: "${key}"`);
|
|
22078
|
-
}
|
|
22079
|
-
}
|
|
22080
|
-
for (const [key, value] of Object.entries(argRecord)) {
|
|
22081
|
-
const propSchema = props[key];
|
|
22082
|
-
if (!propSchema) {
|
|
22083
|
-
continue;
|
|
22084
|
-
}
|
|
22085
|
-
const expectedType = propSchema.type;
|
|
22086
|
-
if (!expectedType) {
|
|
22087
|
-
continue;
|
|
22088
|
-
}
|
|
22089
|
-
const actualType = Array.isArray(value) ? "array" : typeof value;
|
|
22090
|
-
if (expectedType === "number" || expectedType === "integer") {
|
|
22091
|
-
if (actualType === "number") {
|
|
22092
|
-
continue;
|
|
22093
|
-
}
|
|
22094
|
-
if (actualType === "string") {
|
|
22095
|
-
if (!Number.isNaN(Number(value))) {
|
|
22096
|
-
continue;
|
|
22097
|
-
}
|
|
22098
|
-
errors.push(`Argument "${key}" should be a number, got non-numeric string "${value}"`);
|
|
22099
|
-
continue;
|
|
22100
|
-
}
|
|
22101
|
-
errors.push(`Argument "${key}" should be a number, got ${actualType}`);
|
|
22102
|
-
continue;
|
|
22103
|
-
}
|
|
22104
|
-
if (expectedType === "string" && actualType !== "string") {
|
|
22105
|
-
errors.push(`Argument "${key}" should be a string, got ${actualType}`);
|
|
22106
|
-
} else if (expectedType === "boolean" && actualType !== "boolean") {
|
|
22107
|
-
errors.push(`Argument "${key}" should be a boolean, got ${actualType}`);
|
|
22108
|
-
} else if (expectedType === "array" && actualType !== "array") {
|
|
22109
|
-
errors.push(`Argument "${key}" should be an array, got ${actualType}`);
|
|
22110
|
-
} else if (expectedType === "object" && actualType !== "object") {
|
|
22111
|
-
errors.push(`Argument "${key}" should be an object, got ${actualType}`);
|
|
22112
|
-
}
|
|
22113
|
-
}
|
|
22114
|
-
return errors.length > 0 ? { valid: false, errors } : { valid: true };
|
|
22115
|
-
},
|
|
22116
|
-
async execute(toolName, args) {
|
|
22117
|
-
const tool = _tools.get(toolName);
|
|
22118
|
-
if (!tool) {
|
|
22119
|
-
return toolError(`Unknown tool: "${toolName}"`);
|
|
22120
|
-
}
|
|
22121
|
-
const validation = this.validate(toolName, args);
|
|
22122
|
-
if (!validation.valid) {
|
|
22123
|
-
return toolError(`Validation failed: ${(validation.errors || []).join("; ")}`);
|
|
22124
|
-
}
|
|
22125
|
-
try {
|
|
22126
|
-
return await tool.execute(args);
|
|
22127
|
-
} catch (error) {
|
|
22128
|
-
return toolError(`Tool "${toolName}" execution error: ${error.message}`);
|
|
22129
|
-
}
|
|
22130
|
-
}
|
|
22131
|
-
};
|
|
22132
|
-
}
|
|
22133
22091
|
// ../ai/src/chat-state.ts
|
|
22134
22092
|
var _idCounter = 0;
|
|
22135
22093
|
function uid() {
|
|
22136
22094
|
_idCounter += 1;
|
|
22137
22095
|
return `msg_${Date.now()}_${_idCounter}`;
|
|
22138
22096
|
}
|
|
22097
|
+
function isCount(value) {
|
|
22098
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
22099
|
+
}
|
|
22139
22100
|
function createChatState(opts = {}) {
|
|
22140
22101
|
const model = opts.model || "gpt-4o";
|
|
22141
22102
|
const store = reactive({
|
|
@@ -22146,7 +22107,8 @@ function createChatState(opts = {}) {
|
|
|
22146
22107
|
error: null,
|
|
22147
22108
|
model,
|
|
22148
22109
|
tokenCount: 0,
|
|
22149
|
-
contextWarning: false
|
|
22110
|
+
contextWarning: false,
|
|
22111
|
+
usage: null
|
|
22150
22112
|
});
|
|
22151
22113
|
let _streamingMessage = null;
|
|
22152
22114
|
function sendMessage(text) {
|
|
@@ -22227,15 +22189,14 @@ function createChatState(opts = {}) {
|
|
|
22227
22189
|
}
|
|
22228
22190
|
function appendToolCallEnd(_id) {}
|
|
22229
22191
|
function appendToolResult(id, result) {
|
|
22230
|
-
|
|
22231
|
-
|
|
22232
|
-
|
|
22192
|
+
let index = store.messages.length - 1;
|
|
22193
|
+
while (index >= 0 && store.messages[index].role === "tool") {
|
|
22194
|
+
index -= 1;
|
|
22233
22195
|
}
|
|
22234
|
-
|
|
22235
|
-
|
|
22236
|
-
|
|
22237
|
-
|
|
22238
|
-
}
|
|
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;
|
|
22239
22200
|
}
|
|
22240
22201
|
}
|
|
22241
22202
|
function finishStream(_stopReason) {
|
|
@@ -22248,6 +22209,7 @@ function createChatState(opts = {}) {
|
|
|
22248
22209
|
store.status = "error";
|
|
22249
22210
|
store.error = message;
|
|
22250
22211
|
store.streamingContent = "";
|
|
22212
|
+
store.pendingToolCalls = [];
|
|
22251
22213
|
if (_streamingMessage) {
|
|
22252
22214
|
const idx = store.messages.lastIndexOf(_streamingMessage);
|
|
22253
22215
|
if (idx !== -1) {
|
|
@@ -22276,6 +22238,7 @@ function createChatState(opts = {}) {
|
|
|
22276
22238
|
store.pendingToolCalls = [];
|
|
22277
22239
|
store.error = null;
|
|
22278
22240
|
store.contextWarning = false;
|
|
22241
|
+
store.usage = null;
|
|
22279
22242
|
_streamingMessage = null;
|
|
22280
22243
|
}
|
|
22281
22244
|
function retryLast() {
|
|
@@ -22294,6 +22257,27 @@ function createChatState(opts = {}) {
|
|
|
22294
22257
|
function setTokenCount(count) {
|
|
22295
22258
|
store.tokenCount = count;
|
|
22296
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
|
+
}
|
|
22297
22281
|
function setContextWarning(warning) {
|
|
22298
22282
|
store.contextWarning = warning;
|
|
22299
22283
|
}
|
|
@@ -22355,6 +22339,8 @@ function createChatState(opts = {}) {
|
|
|
22355
22339
|
retryLast,
|
|
22356
22340
|
setModel,
|
|
22357
22341
|
setTokenCount,
|
|
22342
|
+
recordUsage,
|
|
22343
|
+
clearUsage,
|
|
22358
22344
|
setContextWarning,
|
|
22359
22345
|
toMessagesArray
|
|
22360
22346
|
});
|
|
@@ -22391,6 +22377,7 @@ function createProxyStreamingClient({
|
|
|
22391
22377
|
apiKey,
|
|
22392
22378
|
baseUrl
|
|
22393
22379
|
}) {
|
|
22380
|
+
let resolvedUrl = null;
|
|
22394
22381
|
async function* streamChat(messages, tools, systemPrompt, signal) {
|
|
22395
22382
|
const headers = { "Content-Type": "application/json" };
|
|
22396
22383
|
if (apiKey) {
|
|
@@ -22399,9 +22386,24 @@ function createProxyStreamingClient({
|
|
|
22399
22386
|
if (baseUrl) {
|
|
22400
22387
|
headers["X-Api-Base-URL"] = baseUrl;
|
|
22401
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
|
+
}
|
|
22402
22404
|
let response;
|
|
22403
22405
|
try {
|
|
22404
|
-
response = await fetch(
|
|
22406
|
+
response = await fetch(url, {
|
|
22405
22407
|
method: "POST",
|
|
22406
22408
|
headers,
|
|
22407
22409
|
body: JSON.stringify({ messages, tools, systemPrompt, model }),
|
|
@@ -22487,54 +22489,28 @@ function createProxyStreamingClient({
|
|
|
22487
22489
|
}
|
|
22488
22490
|
return { streamChat };
|
|
22489
22491
|
}
|
|
22490
|
-
// src/services/
|
|
22491
|
-
|
|
22492
|
-
|
|
22493
|
-
|
|
22494
|
-
function beginTurn(id) {
|
|
22495
|
-
if (open?.id === id) {
|
|
22496
|
-
return;
|
|
22492
|
+
// src/services/project-path.ts
|
|
22493
|
+
function normalizeRelPath(path) {
|
|
22494
|
+
if (typeof path !== "string" || !path.trim()) {
|
|
22495
|
+
return null;
|
|
22497
22496
|
}
|
|
22498
|
-
|
|
22499
|
-
|
|
22500
|
-
|
|
22501
|
-
open?.writes.push(write);
|
|
22502
|
-
}
|
|
22503
|
-
function endTurn(id) {
|
|
22504
|
-
if (!open) {
|
|
22505
|
-
return [];
|
|
22497
|
+
let p = path.trim().replaceAll("\\", "/");
|
|
22498
|
+
while (p.startsWith("./")) {
|
|
22499
|
+
p = p.slice(2);
|
|
22506
22500
|
}
|
|
22507
|
-
|
|
22508
|
-
|
|
22509
|
-
if (turn.writes.length === 0) {
|
|
22510
|
-
return [];
|
|
22501
|
+
if (p.startsWith("/") || p.startsWith("~") || /^[A-Za-z]:/.test(p)) {
|
|
22502
|
+
return null;
|
|
22511
22503
|
}
|
|
22512
|
-
|
|
22513
|
-
|
|
22514
|
-
turns.shift();
|
|
22504
|
+
if (p.split("/").includes("..")) {
|
|
22505
|
+
return null;
|
|
22515
22506
|
}
|
|
22516
|
-
return
|
|
22517
|
-
}
|
|
22518
|
-
function writesForTurn(id) {
|
|
22519
|
-
return turns.find((turn) => turn.id === id)?.writes ?? [];
|
|
22507
|
+
return p;
|
|
22520
22508
|
}
|
|
22521
|
-
function
|
|
22522
|
-
|
|
22523
|
-
|
|
22524
|
-
|
|
22525
|
-
|
|
22526
|
-
return failed > 0 ? `${failed} change${failed === 1 ? "" : "s"} failed` : "";
|
|
22527
|
-
}
|
|
22528
|
-
const files = `Changed ${paths.size} file${paths.size === 1 ? "" : "s"}`;
|
|
22529
|
-
const diskPaths = new Set(applied.filter((w) => w.disk).map((w) => w.path));
|
|
22530
|
-
const parts = [files];
|
|
22531
|
-
if (diskPaths.size > 0) {
|
|
22532
|
-
parts.push(`${diskPaths.size} written to disk — undo cannot reach ${diskPaths.size === 1 ? "it" : "them"}`);
|
|
22533
|
-
}
|
|
22534
|
-
if (failed > 0) {
|
|
22535
|
-
parts.push(`${failed} failed`);
|
|
22536
|
-
}
|
|
22537
|
-
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
|
+
};
|
|
22538
22514
|
}
|
|
22539
22515
|
|
|
22540
22516
|
// src/services/token-lint.ts
|
|
@@ -22704,7 +22680,7 @@ function noDocError() {
|
|
|
22704
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."
|
|
22705
22681
|
};
|
|
22706
22682
|
}
|
|
22707
|
-
async function applyAndValidate(tab, mutationFn, summary, validate, renderCheck, projectStyle) {
|
|
22683
|
+
async function applyAndValidate(ledger, tab, mutationFn, summary, validate, renderCheck, projectStyle) {
|
|
22708
22684
|
const deps = { getProjectStyle: () => projectStyle, renderCheck, validate };
|
|
22709
22685
|
const before = await snapshotBeforeWrite(tab, deps);
|
|
22710
22686
|
if (!transactDoc(tab, mutationFn)) {
|
|
@@ -22713,7 +22689,7 @@ async function applyAndValidate(tab, mutationFn, summary, validate, renderCheck,
|
|
|
22713
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."
|
|
22714
22690
|
};
|
|
22715
22691
|
}
|
|
22716
|
-
|
|
22692
|
+
ledger.record({ disk: false, ok: true, path: tab.documentPath ?? "(untitled)", tool: summary });
|
|
22717
22693
|
return reportDocumentWrite(tab, before, summary, deps);
|
|
22718
22694
|
}
|
|
22719
22695
|
function registerAiTools(registry, {
|
|
@@ -22792,7 +22768,7 @@ function registerAiTools(registry, {
|
|
|
22792
22768
|
},
|
|
22793
22769
|
required: ["path", "key"]
|
|
22794
22770
|
},
|
|
22795
|
-
async execute(args) {
|
|
22771
|
+
async execute(args, ctx) {
|
|
22796
22772
|
const tab = getTab();
|
|
22797
22773
|
if (!tab) {
|
|
22798
22774
|
return noDocError();
|
|
@@ -22802,7 +22778,7 @@ function registerAiTools(registry, {
|
|
|
22802
22778
|
if (node === undefined) {
|
|
22803
22779
|
return { success: false, error: `No node exists at path ${JSON.stringify(path)}.` };
|
|
22804
22780
|
}
|
|
22805
|
-
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());
|
|
22806
22782
|
}
|
|
22807
22783
|
}));
|
|
22808
22784
|
registry.register(createToolDefinition({
|
|
@@ -22824,7 +22800,7 @@ function registerAiTools(registry, {
|
|
|
22824
22800
|
},
|
|
22825
22801
|
required: ["parentPath", "index", "node"]
|
|
22826
22802
|
},
|
|
22827
|
-
async execute(args) {
|
|
22803
|
+
async execute(args, ctx) {
|
|
22828
22804
|
const tab = getTab();
|
|
22829
22805
|
if (!tab) {
|
|
22830
22806
|
return noDocError();
|
|
@@ -22850,7 +22826,7 @@ function registerAiTools(registry, {
|
|
|
22850
22826
|
error: "Cannot insert into mapped-array children; edit the map template instead."
|
|
22851
22827
|
};
|
|
22852
22828
|
}
|
|
22853
|
-
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());
|
|
22854
22830
|
}
|
|
22855
22831
|
}));
|
|
22856
22832
|
registry.register(createToolDefinition({
|
|
@@ -22874,7 +22850,7 @@ function registerAiTools(registry, {
|
|
|
22874
22850
|
},
|
|
22875
22851
|
required: ["path", "property"]
|
|
22876
22852
|
},
|
|
22877
|
-
async execute(args) {
|
|
22853
|
+
async execute(args, ctx) {
|
|
22878
22854
|
const tab = getTab();
|
|
22879
22855
|
if (!tab) {
|
|
22880
22856
|
return noDocError();
|
|
@@ -22885,7 +22861,7 @@ function registerAiTools(registry, {
|
|
|
22885
22861
|
}
|
|
22886
22862
|
const prop = property;
|
|
22887
22863
|
const val = value == null ? undefined : String(value);
|
|
22888
|
-
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());
|
|
22889
22865
|
}
|
|
22890
22866
|
}));
|
|
22891
22867
|
registry.register(createToolDefinition({
|
|
@@ -22903,7 +22879,7 @@ function registerAiTools(registry, {
|
|
|
22903
22879
|
},
|
|
22904
22880
|
required: ["path", "value"]
|
|
22905
22881
|
},
|
|
22906
|
-
async execute(args) {
|
|
22882
|
+
async execute(args, ctx) {
|
|
22907
22883
|
const tab = getTab();
|
|
22908
22884
|
if (!tab) {
|
|
22909
22885
|
return noDocError();
|
|
@@ -22912,10 +22888,9 @@ function registerAiTools(registry, {
|
|
|
22912
22888
|
if (getNodeAtPath2(tab.doc.document, path) === undefined) {
|
|
22913
22889
|
return { success: false, error: `No node exists at path ${JSON.stringify(path)}.` };
|
|
22914
22890
|
}
|
|
22915
|
-
return applyAndValidate(tab, (t) => {
|
|
22916
|
-
|
|
22917
|
-
|
|
22918
|
-
node.children = [value];
|
|
22891
|
+
return applyAndValidate(ctx.ledger, tab, (t) => {
|
|
22892
|
+
mutateUpdateProperty(t, path, "textContent");
|
|
22893
|
+
mutateUpdateProperty(t, path, "children", [value]);
|
|
22919
22894
|
}, `Set text at ${JSON.stringify(path)}.`, validate, renderCheck, styleOf());
|
|
22920
22895
|
}
|
|
22921
22896
|
}));
|
|
@@ -22932,7 +22907,7 @@ function registerAiTools(registry, {
|
|
|
22932
22907
|
},
|
|
22933
22908
|
required: ["key", "value"]
|
|
22934
22909
|
},
|
|
22935
|
-
async execute(args) {
|
|
22910
|
+
async execute(args, ctx) {
|
|
22936
22911
|
const tab = getTab();
|
|
22937
22912
|
if (!tab) {
|
|
22938
22913
|
return noDocError();
|
|
@@ -22944,12 +22919,7 @@ function registerAiTools(registry, {
|
|
|
22944
22919
|
error: `State key "${key}" already exists. Use update_state to change it, or remove it first.`
|
|
22945
22920
|
};
|
|
22946
22921
|
}
|
|
22947
|
-
return applyAndValidate(tab, (t) => {
|
|
22948
|
-
if (!t.doc.document.state) {
|
|
22949
|
-
t.doc.document.state = {};
|
|
22950
|
-
}
|
|
22951
|
-
t.doc.document.state[key] = value;
|
|
22952
|
-
}, `Added state "${key}".`, validate, renderCheck, styleOf());
|
|
22922
|
+
return applyAndValidate(ctx.ledger, tab, (t) => mutateAddDef(t, key, value), `Added state "${key}".`, validate, renderCheck, styleOf());
|
|
22953
22923
|
}
|
|
22954
22924
|
}));
|
|
22955
22925
|
registry.register(createToolDefinition({
|
|
@@ -22965,7 +22935,7 @@ function registerAiTools(registry, {
|
|
|
22965
22935
|
},
|
|
22966
22936
|
required: ["key"]
|
|
22967
22937
|
},
|
|
22968
|
-
async execute(args) {
|
|
22938
|
+
async execute(args, ctx) {
|
|
22969
22939
|
const tab = getTab();
|
|
22970
22940
|
if (!tab) {
|
|
22971
22941
|
return noDocError();
|
|
@@ -22977,15 +22947,11 @@ function registerAiTools(registry, {
|
|
|
22977
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)"}`
|
|
22978
22948
|
};
|
|
22979
22949
|
}
|
|
22980
|
-
return applyAndValidate(tab, (t) => {
|
|
22981
|
-
const { state } = t.doc.document;
|
|
22982
|
-
if (!state) {
|
|
22983
|
-
return;
|
|
22984
|
-
}
|
|
22950
|
+
return applyAndValidate(ctx.ledger, tab, (t) => {
|
|
22985
22951
|
if (value == null) {
|
|
22986
|
-
|
|
22952
|
+
mutateRemoveDef(t, key);
|
|
22987
22953
|
} else {
|
|
22988
|
-
|
|
22954
|
+
mutateAddDef(t, key, value);
|
|
22989
22955
|
}
|
|
22990
22956
|
}, value == null ? `Removed state "${key}".` : `Updated state "${key}".`, validate, renderCheck, styleOf());
|
|
22991
22957
|
}
|
|
@@ -23013,7 +22979,7 @@ function registerAiTools(registry, {
|
|
|
23013
22979
|
},
|
|
23014
22980
|
required: ["fromPath", "toParentPath", "toIndex"]
|
|
23015
22981
|
},
|
|
23016
|
-
async execute(args) {
|
|
22982
|
+
async execute(args, ctx) {
|
|
23017
22983
|
const tab = getTab();
|
|
23018
22984
|
if (!tab) {
|
|
23019
22985
|
return noDocError();
|
|
@@ -23034,7 +23000,7 @@ function registerAiTools(registry, {
|
|
|
23034
23000
|
error: `No node exists at toParentPath ${JSON.stringify(toParentPath)}.`
|
|
23035
23001
|
};
|
|
23036
23002
|
}
|
|
23037
|
-
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());
|
|
23038
23004
|
}
|
|
23039
23005
|
}));
|
|
23040
23006
|
registry.register(createToolDefinition({
|
|
@@ -23054,14 +23020,18 @@ function registerAiTools(registry, {
|
|
|
23054
23020
|
},
|
|
23055
23021
|
required: ["path", "content"]
|
|
23056
23022
|
},
|
|
23057
|
-
async execute(args) {
|
|
23023
|
+
async execute(args, ctx) {
|
|
23058
23024
|
if (!saveFile) {
|
|
23059
23025
|
return {
|
|
23060
23026
|
success: false,
|
|
23061
23027
|
error: "File operations are not available in this environment."
|
|
23062
23028
|
};
|
|
23063
23029
|
}
|
|
23064
|
-
const { path
|
|
23030
|
+
const { path, content } = args;
|
|
23031
|
+
const relPath = normalizeRelPath(path);
|
|
23032
|
+
if (relPath === null) {
|
|
23033
|
+
return invalidPathResult(path);
|
|
23034
|
+
}
|
|
23065
23035
|
const dirtyError = dirtyTabError(relPath);
|
|
23066
23036
|
if (dirtyError) {
|
|
23067
23037
|
return dirtyError;
|
|
@@ -23088,14 +23058,14 @@ ${formatted}`
|
|
|
23088
23058
|
}
|
|
23089
23059
|
try {
|
|
23090
23060
|
await saveFile(relPath, serializeJson(content, null));
|
|
23091
|
-
|
|
23061
|
+
ctx.ledger.record({ disk: true, ok: true, path: relPath, tool: "create_component" });
|
|
23092
23062
|
return {
|
|
23093
23063
|
success: true,
|
|
23094
23064
|
summary: await reconcileAfterWrite(relPath, `Created component at "${relPath}".`)
|
|
23095
23065
|
};
|
|
23096
23066
|
} catch (error) {
|
|
23097
23067
|
const message = error instanceof Error ? error.message : String(error);
|
|
23098
|
-
|
|
23068
|
+
ctx.ledger.record({
|
|
23099
23069
|
disk: true,
|
|
23100
23070
|
error: message,
|
|
23101
23071
|
ok: false,
|
|
@@ -23126,14 +23096,18 @@ ${formatted}`
|
|
|
23126
23096
|
},
|
|
23127
23097
|
required: ["path", "content"]
|
|
23128
23098
|
},
|
|
23129
|
-
async execute(args) {
|
|
23099
|
+
async execute(args, ctx) {
|
|
23130
23100
|
if (!saveFile) {
|
|
23131
23101
|
return {
|
|
23132
23102
|
success: false,
|
|
23133
23103
|
error: "File operations are not available in this environment."
|
|
23134
23104
|
};
|
|
23135
23105
|
}
|
|
23136
|
-
const { path
|
|
23106
|
+
const { path, content } = args;
|
|
23107
|
+
const relPath = normalizeRelPath(path);
|
|
23108
|
+
if (relPath === null) {
|
|
23109
|
+
return invalidPathResult(path);
|
|
23110
|
+
}
|
|
23137
23111
|
const dirtyError = dirtyTabError(relPath);
|
|
23138
23112
|
if (dirtyError) {
|
|
23139
23113
|
return dirtyError;
|
|
@@ -23160,14 +23134,14 @@ ${formatted}`
|
|
|
23160
23134
|
}
|
|
23161
23135
|
try {
|
|
23162
23136
|
await saveFile(relPath, serializeJson(content, null));
|
|
23163
|
-
|
|
23137
|
+
ctx.ledger.record({ disk: true, ok: true, path: relPath, tool: "create_page" });
|
|
23164
23138
|
return {
|
|
23165
23139
|
success: true,
|
|
23166
23140
|
summary: await reconcileAfterWrite(relPath, `Created page at "${relPath}".`)
|
|
23167
23141
|
};
|
|
23168
23142
|
} catch (error) {
|
|
23169
23143
|
const message = error instanceof Error ? error.message : String(error);
|
|
23170
|
-
|
|
23144
|
+
ctx.ledger.record({
|
|
23171
23145
|
disk: true,
|
|
23172
23146
|
error: message,
|
|
23173
23147
|
ok: false,
|
|
@@ -23183,26 +23157,6 @@ ${formatted}`
|
|
|
23183
23157
|
}));
|
|
23184
23158
|
}
|
|
23185
23159
|
|
|
23186
|
-
// src/services/ai-turn-signal.ts
|
|
23187
|
-
var _signal;
|
|
23188
|
-
function beginTurnSignal(signal) {
|
|
23189
|
-
_signal = signal;
|
|
23190
|
-
}
|
|
23191
|
-
function turnSignal() {
|
|
23192
|
-
return _signal;
|
|
23193
|
-
}
|
|
23194
|
-
function endTurnSignal() {
|
|
23195
|
-
_signal = undefined;
|
|
23196
|
-
_toolCallId = "";
|
|
23197
|
-
}
|
|
23198
|
-
var _toolCallId = "";
|
|
23199
|
-
function beginToolCall(id) {
|
|
23200
|
-
_toolCallId = id;
|
|
23201
|
-
}
|
|
23202
|
-
function currentToolCallId() {
|
|
23203
|
-
return _toolCallId;
|
|
23204
|
-
}
|
|
23205
|
-
|
|
23206
23160
|
// src/services/ai-ask.ts
|
|
23207
23161
|
var MAX_OPTIONS = 6;
|
|
23208
23162
|
var state2 = reactive({ pending: null });
|
|
@@ -23220,11 +23174,10 @@ function pendingAsk() {
|
|
|
23220
23174
|
function isAwaitingAnswer() {
|
|
23221
23175
|
return state2.pending !== null;
|
|
23222
23176
|
}
|
|
23223
|
-
function askUser(ask) {
|
|
23177
|
+
function askUser(ask, signal) {
|
|
23224
23178
|
return new Promise((resolve) => {
|
|
23225
23179
|
state2.pending = ask;
|
|
23226
23180
|
settle2 = resolve;
|
|
23227
|
-
const signal = turnSignal();
|
|
23228
23181
|
if (signal?.aborted) {
|
|
23229
23182
|
clear();
|
|
23230
23183
|
resolve({ answer: null, skipped: false });
|
|
@@ -23269,9 +23222,10 @@ function resetAsk() {
|
|
|
23269
23222
|
cancelAsk();
|
|
23270
23223
|
clear();
|
|
23271
23224
|
}
|
|
23272
|
-
function registerAskTool(registry) {
|
|
23225
|
+
function registerAskTool(registry, { onPending } = {}) {
|
|
23273
23226
|
registry.register(createToolDefinition({
|
|
23274
23227
|
name: "ask_user",
|
|
23228
|
+
interactive: true,
|
|
23275
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.",
|
|
23276
23230
|
parameters: {
|
|
23277
23231
|
type: "object",
|
|
@@ -23292,7 +23246,7 @@ function registerAskTool(registry) {
|
|
|
23292
23246
|
},
|
|
23293
23247
|
required: ["question"]
|
|
23294
23248
|
},
|
|
23295
|
-
execute: async (args) => {
|
|
23249
|
+
execute: async (args, ctx) => {
|
|
23296
23250
|
const { question, options, context } = args;
|
|
23297
23251
|
if (typeof question !== "string" || !question.trim()) {
|
|
23298
23252
|
return toolError('Pass "question" — the question to put to the user.');
|
|
@@ -23301,12 +23255,16 @@ function registerAskTool(registry) {
|
|
|
23301
23255
|
return toolError("A question is already waiting for the user.");
|
|
23302
23256
|
}
|
|
23303
23257
|
const choices = Array.isArray(options) ? options.filter((o) => typeof o === "string" && o.trim().length > 0).slice(0, MAX_OPTIONS) : [];
|
|
23304
|
-
const
|
|
23258
|
+
const answering = askUser({
|
|
23305
23259
|
context: typeof context === "string" ? context.trim() : "",
|
|
23306
|
-
id:
|
|
23260
|
+
id: ctx.callId,
|
|
23307
23261
|
options: choices,
|
|
23308
23262
|
question: question.trim()
|
|
23309
|
-
});
|
|
23263
|
+
}, ctx.signal);
|
|
23264
|
+
if (isAwaitingAnswer()) {
|
|
23265
|
+
onPending?.();
|
|
23266
|
+
}
|
|
23267
|
+
const { answer, skipped } = await answering;
|
|
23310
23268
|
if (skipped) {
|
|
23311
23269
|
return toolSuccess({ answer: null, skipped: true }, "The user declined to answer; proceed with your best judgment.");
|
|
23312
23270
|
}
|
|
@@ -23373,7 +23331,7 @@ function definitionOf(registry, command, execute) {
|
|
|
23373
23331
|
parameters,
|
|
23374
23332
|
strict: false,
|
|
23375
23333
|
llmStrict: false,
|
|
23376
|
-
execute: (args) => execute(tool.name, args)
|
|
23334
|
+
execute: (args, ctx) => execute(tool.name, args, ctx)
|
|
23377
23335
|
};
|
|
23378
23336
|
}
|
|
23379
23337
|
function advertisedCommandTools(registry, execute = async (name) => toolError(`Tool "${name}" was listed without an executor.`)) {
|
|
@@ -23416,20 +23374,20 @@ function defaultPaths(command, tab) {
|
|
|
23416
23374
|
}
|
|
23417
23375
|
return ["(unknown file)"];
|
|
23418
23376
|
}
|
|
23419
|
-
function fileLedger(command, tab, wrote) {
|
|
23377
|
+
function fileLedger(ledger, command, tab, wrote) {
|
|
23420
23378
|
if (command.undo === undefined) {
|
|
23421
23379
|
return;
|
|
23422
23380
|
}
|
|
23423
23381
|
const disk = command.undo === "none";
|
|
23424
23382
|
for (const path of wrote ?? defaultPaths(command, tab)) {
|
|
23425
|
-
|
|
23383
|
+
ledger.record({ disk, ok: true, path, tool: command.title });
|
|
23426
23384
|
}
|
|
23427
23385
|
}
|
|
23428
23386
|
function createCommandToolRegistry(deps) {
|
|
23429
23387
|
function commandFor(name) {
|
|
23430
23388
|
return activeRegistry()?.list().find((command) => command.aiTool?.name === name);
|
|
23431
23389
|
}
|
|
23432
|
-
async function execute(name, received) {
|
|
23390
|
+
async function execute(name, received, ctx = createToolContext()) {
|
|
23433
23391
|
const registry = activeRegistry();
|
|
23434
23392
|
const command = commandFor(name);
|
|
23435
23393
|
if (!registry || !command) {
|
|
@@ -23471,7 +23429,7 @@ function createCommandToolRegistry(deps) {
|
|
|
23471
23429
|
try {
|
|
23472
23430
|
report = normaliseReport(tool.report({ after, args, before }));
|
|
23473
23431
|
} catch (error) {
|
|
23474
|
-
fileLedger(command, tab, projectUnchanged ? [] : undefined);
|
|
23432
|
+
fileLedger(ctx.ledger, command, tab, projectUnchanged ? [] : undefined);
|
|
23475
23433
|
const message = error instanceof Error ? error.message : String(error);
|
|
23476
23434
|
return { error: `${command.title} ran, but its report failed: ${message}`, success: false };
|
|
23477
23435
|
}
|
|
@@ -23482,7 +23440,7 @@ function createCommandToolRegistry(deps) {
|
|
|
23482
23440
|
summary = `${command.title} changed nothing: project.json is exactly as it was.`;
|
|
23483
23441
|
}
|
|
23484
23442
|
}
|
|
23485
|
-
fileLedger(command, tab, wrote);
|
|
23443
|
+
fileLedger(ctx.ledger, command, tab, wrote);
|
|
23486
23444
|
if (tab && snapshot) {
|
|
23487
23445
|
const verdict = await reportDocumentWrite(tab, snapshot, summary, deps);
|
|
23488
23446
|
if (!verdict.success) {
|
|
@@ -23549,9 +23507,9 @@ function composeToolRegistries(hand, commands) {
|
|
|
23549
23507
|
const side = sideOf(name);
|
|
23550
23508
|
return side ? side.validate(name, args) : { valid: false, errors: [`Unknown tool: "${name}"`] };
|
|
23551
23509
|
},
|
|
23552
|
-
async execute(name, args) {
|
|
23510
|
+
async execute(name, args, ctx) {
|
|
23553
23511
|
const side = sideOf(name);
|
|
23554
|
-
return side ? side.execute(name, args) : toolError(`Unknown tool: "${name}"`);
|
|
23512
|
+
return side ? side.execute(name, args, ctx) : toolError(`Unknown tool: "${name}"`);
|
|
23555
23513
|
}
|
|
23556
23514
|
};
|
|
23557
23515
|
}
|
|
@@ -23569,6 +23527,14 @@ async function adoptProject(root) {
|
|
|
23569
23527
|
}
|
|
23570
23528
|
async function adoptCreatedProject(root, { getTab, adopt = adoptProject, initRepo = initProjectRepo2 }) {
|
|
23571
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
|
+
}
|
|
23572
23538
|
const wasBatching = isBatching();
|
|
23573
23539
|
if (wasBatching) {
|
|
23574
23540
|
endBatch();
|
|
@@ -23582,7 +23548,7 @@ async function adoptCreatedProject(root, { getTab, adopt = adoptProject, initRep
|
|
|
23582
23548
|
if (wasBatching) {
|
|
23583
23549
|
beginBatch(getTab());
|
|
23584
23550
|
}
|
|
23585
|
-
return { adopted: workspace2.projectRoot === root, error: adoptionError };
|
|
23551
|
+
return { adopted: workspace2.projectRoot === root, error: adoptionError, openedElsewhere };
|
|
23586
23552
|
}
|
|
23587
23553
|
|
|
23588
23554
|
// src/services/ai-project-tools.ts
|
|
@@ -23592,28 +23558,6 @@ var SEARCH_CAP = 100;
|
|
|
23592
23558
|
var READ_CAP_BYTES = 48 * 1024;
|
|
23593
23559
|
var WRITE_CAP_BYTES = 256 * 1024;
|
|
23594
23560
|
var NOT_UNDOABLE = "(saved to disk; not undoable with Cmd+Z)";
|
|
23595
|
-
function normalizeRelPath(path) {
|
|
23596
|
-
if (typeof path !== "string" || !path.trim()) {
|
|
23597
|
-
return null;
|
|
23598
|
-
}
|
|
23599
|
-
let p = path.trim().replaceAll("\\", "/");
|
|
23600
|
-
while (p.startsWith("./")) {
|
|
23601
|
-
p = p.slice(2);
|
|
23602
|
-
}
|
|
23603
|
-
if (p.startsWith("/") || p.startsWith("~") || /^[A-Za-z]:/.test(p)) {
|
|
23604
|
-
return null;
|
|
23605
|
-
}
|
|
23606
|
-
if (p.split("/").includes("..")) {
|
|
23607
|
-
return null;
|
|
23608
|
-
}
|
|
23609
|
-
return p;
|
|
23610
|
-
}
|
|
23611
|
-
function pathError(path) {
|
|
23612
|
-
return {
|
|
23613
|
-
success: false,
|
|
23614
|
-
error: `Invalid path ${JSON.stringify(path)} — use a path relative to the project root (no leading "/", no "..").`
|
|
23615
|
-
};
|
|
23616
|
-
}
|
|
23617
23561
|
function isJxDocPath(path) {
|
|
23618
23562
|
return /^(pages|layouts|components|elements)\//.test(path) && path.endsWith(".json");
|
|
23619
23563
|
}
|
|
@@ -23649,7 +23593,7 @@ function registerProjectTools(registry, {
|
|
|
23649
23593
|
const { dir } = args;
|
|
23650
23594
|
const start = dir === undefined || dir === "" ? "." : normalizeRelPath(dir);
|
|
23651
23595
|
if (start === null) {
|
|
23652
|
-
return
|
|
23596
|
+
return invalidPathResult(dir);
|
|
23653
23597
|
}
|
|
23654
23598
|
const platform = getPlatform2();
|
|
23655
23599
|
const entries = [];
|
|
@@ -23706,7 +23650,7 @@ function registerProjectTools(registry, {
|
|
|
23706
23650
|
async execute(args) {
|
|
23707
23651
|
const relPath = normalizeRelPath(args.path);
|
|
23708
23652
|
if (relPath === null) {
|
|
23709
|
-
return
|
|
23653
|
+
return invalidPathResult(args.path);
|
|
23710
23654
|
}
|
|
23711
23655
|
try {
|
|
23712
23656
|
const content = await getPlatform2().readFile(relPath);
|
|
@@ -23746,11 +23690,11 @@ function registerProjectTools(registry, {
|
|
|
23746
23690
|
},
|
|
23747
23691
|
required: ["path", "content"]
|
|
23748
23692
|
},
|
|
23749
|
-
async execute(args) {
|
|
23693
|
+
async execute(args, ctx) {
|
|
23750
23694
|
const { path, content } = args;
|
|
23751
23695
|
const relPath = normalizeRelPath(path);
|
|
23752
23696
|
if (relPath === null) {
|
|
23753
|
-
return
|
|
23697
|
+
return invalidPathResult(path);
|
|
23754
23698
|
}
|
|
23755
23699
|
if (content.length > WRITE_CAP_BYTES) {
|
|
23756
23700
|
return {
|
|
@@ -23831,10 +23775,16 @@ ${formatted}`
|
|
|
23831
23775
|
}
|
|
23832
23776
|
try {
|
|
23833
23777
|
await getPlatform2().writeFile(relPath, content);
|
|
23834
|
-
|
|
23778
|
+
ctx.ledger.record({ disk: true, ok: true, path: relPath, tool: "write_file" });
|
|
23835
23779
|
} catch (error) {
|
|
23836
23780
|
const message = error instanceof Error ? error.message : String(error);
|
|
23837
|
-
|
|
23781
|
+
ctx.ledger.record({
|
|
23782
|
+
disk: true,
|
|
23783
|
+
error: message,
|
|
23784
|
+
ok: false,
|
|
23785
|
+
path: relPath,
|
|
23786
|
+
tool: "write_file"
|
|
23787
|
+
});
|
|
23838
23788
|
return {
|
|
23839
23789
|
success: false,
|
|
23840
23790
|
error: `Failed to write "${relPath}": ${message}`
|
|
@@ -23921,7 +23871,7 @@ ${tokenHints}` : summary };
|
|
|
23921
23871
|
},
|
|
23922
23872
|
required: ["name"]
|
|
23923
23873
|
},
|
|
23924
|
-
async execute(args) {
|
|
23874
|
+
async execute(args, ctx) {
|
|
23925
23875
|
const {
|
|
23926
23876
|
name,
|
|
23927
23877
|
description,
|
|
@@ -23985,6 +23935,7 @@ ${tokenHints}` : summary };
|
|
|
23985
23935
|
error: `Failed to create project: ${error instanceof Error ? error.message : String(error)}`
|
|
23986
23936
|
};
|
|
23987
23937
|
}
|
|
23938
|
+
ctx.ledger.record({ disk: true, ok: true, path: result.root, tool: "create_project" });
|
|
23988
23939
|
const { adopted, error: adoptionError } = await adoptCreatedProject(result.root, {
|
|
23989
23940
|
adopt: adoptProject,
|
|
23990
23941
|
getTab
|
|
@@ -24147,10 +24098,7 @@ function destinationProblem(destination) {
|
|
|
24147
24098
|
}
|
|
24148
24099
|
return null;
|
|
24149
24100
|
}
|
|
24150
|
-
var
|
|
24151
|
-
function resetImportGuard() {
|
|
24152
|
-
imported = false;
|
|
24153
|
-
}
|
|
24101
|
+
var IMPORT_DONE = "import.done";
|
|
24154
24102
|
function describeRun(id, root, summary) {
|
|
24155
24103
|
const record = importRun(id);
|
|
24156
24104
|
const warnings = [...record?.warnings ?? [], ...summary?.warnings ?? []];
|
|
@@ -24180,7 +24128,12 @@ function describeRun(id, root, summary) {
|
|
|
24180
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).");
|
|
24181
24129
|
return lines.join(" ");
|
|
24182
24130
|
}
|
|
24183
|
-
function registerImportTools(registry, {
|
|
24131
|
+
function registerImportTools(registry, {
|
|
24132
|
+
getTab,
|
|
24133
|
+
adoptProject,
|
|
24134
|
+
onProjectAdopted,
|
|
24135
|
+
brief: briefStore = { clear: clearPendingImportBrief, read: pendingImportBrief }
|
|
24136
|
+
}) {
|
|
24184
24137
|
registry.register(createToolDefinition({
|
|
24185
24138
|
name: "import_site",
|
|
24186
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.",
|
|
@@ -24226,7 +24179,7 @@ function registerImportTools(registry, { getTab, adoptProject, onProjectAdopted
|
|
|
24226
24179
|
},
|
|
24227
24180
|
required: ["url"]
|
|
24228
24181
|
},
|
|
24229
|
-
async execute(args) {
|
|
24182
|
+
async execute(args, ctx) {
|
|
24230
24183
|
const {
|
|
24231
24184
|
url,
|
|
24232
24185
|
directory,
|
|
@@ -24242,7 +24195,7 @@ function registerImportTools(registry, { getTab, adoptProject, onProjectAdopted
|
|
|
24242
24195
|
if (workspace2.projectRoot) {
|
|
24243
24196
|
return toolError("A project is already open in this window — import_site is only for bootstrapping.");
|
|
24244
24197
|
}
|
|
24245
|
-
if (
|
|
24198
|
+
if (ctx.session.get(IMPORT_DONE) === true) {
|
|
24246
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.");
|
|
24247
24200
|
}
|
|
24248
24201
|
const platform = getPlatform2();
|
|
@@ -24258,7 +24211,7 @@ function registerImportTools(registry, { getTab, adoptProject, onProjectAdopted
|
|
|
24258
24211
|
if (target.protocol !== "http:" && target.protocol !== "https:") {
|
|
24259
24212
|
return toolError("The url must start with http:// or https://.");
|
|
24260
24213
|
}
|
|
24261
|
-
const brief =
|
|
24214
|
+
const brief = briefStore.read();
|
|
24262
24215
|
const requested = typeof directory === "string" ? directory.trim() : "";
|
|
24263
24216
|
if (requested && brief && requested !== brief.directory) {
|
|
24264
24217
|
return toolError(`The destination is already set to ${brief.directory} — call import_site without ` + '"directory", or ask the user before changing it.');
|
|
@@ -24271,12 +24224,14 @@ function registerImportTools(registry, { getTab, adoptProject, onProjectAdopted
|
|
|
24271
24224
|
if (problem) {
|
|
24272
24225
|
return toolError(problem);
|
|
24273
24226
|
}
|
|
24274
|
-
const id =
|
|
24227
|
+
const id = ctx.callId;
|
|
24275
24228
|
const signal = beginImportRun(id, { directory: destination, url: target.href });
|
|
24276
|
-
|
|
24229
|
+
const callSignal = ctx.signal;
|
|
24230
|
+
const stopRun = () => {
|
|
24277
24231
|
abortImportRun();
|
|
24278
24232
|
finishImportRun(id, { status: "stopped" });
|
|
24279
|
-
}
|
|
24233
|
+
};
|
|
24234
|
+
callSignal.addEventListener("abort", stopRun, { once: true });
|
|
24280
24235
|
const apiKey = getOpenAiKey();
|
|
24281
24236
|
const baseUrl = getBaseUrl();
|
|
24282
24237
|
const model = brief?.model || preferredModel();
|
|
@@ -24303,8 +24258,11 @@ function registerImportTools(registry, { getTab, adoptProject, onProjectAdopted
|
|
|
24303
24258
|
...apiKey ? { apiKey } : {},
|
|
24304
24259
|
...baseUrl ? { baseUrl } : {},
|
|
24305
24260
|
...model ? { model } : {}
|
|
24306
|
-
}, (evt) =>
|
|
24261
|
+
}, (evt) => {
|
|
24262
|
+
ctx.progress(evt);
|
|
24263
|
+
}, signal, onReady);
|
|
24307
24264
|
} catch (error) {
|
|
24265
|
+
callSignal.removeEventListener("abort", stopRun);
|
|
24308
24266
|
const message = error instanceof Error ? error.message : String(error);
|
|
24309
24267
|
if (signal.aborted) {
|
|
24310
24268
|
finishImportRun(id, { error: message, status: "stopped" });
|
|
@@ -24318,9 +24276,11 @@ function registerImportTools(registry, { getTab, adoptProject, onProjectAdopted
|
|
|
24318
24276
|
Last steps:
|
|
24319
24277
|
${tail}` : ""}`);
|
|
24320
24278
|
}
|
|
24279
|
+
callSignal.removeEventListener("abort", stopRun);
|
|
24321
24280
|
finishImportRun(id, { status: "done" });
|
|
24322
|
-
|
|
24323
|
-
|
|
24281
|
+
ctx.ledger.record({ disk: true, ok: true, path: result.root, tool: "import_site" });
|
|
24282
|
+
ctx.session.set(IMPORT_DONE, true);
|
|
24283
|
+
briefStore.clear();
|
|
24324
24284
|
const { adopted, error: adoptionError } = await (adoption ?? adoptCreatedProject(result.root, {
|
|
24325
24285
|
getTab,
|
|
24326
24286
|
...adoptProject ? { adopt: adoptProject } : {}
|
|
@@ -24357,7 +24317,7 @@ function createGatedToolRegistry(inner, availability) {
|
|
|
24357
24317
|
validate(toolName, args) {
|
|
24358
24318
|
return inner.validate(toolName, args);
|
|
24359
24319
|
},
|
|
24360
|
-
async execute(toolName, args) {
|
|
24320
|
+
async execute(toolName, args, ctx) {
|
|
24361
24321
|
if (inner.getDefinition(toolName) && !isAvailable(toolName)) {
|
|
24362
24322
|
const gate = availability.get(toolName);
|
|
24363
24323
|
return {
|
|
@@ -24365,28 +24325,207 @@ function createGatedToolRegistry(inner, availability) {
|
|
|
24365
24325
|
error: `Tool "${toolName}" is not available right now — it requires ${gate?.requires ?? "a different studio state"}.`
|
|
24366
24326
|
};
|
|
24367
24327
|
}
|
|
24368
|
-
return inner.execute(toolName, args);
|
|
24328
|
+
return inner.execute(toolName, args, ctx);
|
|
24369
24329
|
}
|
|
24370
24330
|
};
|
|
24371
24331
|
}
|
|
24372
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
|
+
|
|
24373
24505
|
// src/services/tool-executor.ts
|
|
24374
24506
|
var MAX_ROUNDS = 5;
|
|
24507
|
+
var EMPTY_TURN_TEXT = "The model sent back an empty reply.";
|
|
24375
24508
|
var MAX_TOTAL_ROUNDS = 25;
|
|
24376
|
-
var INTERACTIVE_TOOLS = new Set(["ask_user"]);
|
|
24377
24509
|
async function runAgentLoop({
|
|
24378
24510
|
chatState,
|
|
24379
24511
|
streamingClient,
|
|
24380
24512
|
toolRegistry,
|
|
24381
24513
|
systemPrompt,
|
|
24382
24514
|
signal,
|
|
24383
|
-
getTab
|
|
24515
|
+
getTab,
|
|
24516
|
+
session = createSessionFacts()
|
|
24384
24517
|
}) {
|
|
24385
24518
|
const allErrors = [];
|
|
24386
24519
|
const appliedSummaries = [];
|
|
24387
|
-
const
|
|
24388
|
-
|
|
24389
|
-
|
|
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
|
+
};
|
|
24390
24529
|
if (getTab) {
|
|
24391
24530
|
beginBatch(getTab());
|
|
24392
24531
|
}
|
|
@@ -24436,6 +24575,10 @@ async function runAgentLoop({
|
|
|
24436
24575
|
chatState.appendToolCallEnd(event.id);
|
|
24437
24576
|
break;
|
|
24438
24577
|
}
|
|
24578
|
+
case "usage": {
|
|
24579
|
+
chatState.recordUsage(event, { systemTokens: estimatePromptTokens(systemPrompt) });
|
|
24580
|
+
break;
|
|
24581
|
+
}
|
|
24439
24582
|
case "done": {
|
|
24440
24583
|
({ stopReason } = event);
|
|
24441
24584
|
break;
|
|
@@ -24450,7 +24593,6 @@ async function runAgentLoop({
|
|
|
24450
24593
|
}
|
|
24451
24594
|
}
|
|
24452
24595
|
}
|
|
24453
|
-
chatState.finishStream(stopReason);
|
|
24454
24596
|
if (streamError) {
|
|
24455
24597
|
if (streamErrorCode === "cf_reconnect_required") {
|
|
24456
24598
|
resetModelCache();
|
|
@@ -24459,30 +24601,55 @@ async function runAgentLoop({
|
|
|
24459
24601
|
chatState.setError(streamError);
|
|
24460
24602
|
return;
|
|
24461
24603
|
}
|
|
24462
|
-
|
|
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) {
|
|
24463
24611
|
return;
|
|
24464
24612
|
}
|
|
24465
24613
|
let didWork = false;
|
|
24466
24614
|
for (const [id, call] of toolCalls) {
|
|
24615
|
+
if (signal?.aborted) {
|
|
24616
|
+
return;
|
|
24617
|
+
}
|
|
24467
24618
|
let result;
|
|
24468
|
-
|
|
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
|
+
});
|
|
24469
24631
|
try {
|
|
24470
|
-
|
|
24471
|
-
result = await toolRegistry.execute(call.name, args);
|
|
24632
|
+
result = await toolRegistry.execute(call.name, parseToolArguments(call.arguments), ctx);
|
|
24472
24633
|
} catch (error) {
|
|
24473
24634
|
result = {
|
|
24474
24635
|
success: false,
|
|
24475
24636
|
error: `Failed to parse arguments: ${error.message}`
|
|
24476
24637
|
};
|
|
24638
|
+
} finally {
|
|
24639
|
+
link?.release();
|
|
24477
24640
|
}
|
|
24478
|
-
if (!
|
|
24641
|
+
if (turnUserId !== undefined && !chatState.messages.some((m) => m.id === turnUserId)) {
|
|
24642
|
+
return;
|
|
24643
|
+
}
|
|
24644
|
+
if (toolRegistry.getDefinition(call.name)?.interactive !== true) {
|
|
24479
24645
|
didWork = true;
|
|
24480
24646
|
}
|
|
24481
24647
|
reanchorBatch();
|
|
24482
24648
|
if (!result.success && result.error) {
|
|
24483
24649
|
allErrors.push(result.error);
|
|
24484
24650
|
}
|
|
24485
|
-
|
|
24651
|
+
const wrote = ledger.writes.slice(writesBefore).some((write) => write.ok);
|
|
24652
|
+
if (result.success && result.summary && wrote) {
|
|
24486
24653
|
appliedSummaries.push(result.summary);
|
|
24487
24654
|
}
|
|
24488
24655
|
chatState.appendToolResult(id, result);
|
|
@@ -24491,6 +24658,9 @@ async function runAgentLoop({
|
|
|
24491
24658
|
if (didWork) {
|
|
24492
24659
|
workRounds += 1;
|
|
24493
24660
|
}
|
|
24661
|
+
if (signal?.aborted) {
|
|
24662
|
+
return;
|
|
24663
|
+
}
|
|
24494
24664
|
if (workRounds < MAX_ROUNDS && round < MAX_TOTAL_ROUNDS) {
|
|
24495
24665
|
chatState.beginAssistantTurn();
|
|
24496
24666
|
}
|
|
@@ -24517,10 +24687,20 @@ You can continue by sending another message, or try a more specific request.`;
|
|
|
24517
24687
|
}
|
|
24518
24688
|
chatState.setError(tail);
|
|
24519
24689
|
} finally {
|
|
24520
|
-
endTurnSignal();
|
|
24521
24690
|
endBatch();
|
|
24522
|
-
|
|
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}`);
|
|
24523
24702
|
}
|
|
24703
|
+
return parsed;
|
|
24524
24704
|
}
|
|
24525
24705
|
|
|
24526
24706
|
// src/services/ai-system-prompt.ts
|
|
@@ -25232,153 +25412,6 @@ function buildProjectSummary({
|
|
|
25232
25412
|
`);
|
|
25233
25413
|
}
|
|
25234
25414
|
|
|
25235
|
-
// src/services/context-manager.ts
|
|
25236
|
-
var CHARS_PER_TOKEN = 4;
|
|
25237
|
-
var MODEL_CONTEXT_WINDOWS = [
|
|
25238
|
-
["gpt-4o", 128000],
|
|
25239
|
-
["gpt-4.1", 1e6],
|
|
25240
|
-
["gpt-4-turbo", 128000],
|
|
25241
|
-
["gpt-4", 8192],
|
|
25242
|
-
["gpt-3.5", 16385],
|
|
25243
|
-
["o1", 200000],
|
|
25244
|
-
["o3", 200000],
|
|
25245
|
-
["claude", 200000]
|
|
25246
|
-
];
|
|
25247
|
-
var DEFAULT_CONTEXT_WINDOW = 32000;
|
|
25248
|
-
var BUDGET_FRACTION = 0.8;
|
|
25249
|
-
var WARN_FRACTION = 0.5;
|
|
25250
|
-
function contextWindowFor(model) {
|
|
25251
|
-
if (!model) {
|
|
25252
|
-
return DEFAULT_CONTEXT_WINDOW;
|
|
25253
|
-
}
|
|
25254
|
-
const reported = modelContextWindow(model);
|
|
25255
|
-
if (reported !== undefined && reported > 0) {
|
|
25256
|
-
return reported;
|
|
25257
|
-
}
|
|
25258
|
-
const id = model.toLowerCase();
|
|
25259
|
-
let best = 0;
|
|
25260
|
-
let window2 = DEFAULT_CONTEXT_WINDOW;
|
|
25261
|
-
for (const [prefix, size] of MODEL_CONTEXT_WINDOWS) {
|
|
25262
|
-
if (id.startsWith(prefix) && prefix.length > best) {
|
|
25263
|
-
best = prefix.length;
|
|
25264
|
-
window2 = size;
|
|
25265
|
-
}
|
|
25266
|
-
}
|
|
25267
|
-
return window2;
|
|
25268
|
-
}
|
|
25269
|
-
var KEEP_RECENT = 20;
|
|
25270
|
-
var MIN_USER_TURNS = 3;
|
|
25271
|
-
function estimateTokens(input) {
|
|
25272
|
-
if (typeof input === "string") {
|
|
25273
|
-
return Math.ceil(input.length / CHARS_PER_TOKEN);
|
|
25274
|
-
}
|
|
25275
|
-
let total = 0;
|
|
25276
|
-
for (const msg of input) {
|
|
25277
|
-
total += estimateTokens(msg.content || "");
|
|
25278
|
-
if (msg.tool_calls) {
|
|
25279
|
-
for (const tc of msg.tool_calls) {
|
|
25280
|
-
total += estimateTokens(tc.function?.name || "") + estimateTokens(tc.function?.arguments || "") + 4;
|
|
25281
|
-
}
|
|
25282
|
-
}
|
|
25283
|
-
total += 4;
|
|
25284
|
-
}
|
|
25285
|
-
return total;
|
|
25286
|
-
}
|
|
25287
|
-
function trimContext(chatState, systemPrompt) {
|
|
25288
|
-
const window2 = contextWindowFor(chatState.model);
|
|
25289
|
-
const maxTokens = Math.floor(window2 * BUDGET_FRACTION);
|
|
25290
|
-
const warnTokens = Math.floor(window2 * WARN_FRACTION);
|
|
25291
|
-
const systemTokens = estimateTokens(systemPrompt);
|
|
25292
|
-
const allMessages = chatState.messages;
|
|
25293
|
-
const messageTokens = estimateTokens(chatState.toMessagesArray());
|
|
25294
|
-
const total = systemTokens + messageTokens;
|
|
25295
|
-
if (total <= maxTokens) {
|
|
25296
|
-
chatState.setTokenCount(total);
|
|
25297
|
-
chatState.setContextWarning(total >= warnTokens);
|
|
25298
|
-
return { estimatedTokens: total, droppedCount: 0 };
|
|
25299
|
-
}
|
|
25300
|
-
let keepFrom = Math.max(0, allMessages.length - KEEP_RECENT);
|
|
25301
|
-
let preservedTurns = 0;
|
|
25302
|
-
for (let i = allMessages.length - 1;i >= keepFrom; i--) {
|
|
25303
|
-
const { role } = allMessages[i];
|
|
25304
|
-
if (role === "user" || role === "tool") {
|
|
25305
|
-
preservedTurns += 1;
|
|
25306
|
-
}
|
|
25307
|
-
}
|
|
25308
|
-
if (preservedTurns < MIN_USER_TURNS) {
|
|
25309
|
-
for (let i = keepFrom - 1;i >= 0 && preservedTurns < MIN_USER_TURNS; i--) {
|
|
25310
|
-
keepFrom = i;
|
|
25311
|
-
const { role } = allMessages[i];
|
|
25312
|
-
if (role === "user" || role === "tool") {
|
|
25313
|
-
preservedTurns += 1;
|
|
25314
|
-
}
|
|
25315
|
-
}
|
|
25316
|
-
}
|
|
25317
|
-
if (keepFrom <= 0) {
|
|
25318
|
-
chatState.setTokenCount(total);
|
|
25319
|
-
chatState.setContextWarning(true);
|
|
25320
|
-
return { estimatedTokens: total, droppedCount: 0 };
|
|
25321
|
-
}
|
|
25322
|
-
const droppedCount = keepFrom;
|
|
25323
|
-
allMessages.splice(0, keepFrom);
|
|
25324
|
-
allMessages.unshift({
|
|
25325
|
-
id: `ctx_summary_${Date.now()}`,
|
|
25326
|
-
role: "user",
|
|
25327
|
-
content: `[Earlier conversation truncated. ${droppedCount} messages dropped to stay within token budget. The most recent ${allMessages.length - 1} messages are preserved.]`,
|
|
25328
|
-
timestamp: Date.now()
|
|
25329
|
-
});
|
|
25330
|
-
const newTotal = systemTokens + estimateTokens(chatState.toMessagesArray());
|
|
25331
|
-
chatState.setTokenCount(newTotal);
|
|
25332
|
-
chatState.setContextWarning(true);
|
|
25333
|
-
return { estimatedTokens: newTotal, droppedCount };
|
|
25334
|
-
}
|
|
25335
|
-
var UNANSWERED_TOOL_RESULT = JSON.stringify({
|
|
25336
|
-
success: false,
|
|
25337
|
-
error: "This tool call was never completed — the session was reloaded or the history was trimmed."
|
|
25338
|
-
});
|
|
25339
|
-
function pruneOrphanToolMessages(chatState) {
|
|
25340
|
-
const { messages } = chatState;
|
|
25341
|
-
const requested = new Set;
|
|
25342
|
-
const answered = new Set;
|
|
25343
|
-
for (const msg of messages) {
|
|
25344
|
-
if (msg.role === "assistant" && msg.toolCalls) {
|
|
25345
|
-
for (const call of msg.toolCalls) {
|
|
25346
|
-
requested.add(call.id);
|
|
25347
|
-
}
|
|
25348
|
-
} else if (msg.role === "tool" && msg.toolCallId) {
|
|
25349
|
-
answered.add(msg.toolCallId);
|
|
25350
|
-
}
|
|
25351
|
-
}
|
|
25352
|
-
let dropped = 0;
|
|
25353
|
-
for (let i = messages.length - 1;i >= 0; i--) {
|
|
25354
|
-
const msg = messages[i];
|
|
25355
|
-
if (msg.role === "tool" && (!msg.toolCallId || !requested.has(msg.toolCallId))) {
|
|
25356
|
-
messages.splice(i, 1);
|
|
25357
|
-
dropped += 1;
|
|
25358
|
-
}
|
|
25359
|
-
}
|
|
25360
|
-
let sealed = 0;
|
|
25361
|
-
for (let i = messages.length - 1;i >= 0; i--) {
|
|
25362
|
-
const msg = messages[i];
|
|
25363
|
-
if (msg.role !== "assistant" || !msg.toolCalls) {
|
|
25364
|
-
continue;
|
|
25365
|
-
}
|
|
25366
|
-
const missing = msg.toolCalls.filter((call) => !answered.has(call.id));
|
|
25367
|
-
if (missing.length === 0) {
|
|
25368
|
-
continue;
|
|
25369
|
-
}
|
|
25370
|
-
messages.splice(i + 1, 0, ...missing.map((call, n) => ({
|
|
25371
|
-
content: UNANSWERED_TOOL_RESULT,
|
|
25372
|
-
id: `sealed_${call.id}_${n}`,
|
|
25373
|
-
role: "tool",
|
|
25374
|
-
timestamp: msg.timestamp,
|
|
25375
|
-
toolCallId: call.id
|
|
25376
|
-
})));
|
|
25377
|
-
sealed += missing.length;
|
|
25378
|
-
}
|
|
25379
|
-
return { dropped, sealed };
|
|
25380
|
-
}
|
|
25381
|
-
|
|
25382
25415
|
// src/services/render-critic.ts
|
|
25383
25416
|
function translateRenderError(err) {
|
|
25384
25417
|
const msg = err.message || String(err);
|
|
@@ -25581,6 +25614,76 @@ function moveSession(fromRoot, toRoot, id) {
|
|
|
25581
25614
|
writeIndex(toRoot, toIndex);
|
|
25582
25615
|
}
|
|
25583
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
|
+
|
|
25584
25687
|
// src/services/document-assistant.ts
|
|
25585
25688
|
function projectRoot() {
|
|
25586
25689
|
return workspace2.projectRoot || "";
|
|
@@ -25588,6 +25691,7 @@ function projectRoot() {
|
|
|
25588
25691
|
function createDocumentAssistant() {
|
|
25589
25692
|
const chatState = createChatState({ model: preferredModel() });
|
|
25590
25693
|
let sessionId = null;
|
|
25694
|
+
let sessionFacts = createSessionFacts();
|
|
25591
25695
|
const getProjectStyle = () => workspace2.projectConfig?.style;
|
|
25592
25696
|
const findOpenTab = (path) => {
|
|
25593
25697
|
for (const tab of workspace2.tabs.values()) {
|
|
@@ -25598,7 +25702,7 @@ function createDocumentAssistant() {
|
|
|
25598
25702
|
return null;
|
|
25599
25703
|
};
|
|
25600
25704
|
const innerRegistry = createToolRegistry();
|
|
25601
|
-
registerAskTool(innerRegistry);
|
|
25705
|
+
registerAskTool(innerRegistry, { onPending: () => persistChat() });
|
|
25602
25706
|
registerAiTools(innerRegistry, {
|
|
25603
25707
|
getTab: () => activeTab2.value,
|
|
25604
25708
|
saveFile: async (relPath, content) => {
|
|
@@ -25667,6 +25771,8 @@ function createDocumentAssistant() {
|
|
|
25667
25771
|
});
|
|
25668
25772
|
const toolRegistry = composeToolRegistries(createGatedToolRegistry(innerRegistry, availability), commandTools);
|
|
25669
25773
|
let controller = null;
|
|
25774
|
+
const turnActive = shallowRef(false);
|
|
25775
|
+
let turnEnded = Promise.resolve();
|
|
25670
25776
|
function buildPrompt() {
|
|
25671
25777
|
const tab = activeTab2.value;
|
|
25672
25778
|
const inventory = projectState ? [...projectState.dirs.values()].flat().filter((e) => e.type === "file").map((e) => e.path) : undefined;
|
|
@@ -25693,7 +25799,7 @@ function createDocumentAssistant() {
|
|
|
25693
25799
|
});
|
|
25694
25800
|
}
|
|
25695
25801
|
async function sendMessage(text) {
|
|
25696
|
-
if (!text.trim() || chatState.status === "streaming") {
|
|
25802
|
+
if (!text.trim() || turnActive.value || chatState.status === "streaming") {
|
|
25697
25803
|
return;
|
|
25698
25804
|
}
|
|
25699
25805
|
if (!sessionId) {
|
|
@@ -25706,32 +25812,49 @@ function createDocumentAssistant() {
|
|
|
25706
25812
|
}
|
|
25707
25813
|
pruneOrphanToolMessages(chatState);
|
|
25708
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
|
+
});
|
|
25709
25822
|
try {
|
|
25710
25823
|
const plat = getPlatform2();
|
|
25711
|
-
const chatUrl = await Promise.resolve(plat.aiChatUrl());
|
|
25712
25824
|
chatState.setModel(preferredModel());
|
|
25713
25825
|
const streamingClient = createProxyStreamingClient({
|
|
25714
|
-
chatUrl,
|
|
25826
|
+
chatUrl: () => plat.aiChatUrl(),
|
|
25715
25827
|
model: chatState.model,
|
|
25716
25828
|
apiKey: getOpenAiKey() || undefined,
|
|
25717
25829
|
baseUrl: getBaseUrl() || undefined
|
|
25718
25830
|
});
|
|
25719
|
-
|
|
25831
|
+
if (sessionFacts.sessionId !== sessionId) {
|
|
25832
|
+
sessionFacts = createSessionFacts(sessionId, sessionFacts.toJSON());
|
|
25833
|
+
}
|
|
25720
25834
|
await runAgentLoop({
|
|
25721
25835
|
chatState,
|
|
25722
25836
|
streamingClient,
|
|
25723
25837
|
toolRegistry,
|
|
25724
25838
|
systemPrompt: buildPrompt(),
|
|
25725
|
-
signal
|
|
25839
|
+
signal,
|
|
25840
|
+
session: sessionFacts,
|
|
25726
25841
|
getTab: () => activeTab2.value
|
|
25727
25842
|
});
|
|
25728
25843
|
} catch (error) {
|
|
25729
25844
|
chatState.setError(error instanceof Error ? error.message : String(error));
|
|
25730
25845
|
} finally {
|
|
25731
25846
|
controller = null;
|
|
25847
|
+
turnActive.value = false;
|
|
25732
25848
|
persistChat();
|
|
25849
|
+
settleTurnEnded();
|
|
25733
25850
|
}
|
|
25734
25851
|
}
|
|
25852
|
+
function isTurnActive() {
|
|
25853
|
+
return turnActive.value;
|
|
25854
|
+
}
|
|
25855
|
+
function whenTurnEnds() {
|
|
25856
|
+
return turnEnded;
|
|
25857
|
+
}
|
|
25735
25858
|
function stop() {
|
|
25736
25859
|
controller?.abort();
|
|
25737
25860
|
cancelAsk();
|
|
@@ -25742,7 +25865,8 @@ function createDocumentAssistant() {
|
|
|
25742
25865
|
stop();
|
|
25743
25866
|
resetAsk();
|
|
25744
25867
|
resetImportRuns();
|
|
25745
|
-
|
|
25868
|
+
resetAiWrites();
|
|
25869
|
+
sessionFacts = createSessionFacts();
|
|
25746
25870
|
chatState.clearChat();
|
|
25747
25871
|
sessionId = null;
|
|
25748
25872
|
setActiveSession(projectRoot(), null);
|
|
@@ -25779,10 +25903,10 @@ function createDocumentAssistant() {
|
|
|
25779
25903
|
return;
|
|
25780
25904
|
}
|
|
25781
25905
|
const msgs = chatState.messages.filter((m) => m.role !== "assistant" || m.content || (m.toolCalls?.length ?? 0) > 0);
|
|
25782
|
-
saveSession(projectRoot(), sessionId, msgs);
|
|
25906
|
+
saveSession(projectRoot(), sessionId, stripToolResults(msgs));
|
|
25783
25907
|
}
|
|
25784
25908
|
function pushRestoredMessages(msgs) {
|
|
25785
|
-
for (const m of msgs) {
|
|
25909
|
+
for (const m of backfillToolResults(msgs)) {
|
|
25786
25910
|
chatState.messages.push({
|
|
25787
25911
|
...m,
|
|
25788
25912
|
id: m.id || `restored_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`
|
|
@@ -25805,7 +25929,9 @@ function createDocumentAssistant() {
|
|
|
25805
25929
|
restoreChat();
|
|
25806
25930
|
return {
|
|
25807
25931
|
chatState,
|
|
25932
|
+
isTurnActive,
|
|
25808
25933
|
sendMessage,
|
|
25934
|
+
whenTurnEnds,
|
|
25809
25935
|
stop,
|
|
25810
25936
|
newChat,
|
|
25811
25937
|
listSessions: listSessions2,
|
|
@@ -25818,7 +25944,7 @@ function createDocumentAssistant() {
|
|
|
25818
25944
|
var ai_credentials_form_default = {
|
|
25819
25945
|
$schema: "https://jxsuite.com/schema/v1",
|
|
25820
25946
|
$id: "StudioAiCredentialsForm",
|
|
25821
|
-
$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.",
|
|
25822
25948
|
tagName: "div",
|
|
25823
25949
|
attributes: {
|
|
25824
25950
|
part: "ai-creds-form",
|
|
@@ -25834,8 +25960,7 @@ var ai_credentials_form_default = {
|
|
|
25834
25960
|
maxWidth: "320px",
|
|
25835
25961
|
textAlign: "left",
|
|
25836
25962
|
'& [part="title"]': {
|
|
25837
|
-
$description: "
|
|
25838
|
-
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.",
|
|
25839
25964
|
fontWeight: "600"
|
|
25840
25965
|
},
|
|
25841
25966
|
'& [part="note"]': {
|
|
@@ -25855,17 +25980,22 @@ var ai_credentials_form_default = {
|
|
|
25855
25980
|
width: "100%"
|
|
25856
25981
|
},
|
|
25857
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.",
|
|
25858
25984
|
display: "flex",
|
|
25859
|
-
|
|
25985
|
+
flexWrap: "wrap",
|
|
25986
|
+
alignItems: "flex-start",
|
|
25860
25987
|
gap: "var(--jx-space-3)"
|
|
25861
25988
|
},
|
|
25862
25989
|
'& [part="models-error"]': {
|
|
25863
|
-
$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.',
|
|
25864
25991
|
color: "var(--danger)",
|
|
25865
|
-
fontSize: "var(--jx-text-sm)"
|
|
25992
|
+
fontSize: "var(--jx-text-sm)",
|
|
25993
|
+
flexBasis: "100%",
|
|
25994
|
+
minWidth: "0",
|
|
25995
|
+
overflowWrap: "anywhere"
|
|
25866
25996
|
},
|
|
25867
25997
|
'& [part="actions"]': {
|
|
25868
|
-
$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.',
|
|
25869
25999
|
display: "flex",
|
|
25870
26000
|
alignSelf: "flex-end",
|
|
25871
26001
|
gap: "var(--jx-space-3)"
|
|
@@ -26067,26 +26197,26 @@ var ai_credentials_form_default = {
|
|
|
26067
26197
|
}
|
|
26068
26198
|
},
|
|
26069
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.",
|
|
26070
26201
|
tagName: "div",
|
|
26071
26202
|
attributes: {
|
|
26072
|
-
part: "actions"
|
|
26203
|
+
part: "actions-slot",
|
|
26204
|
+
role: "none"
|
|
26073
26205
|
},
|
|
26074
|
-
|
|
26075
|
-
|
|
26206
|
+
style: {
|
|
26207
|
+
display: "contents"
|
|
26208
|
+
},
|
|
26209
|
+
$switch: {
|
|
26210
|
+
$ref: "#/state/dirty"
|
|
26211
|
+
},
|
|
26212
|
+
cases: {
|
|
26213
|
+
true: {
|
|
26076
26214
|
tagName: "div",
|
|
26077
26215
|
attributes: {
|
|
26078
|
-
part: "
|
|
26079
|
-
role: "none"
|
|
26080
|
-
},
|
|
26081
|
-
style: {
|
|
26082
|
-
display: "contents"
|
|
26083
|
-
},
|
|
26084
|
-
$switch: {
|
|
26085
|
-
$ref: "#/state/haveKey"
|
|
26216
|
+
part: "actions"
|
|
26086
26217
|
},
|
|
26087
|
-
|
|
26088
|
-
|
|
26089
|
-
$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
|
+
{
|
|
26090
26220
|
tagName: "jx-button",
|
|
26091
26221
|
attributes: {
|
|
26092
26222
|
part: "cancel"
|
|
@@ -26112,38 +26242,38 @@ var ai_credentials_form_default = {
|
|
|
26112
26242
|
textContent: "Cancel"
|
|
26113
26243
|
}
|
|
26114
26244
|
]
|
|
26115
|
-
}
|
|
26116
|
-
}
|
|
26117
|
-
},
|
|
26118
|
-
{
|
|
26119
|
-
tagName: "jx-button",
|
|
26120
|
-
attributes: {
|
|
26121
|
-
part: "save"
|
|
26122
|
-
},
|
|
26123
|
-
$props: {
|
|
26124
|
-
size: "sm",
|
|
26125
|
-
variant: "accent"
|
|
26126
|
-
},
|
|
26127
|
-
onclick: {
|
|
26128
|
-
$prototype: "Function",
|
|
26129
|
-
body: [
|
|
26130
|
-
{
|
|
26131
|
-
operator: "call",
|
|
26132
|
-
target: {
|
|
26133
|
-
$ref: "#/state/save"
|
|
26134
|
-
},
|
|
26135
|
-
value: []
|
|
26136
|
-
}
|
|
26137
|
-
]
|
|
26138
|
-
},
|
|
26139
|
-
children: [
|
|
26245
|
+
},
|
|
26140
26246
|
{
|
|
26141
|
-
tagName: "
|
|
26142
|
-
|
|
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
|
+
]
|
|
26143
26273
|
}
|
|
26144
26274
|
]
|
|
26145
26275
|
}
|
|
26146
|
-
|
|
26276
|
+
}
|
|
26147
26277
|
}
|
|
26148
26278
|
]
|
|
26149
26279
|
};
|
|
@@ -26174,7 +26304,11 @@ function createAiCredentialsSurface(view, actions) {
|
|
|
26174
26304
|
return {
|
|
26175
26305
|
host,
|
|
26176
26306
|
update(next) {
|
|
26307
|
+
const answering = !next.dirty && host.querySelector('[part="actions"]')?.contains(document.activeElement);
|
|
26177
26308
|
Object.assign(scope, derive3(next));
|
|
26309
|
+
if (answering && !host.contains(document.activeElement)) {
|
|
26310
|
+
host.querySelector('[part="key"] [part="input"]')?.focus();
|
|
26311
|
+
}
|
|
26178
26312
|
if (mounting || handle !== null && host.contains(handle.root)) {
|
|
26179
26313
|
return;
|
|
26180
26314
|
}
|
|
@@ -26195,11 +26329,23 @@ function createAiCredentialsForm(opts) {
|
|
|
26195
26329
|
let availableModels = [];
|
|
26196
26330
|
let modelsLoading = false;
|
|
26197
26331
|
let modelsError = "";
|
|
26332
|
+
let basis = { apiKey: "", baseUrl: "", model: "" };
|
|
26198
26333
|
let surface = null;
|
|
26334
|
+
function stored() {
|
|
26335
|
+
return { apiKey: getOpenAiKey(), baseUrl: getBaseUrl(), model: storedModel() };
|
|
26336
|
+
}
|
|
26199
26337
|
function loadDrafts() {
|
|
26200
|
-
|
|
26201
|
-
|
|
26202
|
-
|
|
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;
|
|
26203
26349
|
}
|
|
26204
26350
|
function startEdit() {
|
|
26205
26351
|
loadDrafts();
|
|
@@ -26250,8 +26396,8 @@ function createAiCredentialsForm(opts) {
|
|
|
26250
26396
|
function view() {
|
|
26251
26397
|
return {
|
|
26252
26398
|
baseUrlDraft,
|
|
26399
|
+
dirty: aiProviderDiffers({ apiKey: keyDraft, baseUrl: baseUrlDraft, model: modelDraft }),
|
|
26253
26400
|
fetchLabel: modelsLoading ? "Fetching…" : availableModels.length > 0 ? "Refresh models" : "Fetch models",
|
|
26254
|
-
haveKey: hasOpenAiKey(),
|
|
26255
26401
|
intro: opts.intro ?? DEFAULT_INTRO,
|
|
26256
26402
|
keyDraft,
|
|
26257
26403
|
modelDraft,
|
|
@@ -26273,6 +26419,7 @@ function createAiCredentialsForm(opts) {
|
|
|
26273
26419
|
surface?.update(view());
|
|
26274
26420
|
}
|
|
26275
26421
|
function render() {
|
|
26422
|
+
followStore();
|
|
26276
26423
|
if (surface) {
|
|
26277
26424
|
surface.update(view());
|
|
26278
26425
|
} else {
|
|
@@ -26316,8 +26463,8 @@ var ai_managed_connect_default = {
|
|
|
26316
26463
|
maxWidth: "280px",
|
|
26317
26464
|
textAlign: "center"
|
|
26318
26465
|
},
|
|
26319
|
-
'& [part="
|
|
26320
|
-
$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.',
|
|
26321
26468
|
margin: "0",
|
|
26322
26469
|
color: "var(--danger)",
|
|
26323
26470
|
fontSize: "var(--jx-text-sm)",
|
|
@@ -26372,7 +26519,7 @@ var ai_managed_connect_default = {
|
|
|
26372
26519
|
{
|
|
26373
26520
|
tagName: "div",
|
|
26374
26521
|
attributes: {
|
|
26375
|
-
part: "
|
|
26522
|
+
part: "connect-failure-slot",
|
|
26376
26523
|
role: "none"
|
|
26377
26524
|
},
|
|
26378
26525
|
style: {
|
|
@@ -26385,7 +26532,7 @@ var ai_managed_connect_default = {
|
|
|
26385
26532
|
true: {
|
|
26386
26533
|
tagName: "p",
|
|
26387
26534
|
attributes: {
|
|
26388
|
-
part: "
|
|
26535
|
+
part: "connect-failure"
|
|
26389
26536
|
},
|
|
26390
26537
|
textContent: "${state.error}"
|
|
26391
26538
|
}
|
|
@@ -26438,6 +26585,7 @@ function createManagedConnectSurface(view, actions) {
|
|
|
26438
26585
|
// src/ui/ai-managed-connect.ts
|
|
26439
26586
|
var LEDE_FRESH = "Recommended — run the assistant on Workers AI in your own Cloudflare account. No API key to create, copy or rotate.";
|
|
26440
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.";
|
|
26441
26589
|
function createManagedConnect(opts) {
|
|
26442
26590
|
let busy = false;
|
|
26443
26591
|
let connectError = "";
|
|
@@ -26448,6 +26596,26 @@ function createManagedConnect(opts) {
|
|
|
26448
26596
|
}
|
|
26449
26597
|
return !isProxyConfigured() || proxyStateCode() === "cf_reconnect_required";
|
|
26450
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
|
+
}
|
|
26451
26619
|
function ensureProbe() {
|
|
26452
26620
|
ensureProxyProbe(opts.requestRender);
|
|
26453
26621
|
}
|
|
@@ -26460,17 +26628,10 @@ function createManagedConnect(opts) {
|
|
|
26460
26628
|
return;
|
|
26461
26629
|
}
|
|
26462
26630
|
if (!outcome.connection.accountId) {
|
|
26463
|
-
|
|
26464
|
-
|
|
26465
|
-
connectError = "Cloudflare is connected, but no account is chosen yet — pick one to finish setting up the assistant.";
|
|
26466
|
-
return;
|
|
26467
|
-
}
|
|
26468
|
-
}
|
|
26469
|
-
resetModelCache();
|
|
26470
|
-
await fetchAvailableModels({ force: true });
|
|
26471
|
-
if (!isProxyConfigured() || proxyStateCode() === "cf_reconnect_required") {
|
|
26472
|
-
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;
|
|
26473
26633
|
}
|
|
26634
|
+
await verifyConnection();
|
|
26474
26635
|
}
|
|
26475
26636
|
async function connect() {
|
|
26476
26637
|
if (busy) {
|
|
@@ -26480,7 +26641,7 @@ function createManagedConnect(opts) {
|
|
|
26480
26641
|
connectError = "";
|
|
26481
26642
|
opts.requestRender();
|
|
26482
26643
|
try {
|
|
26483
|
-
await settle(await getPlatform2().cfConnect?.() ?? null);
|
|
26644
|
+
await (needsAccount() ? chooseAccount() : settle(await getPlatform2().cfConnect?.() ?? null));
|
|
26484
26645
|
} catch (error) {
|
|
26485
26646
|
connectError = error instanceof Error ? error.message : String(error);
|
|
26486
26647
|
}
|
|
@@ -26488,6 +26649,14 @@ function createManagedConnect(opts) {
|
|
|
26488
26649
|
opts.requestRender();
|
|
26489
26650
|
}
|
|
26490
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
|
+
}
|
|
26491
26660
|
const lapsed = proxyStateCode() === "cf_reconnect_required";
|
|
26492
26661
|
const idleLabel = lapsed ? "Reconnect Cloudflare" : "Connect Cloudflare";
|
|
26493
26662
|
return {
|
|
@@ -26781,7 +26950,8 @@ var preferences_default = {
|
|
|
26781
26950
|
maxHeight: "60vh",
|
|
26782
26951
|
overflowY: "auto"
|
|
26783
26952
|
},
|
|
26784
|
-
'& [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.',
|
|
26785
26955
|
margin: "0",
|
|
26786
26956
|
fontSize: "var(--jx-text-lg)",
|
|
26787
26957
|
color: "var(--fg)"
|
|
@@ -26817,8 +26987,10 @@ var preferences_default = {
|
|
|
26817
26987
|
gap: "var(--jx-space-2)"
|
|
26818
26988
|
},
|
|
26819
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.",
|
|
26820
26991
|
display: "flex",
|
|
26821
|
-
|
|
26992
|
+
flexWrap: "wrap",
|
|
26993
|
+
alignItems: "flex-start",
|
|
26822
26994
|
justifyContent: "space-between",
|
|
26823
26995
|
gap: "var(--jx-space-4)",
|
|
26824
26996
|
padding: "var(--jx-space-3)",
|
|
@@ -26826,8 +26998,9 @@ var preferences_default = {
|
|
|
26826
26998
|
borderRadius: "var(--radius)"
|
|
26827
26999
|
},
|
|
26828
27000
|
'& [part="account-text"]': {
|
|
26829
|
-
$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.",
|
|
26830
27002
|
display: "flex",
|
|
27003
|
+
flex: "1 1 120px",
|
|
26831
27004
|
flexDirection: "column",
|
|
26832
27005
|
gap: "var(--jx-space-1)",
|
|
26833
27006
|
minWidth: "0"
|
|
@@ -26836,19 +27009,25 @@ var preferences_default = {
|
|
|
26836
27009
|
color: "var(--fg)"
|
|
26837
27010
|
},
|
|
26838
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.",
|
|
26839
27013
|
fontSize: "var(--jx-text-sm)",
|
|
26840
|
-
color: "var(--fg-dim)"
|
|
27014
|
+
color: "var(--fg-dim)",
|
|
27015
|
+
overflowWrap: "anywhere"
|
|
26841
27016
|
},
|
|
26842
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.",
|
|
26843
27019
|
display: "flex",
|
|
26844
27020
|
alignItems: "center",
|
|
26845
27021
|
gap: "var(--jx-space-3)",
|
|
26846
|
-
flexShrink: "0"
|
|
27022
|
+
flexShrink: "0",
|
|
27023
|
+
marginInlineStart: "auto"
|
|
26847
27024
|
},
|
|
26848
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.",
|
|
26849
27027
|
display: "flex",
|
|
26850
27028
|
flexDirection: "column",
|
|
26851
|
-
gap: "var(--jx-space-1)"
|
|
27029
|
+
gap: "var(--jx-space-1)",
|
|
27030
|
+
paddingInlineEnd: "var(--jx-space-2)"
|
|
26852
27031
|
},
|
|
26853
27032
|
'& [part="refusal"]': {
|
|
26854
27033
|
display: "flex",
|
|
@@ -26873,9 +27052,10 @@ var preferences_default = {
|
|
|
26873
27052
|
textTransform: "uppercase",
|
|
26874
27053
|
color: "var(--fg-dim)"
|
|
26875
27054
|
},
|
|
26876
|
-
'& [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.",
|
|
26877
27057
|
display: "flex",
|
|
26878
|
-
alignItems: "
|
|
27058
|
+
alignItems: "center",
|
|
26879
27059
|
gap: "var(--jx-space-4)",
|
|
26880
27060
|
padding: "var(--jx-space-1) 0"
|
|
26881
27061
|
},
|
|
@@ -26887,6 +27067,8 @@ var preferences_default = {
|
|
|
26887
27067
|
color: "var(--fg)"
|
|
26888
27068
|
},
|
|
26889
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",
|
|
26890
27072
|
color: "var(--fg-dim)",
|
|
26891
27073
|
minWidth: "0"
|
|
26892
27074
|
}
|
|
@@ -27631,6 +27813,7 @@ function openPreferencesSurface(options) {
|
|
|
27631
27813
|
const box = element.querySelector('dialog[part="dialog"]') ?? element;
|
|
27632
27814
|
box.setAttribute(REGION_ATTR, overlayRegion("dialog", "preferences"));
|
|
27633
27815
|
showModal(element);
|
|
27816
|
+
element.querySelector('[part="nav-item"][aria-current="true"]')?.focus();
|
|
27634
27817
|
return element;
|
|
27635
27818
|
});
|
|
27636
27819
|
return {
|
|
@@ -38918,6 +39101,9 @@ function formatErrorAdvice(error) {
|
|
|
38918
39101
|
if (lower.includes("500") || lower.includes("internal")) {
|
|
38919
39102
|
return "The upstream API returned a server error. Try again in a moment.";
|
|
38920
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
|
+
}
|
|
38921
39107
|
return "";
|
|
38922
39108
|
}
|
|
38923
39109
|
function projectCommand(id, opts = {}, registry = activeRegistry()) {
|
|
@@ -38947,7 +39133,7 @@ function tokenLabel2(tokens) {
|
|
|
38947
39133
|
function tokenHint(tokens, overBudget) {
|
|
38948
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`;
|
|
38949
39135
|
}
|
|
38950
|
-
var
|
|
39136
|
+
var ASK_TOOL2 = "ask_user";
|
|
38951
39137
|
var IMPORT_TOOL = "import_site";
|
|
38952
39138
|
function parseAsk(tc) {
|
|
38953
39139
|
let parsed;
|
|
@@ -38967,7 +39153,7 @@ function parseAsk(tc) {
|
|
|
38967
39153
|
}
|
|
38968
39154
|
function toolOutcome(tc, live = true) {
|
|
38969
39155
|
if (!tc.result) {
|
|
38970
|
-
return tc.name ===
|
|
39156
|
+
return tc.name === ASK_TOOL2 && !live ? "unanswered" : "pending";
|
|
38971
39157
|
}
|
|
38972
39158
|
return tc.result.success ? "ok" : "failed";
|
|
38973
39159
|
}
|
|
@@ -38976,7 +39162,12 @@ function toolOutcomeText(tc) {
|
|
|
38976
39162
|
if (!result) {
|
|
38977
39163
|
return "";
|
|
38978
39164
|
}
|
|
38979
|
-
|
|
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;
|
|
38980
39171
|
}
|
|
38981
39172
|
function noImport() {
|
|
38982
39173
|
return {
|
|
@@ -39015,7 +39206,7 @@ function projectChip(tc, handlers = {}) {
|
|
|
39015
39206
|
const outcome = toolOutcome(tc, tc.id === handlers.pendingId || Boolean(tc.result));
|
|
39016
39207
|
const text = toolOutcomeText(tc);
|
|
39017
39208
|
const run = tc.name === IMPORT_TOOL ? handlers.importRun?.(tc.id) ?? null : null;
|
|
39018
|
-
const ask = tc.name ===
|
|
39209
|
+
const ask = tc.name === ASK_TOOL2 ? parseAsk(tc) : null;
|
|
39019
39210
|
const answered = tc.result?.success ? tc.result.data ?? null : null;
|
|
39020
39211
|
const base = {
|
|
39021
39212
|
answer: answered ? answered.skipped ? "You decide" : answered.answer ?? "" : "",
|
|
@@ -39464,7 +39655,7 @@ function createComposer(opts) {
|
|
|
39464
39655
|
}
|
|
39465
39656
|
}
|
|
39466
39657
|
function send() {
|
|
39467
|
-
if (!draft.trim() || opts.isStreaming()) {
|
|
39658
|
+
if (!draft.trim() || opts.isStreaming() && !(opts.isAwaiting?.() ?? false)) {
|
|
39468
39659
|
return;
|
|
39469
39660
|
}
|
|
39470
39661
|
const text = buildMessageWithContext(draft, chips);
|
|
@@ -39497,7 +39688,7 @@ function createComposer(opts) {
|
|
|
39497
39688
|
placeholder: awaiting ? "Answer the assistant… (Enter to reply)" : "Ask the assistant… (Enter to send)",
|
|
39498
39689
|
sendDisabled: draft.trim().length === 0,
|
|
39499
39690
|
sendLabel: awaiting ? "Answer" : "Send",
|
|
39500
|
-
sendState: streaming ? "stop" : "send"
|
|
39691
|
+
sendState: !awaiting && streaming ? "stop" : "send"
|
|
39501
39692
|
};
|
|
39502
39693
|
}
|
|
39503
39694
|
return {
|
|
@@ -39658,12 +39849,15 @@ var ai_chat_default = {
|
|
|
39658
39849
|
color: "var(--fg-dim)"
|
|
39659
39850
|
},
|
|
39660
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.",
|
|
39661
39853
|
whiteSpace: "pre-wrap",
|
|
39662
|
-
overflowWrap: "anywhere"
|
|
39854
|
+
overflowWrap: "anywhere",
|
|
39855
|
+
userSelect: "text"
|
|
39663
39856
|
},
|
|
39664
39857
|
'& [part="md"]': {
|
|
39665
|
-
$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).",
|
|
39666
|
-
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"
|
|
39667
39861
|
},
|
|
39668
39862
|
'& [part="md"] > *:first-child': {
|
|
39669
39863
|
marginTop: "0"
|
|
@@ -39698,8 +39892,10 @@ var ai_chat_default = {
|
|
|
39698
39892
|
padding: "var(--jx-space-1) 6px"
|
|
39699
39893
|
},
|
|
39700
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.',
|
|
39701
39896
|
whiteSpace: "pre-wrap",
|
|
39702
39897
|
overflowWrap: "anywhere",
|
|
39898
|
+
userSelect: "text",
|
|
39703
39899
|
"::after": {
|
|
39704
39900
|
content: '"▋"',
|
|
39705
39901
|
color: "var(--accent)",
|
|
@@ -39875,6 +40071,8 @@ var ai_chat_default = {
|
|
|
39875
40071
|
marginRight: "var(--jx-space-2)"
|
|
39876
40072
|
},
|
|
39877
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",
|
|
39878
40076
|
marginTop: "var(--jx-space-2)",
|
|
39879
40077
|
maxHeight: "40vh",
|
|
39880
40078
|
overflowY: "auto",
|
|
@@ -39930,7 +40128,8 @@ var ai_chat_default = {
|
|
|
39930
40128
|
color: "var(--warning)",
|
|
39931
40129
|
fontStyle: "normal"
|
|
39932
40130
|
},
|
|
39933
|
-
'& [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.",
|
|
39934
40133
|
borderLeft: "2px solid var(--danger)",
|
|
39935
40134
|
borderRadius: "var(--jx-radius-sm)",
|
|
39936
40135
|
background: "var(--hover-bg)",
|
|
@@ -40113,7 +40312,7 @@ var ai_chat_default = {
|
|
|
40113
40312
|
'& [part="tool-chip"][data-outcome="failed"]': {
|
|
40114
40313
|
borderLeftColor: "LinkText"
|
|
40115
40314
|
},
|
|
40116
|
-
'& [part="error"]': {
|
|
40315
|
+
'& [part="error-card"]': {
|
|
40117
40316
|
borderLeftColor: "LinkText"
|
|
40118
40317
|
}
|
|
40119
40318
|
},
|
|
@@ -41072,7 +41271,7 @@ var ai_chat_default = {
|
|
|
41072
41271
|
shown: {
|
|
41073
41272
|
tagName: "div",
|
|
41074
41273
|
attributes: {
|
|
41075
|
-
part: "error",
|
|
41274
|
+
part: "error-card",
|
|
41076
41275
|
role: "alert"
|
|
41077
41276
|
},
|
|
41078
41277
|
children: [
|
|
@@ -42018,8 +42217,13 @@ function watchAssistant() {
|
|
|
42018
42217
|
const last = cs.messages.at(-1);
|
|
42019
42218
|
last?.content;
|
|
42020
42219
|
last?.toolCalls?.length;
|
|
42220
|
+
for (const tc of latestRequest(cs.messages)?.toolCalls ?? []) {
|
|
42221
|
+
tc.result;
|
|
42222
|
+
}
|
|
42021
42223
|
cs.status;
|
|
42022
42224
|
cs.error;
|
|
42225
|
+
assistant.isTurnActive();
|
|
42226
|
+
cs.tokenCount;
|
|
42023
42227
|
activeRegistry();
|
|
42024
42228
|
pendingAsk();
|
|
42025
42229
|
const run = activeImportRun();
|
|
@@ -42030,6 +42234,14 @@ function watchAssistant() {
|
|
|
42030
42234
|
});
|
|
42031
42235
|
});
|
|
42032
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
|
+
}
|
|
42033
42245
|
function mountAiPanel() {
|
|
42034
42246
|
if (mounted) {
|
|
42035
42247
|
return;
|
|
@@ -42038,7 +42250,7 @@ function mountAiPanel() {
|
|
|
42038
42250
|
onCredentialsChanged(renderAiPanel);
|
|
42039
42251
|
}
|
|
42040
42252
|
async function handleAssistantSend(text) {
|
|
42041
|
-
if (!text.trim()
|
|
42253
|
+
if (!text.trim()) {
|
|
42042
42254
|
return;
|
|
42043
42255
|
}
|
|
42044
42256
|
if (answerAsk(text.trim())) {
|
|
@@ -42046,12 +42258,17 @@ async function handleAssistantSend(text) {
|
|
|
42046
42258
|
renderAiPanel();
|
|
42047
42259
|
return;
|
|
42048
42260
|
}
|
|
42261
|
+
if (isAssistantStreaming()) {
|
|
42262
|
+
return;
|
|
42263
|
+
}
|
|
42049
42264
|
view5 = "chat";
|
|
42050
42265
|
surface?.pin();
|
|
42051
42266
|
renderAiPanel();
|
|
42052
42267
|
try {
|
|
42053
42268
|
await assistant.sendMessage(text);
|
|
42054
|
-
} catch {}
|
|
42269
|
+
} catch {} finally {
|
|
42270
|
+
renderAiPanel();
|
|
42271
|
+
}
|
|
42055
42272
|
}
|
|
42056
42273
|
async function handleRetry() {
|
|
42057
42274
|
const cs = assistant.chatState;
|
|
@@ -42085,6 +42302,9 @@ function handleRestore(messageId) {
|
|
|
42085
42302
|
renderAiPanel();
|
|
42086
42303
|
}
|
|
42087
42304
|
async function seedAssistantPrompt(text) {
|
|
42305
|
+
if (assistant.isTurnActive()) {
|
|
42306
|
+
await assistant.whenTurnEnds();
|
|
42307
|
+
}
|
|
42088
42308
|
await handleAssistantSend(text);
|
|
42089
42309
|
}
|
|
42090
42310
|
async function revealImportHandoff(brief) {
|
|
@@ -42143,7 +42363,7 @@ function isAssistantWaiting() {
|
|
|
42143
42363
|
return isAwaitingAnswer();
|
|
42144
42364
|
}
|
|
42145
42365
|
function isAssistantStreaming() {
|
|
42146
|
-
return assistant.
|
|
42366
|
+
return assistant.isTurnActive();
|
|
42147
42367
|
}
|
|
42148
42368
|
function stop() {
|
|
42149
42369
|
assistant.stop();
|
|
@@ -42316,6 +42536,9 @@ function assistantCommands() {
|
|
|
42316
42536
|
];
|
|
42317
42537
|
}
|
|
42318
42538
|
|
|
42539
|
+
// src/hosting/document.ts
|
|
42540
|
+
var BOOT_META = { name: "jx-boot", content: "launcher" };
|
|
42541
|
+
|
|
42319
42542
|
// src/services/import-client.ts
|
|
42320
42543
|
var IMPORT_WARNING_PHASE = "warning";
|
|
42321
42544
|
async function streamImport(endpoint, opts, onProgress, signal, onReady) {
|
|
@@ -42489,7 +42712,7 @@ var STUDIO_ROUTES = {
|
|
|
42489
42712
|
resolveSite: route("GET", "/__studio/resolve-site", "Resolve the owning site of a file path {sitePath, projectConfig?, fileRelPath?}"),
|
|
42490
42713
|
sites: route("GET", "/__studio/sites", "Enumerate site projects [{config, path}] (backs listProjects)", "The welcome screen's Projects catalogue stays hidden."),
|
|
42491
42714
|
findProject: route("GET", "/__studio/find-project", "Locate a project directory by name outside the root", "openProject falls back to config-matching only."),
|
|
42492
|
-
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."),
|
|
42493
42716
|
createProject: route("POST", "/__studio/create-project", "Scaffold a project at a caller-chosen destination → {root, config}"),
|
|
42494
42717
|
starters: route("GET", "/__studio/starters", "Starter templates (StarterInfo[])", "The New Project picker offers only blank/templates."),
|
|
42495
42718
|
importSite: route("POST", "/__studio/import-site", "Clone a live website into a project; streams NDJSON progress", "The New Project Import tab is unavailable."),
|
|
@@ -42498,6 +42721,7 @@ var STUDIO_ROUTES = {
|
|
|
42498
42721
|
fileWrite: route("PUT", "/__studio/file", "Write a file's text content"),
|
|
42499
42722
|
fileDelete: route("DELETE", "/__studio/file", "Delete a file"),
|
|
42500
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."),
|
|
42501
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."),
|
|
42502
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)"),
|
|
42503
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."),
|
|
@@ -42798,6 +43022,13 @@ function createCloudPlatform(project) {
|
|
|
42798
43022
|
const data = await okJson(await api(`/file?path=${encodeURIComponent(path)}`), `Failed to read file: ${path}`);
|
|
42799
43023
|
return data.content;
|
|
42800
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
|
+
},
|
|
42801
43032
|
async writeFile(path, content) {
|
|
42802
43033
|
const res = await api("/file", {
|
|
42803
43034
|
method: "PUT",
|
|
@@ -42848,7 +43079,7 @@ function createCloudPlatform(project) {
|
|
|
42848
43079
|
}
|
|
42849
43080
|
const { offer } = negotiated;
|
|
42850
43081
|
collabConnection ??= (async () => {
|
|
42851
|
-
const { createWsCollabConnection } = await (__preload("
|
|
43082
|
+
const { createWsCollabConnection } = await (__preload("erze4czt"), import("./chunks/ws-client-erze4czt.js"));
|
|
42852
43083
|
const scheme = location.protocol === "https:" ? "wss" : "ws";
|
|
42853
43084
|
return createWsCollabConnection({
|
|
42854
43085
|
hydratePath: async (path) => {
|
|
@@ -43348,32 +43579,50 @@ function newLocationId() {
|
|
|
43348
43579
|
globalThis.crypto.getRandomValues(bytes);
|
|
43349
43580
|
return [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
43350
43581
|
}
|
|
43582
|
+
function nameOf(error) {
|
|
43583
|
+
return typeof error === "object" && error !== null && "name" in error && typeof error.name === "string" ? error.name : "";
|
|
43584
|
+
}
|
|
43351
43585
|
async function pickDirectoryPath(locate) {
|
|
43352
43586
|
const show = globalThis.showDirectoryPicker;
|
|
43353
43587
|
if (typeof show !== "function") {
|
|
43354
|
-
|
|
43588
|
+
throw new TypeError("This browser has no folder chooser. Type the folder's path into Location instead.");
|
|
43355
43589
|
}
|
|
43356
43590
|
let handle;
|
|
43357
43591
|
try {
|
|
43358
43592
|
handle = await show({ id: "jx-new-project-location", mode: "readwrite", startIn: "documents" });
|
|
43359
|
-
} catch {
|
|
43360
|
-
|
|
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 });
|
|
43361
43598
|
}
|
|
43599
|
+
const { name } = handle;
|
|
43362
43600
|
const id = newLocationId();
|
|
43601
|
+
let tagged = false;
|
|
43363
43602
|
try {
|
|
43364
|
-
|
|
43365
|
-
|
|
43366
|
-
|
|
43367
|
-
|
|
43368
|
-
|
|
43369
|
-
|
|
43370
|
-
|
|
43371
|
-
|
|
43372
|
-
|
|
43373
|
-
|
|
43374
|
-
|
|
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;
|
|
43375
43622
|
} finally {
|
|
43376
|
-
|
|
43623
|
+
if (tagged) {
|
|
43624
|
+
await handle.removeEntry(LOCATION_ID_FILE).catch(() => {});
|
|
43625
|
+
}
|
|
43377
43626
|
}
|
|
43378
43627
|
}
|
|
43379
43628
|
|
|
@@ -43509,7 +43758,8 @@ function createDevServerPlatform() {
|
|
|
43509
43758
|
return pickDirectoryPath(async ({ id, name }) => {
|
|
43510
43759
|
const res = await fetch(`/__studio/locate-directory?name=${encodeURIComponent(name)}&id=${encodeURIComponent(id)}`);
|
|
43511
43760
|
if (!res.ok) {
|
|
43512
|
-
|
|
43761
|
+
const body = await res.json().catch(() => null);
|
|
43762
|
+
throw new Error(problemMessage(body, res.status));
|
|
43513
43763
|
}
|
|
43514
43764
|
const found = await readJson2(res);
|
|
43515
43765
|
return found.path ?? null;
|
|
@@ -43545,6 +43795,13 @@ function createDevServerPlatform() {
|
|
|
43545
43795
|
const data = await readJson2(res);
|
|
43546
43796
|
return data.content;
|
|
43547
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
|
+
},
|
|
43548
43805
|
async writeFile(path, content) {
|
|
43549
43806
|
const res = await fetch(`/__studio/file?path=${encodeURIComponent(serverPath(path))}`, {
|
|
43550
43807
|
body: content,
|
|
@@ -43644,7 +43901,7 @@ function createDevServerPlatform() {
|
|
|
43644
43901
|
}
|
|
43645
43902
|
const { offer } = negotiation;
|
|
43646
43903
|
_collabConnection ??= (async () => {
|
|
43647
|
-
const { createWsCollabConnection } = await (__preload("
|
|
43904
|
+
const { createWsCollabConnection } = await (__preload("erze4czt"), import("./chunks/ws-client-erze4czt.js"));
|
|
43648
43905
|
const scheme = location.protocol === "https:" ? "wss" : "ws";
|
|
43649
43906
|
return createWsCollabConnection({
|
|
43650
43907
|
protocols: offer,
|
|
@@ -44166,7 +44423,7 @@ function createDevServerPlatform() {
|
|
|
44166
44423
|
}
|
|
44167
44424
|
},
|
|
44168
44425
|
async cfApi(apiPath, init) {
|
|
44169
|
-
const { getCfToken } = await (__preload("
|
|
44426
|
+
const { getCfToken } = await (__preload("m79r7p5y"), import("./chunks/cf-settings-m79r7p5y.js"));
|
|
44170
44427
|
const token = getCfToken();
|
|
44171
44428
|
if (!token) {
|
|
44172
44429
|
throw new Error("No Cloudflare API token configured");
|
|
@@ -44184,7 +44441,7 @@ function createDevServerPlatform() {
|
|
|
44184
44441
|
return envelope.result ?? envelope;
|
|
44185
44442
|
},
|
|
44186
44443
|
async cfConnection() {
|
|
44187
|
-
const { getCfAccountId, getCfToken, setCfAccountId } = await (__preload("
|
|
44444
|
+
const { getCfAccountId, getCfToken, setCfAccountId } = await (__preload("m79r7p5y"), import("./chunks/cf-settings-m79r7p5y.js"));
|
|
44188
44445
|
if (!getCfToken()) {
|
|
44189
44446
|
return null;
|
|
44190
44447
|
}
|
|
@@ -44222,7 +44479,59 @@ function createDevServerPlatform() {
|
|
|
44222
44479
|
}
|
|
44223
44480
|
|
|
44224
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
|
+
}
|
|
44225
44530
|
function resolveDefaultPlatform() {
|
|
44531
|
+
const refusal = bootRefusal();
|
|
44532
|
+
if (refusal) {
|
|
44533
|
+
throw new PlatformUnavailableError(refusal);
|
|
44534
|
+
}
|
|
44226
44535
|
const cloud = globalThis.__jxCloud;
|
|
44227
44536
|
return cloud ? createCloudPlatform(cloud.project) : createDevServerPlatform();
|
|
44228
44537
|
}
|
|
@@ -45934,6 +46243,7 @@ function capabilities(platform) {
|
|
|
45934
46243
|
gitClone: has("gitClone"),
|
|
45935
46244
|
importSite: has("importSite"),
|
|
45936
46245
|
openProjectInNewWindow: has("openProjectInNewWindow"),
|
|
46246
|
+
readFileBytes: has("readFileBytes"),
|
|
45937
46247
|
windowControls: has("newWindow")
|
|
45938
46248
|
};
|
|
45939
46249
|
}
|
|
@@ -46103,7 +46413,7 @@ function openSettingsMenu(anchor, opts = {}) {
|
|
|
46103
46413
|
var rail_default = {
|
|
46104
46414
|
$schema: "https://jxsuite.com/schema/v1",
|
|
46105
46415
|
$id: "StudioRail",
|
|
46106
|
-
$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.",
|
|
46107
46417
|
tagName: "div",
|
|
46108
46418
|
attributes: {
|
|
46109
46419
|
part: "rail"
|
|
@@ -46247,7 +46557,7 @@ var rail_default = {
|
|
|
46247
46557
|
children: [
|
|
46248
46558
|
{
|
|
46249
46559
|
tagName: "span",
|
|
46250
|
-
textContent: "${$map.item.
|
|
46560
|
+
textContent: "${$map.item.railLabel}"
|
|
46251
46561
|
}
|
|
46252
46562
|
]
|
|
46253
46563
|
}
|
|
@@ -46378,6 +46688,7 @@ function project17() {
|
|
|
46378
46688
|
badge: String(panel.badge?.(ctx) ?? ""),
|
|
46379
46689
|
icon: panel.icon,
|
|
46380
46690
|
id: panel.id,
|
|
46691
|
+
railLabel: panel.railLabel ?? panel.title,
|
|
46381
46692
|
selected: isRailPanelShowing(panel),
|
|
46382
46693
|
title: panel.title
|
|
46383
46694
|
}))
|
|
@@ -48599,7 +48910,7 @@ function installUrlOf(error) {
|
|
|
48599
48910
|
var new_project_default = {
|
|
48600
48911
|
$schema: "https://jxsuite.com/schema/v1",
|
|
48601
48912
|
$id: "StudioNewProject",
|
|
48602
|
-
$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).",
|
|
48603
48914
|
tagName: "jx-dialog",
|
|
48604
48915
|
$props: {
|
|
48605
48916
|
headline: {
|
|
@@ -50504,6 +50815,8 @@ async function browseLocation(rerender) {
|
|
|
50504
50815
|
_parent = picked;
|
|
50505
50816
|
_error2 = "";
|
|
50506
50817
|
}
|
|
50818
|
+
} catch (error) {
|
|
50819
|
+
_error2 = errorMessage2(error) || "The folder could not be chosen. Type its path into Location instead.";
|
|
50507
50820
|
} finally {
|
|
50508
50821
|
_browsing = false;
|
|
50509
50822
|
rerender();
|
|
@@ -50884,7 +51197,15 @@ function finish(result) {
|
|
|
50884
51197
|
settle3(result);
|
|
50885
51198
|
}
|
|
50886
51199
|
async function finishCreated(result) {
|
|
50887
|
-
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
|
+
}
|
|
50888
51209
|
finish(result);
|
|
50889
51210
|
}
|
|
50890
51211
|
function deriveSlug(name) {
|
|
@@ -51354,7 +51675,7 @@ var seo_default = {
|
|
|
51354
51675
|
}
|
|
51355
51676
|
},
|
|
51356
51677
|
'& [part="chip"]': {
|
|
51357
|
-
$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.",
|
|
51358
51679
|
flexShrink: "0"
|
|
51359
51680
|
},
|
|
51360
51681
|
'& jx-dot[part="chip"][data-state="set"]': {
|
|
@@ -54287,8 +54608,8 @@ var settings_css_vars_default = {
|
|
|
54287
54608
|
fontWeight: "600",
|
|
54288
54609
|
color: "var(--fg)"
|
|
54289
54610
|
},
|
|
54290
|
-
'& [part="error"]': {
|
|
54291
|
-
$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.',
|
|
54292
54613
|
margin: "0 0 12px",
|
|
54293
54614
|
fontSize: "var(--jx-text-md)",
|
|
54294
54615
|
color: "var(--danger)"
|
|
@@ -54444,7 +54765,7 @@ var settings_css_vars_default = {
|
|
|
54444
54765
|
{
|
|
54445
54766
|
tagName: "div",
|
|
54446
54767
|
attributes: {
|
|
54447
|
-
part: "error-slot",
|
|
54768
|
+
part: "section-error-slot",
|
|
54448
54769
|
role: "none"
|
|
54449
54770
|
},
|
|
54450
54771
|
$switch: {
|
|
@@ -54454,7 +54775,7 @@ var settings_css_vars_default = {
|
|
|
54454
54775
|
true: {
|
|
54455
54776
|
tagName: "p",
|
|
54456
54777
|
attributes: {
|
|
54457
|
-
part: "error",
|
|
54778
|
+
part: "section-error",
|
|
54458
54779
|
role: "alert"
|
|
54459
54780
|
},
|
|
54460
54781
|
textContent: "${state.error}"
|
|
@@ -57934,8 +58255,8 @@ var settings_extensions_default = {
|
|
|
57934
58255
|
fontSize: "var(--jx-text-md)",
|
|
57935
58256
|
color: "var(--fg-dim)"
|
|
57936
58257
|
},
|
|
57937
|
-
'& [part="error"]': {
|
|
57938
|
-
$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.',
|
|
57939
58260
|
margin: "6px 0 0",
|
|
57940
58261
|
fontSize: "var(--jx-text-md)",
|
|
57941
58262
|
color: "var(--danger)"
|
|
@@ -57952,7 +58273,7 @@ var settings_extensions_default = {
|
|
|
57952
58273
|
color: "var(--fg)"
|
|
57953
58274
|
},
|
|
57954
58275
|
'& [part="row"]': {
|
|
57955
|
-
$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.",
|
|
57956
58277
|
display: "flex",
|
|
57957
58278
|
flexWrap: "wrap",
|
|
57958
58279
|
alignItems: "flex-start",
|
|
@@ -58028,7 +58349,7 @@ var settings_extensions_default = {
|
|
|
58028
58349
|
flexShrink: "0"
|
|
58029
58350
|
},
|
|
58030
58351
|
'& [part="empty"]': {
|
|
58031
|
-
$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.',
|
|
58032
58353
|
display: "flex",
|
|
58033
58354
|
flexDirection: "column",
|
|
58034
58355
|
alignItems: "flex-start",
|
|
@@ -58065,7 +58386,7 @@ var settings_extensions_default = {
|
|
|
58065
58386
|
{
|
|
58066
58387
|
tagName: "div",
|
|
58067
58388
|
attributes: {
|
|
58068
|
-
part: "error-slot",
|
|
58389
|
+
part: "section-error-slot",
|
|
58069
58390
|
role: "none"
|
|
58070
58391
|
},
|
|
58071
58392
|
style: {
|
|
@@ -58078,7 +58399,7 @@ var settings_extensions_default = {
|
|
|
58078
58399
|
true: {
|
|
58079
58400
|
tagName: "p",
|
|
58080
58401
|
attributes: {
|
|
58081
|
-
part: "error",
|
|
58402
|
+
part: "section-error",
|
|
58082
58403
|
role: "alert"
|
|
58083
58404
|
},
|
|
58084
58405
|
textContent: "${state.error}"
|
|
@@ -58620,8 +58941,8 @@ var settings_deploy_default = {
|
|
|
58620
58941
|
fontWeight: "600",
|
|
58621
58942
|
color: "var(--fg)"
|
|
58622
58943
|
},
|
|
58623
|
-
'& [part="error"]': {
|
|
58624
|
-
$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.',
|
|
58625
58946
|
margin: "0 0 16px",
|
|
58626
58947
|
fontSize: "var(--jx-text-md)",
|
|
58627
58948
|
color: "var(--danger)"
|
|
@@ -58642,7 +58963,7 @@ var settings_deploy_default = {
|
|
|
58642
58963
|
{
|
|
58643
58964
|
tagName: "div",
|
|
58644
58965
|
attributes: {
|
|
58645
|
-
part: "error-slot",
|
|
58966
|
+
part: "section-error-slot",
|
|
58646
58967
|
role: "none"
|
|
58647
58968
|
},
|
|
58648
58969
|
style: {
|
|
@@ -58655,7 +58976,7 @@ var settings_deploy_default = {
|
|
|
58655
58976
|
true: {
|
|
58656
58977
|
tagName: "p",
|
|
58657
58978
|
attributes: {
|
|
58658
|
-
part: "error",
|
|
58979
|
+
part: "section-error",
|
|
58659
58980
|
role: "alert"
|
|
58660
58981
|
},
|
|
58661
58982
|
textContent: "${state.error}"
|
|
@@ -60616,8 +60937,8 @@ var settings_locales_default = {
|
|
|
60616
60937
|
fontSize: "var(--jx-text-md)",
|
|
60617
60938
|
color: "var(--fg-dim)"
|
|
60618
60939
|
},
|
|
60619
|
-
'& [part="error"]': {
|
|
60620
|
-
$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.",
|
|
60621
60942
|
margin: "6px 0 0",
|
|
60622
60943
|
fontSize: "var(--jx-text-md)",
|
|
60623
60944
|
color: "var(--danger)"
|
|
@@ -60691,7 +61012,7 @@ var settings_locales_default = {
|
|
|
60691
61012
|
{
|
|
60692
61013
|
tagName: "div",
|
|
60693
61014
|
attributes: {
|
|
60694
|
-
part: "error-slot",
|
|
61015
|
+
part: "section-error-slot",
|
|
60695
61016
|
role: "none"
|
|
60696
61017
|
},
|
|
60697
61018
|
$switch: {
|
|
@@ -60701,7 +61022,7 @@ var settings_locales_default = {
|
|
|
60701
61022
|
true: {
|
|
60702
61023
|
tagName: "p",
|
|
60703
61024
|
attributes: {
|
|
60704
|
-
part: "error",
|
|
61025
|
+
part: "section-error",
|
|
60705
61026
|
role: "alert"
|
|
60706
61027
|
},
|
|
60707
61028
|
textContent: "${state.error}"
|
|
@@ -61297,7 +61618,7 @@ async function openProjectSettings(section) {
|
|
|
61297
61618
|
showSettingsDocument();
|
|
61298
61619
|
notifySettingsDocument2();
|
|
61299
61620
|
try {
|
|
61300
|
-
const { syncExtensionSettingsSections } = await (__preload("
|
|
61621
|
+
const { syncExtensionSettingsSections } = await (__preload("r73an5s6"), import("./chunks/extension-sections-r73an5s6.js"));
|
|
61301
61622
|
await syncExtensionSettingsSections();
|
|
61302
61623
|
} catch (error) {
|
|
61303
61624
|
notify2.warn(`Could not load extension settings sections — ${errorMessage2(error)}`, {
|
|
@@ -61434,9 +61755,11 @@ var about_default = {
|
|
|
61434
61755
|
color: "var(--fg-dim)"
|
|
61435
61756
|
},
|
|
61436
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.",
|
|
61437
61759
|
margin: "0",
|
|
61438
61760
|
color: "var(--fg)",
|
|
61439
|
-
wordBreak: "break-word"
|
|
61761
|
+
wordBreak: "break-word",
|
|
61762
|
+
userSelect: "text"
|
|
61440
61763
|
},
|
|
61441
61764
|
'& [part="links"]': {
|
|
61442
61765
|
display: "flex",
|
|
@@ -61474,10 +61797,12 @@ var about_default = {
|
|
|
61474
61797
|
gap: "4px"
|
|
61475
61798
|
},
|
|
61476
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.",
|
|
61477
61801
|
display: "flex",
|
|
61478
61802
|
justifyContent: "space-between",
|
|
61479
61803
|
gap: "12px",
|
|
61480
|
-
fontSize: "var(--jx-text-md)"
|
|
61804
|
+
fontSize: "var(--jx-text-md)",
|
|
61805
|
+
userSelect: "text"
|
|
61481
61806
|
},
|
|
61482
61807
|
'& [part="package-name"]': {
|
|
61483
61808
|
color: "var(--fg-dim)"
|
|
@@ -61707,9 +62032,9 @@ function openAboutSurface(options) {
|
|
|
61707
62032
|
|
|
61708
62033
|
// src/version.ts
|
|
61709
62034
|
var APP_NAME = "Jx Studio";
|
|
61710
|
-
var VERSION = "5.
|
|
61711
|
-
var BUILD_DATE = "2026-09-
|
|
61712
|
-
var GIT_COMMIT = "
|
|
62035
|
+
var VERSION = "5.4.0";
|
|
62036
|
+
var BUILD_DATE = "2026-09-29T19:41:26.105Z";
|
|
62037
|
+
var GIT_COMMIT = "fd18dbe";
|
|
61713
62038
|
var LINKS = {
|
|
61714
62039
|
github: "https://github.com/jxsuite/jx",
|
|
61715
62040
|
docs: "https://github.com/jxsuite/jx#readme",
|
|
@@ -62117,9 +62442,9 @@ function withExtension(path, ext) {
|
|
|
62117
62442
|
return current === "" ? `${path}${ext}` : `${path.slice(0, path.length - current.length)}${ext}`;
|
|
62118
62443
|
}
|
|
62119
62444
|
async function readDocument(path, source) {
|
|
62120
|
-
const { formatForPath } = await (__preload("
|
|
62445
|
+
const { formatForPath } = await (__preload("mym8qnd0"), import("./chunks/format-host-mym8qnd0.js"));
|
|
62121
62446
|
if (formatForPath(path)) {
|
|
62122
|
-
const { parseSourceForPath } = await (__preload("
|
|
62447
|
+
const { parseSourceForPath } = await (__preload("3xan8mnj"), import("./chunks/file-ops-3xan8mnj.js"));
|
|
62123
62448
|
const parsed = await parseSourceForPath(path, source);
|
|
62124
62449
|
return { document: parsed.document, frontmatter: parsed.frontmatter };
|
|
62125
62450
|
}
|
|
@@ -62130,7 +62455,7 @@ async function writeDocument(targetExt, document2, frontmatter) {
|
|
|
62130
62455
|
if (targetExt === ".json") {
|
|
62131
62456
|
return serializeJson(merged, null);
|
|
62132
62457
|
}
|
|
62133
|
-
const { formatByExtension, formatSerialize } = await (__preload("
|
|
62458
|
+
const { formatByExtension, formatSerialize } = await (__preload("mym8qnd0"), import("./chunks/format-host-mym8qnd0.js"));
|
|
62134
62459
|
const format = formatByExtension(targetExt, "serialize");
|
|
62135
62460
|
if (!format) {
|
|
62136
62461
|
throw new Error(`No installed format writes ${targetExt} files.`);
|
|
@@ -62185,7 +62510,7 @@ async function buildPlan(path, source, targetExt) {
|
|
|
62185
62510
|
if (!await readsBackIdentically(targetPath, text)) {
|
|
62186
62511
|
lines.push("This file will not read back identically after conversion.");
|
|
62187
62512
|
}
|
|
62188
|
-
const { loadUsages, usageWarning } = await (__preload("
|
|
62513
|
+
const { loadUsages, usageWarning } = await (__preload("f7bdv3z5"), import("./chunks/references-f7bdv3z5.js"));
|
|
62189
62514
|
const sentence = usageWarning(await loadUsages({ path }), "convert");
|
|
62190
62515
|
if (sentence !== null) {
|
|
62191
62516
|
lines.push(sentence);
|
|
@@ -62200,14 +62525,14 @@ async function convertFile(path, targetExt) {
|
|
|
62200
62525
|
notify.error(message, { detail, path, source: "Convert" });
|
|
62201
62526
|
return null;
|
|
62202
62527
|
};
|
|
62203
|
-
const { loadFormats } = await (__preload("
|
|
62528
|
+
const { loadFormats } = await (__preload("mym8qnd0"), import("./chunks/format-host-mym8qnd0.js"));
|
|
62204
62529
|
await loadFormats();
|
|
62205
62530
|
const targets = convertTargets(path);
|
|
62206
62531
|
if (targets.length === 0) {
|
|
62207
62532
|
return fail(`${path} cannot be converted to another format.`, CANNOT_CONVERT);
|
|
62208
62533
|
}
|
|
62209
|
-
const { workspace } = await (__preload("
|
|
62210
|
-
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"));
|
|
62211
62536
|
const openTabEntry = [...workspace.tabs.values()].find((tab) => tab.documentPath === path);
|
|
62212
62537
|
if (openTabEntry) {
|
|
62213
62538
|
await commitTabBuffers(openTabEntry);
|
|
@@ -62222,7 +62547,7 @@ async function convertFile(path, targetExt) {
|
|
|
62222
62547
|
if (!targets.some((target) => target.ext === chosen)) {
|
|
62223
62548
|
return fail(`${path} cannot be converted to ${chosen}.`, CANNOT_CONVERT);
|
|
62224
62549
|
}
|
|
62225
|
-
const { getPlatform } = await (__preload("
|
|
62550
|
+
const { getPlatform } = await (__preload("fnchmhy7"), import("./chunks/platform-fnchmhy7.js"));
|
|
62226
62551
|
const platform = getPlatform();
|
|
62227
62552
|
const targetPath = withExtension(path, chosen);
|
|
62228
62553
|
try {
|
|
@@ -62244,7 +62569,7 @@ async function convertFile(path, targetExt) {
|
|
|
62244
62569
|
if (plan.blocker !== null) {
|
|
62245
62570
|
return fail(`${path} cannot be converted to ${chosen}.`, plan.blocker);
|
|
62246
62571
|
}
|
|
62247
|
-
const { showConfirmDialog } = await (__preload("
|
|
62572
|
+
const { showConfirmDialog } = await (__preload("s6mc01ry"), import("./chunks/layers-s6mc01ry.js"));
|
|
62248
62573
|
const { html } = await (__preload("m244w9nh"), import("./chunks/lit-html-m244w9nh.js"));
|
|
62249
62574
|
const confirmed = await showConfirmDialog("Convert Format", html`<span
|
|
62250
62575
|
>Convert <strong>${path.split("/").pop()}</strong> to
|
|
@@ -62253,8 +62578,8 @@ async function convertFile(path, targetExt) {
|
|
|
62253
62578
|
if (!confirmed) {
|
|
62254
62579
|
return null;
|
|
62255
62580
|
}
|
|
62256
|
-
const { markLocalMutation } = await (__preload("
|
|
62257
|
-
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"));
|
|
62258
62583
|
markLocalMutation(path, plan.targetPath);
|
|
62259
62584
|
try {
|
|
62260
62585
|
await platform.writeFile(path, plan.text);
|
|
@@ -62279,8 +62604,8 @@ async function convertFile(path, targetExt) {
|
|
|
62279
62604
|
return plan.targetPath;
|
|
62280
62605
|
}
|
|
62281
62606
|
async function rebuildTab(from, to, text) {
|
|
62282
|
-
const { openTab, renameTab } = await (__preload("
|
|
62283
|
-
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"));
|
|
62284
62609
|
renameTab(from, to, to);
|
|
62285
62610
|
const format = formatForPath(to);
|
|
62286
62611
|
let document2;
|
|
@@ -62288,7 +62613,7 @@ async function rebuildTab(from, to, text) {
|
|
|
62288
62613
|
let layout = null;
|
|
62289
62614
|
try {
|
|
62290
62615
|
if (format) {
|
|
62291
|
-
const { parseSourceForPath } = await (__preload("
|
|
62616
|
+
const { parseSourceForPath } = await (__preload("3xan8mnj"), import("./chunks/file-ops-3xan8mnj.js"));
|
|
62292
62617
|
({ document: document2, frontmatter } = await parseSourceForPath(to, text));
|
|
62293
62618
|
} else {
|
|
62294
62619
|
({ document: document2, layout } = parseJsonDocument(text));
|
|
@@ -62317,7 +62642,7 @@ async function pickTarget(path, targets) {
|
|
|
62317
62642
|
if (targets.length === 1) {
|
|
62318
62643
|
return targets[0].ext;
|
|
62319
62644
|
}
|
|
62320
|
-
const { showPromptDialog } = await (__preload("
|
|
62645
|
+
const { showPromptDialog } = await (__preload("s6mc01ry"), import("./chunks/layers-s6mc01ry.js"));
|
|
62321
62646
|
let picked = targets[0].ext;
|
|
62322
62647
|
const answer = await showPromptDialog("Convert Format", {
|
|
62323
62648
|
choice: {
|
|
@@ -62442,7 +62767,7 @@ function publishCommands() {
|
|
|
62442
62767
|
when: (ctx) => ctx.project.open,
|
|
62443
62768
|
enablement: () => platformSupportsPublish(),
|
|
62444
62769
|
run: async () => {
|
|
62445
|
-
const { openPublishPanel } = await (__preload("
|
|
62770
|
+
const { openPublishPanel } = await (__preload("ak6hxfma"), import("./chunks/publish-panel-ak6hxfma.js"));
|
|
62446
62771
|
openPublishPanel();
|
|
62447
62772
|
},
|
|
62448
62773
|
title: "Set Up Publishing"
|
|
@@ -63143,7 +63468,7 @@ function i18nCommands() {
|
|
|
63143
63468
|
});
|
|
63144
63469
|
return;
|
|
63145
63470
|
}
|
|
63146
|
-
const { openFileInTab } = await (__preload("
|
|
63471
|
+
const { openFileInTab } = await (__preload("egsb0ry8"), import("./chunks/files-egsb0ry8.js"));
|
|
63147
63472
|
await openFileInTab(path);
|
|
63148
63473
|
},
|
|
63149
63474
|
title: "Open Translation"
|
|
@@ -63188,7 +63513,7 @@ function i18nCommands() {
|
|
|
63188
63513
|
return;
|
|
63189
63514
|
}
|
|
63190
63515
|
const cut = path.lastIndexOf("/");
|
|
63191
|
-
const { createFileIn, openFileInTab } = await (__preload("
|
|
63516
|
+
const { createFileIn, openFileInTab } = await (__preload("egsb0ry8"), import("./chunks/files-egsb0ry8.js"));
|
|
63192
63517
|
const created = await createFileIn({
|
|
63193
63518
|
content,
|
|
63194
63519
|
dir: cut === -1 ? "." : path.slice(0, cut),
|
|
@@ -63359,6 +63684,293 @@ registerSurface("shell", shell_default);
|
|
|
63359
63684
|
function mountShellSurface(host) {
|
|
63360
63685
|
return mountSurface("shell", {}, host);
|
|
63361
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
|
+
}
|
|
63362
63974
|
|
|
63363
63975
|
// src/shell/tree.ts
|
|
63364
63976
|
function overlayLayers() {
|
|
@@ -63393,6 +64005,12 @@ async function mountShellTree(host = document.body) {
|
|
|
63393
64005
|
await mountShellSurface(root);
|
|
63394
64006
|
render4(overlayLayers(), root);
|
|
63395
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
|
+
}
|
|
63396
64014
|
function shellRoot(host) {
|
|
63397
64015
|
const existing = host.querySelector?.("#shell-root");
|
|
63398
64016
|
if (existing) {
|
|
@@ -63444,7 +64062,14 @@ async function navigateToComponent(componentPath) {
|
|
|
63444
64062
|
}
|
|
63445
64063
|
initCssData(webdata_default);
|
|
63446
64064
|
if (!hasPlatform2()) {
|
|
63447
|
-
|
|
64065
|
+
try {
|
|
64066
|
+
registerPlatform2(resolveDefaultPlatform());
|
|
64067
|
+
} catch (error) {
|
|
64068
|
+
if (error instanceof PlatformUnavailableError) {
|
|
64069
|
+
await mountBootFailureTree(error.refusal);
|
|
64070
|
+
}
|
|
64071
|
+
throw error;
|
|
64072
|
+
}
|
|
63448
64073
|
}
|
|
63449
64074
|
mountResizeEdges();
|
|
63450
64075
|
registerKit();
|
|
@@ -63471,6 +64096,7 @@ mount5(primaryCell?.chrome ?? document.createElement("div"), {
|
|
|
63471
64096
|
parseMediaEntries,
|
|
63472
64097
|
setCanvasMode
|
|
63473
64098
|
});
|
|
64099
|
+
setDiffChromeRepaint(render7);
|
|
63474
64100
|
mount4({
|
|
63475
64101
|
isEditing,
|
|
63476
64102
|
renderBlockActionBar
|
|
@@ -63552,11 +64178,8 @@ initWelcome({
|
|
|
63552
64178
|
}
|
|
63553
64179
|
},
|
|
63554
64180
|
cloneRepository: () => cloneRepository2({ openRecentProject }),
|
|
63555
|
-
openNewProject:
|
|
63556
|
-
|
|
63557
|
-
if (result) {
|
|
63558
|
-
openRecentProject(result.root);
|
|
63559
|
-
}
|
|
64181
|
+
openNewProject: (options) => {
|
|
64182
|
+
openNewProjectModal(options);
|
|
63560
64183
|
},
|
|
63561
64184
|
openProject: () => openProject(),
|
|
63562
64185
|
openRecentProject: (root) => openRecentProject(root)
|
|
@@ -64084,5 +64707,5 @@ export {
|
|
|
64084
64707
|
hasUnsavedTabs
|
|
64085
64708
|
};
|
|
64086
64709
|
|
|
64087
|
-
//# debugId=
|
|
64710
|
+
//# debugId=17BBB037A446E70F64756E2164756E21
|
|
64088
64711
|
//# sourceMappingURL=studio.js.map
|