@jxsuite/studio 2.0.0 → 2.1.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.
Files changed (419) hide show
  1. package/canvas.html +21 -0
  2. package/dist/chunks/Overlay.dev-qfjzkms8.js +23 -0
  3. package/dist/chunks/{Overlay.dev-cszx7yn5.js.map → Overlay.dev-qfjzkms8.js.map} +1 -1
  4. package/dist/chunks/{adapter-z8fkmbmv.js → adapter-qfnesn5v.js} +2 -2
  5. package/dist/chunks/{cf-settings-mmyv436d.js → cf-settings-73sh5z4p.js} +3 -3
  6. package/dist/chunks/{components-0hzentwq.js → components-2bpqp4mq.js} +5 -7
  7. package/dist/chunks/{components-0hzentwq.js.map → components-2bpqp4mq.js.map} +1 -1
  8. package/dist/chunks/content-source-34gnkbnn.js +38 -0
  9. package/dist/chunks/content-source-34gnkbnn.js.map +9 -0
  10. package/dist/chunks/convert-to-repeater-t8kxre5s.js +258 -0
  11. package/dist/chunks/convert-to-repeater-t8kxre5s.js.map +10 -0
  12. package/dist/chunks/extension-sections-6mqydz35.js +301 -0
  13. package/dist/chunks/extension-sections-6mqydz35.js.map +11 -0
  14. package/dist/chunks/files-8kn321h1.js +70 -0
  15. package/dist/chunks/files-8kn321h1.js.map +9 -0
  16. package/dist/chunks/{format-host-2c188n41.js → format-host-mkwsmt8d.js} +3 -3
  17. package/dist/chunks/git-panel-tjbr191d.js +51 -0
  18. package/dist/chunks/git-panel-tjbr191d.js.map +9 -0
  19. package/dist/chunks/{index-baeg2x3y.js → index-7pxchcce.js} +4 -3
  20. package/dist/chunks/{index-baeg2x3y.js.map → index-7pxchcce.js.map} +3 -3
  21. package/dist/chunks/{jx-validate-qx8degj7.js → jx-validate-v6qgknrs.js} +3 -3
  22. package/dist/chunks/layers-panel-87pb25km.js +58 -0
  23. package/dist/chunks/layers-panel-87pb25km.js.map +9 -0
  24. package/dist/chunks/{monaco-setup-zjdp9zdw.js → monaco-setup-e0yh25j4.js} +4 -75
  25. package/dist/chunks/{monaco-setup-zjdp9zdw.js.map → monaco-setup-e0yh25j4.js.map} +3 -4
  26. package/dist/chunks/publish-panel-m0fcgdrt.js +21 -0
  27. package/dist/chunks/publish-panel-m0fcgdrt.js.map +9 -0
  28. package/dist/chunks/quick-search-rhs6cbsb.js +53 -0
  29. package/dist/chunks/quick-search-rhs6cbsb.js.map +9 -0
  30. package/dist/chunks/right-panel-zpdxd98t.js +37 -0
  31. package/dist/chunks/right-panel-zpdxd98t.js.map +9 -0
  32. package/dist/chunks/section-registry-zmckaxf4.js +30 -0
  33. package/dist/chunks/section-registry-zmckaxf4.js.map +9 -0
  34. package/dist/chunks/sp-overlay.dev-9ran0sfq.js +18 -0
  35. package/dist/chunks/sp-overlay.dev-9ran0sfq.js.map +9 -0
  36. package/dist/chunks/sp-popover.dev-rebmdncr.js +9 -0
  37. package/dist/chunks/sp-popover.dev-rebmdncr.js.map +9 -0
  38. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js +15 -0
  39. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js.map +9 -0
  40. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js +15 -0
  41. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js.map +9 -0
  42. package/dist/chunks/{sp-tray.dev-pb2fsd65.js → sp-tray.dev-ey59vfh7.js} +10 -8
  43. package/dist/chunks/{sp-tray.dev-pb2fsd65.js.map → sp-tray.dev-ey59vfh7.js.map} +2 -2
  44. package/dist/chunks/{studio-9qr6fq7p.js → studio-0rqqhrdy.js} +10 -378
  45. package/dist/chunks/{studio-9qr6fq7p.js.map → studio-0rqqhrdy.js.map} +4 -14
  46. package/dist/chunks/{studio-5x6tqqec.js → studio-2sj1jj3v.js} +10 -8
  47. package/dist/chunks/{studio-5x6tqqec.js.map → studio-2sj1jj3v.js.map} +2 -2
  48. package/dist/chunks/{studio-h3fw0wn8.js → studio-3fk1ac9z.js} +6 -4
  49. package/dist/chunks/{studio-h3fw0wn8.js.map → studio-3fk1ac9z.js.map} +2 -2
  50. package/dist/chunks/studio-41exgzhq.js +6690 -0
  51. package/dist/chunks/studio-41exgzhq.js.map +38 -0
  52. package/dist/chunks/{studio-7v2bpweq.js → studio-4mqb9a0j.js} +6 -4
  53. package/dist/chunks/{studio-7v2bpweq.js.map → studio-4mqb9a0j.js.map} +2 -2
  54. package/dist/chunks/studio-50k77cca.js +1787 -0
  55. package/dist/chunks/studio-50k77cca.js.map +18 -0
  56. package/dist/chunks/{studio-56v3zwk6.js → studio-6a0h7sne.js} +10 -8
  57. package/dist/chunks/{studio-56v3zwk6.js.map → studio-6a0h7sne.js.map} +2 -2
  58. package/dist/chunks/studio-6f0am8f2.js +53 -0
  59. package/dist/chunks/studio-6f0am8f2.js.map +10 -0
  60. package/dist/chunks/studio-6nmjc5qk.js +7002 -0
  61. package/dist/chunks/studio-6nmjc5qk.js.map +32 -0
  62. package/dist/chunks/studio-7afd21nw.js +67 -0
  63. package/dist/chunks/studio-7afd21nw.js.map +10 -0
  64. package/dist/chunks/{studio-xze4zrs1.js → studio-7bmdvzne.js} +2 -2
  65. package/dist/chunks/studio-7bmdvzne.js.map +10 -0
  66. package/dist/chunks/studio-8jpzqk5s.js +128 -0
  67. package/dist/chunks/studio-8jpzqk5s.js.map +10 -0
  68. package/dist/chunks/studio-94m77tgk.js +1482 -0
  69. package/dist/chunks/studio-94m77tgk.js.map +13 -0
  70. package/dist/chunks/studio-9je6w5sq.js +1000 -0
  71. package/dist/chunks/studio-9je6w5sq.js.map +16 -0
  72. package/dist/chunks/{studio-w7c6kvk7.js → studio-a7pzveq1.js} +2 -2
  73. package/dist/chunks/{studio-97qcgvdn.js → studio-adn0z1k3.js} +14 -8
  74. package/dist/chunks/{studio-97qcgvdn.js.map → studio-adn0z1k3.js.map} +3 -3
  75. package/dist/chunks/{studio-kr88ryr7.js → studio-bsxxx3x2.js} +17 -15
  76. package/dist/chunks/{studio-kr88ryr7.js.map → studio-bsxxx3x2.js.map} +7 -6
  77. package/dist/chunks/studio-bw5dz6km.js +608 -0
  78. package/dist/chunks/studio-bw5dz6km.js.map +21 -0
  79. package/dist/chunks/studio-c6s5getc.js +4232 -0
  80. package/dist/chunks/studio-c6s5getc.js.map +39 -0
  81. package/dist/chunks/studio-cjm7fdjf.js +451 -0
  82. package/dist/chunks/studio-cjm7fdjf.js.map +14 -0
  83. package/dist/chunks/{studio-cx91mnh8.js → studio-ckwz4d3x.js} +53 -53
  84. package/dist/chunks/{studio-cx91mnh8.js.map → studio-ckwz4d3x.js.map} +5 -5
  85. package/dist/chunks/studio-dg7zyt5x.js +28 -0
  86. package/dist/chunks/studio-dg7zyt5x.js.map +10 -0
  87. package/dist/chunks/studio-dzcyvvvz.js +166 -0
  88. package/dist/chunks/studio-dzcyvvvz.js.map +10 -0
  89. package/dist/chunks/{studio-7kqe41sa.js → studio-evkr5d3b.js} +15 -7
  90. package/dist/chunks/{studio-7kqe41sa.js.map → studio-evkr5d3b.js.map} +5 -4
  91. package/dist/chunks/studio-f12h6177.js +411 -0
  92. package/dist/chunks/studio-f12h6177.js.map +13 -0
  93. package/dist/chunks/studio-hbp1s8m9.js +1817 -0
  94. package/dist/chunks/studio-hbp1s8m9.js.map +14 -0
  95. package/dist/chunks/studio-k2sxjph5.js +15295 -0
  96. package/dist/chunks/studio-k2sxjph5.js.map +235 -0
  97. package/dist/chunks/studio-myje51t3.js +385 -0
  98. package/dist/chunks/studio-myje51t3.js.map +12 -0
  99. package/dist/chunks/studio-na6rwzhw.js +353 -0
  100. package/dist/chunks/studio-na6rwzhw.js.map +11 -0
  101. package/dist/chunks/{studio-4k0m4fmp.js → studio-p471gg5n.js} +126 -3
  102. package/dist/chunks/studio-p471gg5n.js.map +14 -0
  103. package/dist/chunks/{studio-9bjwf0d4.js → studio-rgwcpast.js} +5 -46
  104. package/dist/chunks/{studio-9bjwf0d4.js.map → studio-rgwcpast.js.map} +4 -5
  105. package/dist/chunks/{studio-hhj92gcr.js → studio-s519zc99.js} +3 -3
  106. package/dist/chunks/studio-sas7mt3k.js +75 -0
  107. package/dist/chunks/studio-sas7mt3k.js.map +10 -0
  108. package/dist/chunks/studio-t5wqwzdh.js +74 -0
  109. package/dist/chunks/studio-t5wqwzdh.js.map +10 -0
  110. package/dist/chunks/{studio-9qpspwfn.js → studio-temv5bw4.js} +5 -5
  111. package/dist/chunks/{studio-9qpspwfn.js.map → studio-temv5bw4.js.map} +1 -1
  112. package/dist/chunks/{studio-qkvt37c0.js → studio-tr20m8g8.js} +669 -18
  113. package/dist/chunks/studio-tr20m8g8.js.map +9 -0
  114. package/dist/chunks/{studio-ygzav7z5.js → studio-v96qfnww.js} +5514 -4291
  115. package/dist/chunks/studio-v96qfnww.js.map +54 -0
  116. package/dist/chunks/studio-vcetc91k.js +659 -0
  117. package/dist/chunks/studio-vcetc91k.js.map +10 -0
  118. package/dist/chunks/{studio-n9zmpxjg.js → studio-vmymjefp.js} +2972 -4937
  119. package/dist/chunks/studio-vmymjefp.js.map +98 -0
  120. package/dist/chunks/{studio-bghqfmea.js → studio-wwseqyw0.js} +2 -2
  121. package/dist/chunks/studio-x9hmy5kr.js +390 -0
  122. package/dist/chunks/studio-x9hmy5kr.js.map +10 -0
  123. package/dist/chunks/studio-xa0ce0n7.js +335 -0
  124. package/dist/chunks/studio-xa0ce0n7.js.map +10 -0
  125. package/dist/chunks/studio-xm3dbxvn.js +378 -0
  126. package/dist/chunks/studio-xm3dbxvn.js.map +19 -0
  127. package/dist/chunks/{studio-nqw433kv.js → studio-xmvbhg31.js} +2 -2
  128. package/dist/chunks/studio-zv37yrfa.js +1054 -0
  129. package/dist/chunks/studio-zv37yrfa.js.map +13 -0
  130. package/dist/chunks/{ws-client-q7hkz3j9.js → ws-client-654ts66j.js} +6 -4
  131. package/dist/chunks/ws-client-654ts66j.js.map +11 -0
  132. package/dist/iframe-entry.js +668 -230
  133. package/dist/studio.js +23711 -35185
  134. package/index.html +35 -4685
  135. package/package.json +17 -15
  136. package/src/about/about-modal.ts +42 -11
  137. package/src/browse/library-commands.ts +240 -0
  138. package/src/browse/library-layouts.ts +369 -0
  139. package/src/browse/library-model.ts +396 -0
  140. package/src/browse/library-pane.ts +1049 -0
  141. package/src/browse/library-preview.ts +252 -0
  142. package/src/browse/library-source.ts +150 -0
  143. package/src/canvas/canvas-helpers.ts +33 -9
  144. package/src/canvas/canvas-live-render.ts +45 -29
  145. package/src/canvas/canvas-patcher.ts +151 -31
  146. package/src/canvas/canvas-perf.ts +46 -7
  147. package/src/canvas/canvas-render.ts +1092 -220
  148. package/src/canvas/canvas-surface.ts +240 -0
  149. package/src/canvas/canvas-utils.ts +1166 -124
  150. package/src/canvas/iframe-drop.ts +2 -1
  151. package/src/canvas/iframe-entry.ts +241 -4
  152. package/src/canvas/iframe-host.ts +1226 -143
  153. package/src/canvas/iframe-inline-edit.ts +31 -0
  154. package/src/canvas/iframe-interaction.ts +76 -16
  155. package/src/canvas/iframe-keys.ts +107 -45
  156. package/src/canvas/iframe-overlay.ts +38 -1
  157. package/src/canvas/iframe-protocol.ts +137 -2
  158. package/src/canvas/iframe-render.ts +70 -2
  159. package/src/canvas/iframe-slash.ts +6 -0
  160. package/src/canvas/path-mapping.ts +56 -6
  161. package/src/canvas/surface-registry.ts +292 -0
  162. package/src/collab/collab-commands.ts +244 -0
  163. package/src/collab/collab-session.ts +198 -32
  164. package/src/collab/collab-state.ts +28 -2
  165. package/src/collab/presence-chips.ts +100 -9
  166. package/src/commands/active-registry.ts +42 -0
  167. package/src/commands/app-commands.ts +176 -0
  168. package/src/commands/budget.ts +158 -0
  169. package/src/commands/command-args.ts +253 -0
  170. package/src/commands/context.ts +387 -0
  171. package/src/commands/defaults.ts +580 -0
  172. package/src/commands/keymap.ts +571 -0
  173. package/src/commands/levels.ts +345 -0
  174. package/src/commands/live-context.ts +193 -0
  175. package/src/commands/reference.ts +227 -0
  176. package/src/commands/registry.ts +293 -0
  177. package/src/component-props.ts +3 -2
  178. package/src/content/draft-state.ts +106 -0
  179. package/src/content/entry-commands.ts +282 -0
  180. package/src/content/entry-editor.ts +327 -0
  181. package/src/content/entry-fields.ts +116 -0
  182. package/src/content/entry-model.ts +175 -0
  183. package/src/editor/canvas-context-menu.ts +17 -11
  184. package/src/editor/canvas-slash-bridge.ts +11 -4
  185. package/src/editor/context-menu.ts +804 -204
  186. package/src/editor/convert-to-component.ts +31 -10
  187. package/src/editor/convert-to-repeater.ts +3 -2
  188. package/src/editor/inline-edit-apply.ts +5 -5
  189. package/src/editor/inline-edit.ts +86 -50
  190. package/src/editor/insert-zone-action.ts +1 -1
  191. package/src/editor/shortcuts.ts +1043 -329
  192. package/src/editor/slash-menu.ts +1 -0
  193. package/src/files/elements.ts +166 -0
  194. package/src/files/file-ops.ts +178 -31
  195. package/src/files/files.ts +898 -268
  196. package/src/files/fs-events.ts +21 -0
  197. package/src/files/media-meta.ts +203 -0
  198. package/src/files/media-paths.ts +141 -0
  199. package/src/files/media-upload.ts +7 -3
  200. package/src/files/media-usage.ts +189 -0
  201. package/src/github/github-auth.ts +235 -33
  202. package/src/github/github-publish.ts +80 -21
  203. package/src/grid/grid-controller.ts +152 -12
  204. package/src/grid/grid-layout.ts +225 -20
  205. package/src/grid/grid-open.ts +120 -0
  206. package/src/grid/grid-panel.ts +554 -39
  207. package/src/grid/grid-source.ts +27 -1
  208. package/src/grid/redirects-grid.ts +599 -0
  209. package/src/grid/redirects.ts +398 -0
  210. package/src/grid/sources/content-source.ts +108 -2
  211. package/src/i18n/i18n-commands.ts +322 -0
  212. package/src/new-project/add-repo-modal.ts +8 -10
  213. package/src/new-project/import-tab.ts +34 -9
  214. package/src/new-project/location-fields.ts +9 -1
  215. package/src/new-project/new-project-modal.ts +270 -262
  216. package/src/packages/jxsuite-update.ts +97 -48
  217. package/src/packages/pull-package-sync.ts +18 -9
  218. package/src/page-params.ts +2 -1
  219. package/src/panels/activity-bar.ts +152 -91
  220. package/src/panels/activity-panel.ts +481 -0
  221. package/src/panels/ai-chat/chat-markdown.ts +8 -3
  222. package/src/panels/ai-chat/chat-view.ts +245 -18
  223. package/src/panels/ai-chat/composer.ts +31 -5
  224. package/src/panels/ai-chat/sessions-view.ts +17 -10
  225. package/src/panels/ai-panel.ts +279 -120
  226. package/src/panels/block-action-bar.ts +1115 -224
  227. package/src/panels/bottom-dock.ts +337 -0
  228. package/src/panels/canvas-dnd-bridge.ts +37 -8
  229. package/src/panels/chat-panel.ts +29 -12
  230. package/src/panels/data-explorer.ts +254 -112
  231. package/src/panels/data-grid.ts +10 -4
  232. package/src/panels/dnd.ts +28 -51
  233. package/src/panels/editors.ts +434 -107
  234. package/src/panels/elements-panel.ts +66 -2
  235. package/src/panels/empty-state.ts +112 -0
  236. package/src/panels/events-panel.ts +596 -82
  237. package/src/panels/formula-workspace.ts +408 -218
  238. package/src/panels/frontmatter-fields.ts +51 -10
  239. package/src/panels/frontmatter-panel.ts +357 -79
  240. package/src/panels/git-panel.ts +375 -164
  241. package/src/panels/head-panel.ts +791 -85
  242. package/src/panels/i18n-panel.ts +592 -0
  243. package/src/panels/imports-panel.ts +95 -85
  244. package/src/panels/jump-bar.ts +584 -0
  245. package/src/panels/layers-panel.ts +1106 -328
  246. package/src/panels/left-panel.ts +109 -204
  247. package/src/panels/navigator-panels.ts +131 -0
  248. package/src/panels/overlays.ts +7 -2
  249. package/src/panels/pane-context.ts +1477 -0
  250. package/src/panels/pane-grid.ts +406 -0
  251. package/src/panels/panel-registry.ts +340 -0
  252. package/src/panels/panel-scheduler.ts +75 -0
  253. package/src/panels/problems-panel.ts +234 -0
  254. package/src/panels/properties-panel.ts +946 -1139
  255. package/src/panels/provenance.ts +258 -0
  256. package/src/panels/quick-search.ts +791 -122
  257. package/src/panels/right-panel.ts +228 -102
  258. package/src/panels/seo-modal.ts +395 -0
  259. package/src/panels/settings-pane.ts +203 -0
  260. package/src/panels/signals-panel.ts +354 -137
  261. package/src/panels/statement-editor.ts +41 -25
  262. package/src/panels/statusbar.ts +342 -106
  263. package/src/panels/style-panel.ts +974 -463
  264. package/src/panels/style-utils.ts +3 -3
  265. package/src/panels/stylebook-doc.ts +4 -2
  266. package/src/panels/stylebook-layers-panel.ts +3 -2
  267. package/src/panels/stylebook-panel.ts +51 -26
  268. package/src/panels/tab-strip.ts +1058 -78
  269. package/src/panels/target-line.ts +274 -0
  270. package/src/panels/toolbar.ts +631 -484
  271. package/src/panels/welcome-screen.ts +301 -198
  272. package/src/platform-errors.ts +26 -3
  273. package/src/platform.ts +96 -2
  274. package/src/platforms/cloud.ts +71 -8
  275. package/src/platforms/devserver.ts +92 -29
  276. package/src/publish/deploy-checklist.ts +383 -0
  277. package/src/publish/pages-service.ts +10 -1
  278. package/src/publish/publish-commands.ts +218 -0
  279. package/src/publish/publish-panel.ts +110 -16
  280. package/src/recent-projects.ts +3 -2
  281. package/src/services/a11y-report.ts +505 -0
  282. package/src/services/ai-models.ts +75 -2
  283. package/src/services/ai-project-tools.ts +29 -10
  284. package/src/services/ai-system-prompt.ts +53 -14
  285. package/src/services/ai-tools.ts +39 -3
  286. package/src/services/ai-writes.ts +154 -0
  287. package/src/services/announce.ts +97 -0
  288. package/src/services/automation.ts +424 -175
  289. package/src/services/cem-export.ts +9 -4
  290. package/src/services/clock.ts +61 -0
  291. package/src/services/document-assistant.ts +36 -8
  292. package/src/services/idle.ts +179 -0
  293. package/src/services/import-client.ts +26 -0
  294. package/src/services/monaco-buffer.ts +627 -0
  295. package/src/services/monaco-lazy.ts +36 -0
  296. package/src/services/notify.ts +271 -0
  297. package/src/services/profile.ts +155 -0
  298. package/src/services/references.ts +0 -0
  299. package/src/services/token-lint.ts +3 -2
  300. package/src/services/tool-executor.ts +53 -7
  301. package/src/services/trusted-types.ts +123 -0
  302. package/src/settings/contexts-section.ts +427 -0
  303. package/src/settings/contributed-section.ts +149 -11
  304. package/src/settings/css-vars-editor.ts +306 -349
  305. package/src/settings/defs-editor.ts +93 -34
  306. package/src/settings/dependencies-editor.ts +7 -7
  307. package/src/settings/extension-sections.ts +37 -5
  308. package/src/settings/general-settings.ts +204 -121
  309. package/src/settings/locales-section.ts +347 -0
  310. package/src/settings/preferences-accounts.ts +140 -0
  311. package/src/settings/preferences-dialog.ts +620 -0
  312. package/src/settings/preferences-keymap.ts +270 -0
  313. package/src/settings/project-sections.ts +260 -0
  314. package/src/settings/section-registry.ts +169 -0
  315. package/src/settings/settings-document.ts +385 -0
  316. package/src/shell.ts +1549 -0
  317. package/src/site-context.ts +45 -20
  318. package/src/state.ts +14 -275
  319. package/src/store.ts +88 -43
  320. package/src/studio.ts +790 -346
  321. package/src/style/live-preview.ts +36 -0
  322. package/src/style/project-styles.ts +349 -0
  323. package/src/style/token-ref.ts +123 -0
  324. package/src/tabs/patch-ops.ts +16 -4
  325. package/src/tabs/project-config.ts +416 -0
  326. package/src/tabs/selection.ts +323 -0
  327. package/src/tabs/tab.ts +182 -47
  328. package/src/tabs/transact.ts +209 -28
  329. package/src/types.ts +68 -13
  330. package/src/ui/ai-credentials-form.ts +39 -35
  331. package/src/ui/ai-managed-connect.ts +113 -0
  332. package/src/ui/dynamic-slot.ts +224 -81
  333. package/src/ui/expression-editor.ts +71 -63
  334. package/src/ui/field-input.ts +4 -6
  335. package/src/ui/field-row.ts +215 -38
  336. package/src/ui/form-controls.ts +194 -7
  337. package/src/ui/layers.ts +492 -69
  338. package/src/ui/media-picker.ts +115 -25
  339. package/src/ui/panel-resize.ts +109 -91
  340. package/src/ui/progress-modal.ts +131 -50
  341. package/src/ui/regions.ts +462 -0
  342. package/src/ui/schema-form.ts +229 -45
  343. package/src/ui/spectrum.ts +24 -8
  344. package/src/ui/timing.ts +49 -0
  345. package/src/ui/value-selector.ts +20 -0
  346. package/src/ui/value-source.ts +348 -0
  347. package/src/ui/virtual-window.ts +400 -0
  348. package/src/utils/edit-display.ts +3 -2
  349. package/src/utils/inherited-style.ts +67 -37
  350. package/src/view.ts +64 -78
  351. package/src/workspace/pane-derive.ts +1258 -0
  352. package/src/workspace/session.ts +317 -0
  353. package/src/workspace/workspace.ts +1345 -23
  354. package/styles/canvas.css +653 -0
  355. package/styles/forced-colors.css +106 -0
  356. package/styles/inspector.css +1454 -0
  357. package/styles/overlays.css +2704 -0
  358. package/styles/panels.css +1701 -0
  359. package/styles/shell.css +1423 -0
  360. package/styles/tokens.css +151 -0
  361. package/dist/chunks/Overlay.dev-cszx7yn5.js +0 -22
  362. package/dist/chunks/extension-sections-qct0gt8f.js +0 -531
  363. package/dist/chunks/extension-sections-qct0gt8f.js.map +0 -12
  364. package/dist/chunks/files-02zn5y24.js +0 -50
  365. package/dist/chunks/files-02zn5y24.js.map +0 -9
  366. package/dist/chunks/git-panel-mbb0y0hz.js +0 -28
  367. package/dist/chunks/git-panel-mbb0y0hz.js.map +0 -9
  368. package/dist/chunks/layers-panel-9qekmqk1.js +0 -26
  369. package/dist/chunks/layers-panel-9qekmqk1.js.map +0 -9
  370. package/dist/chunks/settings-modal-gj0hrah4.js +0 -29
  371. package/dist/chunks/settings-modal-gj0hrah4.js.map +0 -9
  372. package/dist/chunks/sp-overlay.dev-earn5as8.js +0 -17
  373. package/dist/chunks/sp-overlay.dev-earn5as8.js.map +0 -9
  374. package/dist/chunks/sp-popover.dev-frxtfejn.js +0 -8
  375. package/dist/chunks/sp-popover.dev-frxtfejn.js.map +0 -9
  376. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js +0 -14
  377. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js.map +0 -9
  378. package/dist/chunks/sp-tooltip.dev-a9fywesm.js +0 -21
  379. package/dist/chunks/sp-tooltip.dev-a9fywesm.js.map +0 -10
  380. package/dist/chunks/studio-4k0m4fmp.js.map +0 -13
  381. package/dist/chunks/studio-568nx4t5.js +0 -469
  382. package/dist/chunks/studio-568nx4t5.js.map +0 -14
  383. package/dist/chunks/studio-9smfzqhc.js +0 -19
  384. package/dist/chunks/studio-9smfzqhc.js.map +0 -10
  385. package/dist/chunks/studio-asvf0j9e.js +0 -429
  386. package/dist/chunks/studio-asvf0j9e.js.map +0 -13
  387. package/dist/chunks/studio-d77qm3pk.js +0 -3427
  388. package/dist/chunks/studio-d77qm3pk.js.map +0 -26
  389. package/dist/chunks/studio-j6gqyjf4.js +0 -1854
  390. package/dist/chunks/studio-j6gqyjf4.js.map +0 -18
  391. package/dist/chunks/studio-n9zmpxjg.js.map +0 -115
  392. package/dist/chunks/studio-qkvt37c0.js.map +0 -9
  393. package/dist/chunks/studio-rq0vxb19.js +0 -3744
  394. package/dist/chunks/studio-rq0vxb19.js.map +0 -22
  395. package/dist/chunks/studio-wd6ewdvd.js +0 -853
  396. package/dist/chunks/studio-wd6ewdvd.js.map +0 -12
  397. package/dist/chunks/studio-xq0t7n5t.js +0 -140
  398. package/dist/chunks/studio-xq0t7n5t.js.map +0 -10
  399. package/dist/chunks/studio-xze4zrs1.js.map +0 -10
  400. package/dist/chunks/studio-ygzav7z5.js.map +0 -46
  401. package/dist/chunks/studio-zmy7241e.js +0 -399
  402. package/dist/chunks/studio-zmy7241e.js.map +0 -12
  403. package/dist/chunks/ws-client-q7hkz3j9.js.map +0 -11
  404. package/src/browse/browse-modal.ts +0 -69
  405. package/src/browse/browse.ts +0 -904
  406. package/src/canvas/canvas-diff.ts +0 -216
  407. package/src/canvas/nested-site-style.ts +0 -58
  408. package/src/new-project/design-fields.ts +0 -264
  409. package/src/new-project/templates.ts +0 -61
  410. package/src/panels/tab-bar.ts +0 -548
  411. package/src/settings/settings-modal.ts +0 -203
  412. /package/dist/chunks/{adapter-z8fkmbmv.js.map → adapter-qfnesn5v.js.map} +0 -0
  413. /package/dist/chunks/{cf-settings-mmyv436d.js.map → cf-settings-73sh5z4p.js.map} +0 -0
  414. /package/dist/chunks/{format-host-2c188n41.js.map → format-host-mkwsmt8d.js.map} +0 -0
  415. /package/dist/chunks/{jx-validate-qx8degj7.js.map → jx-validate-v6qgknrs.js.map} +0 -0
  416. /package/dist/chunks/{studio-w7c6kvk7.js.map → studio-a7pzveq1.js.map} +0 -0
  417. /package/dist/chunks/{studio-hhj92gcr.js.map → studio-s519zc99.js.map} +0 -0
  418. /package/dist/chunks/{studio-bghqfmea.js.map → studio-wwseqyw0.js.map} +0 -0
  419. /package/dist/chunks/{studio-nqw433kv.js.map → studio-xmvbhg31.js.map} +0 -0
@@ -3,9 +3,16 @@
3
3
  * Ai-panel.ts — AI assistant tab for the right panel (Stack B document assistant).
4
4
  *
5
5
  * Native lit-html chat UI (VSCode-Copilot style) over the reactive document-assistant
6
- * session: a view-state machine (key gate → sessions list ↔ chat), a message list with
6
+ * session: a view-state machine (sessions list ↔ chat), a message list with
7
7
  * stick-to-bottom scrolling, and the sticky composer.
8
8
  *
9
+ * Credentials are NOT a state of this panel. A provider key is a roaming APPLICATION setting
10
+ * configured once, so it lives in Preferences › Assistant (⌘,) — not in a dialog reachable only
11
+ * from the panel that is broken for want of one. The panel with no key configured still opens on an
12
+ * invitation to talk, with the way to fix it offered beneath. It repaints when a credential is
13
+ * saved or revoked by subscribing to `settings/preferences-accounts.ts`, which is a LEAF both
14
+ * modules depend on rather than an import back into the sheet.
15
+ *
9
16
  * Rendering: this panel owns a private rAF-coalesced render loop into the assistant
10
17
  * `.panel-body` container (bound once via {@link bindAiPanelHost}). It deliberately
11
18
  * bypasses the right-panel scheduler's focus guard — streaming must repaint while the
@@ -13,49 +20,39 @@
13
20
  * textarea is uncontrolled). The right panel renders the same template into the same
14
21
  * container on tab switches; lit reconciles both paths through one part cache.
15
22
  *
23
+ * @docs studio/ai/chat
16
24
  * @license MIT
17
25
  */
18
26
 
19
27
  import { html, render as litRender, nothing } from "lit-html";
20
28
  import type { TemplateResult } from "lit-html";
21
- import { getPlatform } from "../platform";
22
29
  import { effect, effectScope } from "../reactivity";
23
30
  import { createDocumentAssistant } from "../services/document-assistant";
24
- import { hasOpenAiKey, setOpenAiKey } from "../services/ai-settings";
25
- import { fetchAvailableModels, isManagedProxy, isProxyConfigured } from "../services/ai-models";
26
- import { createAiCredentialsForm } from "../ui/ai-credentials-form";
31
+ import { writesForTurn } from "../services/ai-writes";
32
+ import { notify } from "../services/notify";
33
+ import { undo } from "../tabs/transact";
34
+ import { activeTab } from "../workspace/workspace";
35
+ import { setOpenAiKey } from "../services/ai-settings";
36
+ import { hasAiCredentials } from "../services/ai-models";
37
+ import { openPreferences } from "../settings/preferences-dialog";
38
+ import { onCredentialsChanged } from "../settings/preferences-accounts";
39
+ import { setDockCollapsed } from "../shell";
40
+ import { hasSelection } from "../commands/context";
27
41
  import { clearMarkdownCache } from "./ai-chat/chat-markdown";
28
42
  import { renderChatHeader, renderMessageList } from "./ai-chat/chat-view";
29
43
  import { createComposer } from "./ai-chat/composer";
30
44
  import { renderSessionsList } from "./ai-chat/sessions-view";
45
+ import { setInspectorTab } from "./right-panel";
31
46
 
47
+ import type { AnyCommand } from "../commands/registry";
48
+ import type { CommandContext } from "../commands/context";
32
49
  import type { EffectScope } from "@vue/reactivity";
33
50
 
34
51
  // ─── State (module-level, persists across tab switches) ─────────────────────
35
52
 
36
53
  let mounted = false;
37
54
 
38
- /** Whether the OpenAI key form is showing (gate when no key, or re-edit via the gear). */
39
- let keyEditing = false;
40
-
41
- /**
42
- * One-time proxy probe: managed platforms (cloud Workers AI) and env-keyed dev servers report
43
- * `configured` from /models, unlocking the assistant without a locally stored key. Fired lazily on
44
- * the first gated render.
45
- */
46
- let proxyProbe: Promise<void> | null = null;
47
-
48
- function ensureProxyProbe() {
49
- proxyProbe ??= fetchAvailableModels({ force: true })
50
- .catch(() => {
51
- // Unreachable proxy — the key gate stays up.
52
- })
53
- .then(() => {
54
- scheduleAiRender();
55
- });
56
- }
57
-
58
- /** Which pane the panel shows once the key gate is passed. */
55
+ /** Which pane the panel shows. */
59
56
  let view: "chat" | "sessions" = "chat";
60
57
 
61
58
  /** Document AST assistant session — created lazily, persists across tab switches. */
@@ -124,6 +121,9 @@ export function mountAiPanel() {
124
121
  return;
125
122
  }
126
123
  mounted = true;
124
+ // A key saved (or revoked) in Preferences changes what this panel shows — the setup notice, and
125
+ // Whether the composer can send. One subscription, and no import back into the sheet.
126
+ onCredentialsChanged(scheduleAiRender);
127
127
  }
128
128
 
129
129
  // ─── Auto-scroll (stick to bottom unless the user scrolled up) ──────────────
@@ -150,88 +150,29 @@ function maintainScroll() {
150
150
  }
151
151
  }
152
152
 
153
- // ─── OpenAI key settings ─────────────────────────────────────────────────────
154
-
155
- /** The panel's shared credentials form (draft/model state lives inside the form's closure). */
156
- const credsForm = createAiCredentialsForm({
157
- onCancel: () => {
158
- keyEditing = false;
159
- },
160
- onSaved: () => {
161
- keyEditing = false;
162
- },
163
- requestRender: scheduleAiRender,
164
- });
165
-
166
- /** Open the key form, pre-filled with the current settings. */
167
- function startEditApiKey() {
168
- keyEditing = true;
169
- credsForm.startEdit();
170
- }
171
-
172
- // ─── Managed Cloudflare connect (Workers AI) ─────────────────────────────────
173
-
174
- let cfConnectBusy = false;
175
- let cfConnectError = "";
153
+ // ─── The credentials gate's in-panel residue ─────────────────────────────────
176
154
 
177
155
  /**
178
- * Managed platforms broker Workers AI on the user's own Cloudflare account: offer connecting it as
179
- * the keyless alternative whenever the proxy says managed-but-unconfigured and the platform can run
180
- * the hosted OAuth flow (the PAL seam — desktop shells can implement cfConnect later).
156
+ * One line and the action that fixes it, under a chat that still invites a conversation.
157
+ *
158
+ * The action is `Preferences › Assistant`, not a dialog of this panel's own: a provider key is an
159
+ * application setting, and the surface that owns application settings is the one that can also list
160
+ * and revoke it. The probe fires here for the same reason the old gate fired it — a managed
161
+ * platform or an env-keyed dev server may already be configured, and only `/models` knows.
181
162
  */
182
- function canOfferManagedConnect(): boolean {
183
- return isManagedProxy() && !isProxyConfigured() && Boolean(getPlatform().cfConnect);
184
- }
185
-
186
- async function connectCloudflareForAi() {
187
- if (cfConnectBusy) {
188
- return;
189
- }
190
- cfConnectBusy = true;
191
- cfConnectError = "";
192
- scheduleAiRender();
193
- try {
194
- const connection = await getPlatform().cfConnect?.();
195
- if (connection) {
196
- // Re-probe: /models flips to configured once the connection lands, opening the gate.
197
- await fetchAvailableModels({ force: true });
198
- } else {
199
- cfConnectError = "Cloudflare connection was not completed.";
200
- }
201
- } catch (error) {
202
- cfConnectError = error instanceof Error ? error.message : String(error);
203
- }
204
- cfConnectBusy = false;
205
- scheduleAiRender();
206
- }
207
-
208
- function renderManagedConnect() {
163
+ function renderSetupNotice(): TemplateResult {
209
164
  return html`
210
- <div class="ai-managed-connect">
211
- <div>Use Workers AI on your own Cloudflare account — no API key needed.</div>
212
- <sp-button size="s" ?disabled=${cfConnectBusy} @click=${() => void connectCloudflareForAi()}>
213
- ${cfConnectBusy ? "Connecting…" : "Connect Cloudflare"}
165
+ <div class="ai-setup-notice">
166
+ <span>No AI provider is connected yet.</span>
167
+ <sp-button
168
+ size="s"
169
+ variant="secondary"
170
+ @click=${() => {
171
+ void openPreferences("assistant");
172
+ }}
173
+ >
174
+ Open Preferences…
214
175
  </sp-button>
215
- ${
216
- cfConnectError
217
- ? html`<div class="ai-managed-connect-error">${cfConnectError}</div>`
218
- : nothing
219
- }
220
- <div class="ai-managed-connect-divider">— or bring your own key —</div>
221
- </div>
222
- `;
223
- }
224
-
225
- /**
226
- * The credentials gate: on managed platforms a keyless "Connect Cloudflare" (Workers AI) option
227
- * sits above the OpenAI-compatible key form — both are real, working paths.
228
- */
229
- function renderKeyGate() {
230
- return html`
231
- <div class="ai-tab-body">
232
- <div class="ai-status-center">
233
- ${canOfferManagedConnect() ? renderManagedConnect() : nothing} ${credsForm.render()}
234
- </div>
235
176
  </div>
236
177
  `;
237
178
  }
@@ -255,6 +196,64 @@ async function handleAssistantSend(text: string) {
255
196
  }
256
197
  }
257
198
 
199
+ /**
200
+ * Re-send the last user message — §7.4's Retry.
201
+ *
202
+ * `chatState.retryLast()` pops the failed assistant turn AND the user message that caused it, on
203
+ * the contract that the caller re-sends. It has been exported with zero callers since it was
204
+ * written, so a failed turn's only recovery was retyping the prompt. Read the text before popping;
205
+ * there is nowhere else it survives.
206
+ */
207
+ async function handleRetry(): Promise<void> {
208
+ const cs = assistant.chatState;
209
+ const lastUser = cs.messages.toReversed().find((m) => m.role === "user");
210
+ if (!lastUser) {
211
+ return;
212
+ }
213
+ const { content } = lastUser;
214
+ cs.retryLast();
215
+ scheduleAiRender();
216
+ await handleAssistantSend(content);
217
+ }
218
+
219
+ /**
220
+ * Undo everything one assistant turn changed — §7.4's "Restore to here".
221
+ *
222
+ * The loop opens one batch per turn per document, so undoing the turn is undoing that batch. The
223
+ * button is offered by the renderer only when every recorded change was transactional; this guard
224
+ * is the second half of the same promise, because a ledger can be trimmed (MAX_TURNS) between the
225
+ * render and the click and a Restore that silently restored SOME of a turn would be worse than one
226
+ * that refused.
227
+ *
228
+ * @param {string} messageId
229
+ */
230
+ /* Exported so the guard can be exercised directly: the button is not RENDERED for a turn that
231
+ touched disk, which would otherwise make the refusal path unreachable from the panel. */
232
+ export function handleRestore(messageId: string): void {
233
+ const writes = writesForTurn(messageId);
234
+ if (writes.length === 0) {
235
+ notify.warn("There is no longer a record of what that turn changed.", { source: "Assistant" });
236
+ return;
237
+ }
238
+ const disk = writes.filter((w) => w.disk).map((w) => w.path);
239
+ if (disk.length > 0) {
240
+ notify.warn(
241
+ `Cannot restore: ${disk.join(", ")} ${disk.length === 1 ? "was" : "were"} written straight ` +
242
+ "to disk, which undo cannot reach.",
243
+ { source: "Assistant" },
244
+ );
245
+ return;
246
+ }
247
+ const tab = activeTab.value;
248
+ if (!tab) {
249
+ notify.warn("Open the document that turn edited to restore it.", { source: "Assistant" });
250
+ return;
251
+ }
252
+ undo(tab);
253
+ notify.success("Restored to before that turn.", { action: "edit.redo", source: "Assistant" });
254
+ scheduleAiRender();
255
+ }
256
+
258
257
  /**
259
258
  * Seed the assistant with a prompt programmatically (e.g. the New Project flow handing off a
260
259
  * project brief). Delegates to the same send path as the composer. Safe to call right after the
@@ -292,7 +291,6 @@ let seededCount = 0;
292
291
  */
293
292
  export function seedAssistantMessages(messages: SeededAssistantMessage[]): void {
294
293
  setOpenAiKey("sk-demo");
295
- keyEditing = false;
296
294
  view = "chat";
297
295
  stickToBottom = true;
298
296
  for (const msg of messages) {
@@ -319,6 +317,35 @@ export function seedAssistantMessages(messages: SeededAssistantMessage[]): void
319
317
 
320
318
  // ─── Controls ─────────────────────────────────────────────────────────────────
321
319
 
320
+ /**
321
+ * Show the assistant: open the Inspector, then select its fourth tab.
322
+ *
323
+ * The two lines `view.setAssistant { open: true }` runs, in the module that owns the surface, so
324
+ * the three callers that need them — that record, `chat-panel.ts`'s pending-prompt handoff, and
325
+ * every `Assistant:` command below — do not each keep their own copy. `chat-panel.ts` had one
326
+ * inline, and a reveal that opens the dock but forgets the tab (or the reverse) is a silent
327
+ * half-success.
328
+ */
329
+ export function revealAssistant(): void {
330
+ setDockCollapsed("right", false);
331
+ setInspectorTab("assistant");
332
+ }
333
+
334
+ /**
335
+ * Whether a turn is in flight — the probe `commands/live-context.ts` declares as `aiStreaming` and
336
+ * projects onto `ctx.ai.streaming`.
337
+ *
338
+ * That source was declared optional with the note "there is nothing to read yet; the caller passes
339
+ * a probe when one exists", and no caller ever did — so `ctx.ai.streaming` read `false` forever and
340
+ * `assistant.stop` would have been permanently refused. Reading the reactive chat state here is
341
+ * what makes the fact LIVE: `createLiveContext` builds a fresh record per predicate evaluation, so
342
+ * a surface repainting from an effect tracks this status and re-renders when the stream starts or
343
+ * ends.
344
+ */
345
+ export function isAssistantStreaming(): boolean {
346
+ return assistant.chatState.status === "streaming";
347
+ }
348
+
322
349
  function stop() {
323
350
  assistant.stop();
324
351
  }
@@ -361,8 +388,10 @@ function activeSessionTitle(): string | null {
361
388
  // ─── Composer ───────────────────────────────────────────────────────────────
362
389
 
363
390
  const composer = createComposer({
364
- isStreaming: () => assistant.chatState.status === "streaming",
365
- onOpenSettings: startEditApiKey,
391
+ isStreaming: isAssistantStreaming,
392
+ onOpenSettings: () => {
393
+ void openPreferences("assistant");
394
+ },
366
395
  onSend: (text) => {
367
396
  void handleAssistantSend(text);
368
397
  },
@@ -370,26 +399,155 @@ const composer = createComposer({
370
399
  requestRender: scheduleAiRender,
371
400
  });
372
401
 
402
+ /**
403
+ * Reveal the assistant, put it on the chat view, and place the caret in the composer.
404
+ *
405
+ * The focus rides a frame because {@link scheduleAiRender} is rAF-coalesced: revealing from the
406
+ * sessions list means the textarea does not exist yet, and focusing a node that is about to be
407
+ * replaced would leave the caret nowhere. Ordering holds because this frame is queued after the
408
+ * render's.
409
+ */
410
+ function focusComposer(): void {
411
+ revealAssistant();
412
+ if (view !== "chat") {
413
+ view = "chat";
414
+ scheduleAiRender();
415
+ }
416
+ requestAnimationFrame(() => {
417
+ composer.focus();
418
+ });
419
+ }
420
+
421
+ // ─── The `Assistant:` command family (§11.1) ────────────────────────────────
422
+
423
+ /**
424
+ * Six records, beside the chat session they write.
425
+ *
426
+ * §11.1 pays for the chat column's demotion into an Inspector tab with a command family, and the
427
+ * `Assistant` category — declared in `commands/levels.ts` since the taxonomy landed — held ZERO
428
+ * records. Every capability below already existed and every one of them existed ONLY as a button in
429
+ * this panel: not in the palette, not bindable, not reachable by `__jxAutomation` or by name, and
430
+ * absent from the generated commands and shortcuts sheets. A capability that one surface can reach
431
+ * and the registry cannot is the second definition site inverted.
432
+ *
433
+ * **All six are `level: "application"`, by principle 3 — a record is filed by the level of the
434
+ * state it WRITES, not the state it reads.** The chat session is application state: it outlives the
435
+ * open document, survives project close, and `assistant.attachSelection` is the case that proves
436
+ * the rule — it READS the canvas selection and writes a chip into the composer, so it is
437
+ * application, not selection, however selection-ish it looks. That is also why it may declare
438
+ * `menus: ["palette"]` at all: `blockbar` and `context/element` admit selection-level records
439
+ * only.
440
+ *
441
+ * **No `aiTool` on any of them.** The assistant projecting "start a new chat" into its own tool
442
+ * list would let a turn end its own conversation; the tier tables in `services/ai-tools.ts` gate
443
+ * what the agent may do TO A PROJECT, not to the chat it is running inside.
444
+ */
445
+ export function assistantCommands(): AnyCommand[] {
446
+ return [
447
+ {
448
+ category: "Assistant",
449
+ id: "assistant.focus",
450
+ level: "application",
451
+ /* A sixth record, and it earns its place: `inspector.focus.assistant` (⌘⇧4) is DOCUMENT-level
452
+ and refuses when nothing is open — which is exactly the state the assistant is most wanted
453
+ in, because the panel deliberately renders with no project and no document. `view.setAssistant`
454
+ is `menus: ["never"]`. Neither puts the caret anywhere, so neither answers "let me type".
455
+ The title is distinct on purpose: two palette rows printing one sentence is the defect
456
+ `tests/app-commands-composition.test.ts` refuses, and "Show Assistant" is already taken. */
457
+ title: "Focus Composer",
458
+ keybinding: "mod+shift+a",
459
+ menus: ["palette"],
460
+ group: "1_chat",
461
+ run: focusComposer,
462
+ },
463
+ {
464
+ category: "Assistant",
465
+ id: "assistant.newChat",
466
+ level: "application",
467
+ title: "New Chat",
468
+ menus: ["palette"],
469
+ group: "1_chat",
470
+ // No `enablement` on the stream: `newChat()` calls `stop()` first (see
471
+ // `services/document-assistant.ts`), so starting one mid-turn is already the handled case.
472
+ run: () => {
473
+ revealAssistant();
474
+ newChat();
475
+ },
476
+ },
477
+ {
478
+ category: "Assistant",
479
+ id: "assistant.history",
480
+ level: "application",
481
+ title: "Chat History",
482
+ menus: ["palette"],
483
+ group: "1_chat",
484
+ run: () => {
485
+ revealAssistant();
486
+ showSessions();
487
+ },
488
+ },
489
+ {
490
+ category: "Assistant",
491
+ id: "assistant.attachSelection",
492
+ level: "application",
493
+ title: "Attach Selection",
494
+ menus: ["palette"],
495
+ group: "2_turn",
496
+ requires: "an element selected on the canvas",
497
+ // `hasSelection` is `commands/context.ts`'s shared predicate — the same one the element menu
498
+ // And the structural verbs are gated on, so "a selection" means one thing in three menus.
499
+ when: (ctx: CommandContext) => hasSelection(ctx),
500
+ run: () => {
501
+ if (!composer.attachSelection()) {
502
+ // Reachable despite `when`: a script or the palette can race the selection away between
503
+ // The predicate and the run, and a silent no-op would look like the chip had landed.
504
+ notify.warn("Nothing is selected to attach.", { source: "Assistant" });
505
+ return;
506
+ }
507
+ focusComposer();
508
+ },
509
+ },
510
+ {
511
+ category: "Assistant",
512
+ id: "assistant.retry",
513
+ level: "application",
514
+ title: "Retry Last Message",
515
+ menus: ["palette"],
516
+ group: "2_turn",
517
+ requires: "a connected AI provider, and no turn already in flight",
518
+ /* `ctx.ai.configured` and `ctx.ai.streaming` have been declared in `commands/context.ts` with
519
+ zero readers; this record and `assistant.stop` are the first. Re-sending with no provider
520
+ connected reproduces the same failure the error row is already explaining, and re-sending
521
+ mid-stream would interleave two turns. */
522
+ enablement: (ctx: CommandContext) => ctx.ai.configured && !ctx.ai.streaming,
523
+ run: () => {
524
+ revealAssistant();
525
+ void handleRetry();
526
+ },
527
+ },
528
+ {
529
+ category: "Assistant",
530
+ id: "assistant.stop",
531
+ level: "application",
532
+ title: "Stop Responding",
533
+ menus: ["palette"],
534
+ group: "2_turn",
535
+ requires: "a turn in flight",
536
+ enablement: (ctx: CommandContext) => ctx.ai.streaming,
537
+ run: stop,
538
+ },
539
+ ];
540
+ }
541
+
373
542
  // ─── Template ───────────────────────────────────────────────────────────────
374
543
 
375
544
  /** @returns {TemplateResult} */
376
545
  export function renderAiPanelTemplate(): TemplateResult {
377
- /* The document assistant authenticates via the AI proxy. Gate the chat
378
- behind the key form until a key is stored locally OR the proxy reports
379
- itself configured (managed platforms, env-keyed dev servers). */
380
- if ((!hasOpenAiKey() && !isProxyConfigured()) || keyEditing) {
381
- if (!keyEditing) {
382
- ensureProxyProbe();
383
- }
384
- return renderKeyGate();
385
- }
386
-
387
546
  if (view === "sessions") {
388
547
  return html`
389
548
  <div class="ai-tab-body">
390
549
  ${renderSessionsList({
391
550
  onDelete: deleteSession,
392
- onNew: newChat,
393
551
  onOpen: openSession,
394
552
  sessions: assistant.listSessions(),
395
553
  })}
@@ -401,19 +559,20 @@ export function renderAiPanelTemplate(): TemplateResult {
401
559
  return html`
402
560
  <div class="ai-tab-body">
403
561
  ${renderChatHeader({
404
- onNewChat: newChat,
405
- onShowSessions: showSessions,
562
+ overBudget: cs.contextWarning,
406
563
  streaming: cs.status === "streaming",
407
564
  title: activeSessionTitle(),
565
+ tokens: cs.tokenCount,
408
566
  })}
409
567
  ${renderMessageList({
410
568
  error: cs.error,
411
569
  listRef: onMessagesListRef,
412
570
  messages: cs.messages,
571
+ onRestore: handleRestore,
413
572
  onScroll: onMessagesScroll,
414
573
  status: cs.status,
415
574
  })}
416
- ${composer.render()}
575
+ ${hasAiCredentials() ? nothing : renderSetupNotice()} ${composer.render()}
417
576
  </div>
418
577
  `;
419
578
  }