@jxsuite/studio 2.0.1 → 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-799jpvhm.js → components-2bpqp4mq.js} +5 -7
  7. package/dist/chunks/{components-799jpvhm.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-8jxwts2p.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-gs6kqdt1.js → studio-adn0z1k3.js} +14 -8
  74. package/dist/chunks/{studio-gs6kqdt1.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-esrewxap.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-v7jkjqng.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-jm0z592w.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 +21080 -32585
  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 +7 -0
  214. package/src/new-project/location-fields.ts +9 -1
  215. package/src/new-project/new-project-modal.ts +221 -245
  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 -53
  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 +30 -1
  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 +7 -1
  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-qr3drkkw.js +0 -531
  363. package/dist/chunks/extension-sections-qr3drkkw.js.map +0 -12
  364. package/dist/chunks/files-j8a1t06k.js +0 -50
  365. package/dist/chunks/files-j8a1t06k.js.map +0 -9
  366. package/dist/chunks/git-panel-xtq5z81x.js +0 -28
  367. package/dist/chunks/git-panel-xtq5z81x.js.map +0 -9
  368. package/dist/chunks/layers-panel-ancn2wpq.js +0 -26
  369. package/dist/chunks/layers-panel-ancn2wpq.js.map +0 -9
  370. package/dist/chunks/settings-modal-17ttr96g.js +0 -29
  371. package/dist/chunks/settings-modal-17ttr96g.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-0xgs7xrs.js +0 -429
  381. package/dist/chunks/studio-0xgs7xrs.js.map +0 -13
  382. package/dist/chunks/studio-4d8hzcz5.js +0 -3427
  383. package/dist/chunks/studio-4d8hzcz5.js.map +0 -26
  384. package/dist/chunks/studio-4k0m4fmp.js.map +0 -13
  385. package/dist/chunks/studio-9smfzqhc.js +0 -19
  386. package/dist/chunks/studio-9smfzqhc.js.map +0 -10
  387. package/dist/chunks/studio-jm0z592w.js.map +0 -115
  388. package/dist/chunks/studio-m2t4mdg5.js +0 -1854
  389. package/dist/chunks/studio-m2t4mdg5.js.map +0 -18
  390. package/dist/chunks/studio-n4p11dfy.js +0 -853
  391. package/dist/chunks/studio-n4p11dfy.js.map +0 -12
  392. package/dist/chunks/studio-qkvt37c0.js.map +0 -9
  393. package/dist/chunks/studio-sjh1wt1a.js +0 -3744
  394. package/dist/chunks/studio-sjh1wt1a.js.map +0 -22
  395. package/dist/chunks/studio-v7jkjqng.js.map +0 -46
  396. package/dist/chunks/studio-xq0t7n5t.js +0 -140
  397. package/dist/chunks/studio-xq0t7n5t.js.map +0 -10
  398. package/dist/chunks/studio-xze4zrs1.js.map +0 -10
  399. package/dist/chunks/studio-yfcstdzy.js +0 -399
  400. package/dist/chunks/studio-yfcstdzy.js.map +0 -12
  401. package/dist/chunks/studio-zp7jncs8.js +0 -469
  402. package/dist/chunks/studio-zp7jncs8.js.map +0 -14
  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-8jxwts2p.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-esrewxap.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
@@ -6,7 +6,27 @@
6
6
  * blocks become chips), assistant messages render sanitized markdown plus tool-call
7
7
  * chips, tool messages surface failures only (ADR §11.3), the streaming tail renders
8
8
  * as plain text with a cursor (markdown parses once on finalize), and chat errors get
9
- * a danger row with recovery advice. Pure templates — state lives in ai-panel.
9
+ * a danger row with recovery advice and a Retry. Templates only — chat state lives in ai-panel, and
10
+ * the three buttons are records the registry holds (see below).
11
+ *
12
+ * §7.4 (AI honesty) is why three things here are not what they were:
13
+ *
14
+ * - **A chip renders its OUTCOME.** `ToolCallRecord.result` has always been populated by the loop
15
+ * and ignored by this renderer, so a chip that said `update_style: ["children",0]` said exactly
16
+ * as much when the edit had been refused as when it had landed.
17
+ * - **A turn renders what it CHANGED.** The changed-files summary comes off the write ledger
18
+ * (`services/ai-writes.ts`), which records whether each change went through a transaction or
19
+ * straight to disk — so the undo caveat is rendered to the human holding ⌘Z instead of being
20
+ * appended to the model-facing tool summary.
21
+ * - **An error offers Retry.** `chatState.retryLast()` has been implemented, exported and called by
22
+ * nobody; the error row is where it belongs.
23
+ *
24
+ * §11.1 is why the three buttons here are not callbacks any more. History, New Chat and Retry were
25
+ * closures this module received and invoked, so the capabilities existed ONLY as buttons: the
26
+ * `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 run {@link commandButton} now, in the
28
+ * idiom `panels/statusbar.ts` established — the record is the definition site, and this file only
29
+ * decides where it is drawn.
10
30
  *
11
31
  * @license MIT
12
32
  */
@@ -17,6 +37,8 @@ import { ref } from "lit-html/directives/ref.js";
17
37
  import type { Message, ToolCallRecord } from "@jxsuite/ai/chat-state";
18
38
  import { splitAttachedContext } from "./attached-context";
19
39
  import { renderMarkdown } from "./chat-markdown";
40
+ import { summarizeWrites, writesForTurn } from "../../services/ai-writes";
41
+ import { activeRegistry } from "../../commands/active-registry";
20
42
 
21
43
  // ─── Helpers (moved from ai-panel.ts) ────────────────────────────────────────
22
44
 
@@ -89,33 +111,125 @@ export function formatErrorAdvice(error: string): string {
89
111
  return "";
90
112
  }
91
113
 
114
+ // ─── Commands as buttons ────────────────────────────────────────────────────
115
+
116
+ /** What {@link commandButton} draws inside the button, and how. */
117
+ export interface CommandButtonOptions {
118
+ /** Slotted content — an icon element for the header, a label for the error row's Retry. */
119
+ content: TemplateResult | string;
120
+ /** Extra class, so the CSS that already targets `.ai-msg-retry` keeps landing. */
121
+ className?: string;
122
+ /** Quiet chrome. The header's icon buttons are quiet; the labelled Retry is not. */
123
+ quiet?: boolean;
124
+ }
125
+
126
+ /**
127
+ * One control that IS a command — `panels/statusbar.ts`'s `itemTpl`, for the assistant.
128
+ *
129
+ * A command the registry does not hold, or whose `when` is false, renders NOTHING rather than a
130
+ * dead button; a visible-but-refused one renders disabled with its `requires` sentence in the
131
+ * tooltip. That is what keeps this file a rendering of the registry instead of a second place the
132
+ * assistant's capabilities are decided — and it is why `tests/ai-chat-view.test.ts` asserts the
133
+ * ids, exactly as `tests/statusbar.test.ts` does: an id is not an interface between two files
134
+ * unless something checks it.
135
+ *
136
+ * Before any registry exists (the bootstrap composes one at the END of `studio.ts`, and a reduced
137
+ * test fixture may compose none) the button is simply absent. The chat is still readable, which is
138
+ * the same bargain the status bar strikes for the frame it paints early.
139
+ */
140
+ export function commandButton(
141
+ id: string,
142
+ opts: CommandButtonOptions,
143
+ ): TemplateResult | typeof nothing {
144
+ const registry = activeRegistry();
145
+ const command = registry?.get(id);
146
+ if (!registry || !command || !registry.isVisible(id)) {
147
+ return nothing;
148
+ }
149
+ const reason = registry.disabledReason(id);
150
+ const chord = registry.keymap.formatBinding(id);
151
+ const title = reason
152
+ ? `${command.title} — requires ${reason}`
153
+ : chord
154
+ ? `${command.title} (${chord})`
155
+ : command.title;
156
+ return html`<sp-action-button
157
+ size="s"
158
+ ?quiet=${opts.quiet ?? false}
159
+ class=${opts.className ?? ""}
160
+ ?disabled=${reason !== undefined}
161
+ title=${title}
162
+ @click=${() => {
163
+ /* Re-asked at click time, not trusted from the render: state moves between the two, and
164
+ `registry.run` THROWS on a refusal. Same bargain `registry.handleKeyEvent` strikes for a
165
+ chord bound to a disabled command — swallow it here rather than make every surface wrap a
166
+ dispatch in try/catch. */
167
+ if (registry.isEnabled(id)) {
168
+ void registry.run(id);
169
+ }
170
+ }}
171
+ >
172
+ ${opts.content}
173
+ </sp-action-button>`;
174
+ }
175
+
92
176
  // ─── Header ─────────────────────────────────────────────────────────────────
93
177
 
94
178
  export interface ChatHeaderOptions {
95
179
  /** The open session's title, or null for a fresh unsaved chat. */
96
180
  title: string | null;
97
181
  streaming: boolean;
98
- onShowSessions: () => void;
99
- onNewChat: () => void;
182
+ /**
183
+ * The conversation's estimated token count, and whether it is over the warning line.
184
+ *
185
+ * `services/context-manager.ts` has computed both on every turn since it was written, and
186
+ * `chat-state.ts` has stored them — with NO READER anywhere. Plan §11.6: "Context budget manager
187
+ * → tokenCount / contextWarning actually rendered". So a conversation was silently trimmed, the
188
+ * assistant forgot what you told it ten turns ago, and the two numbers that would have explained
189
+ * why sat in the store.
190
+ */
191
+ tokens: number;
192
+ overBudget: boolean;
193
+ }
194
+
195
+ /** Compact token count: 18400 → "18.4k". A four-digit number in a 28px header is noise. */
196
+ function tokenLabel(tokens: number): string {
197
+ return tokens >= 1000 ? `${(tokens / 1000).toFixed(1)}k` : String(tokens);
100
198
  }
101
199
 
102
- /** The chat header: history button, session title, streaming spinner, New Chat. */
200
+ /** The chat header: history button, session title, the context budget, spinner, New Chat. */
103
201
  export function renderChatHeader(opts: ChatHeaderOptions): TemplateResult {
104
202
  return html`
105
203
  <div class="ai-chat-header">
106
- <sp-action-button size="s" quiet title="Chat history" @click=${opts.onShowSessions}>
107
- <sp-icon-history slot="icon"></sp-icon-history>
108
- </sp-action-button>
204
+ ${commandButton("assistant.history", {
205
+ content: html`<sp-icon-history slot="icon"></sp-icon-history>`,
206
+ quiet: true,
207
+ })}
109
208
  <span class="ai-chat-title">${opts.title ?? "New chat"}</span>
110
209
  <span class="ai-header-spacer"></span>
210
+ ${
211
+ opts.tokens > 0
212
+ ? html`<span
213
+ class=${opts.overBudget ? "ai-tokens ai-tokens--warn" : "ai-tokens"}
214
+ title=${
215
+ opts.overBudget
216
+ ? `About ${opts.tokens.toLocaleString()} tokens — past half the model's context. ` +
217
+ `The oldest turns are dropped as this grows; start a new chat to keep them.`
218
+ : `About ${opts.tokens.toLocaleString()} tokens of the model's context in use`
219
+ }
220
+ >${tokenLabel(opts.tokens)}</span
221
+ >`
222
+ : nothing
223
+ }
111
224
  ${
112
225
  opts.streaming
113
226
  ? html`<sp-progress-circle size="s" indeterminate></sp-progress-circle>`
114
227
  : nothing
115
228
  }
116
- <sp-action-button size="s" quiet title="New chat" @click=${opts.onNewChat}>
117
- <sp-icon-add slot="icon"></sp-icon-add>
118
- </sp-action-button>
229
+ ${commandButton("assistant.newChat", {
230
+ content: html`<sp-icon-add slot="icon"></sp-icon-add>`,
231
+ quiet: true,
232
+ })}
119
233
  </div>
120
234
  `;
121
235
  }
@@ -140,25 +254,125 @@ function renderUserMessage(msg: Message): TemplateResult {
140
254
  `;
141
255
  }
142
256
 
257
+ /**
258
+ * What became of one tool call: `"pending"` while the loop has not reported, else ok/failed.
259
+ *
260
+ * @param {ToolCallRecord} tc
261
+ * @returns {"pending" | "ok" | "failed"}
262
+ */
263
+ export function toolOutcome(tc: ToolCallRecord): "pending" | "ok" | "failed" {
264
+ if (!tc.result) {
265
+ return "pending";
266
+ }
267
+ return tc.result.success ? "ok" : "failed";
268
+ }
269
+
270
+ /**
271
+ * The one line a chip says about its outcome — the tool's own words where it has any.
272
+ *
273
+ * A tool that succeeded already writes a human sentence into `summary` for the model to read; there
274
+ * is no reason the human could not have been reading it all along. A tool that failed writes
275
+ * `error`, which was surfaced only through the separate tool-message row and only after the fact.
276
+ *
277
+ * @param {ToolCallRecord} tc
278
+ * @returns {string}
279
+ */
280
+ export function toolOutcomeText(tc: ToolCallRecord): string {
281
+ const { result } = tc;
282
+ if (!result) {
283
+ return "";
284
+ }
285
+ return (result.success ? result.summary : result.error) ?? "";
286
+ }
287
+
143
288
  function renderToolChips(toolCalls: ToolCallRecord[]): TemplateResult | typeof nothing {
144
289
  if (toolCalls.length === 0) {
145
290
  return nothing;
146
291
  }
147
292
  return html`
148
293
  <div class="ai-msg-tools">
149
- ${toolCalls.map(
150
- (tc) => html`
151
- <span class="ai-tool-chip">
294
+ ${toolCalls.map((tc) => {
295
+ const outcome = toolOutcome(tc);
296
+ const text = toolOutcomeText(tc);
297
+ return html`
298
+ <span class="ai-tool-chip" data-outcome=${outcome} title=${text || formatToolLabel(tc)}>
152
299
  <sp-icon-gears size="xs"></sp-icon-gears>
153
- ${formatToolLabel(tc)}
300
+ <span class="ai-tool-chip-name">${formatToolLabel(tc)}</span>
301
+ ${
302
+ outcome === "pending"
303
+ ? nothing
304
+ : html`<span class="ai-tool-chip-outcome"
305
+ >${outcome === "ok" ? "✓" : "✗"} ${text}</span
306
+ >`
307
+ }
154
308
  </span>
155
- `,
156
- )}
309
+ `;
310
+ })}
157
311
  </div>
158
312
  `;
159
313
  }
160
314
 
161
- function renderAssistantMessage(msg: Message): TemplateResult | typeof nothing {
315
+ /**
316
+ * The turn's changed-files summary, with the two things it can honestly offer.
317
+ *
318
+ * Rendered only for a turn that changed something — "Changed 0 files" is noise, and a turn that
319
+ * only read is the common case. **Restore to here** is offered only when every recorded change went
320
+ * through a transaction: a disk write has no history behind it, so a button that claimed to restore
321
+ * one would be the same lie the model-facing caveat used to be.
322
+ */
323
+ function renderChangedFiles(
324
+ msg: Message,
325
+ onRestore?: (messageId: string) => void,
326
+ ): TemplateResult | typeof nothing {
327
+ const writes = writesForTurn(msg.id);
328
+ if (writes.length === 0) {
329
+ return nothing;
330
+ }
331
+ const summary = summarizeWrites(writes);
332
+ if (!summary) {
333
+ return nothing;
334
+ }
335
+ const restorable = writes.every((w) => !w.disk);
336
+ return html`
337
+ <details class="ai-msg-changes">
338
+ <summary>
339
+ ${summary}
340
+ ${
341
+ onRestore && restorable
342
+ ? html`<sp-action-button
343
+ size="xs"
344
+ quiet
345
+ title="Undo everything this turn changed"
346
+ @click=${(e: Event) => {
347
+ e.preventDefault();
348
+ e.stopPropagation();
349
+ onRestore(msg.id);
350
+ }}
351
+ >
352
+ Restore to here
353
+ </sp-action-button>`
354
+ : nothing
355
+ }
356
+ </summary>
357
+ <ul class="ai-msg-changes-list">
358
+ ${writes.map(
359
+ (w) => html`
360
+ <li data-disk=${String(w.disk)} data-ok=${String(w.ok)}>
361
+ <code>${w.path}</code>
362
+ <span>${w.ok ? w.tool : `failed — ${w.error ?? w.tool}`}</span>
363
+ ${w.disk ? html`<em>written to disk — undo cannot reach it</em>` : nothing}
364
+ </li>
365
+ `,
366
+ )}
367
+ </ul>
368
+ </details>
369
+ `;
370
+ }
371
+
372
+ function renderAssistantMessage(
373
+ msg: Message,
374
+ onRestore?: (messageId: string) => void,
375
+ ): TemplateResult | typeof nothing {
162
376
  const toolCalls = msg.toolCalls ?? [];
163
377
  if (!msg.content && toolCalls.length === 0) {
164
378
  return nothing;
@@ -166,6 +380,7 @@ function renderAssistantMessage(msg: Message): TemplateResult | typeof nothing {
166
380
  return html`
167
381
  <div class="ai-msg-assistant">
168
382
  ${msg.content ? renderMarkdown(msg.id, msg.content) : nothing} ${renderToolChips(toolCalls)}
383
+ ${renderChangedFiles(msg, onRestore)}
169
384
  </div>
170
385
  `;
171
386
  }
@@ -208,6 +423,8 @@ export interface MessageListOptions {
208
423
  onScroll: (e: Event) => void;
209
424
  /** Ref to the scrolling element, for stick-to-bottom maintenance. */
210
425
  listRef: (el: Element | undefined) => void;
426
+ /** Undo everything one turn changed. Offered only for turns whose changes are all transactional. */
427
+ onRestore?: ((messageId: string) => void) | undefined;
211
428
  }
212
429
 
213
430
  /** The scrollable message list — THE scroller of the chat view. */
@@ -237,7 +454,7 @@ export function renderMessageList(opts: MessageListOptions): TemplateResult {
237
454
  if (status === "streaming" && i === lastIdx) {
238
455
  return renderStreamingTail(msg);
239
456
  }
240
- return renderAssistantMessage(msg);
457
+ return renderAssistantMessage(msg, opts.onRestore);
241
458
  }
242
459
  return nothing;
243
460
  })}
@@ -251,6 +468,16 @@ export function renderMessageList(opts: MessageListOptions): TemplateResult {
251
468
  ? html`<div class="ai-msg-error-advice">${formatErrorAdvice(opts.error)}</div>`
252
469
  : nothing
253
470
  }
471
+ ${
472
+ /* `assistant.retry`, not a closure. Its `enablement` reads `ctx.ai.configured`,
473
+ so the one error this row cannot recover from — no provider connected, whose
474
+ advice line above already says to add a key — draws the button disabled with
475
+ that sentence rather than offering a send that will fail identically. */
476
+ commandButton("assistant.retry", {
477
+ className: "ai-msg-retry",
478
+ content: "Retry",
479
+ })
480
+ }
254
481
  </div>
255
482
  `
256
483
  : nothing
@@ -15,6 +15,7 @@
15
15
  */
16
16
 
17
17
  import { html, nothing } from "lit-html";
18
+ import { displayTagName } from "@jxsuite/schema/guards";
18
19
  import type { TemplateResult } from "lit-html";
19
20
  import { live } from "lit-html/directives/live.js";
20
21
  import { ref } from "lit-html/directives/ref.js";
@@ -22,10 +23,11 @@ import { getNodeAtPath } from "../../state";
22
23
  import { fetchAvailableModels } from "../../services/ai-models";
23
24
  import { getModel, setModel } from "../../services/ai-settings";
24
25
  import { activeTab } from "../../workspace/workspace";
26
+ import { primarySelection } from "../../tabs/selection";
25
27
  import { buildMessageWithContext } from "./attached-context";
26
28
  import type { ContextChip } from "./attached-context";
27
29
  import type { AiModel } from "../../services/ai-models";
28
- import type { JxMutableNode, JxPath } from "@jxsuite/schema/types";
30
+ import type { JxMutableNode } from "@jxsuite/schema/types";
29
31
 
30
32
  /** Tallest the textarea auto-grows before it scrolls internally. */
31
33
  const MAX_INPUT_HEIGHT = 120;
@@ -46,6 +48,16 @@ export interface Composer {
46
48
  render: () => TemplateResult;
47
49
  focus: () => void;
48
50
  clear: () => void;
51
+ /**
52
+ * Attach the canvas selection as a context chip, as the attach menu's second item does.
53
+ *
54
+ * `false` when nothing is selected, so a caller can say why nothing happened. This exists because
55
+ * `assistant.attachSelection` (`panels/ai-panel.ts`) must reach the SAME chip the menu builds —
56
+ * the attach convention is one delimiter and one `ContextChip` shape (`attached-context.ts`), and
57
+ * a command that assembled its own line would be a second way to say "this element", diverging
58
+ * the first time the label changes.
59
+ */
60
+ attachSelection: () => boolean;
49
61
  }
50
62
 
51
63
  /**
@@ -137,13 +149,17 @@ export function createComposer(opts: ComposerOptions): Composer {
137
149
  function contextCandidates() {
138
150
  const tab = activeTab.value;
139
151
  const documentPath = tab?.documentPath || null;
140
- const selection = (tab?.session.selection as JxPath | null) || null;
152
+ const selection = primarySelection(tab?.session.selection);
141
153
  let selectionChip: ContextChip | null = null;
142
154
  if (tab && selection) {
155
+ /* No `tagName?: string` in this cast any more. It overrode the widened type — a tag may be
156
+ a name or a choice between names — so the compiler could not see that this chip would
157
+ render `[object Object]` for a chosen one. A cast that narrows a field back to what it
158
+ used to be is a hole the type system cannot report. */
143
159
  const node = getNodeAtPath(tab.doc.document as JxMutableNode, selection) as
144
- | (JxMutableNode & { tagName?: string; textContent?: string })
160
+ | (JxMutableNode & { textContent?: string })
145
161
  | undefined;
146
- const tag = node?.tagName || "element";
162
+ const tag = displayTagName(node?.tagName) || "element";
147
163
  const text = typeof node?.textContent === "string" ? node.textContent.slice(0, 40) : "";
148
164
  selectionChip = {
149
165
  detail: `Selected element at ${JSON.stringify(selection)}: <${tag}>${text ? ` "${text}"` : ""}`,
@@ -170,6 +186,16 @@ export function createComposer(opts: ComposerOptions): Composer {
170
186
  opts.requestRender();
171
187
  }
172
188
 
189
+ /** {@link Composer.attachSelection} — the attach menu's "Selected element" item, by name. */
190
+ function attachSelection(): boolean {
191
+ const { selectionChip } = contextCandidates();
192
+ if (!selectionChip) {
193
+ return false;
194
+ }
195
+ addChip(selectionChip);
196
+ return true;
197
+ }
198
+
173
199
  function renderAttachMenu(): TemplateResult {
174
200
  const { pageChip, selectionChip } = contextCandidates();
175
201
  return html`
@@ -317,5 +343,5 @@ export function createComposer(opts: ComposerOptions): Composer {
317
343
  `;
318
344
  }
319
345
 
320
- return { clear, focus, render };
346
+ return { attachSelection, clear, focus, render };
321
347
  }
@@ -3,13 +3,21 @@
3
3
  * Sessions-view.js — the full-pane chat history list (VSCode-Copilot style).
4
4
  *
5
5
  * Renders the session rows (title, relative time, message count) with per-row delete,
6
- * a header with a New Chat action, and an empty state. Pure template functions — all
7
- * state and storage live in ai-panel / document-assistant.
6
+ * a header with a New Chat action, and an empty state. State and storage live in
7
+ * ai-panel / document-assistant; the row callbacks address ONE session each, which is
8
+ * why they stay callbacks.
9
+ *
10
+ * New Chat is the exception, and it is `assistant.newChat` — the same record the chat
11
+ * header runs ({@link commandButton}). The two headers draw the same button, so a
12
+ * second definition site is exactly how they would come to disagree about its name, its
13
+ * chord, or when it is offered.
8
14
  *
9
15
  * @license MIT
10
16
  */
11
17
 
12
18
  import { html } from "lit-html";
19
+ import { now } from "../../services/clock";
20
+ import { commandButton } from "./chat-view";
13
21
  import type { TemplateResult } from "lit-html";
14
22
  import type { SessionMeta } from "../../services/ai-session-store";
15
23
 
@@ -22,11 +30,11 @@ const DAY = 24 * HOUR;
22
30
  * date.
23
31
  *
24
32
  * @param {number} ts
25
- * @param {number} [now]
33
+ * @param {number} [at] Epoch milliseconds to measure against; defaults to the {@link now} seam.
26
34
  * @returns {string}
27
35
  */
28
- export function relativeTime(ts: number, now: number = Date.now()): string {
29
- const delta = Math.max(0, now - ts);
36
+ export function relativeTime(ts: number, at: number = now()): string {
37
+ const delta = Math.max(0, at - ts);
30
38
  if (delta < MINUTE) {
31
39
  return "just now";
32
40
  }
@@ -49,7 +57,6 @@ export interface SessionsListOptions {
49
57
  sessions: SessionMeta[];
50
58
  onOpen: (id: string) => void;
51
59
  onDelete: (id: string) => void;
52
- onNew: () => void;
53
60
  }
54
61
 
55
62
  function renderRow(s: SessionMeta, opts: SessionsListOptions): TemplateResult {
@@ -84,10 +91,10 @@ export function renderSessionsList(opts: SessionsListOptions): TemplateResult {
84
91
  <div class="ai-chat-header">
85
92
  <span class="ai-chat-title">Chats</span>
86
93
  <span class="ai-header-spacer"></span>
87
- <sp-action-button size="s" quiet title="New chat" @click=${opts.onNew}>
88
- <sp-icon-add slot="icon"></sp-icon-add>
89
- New Chat
90
- </sp-action-button>
94
+ ${commandButton("assistant.newChat", {
95
+ content: html`<sp-icon-add slot="icon"></sp-icon-add>New Chat`,
96
+ quiet: true,
97
+ })}
91
98
  </div>
92
99
  <div class="ai-sessions">
93
100
  ${