@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
|
@@ -9,19 +9,19 @@
|
|
|
9
9
|
* different command owned by a different module. Nothing in Studio held the chain, so no surface
|
|
10
10
|
* could say which link was missing.
|
|
11
11
|
*
|
|
12
|
-
* This module is the chain (
|
|
12
|
+
* This module is the chain (`docs/studio/publish.md`):
|
|
13
13
|
*
|
|
14
14
|
* - {@link deployChecklist} is the ordered prerequisite list, each step carrying **the command that
|
|
15
15
|
* satisfies it** rather than a sentence telling the reader to go and find one;
|
|
16
16
|
* - {@link deployChecklistView} is its projection and {@link syncDeployChecklist} mounts the document
|
|
17
17
|
* that draws it (`surfaces/panel-deploy-checklist.json`), in the Bottom dock's **Activity** tab
|
|
18
|
-
* because a deploy is a long operation with a log and the dock cap is four
|
|
19
|
-
* draws in the Activity tab's vocabulary — the same row,
|
|
20
|
-
* — for the same reason: a fifth visual idiom for "an
|
|
21
|
-
* be a second design of a thing the tab already has;
|
|
18
|
+
* because a deploy is a long operation with a log and the dock cap is four
|
|
19
|
+
* (`studio-ui-guidelines.md` §12.2). It draws in the Activity tab's vocabulary — the same row,
|
|
20
|
+
* the same ordered steps, the same glyphs — for the same reason: a fifth visual idiom for "an
|
|
21
|
+
* ordered list of stages that finish" would be a second design of a thing the tab already has;
|
|
22
22
|
* - {@link deployStatusItem} is the status bar's project-field item, whose **label is the next
|
|
23
23
|
* blocking prerequisite** — the shortcut and the explanation in one 24px item, and ambient state
|
|
24
|
-
* rather than a transient message, which is the only thing that bar carries (§
|
|
24
|
+
* rather than a transient message, which is the only thing that bar carries (§16.2).
|
|
25
25
|
*
|
|
26
26
|
* **A step it cannot answer says `unknown`.** `services/references.ts` established the rule and it
|
|
27
27
|
* matters more here: "no deployment" and "we have not asked Cloudflare" look identical from inside
|
|
@@ -81,8 +81,9 @@ function currentConfig(): ProjectConfig | null {
|
|
|
81
81
|
/**
|
|
82
82
|
* The connected provider block, or `undefined`.
|
|
83
83
|
*
|
|
84
|
-
* Exported because the Publish panel asks the same question and
|
|
85
|
-
* one derivation drift apart in silence — there is one reader of
|
|
84
|
+
* Exported because the Publish panel asks the same question and the shell redesign's configuration
|
|
85
|
+
* phase taught that two copies of one derivation drift apart in silence — there is one reader of
|
|
86
|
+
* `build.deploy` in the app.
|
|
86
87
|
*/
|
|
87
88
|
export function currentDeploy(): DeployConfig | undefined {
|
|
88
89
|
return currentConfig()?.build?.deploy;
|
|
@@ -336,7 +337,7 @@ function stepView(step: DeployStep): DeployStepRowView {
|
|
|
336
337
|
*
|
|
337
338
|
* A command the registry does not have, or whose `when` is false, draws NOTHING rather than a dead
|
|
338
339
|
* label. The status bar's projection takes the same position for the same reason: a surface may
|
|
339
|
-
* choose whether to show a command, never whether it exists (§12.
|
|
340
|
+
* choose whether to show a command, never whether it exists (`studio-ui-guidelines.md` §12.3).
|
|
340
341
|
*/
|
|
341
342
|
function actionView(
|
|
342
343
|
step: DeployStep | null,
|
|
@@ -415,7 +416,7 @@ const CHECKLIST_SURFACE = { project: deployChecklistView, runAction: runNextStep
|
|
|
415
416
|
* Rendered ABOVE the operation feed rather than as an entry in it: an activity is something that
|
|
416
417
|
* happened, and this is something that has not. It carries no `data-jx-region` stamp — the region
|
|
417
418
|
* is `dock.bottom/panel:activity`, which the dock derives, and a hand-stamped leaf is a committed
|
|
418
|
-
* budget (
|
|
419
|
+
* budget (`scripts/screenshots/README.md`, "The region grammar") that this does not need to spend.
|
|
419
420
|
*
|
|
420
421
|
* @param host The dock body element the tab was rendered into.
|
|
421
422
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Publish-commands.ts — the `Publish:` family (
|
|
2
|
+
* Publish-commands.ts — the `Publish:` family (`docs/studio/publish.md`), defined beside the deploy
|
|
3
|
+
* it drives.
|
|
3
4
|
*
|
|
4
5
|
* Two unrelated operations were called "Publish" and neither was a command. `publishToGithub`
|
|
5
6
|
* created a git repository; `openPublishPanel` connected Cloudflare Pages — and it had **zero call
|
|
@@ -16,7 +17,7 @@
|
|
|
16
17
|
* | `publish.openDashboard` | opens the connected Pages project on Cloudflare |
|
|
17
18
|
*
|
|
18
19
|
* **`runDeploy` is an Activity, not a toast.** A deploy pushes a branch and then waits on a build
|
|
19
|
-
* that is happening somewhere else; that is the definition of the long operation §
|
|
20
|
+
* that is happening somewhere else; that is the definition of the long operation §16.4 gave the
|
|
20
21
|
* Activity tab, and `fail()` raises the Problem so the failure outlives the run and carries a
|
|
21
22
|
* Retry. The caller therefore never also notifies (§16).
|
|
22
23
|
*
|
|
@@ -42,7 +43,7 @@ import type { AnyCommand, CommandRegistry } from "../commands/registry";
|
|
|
42
43
|
* Six attempts five seconds apart: a Pages build takes minutes, so this is not "wait for the build"
|
|
43
44
|
* — it is "wait for the deployment RECORD", which appears within seconds of the push. The activity
|
|
44
45
|
* says which of the two it is waiting for, because a progress line that implies the former and
|
|
45
|
-
* delivers the latter is the dishonesty §
|
|
46
|
+
* delivers the latter is the dishonesty §16.4 exists to end.
|
|
46
47
|
*/
|
|
47
48
|
export const DEPLOY_POLL = { attempts: 6, delayMs: 5000 } as const;
|
|
48
49
|
|
|
@@ -135,7 +136,7 @@ export async function runDeploy(
|
|
|
135
136
|
*
|
|
136
137
|
* `enablement` reads `platformSupportsPublish()` rather than a `capability.*` key because there is
|
|
137
138
|
* no `publish` capability yet — adding one is a `commands/context.ts` + `commands/live-context.ts`
|
|
138
|
-
* change, and §
|
|
139
|
+
* change, and §13.4 is explicit that PAL differences belong there. Until it exists this is the one
|
|
139
140
|
* `if (platform.x)` the family carries, in the one place a reader looks for it.
|
|
140
141
|
*/
|
|
141
142
|
export function publishCommands(): AnyCommand[] {
|
|
@@ -157,8 +158,8 @@ export function publishCommands(): AnyCommand[] {
|
|
|
157
158
|
when: (ctx) => ctx.project.open,
|
|
158
159
|
// `when` already asked about the project.
|
|
159
160
|
enablement: () => platformSupportsPublish(),
|
|
160
|
-
/* No `aiTool`, by §12.4's second
|
|
161
|
-
credentials. */
|
|
161
|
+
/* No `aiTool`, by `studio-ui-guidelines.md` §12.4's second rule: an auth flow that waits on a
|
|
162
|
+
person, with credentials. */
|
|
162
163
|
run: async () => {
|
|
163
164
|
const { openPublishPanel } = await import("./publish-panel");
|
|
164
165
|
openPublishPanel();
|
|
@@ -177,8 +178,8 @@ export function publishCommands(): AnyCommand[] {
|
|
|
177
178
|
when: (ctx) => ctx.project.open,
|
|
178
179
|
enablement: (ctx) =>
|
|
179
180
|
ctx.project.isRepo && currentDeploy() !== undefined && platformSupportsPublish(),
|
|
180
|
-
/* No `aiTool`, by §12.4's third
|
|
181
|
-
`packages.remove`. */
|
|
181
|
+
/* No `aiTool`, by `studio-ui-guidelines.md` §12.4's third rule: remote and irreversible, the
|
|
182
|
+
same class as `packages.remove`. */
|
|
182
183
|
run: async () => {
|
|
183
184
|
await runDeploy();
|
|
184
185
|
},
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* alt, an aria reference to an id nothing has, a tab, menu item or option outside its container, a
|
|
30
30
|
* tablist with no selected tab, an unnamed dialog, aria-activedescendant on an element that cannot
|
|
31
31
|
* take focus — so `jx validate`, the kit's conformance tests and this report judge a document alike
|
|
32
|
-
* (spec §8.8). What stays here is what needs the whole page rather than a node: the heading
|
|
32
|
+
* (`spec.md` §8.8). What stays here is what needs the whole page rather than a node: the heading
|
|
33
33
|
* outline, duplicate ids, the document language, link wording, autocomplete purposes and media.
|
|
34
34
|
*
|
|
35
35
|
* @docs studio/interface/problems-and-progress
|
package/src/services/ai-ask.ts
CHANGED
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
|
|
26
26
|
import { createToolDefinition, toolError, toolSuccess } from "@jxsuite/ai/tools";
|
|
27
27
|
import { reactive } from "../reactivity";
|
|
28
|
-
import { currentToolCallId, turnSignal } from "./ai-turn-signal";
|
|
29
28
|
|
|
30
29
|
import type { ToolRegistry } from "@jxsuite/ai/tools";
|
|
31
30
|
|
|
@@ -81,17 +80,17 @@ export function isAwaitingAnswer(): boolean {
|
|
|
81
80
|
* Register a question and suspend until it is settled.
|
|
82
81
|
*
|
|
83
82
|
* @param {PendingAsk} ask
|
|
83
|
+
* @param {AbortSignal} [signal] - The asking call's signal. Without one the question can still be
|
|
84
|
+
* settled by {@link cancelAsk}, but a Stop would leave the turn awaiting a promise nothing could
|
|
85
|
+
* resolve.
|
|
84
86
|
* @returns {Promise<AskAnswer>} Resolves on an answer, a skip, or the turn being stopped (`{
|
|
85
87
|
* answer: null, skipped: false }` — the shape the tool reports as a failure).
|
|
86
88
|
*/
|
|
87
|
-
export function askUser(ask: PendingAsk): Promise<AskAnswer> {
|
|
89
|
+
export function askUser(ask: PendingAsk, signal?: AbortSignal): Promise<AskAnswer> {
|
|
88
90
|
return new Promise<AskAnswer>((resolve) => {
|
|
89
91
|
state.pending = ask;
|
|
90
92
|
settle = resolve;
|
|
91
93
|
|
|
92
|
-
/* The turn's signal, not a parameter: `ToolRegistry.execute` has nowhere to pass one. Without
|
|
93
|
-
this, Stop would leave the loop awaiting a promise nothing could ever resolve. */
|
|
94
|
-
const signal = turnSignal();
|
|
95
94
|
if (signal?.aborted) {
|
|
96
95
|
clear();
|
|
97
96
|
resolve({ answer: null, skipped: false });
|
|
@@ -163,10 +162,26 @@ export function resetAsk(): void {
|
|
|
163
162
|
*
|
|
164
163
|
* @param {Pick<ToolRegistry, "register">} registry
|
|
165
164
|
*/
|
|
166
|
-
|
|
165
|
+
/** What the host that registers the question tool may hook. */
|
|
166
|
+
export interface AskToolOptions {
|
|
167
|
+
/**
|
|
168
|
+
* Called once a question has been put to the author, before the turn waits on the answer. A host
|
|
169
|
+
* saves the conversation here: a turn suspended on a person may wait for as long as they like,
|
|
170
|
+
* and a reload in the meantime must still find the question to restore as open (`specs/ai.md`
|
|
171
|
+
* §3.4).
|
|
172
|
+
*/
|
|
173
|
+
onPending?: () => void;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function registerAskTool(
|
|
177
|
+
registry: Pick<ToolRegistry, "register">,
|
|
178
|
+
{ onPending }: AskToolOptions = {},
|
|
179
|
+
): void {
|
|
167
180
|
registry.register(
|
|
168
181
|
createToolDefinition({
|
|
169
182
|
name: "ask_user",
|
|
183
|
+
// It suspends the turn on the author, so a round that only asked spends no work budget.
|
|
184
|
+
interactive: true,
|
|
170
185
|
description:
|
|
171
186
|
"Pause and put a question to the user; the turn waits for their reply and resumes with " +
|
|
172
187
|
"it. Use it ONLY for a judgement that is genuinely theirs — which pages matter, whether to " +
|
|
@@ -196,7 +211,7 @@ export function registerAskTool(registry: Pick<ToolRegistry, "register">): void
|
|
|
196
211
|
},
|
|
197
212
|
required: ["question"],
|
|
198
213
|
},
|
|
199
|
-
execute: async (args) => {
|
|
214
|
+
execute: async (args, ctx) => {
|
|
200
215
|
/* Everything `unknown`, and every field narrowed by `typeof` rather than by `?.`. The
|
|
201
216
|
registry's `validate()` is "a lightweight structural check, not a JSON Schema validator",
|
|
202
217
|
`strict: false` skips it entirely, and this tool may be registered into someone else's
|
|
@@ -219,15 +234,23 @@ export function registerAskTool(registry: Pick<ToolRegistry, "register">): void
|
|
|
219
234
|
.slice(0, MAX_OPTIONS)
|
|
220
235
|
: [];
|
|
221
236
|
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
237
|
+
const answering = askUser(
|
|
238
|
+
{
|
|
239
|
+
context: typeof context === "string" ? context.trim() : "",
|
|
240
|
+
/* The tool-call id, so the chip the transcript draws and the question this registers
|
|
241
|
+
are the same thing. Without it a restored, permanently unanswered question would
|
|
242
|
+
render identically to the live one. */
|
|
243
|
+
id: ctx.callId,
|
|
244
|
+
options: choices,
|
|
245
|
+
question: question.trim(),
|
|
246
|
+
},
|
|
247
|
+
ctx.signal,
|
|
248
|
+
);
|
|
249
|
+
// The question is published by now (`askUser` sets it synchronously); the answer is not.
|
|
250
|
+
if (isAwaitingAnswer()) {
|
|
251
|
+
onPending?.();
|
|
252
|
+
}
|
|
253
|
+
const { answer, skipped } = await answering;
|
|
231
254
|
|
|
232
255
|
if (skipped) {
|
|
233
256
|
/* A success: the model asked, and "you decide" is an answer. Rendering it as a failure
|
|
@@ -59,14 +59,19 @@
|
|
|
59
59
|
* @license MIT
|
|
60
60
|
*/
|
|
61
61
|
|
|
62
|
-
import { toolError } from "@jxsuite/ai/tools";
|
|
63
|
-
import type {
|
|
62
|
+
import { createToolContext, toolError } from "@jxsuite/ai/tools";
|
|
63
|
+
import type {
|
|
64
|
+
ToolContext,
|
|
65
|
+
ToolDefinition,
|
|
66
|
+
ToolRegistry,
|
|
67
|
+
ToolResult,
|
|
68
|
+
WriteLedger,
|
|
69
|
+
} from "@jxsuite/ai/tools";
|
|
64
70
|
import { activeRegistry } from "../commands/active-registry";
|
|
65
71
|
import { argsSchema } from "../commands/command-args";
|
|
66
72
|
import { inCanvas } from "../commands/context";
|
|
67
73
|
import { toRaw } from "../reactivity";
|
|
68
74
|
import { projectState } from "../state";
|
|
69
|
-
import { recordWrite } from "./ai-writes";
|
|
70
75
|
import { reportDocumentWrite, snapshotBeforeWrite } from "./ai-write-report";
|
|
71
76
|
import type { WriteReportDeps, WriteSnapshot } from "./ai-write-report";
|
|
72
77
|
import type { CommandContext } from "../commands/context";
|
|
@@ -198,7 +203,7 @@ function projected(registry: CommandRegistry): AnyCommand[] {
|
|
|
198
203
|
function definitionOf(
|
|
199
204
|
registry: CommandRegistry,
|
|
200
205
|
command: AnyCommand,
|
|
201
|
-
execute: (name: string, args: object) => Promise<ToolResult>,
|
|
206
|
+
execute: (name: string, args: object, ctx: ToolContext) => Promise<ToolResult>,
|
|
202
207
|
): ToolDefinition | undefined {
|
|
203
208
|
const tool = command.aiTool!;
|
|
204
209
|
let parameters: object;
|
|
@@ -217,7 +222,7 @@ function definitionOf(
|
|
|
217
222
|
parameters: parameters as ToolDefinition["parameters"],
|
|
218
223
|
strict: false,
|
|
219
224
|
llmStrict: false,
|
|
220
|
-
execute: (args) => execute(tool.name, args),
|
|
225
|
+
execute: (args, ctx) => execute(tool.name, args, ctx),
|
|
221
226
|
};
|
|
222
227
|
}
|
|
223
228
|
|
|
@@ -235,7 +240,7 @@ function definitionOf(
|
|
|
235
240
|
*/
|
|
236
241
|
export function advertisedCommandTools(
|
|
237
242
|
registry: CommandRegistry,
|
|
238
|
-
execute: (name: string, args: object) => Promise<ToolResult> = async (name) =>
|
|
243
|
+
execute: (name: string, args: object, ctx: ToolContext) => Promise<ToolResult> = async (name) =>
|
|
239
244
|
toolError(`Tool "${name}" was listed without an executor.`),
|
|
240
245
|
): ToolDefinition[] {
|
|
241
246
|
const definitions: ToolDefinition[] = [];
|
|
@@ -324,13 +329,18 @@ function defaultPaths(command: AnyCommand, tab: Tab | null): string[] {
|
|
|
324
329
|
* had — and files nothing, because the ledger names files that changed; `undefined` is a report
|
|
325
330
|
* that named none and takes the record's defaults.
|
|
326
331
|
*/
|
|
327
|
-
function fileLedger(
|
|
332
|
+
function fileLedger(
|
|
333
|
+
ledger: WriteLedger,
|
|
334
|
+
command: AnyCommand,
|
|
335
|
+
tab: Tab | null,
|
|
336
|
+
wrote?: readonly string[],
|
|
337
|
+
): void {
|
|
328
338
|
if (command.undo === undefined) {
|
|
329
339
|
return;
|
|
330
340
|
}
|
|
331
341
|
const disk = command.undo === "none";
|
|
332
342
|
for (const path of wrote ?? defaultPaths(command, tab)) {
|
|
333
|
-
|
|
343
|
+
ledger.record({ disk, ok: true, path, tool: command.title });
|
|
334
344
|
}
|
|
335
345
|
}
|
|
336
346
|
|
|
@@ -349,13 +359,17 @@ export function createCommandToolRegistry(deps: CommandToolDeps): ToolRegistry {
|
|
|
349
359
|
}
|
|
350
360
|
|
|
351
361
|
/**
|
|
352
|
-
* Run one projected record as the agent — the whole of §3
|
|
362
|
+
* Run one projected record as the agent — the whole of `ai.md` §3.6.
|
|
353
363
|
*
|
|
354
364
|
* @param {string} name
|
|
355
365
|
* @param {object} received
|
|
356
366
|
* @returns {Promise<ToolResult>}
|
|
357
367
|
*/
|
|
358
|
-
async function execute(
|
|
368
|
+
async function execute(
|
|
369
|
+
name: string,
|
|
370
|
+
received: object,
|
|
371
|
+
ctx: ToolContext = createToolContext(),
|
|
372
|
+
): Promise<ToolResult> {
|
|
359
373
|
const registry = activeRegistry();
|
|
360
374
|
const command = commandFor(name);
|
|
361
375
|
if (!registry || !command) {
|
|
@@ -426,7 +440,7 @@ export function createCommandToolRegistry(deps: CommandToolDeps): ToolRegistry {
|
|
|
426
440
|
Left to the loop, the throw would be labelled "Failed to parse arguments" and the ledger
|
|
427
441
|
would never be filed. So the ledger is filed from `undo`'s defaults — the report that would
|
|
428
442
|
have named the paths is the thing that failed — and the model is told both halves. */
|
|
429
|
-
fileLedger(command, tab, projectUnchanged ? [] : undefined);
|
|
443
|
+
fileLedger(ctx.ledger, command, tab, projectUnchanged ? [] : undefined);
|
|
430
444
|
const message = error instanceof Error ? error.message : String(error);
|
|
431
445
|
return { error: `${command.title} ran, but its report failed: ${message}`, success: false };
|
|
432
446
|
}
|
|
@@ -442,7 +456,7 @@ export function createCommandToolRegistry(deps: CommandToolDeps): ToolRegistry {
|
|
|
442
456
|
summary = `${command.title} changed nothing: project.json is exactly as it was.`;
|
|
443
457
|
}
|
|
444
458
|
}
|
|
445
|
-
fileLedger(command, tab, wrote);
|
|
459
|
+
fileLedger(ctx.ledger, command, tab, wrote);
|
|
446
460
|
if (tab && snapshot) {
|
|
447
461
|
const verdict = await reportDocumentWrite(tab, snapshot, summary, deps);
|
|
448
462
|
if (!verdict.success) {
|
|
@@ -530,9 +544,9 @@ export function composeToolRegistries(hand: ToolRegistry, commands: ToolRegistry
|
|
|
530
544
|
? side.validate(name, args)
|
|
531
545
|
: { valid: false, errors: [`Unknown tool: "${name}"`] };
|
|
532
546
|
},
|
|
533
|
-
async execute(name, args) {
|
|
547
|
+
async execute(name, args, ctx) {
|
|
534
548
|
const side = sideOf(name);
|
|
535
|
-
return side ? side.execute(name, args) : toolError(`Unknown tool: "${name}"`);
|
|
549
|
+
return side ? side.execute(name, args, ctx) : toolError(`Unknown tool: "${name}"`);
|
|
536
550
|
},
|
|
537
551
|
};
|
|
538
552
|
}
|
|
@@ -28,18 +28,12 @@ import { workspace } from "../workspace/workspace";
|
|
|
28
28
|
import { getBaseUrl, getOpenAiKey } from "./ai-settings";
|
|
29
29
|
import { preferredModel } from "./ai-models";
|
|
30
30
|
import { clearPendingImportBrief, pendingImportBrief } from "./import-seed";
|
|
31
|
-
import {
|
|
32
|
-
|
|
33
|
-
beginImportRun,
|
|
34
|
-
finishImportRun,
|
|
35
|
-
importRun,
|
|
36
|
-
recordImportProgress,
|
|
37
|
-
} from "./import-run";
|
|
31
|
+
import type { ImportBrief } from "./import-seed";
|
|
32
|
+
import { abortImportRun, beginImportRun, finishImportRun, importRun } from "./import-run";
|
|
38
33
|
import { adoptCreatedProject } from "./project-adoption";
|
|
39
34
|
import type { AdoptOutcome } from "./project-adoption";
|
|
40
|
-
import { currentToolCallId, turnSignal } from "./ai-turn-signal";
|
|
41
35
|
|
|
42
|
-
import type { ToolRegistry } from "@jxsuite/ai/tools";
|
|
36
|
+
import type { JsonValue, ToolRegistry } from "@jxsuite/ai/tools";
|
|
43
37
|
import type { Tab } from "../tabs/tab";
|
|
44
38
|
import type { ImportBreakpointPolicy, ImportReadyEvent, ImportSiteSummary } from "../types";
|
|
45
39
|
|
|
@@ -72,6 +66,11 @@ export interface ImportToolsCtx {
|
|
|
72
66
|
adoptProject?: (root: string) => Promise<void>;
|
|
73
67
|
/** Fired once adoption is VERIFIED — the session store re-keys the live chat here. */
|
|
74
68
|
onProjectAdopted?: (root: string) => void;
|
|
69
|
+
/**
|
|
70
|
+
* The New Project Import form's brief, when the run started there. Defaults to the one the form
|
|
71
|
+
* stores (`services/import-seed.ts`); a host with no form binds its own.
|
|
72
|
+
*/
|
|
73
|
+
brief?: { read: () => ImportBrief | null; clear: () => void };
|
|
75
74
|
}
|
|
76
75
|
|
|
77
76
|
function clamp(value: number, min: number, max: number): number {
|
|
@@ -151,19 +150,15 @@ function destinationProblem(destination: string): string | null {
|
|
|
151
150
|
}
|
|
152
151
|
|
|
153
152
|
/**
|
|
154
|
-
*
|
|
153
|
+
* The session fact that one run has already succeeded in this conversation.
|
|
155
154
|
*
|
|
156
155
|
* Not the same question as `workspace.projectRoot`, and that is the whole reason it exists: on
|
|
157
156
|
* desktop, adoption may open the project in ANOTHER window, leaving this one's root empty — so the
|
|
158
157
|
* `no-project` tier would happily advertise a second import over the top of the first.
|
|
159
|
-
* `create_project` has the identical hazard today.
|
|
158
|
+
* `create_project` has the identical hazard today. A session fact rather than a module flag, and it
|
|
159
|
+
* lasts as the flag did: New Chat starts with none, and opening another chat keeps it.
|
|
160
160
|
*/
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
/** Forget that an import ran. New Chat, and tests. */
|
|
164
|
-
export function resetImportGuard(): void {
|
|
165
|
-
imported = false;
|
|
166
|
-
}
|
|
161
|
+
const IMPORT_DONE = "import.done";
|
|
167
162
|
|
|
168
163
|
/**
|
|
169
164
|
* A compact, factual account of the run for the model to reason about — and to ask about.
|
|
@@ -258,7 +253,12 @@ function describeRun(id: string, root: string, summary?: ImportSiteSummary): str
|
|
|
258
253
|
*/
|
|
259
254
|
export function registerImportTools(
|
|
260
255
|
registry: Pick<ToolRegistry, "register">,
|
|
261
|
-
{
|
|
256
|
+
{
|
|
257
|
+
getTab,
|
|
258
|
+
adoptProject,
|
|
259
|
+
onProjectAdopted,
|
|
260
|
+
brief: briefStore = { clear: clearPendingImportBrief, read: pendingImportBrief },
|
|
261
|
+
}: ImportToolsCtx,
|
|
262
262
|
): void {
|
|
263
263
|
registry.register(
|
|
264
264
|
createToolDefinition({
|
|
@@ -334,7 +334,7 @@ export function registerImportTools(
|
|
|
334
334
|
},
|
|
335
335
|
required: ["url"],
|
|
336
336
|
},
|
|
337
|
-
async execute(args) {
|
|
337
|
+
async execute(args, ctx) {
|
|
338
338
|
const {
|
|
339
339
|
url,
|
|
340
340
|
directory,
|
|
@@ -364,7 +364,7 @@ export function registerImportTools(
|
|
|
364
364
|
"A project is already open in this window — import_site is only for bootstrapping.",
|
|
365
365
|
);
|
|
366
366
|
}
|
|
367
|
-
if (
|
|
367
|
+
if (ctx.session.get(IMPORT_DONE) === true) {
|
|
368
368
|
return toolError(
|
|
369
369
|
"A site has already been imported in this conversation. Start a new chat to import " +
|
|
370
370
|
"another, or use the file tools on the project that was created.",
|
|
@@ -385,7 +385,7 @@ export function registerImportTools(
|
|
|
385
385
|
return toolError("The url must start with http:// or https://.");
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
-
const brief =
|
|
388
|
+
const brief = briefStore.read();
|
|
389
389
|
const requested = typeof directory === "string" ? directory.trim() : "";
|
|
390
390
|
if (requested && brief && requested !== brief.directory) {
|
|
391
391
|
/* The wizard's Location field is the user's answer to "where does this go". A model that
|
|
@@ -410,16 +410,23 @@ export function registerImportTools(
|
|
|
410
410
|
return toolError(problem);
|
|
411
411
|
}
|
|
412
412
|
|
|
413
|
-
const id =
|
|
413
|
+
const id = ctx.callId;
|
|
414
414
|
const signal = beginImportRun(id, { directory: destination, url: target.href });
|
|
415
|
-
/* The
|
|
415
|
+
/* The call's own signal too. `assistant.stop` aborts the request through `abortImportRun`,
|
|
416
416
|
but that is not the only way a turn ends: the loop's signal can be aborted directly, and
|
|
417
417
|
a run left going after its turn died would keep a headless browser open for minutes with
|
|
418
|
-
nothing left to receive the result.
|
|
419
|
-
|
|
418
|
+
nothing left to receive the result.
|
|
419
|
+
|
|
420
|
+
It stops THIS run and nothing after it, so it is removed the moment the run settles: left
|
|
421
|
+
on the signal, a Stop later in the same turn (a question after the import, say) rewrote
|
|
422
|
+
the finished run as stopped. The loop also unlinks the call's signal once the call
|
|
423
|
+
settles, so a later Stop reaches it no more than it reaches the listener. */
|
|
424
|
+
const callSignal = ctx.signal;
|
|
425
|
+
const stopRun = () => {
|
|
420
426
|
abortImportRun();
|
|
421
427
|
finishImportRun(id, { status: "stopped" });
|
|
422
|
-
}
|
|
428
|
+
};
|
|
429
|
+
callSignal.addEventListener("abort", stopRun, { once: true });
|
|
423
430
|
|
|
424
431
|
const apiKey = getOpenAiKey();
|
|
425
432
|
const baseUrl = getBaseUrl();
|
|
@@ -479,11 +486,14 @@ export function registerImportTools(
|
|
|
479
486
|
...(baseUrl ? { baseUrl } : {}),
|
|
480
487
|
...(model ? { model } : {}),
|
|
481
488
|
},
|
|
482
|
-
(evt) =>
|
|
489
|
+
(evt) => {
|
|
490
|
+
ctx.progress(evt as unknown as JsonValue);
|
|
491
|
+
},
|
|
483
492
|
signal,
|
|
484
493
|
onReady,
|
|
485
494
|
);
|
|
486
495
|
} catch (error) {
|
|
496
|
+
callSignal.removeEventListener("abort", stopRun);
|
|
487
497
|
const message = error instanceof Error ? error.message : String(error);
|
|
488
498
|
if (signal.aborted) {
|
|
489
499
|
finishImportRun(id, { error: message, status: "stopped" });
|
|
@@ -502,9 +512,13 @@ export function registerImportTools(
|
|
|
502
512
|
);
|
|
503
513
|
}
|
|
504
514
|
|
|
515
|
+
callSignal.removeEventListener("abort", stopRun);
|
|
505
516
|
finishImportRun(id, { status: "done" });
|
|
506
|
-
imported
|
|
507
|
-
|
|
517
|
+
/* The imported project is on disk from here, and no undo reaches it: it is what the turn
|
|
518
|
+
changed, whichever window ends up opening it. */
|
|
519
|
+
ctx.ledger.record({ disk: true, ok: true, path: result.root, tool: "import_site" });
|
|
520
|
+
ctx.session.set(IMPORT_DONE, true);
|
|
521
|
+
briefStore.clear();
|
|
508
522
|
|
|
509
523
|
/* The early adoption if there was one, otherwise adopt now: a backend that sends no `ready`
|
|
510
524
|
line is not broken, it is older, and the project must still open. */
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { createToolDefinition } from "@jxsuite/ai/tools";
|
|
18
18
|
import type { CreateProjectDestination } from "../types";
|
|
19
|
-
import type { ToolRegistry
|
|
19
|
+
import type { ToolRegistry } from "@jxsuite/ai/tools";
|
|
20
20
|
import type { JxMutableNode, ProjectConfig } from "@jxsuite/schema/types";
|
|
21
21
|
import { getPlatform } from "../platform";
|
|
22
22
|
import { workspace } from "../workspace/workspace";
|
|
@@ -25,8 +25,8 @@ import type { Tab } from "../tabs/tab";
|
|
|
25
25
|
import { adoptCreatedProject } from "./project-adoption";
|
|
26
26
|
import { translateValidationError } from "./ai-tools";
|
|
27
27
|
import { validateDoc, validateProjectConfig } from "./jx-validate";
|
|
28
|
-
import { recordWrite } from "./ai-writes";
|
|
29
28
|
import { flagHardcodedTokens, formatTokenHints } from "./token-lint";
|
|
29
|
+
import { invalidPathResult, normalizeRelPath } from "./project-path";
|
|
30
30
|
|
|
31
31
|
/** Directories the file tools never descend into or report. */
|
|
32
32
|
const EXCLUDED_DIRS = new Set(["node_modules", "dist", ".git", ".jx-cache"]);
|
|
@@ -39,34 +39,6 @@ const WRITE_CAP_BYTES = 256 * 1024;
|
|
|
39
39
|
|
|
40
40
|
const NOT_UNDOABLE = "(saved to disk; not undoable with Cmd+Z)";
|
|
41
41
|
|
|
42
|
-
/**
|
|
43
|
-
* Normalize a project-relative path, or return null when it escapes the project (absolute paths,
|
|
44
|
-
* `..` segments, drive letters). The server re-checks; this keeps the error actionable.
|
|
45
|
-
*/
|
|
46
|
-
export function normalizeRelPath(path: unknown): string | null {
|
|
47
|
-
if (typeof path !== "string" || !path.trim()) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
let p = path.trim().replaceAll("\\", "/");
|
|
51
|
-
while (p.startsWith("./")) {
|
|
52
|
-
p = p.slice(2);
|
|
53
|
-
}
|
|
54
|
-
if (p.startsWith("/") || p.startsWith("~") || /^[A-Za-z]:/.test(p)) {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
if (p.split("/").includes("..")) {
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
return p;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function pathError(path: unknown): ToolResult {
|
|
64
|
-
return {
|
|
65
|
-
success: false,
|
|
66
|
-
error: `Invalid path ${JSON.stringify(path)} — use a path relative to the project root (no leading "/", no "..").`,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
42
|
/**
|
|
71
43
|
* Whether a project-relative path conventionally holds a Jx document. Kept in step with
|
|
72
44
|
* monaco-setup.ts's DOCUMENT_FILE_MATCH so the assistant's gate and the editor's diagnostics cover
|
|
@@ -170,7 +142,7 @@ export function registerProjectTools(
|
|
|
170
142
|
const { dir } = args as { dir?: string };
|
|
171
143
|
const start = dir === undefined || dir === "" ? "." : normalizeRelPath(dir);
|
|
172
144
|
if (start === null) {
|
|
173
|
-
return
|
|
145
|
+
return invalidPathResult(dir);
|
|
174
146
|
}
|
|
175
147
|
const platform = getPlatform();
|
|
176
148
|
const entries: { path: string; type: string; size?: number }[] = [];
|
|
@@ -234,7 +206,7 @@ export function registerProjectTools(
|
|
|
234
206
|
async execute(args) {
|
|
235
207
|
const relPath = normalizeRelPath((args as { path: string }).path);
|
|
236
208
|
if (relPath === null) {
|
|
237
|
-
return
|
|
209
|
+
return invalidPathResult((args as { path: string }).path);
|
|
238
210
|
}
|
|
239
211
|
try {
|
|
240
212
|
const content = await getPlatform().readFile(relPath);
|
|
@@ -282,11 +254,11 @@ export function registerProjectTools(
|
|
|
282
254
|
},
|
|
283
255
|
required: ["path", "content"],
|
|
284
256
|
},
|
|
285
|
-
async execute(args) {
|
|
257
|
+
async execute(args, ctx) {
|
|
286
258
|
const { path, content } = args as { path: string; content: string };
|
|
287
259
|
const relPath = normalizeRelPath(path);
|
|
288
260
|
if (relPath === null) {
|
|
289
|
-
return
|
|
261
|
+
return invalidPathResult(path);
|
|
290
262
|
}
|
|
291
263
|
if (content.length > WRITE_CAP_BYTES) {
|
|
292
264
|
return {
|
|
@@ -389,11 +361,17 @@ export function registerProjectTools(
|
|
|
389
361
|
await getPlatform().writeFile(relPath, content);
|
|
390
362
|
/* Disk, not transaction: there is no undo behind this and there never was. Recorded as
|
|
391
363
|
`disk: true` so the panel can say so to the person holding ⌘Z — the caveat used to be
|
|
392
|
-
appended to the model-facing summary only (§
|
|
393
|
-
|
|
364
|
+
appended to the model-facing summary only (`ai.md` §3.2). */
|
|
365
|
+
ctx.ledger.record({ disk: true, ok: true, path: relPath, tool: "write_file" });
|
|
394
366
|
} catch (error) {
|
|
395
367
|
const message = error instanceof Error ? error.message : String(error);
|
|
396
|
-
|
|
368
|
+
ctx.ledger.record({
|
|
369
|
+
disk: true,
|
|
370
|
+
error: message,
|
|
371
|
+
ok: false,
|
|
372
|
+
path: relPath,
|
|
373
|
+
tool: "write_file",
|
|
374
|
+
});
|
|
397
375
|
return {
|
|
398
376
|
success: false,
|
|
399
377
|
error: `Failed to write "${relPath}": ${message}`,
|
|
@@ -511,7 +489,7 @@ export function registerProjectTools(
|
|
|
511
489
|
},
|
|
512
490
|
required: ["name"],
|
|
513
491
|
},
|
|
514
|
-
async execute(args) {
|
|
492
|
+
async execute(args, ctx) {
|
|
515
493
|
const {
|
|
516
494
|
name,
|
|
517
495
|
description,
|
|
@@ -598,6 +576,10 @@ export function registerProjectTools(
|
|
|
598
576
|
error: `Failed to create project: ${error instanceof Error ? error.message : String(error)}`,
|
|
599
577
|
};
|
|
600
578
|
}
|
|
579
|
+
/* The project is on disk from here, whether or not this window goes on to open it, and no
|
|
580
|
+
undo reaches it. Recorded, it is what the turn changed: without it a turn that bootstrapped
|
|
581
|
+
a project and then only read reported that it had applied nothing. */
|
|
582
|
+
ctx.ledger.record({ disk: true, ok: true, path: result.root, tool: "create_project" });
|
|
601
583
|
|
|
602
584
|
/*
|
|
603
585
|
* Git init, then the full project-open flow, then a check that it landed — all three in
|