@jxsuite/studio 5.2.0 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (398) hide show
  1. package/data/css-meta.json +28 -27
  2. package/dist/chunks/{cf-account-picker-h4dab9bq.js → cf-account-picker-3f08rry6.js} +21 -18
  3. package/dist/chunks/{cf-settings-adg74qsk.js → cf-settings-m79r7p5y.js} +4 -4
  4. package/dist/chunks/{components-5y78e05d.js → components-xm826zjw.js} +3 -3
  5. package/dist/chunks/{content-source-sgg1sv6n.js → content-source-jt7rbcbc.js} +22 -22
  6. package/dist/chunks/{convert-to-repeater-mf1cxgfn.js → convert-to-repeater-vg38bq99.js} +22 -22
  7. package/dist/chunks/{editor-3qhbg51x.js → editor-ddy019sn.js} +2 -2
  8. package/dist/chunks/entry-commands-64nb2a9a.js +42 -0
  9. package/dist/chunks/{extension-sections-c6zwzbre.js → extension-sections-r73an5s6.js} +31 -30
  10. package/dist/chunks/{file-ops-vrpq1jjk.js → file-ops-3xan8mnj.js} +21 -21
  11. package/dist/chunks/{files-msaf95y6.js → files-egsb0ry8.js} +30 -29
  12. package/dist/chunks/{format-host-f64fnesp.js → format-host-mym8qnd0.js} +3 -3
  13. package/dist/chunks/{fs-events-3y6y0nrm.js → fs-events-z2gg8qjt.js} +4 -4
  14. package/dist/chunks/{git-panel-bv5e22j0.js → git-panel-a05atk6q.js} +20 -20
  15. package/dist/chunks/{grid-view-1p6hsyjx.css → grid-view-3g1h2re8.css} +956 -139
  16. package/dist/chunks/{grid-view-paj67nr5.js → grid-view-qaqer9kr.js} +24 -24
  17. package/dist/chunks/{icons-yy10wk9r.js → icons-drxq9q10.js} +8 -2
  18. package/dist/chunks/{iframe-host-dh5np8vk.js → iframe-host-psetcv20.js} +11 -11
  19. package/dist/chunks/{index-n4j7qxyr.js → index-9yjae3hz.js} +4 -4
  20. package/dist/chunks/{javascript-4abzjcy9.css → javascript-bc9z461d.css} +688 -54
  21. package/dist/chunks/{javascript-xvrfrpkb.js → javascript-hr5sg57n.js} +2 -2
  22. package/dist/chunks/{jsonMode-vvmpmk34.css → jsonMode-qqd9nm1t.css} +1120 -303
  23. package/dist/chunks/{jsonMode-tyx2kj6p.js → jsonMode-zs88v0wr.js} +4 -4
  24. package/dist/chunks/{jx-validate-j0wxk72w.js → jx-validate-epp2z3s3.js} +2 -2
  25. package/dist/chunks/layers-panel-83a16ytx.js +41 -0
  26. package/dist/chunks/{layers-1vcfdbet.js → layers-s6mc01ry.js} +3 -3
  27. package/dist/chunks/{listbox-6v2ac08j.js → listbox-b7jxnhq1.js} +19 -3
  28. package/dist/chunks/{media-picker-p49nhqhs.js → media-picker-nhn6rv4w.js} +11 -11
  29. package/dist/chunks/{menu-qht48vnc.js → menu-8j4qxjrp.js} +4 -4
  30. package/dist/chunks/{monaco-binding-sw6en4dw.js → monaco-binding-e8d9wzyk.js} +3 -3
  31. package/dist/chunks/{monaco-buffer-pykwbm92.js → monaco-buffer-jaq1t9zj.js} +2 -2
  32. package/dist/chunks/{monaco-setup-8vbyqnhr.css → monaco-setup-ey9cjcm3.css} +954 -137
  33. package/dist/chunks/{monaco-setup-8r62fj6y.js → monaco-setup-gsjdysze.js} +7 -7
  34. package/dist/chunks/{platform-ts9cg1a4.js → platform-fnchmhy7.js} +5 -3
  35. package/dist/chunks/publish-panel-ak6hxfma.js +27 -0
  36. package/dist/chunks/quick-search-c63mkt3b.js +47 -0
  37. package/dist/chunks/{references-h69xp9zz.js → references-f7bdv3z5.js} +3 -3
  38. package/dist/chunks/right-panel-t5n8wttg.js +45 -0
  39. package/dist/chunks/{split-g7nm1rgb.js → split-4cz4b6ef.js} +14 -3
  40. package/dist/chunks/{studio-sar8g54p.js → studio-01269reb.js} +8 -8
  41. package/dist/chunks/{studio-rnyf3f8e.js → studio-0a4gncx0.js} +3 -3
  42. package/dist/chunks/{studio-e1f24eff.js → studio-0d4p4wxj.js} +6778 -2993
  43. package/dist/chunks/{studio-4wbb9ct0.js → studio-1tejxvba.js} +3006 -1855
  44. package/dist/chunks/{studio-p6pmwkvh.js → studio-3nprwbd9.js} +147 -28
  45. package/dist/chunks/{studio-4c041r8y.js → studio-40z39dme.js} +3 -3
  46. package/dist/chunks/{studio-68fydsrj.js → studio-482eeazk.js} +20 -20
  47. package/dist/chunks/{studio-y4f9fdge.js → studio-4998spet.js} +10 -9
  48. package/dist/chunks/{studio-nq5yf5mp.js → studio-557bmn0x.js} +2 -2
  49. package/dist/chunks/{studio-hdc2jt3z.js → studio-5w26wza5.js} +3 -3
  50. package/dist/chunks/{studio-6nzaktvy.js → studio-6pwn9j8c.js} +6 -3
  51. package/dist/chunks/{studio-wnsyffg1.js → studio-6vg0x5fz.js} +3 -2
  52. package/dist/chunks/{studio-vn5hske5.js → studio-7d3c1m59.js} +12 -5
  53. package/dist/chunks/{studio-28vs5k2y.js → studio-9vsvz7w7.js} +7 -7
  54. package/dist/chunks/{studio-t3adrr56.js → studio-9wm0xzpf.js} +19 -19
  55. package/dist/chunks/{studio-38emantv.js → studio-a5h5j33a.js} +19 -5
  56. package/dist/chunks/{studio-ympe0n7a.js → studio-ag3z6shb.js} +10 -4
  57. package/dist/chunks/studio-aj9drheq.js +177 -0
  58. package/dist/chunks/{studio-vt0tj32j.js → studio-apv9x70c.js} +189 -130
  59. package/dist/chunks/{studio-c04nzbby.js → studio-b9q3znz8.js} +2 -2
  60. package/dist/chunks/{studio-2mdbbc76.js → studio-bh47w0cy.js} +9 -10
  61. package/dist/chunks/{studio-ez11dmdh.js → studio-dgdm6vvt.js} +264 -52
  62. package/dist/chunks/{studio-5nz00bhh.js → studio-dwdcr2e8.js} +4 -4
  63. package/dist/chunks/{studio-nqgawfks.js → studio-fbcyz234.js} +146 -139
  64. package/dist/chunks/{studio-kcgff4r0.js → studio-fgmfksz8.js} +2 -2
  65. package/dist/chunks/{studio-x72j58vw.js → studio-fqpkpq1t.js} +7 -4
  66. package/dist/chunks/{studio-15f3kxqe.js → studio-fy320dfc.js} +62 -68
  67. package/dist/chunks/{studio-zjjde71a.js → studio-gjj4bbfx.js} +2 -2
  68. package/dist/chunks/{studio-66arbpc2.js → studio-jfhxy8h3.js} +16 -15
  69. package/dist/chunks/{studio-d0300636.js → studio-ks2zzth2.js} +17 -2
  70. package/dist/chunks/{studio-c07s5ckd.js → studio-kwewmhhq.js} +4 -4
  71. package/dist/chunks/{studio-01jeck2y.js → studio-m58xn8at.js} +26 -25
  72. package/dist/chunks/{studio-mpzwze9x.js → studio-mfkh1q8d.js} +17 -17
  73. package/dist/chunks/{studio-etvx9s2g.js → studio-mrh0a92f.js} +17 -17
  74. package/dist/chunks/{studio-qa7fzxan.js → studio-mt79tn56.js} +3 -3
  75. package/dist/chunks/{studio-hhskq2mr.js → studio-ne593z1j.js} +39 -32
  76. package/dist/chunks/{studio-h4nvnjvz.js → studio-ptfcxkz5.js} +418 -232
  77. package/dist/chunks/{studio-4xmjcrg3.js → studio-qxye31h7.js} +7 -7
  78. package/dist/chunks/{studio-a1pswps3.js → studio-rav5bpmp.js} +81 -59
  79. package/dist/chunks/{studio-ncn9e189.js → studio-rwmmbrth.js} +6 -6
  80. package/dist/chunks/{studio-en82c639.js → studio-sceg8ws0.js} +8 -8
  81. package/dist/chunks/{studio-wjpgxywa.js → studio-sjas5b3a.js} +19 -13
  82. package/dist/chunks/{studio-4r14yzyy.js → studio-vd67b66d.js} +3 -3
  83. package/dist/chunks/{studio-14d9tq4d.js → studio-w475cbf9.js} +10 -10
  84. package/dist/chunks/{studio-ctm7bhx5.js → studio-w7gpcvfj.js} +9 -9
  85. package/dist/chunks/{studio-9j6sfeba.js → studio-xrbfdxgk.js} +7 -7
  86. package/dist/chunks/{studio-x10fp4tp.js → studio-y49b6mjv.js} +35 -35
  87. package/dist/chunks/{studio-5t7ena5z.js → studio-ygvr9e4q.js} +10 -10
  88. package/dist/chunks/{studio-z2smx5ce.js → studio-yxmmjy2a.js} +9 -6
  89. package/dist/chunks/{tsMode-jy4m86rv.js → tsMode-ay4c78ne.js} +4 -4
  90. package/dist/chunks/{tsMode-hnxxb73h.css → tsMode-c72wyz27.css} +954 -137
  91. package/dist/chunks/{workspace-bnw54cha.js → workspace-b9k2frks.js} +5 -5
  92. package/dist/chunks/{ws-client-ny4zc51r.js → ws-client-erze4czt.js} +356 -20
  93. package/dist/codicon.ttf +0 -0
  94. package/dist/iframe-entry.js +20 -18
  95. package/dist/studio.css +956 -139
  96. package/dist/studio.js +1511 -851
  97. package/dist/workers/editor.worker.js +196 -107
  98. package/dist/workers/json.worker.js +196 -107
  99. package/dist/workers/ts.worker.js +196 -107
  100. package/package.json +18 -17
  101. package/src/about/about-modal.ts +4 -3
  102. package/src/browse/library-commands.ts +15 -13
  103. package/src/browse/library-pane.ts +2 -2
  104. package/src/canvas/canvas-render.ts +97 -83
  105. package/src/canvas/canvas-surface.ts +2 -2
  106. package/src/canvas/canvas-utils.ts +42 -32
  107. package/src/canvas/diff-toolbar.ts +21 -2
  108. package/src/canvas/diff-view.ts +30 -6
  109. package/src/canvas/iframe-entry.ts +2 -2
  110. package/src/canvas/iframe-host.ts +5 -5
  111. package/src/canvas/iframe-interaction.ts +7 -6
  112. package/src/canvas/iframe-overlay.ts +1 -1
  113. package/src/canvas/iframe-protocol.ts +6 -5
  114. package/src/canvas/iframe-render.ts +3 -3
  115. package/src/canvas/popover-state.ts +1 -1
  116. package/src/canvas/stage-scrollbar.ts +112 -0
  117. package/src/canvas/surface-registry.ts +9 -2
  118. package/src/collab/collab-commands.ts +8 -6
  119. package/src/collab/collab-session.ts +4 -4
  120. package/src/collab/collab-state.ts +4 -4
  121. package/src/collab/presence-chips.ts +3 -3
  122. package/src/commands/app-commands.ts +7 -7
  123. package/src/commands/budget.ts +17 -16
  124. package/src/commands/command-args.ts +10 -9
  125. package/src/commands/context.ts +20 -10
  126. package/src/commands/defaults.ts +30 -29
  127. package/src/commands/levels.ts +8 -6
  128. package/src/commands/live-context.ts +12 -10
  129. package/src/commands/reference.ts +5 -5
  130. package/src/commands/registry.ts +2 -2
  131. package/src/content/draft-state.ts +9 -8
  132. package/src/content/entry-commands.ts +16 -14
  133. package/src/content/entry-editor.ts +2 -2
  134. package/src/content/entry-fields.ts +1 -1
  135. package/src/editor/context-menu.ts +4 -4
  136. package/src/editor/convert-to-component.ts +6 -5
  137. package/src/editor/inline-edit.ts +3 -3
  138. package/src/editor/shortcuts.ts +88 -20
  139. package/src/editor/slash-menu.ts +11 -10
  140. package/src/files/elements.ts +4 -5
  141. package/src/files/file-ops.ts +1 -1
  142. package/src/files/files.ts +21 -17
  143. package/src/files/serialize-document.ts +1 -1
  144. package/src/format/convert-file.ts +5 -5
  145. package/src/github/github-auth-status.ts +107 -0
  146. package/src/github/github-auth.ts +18 -82
  147. package/src/github/github-publish.ts +7 -6
  148. package/src/grid/cell-editors.ts +8 -7
  149. package/src/grid/grid-layout.ts +1 -1
  150. package/src/grid/grid-open.ts +6 -5
  151. package/src/grid/grid-panel.ts +13 -12
  152. package/src/grid/redirects-grid.ts +14 -11
  153. package/src/grid/redirects.ts +10 -5
  154. package/src/grid/sources/content-source.ts +10 -9
  155. package/src/hosting/document.ts +28 -1
  156. package/src/hosting/stage.ts +15 -4
  157. package/src/i18n/i18n-commands.ts +9 -7
  158. package/src/new-project/location-fields.ts +16 -3
  159. package/src/new-project/new-project-modal.ts +23 -7
  160. package/src/page-params.ts +6 -6
  161. package/src/panels/activity-panel.ts +11 -10
  162. package/src/panels/ai-chat/chat-view.ts +27 -14
  163. package/src/panels/ai-chat/composer.ts +10 -3
  164. package/src/panels/ai-panel.ts +87 -29
  165. package/src/panels/block-action-bar.ts +59 -33
  166. package/src/panels/bottom-dock.ts +22 -22
  167. package/src/panels/chat-panel.ts +40 -5
  168. package/src/panels/data-explorer.ts +9 -9
  169. package/src/panels/data-grid.ts +1 -1
  170. package/src/panels/editors.ts +8 -8
  171. package/src/panels/elements-panel.ts +7 -8
  172. package/src/panels/empty-state.ts +1 -1
  173. package/src/panels/events-panel.ts +13 -12
  174. package/src/panels/formula-workspace.ts +8 -5
  175. package/src/panels/frontmatter-fields.ts +4 -1
  176. package/src/panels/frontmatter-panel.ts +18 -18
  177. package/src/panels/git-panel.ts +19 -13
  178. package/src/panels/head-panel.ts +9 -10
  179. package/src/panels/i18n-panel.ts +6 -4
  180. package/src/panels/imports-panel.ts +1 -1
  181. package/src/panels/jump-bar.ts +20 -18
  182. package/src/panels/layers-panel.ts +19 -17
  183. package/src/panels/left-panel.ts +3 -3
  184. package/src/panels/navigator-panels.ts +8 -8
  185. package/src/panels/overlays.ts +1 -1
  186. package/src/panels/pane-context.ts +56 -28
  187. package/src/panels/pane-grid.ts +9 -8
  188. package/src/panels/panel-registry.ts +47 -19
  189. package/src/panels/problems-panel.ts +11 -12
  190. package/src/panels/properties-panel.ts +21 -20
  191. package/src/panels/provenance.ts +3 -3
  192. package/src/panels/quick-search.ts +15 -11
  193. package/src/panels/right-panel.ts +4 -3
  194. package/src/panels/seo-modal.ts +5 -4
  195. package/src/panels/settings-menu.ts +9 -9
  196. package/src/panels/settings-pane.ts +3 -3
  197. package/src/panels/signals-panel.ts +16 -9
  198. package/src/panels/statement-editor.ts +12 -9
  199. package/src/panels/style-panel.ts +45 -41
  200. package/src/panels/stylebook-panel.ts +0 -1
  201. package/src/panels/tab-strip.ts +26 -23
  202. package/src/platform-errors.ts +2 -2
  203. package/src/platform.ts +44 -4
  204. package/src/platforms/cloud.ts +11 -1
  205. package/src/platforms/default-platform.ts +141 -4
  206. package/src/platforms/devserver.ts +23 -3
  207. package/src/publish/deploy-checklist.ts +11 -10
  208. package/src/publish/publish-commands.ts +9 -8
  209. package/src/services/a11y-report.ts +1 -1
  210. package/src/services/ai-ask.ts +39 -16
  211. package/src/services/ai-command-tools.ts +28 -14
  212. package/src/services/ai-import-tools.ts +43 -29
  213. package/src/services/ai-project-tools.ts +20 -38
  214. package/src/services/ai-settings.ts +55 -2
  215. package/src/services/ai-tools.ts +69 -58
  216. package/src/services/ai-write-report.ts +4 -4
  217. package/src/services/ai-writes.ts +74 -55
  218. package/src/services/automation.ts +12 -12
  219. package/src/services/clock.ts +2 -1
  220. package/src/services/connection.ts +2 -2
  221. package/src/services/context-manager.ts +69 -6
  222. package/src/services/directory-picker.ts +93 -27
  223. package/src/services/document-assistant.ts +86 -17
  224. package/src/services/gated-registry.ts +4 -3
  225. package/src/services/idle.ts +5 -4
  226. package/src/services/live-surfaces.ts +2 -1
  227. package/src/services/module-preloads.ts +67 -0
  228. package/src/services/notify.ts +9 -7
  229. package/src/services/popover-report.ts +3 -3
  230. package/src/services/preview-eval.ts +1 -1
  231. package/src/services/project-adoption.ts +34 -1
  232. package/src/services/project-path.ts +53 -0
  233. package/src/services/settings/kernel.ts +13 -2
  234. package/src/services/tool-executor.ts +135 -30
  235. package/src/services/tool-outcomes.ts +143 -0
  236. package/src/settings/contexts-section.ts +8 -8
  237. package/src/settings/contributed-section.ts +6 -6
  238. package/src/settings/css-vars-editor.ts +6 -5
  239. package/src/settings/extension-commands.ts +19 -18
  240. package/src/settings/extension-sections.ts +7 -7
  241. package/src/settings/extensions-section.ts +3 -3
  242. package/src/settings/general-settings.ts +21 -18
  243. package/src/settings/preferences-accounts.ts +3 -3
  244. package/src/settings/preferences-dialog.ts +9 -8
  245. package/src/settings/preferences-sections.ts +1 -1
  246. package/src/settings/project-sections.ts +2 -2
  247. package/src/settings/section-registry.ts +7 -6
  248. package/src/settings/settings-document.ts +41 -43
  249. package/src/shell/tree.ts +29 -0
  250. package/src/shell.ts +63 -55
  251. package/src/site-context.ts +1 -1
  252. package/src/store.ts +2 -2
  253. package/src/studio.ts +52 -28
  254. package/src/style/live-preview.ts +8 -8
  255. package/src/style/project-styles.ts +8 -9
  256. package/src/style/token-ref.ts +2 -2
  257. package/src/surfaces/about.json +6 -2
  258. package/src/surfaces/add-repo.json +2 -2
  259. package/src/surfaces/add-repo.ts +2 -2
  260. package/src/surfaces/ai-chat.json +14 -6
  261. package/src/surfaces/ai-chat.ts +4 -2
  262. package/src/surfaces/ai-credentials-form.json +53 -49
  263. package/src/surfaces/ai-credentials-form.ts +18 -4
  264. package/src/surfaces/ai-managed-connect.json +4 -4
  265. package/src/surfaces/block-action-bar.json +4 -4
  266. package/src/surfaces/block-action-bar.ts +3 -3
  267. package/src/surfaces/boot-failure.json +228 -0
  268. package/src/surfaces/boot-failure.ts +156 -0
  269. package/src/surfaces/bottom-dock.json +1 -1
  270. package/src/surfaces/canvas-stage.json +9 -40
  271. package/src/surfaces/canvas-stage.ts +9 -9
  272. package/src/surfaces/cf-account-picker.json +11 -8
  273. package/src/surfaces/commandbar.ts +11 -10
  274. package/src/surfaces/convert-repeater.ts +3 -2
  275. package/src/surfaces/dialog.json +7 -1
  276. package/src/surfaces/dialog.ts +2 -2
  277. package/src/surfaces/doc-header.json +6 -13
  278. package/src/surfaces/doc-header.ts +4 -5
  279. package/src/surfaces/entry-editor.json +1 -1
  280. package/src/surfaces/entry-editor.ts +2 -1
  281. package/src/surfaces/expression-editor.json +5 -5
  282. package/src/surfaces/files-panel.json +4 -3
  283. package/src/surfaces/files-panel.ts +1 -1
  284. package/src/surfaces/formula-palette.json +10 -9
  285. package/src/surfaces/formula-palette.ts +11 -7
  286. package/src/surfaces/git-panel.json +167 -109
  287. package/src/surfaces/git-panel.ts +2 -2
  288. package/src/surfaces/github-auth.json +2 -1
  289. package/src/surfaces/github-publish.ts +3 -3
  290. package/src/surfaces/grid-open.json +1 -1
  291. package/src/surfaces/grid-panel.json +2 -2
  292. package/src/surfaces/grid-views.json +1 -1
  293. package/src/surfaces/inspector-dock.json +2 -2
  294. package/src/surfaces/jump-bar.json +1 -1
  295. package/src/surfaces/jump-bar.ts +1 -1
  296. package/src/surfaces/library-pane.ts +2 -1
  297. package/src/surfaces/logic-panel.json +7 -7
  298. package/src/surfaces/logic-panel.ts +6 -4
  299. package/src/surfaces/logic-workspace.json +2 -2
  300. package/src/surfaces/logic-workspace.ts +2 -2
  301. package/src/surfaces/media-browser.ts +3 -3
  302. package/src/surfaces/media-pane.ts +2 -1
  303. package/src/surfaces/menu.ts +5 -5
  304. package/src/surfaces/new-project.json +1 -1
  305. package/src/surfaces/palette.json +8 -7
  306. package/src/surfaces/pane-context.json +86 -57
  307. package/src/surfaces/pane-context.ts +2 -1
  308. package/src/surfaces/pane-grid.json +3 -3
  309. package/src/surfaces/panel-activity.json +2 -1
  310. package/src/surfaces/panel-deploy-checklist.json +1 -1
  311. package/src/surfaces/panel-elements.ts +2 -1
  312. package/src/surfaces/panel-i18n.ts +2 -1
  313. package/src/surfaces/panel-outline.json +1 -1
  314. package/src/surfaces/panel-outline.ts +4 -3
  315. package/src/surfaces/panel-page.json +2 -2
  316. package/src/surfaces/panel-page.ts +4 -1
  317. package/src/surfaces/panel-problems.json +4 -3
  318. package/src/surfaces/panel-problems.ts +2 -2
  319. package/src/surfaces/panel-signals.json +23 -8
  320. package/src/surfaces/panel-signals.ts +5 -1
  321. package/src/surfaces/panel-stylebook-layers.json +1 -1
  322. package/src/surfaces/panel-stylebook-layers.ts +2 -2
  323. package/src/surfaces/preferences.json +21 -8
  324. package/src/surfaces/preferences.ts +9 -1
  325. package/src/surfaces/properties-panel.json +4 -4
  326. package/src/surfaces/properties-panel.ts +2 -2
  327. package/src/surfaces/publish.json +1 -1
  328. package/src/surfaces/publish.ts +2 -1
  329. package/src/surfaces/push-plan.json +4 -3
  330. package/src/surfaces/rail.json +2 -2
  331. package/src/surfaces/rail.ts +10 -4
  332. package/src/surfaces/reference-field.json +1 -1
  333. package/src/surfaces/schema-form.json +2 -2
  334. package/src/surfaces/schema-form.ts +1 -1
  335. package/src/surfaces/seo.json +1 -1
  336. package/src/surfaces/seo.ts +2 -2
  337. package/src/surfaces/settings-contributed.ts +2 -1
  338. package/src/surfaces/settings-css-vars.json +4 -4
  339. package/src/surfaces/settings-deploy.json +4 -4
  340. package/src/surfaces/settings-deploy.ts +2 -1
  341. package/src/surfaces/settings-extensions.json +6 -6
  342. package/src/surfaces/settings-extensions.ts +6 -2
  343. package/src/surfaces/settings-locales.json +4 -4
  344. package/src/surfaces/settings-overview.ts +2 -1
  345. package/src/surfaces/settings-pane.ts +3 -2
  346. package/src/surfaces/settings-rawjson.ts +1 -1
  347. package/src/surfaces/slash-menu.ts +4 -4
  348. package/src/surfaces/statements.json +3 -3
  349. package/src/surfaces/statusbar.ts +23 -23
  350. package/src/surfaces/style-panel.json +9 -5
  351. package/src/surfaces/style-panel.ts +3 -3
  352. package/src/surfaces/tab-strip.json +2 -2
  353. package/src/surfaces/target-line.json +1 -1
  354. package/src/surfaces/target-line.ts +12 -10
  355. package/src/surfaces/welcome.json +7 -2
  356. package/src/tabs/doc-op-apply.ts +5 -5
  357. package/src/tabs/patch-ops.ts +3 -3
  358. package/src/tabs/project-config.ts +10 -10
  359. package/src/tabs/selection.ts +1 -1
  360. package/src/tabs/tab.ts +13 -14
  361. package/src/tabs/transact.ts +18 -24
  362. package/src/types.ts +27 -3
  363. package/src/ui/ai-credentials-form.ts +74 -16
  364. package/src/ui/ai-managed-connect.ts +68 -19
  365. package/src/ui/color-selector.ts +1 -1
  366. package/src/ui/expression-editor.ts +3 -3
  367. package/src/ui/form-controls.ts +6 -5
  368. package/src/ui/formula-catalog.ts +4 -4
  369. package/src/ui/formula-chips.ts +2 -2
  370. package/src/ui/layers.ts +20 -18
  371. package/src/ui/panel-resize.ts +8 -28
  372. package/src/ui/popover-surface.ts +2 -2
  373. package/src/ui/progress-modal.ts +3 -3
  374. package/src/ui/regions.ts +14 -9
  375. package/src/ui/schema-form.ts +10 -9
  376. package/src/ui/timing.ts +9 -7
  377. package/src/ui/value-source.ts +4 -4
  378. package/src/ui/virtual-window.ts +58 -5
  379. package/src/utils/base64.ts +20 -1
  380. package/src/utils/canvas-media.ts +1 -1
  381. package/src/utils/studio-utils.ts +0 -31
  382. package/src/view.ts +4 -3
  383. package/src/workspace/pane-derive.ts +14 -13
  384. package/src/workspace/session.ts +7 -6
  385. package/src/workspace/workspace.ts +49 -39
  386. package/styles/canvas.css +1 -27
  387. package/styles/inspector.css +4 -3
  388. package/styles/overlays.css +1 -1
  389. package/styles/tokens.css +17 -1
  390. package/styles/tokens.json +6 -1
  391. package/dist/chunks/entry-commands-w9qsxa6q.js +0 -42
  392. package/dist/chunks/layers-panel-fksbdg4q.js +0 -40
  393. package/dist/chunks/publish-panel-vz23wwmg.js +0 -27
  394. package/dist/chunks/quick-search-e9rp74m8.js +0 -46
  395. package/dist/chunks/right-panel-j97t2yvy.js +0 -45
  396. package/dist/chunks/studio-727xh0e3.js +0 -76
  397. package/src/files/json-layout.ts +0 -474
  398. package/src/services/ai-turn-signal.ts +0 -70
package/src/studio.ts CHANGED
@@ -12,6 +12,7 @@
12
12
  * registration) on first use by source mode, the function editor, or the formula workspace.
13
13
  */
14
14
  import { errorMessage } from "@jxsuite/schema/parse";
15
+ import { installModulePreloadBatcher } from "./services/module-preloads";
15
16
  import { getPanel } from "./panels/panel-registry";
16
17
 
17
18
  import {
@@ -54,7 +55,7 @@ import {
54
55
 
55
56
  import { isEditing } from "./editor/inline-edit";
56
57
  import { applyTransform, registerCanvasViewCommands } from "./canvas/canvas-utils";
57
- import { diffCommands } from "./canvas/diff-toolbar";
58
+ import { diffCommands, setDiffChromeRepaint } from "./canvas/diff-toolbar";
58
59
  import type { CanvasSurface } from "./canvas/canvas-surface";
59
60
  import {
60
61
  initCanvasRender,
@@ -106,8 +107,8 @@ import {
106
107
  setDocumentSavedListener,
107
108
  } from "./files/file-ops";
108
109
  import { serializeDocument } from "./files/serialize-document";
109
- import { parseJsonDocument } from "./files/json-layout";
110
- import type { JsonLayout } from "./files/json-layout";
110
+ import { parseJsonDocument } from "@jxsuite/schema/json-layout";
111
+ import type { JsonLayout } from "@jxsuite/schema/json-layout";
111
112
  import {
112
113
  formatForPath,
113
114
  loadFormats,
@@ -153,7 +154,7 @@ import { seedPublishConnected } from "./publish/publish-panel";
153
154
 
154
155
  import { getPlatform, hasPlatform, registerPlatform } from "./platform";
155
156
  import { parseMediaEntries } from "./utils/canvas-media";
156
- import { resolveDefaultPlatform } from "./platforms/default-platform";
157
+ import { PlatformUnavailableError, resolveDefaultPlatform } from "./platforms/default-platform";
157
158
  import { mountResizeEdges } from "./resize-edges";
158
159
  import {
159
160
  defBadgeLabel,
@@ -265,7 +266,12 @@ import type { GitDiffState } from "./types";
265
266
  import type { Tab } from "./tabs/tab";
266
267
  import type { JxDocument, JxMutableNode, ProjectConfig } from "@jxsuite/schema/types";
267
268
  import { setBundleBase } from "./services/bundle-base";
268
- import { mountShellTree } from "./shell/tree";
269
+ import { mountBootFailureTree, mountShellTree } from "./shell/tree";
270
+
271
+ // The bundler's `<link rel=modulepreload>` batching is installed on the module scope: before the
272
+ // First lazy `import()` of any surface that runs, so the palette's first open does not pay a
273
+ // Document recalculation per chunk (see the module's header for the trace that asked for this).
274
+ installModulePreloadBatcher();
269
275
 
270
276
  /**
271
277
  * Anchor every shipped-asset URL to THIS module's directory.
@@ -382,9 +388,9 @@ function setCanvasMode(tab: Tab | null, mode: string) {
382
388
  *
383
389
  * Four deliberate properties, three of them new and each with a failure behind it:
384
390
  *
385
- * 1. **To the side.** §8.2 has promised this since P3 and it never shipped — the chain ran
386
- * `openFileInTab` → `openTab` → `activePane()`, so "open the layout that wraps this page" opened
387
- * it ON TOP of the page it was teaching about.
391
+ * 1. **To the side.** §14.2 says so, the shell redesign had promised it since its chrome phase, and it
392
+ * never shipped — the chain ran `openFileInTab` → `openTab` → `activePane()`, so "open the
393
+ * layout that wraps this page" opened it ON TOP of the page it was teaching about.
388
394
  * 2. **The focus FOLLOWS.** The open is a gesture, not a read: the author asked to edit the component,
389
395
  * so the pane it lands in takes the keyboard. The failure it replaces is the opposite one — an
390
396
  * assistant pane that takes the keyboard means the author's next keystroke edits the definition
@@ -447,8 +453,24 @@ initCssData(webdata);
447
453
  // Register the default platform adapter (PAL) if none was pre-registered (desktop registers its own
448
454
  // On window.__jxPlatform). resolveDefaultPlatform picks cloud when the shell signalled it, creating
449
455
  // The cloud adapter inside THIS bundle so collab shares studio's single yjs, else the dev server.
456
+ //
457
+ // Unless a launcher was declared or announced and registered nothing: then there is no adapter to
458
+ // Pick, and resolveDefaultPlatform throws rather than hand a desktop window the dev-server adapter
459
+ // (what 5.0.0-5.1.3 did — a frame that drew, and a first click that said "Failed to fetch"). The
460
+ // Boot-failure screen is drawn INSTEAD of the frame, and the error is rethrown because ESM has no
461
+ // Top-level return: stopping the module is the only way to keep every line below from running
462
+ // Against no platform, and the uncaught error is the console's copy of what the screen says.
463
+ // Nothing above this line may call getPlatform(), or it would throw first with a message about
464
+ // Registration order instead of this one.
450
465
  if (!hasPlatform()) {
451
- registerPlatform(resolveDefaultPlatform());
466
+ try {
467
+ registerPlatform(resolveDefaultPlatform());
468
+ } catch (error) {
469
+ if (error instanceof PlatformUnavailableError) {
470
+ await mountBootFailureTree(error.refusal);
471
+ }
472
+ throw error;
473
+ }
452
474
  }
453
475
 
454
476
  mountResizeEdges();
@@ -504,6 +526,9 @@ paneContext.mount(primaryCell?.chrome ?? document.createElement("div"), {
504
526
  parseMediaEntries,
505
527
  setCanvasMode,
506
528
  });
529
+ /* The diff's Visual/Code switch decides whether the zoom pod is drawn, and the half it writes is
530
+ not reactive — so the switch repaints the chrome as well as the stage. See `setDiffChromeRepaint`. */
531
+ setDiffChromeRepaint(paneContext.render);
507
532
 
508
533
  overlaysPanel.mount({
509
534
  isEditing,
@@ -605,7 +630,7 @@ export function hasUnsavedTabs(): boolean {
605
630
  }
606
631
 
607
632
  window.addEventListener("beforeunload", (e: BeforeUnloadEvent) => {
608
- // The session, so a relaunch reopens what was open (§4.4). Before the prompt, not after: the
633
+ // The session, so a relaunch reopens what was open (§14.8). Before the prompt, not after: the
609
634
  // Author may cancel the close, and the record is the same either way.
610
635
  flushSession();
611
636
  if (hasUnsavedTabs()) {
@@ -661,11 +686,10 @@ initWelcome({
661
686
  }
662
687
  },
663
688
  cloneRepository: () => cloneRepository({ openRecentProject }),
664
- openNewProject: async (options) => {
665
- const result = await openNewProjectModal(options);
666
- if (result) {
667
- void openRecentProject(result.root);
668
- }
689
+ // The wizard adopts what it creates itself now (new-project-modal.ts's finishCreated) — there is
690
+ // Nothing left for the welcome screen to do with the result.
691
+ openNewProject: (options) => {
692
+ void openNewProjectModal(options);
669
693
  },
670
694
  openProject: () => openProject(),
671
695
  openRecentProject: (root: string) => openRecentProject(root),
@@ -886,7 +910,7 @@ registerRenderer("chatPanel", () => chatPanelMod.render());
886
910
  registerRenderer("overlays", () => overlaysPanel.render());
887
911
  renderStatusbar();
888
912
  mountStatusbar();
889
- // ⑥ The jump bar, in the pane's own grid cell above the context bar. It renders the whole address
913
+ // The jump bar, in the pane's own grid cell above the context bar. It renders the whole address
890
914
  // — project › file › node › node — and it is the only breadcrumb in the shell: the pane context
891
915
  // Bar drew a second one, and it named a sub-document stack nothing could push onto.
892
916
  mountJumpBar(primaryCell?.jump ?? document.createElement("div"));
@@ -1050,7 +1074,7 @@ if (_projectParam) {
1050
1074
  *
1051
1075
  * A named document is an instruction and wins, whether it came from `?file=` or from a
1052
1076
  * `?project=` that pointed INTO the project. A bare `?project=<dir>` is "open this
1053
- * project", and what that means is the documents it was last left with (§4.4) — the
1077
+ * project", and what that means is the documents it was last left with (§14.8) — the
1054
1078
  * `project.json → home page` redirect below is the answer this replaces.
1055
1079
  *
1056
1080
  * This branch opens inline and never went near `openHomePage`, so it was the one door of
@@ -1124,7 +1148,7 @@ if (_projectParam) {
1124
1148
  render();
1125
1149
  // Opening a file is stated by the tab strip and the status bar's DOCUMENT field.
1126
1150
  }
1127
- /* This window may now write its session (§4.4). The `?file=` branch above opens inline and
1151
+ /* This window may now write its session (§14.8). The `?file=` branch above opens inline and
1128
1152
  never reaches `openLastSessionOrHome`, which is the other place that says this — so
1129
1153
  without it a window opened at a named file would restore other windows' sessions and
1130
1154
  never record its own. */
@@ -1471,11 +1495,11 @@ commandRegistry.registerAll(derivationCommands(derivationDeps));
1471
1495
  /*
1472
1496
  * The rest of the app's contribution points, each defined beside the state it writes.
1473
1497
  *
1474
- * This block is the bootstrap's whole share of plan §13's registry work: every record below lives
1475
- * in the module that implements it, and this is the ONE place that composes them into the registry
1476
- * `__jxAutomation.run` projects. Nothing here decides what a command is called, when it is
1477
- * available or what it does — that would be the second definition site the design exists to
1478
- * prevent (plan §2, principle 1).
1498
+ * This block is the bootstrap's whole share of the registry work (§13, §13.5): every record below
1499
+ * lives in the module that implements it, and this is the ONE place that composes them into the
1500
+ * registry `__jxAutomation.run` projects. Nothing here decides what a command is called, when it is
1501
+ * available or what it does — that would be the second definition site the design exists to prevent
1502
+ * (§13).
1479
1503
  */
1480
1504
  registerShellViewCommands(commandRegistry, {
1481
1505
  // The registry's own answer, so a gated-off panel cannot be persisted as a showing tab.
@@ -1562,11 +1586,11 @@ registerGridViewCommands(commandRegistry);
1562
1586
  registerRedirectsCommands(commandRegistry);
1563
1587
  registerAboutCommands(commandRegistry);
1564
1588
  registerCollabCommands(commandRegistry);
1565
- /* The `Assistant:` family (§11.1) — Focus Composer, New Chat, Chat History, Attach Selection, Retry
1566
- and Stop. Every one existed as a button in the chat view and as nothing else, so the category held
1567
- zero records and none of them was in the palette, bindable, or reachable by name. The chat header
1568
- and the error row render these ids through the registry now, which is what makes this line the
1569
- definition site rather than a second copy. */
1589
+ /* The `Assistant:` family (`ai.md` §3.0) — Focus Composer, New Chat, Chat History, Attach
1590
+ Selection, Retry and Stop. Every one existed as a button in the chat view and as nothing else, so
1591
+ the category held zero records and none of them was in the palette, bindable, or reachable by
1592
+ name. The chat header and the error row render these ids through the registry now, which is what
1593
+ makes this line the definition site rather than a second copy. */
1570
1594
  commandRegistry.registerAll(assistantCommands());
1571
1595
  /*
1572
1596
  * The structural selection verbs — Move Up/Down/In/Out, Convert to Component, Edit Component.
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * Pushing a project-style edit to every live canvas — one definition site.
3
3
  *
4
- * Plan §9.4's promise is "tune a design token and watch the page change", and until now that was
5
- * true of exactly one kind of canvas. `postSiteStyleToLiveHosts` skips stylebook hosts by
6
- * construction ({@link file://../canvas/iframe-host.ts}: `host.ready && !host.stylebook`), because
7
- * a specimen canvas does not render the site sheet — it renders a GENERATED document whose root
8
- * carries the transposed effective style, and its live channel is `styleUpdate`, not
9
- * `siteStyleUpdate`. So a token edited in the token editor reached every page canvas immediately
10
- * and left the Project Styles canvas — the one the plan puts _beside_ the editor — showing the old
11
- * palette until something forced a full re-render.
4
+ * §7.1's promise is that tuning a token shows the page changing, and until now that was true of
5
+ * exactly one kind of canvas. `postSiteStyleToLiveHosts` skips stylebook hosts by construction
6
+ * ({@link file://../canvas/iframe-host.ts}: `host.ready && !host.stylebook`), because a specimen
7
+ * canvas does not render the site sheet — it renders a GENERATED document whose root carries the
8
+ * transposed effective style, and its live channel is `styleUpdate`, not `siteStyleUpdate`. So a
9
+ * token edited in the token editor reached every page canvas immediately and left the Project
10
+ * Styles canvas — the one §7.1 puts _beside_ the editor — showing the old palette until something
11
+ * forced a full re-render.
12
12
  *
13
13
  * Two hosts, two messages, one event. Every surface that writes `projectConfig.style` calls this
14
14
  * instead of picking one of the two posts, which is the only arrangement in which the pair cannot
@@ -3,13 +3,12 @@
3
3
  *
4
4
  * ## The name and the wire value are different things
5
5
  *
6
- * The surface a user reaches is called **Project Styles** (plan §9.4). The canvas view it opens is
7
- * the string `"stylebook"`, a member of `CANVAS_MODES` in
8
- * {@link file://../canvas/iframe-protocol.ts} and therefore half of a `ParentToIframe` union: the
9
- * studio bundle and `dist/iframe-entry.js` agree on it, and changing it means rebuilding both in
10
- * lockstep. So it is not a name and may never be renamed to follow one.
11
- * {@link PROJECT_STYLES_TITLE} is the name; {@link PROJECT_STYLES_VIEW} is the wire value; a
12
- * surface that needs one must not reach for the other.
6
+ * The surface a user reaches is called **Project Styles** (§7.1). The canvas view it opens is the
7
+ * string `"stylebook"`, a member of `CANVAS_MODES` in {@link file://../canvas/iframe-protocol.ts}
8
+ * and therefore half of a `ParentToIframe` union: the studio bundle and `dist/iframe-entry.js`
9
+ * agree on it, and changing it means rebuilding both in lockstep. So it is not a name and may never
10
+ * be renamed to follow one. {@link PROJECT_STYLES_TITLE} is the name; {@link PROJECT_STYLES_VIEW}
11
+ * is the wire value; a surface that needs one must not reach for the other.
13
12
  *
14
13
  * ## One vocabulary for the `@` blocks
15
14
  *
@@ -19,7 +18,7 @@
19
18
  * {@link TokenContext} list so a surface cannot grow a second override path for the second kind (a
20
19
  * per-scheme writer and a per-media writer that disagreed about pruning an emptied block is exactly
21
20
  * what this replaces). Contexts are **defined** in Project Settings › Contexts and only selected or
22
- * overridden here — §2 principle 5.
21
+ * overridden here — §6.2.
23
22
  *
24
23
  * On-disk format is unchanged by anything in this module: it reads and writes the same
25
24
  * `style["--token"]` and `style["@--ctx"]["--token"]` shapes the compiler already consumes.
@@ -55,7 +54,7 @@ export interface TokenGroup {
55
54
 
56
55
  /**
57
56
  * The groups, in order. `other` is rendered only when something lands in it — a bucket named
58
- * "Other" over an empty list teaches nothing (§2 principle 6).
57
+ * "Other" over an empty list teaches nothing (`studio-ui-guidelines.md` §11.1).
59
58
  *
60
59
  * A group is a name and a prefix and nothing else. The example values an add row hints with are a
61
60
  * property of the FORM, not of the model, and they live beside the form that shows them.
@@ -5,8 +5,8 @@
5
5
  * text says so only to a reader who already knows CSS: `var(--color-brand)` is eleven characters of
6
6
  * syntax around a name, and the thing the author actually wants to know — _which_ token, and what
7
7
  * it currently resolves to — is the part that is not shown. So a bound value renders as a **chip**
8
- * (plan §9.4), in the same vocabulary as the provenance chips shipped in P5: the token's friendly
9
- * name, and for a colour, its resolved swatch.
8
+ * in the same vocabulary as the provenance chips (§6.7): the token's friendly name, and for a
9
+ * colour, its resolved swatch.
10
10
  *
11
11
  * The resolver follows a chain of references, because an alias token (`--color-accent:
12
12
  * var(--color-brand)`) is the normal way a palette is built and a chip that stopped at the first
@@ -59,9 +59,11 @@
59
59
  "color": "var(--fg-dim)"
60
60
  },
61
61
  "& [part=\"meta-value\"]": {
62
+ "$description": "One of the two places in the chrome that opt back in to selection (`STYLING.md` §Text selection). The root sets `user-select: none` and `styles/tokens.json` restates it on every modal `dialog`, but this dialog's body IS its message: the version, the build date, the commit and the channel exist to be pasted into a bug report, and Studio offers no copy action for them.",
62
63
  "margin": "0",
63
64
  "color": "var(--fg)",
64
- "wordBreak": "break-word"
65
+ "wordBreak": "break-word",
66
+ "userSelect": "text"
65
67
  },
66
68
  "& [part=\"links\"]": {
67
69
  "display": "flex",
@@ -99,10 +101,12 @@
99
101
  "gap": "4px"
100
102
  },
101
103
  "& [part=\"package-row\"]": {
104
+ "$description": "Selectable for the same reason `meta-value` is, and on the ROW rather than on the version alone: what a reader pastes is the pair, `@jxsuite/ai 0.37.0`, so a drag has to be able to cross from the name to the number and on down the list.",
102
105
  "display": "flex",
103
106
  "justifyContent": "space-between",
104
107
  "gap": "12px",
105
- "fontSize": "var(--jx-text-md)"
108
+ "fontSize": "var(--jx-text-md)",
109
+ "userSelect": "text"
106
110
  },
107
111
  "& [part=\"package-name\"]": {
108
112
  "color": "var(--fg-dim)"
@@ -113,10 +113,10 @@
113
113
  "color": "var(--danger)"
114
114
  },
115
115
  "& [part=\"access\"]": {
116
- "$description": "The listing is bounded by what the Jx Suite GitHub App was granted, so the dialog always offers a way out of that boundary rather than only saying the list is short.",
116
+ "$description": "The listing is bounded by what the Jx Suite GitHub App was granted, so the dialog always offers a way out of that boundary rather than only saying the list is short. `center`, not `baseline`: the `Refresh` beside the note is a small kit button, and a small kit control is layout-contained so its outset hit area adds nothing to a scrolling ancestor's overflow, which leaves it with no baseline to export; a baseline row would synthesise one from its bottom edge and drop the note and the links onto it. surface-baseline-rows.test.ts holds the rule for every surface.",
117
117
  "display": "flex",
118
118
  "flexWrap": "wrap",
119
- "alignItems": "baseline",
119
+ "alignItems": "center",
120
120
  "gap": "var(--jx-space-2)",
121
121
  "paddingTop": "var(--jx-space-3)",
122
122
  "borderTop": "1px solid var(--border)",
@@ -187,8 +187,8 @@ export function openAddRepoSurface(options: AddRepoSurfaceOptions): AddRepoSurfa
187
187
  setFilter: (value: string) => {
188
188
  /* What the control holds, first and unconditionally. A binding only writes when the scope
189
189
  CHANGES, so a surface that decided a value without announcing the raw one would leave the
190
- field showing text the scope does not have (§9.3). Nothing refuses a filter today; the echo
191
- is what makes a refusal possible at all. */
190
+ field showing text the scope does not have (`studio-ui-guidelines.md` §9.3). Nothing
191
+ refuses a filter today; the echo is what makes a refusal possible at all. */
192
192
  scope.filter = value;
193
193
  options.onFilter(value);
194
194
  },
@@ -97,12 +97,15 @@
97
97
  "color": "var(--fg-dim)"
98
98
  },
99
99
  "& [part=\"user-body\"]": {
100
+ "$description": "The transcript is content, not interface, and the panel offers no copy action, so each message body opts back in to selection against the root's `user-select: none`: this bubble, the model's `md`, the `streaming` text and the `import-log`. The header, the chips and the composer's buttons around them stay unselectable.",
100
101
  "whiteSpace": "pre-wrap",
101
- "overflowWrap": "anywhere"
102
+ "overflowWrap": "anywhere",
103
+ "userSelect": "text"
102
104
  },
103
105
  "& [part=\"md\"]": {
104
- "$description": "The island `panels/ai-chat/chat-markdown.ts` fills — the app's ONE injection sink, and the markup reaching it has been through `markdownToHtml` and the Trusted Types policy that asserts it did. The document renders the host node and nothing inside it (specs/studio-ui-guidelines.md §9.4).",
105
- "overflowWrap": "anywhere"
106
+ "$description": "The island `panels/ai-chat/chat-markdown.ts` fills — the app's ONE injection sink, and the markup reaching it has been through `markdownToHtml` and the Trusted Types policy that asserts it did. The document renders the host node and nothing inside it (specs/studio-ui-guidelines.md §9.4). A reply is the text a reader most often copies out of this panel, code blocks included, so it is selectable.",
107
+ "overflowWrap": "anywhere",
108
+ "userSelect": "text"
106
109
  },
107
110
  "& [part=\"md\"] > *:first-child": {
108
111
  "marginTop": "0"
@@ -137,8 +140,10 @@
137
140
  "padding": "var(--jx-space-1) 6px"
138
141
  },
139
142
  "& [part=\"streaming\"]": {
143
+ "$description": "The reply while it arrives, selectable for the reason `[part=\"md\"]` is. The cursor is generated content, which no selection ever includes.",
140
144
  "whiteSpace": "pre-wrap",
141
145
  "overflowWrap": "anywhere",
146
+ "userSelect": "text",
142
147
  "::after": {
143
148
  "content": "\"▋\"",
144
149
  "color": "var(--accent)",
@@ -314,6 +319,8 @@
314
319
  "marginRight": "var(--jx-space-2)"
315
320
  },
316
321
  "& [part=\"import-log\"]": {
322
+ "$description": "A captured log, kept selectable against the root's `user-select: none`: its lines are what a reader pastes into a bug report, and nothing here offers to copy them.",
323
+ "userSelect": "text",
317
324
  "marginTop": "var(--jx-space-2)",
318
325
  "maxHeight": "40vh",
319
326
  "overflowY": "auto",
@@ -369,7 +376,8 @@
369
376
  "color": "var(--warning)",
370
377
  "fontStyle": "normal"
371
378
  },
372
- "& [part=\"error\"]": {
379
+ "& [part=\"error-card\"]": {
380
+ "$description": "The failed turn's card. It is `error-card` rather than `error` because the model picker mounted into the composer is a `jx-select`, and every kit field draws a permanent, empty `[part=\"error\"]` of its own in this same light-DOM tree: under that name this rule, padding and wash drew an 18×12 red bar over the picker's top-left corner that took the click. Its children keep the `error-` prefix, which the kit does not use.",
373
381
  "borderLeft": "2px solid var(--danger)",
374
382
  "borderRadius": "var(--jx-radius-sm)",
375
383
  "background": "var(--hover-bg)",
@@ -552,7 +560,7 @@
552
560
  "& [part=\"tool-chip\"][data-outcome=\"failed\"]": {
553
561
  "borderLeftColor": "LinkText"
554
562
  },
555
- "& [part=\"error\"]": {
563
+ "& [part=\"error-card\"]": {
556
564
  "borderLeftColor": "LinkText"
557
565
  }
558
566
  },
@@ -1511,7 +1519,7 @@
1511
1519
  "shown": {
1512
1520
  "tagName": "div",
1513
1521
  "attributes": {
1514
- "part": "error",
1522
+ "part": "error-card",
1515
1523
  "role": "alert"
1516
1524
  },
1517
1525
  "children": [
@@ -48,7 +48,8 @@ const LOG_STICK_THRESHOLD = 24;
48
48
  *
49
49
  * The flow asks the registry; the document draws what it is told. A command the registry does not
50
50
  * hold, or whose `when` is false, never becomes one of these — which is what keeps the panel a
51
- * rendering of the registry rather than a second place its capabilities are decided (§12.5).
51
+ * rendering of the registry rather than a second place its capabilities are decided
52
+ * (`studio-ui-guidelines.md` §12.5).
52
53
  */
53
54
  export interface ChatCommandView extends Record<string, unknown> {
54
55
  /** The reconcile key. The command id, because a button is drawn once per bar. */
@@ -458,7 +459,8 @@ export function mountAiChatSurface(
458
459
  host.replaceChildren();
459
460
  /* Nothing is called on an element here, so the mount is all there is to wait for: the islands
460
461
  announce themselves through `onNodeCreated` as they are created, which is one
461
- `connectedCallback` EARLIER than awaiting the element would be (§1.1, "await the element"). */
462
+ `connectedCallback` EARLIER than awaiting the element would be
463
+ (`studio-ui-guidelines.md` §1.1, "await the element"). */
462
464
  void mountSurface("ai-chat", scope, host, {
463
465
  onNodeCreated: (element, _path, def, state) => {
464
466
  const part = partOf(def);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://jxsuite.com/schema/v1",
3
3
  "$id": "StudioAiCredentialsForm",
4
- "$description": "The AI provider credentials form every gate embeds below the keyless offer: a key, a model, an endpoint, and the two buttons that keep or abandon them. Everything here is a projection — `ui/ai-credentials-form.ts` keeps the drafts, decides what a fetch is sent with, what Save actually persists and whether Cancel is on offer at all. This document never reads a setting and never calls the proxy.",
4
+ "$description": "The AI provider credentials form every gate embeds below the keyless offer: a key, a model, an endpoint, and the two buttons that keep or abandon them. Everything here is a projection — `ui/ai-credentials-form.ts` keeps the drafts, decides what a fetch is sent with, what Save actually persists and whether there is anything to Save or Cancel at all. This document never reads a setting and never calls the proxy.",
5
5
  "tagName": "div",
6
6
  "attributes": {
7
7
  "part": "ai-creds-form",
@@ -17,8 +17,7 @@
17
17
  "maxWidth": "320px",
18
18
  "textAlign": "left",
19
19
  "& [part=\"title\"]": {
20
- "$description": "Centred over a left-aligned column: the form is a titled block, not a list of labelled rows.",
21
- "alignSelf": "center",
20
+ "$description": "Weight alone, and no `align-self`. It was centred over the column on the grounds that the form is a titled block rather than a list of labelled rows, and in Preferences it is neither: the sheet's own heading, the blurb, this note, the `Model` label and all three fields start at the column's inline edge, so a centred line 102px wide in a 320px column read as a stray, not as a title. A heading belongs over the first character of the thing it names.",
22
21
  "fontWeight": "600"
23
22
  },
24
23
  "& [part=\"note\"]": {
@@ -38,17 +37,22 @@
38
37
  "width": "100%"
39
38
  },
40
39
  "& [part=\"models\"]": {
40
+ "$description": "A wrapping row that holds the button and, when there is one, the reason the listing did not finish — and the reason always takes a line of its own, which is what `flex-basis: 100%` on it (below) is for. Beside the button it began 96px into a 320px column, so a six-line upstream body read as a paragraph indented past the control it answers for, while the button floated at that block's vertical middle with about 40px of dead space around it. Under the button, at the column's own inline edge, the reason starts where every other line in the form starts. `flex-start` rather than `center` so that a line which does hold two boxes lines up their first lines and not their middles.",
41
41
  "display": "flex",
42
- "alignItems": "center",
42
+ "flexWrap": "wrap",
43
+ "alignItems": "flex-start",
43
44
  "gap": "var(--jx-space-3)"
44
45
  },
45
46
  "& [part=\"models-error\"]": {
46
- "$description": "Why the listing did not finish, beside the button that would try it again. Named `models-error` rather than `error` because the kit's own fields render a permanent `[part=\"error\"]` region of their own, and a surface that reused the name would be addressing three empty paragraphs before it reached its own.",
47
+ "$description": "Why the listing did not finish, said under the button that would try it again. Named `models-error` rather than `error` because the kit's own fields render a permanent `[part=\"error\"]` region of their own, and a surface that reused the name would be addressing three empty paragraphs before it reached its own. `flexBasis: 100%` is what puts it under the button rather than beside it: the row wraps, and a basis of the whole line cannot share one, so however short the reason is it always reads as a line of the form (`[part=\"models\"]`). `minWidth: 0` and `overflowWrap: anywhere` because the reason is often an upstream body passed through whole — a 401's JSON is one unbroken word — and a flex item's automatic minimum is its min-content width: one measured 853px inside the 320px column, and scrolled the whole Preferences section sideways over the buttons below it.",
47
48
  "color": "var(--danger)",
48
- "fontSize": "var(--jx-text-sm)"
49
+ "fontSize": "var(--jx-text-sm)",
50
+ "flexBasis": "100%",
51
+ "minWidth": "0",
52
+ "overflowWrap": "anywhere"
49
53
  },
50
54
  "& [part=\"actions\"]": {
51
- "$description": "Buttons at the end of the column, in the order a dismissal precedes a commitment.",
55
+ "$description": "Buttons at the end of the column, in the order a dismissal precedes a commitment. Still a flex item of the column although it is drawn inside `[part=\"actions-slot\"]`, because that slot is `display: contents` — which is what keeps `alignSelf` meaning the column's end.",
52
56
  "display": "flex",
53
57
  "alignSelf": "flex-end",
54
58
  "gap": "var(--jx-space-3)"
@@ -250,26 +254,26 @@
250
254
  }
251
255
  },
252
256
  {
257
+ "$description": "The commit pair exists only while there is something to commit: `dirty` is the flow's answer to whether Save would change what is stored, compared after the store's own normalisation, so a pasted key's whitespace is not an edit. At rest neither button is drawn. The rule this replaced offered Cancel whenever a key was stored and Save always, so Preferences › Assistant showed a Save that would re-store what was there and a Cancel that abandoned nothing, beside the dialog's own Close. Absent rather than disabled, against the house rule for a control that cannot act, because the pair IS the pending edit's affordance: a disabled pair at rest would still be the two buttons the reader reported seeing with nothing to save, each needing a reason to give for being there. Answering either takes away the button that was pressed, so `surfaces/ai-credentials-form.ts` lands focus on the key field rather than letting it fall out of the sheet.",
253
258
  "tagName": "div",
254
259
  "attributes": {
255
- "part": "actions"
260
+ "part": "actions-slot",
261
+ "role": "none"
256
262
  },
257
- "children": [
258
- {
263
+ "style": {
264
+ "display": "contents"
265
+ },
266
+ "$switch": {
267
+ "$ref": "#/state/dirty"
268
+ },
269
+ "cases": {
270
+ "true": {
259
271
  "tagName": "div",
260
272
  "attributes": {
261
- "part": "cancel-slot",
262
- "role": "none"
263
- },
264
- "style": {
265
- "display": "contents"
266
- },
267
- "$switch": {
268
- "$ref": "#/state/haveKey"
273
+ "part": "actions"
269
274
  },
270
- "cases": {
271
- "true": {
272
- "$description": "Only offered once a key exists: with nothing stored there is nothing to go back to, and a Cancel that abandons an empty form to an empty form is a button that does nothing.",
275
+ "children": [
276
+ {
273
277
  "tagName": "jx-button",
274
278
  "attributes": {
275
279
  "part": "cancel"
@@ -295,38 +299,38 @@
295
299
  "textContent": "Cancel"
296
300
  }
297
301
  ]
298
- }
299
- }
300
- },
301
- {
302
- "tagName": "jx-button",
303
- "attributes": {
304
- "part": "save"
305
- },
306
- "$props": {
307
- "size": "sm",
308
- "variant": "accent"
309
- },
310
- "onclick": {
311
- "$prototype": "Function",
312
- "body": [
313
- {
314
- "operator": "call",
315
- "target": {
316
- "$ref": "#/state/save"
317
- },
318
- "value": []
319
- }
320
- ]
321
- },
322
- "children": [
302
+ },
323
303
  {
324
- "tagName": "span",
325
- "textContent": "Save"
304
+ "tagName": "jx-button",
305
+ "attributes": {
306
+ "part": "save"
307
+ },
308
+ "$props": {
309
+ "size": "sm",
310
+ "variant": "accent"
311
+ },
312
+ "onclick": {
313
+ "$prototype": "Function",
314
+ "body": [
315
+ {
316
+ "operator": "call",
317
+ "target": {
318
+ "$ref": "#/state/save"
319
+ },
320
+ "value": []
321
+ }
322
+ ]
323
+ },
324
+ "children": [
325
+ {
326
+ "tagName": "span",
327
+ "textContent": "Save"
328
+ }
329
+ ]
326
330
  }
327
331
  ]
328
332
  }
329
- ]
333
+ }
330
334
  }
331
335
  ]
332
336
  }
@@ -3,8 +3,8 @@
3
3
  * The AI provider credentials form as a mounted document.
4
4
  *
5
5
  * `ui/ai-credentials-form.ts` is the flow — it holds the three drafts, decides which credentials a
6
- * listing is made with, what Save persists and re-reads, and whether Cancel is on offer — and this
7
- * is the surface it draws into.
6
+ * listing is made with, what Save persists and re-reads, and whether there is anything to commit —
7
+ * and this is the surface it draws into.
8
8
  *
9
9
  * **The host element belongs to this module, not to the gate.** Every credentials gate is still a
10
10
  * lit template that interpolates the form beside the keyless offer, so there is no container in the
@@ -58,8 +58,12 @@ export interface AiCredentialsView {
58
58
  fetchLabel: string;
59
59
  /** Why the last listing did not finish. Empty draws nothing. */
60
60
  modelsError: string;
61
- /** A key is already stored, so abandoning the drafts means something. */
62
- haveKey: boolean;
61
+ /**
62
+ * The drafts differ from what is stored — there is something to Save and something to Cancel. The
63
+ * flow compares them as the store would hold them, so whitespace a Save would trim is not a
64
+ * difference. False at rest, which is when the document draws neither button.
65
+ */
66
+ dirty: boolean;
63
67
  }
64
68
 
65
69
  /** Everything the reader can do here. */
@@ -134,7 +138,17 @@ export function createAiCredentialsSurface(
134
138
  return {
135
139
  host,
136
140
  update(next) {
141
+ /* Save and Cancel exist only while the form is dirty, so answering either takes away the
142
+ button the reader just pressed — and a removed element's focus falls to `<body>`, which in
143
+ a modal sheet leaves a keyboard or screen-reader user nowhere. The key field is where it
144
+ lands instead: the top of the form that now shows what is stored. Only when focus WAS on
145
+ the pair and the update actually lost it, so a repaint never takes the caret from a field. */
146
+ const answering =
147
+ !next.dirty && host.querySelector('[part="actions"]')?.contains(document.activeElement);
137
148
  Object.assign(scope, derive(next));
149
+ if (answering && !host.contains(document.activeElement)) {
150
+ host.querySelector<HTMLElement>('[part="key"] [part="input"]')?.focus();
151
+ }
138
152
  /* A standing mount is only ASSIGNED to: rebuilding it on every repaint of the gate around it
139
153
  would empty the field the reader is typing in. The remount below answers the one case
140
154
  assignment cannot — a host the document has been taken out of — and it cannot fire while a
@@ -22,8 +22,8 @@
22
22
  "maxWidth": "280px",
23
23
  "textAlign": "center"
24
24
  },
25
- "& [part=\"error\"]": {
26
- "$description": "Why the last attempt did not finish, said under the button that would try it again. Narrower than the lede: a refusal is a sentence to read once, not the thing the surface is about.",
25
+ "& [part=\"connect-failure\"]": {
26
+ "$description": "Why the last attempt did not finish, said under the button that would try it again. Narrower than the lede: a refusal is a sentence to read once, not the thing the surface is about. The name is qualified, and BOTH halves of it are load-bearing. Not `error`, because every kit field draws a permanent, empty `[part=\"error\"]` of its own and the kit is light DOM, so a rule on that name would reach any field this surface ever holds. Not the bare `failure` every panel banner uses either, because this document is MOUNTED inside another one: `new-project.json` puts it in `[part=\"managed-island\"]` and styles its own backend refusal with a descendant `[part=\"failure\"]`, which would draw this sentence with that dialog's footer border and padding. A guest surface picks a name its host does not write, and `tests/surface-part-names.test.ts` holds the two island hosts to it.",
27
27
  "margin": "0",
28
28
  "color": "var(--danger)",
29
29
  "fontSize": "var(--jx-text-sm)",
@@ -78,7 +78,7 @@
78
78
  {
79
79
  "tagName": "div",
80
80
  "attributes": {
81
- "part": "error-slot",
81
+ "part": "connect-failure-slot",
82
82
  "role": "none"
83
83
  },
84
84
  "style": {
@@ -91,7 +91,7 @@
91
91
  "true": {
92
92
  "tagName": "p",
93
93
  "attributes": {
94
- "part": "error"
94
+ "part": "connect-failure"
95
95
  },
96
96
  "textContent": "${state.error}"
97
97
  }