@jxsuite/studio 4.1.0 → 5.0.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/README.md +1 -1
- package/data/elements-meta.json +5 -15
- package/data/html-meta.json +51 -6
- package/dist/chunks/{2020-n6tfwnda.js → 2020-g79n01v9.js} +14 -14
- package/dist/chunks/action-group-sp6tna8v.js +118 -0
- package/dist/chunks/adapter-nj7tnjfn.js +13 -0
- package/dist/chunks/cf-account-picker-fyf63pmy.js +495 -0
- package/dist/chunks/cf-settings-71bv5zn9.js +20 -0
- package/dist/chunks/color-area-v17df7b8.js +154 -0
- package/dist/chunks/color-field-anwybnkn.js +208 -0
- package/dist/chunks/color-slider-44ez96fc.js +79 -0
- package/dist/chunks/color-xgg3kfw7.js +35 -0
- package/dist/chunks/combobox-5e7jev23.js +236 -0
- package/dist/chunks/components-2w1f35qs.js +18 -0
- package/dist/chunks/content-source-3wqeas45.js +46 -0
- package/dist/chunks/convert-to-repeater-d0cgnb6d.js +639 -0
- package/dist/chunks/dialog-s890b918.js +85 -0
- package/dist/chunks/editor-3qhbg51x.js +35 -0
- package/dist/chunks/entry-commands-d6g50j7s.js +42 -0
- package/dist/chunks/extension-sections-fy7q4fe9.js +651 -0
- package/dist/chunks/file-ops-3btj6st0.js +47 -0
- package/dist/chunks/files-ef8fgs3w.js +72 -0
- package/dist/chunks/format-host-3c33nwr8.js +40 -0
- package/dist/chunks/fs-events-13tj6sex.js +18 -0
- package/dist/chunks/git-panel-4wp2rn9b.js +49 -0
- package/dist/chunks/icons-yy10wk9r.js +402 -0
- package/dist/chunks/iframe-entry-15g3n6xv.js +114 -0
- package/dist/chunks/iframe-entry-b27vq81n.js +203 -0
- package/dist/chunks/iframe-entry-nbyxee1y.js +154 -0
- package/dist/chunks/iframe-host-jgesd924.js +115 -0
- package/dist/chunks/{index-wcnt3r9g.js → index-mdmyx6s2.js} +11 -11
- package/dist/chunks/index-n4j7qxyr.js +109 -0
- package/dist/chunks/{javascript-q8kpmfah.js → javascript-xvrfrpkb.js} +8 -9
- package/dist/chunks/{jsonMode-dkr040ga.js → jsonMode-tyx2kj6p.js} +526 -527
- package/dist/chunks/jx-validate-j0wxk72w.js +15 -0
- package/dist/chunks/layers-1nq5evsh.js +29 -0
- package/dist/chunks/layers-panel-ar2gh90g.js +40 -0
- package/dist/chunks/listbox-6v2ac08j.js +44 -0
- package/dist/chunks/lit-html-m244w9nh.js +17 -0
- package/dist/chunks/media-picker-2qbetq73.js +33 -0
- package/dist/chunks/menu-q99sy9rf.js +285 -0
- package/dist/chunks/menu-rf7n72mf.js +14 -0
- package/dist/chunks/{monaco-binding-rj6xpx9y.js → monaco-binding-sw6en4dw.js} +12 -15
- package/dist/chunks/monaco-buffer-bhy3hb6m.js +24 -0
- package/dist/chunks/{monaco-setup-vx9bhp1d.js → monaco-setup-8r62fj6y.js} +27 -37
- package/dist/chunks/notify-628t0anh.js +21 -0
- package/dist/chunks/number-field-5cndyft8.js +72 -0
- package/dist/chunks/parse-0zx14y15.js +10 -0
- package/dist/chunks/platform-ts9cg1a4.js +15 -0
- package/dist/chunks/popover-47z3ytva.js +25 -0
- package/dist/chunks/publish-panel-98f7sr3g.js +27 -0
- package/dist/chunks/quick-search-srzz83k2.js +45 -0
- package/dist/chunks/references-n553f0e1.js +26 -0
- package/dist/chunks/right-panel-rrgx2dn3.js +45 -0
- package/dist/chunks/section-registry-5pq32t0q.js +25 -0
- package/dist/chunks/select-2r16ez41.js +49 -0
- package/dist/chunks/split-g7nm1rgb.js +191 -0
- package/dist/chunks/studio-0hqbpbd0.js +62 -0
- package/dist/chunks/studio-18avnhjp.js +2430 -0
- package/dist/chunks/{studio-kryvsw6j.js → studio-1k325pwf.js} +48 -48
- package/dist/chunks/{studio-msd847zn.js → studio-25hkdrkw.js} +357 -86
- package/dist/chunks/studio-26d7myd1.js +258 -0
- package/dist/chunks/{studio-4r1pv6zf.js → studio-38emantv.js} +22 -5
- package/dist/chunks/studio-3gxv60tn.js +422 -0
- package/dist/chunks/{studio-fbf92ckf.js → studio-3hxkd92t.js} +275 -163
- package/dist/chunks/studio-4m0haseg.js +185 -0
- package/dist/chunks/{studio-ynxwkrzd.js → studio-4wbb9ct0.js} +1953 -1785
- package/dist/chunks/{studio-1wagb550.js → studio-5523bpzt.js} +11 -11
- package/dist/chunks/{studio-x4qkdat5.js → studio-57mf66w2.js} +1749 -2481
- package/dist/chunks/studio-5jy81t3s.js +17463 -0
- package/dist/chunks/studio-5rt7sqa0.js +1168 -0
- package/dist/chunks/studio-5te5tqng.js +81 -0
- package/dist/chunks/{studio-p4ngc046.js → studio-6ewqv390.js} +9 -9
- package/dist/chunks/{studio-9ab8gqsm.js → studio-6nzaktvy.js} +7 -7
- package/dist/chunks/studio-6xa21ej9.js +28 -0
- package/dist/chunks/{studio-5ehhackm.js → studio-727xh0e3.js} +19 -19
- package/dist/chunks/{studio-ahn3pdgx.js → studio-79y5sz51.js} +52 -38
- package/dist/chunks/{studio-gcye7qka.js → studio-7e11jpa9.js} +21 -21
- package/dist/chunks/{studio-q7v10cg2.js → studio-7r42c6ca.js} +17 -7
- package/dist/chunks/studio-7xptrg0j.js +954 -0
- package/dist/chunks/{studio-cft9r4r7.js → studio-81390n6f.js} +22 -22
- package/dist/chunks/{studio-q43a5y9h.js → studio-8jn5yhyn.js} +16 -16
- package/dist/chunks/studio-8qtrpzr3.js +839 -0
- package/dist/chunks/{studio-dg7zyt5x.js → studio-98b3z07n.js} +1 -1
- package/dist/chunks/studio-9xxz1q69.js +1356 -0
- package/dist/chunks/studio-akbww0j5.js +13615 -0
- package/dist/chunks/{studio-x813zp48.js → studio-arfpe389.js} +30 -30
- package/dist/chunks/{studio-trxrabqf.js → studio-az3zd49t.js} +475 -283
- package/dist/chunks/studio-cpvb2ed1.js +1171 -0
- package/dist/chunks/{studio-f3wqjtk5.js → studio-d0300636.js} +5076 -18
- package/dist/chunks/{studio-bgxt93ga.js → studio-d055c37e.js} +13 -13
- package/dist/chunks/studio-d1ja2x9r.js +362 -0
- package/dist/chunks/{studio-b9b6t1dg.js → studio-e1f24eff.js} +5664 -5666
- package/dist/chunks/{studio-3bqpr0zh.js → studio-em9nw8eh.js} +299 -110
- package/dist/chunks/studio-erdatzvp.js +1190 -0
- package/dist/chunks/{studio-g1wdche0.js → studio-esrxgwh6.js} +80 -80
- package/dist/chunks/studio-fabxk6r9.js +2811 -0
- package/dist/chunks/{studio-3n4ndrzh.js → studio-h4nvnjvz.js} +303 -303
- package/dist/chunks/{studio-dm19q43r.js → studio-hhvs4sdg.js} +10 -10
- package/dist/chunks/{studio-q9jd1a60.js → studio-kcgff4r0.js} +15 -18
- package/dist/chunks/{studio-685s0ytq.js → studio-nqgawfks.js} +572 -572
- package/dist/chunks/{studio-xrapxwwc.js → studio-p1dw8wfw.js} +55 -29
- package/dist/chunks/studio-pm6hgy7r.js +4763 -0
- package/dist/chunks/studio-q0a3yk4n.js +33 -0
- package/dist/chunks/{studio-wcy8trfx.js → studio-q3x06vmj.js} +23 -22
- package/dist/chunks/studio-qahxvnan.js +111 -0
- package/dist/chunks/{studio-53n8bp04.js → studio-r6xmmtfh.js} +1 -1
- package/dist/chunks/{studio-2b946z26.js → studio-rkstz940.js} +3658 -6134
- package/dist/chunks/{studio-tvgyw9xw.js → studio-rnyf3f8e.js} +83 -250
- package/dist/chunks/studio-rpyah2ew.js +2805 -0
- package/dist/chunks/studio-rs76k67r.js +12 -0
- package/dist/chunks/{studio-jkftxyvz.js → studio-s6z853aq.js} +5858 -2152
- package/dist/chunks/{studio-6yw4y3my.js → studio-sgdc5pfc.js} +8 -8
- package/dist/chunks/studio-tb3ev01j.js +763 -0
- package/dist/chunks/{studio-t5wqwzdh.js → studio-tjjk7jvk.js} +1 -1
- package/dist/chunks/studio-vhv5tgxm.js +51 -0
- package/dist/chunks/{studio-qjy2z9bv.js → studio-vz68yn9e.js} +32 -137
- package/dist/chunks/studio-wqpngpsk.js +174 -0
- package/dist/chunks/{studio-b55y6ewx.js → studio-x3rty6tr.js} +58 -58
- package/dist/chunks/studio-xsykpvsh.js +8448 -0
- package/dist/chunks/{studio-92ab2jqe.js → studio-y9zedyjn.js} +14 -14
- package/dist/chunks/studio-yxa81f45.js +12 -0
- package/dist/chunks/swatch-group-t0n6h2rn.js +139 -0
- package/dist/chunks/tabs-wbp1cdws.js +156 -0
- package/dist/chunks/textfield-abk6qyee.js +73 -0
- package/dist/chunks/toast-780pvfbv.js +27 -0
- package/dist/chunks/toast-host-3sqv46sn.js +116 -0
- package/dist/chunks/toolbar-xbh5bhtr.js +194 -0
- package/dist/chunks/tooltip-eptb614w.js +369 -0
- package/dist/chunks/tree-a73p0pqm.js +367 -0
- package/dist/chunks/{tsMode-76kr53q0.js → tsMode-jy4m86rv.js} +83 -85
- package/dist/chunks/workspace-wrew8zk4.js +69 -0
- package/dist/chunks/{ws-client-6shc5rze.js → ws-client-ny4zc51r.js} +20 -22
- package/dist/iframe-entry.js +1780 -667
- package/dist/studio.js +52819 -36480
- package/dist/workers/editor.worker.js +1040 -1085
- package/dist/workers/json.worker.js +1946 -1991
- package/dist/workers/ts.worker.js +4960 -5005
- package/index.html +1 -0
- package/package.json +18 -52
- package/src/about/about-modal.ts +72 -123
- package/src/browse/library-commands.ts +20 -49
- package/src/browse/library-layouts.ts +87 -218
- package/src/browse/library-pane.ts +406 -438
- package/src/canvas/canvas-helpers.ts +0 -49
- package/src/canvas/canvas-live-render.ts +25 -2
- package/src/canvas/canvas-render.ts +666 -231
- package/src/canvas/canvas-surface.ts +0 -1
- package/src/canvas/canvas-utils.ts +310 -117
- package/src/canvas/dialog-path.ts +76 -0
- package/src/canvas/dialog-state.ts +67 -0
- package/src/canvas/diff-marks.ts +252 -0
- package/src/canvas/diff-toolbar.ts +349 -0
- package/src/canvas/diff-view.ts +123 -0
- package/src/canvas/edit-width-drag.ts +187 -0
- package/src/canvas/edit-width.ts +150 -0
- package/src/canvas/iframe-entry.ts +125 -8
- package/src/canvas/iframe-host.ts +271 -15
- package/src/canvas/iframe-interaction.ts +133 -13
- package/src/canvas/iframe-patch.ts +7 -1
- package/src/canvas/iframe-position.ts +5 -6
- package/src/canvas/iframe-protocol.ts +106 -5
- package/src/canvas/iframe-render.ts +373 -5
- package/src/canvas/iframe-slash.ts +2 -2
- package/src/canvas/iframe-subtree.ts +5 -8
- package/src/canvas/path-mapping.ts +25 -3
- package/src/canvas/popover-path.ts +106 -0
- package/src/canvas/popover-state.ts +155 -0
- package/src/canvas/surface-registry.ts +21 -10
- package/src/collab/presence-chips.ts +51 -73
- package/src/commands/app-commands.ts +10 -0
- package/src/commands/budget.ts +12 -0
- package/src/commands/command-args.ts +369 -6
- package/src/commands/defaults.ts +44 -14
- package/src/commands/live-context.ts +11 -5
- package/src/commands/reference.ts +12 -2
- package/src/commands/registry.ts +165 -8
- package/src/content/entry-commands.ts +16 -23
- package/src/content/entry-editor.ts +144 -101
- package/src/editor/canvas-slash-bridge.ts +2 -2
- package/src/editor/context-menu.ts +49 -200
- package/src/editor/convert-to-component.ts +42 -82
- package/src/editor/convert-to-repeater.ts +132 -173
- package/src/editor/inline-edit.ts +11 -0
- package/src/editor/shortcuts.ts +65 -26
- package/src/editor/slash-menu.ts +150 -157
- package/src/files/file-ops.ts +117 -11
- package/src/files/files.ts +943 -660
- package/src/files/json-layout.ts +474 -0
- package/src/files/serialize-document.ts +8 -1
- package/src/format/convert-file.ts +16 -10
- package/src/format/format-host.ts +50 -1
- package/src/github/github-auth.ts +71 -29
- package/src/github/github-publish.ts +57 -65
- package/src/grid/cell-editors.ts +46 -4
- package/src/grid/cell-popovers.ts +65 -62
- package/src/grid/grid-open.ts +86 -64
- package/src/grid/grid-panel.ts +390 -469
- package/src/grid/redirects-grid.ts +40 -44
- package/src/hosting/layout.ts +2 -0
- package/src/i18n/i18n-commands.ts +127 -62
- package/src/media/media-pane.ts +83 -133
- package/src/new-project/add-repo-modal.ts +125 -176
- package/src/new-project/import-tab.ts +181 -252
- package/src/new-project/location-fields.ts +93 -160
- package/src/new-project/new-project-modal.ts +376 -367
- package/src/packages/jxsuite-update.ts +76 -18
- package/src/panels/activity-panel.ts +137 -104
- package/src/panels/ai-chat/chat-markdown.ts +15 -13
- package/src/panels/ai-chat/chat-view.ts +307 -472
- package/src/panels/ai-chat/composer.ts +175 -163
- package/src/panels/ai-chat/sessions-view.ts +31 -62
- package/src/panels/ai-panel.ts +210 -185
- package/src/panels/block-action-bar.ts +437 -643
- package/src/panels/bottom-dock.ts +112 -47
- package/src/panels/chat-panel.ts +19 -14
- package/src/panels/data-explorer.ts +141 -102
- package/src/panels/data-grid.ts +183 -150
- package/src/panels/dnd.ts +75 -20
- package/src/panels/editors.ts +26 -19
- package/src/panels/elements-panel.ts +243 -186
- package/src/panels/empty-state.ts +26 -42
- package/src/panels/events-panel.ts +950 -729
- package/src/panels/formula-workspace.ts +434 -207
- package/src/panels/frontmatter-fields.ts +185 -153
- package/src/panels/frontmatter-panel.ts +411 -187
- package/src/panels/git-diff-open.ts +148 -0
- package/src/panels/git-panel.ts +679 -605
- package/src/panels/head-panel.ts +617 -435
- package/src/panels/i18n-panel.ts +153 -133
- package/src/panels/imports-panel.ts +388 -414
- package/src/panels/jump-bar.ts +174 -147
- package/src/panels/layers-panel.ts +785 -671
- package/src/panels/left-panel.ts +166 -94
- package/src/panels/navigator-panels.ts +1 -1
- package/src/panels/pane-context.ts +529 -677
- package/src/panels/pane-grid.ts +265 -241
- package/src/panels/panel-registry.ts +15 -4
- package/src/panels/problems-panel.ts +177 -99
- package/src/panels/properties-panel.ts +1525 -966
- package/src/panels/provenance.ts +31 -113
- package/src/panels/quick-search.ts +217 -172
- package/src/panels/right-panel.ts +135 -169
- package/src/panels/seo-modal.ts +346 -167
- package/src/panels/settings-menu.ts +109 -646
- package/src/panels/settings-pane.ts +102 -86
- package/src/panels/signals-panel.ts +1318 -1172
- package/src/panels/statement-editor.ts +556 -428
- package/src/panels/style-panel.ts +1367 -630
- package/src/panels/style-utils.ts +36 -32
- package/src/panels/stylebook-doc.ts +30 -73
- package/src/panels/stylebook-layers-panel.ts +202 -109
- package/src/panels/stylebook-panel.ts +124 -88
- package/src/panels/tab-strip.ts +385 -287
- package/src/platforms/cloud.ts +106 -16
- package/src/platforms/devserver.ts +13 -0
- package/src/publish/deploy-checklist.ts +124 -78
- package/src/publish/publish-commands.ts +4 -12
- package/src/publish/publish-panel.ts +134 -328
- package/src/services/a11y-report.ts +100 -43
- package/src/services/ai-command-tools.ts +468 -0
- package/src/services/ai-session-store.ts +6 -0
- package/src/services/ai-system-prompt.ts +110 -18
- package/src/services/ai-tools.ts +28 -143
- package/src/services/ai-write-report.ts +174 -0
- package/src/services/document-assistant.ts +50 -2
- package/src/services/idle.ts +16 -14
- package/src/services/live-surfaces.ts +237 -0
- package/src/services/model-uri.ts +75 -0
- package/src/services/notify.ts +63 -6
- package/src/services/popover-report.ts +325 -0
- package/src/services/surface-registry.ts +56 -0
- package/src/services/tool-executor.ts +6 -0
- package/src/settings/contexts-section.ts +301 -299
- package/src/settings/contributed-section.ts +361 -222
- package/src/settings/css-vars-editor.ts +320 -325
- package/src/settings/defs-editor.ts +572 -535
- package/src/settings/dependencies-editor.ts +79 -107
- package/src/settings/extension-commands.ts +379 -0
- package/src/settings/extension-rows.ts +113 -0
- package/src/settings/extensions-section.ts +287 -0
- package/src/settings/general-settings.ts +130 -181
- package/src/settings/head-editor.ts +10 -305
- package/src/settings/locales-section.ts +146 -196
- package/src/settings/preferences-accounts.ts +1 -1
- package/src/settings/preferences-dialog.ts +350 -330
- package/src/settings/project-sections.ts +108 -181
- package/src/settings/schema-field-ui.ts +11 -399
- package/src/settings/section-registry.ts +8 -1
- package/src/settings/settings-document.ts +31 -29
- package/src/shell/tree.ts +42 -87
- package/src/shell.ts +48 -36
- package/src/studio.ts +90 -67
- package/src/style/token-ref.ts +0 -39
- package/src/surfaces/about.json +263 -0
- package/src/surfaces/about.ts +163 -0
- package/src/surfaces/add-repo.json +486 -0
- package/src/surfaces/add-repo.ts +236 -0
- package/src/surfaces/ai-chat.json +2185 -0
- package/src/surfaces/ai-chat.ts +549 -0
- package/src/surfaces/ai-credentials-form.json +332 -0
- package/src/surfaces/ai-credentials-form.ts +151 -0
- package/src/surfaces/ai-managed-connect.json +108 -0
- package/src/surfaces/ai-managed-connect.ts +123 -0
- package/src/surfaces/ai-model-picker.json +114 -0
- package/src/surfaces/ai-model-picker.ts +139 -0
- package/src/surfaces/block-action-bar.json +640 -0
- package/src/surfaces/block-action-bar.ts +368 -0
- package/src/surfaces/bottom-dock.json +154 -0
- package/src/surfaces/bottom-dock.ts +125 -0
- package/src/surfaces/canvas-stage.json +444 -0
- package/src/surfaces/canvas-stage.ts +253 -0
- package/src/surfaces/cf-account-picker.json +300 -0
- package/src/surfaces/cf-account-picker.ts +214 -0
- package/src/surfaces/commandbar.json +984 -0
- package/src/{panels/toolbar.ts → surfaces/commandbar.ts} +414 -389
- package/src/surfaces/convert-repeater.json +309 -0
- package/src/surfaces/convert-repeater.ts +276 -0
- package/src/surfaces/data-actions.json +157 -0
- package/src/surfaces/data-actions.ts +115 -0
- package/src/surfaces/dialog.json +289 -0
- package/src/surfaces/dialog.ts +343 -0
- package/src/surfaces/diff-toolbar.json +266 -0
- package/src/surfaces/diff-toolbar.ts +139 -0
- package/src/surfaces/doc-header.json +739 -0
- package/src/surfaces/doc-header.ts +223 -0
- package/src/surfaces/empty-state.json +152 -0
- package/src/surfaces/empty-state.ts +173 -0
- package/src/surfaces/entry-editor.json +281 -0
- package/src/surfaces/entry-editor.ts +191 -0
- package/src/surfaces/expression-editor.json +977 -0
- package/src/surfaces/expression-editor.ts +266 -0
- package/src/surfaces/files-panel.json +592 -0
- package/src/surfaces/files-panel.ts +301 -0
- package/src/surfaces/formula-palette.json +364 -0
- package/src/surfaces/formula-palette.ts +340 -0
- package/src/surfaces/git-panel.json +1822 -0
- package/src/surfaces/git-panel.ts +272 -0
- package/src/surfaces/github-auth.json +112 -0
- package/src/surfaces/github-auth.ts +139 -0
- package/src/surfaces/github-publish.json +206 -0
- package/src/surfaces/github-publish.ts +169 -0
- package/src/surfaces/grid-cell.json +218 -0
- package/src/surfaces/grid-cell.ts +120 -0
- package/src/surfaces/grid-open.json +147 -0
- package/src/surfaces/grid-open.ts +87 -0
- package/src/surfaces/grid-panel.json +706 -0
- package/src/surfaces/grid-panel.ts +206 -0
- package/src/surfaces/grid-replace.json +179 -0
- package/src/surfaces/grid-replace.ts +89 -0
- package/src/surfaces/grid-views.json +446 -0
- package/src/surfaces/grid-views.ts +172 -0
- package/src/surfaces/inspector-dock.json +187 -0
- package/src/surfaces/inspector-dock.ts +161 -0
- package/src/surfaces/jump-bar.json +243 -0
- package/src/surfaces/jump-bar.ts +129 -0
- package/src/surfaces/jxsuite-update.json +146 -0
- package/src/surfaces/jxsuite-update.ts +149 -0
- package/src/surfaces/library-pane.json +1509 -0
- package/src/surfaces/library-pane.ts +363 -0
- package/src/surfaces/logic-panel.json +1949 -0
- package/src/surfaces/logic-panel.ts +369 -0
- package/src/surfaces/logic-workspace.json +685 -0
- package/src/surfaces/logic-workspace.ts +238 -0
- package/src/surfaces/media-browser.json +303 -0
- package/src/surfaces/media-browser.ts +145 -0
- package/src/surfaces/media-field.json +163 -0
- package/src/surfaces/media-field.ts +116 -0
- package/src/surfaces/media-pane.json +513 -0
- package/src/surfaces/media-pane.ts +197 -0
- package/src/surfaces/menu.json +232 -0
- package/src/surfaces/menu.ts +375 -0
- package/src/surfaces/navigator-dock.json +113 -0
- package/src/surfaces/navigator-dock.ts +130 -0
- package/src/surfaces/new-project.json +1654 -0
- package/src/surfaces/new-project.ts +411 -0
- package/src/surfaces/palette.json +535 -0
- package/src/surfaces/pane-context.json +1497 -0
- package/src/surfaces/pane-context.ts +513 -0
- package/src/surfaces/pane-grid.json +188 -0
- package/src/surfaces/pane-grid.ts +250 -0
- package/src/surfaces/panel-activity.json +563 -0
- package/src/surfaces/panel-activity.ts +187 -0
- package/src/surfaces/panel-data.json +141 -0
- package/src/surfaces/panel-data.ts +156 -0
- package/src/surfaces/panel-deploy-checklist.json +283 -0
- package/src/surfaces/panel-deploy-checklist.ts +175 -0
- package/src/surfaces/panel-elements.json +367 -0
- package/src/surfaces/panel-elements.ts +175 -0
- package/src/surfaces/panel-i18n.json +569 -0
- package/src/surfaces/panel-i18n.ts +212 -0
- package/src/surfaces/panel-imports.json +780 -0
- package/src/surfaces/panel-imports.ts +209 -0
- package/src/surfaces/panel-outline.json +734 -0
- package/src/surfaces/panel-outline.ts +326 -0
- package/src/surfaces/panel-page.json +1017 -0
- package/src/surfaces/panel-page.ts +240 -0
- package/src/surfaces/panel-problems.json +497 -0
- package/src/surfaces/panel-problems.ts +157 -0
- package/src/surfaces/panel-signals.json +1973 -0
- package/src/surfaces/panel-signals.ts +436 -0
- package/src/surfaces/panel-stylebook-layers.json +317 -0
- package/src/surfaces/panel-stylebook-layers.ts +155 -0
- package/src/surfaces/preferences.json +850 -0
- package/src/surfaces/preferences.ts +332 -0
- package/src/surfaces/progress-modal.json +110 -0
- package/src/surfaces/progress-modal.ts +150 -0
- package/src/surfaces/properties-panel.json +1578 -0
- package/src/surfaces/properties-panel.ts +337 -0
- package/src/surfaces/publish.json +945 -0
- package/src/surfaces/publish.ts +392 -0
- package/src/surfaces/push-plan.json +110 -0
- package/src/surfaces/push-plan.ts +87 -0
- package/src/surfaces/rail.json +255 -0
- package/src/surfaces/rail.ts +206 -0
- package/src/surfaces/reference-field.json +191 -0
- package/src/surfaces/reference-field.ts +168 -0
- package/src/surfaces/schema-builder.json +1011 -0
- package/src/surfaces/schema-builder.ts +181 -0
- package/src/surfaces/schema-form.json +1013 -0
- package/src/surfaces/schema-form.ts +287 -0
- package/src/surfaces/secret-field.json +60 -0
- package/src/surfaces/secret-field.ts +146 -0
- package/src/surfaces/seo.json +932 -0
- package/src/surfaces/seo.ts +334 -0
- package/src/surfaces/settings-contexts.json +538 -0
- package/src/surfaces/settings-contexts.ts +196 -0
- package/src/surfaces/settings-contributed.json +489 -0
- package/src/surfaces/settings-contributed.ts +199 -0
- package/src/surfaces/settings-css-vars.json +820 -0
- package/src/surfaces/settings-css-vars.ts +199 -0
- package/src/surfaces/settings-defs.json +1363 -0
- package/src/surfaces/settings-defs.ts +232 -0
- package/src/surfaces/settings-deploy.json +108 -0
- package/src/surfaces/settings-deploy.ts +135 -0
- package/src/surfaces/settings-extensions.json +486 -0
- package/src/surfaces/settings-extensions.ts +201 -0
- package/src/surfaces/settings-head.json +779 -0
- package/src/surfaces/settings-head.ts +397 -0
- package/src/surfaces/settings-locales.json +420 -0
- package/src/surfaces/settings-locales.ts +137 -0
- package/src/surfaces/settings-overview.json +423 -0
- package/src/surfaces/settings-overview.ts +218 -0
- package/src/surfaces/settings-packages.json +549 -0
- package/src/surfaces/settings-packages.ts +158 -0
- package/src/surfaces/settings-pane.json +147 -0
- package/src/surfaces/settings-pane.ts +150 -0
- package/src/surfaces/settings-rawjson.json +104 -0
- package/src/surfaces/settings-rawjson.ts +107 -0
- package/src/surfaces/shell.json +99 -0
- package/src/surfaces/shell.ts +30 -0
- package/src/surfaces/slash-menu.json +180 -0
- package/src/surfaces/slash-menu.ts +272 -0
- package/src/surfaces/statements.json +607 -0
- package/src/surfaces/statements.ts +238 -0
- package/src/surfaces/statusbar.json +94 -0
- package/src/{panels → surfaces}/statusbar.ts +163 -90
- package/src/surfaces/style-panel.json +1643 -0
- package/src/surfaces/style-panel.ts +338 -0
- package/src/surfaces/stylebook-chrome.json +122 -0
- package/src/surfaces/stylebook-chrome.ts +122 -0
- package/src/surfaces/tab-strip.json +448 -0
- package/src/surfaces/tab-strip.ts +200 -0
- package/src/surfaces/target-line.json +419 -0
- package/src/surfaces/target-line.ts +349 -0
- package/src/surfaces/toasts.json +189 -0
- package/src/surfaces/welcome.json +571 -0
- package/src/surfaces/welcome.ts +338 -0
- package/src/tabs/project-config.ts +71 -15
- package/src/tabs/tab.ts +52 -1
- package/src/types.ts +22 -0
- package/src/ui/ai-credentials-form.ts +104 -100
- package/src/ui/ai-managed-connect.ts +54 -36
- package/src/ui/ai-model-picker.ts +75 -47
- package/src/ui/cf-account-picker.ts +76 -141
- package/src/ui/color-selector.ts +80 -301
- package/src/ui/dynamic-slot.ts +43 -283
- package/src/ui/expression-editor.ts +797 -576
- package/src/ui/field-input.ts +8 -181
- package/src/ui/form-controls.ts +838 -366
- package/src/ui/formula-chips.ts +80 -85
- package/src/ui/kit.ts +33 -0
- package/src/ui/layers.ts +293 -593
- package/src/ui/media-picker.ts +203 -284
- package/src/ui/panel-resize.ts +251 -64
- package/src/ui/popover-surface.ts +153 -0
- package/src/ui/progress-modal.ts +22 -60
- package/src/ui/regions.ts +13 -7
- package/src/ui/schema-form.ts +906 -440
- package/src/ui/surface.ts +122 -0
- package/src/ui/unit-selector.ts +15 -131
- package/src/ui/value-source.ts +14 -0
- package/src/ui/virtual-window.ts +88 -9
- package/src/utils/canvas-media.ts +72 -0
- package/src/utils/edit-display.ts +7 -2
- package/src/utils/element-selectors.ts +69 -0
- package/src/utils/link-target.ts +2 -2
- package/src/utils/studio-utils.ts +0 -10
- package/src/view.ts +15 -0
- package/src/workspace/pane-derive.ts +36 -14
- package/src/workspace/workspace.ts +15 -6
- package/styles/canvas.css +72 -467
- package/styles/forced-colors.css +48 -82
- package/styles/forced-colors.json +41 -0
- package/styles/inspector.css +2 -1301
- package/styles/overlays.css +96 -2823
- package/styles/panels.css +6 -1423
- package/styles/shell-frame.css +272 -0
- package/styles/shell-frame.json +227 -0
- package/styles/shell.css +0 -1568
- package/styles/tokens.css +65 -136
- package/styles/tokens.json +84 -0
- package/dist/chunks/Overlay.dev-etqjwyf9.js +0 -23
- package/dist/chunks/adapter-jkcyhgtr.js +0 -14
- package/dist/chunks/cf-account-picker-en86v7x7.js +0 -163
- package/dist/chunks/cf-settings-45fgyx8y.js +0 -21
- package/dist/chunks/components-zmx10t62.js +0 -21
- package/dist/chunks/content-source-48xg86wj.js +0 -49
- package/dist/chunks/convert-to-repeater-43tfn7cs.js +0 -268
- package/dist/chunks/editor-cptq5jp1.js +0 -37
- package/dist/chunks/entry-commands-e63f1xc0.js +0 -46
- package/dist/chunks/extension-sections-txv08xxg.js +0 -311
- package/dist/chunks/file-ops-wbh4hhsx.js +0 -48
- package/dist/chunks/files-3kfkwhk4.js +0 -93
- package/dist/chunks/flow-qbfgrpeh.js +0 -634
- package/dist/chunks/focus-trap.esm-9t2drp0g.js +0 -1025
- package/dist/chunks/focus-visible-6eg70z18.js +0 -167
- package/dist/chunks/format-host-w16q81tf.js +0 -43
- package/dist/chunks/fs-events-hrrg328g.js +0 -21
- package/dist/chunks/git-panel-532whr1y.js +0 -59
- package/dist/chunks/index-zqxmqxmh.js +0 -109
- package/dist/chunks/jx-validate-8er6gbxy.js +0 -19
- package/dist/chunks/layers-panel-07hm7g5j.js +0 -67
- package/dist/chunks/layers-q75tj2mf.js +0 -45
- package/dist/chunks/lit-html-j055mkh8.js +0 -22
- package/dist/chunks/monaco-buffer-dqw0xxpx.js +0 -25
- package/dist/chunks/notify-yjm79n4s.js +0 -30
- package/dist/chunks/parse-24d0gay3.js +0 -19
- package/dist/chunks/platform-ba1j1gbh.js +0 -16
- package/dist/chunks/publish-panel-v9st96ja.js +0 -29
- package/dist/chunks/quick-search-zccjp6gv.js +0 -66
- package/dist/chunks/references-3qx8fkyp.js +0 -29
- package/dist/chunks/right-panel-vm3b6kwy.js +0 -46
- package/dist/chunks/section-registry-sx1ykvkw.js +0 -30
- package/dist/chunks/sp-overlay.dev-qgvhnxf4.js +0 -18
- package/dist/chunks/sp-popover.dev-n2etmqdp.js +0 -9
- package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +0 -15
- package/dist/chunks/sp-tooltip.dev-4sastdnw.js +0 -15
- package/dist/chunks/sp-tray.dev-tkbscm06.js +0 -199
- package/dist/chunks/studio-0a5qrh01.js +0 -3
- package/dist/chunks/studio-0bq6eh7v.js +0 -12
- package/dist/chunks/studio-18j3bmcz.js +0 -14
- package/dist/chunks/studio-1ngbejft.js +0 -181
- package/dist/chunks/studio-4b5z1x43.js +0 -424
- package/dist/chunks/studio-4n2b5gzb.js +0 -357
- package/dist/chunks/studio-4rzyrtjz.js +0 -426
- package/dist/chunks/studio-5fx3g9jg.js +0 -4325
- package/dist/chunks/studio-5myknm0a.js +0 -1903
- package/dist/chunks/studio-7afd21nw.js +0 -67
- package/dist/chunks/studio-7nte2hj4.js +0 -613
- package/dist/chunks/studio-8jpzqk5s.js +0 -128
- package/dist/chunks/studio-8q517dfr.js +0 -24
- package/dist/chunks/studio-950k0dx6.js +0 -6939
- package/dist/chunks/studio-9n4mrmnb.js +0 -184
- package/dist/chunks/studio-a0tswy0w.js +0 -58
- package/dist/chunks/studio-ahnzd9rg.js +0 -23
- package/dist/chunks/studio-cmb03bg5.js +0 -13890
- package/dist/chunks/studio-d1ks410h.js +0 -175
- package/dist/chunks/studio-ef49pnzx.js +0 -387
- package/dist/chunks/studio-eqq1w638.js +0 -113
- package/dist/chunks/studio-ew0djmhg.js +0 -341
- package/dist/chunks/studio-gfgb8pe6.js +0 -1652
- package/dist/chunks/studio-h4rxscfc.js +0 -10
- package/dist/chunks/studio-jc994dnr.js +0 -1066
- package/dist/chunks/studio-kyh9avvv.js +0 -28
- package/dist/chunks/studio-m3hwn7y1.js +0 -120
- package/dist/chunks/studio-mtfnaqs9.js +0 -659
- package/dist/chunks/studio-n6j9t10q.js +0 -318
- package/dist/chunks/studio-n8b9wqwy.js +0 -291
- package/dist/chunks/studio-qcy05dp8.js +0 -1743
- package/dist/chunks/studio-qq6jym04.js +0 -53
- package/dist/chunks/studio-rdx8jcxy.js +0 -83
- package/dist/chunks/studio-rn9ta2mz.js +0 -111
- package/dist/chunks/studio-t7qb9db8.js +0 -665
- package/dist/chunks/studio-tmc5emrw.js +0 -8
- package/dist/chunks/studio-z6nw2ek2.js +0 -18
- package/dist/chunks/studio-zczps70q.js +0 -378
- package/dist/chunks/studio-zqazsct2.js +0 -1823
- package/dist/chunks/workspace-fjx52ryv.js +0 -89
- package/src/panels/activity-bar.ts +0 -251
- package/src/panels/panel-scheduler.ts +0 -219
- package/src/panels/style-inputs.ts +0 -237
- package/src/panels/target-line.ts +0 -274
- package/src/panels/welcome-screen.ts +0 -346
- package/src/ui/button-group.ts +0 -102
- package/src/ui/choice-field.ts +0 -72
- package/src/ui/field-row.ts +0 -249
- package/src/ui/formula-palette.ts +0 -213
- package/src/ui/icons.ts +0 -52
- package/src/ui/jx-theme.ts +0 -130
- package/src/ui/spectrum.ts +0 -340
- package/src/ui/value-selector.ts +0 -174
- package/src/ui/widgets.ts +0 -172
- /package/dist/chunks/{javascript-s56a8dpk.css → javascript-4abzjcy9.css} +0 -0
- /package/dist/chunks/{jsonMode-zr9sw0c7.css → jsonMode-vvmpmk34.css} +0 -0
- /package/dist/chunks/{monaco-setup-pav68602.css → monaco-setup-8vbyqnhr.css} +0 -0
- /package/dist/chunks/{tsMode-ef187txx.css → tsMode-hnxxb73h.css} +0 -0
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
/**
|
|
3
|
-
* Chat-view.
|
|
3
|
+
* Chat-view.ts — the transcript's PROJECTION: what the assistant surface is told about a turn.
|
|
4
4
|
*
|
|
5
|
-
* Message-row anatomy: user messages render as right-aligned bubbles (attached-context
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
5
|
+
* Message-row anatomy: user messages render as right-aligned bubbles (attached-context blocks
|
|
6
|
+
* become chips), assistant messages render sanitized markdown plus tool-call chips, tool messages
|
|
7
|
+
* surface failures only (ADR §11.3), the streaming tail renders as plain text with a cursor
|
|
8
|
+
* (markdown parses once on finalize), and chat errors get a danger row with recovery advice and a
|
|
9
|
+
* Retry. **None of that is markup here any more** — `surfaces/ai-chat.json` draws it and this
|
|
10
|
+
* module decides what it says, which is the same split the file always had with a document on the
|
|
11
|
+
* other side of it instead of a lit template.
|
|
11
12
|
*
|
|
12
13
|
* §7.4 (AI honesty) is why three things here are not what they were:
|
|
13
14
|
*
|
|
@@ -24,24 +25,28 @@
|
|
|
24
25
|
* §11.1 is why the three buttons here are not callbacks any more. History, New Chat and Retry were
|
|
25
26
|
* closures this module received and invoked, so the capabilities existed ONLY as buttons: the
|
|
26
27
|
* `Assistant` category held zero records, and nothing could reach them from the palette, a chord,
|
|
27
|
-
* the automation runner or the generated commands sheet. They
|
|
28
|
-
* idiom `
|
|
29
|
-
* decides where it is drawn.
|
|
28
|
+
* the automation runner or the generated commands sheet. They are {@link projectCommand} now, in
|
|
29
|
+
* the idiom `surfaces/statusbar.ts` established — the record is the definition site, and this file
|
|
30
|
+
* only decides where it is drawn and with which glyph.
|
|
30
31
|
*
|
|
31
32
|
* @license MIT
|
|
32
33
|
*/
|
|
33
34
|
|
|
34
|
-
import { html, nothing } from "lit-html";
|
|
35
|
-
import { repeat } from "lit-html/directives/repeat.js";
|
|
36
|
-
import type { TemplateResult } from "lit-html";
|
|
37
|
-
import { ref } from "lit-html/directives/ref.js";
|
|
38
35
|
import type { Message, ToolCallRecord } from "@jxsuite/ai/chat-state";
|
|
39
36
|
import { splitAttachedContext } from "./attached-context";
|
|
40
37
|
import { renderMarkdown } from "./chat-markdown";
|
|
41
38
|
import { summarizeWrites, writesForTurn } from "../../services/ai-writes";
|
|
42
39
|
import { activeRegistry } from "../../commands/active-registry";
|
|
43
40
|
|
|
41
|
+
import type { CommandRegistry } from "../../commands/registry";
|
|
44
42
|
import type { ImportRunRecord } from "../../services/import-run";
|
|
43
|
+
import type {
|
|
44
|
+
ChatChangeView,
|
|
45
|
+
ChatChipView,
|
|
46
|
+
ChatCommandView,
|
|
47
|
+
ChatContextChip,
|
|
48
|
+
ChatRowView,
|
|
49
|
+
} from "../../surfaces/ai-chat";
|
|
45
50
|
|
|
46
51
|
// ─── Helpers (moved from ai-panel.ts) ────────────────────────────────────────
|
|
47
52
|
|
|
@@ -116,146 +121,93 @@ export function formatErrorAdvice(error: string): string {
|
|
|
116
121
|
|
|
117
122
|
// ─── Commands as buttons ────────────────────────────────────────────────────
|
|
118
123
|
|
|
119
|
-
/**
|
|
124
|
+
/** Where a projected command is drawn, and with what. */
|
|
120
125
|
export interface CommandButtonOptions {
|
|
121
|
-
/**
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
|
|
125
|
-
/** Quiet chrome. The header's icon buttons are quiet; the labelled Retry is not. */
|
|
126
|
-
quiet?: boolean;
|
|
126
|
+
/** The kit glyph, by its name in the manifest. Empty for a text-only button. */
|
|
127
|
+
icon?: string;
|
|
128
|
+
/** Visible text. Empty on an icon-only button. */
|
|
129
|
+
text?: string;
|
|
127
130
|
}
|
|
128
131
|
|
|
129
132
|
/**
|
|
130
|
-
* One control that IS a command — `
|
|
133
|
+
* One control that IS a command — `surfaces/statusbar.ts`'s `projectItem`, for the assistant.
|
|
131
134
|
*
|
|
132
|
-
* A command the registry does not hold, or whose `when` is false,
|
|
133
|
-
* dead button; a visible-but-refused one
|
|
134
|
-
* tooltip. That is what keeps
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
135
|
+
* A command the registry does not hold, or whose `when` is false, projects to NOTHING rather than a
|
|
136
|
+
* dead button; a visible-but-refused one projects disabled with its `requires` sentence in the
|
|
137
|
+
* tooltip. That is what keeps the assistant a rendering of the registry instead of a second place
|
|
138
|
+
* its capabilities are decided — and it is why `tests/ai-chat-view.test.ts` asserts the ids,
|
|
139
|
+
* exactly as `tests/statusbar.test.ts` does: an id is not an interface between two files unless
|
|
140
|
+
* something checks it.
|
|
138
141
|
*
|
|
139
142
|
* Before any registry exists (the bootstrap composes one at the END of `studio.ts`, and a reduced
|
|
140
143
|
* test fixture may compose none) the button is simply absent. The chat is still readable, which is
|
|
141
144
|
* the same bargain the status bar strikes for the frame it paints early.
|
|
145
|
+
*
|
|
146
|
+
* @param {string} id
|
|
147
|
+
* @param {CommandButtonOptions} [opts]
|
|
148
|
+
* @param {CommandRegistry | null} [registry] The registry to ask; the active one by default.
|
|
149
|
+
* @returns {ChatCommandView | null}
|
|
142
150
|
*/
|
|
143
|
-
export function
|
|
151
|
+
export function projectCommand(
|
|
144
152
|
id: string,
|
|
145
|
-
opts: CommandButtonOptions,
|
|
146
|
-
|
|
147
|
-
|
|
153
|
+
opts: CommandButtonOptions = {},
|
|
154
|
+
registry: CommandRegistry | null = activeRegistry(),
|
|
155
|
+
): ChatCommandView | null {
|
|
148
156
|
const command = registry?.get(id);
|
|
149
157
|
if (!registry || !command || !registry.isVisible(id)) {
|
|
150
|
-
return
|
|
158
|
+
return null;
|
|
151
159
|
}
|
|
152
160
|
const reason = registry.disabledReason(id);
|
|
153
161
|
const chord = registry.keymap.formatBinding(id);
|
|
154
|
-
const
|
|
162
|
+
const hint = reason
|
|
155
163
|
? `${command.title} — requires ${reason}`
|
|
156
164
|
: chord
|
|
157
165
|
? `${command.title} (${chord})`
|
|
158
166
|
: command.title;
|
|
159
|
-
return
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
chord bound to a disabled command — swallow it here rather than make every surface wrap a
|
|
169
|
-
dispatch in try/catch. */
|
|
170
|
-
if (registry.isEnabled(id)) {
|
|
171
|
-
void registry.run(id);
|
|
172
|
-
}
|
|
173
|
-
}}
|
|
174
|
-
>
|
|
175
|
-
${opts.content}
|
|
176
|
-
</sp-action-button>`;
|
|
167
|
+
return {
|
|
168
|
+
disabled: reason !== undefined,
|
|
169
|
+
hint,
|
|
170
|
+
icon: opts.icon ?? "",
|
|
171
|
+
id,
|
|
172
|
+
key: id,
|
|
173
|
+
label: command.title,
|
|
174
|
+
text: opts.text ?? "",
|
|
175
|
+
};
|
|
177
176
|
}
|
|
178
177
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
/** The open session's title, or null for a fresh unsaved chat. */
|
|
183
|
-
title: string | null;
|
|
184
|
-
streaming: boolean;
|
|
185
|
-
/**
|
|
186
|
-
* The conversation's estimated token count, and whether it is over the warning line.
|
|
187
|
-
*
|
|
188
|
-
* `services/context-manager.ts` has computed both on every turn since it was written, and
|
|
189
|
-
* `chat-state.ts` has stored them — with NO READER anywhere. Plan §11.6: "Context budget manager
|
|
190
|
-
* → tokenCount / contextWarning actually rendered". So a conversation was silently trimmed, the
|
|
191
|
-
* assistant forgot what you told it ten turns ago, and the two numbers that would have explained
|
|
192
|
-
* why sat in the store.
|
|
193
|
-
*/
|
|
194
|
-
tokens: number;
|
|
195
|
-
overBudget: boolean;
|
|
178
|
+
/** The projections that survived, as a list — a `null` is a command that is not on offer. */
|
|
179
|
+
export function projectCommands(entries: readonly (ChatCommandView | null)[]): ChatCommandView[] {
|
|
180
|
+
return entries.filter((entry): entry is ChatCommandView => entry !== null);
|
|
196
181
|
}
|
|
197
182
|
|
|
183
|
+
// ─── Header ─────────────────────────────────────────────────────────────────
|
|
184
|
+
|
|
198
185
|
/** Compact token count: 18400 → "18.4k". A four-digit number in a 28px header is noise. */
|
|
199
|
-
function tokenLabel(tokens: number): string {
|
|
186
|
+
export function tokenLabel(tokens: number): string {
|
|
200
187
|
return tokens >= 1000 ? `${(tokens / 1000).toFixed(1)}k` : String(tokens);
|
|
201
188
|
}
|
|
202
189
|
|
|
203
|
-
/**
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
: `About ${opts.tokens.toLocaleString()} tokens of the model's context in use`
|
|
222
|
-
}
|
|
223
|
-
>${tokenLabel(opts.tokens)}</span
|
|
224
|
-
>`
|
|
225
|
-
: nothing
|
|
226
|
-
}
|
|
227
|
-
${
|
|
228
|
-
opts.streaming
|
|
229
|
-
? html`<sp-progress-circle size="s" indeterminate></sp-progress-circle>`
|
|
230
|
-
: nothing
|
|
231
|
-
}
|
|
232
|
-
${commandButton("assistant.newChat", {
|
|
233
|
-
content: html`<sp-icon-add slot="icon"></sp-icon-add>`,
|
|
234
|
-
quiet: true,
|
|
235
|
-
})}
|
|
236
|
-
</div>
|
|
237
|
-
`;
|
|
190
|
+
/**
|
|
191
|
+
* The sentence behind the token readout.
|
|
192
|
+
*
|
|
193
|
+
* `services/context-manager.ts` has computed both numbers on every turn since it was written, and
|
|
194
|
+
* `chat-state.ts` has stored them — with NO READER anywhere. Plan §11.6: "Context budget manager →
|
|
195
|
+
* tokenCount / contextWarning actually rendered". So a conversation was silently trimmed, the
|
|
196
|
+
* assistant forgot what you told it ten turns ago, and the two numbers that would have explained
|
|
197
|
+
* why sat in the store.
|
|
198
|
+
*
|
|
199
|
+
* @param {number} tokens
|
|
200
|
+
* @param {boolean} overBudget
|
|
201
|
+
* @returns {string}
|
|
202
|
+
*/
|
|
203
|
+
export function tokenHint(tokens: number, overBudget: boolean): string {
|
|
204
|
+
return overBudget
|
|
205
|
+
? `About ${tokens.toLocaleString()} tokens — past half the model's context. ` +
|
|
206
|
+
"The oldest turns are dropped as this grows; start a new chat to keep them."
|
|
207
|
+
: `About ${tokens.toLocaleString()} tokens of the model's context in use`;
|
|
238
208
|
}
|
|
239
209
|
|
|
240
|
-
// ───
|
|
241
|
-
|
|
242
|
-
function renderUserMessage(msg: Message): TemplateResult {
|
|
243
|
-
const { body, contextLines } = splitAttachedContext(msg.content);
|
|
244
|
-
return html`
|
|
245
|
-
<div class="ai-msg-user">
|
|
246
|
-
<div class="ai-msg-user-body">${body}</div>
|
|
247
|
-
${
|
|
248
|
-
contextLines.length > 0
|
|
249
|
-
? html`
|
|
250
|
-
<div class="ai-msg-context-chips">
|
|
251
|
-
${contextLines.map((line) => html`<span class="ai-context-chip">${line}</span>`)}
|
|
252
|
-
</div>
|
|
253
|
-
`
|
|
254
|
-
: nothing
|
|
255
|
-
}
|
|
256
|
-
</div>
|
|
257
|
-
`;
|
|
258
|
-
}
|
|
210
|
+
// ─── Tool calls ─────────────────────────────────────────────────────────────
|
|
259
211
|
|
|
260
212
|
/** The tool whose chip is a question card rather than a chip. */
|
|
261
213
|
const ASK_TOOL = "ask_user";
|
|
@@ -263,32 +215,6 @@ const ASK_TOOL = "ask_user";
|
|
|
263
215
|
/** The tool whose chip grows a live progress line while it runs. */
|
|
264
216
|
const IMPORT_TOOL = "import_site";
|
|
265
217
|
|
|
266
|
-
/** Log lines drawn under a running import. The tail is what a reader wants. */
|
|
267
|
-
/**
|
|
268
|
-
* How close to the bottom counts as "following along".
|
|
269
|
-
*
|
|
270
|
-
* The same threshold and the same reasoning as the transcript's own scroller
|
|
271
|
-
* (`panels/ai-panel.ts`): a reader who has scrolled up is reading something, and yanking them back
|
|
272
|
-
* on every new line is the behaviour that makes a live log unreadable.
|
|
273
|
-
*/
|
|
274
|
-
const IMPORT_STICK_THRESHOLD = 24;
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Keep an import log pinned to its newest line, unless the reader has scrolled away from it.
|
|
278
|
-
*
|
|
279
|
-
* The log is a scroller now rather than a six-line tail, and a scroller that does not follow is a
|
|
280
|
-
* box that shows the same six lines it started with while the interesting ones pile up below.
|
|
281
|
-
*/
|
|
282
|
-
function stickToBottom(element: Element | undefined): void {
|
|
283
|
-
if (!(element instanceof HTMLElement)) {
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
const distance = element.scrollHeight - element.scrollTop - element.clientHeight;
|
|
287
|
-
if (distance <= IMPORT_STICK_THRESHOLD || element.scrollTop === 0) {
|
|
288
|
-
element.scrollTop = element.scrollHeight;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
218
|
/** What `ask_user` was called with, as far as the arguments actually parse. */
|
|
293
219
|
interface AskArgs {
|
|
294
220
|
question: string;
|
|
@@ -366,14 +292,56 @@ export function toolOutcomeText(tc: ToolCallRecord): string {
|
|
|
366
292
|
return (result.success ? result.summary : result.error) ?? "";
|
|
367
293
|
}
|
|
368
294
|
|
|
295
|
+
/** How a question card reaches the store that resolves it, and an import its record. */
|
|
296
|
+
export interface AskHandlers {
|
|
297
|
+
/** The id of the question the loop is still waiting on, or null. */
|
|
298
|
+
pendingId?: string | null;
|
|
299
|
+
/** The live record for an `import_site` call, if this renderer's host has one. */
|
|
300
|
+
importRun?: (id: string) => ImportRunRecord | null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** The empty import fields every chip carries, so a `$switch` never reads an absent path. */
|
|
304
|
+
function noImport(): Pick<
|
|
305
|
+
ChatChipView,
|
|
306
|
+
| "hasPhase"
|
|
307
|
+
| "importCount"
|
|
308
|
+
| "importLog"
|
|
309
|
+
| "importMessage"
|
|
310
|
+
| "importOpen"
|
|
311
|
+
| "importPhase"
|
|
312
|
+
| "importProgress"
|
|
313
|
+
| "importSpinner"
|
|
314
|
+
| "importState"
|
|
315
|
+
> {
|
|
316
|
+
return {
|
|
317
|
+
hasPhase: false,
|
|
318
|
+
importCount: "",
|
|
319
|
+
importLog: [],
|
|
320
|
+
importMessage: "",
|
|
321
|
+
importOpen: false,
|
|
322
|
+
importPhase: "",
|
|
323
|
+
importProgress: "",
|
|
324
|
+
importSpinner: "none",
|
|
325
|
+
importState: "none",
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
369
329
|
/**
|
|
370
330
|
* A running import, under the chip that started it.
|
|
371
331
|
*
|
|
372
|
-
* An import takes minutes and says a line at a time. Rendering it
|
|
373
|
-
* used to live in is what stops a successful run destroying its own account of what it
|
|
374
|
-
* chip and the progress are one thing, joined by the tool-call id.
|
|
332
|
+
* An import takes minutes and says a line at a time. Rendering it under the chip rather than in the
|
|
333
|
+
* modal it used to live in is what stops a successful run destroying its own account of what it
|
|
334
|
+
* did: the chip and the progress are one thing, joined by the tool-call id.
|
|
335
|
+
*
|
|
336
|
+
* Projected for EVERY import chip, not only a running one. A finished run used to lose its whole
|
|
337
|
+
* account of itself at the instant it finished — the record was fetched only while the call was
|
|
338
|
+
* pending, so the log vanished on success. That is the failure the hand-off from the wizard to the
|
|
339
|
+
* assistant was made to fix, reproduced one layer in.
|
|
340
|
+
*
|
|
341
|
+
* @param {ImportRunRecord} record
|
|
342
|
+
* @returns {Partial<ChatChipView>}
|
|
375
343
|
*/
|
|
376
|
-
function
|
|
344
|
+
function projectImport(record: ImportRunRecord): Partial<ChatChipView> {
|
|
377
345
|
const running = record.status === "running";
|
|
378
346
|
const determinate = record.total !== null && record.current !== null;
|
|
379
347
|
const outcome =
|
|
@@ -384,357 +352,224 @@ function renderImportProgress(record: ImportRunRecord): TemplateResult {
|
|
|
384
352
|
: record.status === "stopped"
|
|
385
353
|
? "Import stopped"
|
|
386
354
|
: record.message;
|
|
387
|
-
return
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
<div class="ai-import-log" ${ref(stickToBottom)}>
|
|
405
|
-
${repeat(
|
|
406
|
-
record.log,
|
|
407
|
-
(evt, index) => `${index}:${evt.phase}`,
|
|
408
|
-
(evt) => html`
|
|
409
|
-
<div class="ai-import-log-line">
|
|
410
|
-
<span class="ai-import-phase">${evt.phase}</span>${evt.message}
|
|
411
|
-
</div>
|
|
412
|
-
`,
|
|
413
|
-
)}
|
|
414
|
-
</div>
|
|
415
|
-
</details>
|
|
416
|
-
`;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
/** How a question card reaches the store that resolves it. */
|
|
420
|
-
export interface AskHandlers {
|
|
421
|
-
/** The id of the question the loop is still waiting on, or null. */
|
|
422
|
-
pendingId?: string | null;
|
|
423
|
-
onAnswer?: (text: string) => void;
|
|
424
|
-
onSkip?: () => void;
|
|
425
|
-
/** The live record for an `import_site` call, if this renderer's host has one. */
|
|
426
|
-
importRun?: (id: string) => ImportRunRecord | null;
|
|
355
|
+
return {
|
|
356
|
+
hasPhase: running && record.phase !== "",
|
|
357
|
+
importCount: String(record.log.length),
|
|
358
|
+
importLog: record.log.map((evt, index) => ({
|
|
359
|
+
key: `${index}:${evt.phase}`,
|
|
360
|
+
message: evt.message,
|
|
361
|
+
phase: evt.phase,
|
|
362
|
+
})),
|
|
363
|
+
importMessage: outcome,
|
|
364
|
+
importOpen: running,
|
|
365
|
+
importPhase: record.phase,
|
|
366
|
+
importProgress: determinate
|
|
367
|
+
? String(Math.round((record.current! / Math.max(record.total!, 1)) * 100))
|
|
368
|
+
: "",
|
|
369
|
+
importSpinner: running ? (determinate ? "progress" : "busy") : "none",
|
|
370
|
+
importState: "run",
|
|
371
|
+
};
|
|
427
372
|
}
|
|
428
373
|
|
|
429
374
|
/**
|
|
430
|
-
*
|
|
375
|
+
* One tool call, as the transcript draws it.
|
|
431
376
|
*
|
|
432
|
-
* The card IS the tool chip rather than a row beside it: the question, its options and its
|
|
433
|
-
* are all facts about one tool call, and
|
|
434
|
-
* accounts of the same event that can disagree after a reload.
|
|
377
|
+
* The question card IS the tool chip rather than a row beside it: the question, its options and its
|
|
378
|
+
* answer are all facts about one tool call, and putting them anywhere else would give the
|
|
379
|
+
* transcript two accounts of the same event that can disagree after a reload.
|
|
435
380
|
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
381
|
+
* @param {ToolCallRecord} tc
|
|
382
|
+
* @param {AskHandlers} handlers
|
|
383
|
+
* @returns {ChatChipView}
|
|
439
384
|
*/
|
|
440
|
-
function
|
|
441
|
-
tc
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
): TemplateResult {
|
|
385
|
+
export function projectChip(tc: ToolCallRecord, handlers: AskHandlers = {}): ChatChipView {
|
|
386
|
+
const outcome = toolOutcome(tc, tc.id === handlers.pendingId || Boolean(tc.result));
|
|
387
|
+
const text = toolOutcomeText(tc);
|
|
388
|
+
const run = tc.name === IMPORT_TOOL ? (handlers.importRun?.(tc.id) ?? null) : null;
|
|
389
|
+
const ask = tc.name === ASK_TOOL ? parseAsk(tc) : null;
|
|
446
390
|
const answered = tc.result?.success
|
|
447
391
|
? ((tc.result.data as { answer?: string | null; skipped?: boolean } | undefined) ?? null)
|
|
448
392
|
: null;
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
${
|
|
483
|
-
outcome === "unanswered"
|
|
484
|
-
? html`<div class="ai-ask-outcome">
|
|
485
|
-
This question was still open when the session was reloaded — reply below to carry on.
|
|
486
|
-
</div>`
|
|
487
|
-
: nothing
|
|
488
|
-
}
|
|
489
|
-
${
|
|
490
|
-
answered
|
|
491
|
-
? html`<div class="ai-ask-answer">
|
|
492
|
-
${answered.skipped ? "You decide" : answered.answer}
|
|
493
|
-
</div>`
|
|
494
|
-
: nothing
|
|
495
|
-
}
|
|
496
|
-
${
|
|
497
|
-
outcome === "failed"
|
|
498
|
-
? html`<div class="ai-ask-outcome">${toolOutcomeText(tc)}</div>`
|
|
499
|
-
: nothing
|
|
500
|
-
}
|
|
501
|
-
</div>
|
|
502
|
-
`;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
function renderToolChips(
|
|
506
|
-
toolCalls: ToolCallRecord[],
|
|
507
|
-
handlers: AskHandlers = {},
|
|
508
|
-
): TemplateResult | typeof nothing {
|
|
509
|
-
if (toolCalls.length === 0) {
|
|
510
|
-
return nothing;
|
|
393
|
+
const base: ChatChipView = {
|
|
394
|
+
answer: answered ? (answered.skipped ? "You decide" : (answered.answer ?? "")) : "",
|
|
395
|
+
askState: "none",
|
|
396
|
+
context: ask?.context ?? "",
|
|
397
|
+
hasContext: Boolean(ask?.context),
|
|
398
|
+
hint: text || formatToolLabel(tc),
|
|
399
|
+
key: tc.id,
|
|
400
|
+
kind: ask ? "ask" : "chip",
|
|
401
|
+
label: formatToolLabel(tc),
|
|
402
|
+
mark: outcome === "ok" ? "✓" : "✗",
|
|
403
|
+
options: (ask?.options ?? []).map((option) => ({ key: option, label: option })),
|
|
404
|
+
outcome,
|
|
405
|
+
outcomeState: outcome === "pending" || outcome === "unanswered" ? "hidden" : "shown",
|
|
406
|
+
outcomeText: text,
|
|
407
|
+
question: ask?.question ?? "",
|
|
408
|
+
tool: tc.name,
|
|
409
|
+
...noImport(),
|
|
410
|
+
...(run ? projectImport(run) : {}),
|
|
411
|
+
};
|
|
412
|
+
if (ask) {
|
|
413
|
+
/* The options are a shortcut, never the whole answer — the composer is always live beneath, and
|
|
414
|
+
its placeholder says so. A question whose real answer is "neither, do this instead" must stay
|
|
415
|
+
answerable, which is why `pending` draws the options AND leaves the field alone. */
|
|
416
|
+
base.askState =
|
|
417
|
+
outcome === "pending"
|
|
418
|
+
? "pending"
|
|
419
|
+
: outcome === "unanswered"
|
|
420
|
+
? "unanswered"
|
|
421
|
+
: answered
|
|
422
|
+
? "answered"
|
|
423
|
+
: outcome === "failed"
|
|
424
|
+
? "failed"
|
|
425
|
+
: "none";
|
|
511
426
|
}
|
|
512
|
-
return
|
|
513
|
-
<div class="ai-msg-tools">
|
|
514
|
-
${toolCalls.map((tc) => {
|
|
515
|
-
const ask = tc.name === ASK_TOOL ? parseAsk(tc) : null;
|
|
516
|
-
const outcome = toolOutcome(tc, tc.id === handlers.pendingId || Boolean(tc.result));
|
|
517
|
-
if (ask) {
|
|
518
|
-
return renderAskCard(tc, ask, outcome, handlers);
|
|
519
|
-
}
|
|
520
|
-
const text = toolOutcomeText(tc);
|
|
521
|
-
/*
|
|
522
|
-
* For EVERY import chip, not only a running one.
|
|
523
|
-
*
|
|
524
|
-
* A finished run used to lose its whole account of itself at the instant it finished — the
|
|
525
|
-
* record was fetched only while the call was pending, so the log vanished on success. That
|
|
526
|
-
* is the failure the hand-off from the wizard to the assistant was made to fix, reproduced
|
|
527
|
-
* one layer in. It stays, collapsed, under the chip that produced it.
|
|
528
|
-
*/
|
|
529
|
-
const run = tc.name === IMPORT_TOOL ? (handlers.importRun?.(tc.id) ?? null) : null;
|
|
530
|
-
return html`
|
|
531
|
-
<span class="ai-tool-chip" data-outcome=${outcome} title=${text || formatToolLabel(tc)}>
|
|
532
|
-
<sp-icon-gears size="xs"></sp-icon-gears>
|
|
533
|
-
<span class="ai-tool-chip-name">${formatToolLabel(tc)}</span>
|
|
534
|
-
${
|
|
535
|
-
outcome === "pending" || outcome === "unanswered"
|
|
536
|
-
? nothing
|
|
537
|
-
: html`<span class="ai-tool-chip-outcome"
|
|
538
|
-
>${outcome === "ok" ? "✓" : "✗"} ${text}</span
|
|
539
|
-
>`
|
|
540
|
-
}
|
|
541
|
-
</span>
|
|
542
|
-
${run ? renderImportProgress(run) : nothing}
|
|
543
|
-
`;
|
|
544
|
-
})}
|
|
545
|
-
</div>
|
|
546
|
-
`;
|
|
427
|
+
return base;
|
|
547
428
|
}
|
|
548
429
|
|
|
430
|
+
// ─── Changed files ──────────────────────────────────────────────────────────
|
|
431
|
+
|
|
549
432
|
/**
|
|
550
433
|
* The turn's changed-files summary, with the two things it can honestly offer.
|
|
551
434
|
*
|
|
552
|
-
*
|
|
435
|
+
* Projected only for a turn that changed something — "Changed 0 files" is noise, and a turn that
|
|
553
436
|
* only read is the common case. **Restore to here** is offered only when every recorded change went
|
|
554
437
|
* through a transaction: a disk write has no history behind it, so a button that claimed to restore
|
|
555
438
|
* one would be the same lie the model-facing caveat used to be.
|
|
439
|
+
*
|
|
440
|
+
* @param {string} messageId
|
|
441
|
+
* @returns {{
|
|
442
|
+
* changesState: string;
|
|
443
|
+
* changesSummary: string;
|
|
444
|
+
* canRestore: boolean;
|
|
445
|
+
* changes: ChatChangeView[];
|
|
446
|
+
* }}
|
|
556
447
|
*/
|
|
557
|
-
function
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
const summary = summarizeWrites(writes);
|
|
448
|
+
export function projectChanges(messageId: string): {
|
|
449
|
+
changesState: string;
|
|
450
|
+
changesSummary: string;
|
|
451
|
+
canRestore: boolean;
|
|
452
|
+
changes: ChatChangeView[];
|
|
453
|
+
} {
|
|
454
|
+
const writes = writesForTurn(messageId);
|
|
455
|
+
const summary = writes.length > 0 ? summarizeWrites(writes) : "";
|
|
566
456
|
if (!summary) {
|
|
567
|
-
return
|
|
457
|
+
return { canRestore: false, changes: [], changesState: "none", changesSummary: "" };
|
|
568
458
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
e.preventDefault();
|
|
582
|
-
e.stopPropagation();
|
|
583
|
-
onRestore(msg.id);
|
|
584
|
-
}}
|
|
585
|
-
>
|
|
586
|
-
Restore to here
|
|
587
|
-
</sp-action-button>`
|
|
588
|
-
: nothing
|
|
589
|
-
}
|
|
590
|
-
</summary>
|
|
591
|
-
<ul class="ai-msg-changes-list">
|
|
592
|
-
${writes.map(
|
|
593
|
-
(w) => html`
|
|
594
|
-
<li data-disk=${String(w.disk)} data-ok=${String(w.ok)}>
|
|
595
|
-
<code>${w.path}</code>
|
|
596
|
-
<span>${w.ok ? w.tool : `failed — ${w.error ?? w.tool}`}</span>
|
|
597
|
-
${w.disk ? html`<em>written to disk — undo cannot reach it</em>` : nothing}
|
|
598
|
-
</li>
|
|
599
|
-
`,
|
|
600
|
-
)}
|
|
601
|
-
</ul>
|
|
602
|
-
</details>
|
|
603
|
-
`;
|
|
459
|
+
return {
|
|
460
|
+
canRestore: writes.every((w) => !w.disk),
|
|
461
|
+
changes: writes.map((w, index) => ({
|
|
462
|
+
disk: w.disk,
|
|
463
|
+
key: `${index}:${w.path}`,
|
|
464
|
+
note: w.ok ? w.tool : `failed — ${w.error ?? w.tool}`,
|
|
465
|
+
ok: w.ok,
|
|
466
|
+
path: w.path,
|
|
467
|
+
})),
|
|
468
|
+
changesState: "list",
|
|
469
|
+
changesSummary: summary,
|
|
470
|
+
};
|
|
604
471
|
}
|
|
605
472
|
|
|
606
|
-
|
|
607
|
-
msg: Message,
|
|
608
|
-
onRestore?: (messageId: string) => void,
|
|
609
|
-
ask: AskHandlers = {},
|
|
610
|
-
): TemplateResult | typeof nothing {
|
|
611
|
-
const toolCalls = msg.toolCalls ?? [];
|
|
612
|
-
if (!msg.content && toolCalls.length === 0) {
|
|
613
|
-
return nothing;
|
|
614
|
-
}
|
|
615
|
-
return html`
|
|
616
|
-
<div class="ai-msg-assistant">
|
|
617
|
-
${msg.content ? renderMarkdown(msg.id, msg.content) : nothing}
|
|
618
|
-
${renderToolChips(toolCalls, ask)} ${renderChangedFiles(msg, onRestore)}
|
|
619
|
-
</div>
|
|
620
|
-
`;
|
|
621
|
-
}
|
|
473
|
+
// ─── Rows ───────────────────────────────────────────────────────────────────
|
|
622
474
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
475
|
+
/** Every field a row carries, so a `$switch` case never reads an absent path. */
|
|
476
|
+
function emptyRow(key: string, kind: string): ChatRowView {
|
|
477
|
+
return {
|
|
478
|
+
body: "",
|
|
479
|
+
canRestore: false,
|
|
480
|
+
changes: [],
|
|
481
|
+
changesState: "none",
|
|
482
|
+
changesSummary: "",
|
|
483
|
+
chips: [],
|
|
484
|
+
contextChips: [],
|
|
485
|
+
hasChips: false,
|
|
486
|
+
hasContextChips: false,
|
|
487
|
+
key,
|
|
488
|
+
kind,
|
|
489
|
+
markdown: "",
|
|
490
|
+
};
|
|
631
491
|
}
|
|
632
492
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
<div class="ai-msg-typing">
|
|
637
|
-
<span></span>
|
|
638
|
-
<span></span>
|
|
639
|
-
<span></span>
|
|
640
|
-
</div>
|
|
641
|
-
`;
|
|
642
|
-
}
|
|
643
|
-
return html`
|
|
644
|
-
<div class="ai-msg-assistant">
|
|
645
|
-
<span class="ai-msg-streaming">${msg.content}</span>
|
|
646
|
-
${renderToolChips(msg.toolCalls ?? [], ask)}
|
|
647
|
-
</div>
|
|
648
|
-
`;
|
|
493
|
+
/** The attached-context block on a user turn, back apart into chips. */
|
|
494
|
+
function contextChips(lines: readonly string[]): ChatContextChip[] {
|
|
495
|
+
return lines.map((line, index) => ({ key: `${index}:${line}`, label: line }));
|
|
649
496
|
}
|
|
650
497
|
|
|
651
|
-
// ─── Message list ───────────────────────────────────────────────────────────
|
|
652
|
-
|
|
653
498
|
export interface MessageListOptions {
|
|
654
|
-
messages: Message[];
|
|
655
|
-
/** ChatState status — "streaming"
|
|
499
|
+
messages: readonly Message[];
|
|
500
|
+
/** ChatState status — "streaming" projects the tail live. */
|
|
656
501
|
status: string;
|
|
657
|
-
|
|
658
|
-
onScroll: (e: Event) => void;
|
|
659
|
-
/** Ref to the scrolling element, for stick-to-bottom maintenance. */
|
|
660
|
-
listRef: (el: Element | undefined) => void;
|
|
661
|
-
/** Undo everything one turn changed. Offered only for turns whose changes are all transactional. */
|
|
662
|
-
onRestore?: ((messageId: string) => void) | undefined;
|
|
663
|
-
/**
|
|
664
|
-
* The outstanding question and how to settle it.
|
|
665
|
-
*
|
|
666
|
-
* Passed down rather than read from the store here, because this module renders templates and
|
|
667
|
-
* holds no state — the same reason the three header buttons are command records.
|
|
668
|
-
*/
|
|
502
|
+
/** The outstanding question and the import records, passed down rather than read here. */
|
|
669
503
|
ask?: AskHandlers | undefined;
|
|
670
504
|
}
|
|
671
505
|
|
|
672
|
-
/**
|
|
673
|
-
|
|
674
|
-
|
|
506
|
+
/**
|
|
507
|
+
* The transcript, as rows.
|
|
508
|
+
*
|
|
509
|
+
* Keyed on `msg.id`, for two reasons a reader can see. The last assistant row swaps between the
|
|
510
|
+
* streaming tail and the finished message the moment a stream completes, so an unkeyed list would
|
|
511
|
+
* tear down and rebuild the longest node in the transcript every time one finishes. And an
|
|
512
|
+
* assistant row holds the reader's OWN open/closed state on its changed-files disclosure, which
|
|
513
|
+
* position-based reuse hands to a different message.
|
|
514
|
+
*
|
|
515
|
+
* A message that would draw nothing — an empty assistant turn with no tool calls, a successful tool
|
|
516
|
+
* result — is absent rather than an empty row, which is the same thing the lit template's `nothing`
|
|
517
|
+
* did and one fewer node for the runtime to reconcile.
|
|
518
|
+
*
|
|
519
|
+
* @param {MessageListOptions} opts
|
|
520
|
+
* @returns {ChatRowView[]}
|
|
521
|
+
*/
|
|
522
|
+
export function projectRows(opts: MessageListOptions): ChatRowView[] {
|
|
675
523
|
const ask = opts.ask ?? {};
|
|
676
|
-
const lastIdx = messages.length - 1;
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
(msg, i) => {
|
|
699
|
-
if (msg.role === "user") {
|
|
700
|
-
return renderUserMessage(msg);
|
|
701
|
-
}
|
|
702
|
-
if (msg.role === "tool") {
|
|
703
|
-
return renderToolMessage(msg);
|
|
704
|
-
}
|
|
705
|
-
if (msg.role === "assistant") {
|
|
706
|
-
if (status === "streaming" && i === lastIdx) {
|
|
707
|
-
return renderStreamingTail(msg, ask);
|
|
708
|
-
}
|
|
709
|
-
return renderAssistantMessage(msg, opts.onRestore, ask);
|
|
710
|
-
}
|
|
711
|
-
return nothing;
|
|
712
|
-
},
|
|
713
|
-
)}
|
|
714
|
-
${
|
|
715
|
-
status !== "streaming" && opts.error
|
|
716
|
-
? html`
|
|
717
|
-
<div class="ai-msg-error">
|
|
718
|
-
<div>${opts.error}</div>
|
|
719
|
-
${
|
|
720
|
-
formatErrorAdvice(opts.error)
|
|
721
|
-
? html`<div class="ai-msg-error-advice">${formatErrorAdvice(opts.error)}</div>`
|
|
722
|
-
: nothing
|
|
723
|
-
}
|
|
724
|
-
${
|
|
725
|
-
/* `assistant.retry`, not a closure. Its `enablement` reads `ctx.ai.configured`,
|
|
726
|
-
so the one error this row cannot recover from — no provider connected, whose
|
|
727
|
-
advice line above already says to add a key — draws the button disabled with
|
|
728
|
-
that sentence rather than offering a send that will fail identically. */
|
|
729
|
-
commandButton("assistant.retry", {
|
|
730
|
-
className: "ai-msg-retry",
|
|
731
|
-
content: "Retry",
|
|
732
|
-
})
|
|
733
|
-
}
|
|
734
|
-
</div>
|
|
735
|
-
`
|
|
736
|
-
: nothing
|
|
524
|
+
const lastIdx = opts.messages.length - 1;
|
|
525
|
+
const rows: ChatRowView[] = [];
|
|
526
|
+
for (const [i, msg] of opts.messages.entries()) {
|
|
527
|
+
if (msg.role === "user") {
|
|
528
|
+
const { body, contextLines } = splitAttachedContext(msg.content);
|
|
529
|
+
rows.push({
|
|
530
|
+
...emptyRow(msg.id, "user"),
|
|
531
|
+
body,
|
|
532
|
+
contextChips: contextChips(contextLines),
|
|
533
|
+
hasContextChips: contextLines.length > 0,
|
|
534
|
+
});
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
if (msg.role === "tool") {
|
|
538
|
+
// Show only failed tool results so the user knows why an edit didn't land (ADR §11.3).
|
|
539
|
+
// Successful tool results stay hidden to reduce noise.
|
|
540
|
+
const parsed = tryParseToolResult(msg.content);
|
|
541
|
+
if (parsed && !parsed.success) {
|
|
542
|
+
rows.push({
|
|
543
|
+
...emptyRow(msg.id, "tool-error"),
|
|
544
|
+
body: `⚠️ ${parsed.error || "Tool call failed"}`,
|
|
545
|
+
});
|
|
737
546
|
}
|
|
738
|
-
|
|
739
|
-
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
if (msg.role !== "assistant") {
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
const toolCalls = msg.toolCalls ?? [];
|
|
553
|
+
const chips = toolCalls.map((tc) => projectChip(tc, ask));
|
|
554
|
+
if (opts.status === "streaming" && i === lastIdx) {
|
|
555
|
+
rows.push({
|
|
556
|
+
...emptyRow(msg.id, msg.content ? "streaming" : "typing"),
|
|
557
|
+
body: msg.content,
|
|
558
|
+
chips,
|
|
559
|
+
hasChips: chips.length > 0,
|
|
560
|
+
});
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
if (!msg.content && toolCalls.length === 0) {
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
rows.push({
|
|
567
|
+
...emptyRow(msg.id, "assistant"),
|
|
568
|
+
chips,
|
|
569
|
+
hasChips: chips.length > 0,
|
|
570
|
+
markdown: msg.content ? renderMarkdown(msg.id, msg.content) : "",
|
|
571
|
+
...projectChanges(msg.id),
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
return rows;
|
|
740
575
|
}
|