@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
|
@@ -20,9 +20,11 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { SETTINGS } from "./settings/definitions";
|
|
23
|
+
import type { SettingDefinition } from "./settings/definitions";
|
|
23
24
|
import {
|
|
24
25
|
clearSettings,
|
|
25
26
|
hasSetting,
|
|
27
|
+
normalizeSetting,
|
|
26
28
|
readStoredSetting,
|
|
27
29
|
setSetting,
|
|
28
30
|
setSettings,
|
|
@@ -82,6 +84,13 @@ export function setModel(modelId: string) {
|
|
|
82
84
|
setSetting(SETTINGS.aiModel, modelId || "");
|
|
83
85
|
}
|
|
84
86
|
|
|
87
|
+
/** The provider's three values together — what a credentials form drafts and what Save stores. */
|
|
88
|
+
export interface AiProvider {
|
|
89
|
+
apiKey: string;
|
|
90
|
+
baseUrl: string;
|
|
91
|
+
model: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
85
94
|
/**
|
|
86
95
|
* Store the provider's key, endpoint and model as ONE change.
|
|
87
96
|
*
|
|
@@ -89,9 +98,9 @@ export function setModel(modelId: string) {
|
|
|
89
98
|
* anyway, but they announce three times, and each intermediate announcement describes a provider
|
|
90
99
|
* the user never asked for — a new key against the old endpoint, then against no model.
|
|
91
100
|
*
|
|
92
|
-
* @param {
|
|
101
|
+
* @param {AiProvider} provider
|
|
93
102
|
*/
|
|
94
|
-
export function saveAiProvider(provider:
|
|
103
|
+
export function saveAiProvider(provider: AiProvider) {
|
|
95
104
|
setSettings([
|
|
96
105
|
[SETTINGS.aiOpenAiKey, provider.apiKey],
|
|
97
106
|
[SETTINGS.aiBaseUrl, provider.baseUrl],
|
|
@@ -99,6 +108,50 @@ export function saveAiProvider(provider: { apiKey: string; baseUrl: string; mode
|
|
|
99
108
|
]);
|
|
100
109
|
}
|
|
101
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Whether {@link saveAiProvider} with these values would change what is stored.
|
|
113
|
+
*
|
|
114
|
+
* What a credentials form's Save and Cancel are drawn from: at rest there is nothing to keep and
|
|
115
|
+
* nothing to abandon. Each value is compared as the store WOULD hold it — through its definition's
|
|
116
|
+
* own `normalize` — so a pasted key's whitespace or an endpoint's trailing slash is not an edit,
|
|
117
|
+
* because Save would store exactly what is already there. An absent setting reads as `""`, as every
|
|
118
|
+
* getter here does, so a blank draft over nothing is no change either.
|
|
119
|
+
*
|
|
120
|
+
* **BOTH sides are normalised, not just the draft.** Only the setters normalise on the way in;
|
|
121
|
+
* `hydrateSettings` and `adoptRemoteSettings` put a backend's or another window's values into the
|
|
122
|
+
* cache as they found them, and a hand-edited `settings.json` carries whatever was typed. So the
|
|
123
|
+
* store legitimately holds `http://localhost:11434/v1/` with the trailing slash the docs print, and
|
|
124
|
+
* comparing a normalised draft against that raw value reported an edit nobody had made: Save and
|
|
125
|
+
* Cancel appeared on a form at rest, and Cancel could not clear them, because reloading the drafts
|
|
126
|
+
* loaded the same unnormalised value again. Normalising the stored side is the comparison the
|
|
127
|
+
* sentence above always claimed to be making.
|
|
128
|
+
*
|
|
129
|
+
* @param {AiProvider} provider The drafts.
|
|
130
|
+
* @returns {boolean} `true` when at least one of the three would move.
|
|
131
|
+
*/
|
|
132
|
+
export function aiProviderDiffers(provider: AiProvider): boolean {
|
|
133
|
+
return (
|
|
134
|
+
!storedEquals(SETTINGS.aiOpenAiKey, provider.apiKey, getOpenAiKey()) ||
|
|
135
|
+
!storedEquals(SETTINGS.aiBaseUrl, provider.baseUrl, getBaseUrl()) ||
|
|
136
|
+
!storedEquals(SETTINGS.aiModel, provider.model, storedModel())
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Whether a draft and a stored value are the same setting, judged as the store would hold each.
|
|
142
|
+
*
|
|
143
|
+
* Exported because a form's "has the user touched this field?" has to ask the same question as
|
|
144
|
+
* {@link aiProviderDiffers}, or the two disagree about a whitespace-only draft — which is how a
|
|
145
|
+
* revoked key survived a Disconnect (`ui/ai-credentials-form.ts`).
|
|
146
|
+
*/
|
|
147
|
+
export function storedEquals(
|
|
148
|
+
definition: SettingDefinition,
|
|
149
|
+
draft: string,
|
|
150
|
+
stored: string,
|
|
151
|
+
): boolean {
|
|
152
|
+
return normalizeSetting(definition, draft) === normalizeSetting(definition, stored);
|
|
153
|
+
}
|
|
154
|
+
|
|
102
155
|
/**
|
|
103
156
|
* Forget the provider entirely — key, endpoint and model.
|
|
104
157
|
*
|
package/src/services/ai-tools.ts
CHANGED
|
@@ -3,27 +3,30 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Concrete `.jx` AST tools registered into a `@jxsuite/ai` ToolRegistry. Each tool wraps an
|
|
5
5
|
* existing `transactDoc()` mutation helper so AI edits get the same undo/redo history as manual
|
|
6
|
-
* edits (
|
|
6
|
+
* edits (specs/ai.md §3.1 and §3.2 — optimistic apply, undo as the backstop).
|
|
7
7
|
*
|
|
8
8
|
* @license MIT
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { createToolDefinition } from "@jxsuite/ai/tools";
|
|
12
|
-
import type { ToolRegistry, ToolResult } from "@jxsuite/ai/tools";
|
|
12
|
+
import type { ToolRegistry, ToolResult, WriteLedger } from "@jxsuite/ai/tools";
|
|
13
13
|
import type { JxMutableNode, JxPath, JxStateDefinition } from "@jxsuite/schema/types";
|
|
14
14
|
import { getNodeAtPath } from "../state";
|
|
15
15
|
import type { Tab } from "../tabs/tab";
|
|
16
16
|
import {
|
|
17
|
+
mutateAddDef,
|
|
17
18
|
mutateInsertNode,
|
|
18
19
|
mutateMoveNode,
|
|
20
|
+
mutateRemoveDef,
|
|
19
21
|
mutateUpdateProperty,
|
|
20
22
|
mutateUpdateStyle,
|
|
21
23
|
transactDoc,
|
|
22
24
|
} from "../tabs/transact";
|
|
23
25
|
import type { JxNodeValue } from "../tabs/transact";
|
|
26
|
+
import type { JsonValue } from "../types";
|
|
24
27
|
import { validateDoc } from "./jx-validate";
|
|
25
|
-
import {
|
|
26
|
-
import { serializeJson } from "
|
|
28
|
+
import { invalidPathResult, normalizeRelPath } from "./project-path";
|
|
29
|
+
import { serializeJson } from "@jxsuite/schema/json-layout";
|
|
27
30
|
import {
|
|
28
31
|
reportDocumentWrite,
|
|
29
32
|
snapshotBeforeWrite,
|
|
@@ -52,11 +55,12 @@ function noDocError(): ToolResult {
|
|
|
52
55
|
|
|
53
56
|
/**
|
|
54
57
|
* Apply a mutation, then hand the verdict to `ai-write-report.ts`: only the schema errors the edit
|
|
55
|
-
* NEWLY introduced are reported
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
58
|
+
* NEWLY introduced are reported, the render check runs when a renderCheck was provided and
|
|
59
|
+
* rendering worked before, and a success carries the token hints. The change stays applied either
|
|
60
|
+
* way (optimistic apply + undo); reporting the errors lets the agent loop self-correct on the next
|
|
61
|
+
* round. Both rules are specs/ai.md §3.1.
|
|
59
62
|
*
|
|
63
|
+
* @param {WriteLedger} ledger - The call's ledger (`ctx.ledger`)
|
|
60
64
|
* @param {import("../tabs/tab").Tab} tab
|
|
61
65
|
* @param {(t: import("../tabs/tab").Tab) => void} mutationFn
|
|
62
66
|
* @param {string} summary
|
|
@@ -66,6 +70,7 @@ function noDocError(): ToolResult {
|
|
|
66
70
|
* @returns {Promise<import("@jxsuite/ai/tools").ToolResult>}
|
|
67
71
|
*/
|
|
68
72
|
async function applyAndValidate(
|
|
73
|
+
ledger: WriteLedger,
|
|
69
74
|
tab: Tab,
|
|
70
75
|
mutationFn: (t: Tab) => void,
|
|
71
76
|
summary: string,
|
|
@@ -91,9 +96,9 @@ async function applyAndValidate(
|
|
|
91
96
|
};
|
|
92
97
|
}
|
|
93
98
|
/* One ledger entry per mutation, so the panel's "Changed N files" counts documents the model
|
|
94
|
-
touched rather than sentences it wrote (§
|
|
95
|
-
went through transactDoc, so the tab's history — and "Restore to here" — can reach it. */
|
|
96
|
-
|
|
99
|
+
touched rather than sentences it wrote (`ai.md` §3.2). `disk: false` is the load-bearing half:
|
|
100
|
+
this went through transactDoc, so the tab's history — and "Restore to here" — can reach it. */
|
|
101
|
+
ledger.record({ disk: false, ok: true, path: tab.documentPath ?? "(untitled)", tool: summary });
|
|
97
102
|
|
|
98
103
|
return reportDocumentWrite(tab, before, summary, deps);
|
|
99
104
|
}
|
|
@@ -225,11 +230,11 @@ export function registerAiTools(
|
|
|
225
230
|
},
|
|
226
231
|
/*
|
|
227
232
|
* "value" omitted from required: passing null / omitting it means "remove the property",
|
|
228
|
-
* but the registry rejects null on required args (tools.js:181). See §
|
|
233
|
+
* but the registry rejects null on required args (tools.js:181). See `ai.md` §3.6.
|
|
229
234
|
*/
|
|
230
235
|
required: ["path", "key"],
|
|
231
236
|
},
|
|
232
|
-
async execute(args) {
|
|
237
|
+
async execute(args, ctx) {
|
|
233
238
|
const tab = getTab();
|
|
234
239
|
if (!tab) {
|
|
235
240
|
return noDocError();
|
|
@@ -240,6 +245,7 @@ export function registerAiTools(
|
|
|
240
245
|
return { success: false, error: `No node exists at path ${JSON.stringify(path)}.` };
|
|
241
246
|
}
|
|
242
247
|
return applyAndValidate(
|
|
248
|
+
ctx.ledger,
|
|
243
249
|
tab,
|
|
244
250
|
(t) => mutateUpdateProperty(t, path, key, value ?? undefined),
|
|
245
251
|
`Set "${key}" at ${JSON.stringify(path)}.`,
|
|
@@ -275,7 +281,7 @@ export function registerAiTools(
|
|
|
275
281
|
},
|
|
276
282
|
required: ["parentPath", "index", "node"],
|
|
277
283
|
},
|
|
278
|
-
async execute(args) {
|
|
284
|
+
async execute(args, ctx) {
|
|
279
285
|
const tab = getTab();
|
|
280
286
|
if (!tab) {
|
|
281
287
|
return noDocError();
|
|
@@ -318,6 +324,7 @@ export function registerAiTools(
|
|
|
318
324
|
};
|
|
319
325
|
}
|
|
320
326
|
return applyAndValidate(
|
|
327
|
+
ctx.ledger,
|
|
321
328
|
tab,
|
|
322
329
|
(t) => mutateInsertNode(t, parentPath, index, childNode),
|
|
323
330
|
`Inserted node at ${JSON.stringify([...parentPath, "children", index])}.`,
|
|
@@ -357,11 +364,11 @@ export function registerAiTools(
|
|
|
357
364
|
},
|
|
358
365
|
/*
|
|
359
366
|
* "value" omitted from required so null / omitted = remove (registry rejects null on
|
|
360
|
-
* required args, tools.js:181). See §
|
|
367
|
+
* required args, tools.js:181). See `ai.md` §3.6.
|
|
361
368
|
*/
|
|
362
369
|
required: ["path", "property"],
|
|
363
370
|
},
|
|
364
|
-
async execute(args) {
|
|
371
|
+
async execute(args, ctx) {
|
|
365
372
|
const tab = getTab();
|
|
366
373
|
if (!tab) {
|
|
367
374
|
return noDocError();
|
|
@@ -377,6 +384,7 @@ export function registerAiTools(
|
|
|
377
384
|
const prop = property;
|
|
378
385
|
const val = value == null ? undefined : String(value);
|
|
379
386
|
return applyAndValidate(
|
|
387
|
+
ctx.ledger,
|
|
380
388
|
tab,
|
|
381
389
|
(t) => mutateUpdateStyle(t, path, prop, val),
|
|
382
390
|
`Set style "${prop}" at ${JSON.stringify(path)}.`,
|
|
@@ -407,7 +415,7 @@ export function registerAiTools(
|
|
|
407
415
|
},
|
|
408
416
|
required: ["path", "value"],
|
|
409
417
|
},
|
|
410
|
-
async execute(args) {
|
|
418
|
+
async execute(args, ctx) {
|
|
411
419
|
const tab = getTab();
|
|
412
420
|
if (!tab) {
|
|
413
421
|
return noDocError();
|
|
@@ -417,11 +425,14 @@ export function registerAiTools(
|
|
|
417
425
|
return { success: false, error: `No node exists at path ${JSON.stringify(path)}.` };
|
|
418
426
|
}
|
|
419
427
|
return applyAndValidate(
|
|
428
|
+
ctx.ledger,
|
|
420
429
|
tab,
|
|
430
|
+
/* Through the recording mutators, as two `set-key` ops, rather than by writing the node
|
|
431
|
+
directly. An unrecorded write reaches the canvas only as a full re-render, history only
|
|
432
|
+
as a whole-document snapshot, and collaborators only as a diff. */
|
|
421
433
|
(t) => {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
node.children = [value];
|
|
434
|
+
mutateUpdateProperty(t, path, "textContent");
|
|
435
|
+
mutateUpdateProperty(t, path, "children", [value]);
|
|
425
436
|
},
|
|
426
437
|
`Set text at ${JSON.stringify(path)}.`,
|
|
427
438
|
validate,
|
|
@@ -453,7 +464,7 @@ export function registerAiTools(
|
|
|
453
464
|
},
|
|
454
465
|
required: ["key", "value"],
|
|
455
466
|
},
|
|
456
|
-
async execute(args) {
|
|
467
|
+
async execute(args, ctx) {
|
|
457
468
|
const tab = getTab();
|
|
458
469
|
if (!tab) {
|
|
459
470
|
return noDocError();
|
|
@@ -466,18 +477,12 @@ export function registerAiTools(
|
|
|
466
477
|
};
|
|
467
478
|
}
|
|
468
479
|
return applyAndValidate(
|
|
480
|
+
ctx.ledger,
|
|
469
481
|
tab,
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
}
|
|
475
|
-
/*
|
|
476
|
-
* Directly mutate — bypass mutateUpdateProperty because its "" → delete behaviour
|
|
477
|
-
* (transact.ts:248) is wrong for state defaults (e.g. "title": "").
|
|
478
|
-
*/
|
|
479
|
-
t.doc.document.state[key] = value;
|
|
480
|
-
},
|
|
482
|
+
/* `mutateAddDef` rather than `mutateUpdateProperty`: the latter deletes on "", which is
|
|
483
|
+
wrong for a state default (`"title": ""`). It also records the op, so the canvas, the
|
|
484
|
+
history and collaborators each get the edit as an edit. */
|
|
485
|
+
(t) => mutateAddDef(t, key, value as Record<string, JsonValue>),
|
|
481
486
|
`Added state "${key}".`,
|
|
482
487
|
validate,
|
|
483
488
|
renderCheck,
|
|
@@ -505,11 +510,11 @@ export function registerAiTools(
|
|
|
505
510
|
},
|
|
506
511
|
/*
|
|
507
512
|
* "value" omitted from required so null = remove (registry rejects null on required
|
|
508
|
-
* args, tools.js:181). See §
|
|
513
|
+
* args, tools.js:181). See `ai.md` §3.6.
|
|
509
514
|
*/
|
|
510
515
|
required: ["key"],
|
|
511
516
|
},
|
|
512
|
-
async execute(args) {
|
|
517
|
+
async execute(args, ctx) {
|
|
513
518
|
const tab = getTab();
|
|
514
519
|
if (!tab) {
|
|
515
520
|
return noDocError();
|
|
@@ -522,20 +527,15 @@ export function registerAiTools(
|
|
|
522
527
|
};
|
|
523
528
|
}
|
|
524
529
|
return applyAndValidate(
|
|
530
|
+
ctx.ledger,
|
|
525
531
|
tab,
|
|
532
|
+
/* The recording def mutators, for the reason `add_state` gives. Removing the last key
|
|
533
|
+
drops the empty `state` object, as the Inspector's own removal does. */
|
|
526
534
|
(t) => {
|
|
527
|
-
/*
|
|
528
|
-
* Directly mutate — bypass mutateUpdateProperty because its "" → delete behaviour
|
|
529
|
-
* (transact.ts:248) is wrong for state defaults (e.g. "title": "").
|
|
530
|
-
*/
|
|
531
|
-
const { state } = t.doc.document;
|
|
532
|
-
if (!state) {
|
|
533
|
-
return;
|
|
534
|
-
}
|
|
535
535
|
if (value == null) {
|
|
536
|
-
|
|
536
|
+
mutateRemoveDef(t, key);
|
|
537
537
|
} else {
|
|
538
|
-
|
|
538
|
+
mutateAddDef(t, key, value as Record<string, JsonValue>);
|
|
539
539
|
}
|
|
540
540
|
},
|
|
541
541
|
value == null ? `Removed state "${key}".` : `Updated state "${key}".`,
|
|
@@ -575,7 +575,7 @@ export function registerAiTools(
|
|
|
575
575
|
},
|
|
576
576
|
required: ["fromPath", "toParentPath", "toIndex"],
|
|
577
577
|
},
|
|
578
|
-
async execute(args) {
|
|
578
|
+
async execute(args, ctx) {
|
|
579
579
|
const tab = getTab();
|
|
580
580
|
if (!tab) {
|
|
581
581
|
return noDocError();
|
|
@@ -601,6 +601,7 @@ export function registerAiTools(
|
|
|
601
601
|
};
|
|
602
602
|
}
|
|
603
603
|
return applyAndValidate(
|
|
604
|
+
ctx.ledger,
|
|
604
605
|
tab,
|
|
605
606
|
(t) => mutateMoveNode(t, fromPath, toParentPath, toIndex),
|
|
606
607
|
`Moved node from ${JSON.stringify(fromPath)} to ${JSON.stringify([...toParentPath, "children", toIndex])}.`,
|
|
@@ -636,14 +637,19 @@ export function registerAiTools(
|
|
|
636
637
|
},
|
|
637
638
|
required: ["path", "content"],
|
|
638
639
|
},
|
|
639
|
-
async execute(args) {
|
|
640
|
+
async execute(args, ctx) {
|
|
640
641
|
if (!saveFile) {
|
|
641
642
|
return {
|
|
642
643
|
success: false,
|
|
643
644
|
error: "File operations are not available in this environment.",
|
|
644
645
|
};
|
|
645
646
|
}
|
|
646
|
-
const { path
|
|
647
|
+
const { path, content } = args as { path: string; content: object };
|
|
648
|
+
// Inside the project or not at all (ai.md §4): the platform write is given this path.
|
|
649
|
+
const relPath = normalizeRelPath(path);
|
|
650
|
+
if (relPath === null) {
|
|
651
|
+
return invalidPathResult(path);
|
|
652
|
+
}
|
|
647
653
|
const dirtyError = dirtyTabError(relPath);
|
|
648
654
|
if (dirtyError) {
|
|
649
655
|
return dirtyError;
|
|
@@ -666,17 +672,17 @@ export function registerAiTools(
|
|
|
666
672
|
}
|
|
667
673
|
}
|
|
668
674
|
try {
|
|
669
|
-
// The save serializer, layout-less (
|
|
675
|
+
// The save serializer, layout-less (`@jxsuite/schema/json-layout`): the assistant supplied a
|
|
670
676
|
// Value, not a text, so the formatter's layout for fresh output is the right one.
|
|
671
677
|
await saveFile(relPath, serializeJson(content, null));
|
|
672
|
-
|
|
678
|
+
ctx.ledger.record({ disk: true, ok: true, path: relPath, tool: "create_component" });
|
|
673
679
|
return {
|
|
674
680
|
success: true,
|
|
675
681
|
summary: await reconcileAfterWrite(relPath, `Created component at "${relPath}".`),
|
|
676
682
|
};
|
|
677
683
|
} catch (error) {
|
|
678
684
|
const message = error instanceof Error ? error.message : String(error);
|
|
679
|
-
|
|
685
|
+
ctx.ledger.record({
|
|
680
686
|
disk: true,
|
|
681
687
|
error: message,
|
|
682
688
|
ok: false,
|
|
@@ -715,14 +721,18 @@ export function registerAiTools(
|
|
|
715
721
|
},
|
|
716
722
|
required: ["path", "content"],
|
|
717
723
|
},
|
|
718
|
-
async execute(args) {
|
|
724
|
+
async execute(args, ctx) {
|
|
719
725
|
if (!saveFile) {
|
|
720
726
|
return {
|
|
721
727
|
success: false,
|
|
722
728
|
error: "File operations are not available in this environment.",
|
|
723
729
|
};
|
|
724
730
|
}
|
|
725
|
-
const { path
|
|
731
|
+
const { path, content } = args as { path: string; content: object };
|
|
732
|
+
const relPath = normalizeRelPath(path);
|
|
733
|
+
if (relPath === null) {
|
|
734
|
+
return invalidPathResult(path);
|
|
735
|
+
}
|
|
726
736
|
const dirtyError = dirtyTabError(relPath);
|
|
727
737
|
if (dirtyError) {
|
|
728
738
|
return dirtyError;
|
|
@@ -747,14 +757,14 @@ export function registerAiTools(
|
|
|
747
757
|
try {
|
|
748
758
|
// As `create_component`: a value the assistant supplied, written in the formatter's layout.
|
|
749
759
|
await saveFile(relPath, serializeJson(content, null));
|
|
750
|
-
|
|
760
|
+
ctx.ledger.record({ disk: true, ok: true, path: relPath, tool: "create_page" });
|
|
751
761
|
return {
|
|
752
762
|
success: true,
|
|
753
763
|
summary: await reconcileAfterWrite(relPath, `Created page at "${relPath}".`),
|
|
754
764
|
};
|
|
755
765
|
} catch (error) {
|
|
756
766
|
const message = error instanceof Error ? error.message : String(error);
|
|
757
|
-
|
|
767
|
+
ctx.ledger.record({
|
|
758
768
|
disk: true,
|
|
759
769
|
error: message,
|
|
760
770
|
ok: false,
|
|
@@ -773,9 +783,10 @@ export function registerAiTools(
|
|
|
773
783
|
/*
|
|
774
784
|
* `open_document` is NOT here any more. It is the projection of the `document.open` record
|
|
775
785
|
* (`workspace/workspace.ts`'s `tabCommands`), so the person's palette row and the model's tool
|
|
776
|
-
* are one `run` and one gate (§12.4). The hand tool read `getTab()`
|
|
777
|
-
* whatever it found a success — a missing file left the previous
|
|
778
|
-
* "Switched to" — and it re-anchored the undo batch itself, which
|
|
779
|
-
* after EVERY tool since project adoption started replacing tabs
|
|
786
|
+
* are one `run` and one gate (`studio-ui-guidelines.md` §12.4). The hand tool read `getTab()`
|
|
787
|
+
* after the open and called whatever it found a success — a missing file left the previous
|
|
788
|
+
* document active and reported "Switched to" — and it re-anchored the undo batch itself, which
|
|
789
|
+
* `tool-executor.ts` has done after EVERY tool since project adoption started replacing tabs
|
|
790
|
+
* mid-loop.
|
|
780
791
|
*/
|
|
781
792
|
}
|
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
* with:
|
|
9
9
|
*
|
|
10
10
|
* 1. {@link snapshotBeforeWrite} reads the document's schema errors and render state BEFORE the
|
|
11
|
-
* write, because the
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* write, because the loop's signal (specs/ai.md §3.1) is "what did this edit newly introduce",
|
|
12
|
+
* not "is the document valid": a page that was already broken must not make every subsequent
|
|
13
|
+
* edit read as the culprit.
|
|
14
14
|
* 2. {@link reportDocumentWrite} compares AFTER the write, translating each new schema error into a
|
|
15
15
|
* fix hint the model can act on, running the render check only when rendering worked before,
|
|
16
16
|
* and appending the soft token-discipline hints to a success.
|
|
17
17
|
*
|
|
18
|
-
* The change stays applied either way (optimistic apply + undo,
|
|
18
|
+
* The change stays applied either way (optimistic apply + undo, specs/ai.md §3.1); reporting the errors lets
|
|
19
19
|
* the agent loop self-correct on the next round.
|
|
20
20
|
*
|
|
21
21
|
* @license MIT
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Ai-writes.ts — what the assistant changed, per turn, and whether you can take it back.
|
|
3
3
|
*
|
|
4
|
-
* §
|
|
4
|
+
* `ai.md` §3.2 (the accountable turn). Two facts about an assistant turn were legible only to the
|
|
5
|
+
* model:
|
|
5
6
|
*
|
|
6
7
|
* 1. **Which files it changed.** The loop collected `result.summary` strings and fed them back into
|
|
7
8
|
* the conversation; the human got a paragraph of prose in which "I updated the hero" and "I
|
|
@@ -17,27 +18,15 @@
|
|
|
17
18
|
*
|
|
18
19
|
* The tools record; the loop bounds the turn; the panel renders. Nothing here knows about lit, and
|
|
19
20
|
* nothing here imports the loop, so a tool can be honest without either.
|
|
21
|
+
*
|
|
22
|
+
* @docs studio/ai/chat
|
|
20
23
|
*/
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
tool: string;
|
|
28
|
-
/**
|
|
29
|
-
* True when the change went to disk with no transaction behind it.
|
|
30
|
-
*
|
|
31
|
-
* This is the undo caveat, as a fact rather than a sentence: a `disk` write is NOT reachable by
|
|
32
|
-
* ⌘Z, by the tab's history, or by "Restore to here". The panel says so, in those words, to the
|
|
33
|
-
* person who would otherwise press ⌘Z and believe it worked.
|
|
34
|
-
*/
|
|
35
|
-
disk: boolean;
|
|
36
|
-
/** False when the tool reported a failure — a listed attempt that changed nothing. */
|
|
37
|
-
ok: boolean;
|
|
38
|
-
/** Why it failed, when it did. */
|
|
39
|
-
error?: string;
|
|
40
|
-
}
|
|
25
|
+
import type { Message } from "@jxsuite/ai/chat-state";
|
|
26
|
+
import { createLedger } from "@jxsuite/ai/tools";
|
|
27
|
+
import type { AiWrite, WriteLedger } from "@jxsuite/ai/tools";
|
|
28
|
+
|
|
29
|
+
export type { AiWrite } from "@jxsuite/ai/tools";
|
|
41
30
|
|
|
42
31
|
/** Every write recorded during one assistant turn, in the order the tools made them. */
|
|
43
32
|
export interface AiTurn {
|
|
@@ -52,52 +41,42 @@ const turns: AiTurn[] = [];
|
|
|
52
41
|
/** How many turns of ledger are kept. Older ones drop their summary; the messages remain. */
|
|
53
42
|
export const MAX_TURNS = 50;
|
|
54
43
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
* Start recording. Called by the agent loop once per user turn.
|
|
59
|
-
*
|
|
60
|
-
* Idempotent on the same id so a nested or re-entered loop cannot split one turn's writes across
|
|
61
|
-
* two ledgers — which is the same class of bug as the batch that was opened against whichever tab
|
|
62
|
-
* happened to be active.
|
|
63
|
-
*
|
|
64
|
-
* @param {string} id
|
|
65
|
-
*/
|
|
66
|
-
export function beginTurn(id: string): void {
|
|
67
|
-
if (open?.id === id) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
open = { id, writes: [] };
|
|
44
|
+
/** One turn's ledger, named for the turn it records. */
|
|
45
|
+
export interface TurnLedger extends WriteLedger {
|
|
46
|
+
readonly turnId: string;
|
|
71
47
|
}
|
|
72
48
|
|
|
73
49
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
50
|
+
* A fresh ledger for one turn. The loop opens it when the turn starts, hands it to every call as
|
|
51
|
+
* `ctx.ledger`, and files it with {@link fileTurn} when the turn ends.
|
|
52
|
+
*
|
|
53
|
+
* Per turn, not a module slot: the slot this replaced was one ledger for the whole window, so a
|
|
54
|
+
* tool run outside the loop recorded into whichever turn happened to be open, and two turns could
|
|
55
|
+
* not each keep their own.
|
|
76
56
|
*
|
|
77
|
-
* @param {
|
|
57
|
+
* @param {string} turnId - The turn it records, which also names the actor its calls run as
|
|
58
|
+
* @returns {TurnLedger}
|
|
78
59
|
*/
|
|
79
|
-
export function
|
|
80
|
-
|
|
60
|
+
export function openTurnLedger(turnId: string): TurnLedger {
|
|
61
|
+
return Object.assign(createLedger(), { turnId });
|
|
81
62
|
}
|
|
82
63
|
|
|
83
64
|
/**
|
|
84
|
-
*
|
|
65
|
+
* File a finished turn's writes under `anchor`, the message the transcript draws its summary under.
|
|
85
66
|
*
|
|
86
|
-
* The
|
|
87
|
-
* turn starts
|
|
67
|
+
* The anchor arrives at the END because it is an assistant message's id, which does not exist when
|
|
68
|
+
* the turn starts. A turn with no drawn message is filed under `""`, which no message has, so the
|
|
69
|
+
* transcript draws its changes nowhere. A turn that wrote nothing files nothing.
|
|
88
70
|
*
|
|
89
|
-
* @param {string}
|
|
90
|
-
* @
|
|
71
|
+
* @param {string} anchor - The message the turn's changes are drawn under
|
|
72
|
+
* @param {readonly AiWrite[]} writes - The turn's ledger
|
|
73
|
+
* @returns {AiWrite[]} What was filed
|
|
91
74
|
*/
|
|
92
|
-
export function
|
|
93
|
-
if (
|
|
94
|
-
return [];
|
|
95
|
-
}
|
|
96
|
-
const turn: AiTurn = { id, writes: open.writes };
|
|
97
|
-
open = null;
|
|
98
|
-
if (turn.writes.length === 0) {
|
|
75
|
+
export function fileTurn(anchor: string, writes: readonly AiWrite[]): AiWrite[] {
|
|
76
|
+
if (writes.length === 0) {
|
|
99
77
|
return [];
|
|
100
78
|
}
|
|
79
|
+
const turn: AiTurn = { id: anchor, writes: [...writes] };
|
|
101
80
|
turns.push(turn);
|
|
102
81
|
while (turns.length > MAX_TURNS) {
|
|
103
82
|
turns.shift();
|
|
@@ -147,8 +126,48 @@ export function summarizeWrites(writes: AiWrite[]): string {
|
|
|
147
126
|
return parts.join(" · ");
|
|
148
127
|
}
|
|
149
128
|
|
|
150
|
-
/**
|
|
129
|
+
/**
|
|
130
|
+
* Drop every ledger: New Chat (the messages they are drawn under are gone), leaving a project
|
|
131
|
+
* (their paths name the project left behind; `setWorkspaceProject`), and tests.
|
|
132
|
+
*/
|
|
151
133
|
export function resetAiWrites(): void {
|
|
152
134
|
turns.splice(0);
|
|
153
|
-
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* The message a turn's changes are filed under: its last DRAWN assistant message, which is the one
|
|
139
|
+
* the transcript renders the changed-files summary beneath (panels/ai-chat/chat-view.ts), and so
|
|
140
|
+
* the id {@link fileTurn} is given.
|
|
141
|
+
*
|
|
142
|
+
* Not simply the last message. A turn can end on a `tool` reply (a Stop during the last call, or a
|
|
143
|
+
* cap reached with nothing applied), on a final round that said nothing, or after a stream error
|
|
144
|
+
* removed its round's partial; filed under any of those, the summary and Restore were drawn under
|
|
145
|
+
* nothing and the author never saw what the turn changed. The cap message counts: it is drawn.
|
|
146
|
+
*
|
|
147
|
+
* **An anchor is only ever this turn's.** The scan must MEET the message the turn answers, and only
|
|
148
|
+
* what it passed on the way counts. A transcript that no longer holds that message was replaced
|
|
149
|
+
* under the turn (another chat opened from Chat History while it waited on a tool), and every
|
|
150
|
+
* message in it belongs to some other conversation: filing there would draw "Changed 1 file", and
|
|
151
|
+
* Restore, under a reply that changed nothing.
|
|
152
|
+
*
|
|
153
|
+
* @param {readonly Message[]} messages
|
|
154
|
+
* @param {string} [userId] - The id of the user message this turn answers; without one, the whole
|
|
155
|
+
* transcript is the turn
|
|
156
|
+
* @returns {string | null} Null when the turn drew nothing (so it ran no tool either), or when its
|
|
157
|
+
* user message has left the transcript
|
|
158
|
+
*/
|
|
159
|
+
export function turnAnchor(messages: readonly Message[], userId?: string): string | null {
|
|
160
|
+
let anchor: string | null = null;
|
|
161
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
162
|
+
const message = messages[index]!;
|
|
163
|
+
if (message.id === userId) {
|
|
164
|
+
return anchor;
|
|
165
|
+
}
|
|
166
|
+
const drawn =
|
|
167
|
+
message.role === "assistant" && (message.content || (message.toolCalls?.length ?? 0) > 0);
|
|
168
|
+
if (anchor === null && drawn) {
|
|
169
|
+
anchor = message.id;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return userId === undefined ? anchor : null;
|
|
154
173
|
}
|