@jxsuite/studio 5.3.0 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (389) hide show
  1. package/data/css-meta.json +28 -27
  2. package/dist/chunks/{cf-account-picker-nwnwdm6p.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-4rzvmp0m.js → content-source-jt7rbcbc.js} +22 -22
  6. package/dist/chunks/{convert-to-repeater-jxsv62v7.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-2mrptyek.js → extension-sections-r73an5s6.js} +30 -29
  10. package/dist/chunks/{file-ops-knfqshrm.js → file-ops-3xan8mnj.js} +21 -21
  11. package/dist/chunks/{files-y6fr4he9.js → files-egsb0ry8.js} +28 -28
  12. package/dist/chunks/{format-host-1k4qrcqd.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-2rkrsjva.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-a7b87n2c.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-scpw01h8.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/layers-panel-83a16ytx.js +41 -0
  25. package/dist/chunks/{layers-jnvdbqva.js → layers-s6mc01ry.js} +3 -3
  26. package/dist/chunks/{media-picker-f66nav16.js → media-picker-nhn6rv4w.js} +11 -11
  27. package/dist/chunks/{menu-1dfhry4s.js → menu-8j4qxjrp.js} +4 -4
  28. package/dist/chunks/{monaco-binding-sw6en4dw.js → monaco-binding-e8d9wzyk.js} +3 -3
  29. package/dist/chunks/{monaco-buffer-pykwbm92.js → monaco-buffer-jaq1t9zj.js} +2 -2
  30. package/dist/chunks/{monaco-setup-8vbyqnhr.css → monaco-setup-ey9cjcm3.css} +954 -137
  31. package/dist/chunks/{monaco-setup-km6abc6q.js → monaco-setup-gsjdysze.js} +6 -6
  32. package/dist/chunks/{platform-ts9cg1a4.js → platform-fnchmhy7.js} +5 -3
  33. package/dist/chunks/publish-panel-ak6hxfma.js +27 -0
  34. package/dist/chunks/quick-search-c63mkt3b.js +47 -0
  35. package/dist/chunks/{references-h69xp9zz.js → references-f7bdv3z5.js} +3 -3
  36. package/dist/chunks/right-panel-t5n8wttg.js +45 -0
  37. package/dist/chunks/{studio-ze46gzjh.js → studio-01269reb.js} +7 -7
  38. package/dist/chunks/{studio-rnyf3f8e.js → studio-0a4gncx0.js} +3 -3
  39. package/dist/chunks/{studio-e1f24eff.js → studio-0d4p4wxj.js} +6778 -2993
  40. package/dist/chunks/{studio-4wbb9ct0.js → studio-1tejxvba.js} +3006 -1855
  41. package/dist/chunks/{studio-d1n2bykn.js → studio-3nprwbd9.js} +117 -25
  42. package/dist/chunks/{studio-4c041r8y.js → studio-40z39dme.js} +3 -3
  43. package/dist/chunks/{studio-151w0xnh.js → studio-482eeazk.js} +20 -20
  44. package/dist/chunks/{studio-cgprcdxn.js → studio-4998spet.js} +10 -9
  45. package/dist/chunks/{studio-nq5yf5mp.js → studio-557bmn0x.js} +2 -2
  46. package/dist/chunks/{studio-rtskkm43.js → studio-5w26wza5.js} +3 -3
  47. package/dist/chunks/{studio-6nzaktvy.js → studio-6pwn9j8c.js} +6 -3
  48. package/dist/chunks/{studio-wnsyffg1.js → studio-6vg0x5fz.js} +3 -2
  49. package/dist/chunks/{studio-vn5hske5.js → studio-7d3c1m59.js} +12 -5
  50. package/dist/chunks/{studio-6gryj0sg.js → studio-9vsvz7w7.js} +6 -6
  51. package/dist/chunks/{studio-50wfwy44.js → studio-9wm0xzpf.js} +19 -19
  52. package/dist/chunks/{studio-0j7r7s9g.js → studio-ag3z6shb.js} +10 -4
  53. package/dist/chunks/studio-aj9drheq.js +177 -0
  54. package/dist/chunks/{studio-xajk70w0.js → studio-apv9x70c.js} +189 -130
  55. package/dist/chunks/{studio-c04nzbby.js → studio-b9q3znz8.js} +2 -2
  56. package/dist/chunks/{studio-nfx354t1.js → studio-bh47w0cy.js} +9 -10
  57. package/dist/chunks/{studio-gk2twyh4.js → studio-dgdm6vvt.js} +264 -52
  58. package/dist/chunks/{studio-gtfq1dkz.js → studio-dwdcr2e8.js} +4 -4
  59. package/dist/chunks/{studio-nqgawfks.js → studio-fbcyz234.js} +146 -139
  60. package/dist/chunks/{studio-27q131j3.js → studio-fqpkpq1t.js} +7 -4
  61. package/dist/chunks/{studio-c0qgc3tn.js → studio-fy320dfc.js} +62 -68
  62. package/dist/chunks/{studio-zjjde71a.js → studio-gjj4bbfx.js} +2 -2
  63. package/dist/chunks/{studio-g33v4fe9.js → studio-jfhxy8h3.js} +7 -7
  64. package/dist/chunks/{studio-ab87phj1.js → studio-kwewmhhq.js} +4 -4
  65. package/dist/chunks/{studio-7rrb1esn.js → studio-m58xn8at.js} +25 -24
  66. package/dist/chunks/{studio-fzjx96t2.js → studio-mfkh1q8d.js} +17 -17
  67. package/dist/chunks/{studio-drg8qj9k.js → studio-mrh0a92f.js} +17 -17
  68. package/dist/chunks/{studio-jt27ta6x.js → studio-mt79tn56.js} +3 -3
  69. package/dist/chunks/{studio-b66rsh78.js → studio-ne593z1j.js} +38 -31
  70. package/dist/chunks/{studio-h4nvnjvz.js → studio-ptfcxkz5.js} +418 -232
  71. package/dist/chunks/{studio-dd1qqkry.js → studio-qxye31h7.js} +7 -7
  72. package/dist/chunks/{studio-ye9drbk8.js → studio-rav5bpmp.js} +73 -52
  73. package/dist/chunks/{studio-6kvgrkrp.js → studio-rwmmbrth.js} +6 -6
  74. package/dist/chunks/{studio-b7xacrwe.js → studio-sceg8ws0.js} +8 -8
  75. package/dist/chunks/{studio-msc1ee1m.js → studio-sjas5b3a.js} +19 -13
  76. package/dist/chunks/{studio-a65w20ex.js → studio-vd67b66d.js} +3 -3
  77. package/dist/chunks/{studio-7144epdr.js → studio-w475cbf9.js} +10 -10
  78. package/dist/chunks/{studio-4f3psrk1.js → studio-w7gpcvfj.js} +9 -9
  79. package/dist/chunks/{studio-nsgw7dbk.js → studio-xrbfdxgk.js} +7 -7
  80. package/dist/chunks/{studio-ke09tn9k.js → studio-y49b6mjv.js} +35 -35
  81. package/dist/chunks/{studio-j0w60gft.js → studio-ygvr9e4q.js} +10 -10
  82. package/dist/chunks/{studio-z2smx5ce.js → studio-yxmmjy2a.js} +9 -6
  83. package/dist/chunks/{tsMode-jy4m86rv.js → tsMode-ay4c78ne.js} +4 -4
  84. package/dist/chunks/{tsMode-hnxxb73h.css → tsMode-c72wyz27.css} +954 -137
  85. package/dist/chunks/{workspace-np10mayv.js → workspace-b9k2frks.js} +5 -5
  86. package/dist/chunks/{ws-client-ny4zc51r.js → ws-client-erze4czt.js} +356 -20
  87. package/dist/codicon.ttf +0 -0
  88. package/dist/iframe-entry.js +18 -16
  89. package/dist/studio.css +956 -139
  90. package/dist/studio.js +1453 -830
  91. package/dist/workers/editor.worker.js +196 -107
  92. package/dist/workers/json.worker.js +196 -107
  93. package/dist/workers/ts.worker.js +196 -107
  94. package/package.json +15 -14
  95. package/src/about/about-modal.ts +4 -3
  96. package/src/browse/library-commands.ts +15 -13
  97. package/src/browse/library-pane.ts +2 -2
  98. package/src/canvas/canvas-render.ts +97 -83
  99. package/src/canvas/canvas-surface.ts +2 -2
  100. package/src/canvas/canvas-utils.ts +42 -32
  101. package/src/canvas/diff-toolbar.ts +21 -2
  102. package/src/canvas/diff-view.ts +30 -6
  103. package/src/canvas/iframe-entry.ts +2 -2
  104. package/src/canvas/iframe-host.ts +5 -5
  105. package/src/canvas/iframe-interaction.ts +7 -6
  106. package/src/canvas/iframe-overlay.ts +1 -1
  107. package/src/canvas/iframe-protocol.ts +6 -5
  108. package/src/canvas/iframe-render.ts +3 -3
  109. package/src/canvas/popover-state.ts +1 -1
  110. package/src/canvas/stage-scrollbar.ts +112 -0
  111. package/src/canvas/surface-registry.ts +9 -2
  112. package/src/collab/collab-commands.ts +8 -6
  113. package/src/collab/collab-session.ts +4 -4
  114. package/src/collab/collab-state.ts +4 -4
  115. package/src/collab/presence-chips.ts +3 -3
  116. package/src/commands/app-commands.ts +7 -7
  117. package/src/commands/budget.ts +17 -16
  118. package/src/commands/command-args.ts +10 -9
  119. package/src/commands/context.ts +20 -10
  120. package/src/commands/defaults.ts +30 -29
  121. package/src/commands/levels.ts +8 -6
  122. package/src/commands/live-context.ts +12 -10
  123. package/src/commands/reference.ts +5 -5
  124. package/src/commands/registry.ts +2 -2
  125. package/src/content/draft-state.ts +9 -8
  126. package/src/content/entry-commands.ts +16 -14
  127. package/src/content/entry-editor.ts +2 -2
  128. package/src/content/entry-fields.ts +1 -1
  129. package/src/editor/context-menu.ts +4 -4
  130. package/src/editor/convert-to-component.ts +6 -5
  131. package/src/editor/inline-edit.ts +3 -3
  132. package/src/editor/shortcuts.ts +88 -20
  133. package/src/editor/slash-menu.ts +11 -10
  134. package/src/files/elements.ts +4 -5
  135. package/src/files/file-ops.ts +1 -1
  136. package/src/files/files.ts +21 -17
  137. package/src/files/serialize-document.ts +1 -1
  138. package/src/format/convert-file.ts +5 -5
  139. package/src/github/github-auth-status.ts +107 -0
  140. package/src/github/github-auth.ts +18 -82
  141. package/src/github/github-publish.ts +7 -6
  142. package/src/grid/cell-editors.ts +8 -7
  143. package/src/grid/grid-layout.ts +1 -1
  144. package/src/grid/grid-open.ts +6 -5
  145. package/src/grid/grid-panel.ts +13 -12
  146. package/src/grid/redirects-grid.ts +14 -11
  147. package/src/grid/redirects.ts +10 -5
  148. package/src/grid/sources/content-source.ts +10 -9
  149. package/src/hosting/document.ts +28 -1
  150. package/src/hosting/stage.ts +15 -4
  151. package/src/i18n/i18n-commands.ts +9 -7
  152. package/src/new-project/location-fields.ts +16 -3
  153. package/src/new-project/new-project-modal.ts +23 -7
  154. package/src/page-params.ts +6 -6
  155. package/src/panels/activity-panel.ts +11 -10
  156. package/src/panels/ai-chat/chat-view.ts +27 -14
  157. package/src/panels/ai-chat/composer.ts +10 -3
  158. package/src/panels/ai-panel.ts +87 -29
  159. package/src/panels/block-action-bar.ts +59 -33
  160. package/src/panels/bottom-dock.ts +22 -22
  161. package/src/panels/data-explorer.ts +9 -9
  162. package/src/panels/data-grid.ts +1 -1
  163. package/src/panels/editors.ts +8 -8
  164. package/src/panels/elements-panel.ts +7 -8
  165. package/src/panels/empty-state.ts +1 -1
  166. package/src/panels/events-panel.ts +13 -12
  167. package/src/panels/formula-workspace.ts +8 -5
  168. package/src/panels/frontmatter-fields.ts +4 -1
  169. package/src/panels/frontmatter-panel.ts +18 -18
  170. package/src/panels/git-panel.ts +19 -13
  171. package/src/panels/head-panel.ts +9 -10
  172. package/src/panels/i18n-panel.ts +6 -4
  173. package/src/panels/imports-panel.ts +1 -1
  174. package/src/panels/jump-bar.ts +20 -18
  175. package/src/panels/layers-panel.ts +19 -17
  176. package/src/panels/left-panel.ts +3 -3
  177. package/src/panels/navigator-panels.ts +8 -8
  178. package/src/panels/overlays.ts +1 -1
  179. package/src/panels/pane-context.ts +56 -28
  180. package/src/panels/pane-grid.ts +9 -8
  181. package/src/panels/panel-registry.ts +47 -19
  182. package/src/panels/problems-panel.ts +11 -12
  183. package/src/panels/properties-panel.ts +21 -20
  184. package/src/panels/provenance.ts +3 -3
  185. package/src/panels/quick-search.ts +15 -11
  186. package/src/panels/right-panel.ts +4 -3
  187. package/src/panels/seo-modal.ts +5 -4
  188. package/src/panels/settings-menu.ts +9 -9
  189. package/src/panels/settings-pane.ts +3 -3
  190. package/src/panels/signals-panel.ts +16 -9
  191. package/src/panels/statement-editor.ts +12 -9
  192. package/src/panels/style-panel.ts +45 -41
  193. package/src/panels/stylebook-panel.ts +0 -1
  194. package/src/panels/tab-strip.ts +26 -23
  195. package/src/platform-errors.ts +2 -2
  196. package/src/platform.ts +44 -4
  197. package/src/platforms/cloud.ts +11 -1
  198. package/src/platforms/default-platform.ts +141 -4
  199. package/src/platforms/devserver.ts +23 -3
  200. package/src/publish/deploy-checklist.ts +11 -10
  201. package/src/publish/publish-commands.ts +9 -8
  202. package/src/services/a11y-report.ts +1 -1
  203. package/src/services/ai-ask.ts +39 -16
  204. package/src/services/ai-command-tools.ts +28 -14
  205. package/src/services/ai-import-tools.ts +43 -29
  206. package/src/services/ai-project-tools.ts +20 -38
  207. package/src/services/ai-settings.ts +55 -2
  208. package/src/services/ai-tools.ts +69 -58
  209. package/src/services/ai-write-report.ts +4 -4
  210. package/src/services/ai-writes.ts +74 -55
  211. package/src/services/automation.ts +12 -12
  212. package/src/services/clock.ts +2 -1
  213. package/src/services/connection.ts +2 -2
  214. package/src/services/context-manager.ts +69 -6
  215. package/src/services/directory-picker.ts +93 -27
  216. package/src/services/document-assistant.ts +86 -17
  217. package/src/services/gated-registry.ts +4 -3
  218. package/src/services/idle.ts +5 -4
  219. package/src/services/live-surfaces.ts +2 -1
  220. package/src/services/notify.ts +9 -7
  221. package/src/services/popover-report.ts +3 -3
  222. package/src/services/preview-eval.ts +1 -1
  223. package/src/services/project-adoption.ts +34 -1
  224. package/src/services/project-path.ts +53 -0
  225. package/src/services/settings/kernel.ts +13 -2
  226. package/src/services/tool-executor.ts +135 -30
  227. package/src/services/tool-outcomes.ts +143 -0
  228. package/src/settings/contexts-section.ts +8 -8
  229. package/src/settings/contributed-section.ts +6 -6
  230. package/src/settings/css-vars-editor.ts +6 -5
  231. package/src/settings/extension-commands.ts +19 -18
  232. package/src/settings/extension-sections.ts +7 -7
  233. package/src/settings/extensions-section.ts +3 -3
  234. package/src/settings/general-settings.ts +21 -18
  235. package/src/settings/preferences-accounts.ts +3 -3
  236. package/src/settings/preferences-dialog.ts +9 -8
  237. package/src/settings/preferences-sections.ts +1 -1
  238. package/src/settings/project-sections.ts +2 -2
  239. package/src/settings/section-registry.ts +7 -6
  240. package/src/settings/settings-document.ts +41 -43
  241. package/src/shell/tree.ts +29 -0
  242. package/src/shell.ts +63 -55
  243. package/src/site-context.ts +1 -1
  244. package/src/store.ts +2 -2
  245. package/src/studio.ts +46 -28
  246. package/src/style/live-preview.ts +8 -8
  247. package/src/style/project-styles.ts +8 -9
  248. package/src/style/token-ref.ts +2 -2
  249. package/src/surfaces/about.json +6 -2
  250. package/src/surfaces/add-repo.json +2 -2
  251. package/src/surfaces/add-repo.ts +2 -2
  252. package/src/surfaces/ai-chat.json +14 -6
  253. package/src/surfaces/ai-chat.ts +4 -2
  254. package/src/surfaces/ai-credentials-form.json +53 -49
  255. package/src/surfaces/ai-credentials-form.ts +18 -4
  256. package/src/surfaces/ai-managed-connect.json +4 -4
  257. package/src/surfaces/block-action-bar.json +4 -4
  258. package/src/surfaces/block-action-bar.ts +3 -3
  259. package/src/surfaces/boot-failure.json +228 -0
  260. package/src/surfaces/boot-failure.ts +156 -0
  261. package/src/surfaces/bottom-dock.json +1 -1
  262. package/src/surfaces/canvas-stage.json +9 -40
  263. package/src/surfaces/canvas-stage.ts +9 -9
  264. package/src/surfaces/cf-account-picker.json +11 -8
  265. package/src/surfaces/commandbar.ts +11 -10
  266. package/src/surfaces/convert-repeater.ts +3 -2
  267. package/src/surfaces/dialog.json +7 -1
  268. package/src/surfaces/dialog.ts +2 -2
  269. package/src/surfaces/doc-header.json +6 -13
  270. package/src/surfaces/doc-header.ts +4 -5
  271. package/src/surfaces/entry-editor.json +1 -1
  272. package/src/surfaces/entry-editor.ts +2 -1
  273. package/src/surfaces/expression-editor.json +5 -5
  274. package/src/surfaces/files-panel.json +4 -3
  275. package/src/surfaces/files-panel.ts +1 -1
  276. package/src/surfaces/formula-palette.json +2 -2
  277. package/src/surfaces/formula-palette.ts +1 -1
  278. package/src/surfaces/git-panel.json +167 -109
  279. package/src/surfaces/git-panel.ts +2 -2
  280. package/src/surfaces/github-auth.json +2 -1
  281. package/src/surfaces/github-publish.ts +3 -3
  282. package/src/surfaces/grid-open.json +1 -1
  283. package/src/surfaces/grid-panel.json +2 -2
  284. package/src/surfaces/grid-views.json +1 -1
  285. package/src/surfaces/inspector-dock.json +2 -2
  286. package/src/surfaces/jump-bar.json +1 -1
  287. package/src/surfaces/jump-bar.ts +1 -1
  288. package/src/surfaces/library-pane.ts +2 -1
  289. package/src/surfaces/logic-panel.json +7 -7
  290. package/src/surfaces/logic-panel.ts +6 -4
  291. package/src/surfaces/logic-workspace.json +2 -2
  292. package/src/surfaces/logic-workspace.ts +2 -2
  293. package/src/surfaces/media-browser.ts +3 -3
  294. package/src/surfaces/media-pane.ts +2 -1
  295. package/src/surfaces/menu.ts +5 -5
  296. package/src/surfaces/new-project.json +1 -1
  297. package/src/surfaces/pane-context.json +86 -57
  298. package/src/surfaces/pane-context.ts +2 -1
  299. package/src/surfaces/pane-grid.json +3 -3
  300. package/src/surfaces/panel-activity.json +2 -1
  301. package/src/surfaces/panel-deploy-checklist.json +1 -1
  302. package/src/surfaces/panel-elements.ts +2 -1
  303. package/src/surfaces/panel-i18n.ts +2 -1
  304. package/src/surfaces/panel-outline.json +1 -1
  305. package/src/surfaces/panel-outline.ts +4 -3
  306. package/src/surfaces/panel-page.json +2 -2
  307. package/src/surfaces/panel-page.ts +4 -1
  308. package/src/surfaces/panel-problems.json +4 -3
  309. package/src/surfaces/panel-problems.ts +2 -2
  310. package/src/surfaces/panel-signals.json +23 -8
  311. package/src/surfaces/panel-signals.ts +5 -1
  312. package/src/surfaces/panel-stylebook-layers.json +1 -1
  313. package/src/surfaces/panel-stylebook-layers.ts +2 -2
  314. package/src/surfaces/preferences.json +21 -8
  315. package/src/surfaces/preferences.ts +9 -1
  316. package/src/surfaces/properties-panel.json +4 -4
  317. package/src/surfaces/properties-panel.ts +2 -2
  318. package/src/surfaces/publish.json +1 -1
  319. package/src/surfaces/publish.ts +2 -1
  320. package/src/surfaces/push-plan.json +4 -3
  321. package/src/surfaces/rail.json +2 -2
  322. package/src/surfaces/rail.ts +10 -4
  323. package/src/surfaces/reference-field.json +1 -1
  324. package/src/surfaces/schema-form.json +2 -2
  325. package/src/surfaces/schema-form.ts +1 -1
  326. package/src/surfaces/seo.json +1 -1
  327. package/src/surfaces/seo.ts +2 -2
  328. package/src/surfaces/settings-contributed.ts +2 -1
  329. package/src/surfaces/settings-css-vars.json +4 -4
  330. package/src/surfaces/settings-deploy.json +4 -4
  331. package/src/surfaces/settings-deploy.ts +2 -1
  332. package/src/surfaces/settings-extensions.json +6 -6
  333. package/src/surfaces/settings-extensions.ts +6 -2
  334. package/src/surfaces/settings-locales.json +4 -4
  335. package/src/surfaces/settings-overview.ts +2 -1
  336. package/src/surfaces/settings-pane.ts +3 -2
  337. package/src/surfaces/settings-rawjson.ts +1 -1
  338. package/src/surfaces/slash-menu.ts +4 -4
  339. package/src/surfaces/statements.json +3 -3
  340. package/src/surfaces/statusbar.ts +23 -23
  341. package/src/surfaces/style-panel.json +9 -5
  342. package/src/surfaces/style-panel.ts +3 -3
  343. package/src/surfaces/tab-strip.json +2 -2
  344. package/src/surfaces/target-line.json +1 -1
  345. package/src/surfaces/target-line.ts +12 -10
  346. package/src/surfaces/welcome.json +7 -2
  347. package/src/tabs/doc-op-apply.ts +5 -5
  348. package/src/tabs/patch-ops.ts +3 -3
  349. package/src/tabs/project-config.ts +10 -10
  350. package/src/tabs/selection.ts +1 -1
  351. package/src/tabs/tab.ts +13 -14
  352. package/src/tabs/transact.ts +18 -24
  353. package/src/types.ts +27 -3
  354. package/src/ui/ai-credentials-form.ts +74 -16
  355. package/src/ui/ai-managed-connect.ts +68 -19
  356. package/src/ui/color-selector.ts +1 -1
  357. package/src/ui/expression-editor.ts +3 -3
  358. package/src/ui/form-controls.ts +6 -5
  359. package/src/ui/formula-catalog.ts +4 -4
  360. package/src/ui/formula-chips.ts +2 -2
  361. package/src/ui/layers.ts +20 -18
  362. package/src/ui/panel-resize.ts +5 -5
  363. package/src/ui/popover-surface.ts +2 -2
  364. package/src/ui/progress-modal.ts +3 -3
  365. package/src/ui/regions.ts +14 -9
  366. package/src/ui/schema-form.ts +10 -9
  367. package/src/ui/timing.ts +9 -7
  368. package/src/ui/value-source.ts +4 -4
  369. package/src/ui/virtual-window.ts +1 -1
  370. package/src/utils/base64.ts +20 -1
  371. package/src/utils/canvas-media.ts +1 -1
  372. package/src/utils/studio-utils.ts +0 -31
  373. package/src/view.ts +4 -3
  374. package/src/workspace/pane-derive.ts +14 -13
  375. package/src/workspace/session.ts +7 -6
  376. package/src/workspace/workspace.ts +49 -39
  377. package/styles/canvas.css +1 -27
  378. package/styles/inspector.css +4 -3
  379. package/styles/overlays.css +1 -1
  380. package/styles/tokens.css +17 -1
  381. package/styles/tokens.json +6 -1
  382. package/dist/chunks/entry-commands-1wnsqn5d.js +0 -42
  383. package/dist/chunks/layers-panel-73gfkcga.js +0 -41
  384. package/dist/chunks/publish-panel-z1zd2da1.js +0 -27
  385. package/dist/chunks/quick-search-zdv7h8ht.js +0 -47
  386. package/dist/chunks/right-panel-889vbnm5.js +0 -45
  387. package/dist/chunks/studio-727xh0e3.js +0 -76
  388. package/src/files/json-layout.ts +0 -474
  389. package/src/services/ai-turn-signal.ts +0 -70
@@ -1,11 +1,11 @@
1
1
  /// <reference lib="dom" />
2
2
  /**
3
- * The one surface in Studio that still BLOCKS while it works — and now the only one (plan §7.3).
3
+ * The one surface in Studio that still BLOCKS while it works — and now the only one (§16.4).
4
4
  *
5
5
  * What changed. This modal used to be four things at once: the running state, the error view, the
6
6
  * log and the operation's entire memory. It blocked the whole app, offered no way out, and took all
7
7
  * four with it when it closed — so a failed `bun install` could be read exactly once, and a hung
8
- * one could not be escaped at all. §7.3 splits those apart:
8
+ * one could not be escaped at all. §16.4 splits those apart:
9
9
  *
10
10
  * - The **record** moves to `panels/activity-panel.ts`, which keeps the entry, its steps and its log
11
11
  * after the modal is gone;
@@ -15,7 +15,7 @@
15
15
  * - And the **exit** arrives twice: `Run in the background` hands the app back while the operation
16
16
  * keeps going in Activity, and an operation that passed a `cancel` gets a real Cancel button.
17
17
  *
18
- * What did NOT change is which operations block. §7.3 keeps blocking for dependency install and
18
+ * What did NOT change is which operations block. §16.4 keeps blocking for dependency install and
19
19
  * nothing else, and this module's four call sites are exactly that — `packages/ensure-deps.ts`,
20
20
  * `packages/pull-package-sync.ts`, `packages/jxsuite-update.ts` and
21
21
  * `settings/dependencies-editor.ts`. Every other long operation calls `beginActivity` directly and
package/src/ui/regions.ts CHANGED
@@ -9,7 +9,8 @@
9
9
  * restoration after a toast or a Problems jump, and the screenshot pipeline, which today addresses
10
10
  * the shell through sixteen CSS selectors, seven of which name nodes the shell redesign deletes.
11
11
  *
12
- * **The grammar** (UX-REDESIGN-PLAN §13.2) is `<surface>[.<instance>][/<part>]`:
12
+ * **The grammar** (scripts/screenshots/README.md, "The region grammar") is
13
+ * `<surface>[.<instance>][/<part>]`:
13
14
  *
14
15
  * ```text
15
16
  * navigator the Navigator dock
@@ -38,7 +39,10 @@ import type { FocusRegion } from "../shell";
38
39
  /** The attribute every region carries. One attribute, one grammar, one resolver. */
39
40
  export const REGION_ATTR = "data-jx-region";
40
41
 
41
- /** The eight surfaces §13.2 declares. A region id always begins with one of these. */
42
+ /**
43
+ * The eight surfaces the region grammar declares (`scripts/screenshots/README.md`). A region id
44
+ * always begins with one of these.
45
+ */
42
46
  export const REGION_SURFACES = [
43
47
  "rail",
44
48
  "navigator",
@@ -377,12 +381,12 @@ export function inspectorStatementsRegion(eventKey: string): string {
377
381
  * Region id for a Bottom dock tab, derived from the panel's own id.
378
382
  *
379
383
  * The third member of the derived-region family, and the one that shows the grammar was worth
380
- * specifying before the surface existed: `dock.bottom` has parsed since P3 (it is the only surface
381
- * whose NAME contains the instance separator, which is why {@link DOTTED_SURFACES} exists) and
382
- * resolved to nothing, because nothing hosted it. `panels/bottom-dock.ts` stamps this once, from
383
- * the same id `view.setBottomTab` accepts, so the dock's four tabs are addressable without anyone
384
- * authoring an id — and the host itself carries the bare `dock.bottom` only while it is open, so a
385
- * closed dock resolves to nothing rather than to an invisible box.
384
+ * specifying before the surface existed: `dock.bottom` parsed before the dock was built (it is the
385
+ * only surface whose NAME contains the instance separator, which is why {@link DOTTED_SURFACES}
386
+ * exists) and resolved to nothing, because nothing hosted it. `panels/bottom-dock.ts` stamps this
387
+ * once, from the same id `view.setBottomTab` accepts, so every tab the dock hosts is addressable
388
+ * without anyone authoring an id — and the host itself carries the bare `dock.bottom` only while it
389
+ * is open, so a closed dock resolves to nothing rather than to an invisible box.
386
390
  */
387
391
  export function bottomPanelRegion(panelId: string): string {
388
392
  return `dock.bottom/panel:${panelId}`;
@@ -419,7 +423,8 @@ export function overlayRegion(
419
423
  }
420
424
 
421
425
  /**
422
- * `regions` — the namespace §13.3 names, gathered so a consumer imports one thing.
426
+ * `regions` — the resolver behind the region grammar (`scripts/screenshots/README.md`), gathered so
427
+ * a consumer imports one thing.
423
428
  *
424
429
  * `probe.regions` is a projection of this; nothing here knows the camera exists.
425
430
  */
@@ -162,7 +162,7 @@ export interface RenderFormOptions {
162
162
  ui?: Record<string, { control?: string; enum?: unknown }> | undefined;
163
163
  rerender?: (() => void) | undefined;
164
164
  /**
165
- * Externally-produced diagnostics, keyed by property name — §7.1's inline tier, sourced.
165
+ * Externally-produced diagnostics, keyed by property name — §16.1's inline tier, sourced.
166
166
  *
167
167
  * This is how a validator that runs over the WHOLE document reaches the one field it is about:
168
168
  * `jx-validate`'s `project.json` errors, Monaco's markers for the same file open in the code
@@ -177,7 +177,7 @@ export interface RenderFormOptions {
177
177
  /** How many times each field has been refused in a row; drives the row's repeat counter. */
178
178
  errorCounts?: Record<string, number> | undefined;
179
179
  /**
180
- * Report required-but-empty fields inline. Off by default, and that default is §7.1's rule
180
+ * Report required-but-empty fields inline. Off by default, and that default is §16.5's rule
181
181
  * literally applied: a form the user has not touched yet has not committed anything, so painting
182
182
  * every required field red the moment it renders is telling them they got something wrong before
183
183
  * they did anything. Required-ness is already shown — the row carries the required mark. A host
@@ -313,7 +313,7 @@ function asText(value: unknown): string {
313
313
  return typeof value === "object" ? JSON.stringify(value, null, 2) : String(value);
314
314
  }
315
315
 
316
- // ─── Field validation (§7.1, inline tier) ────────────────────────────────────
316
+ // ─── Field validation (§16.1, inline tier) ───────────────────────────────────
317
317
 
318
318
  /**
319
319
  * Why this value is not acceptable for this property schema, or `""` when it is.
@@ -670,7 +670,7 @@ function createController(): FormController {
670
670
  return raw;
671
671
  }
672
672
 
673
- /** The rung picker: every source this position permits, one action away (§6.3). */
673
+ /** The rung picker: every source this position permits, one action away (§6.6). */
674
674
  function openSourceMenu(key: string, anchor: HTMLElement): void {
675
675
  const plan = plans.get(key);
676
676
  if (!plan?.ladder) {
@@ -985,11 +985,12 @@ function deriveField(prop: string, ps: JsonSchema, args: DeriveArgs): SchemaForm
985
985
  }
986
986
 
987
987
  /* A relationship to another collection (`$ref: "#/content/<type>"`) is the registered `reference`
988
- control, wherever the form is drawn — §9.2's "one picker" is this dispatch plus the single
989
- `registerFormControl("reference", …)` in `ui/form-controls.ts`. It is deliberately NOT an enum:
990
- the choices are entry files on disk, so they are read asynchronously and can be stale, and a
991
- schema `enum` is a closed set the document itself declares. When the control is not registered
992
- (a bare-Bun import of this engine), the field falls through to the plain text control below. */
988
+ control, wherever the form is drawn — site-architecture.md §7.4's one entry picker is this
989
+ dispatch plus the single `registerFormControl("reference", …)` in `ui/form-controls.ts`. It is
990
+ deliberately NOT an enum: the choices are entry files on disk, so they are read asynchronously
991
+ and can be stale, and a schema `enum` is a closed set the document itself declares. When the
992
+ control is not registered (a bare-Bun import of this engine), the field falls through to the
993
+ plain text control below. */
993
994
  if (referenceTarget(ps) !== null) {
994
995
  const reference = controlRegistry.get("reference");
995
996
  if (reference) {
package/src/ui/timing.ts CHANGED
@@ -13,16 +13,17 @@
13
13
  */
14
14
 
15
15
  /**
16
- * Text/style input debounce — the §4.4 standard. Long enough that a normal typing cadence produces
17
- * one commit per pause rather than one per keystroke, short enough that the canvas still feels
18
- * live.
16
+ * Text/style input debounce — the studio-ui-guidelines.md §4.4 standard. Long enough that a normal
17
+ * typing cadence produces one commit per pause rather than one per keystroke, short enough that the
18
+ * canvas still feels live.
19
19
  */
20
20
  export const INPUT_DEBOUNCE = 400;
21
21
 
22
22
  /**
23
- * Code and expression textareas — the §4.4 standard. Longer than {@link INPUT_DEBOUNCE} because
24
- * each commit re-parses source (or re-lints), and because half-typed code is usually invalid, so
25
- * committing eagerly just burns work on states the user is about to leave.
23
+ * Code and expression textareas — the studio-ui-guidelines.md §4.4 standard. Longer than
24
+ * {@link INPUT_DEBOUNCE} because each commit re-parses source (or re-lints), and because half-typed
25
+ * code is usually invalid, so committing eagerly just burns work on states the user is about to
26
+ * leave.
26
27
  */
27
28
  export const CODE_DEBOUNCE = 500;
28
29
 
@@ -38,7 +39,8 @@ export const LIVE_PREVIEW = 350;
38
39
  * `STATUS_MESSAGE = 3000` used to live here: how long a transient message stayed in the status bar
39
40
  * before it erased itself. It had one reader, `statusMessage`, which is deleted — the bar carries
40
41
  * ambient state now and outcomes go to `services/notify.ts`, whose `TOAST_LIFETIME_MS` is per
41
- * SEVERITY (§7.1 caps the band at 4–8s) rather than one number for a failure and a save alike.
42
+ * SEVERITY (4s, or 8s for a warning or an error: studio-ui-guidelines.md §13.2) rather than one
43
+ * number for a failure and a save alike.
42
44
  */
43
45
 
44
46
  /**
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * Value Source — the one vocabulary for "how is this value produced", and the derivation that says
3
- * which productions a given document position actually permits (plan §6.3).
3
+ * which productions a given document position actually permits (§6.6).
4
4
  *
5
5
  * Six controls used to answer that question in six dialects: `abc / $ref / ${} / fx` on the dynamic
6
6
  * slot, `inline / $expression / ref` on the Events tab, `lit / $ref / expr` on every expression
7
7
  * operand, and `Static value / route param / Custom…` in the schema-driven forms. They are the same
8
- * four rungs of the Rule of Least Power ladder (spec §2.2) wearing four costumes, and a user who
8
+ * four rungs of the Rule of Least Power ladder (spec.md §2.2) wearing four costumes, and a user who
9
9
  * learned one learned none of the others. {@link VALUE_SOURCE_LABELS} is the single vocabulary;
10
10
  * every surface that offers the choice spells it the same way.
11
11
  *
@@ -48,11 +48,11 @@ import { isFunctionDef, isRef, isTemplateString } from "@jxsuite/schema/guards";
48
48
  /** A rung of the value ladder — one way a document position can produce its value. */
49
49
  export type SlotMode = "literal" | "ref" | "template" | "expression" | "function";
50
50
 
51
- /** Ladder order, least powerful first (spec §2.2). Every rung list is sorted by this. */
51
+ /** Ladder order, least powerful first (spec.md §2.2). Every rung list is sorted by this. */
52
52
  export const SLOT_MODE_ORDER: SlotMode[] = ["literal", "ref", "template", "expression", "function"];
53
53
 
54
54
  /**
55
- * The user-facing name of each rung — the whole point of §6.3. `ref` keeps its ellipsis because
55
+ * The user-facing name of each rung — the whole point of §6.6. `ref` keeps its ellipsis because
56
56
  * choosing it opens a picker rather than committing a value.
57
57
  */
58
58
  export const VALUE_SOURCE_LABELS: Record<SlotMode, string> = {
@@ -9,7 +9,7 @@
9
9
  * project. The two trees pay a smaller version of the same bill — a 5 000-node page drew 5 000
10
10
  * rows, each an `sp-icon` custom element — and they now pay it through this file.
11
11
  *
12
- * **It lives in `ui/`, not in `browse/`.** P3 workstream 11 promised "one virtual-list primitive
12
+ * **It lives in `ui/`, not in `browse/`.** The shell redesign promised "one virtual-list primitive
13
13
  * behind the Files tree and the Outline tree, reused by the Library grid", and what shipped sat
14
14
  * inside the Library and was imported by the Library alone. A primitive two surfaces share cannot
15
15
  * live inside one of them: the next surface that needs it either reaches across a feature boundary
@@ -1,11 +1,14 @@
1
1
  /// <reference lib="dom" />
2
2
  /**
3
- * Base64 encoding for binary upload payloads.
3
+ * Base64 encoding for binary payloads that cross an RPC boundary.
4
4
  *
5
5
  * `StudioPlatform.uploadFile` accepts `string | File | Blob | ArrayBuffer`, but the RPC platforms
6
6
  * (electrobun, chromium) JSON-serialize their params — a `File`/`Blob` becomes `{}` on the wire.
7
7
  * Those platforms encode here before the call; the HTTP platforms (dev server, cloud) post the
8
8
  * binary body directly and must NOT use this.
9
+ *
10
+ * {@link base64ToBytes} is the same boundary in the other direction, for `readFileBytes`: the RPC
11
+ * backends answer a base64 string for the same reason they accept one, and the caller wants bytes.
9
12
  */
10
13
 
11
14
  /** Bytes per `String.fromCodePoint` call — spreading a whole large file blows the call stack. */
@@ -31,3 +34,19 @@ export async function toBase64(data: string | File | Blob | ArrayBuffer): Promis
31
34
  const buffer = data instanceof ArrayBuffer ? data : await data.arrayBuffer();
32
35
  return bytesToBase64(new Uint8Array(buffer));
33
36
  }
37
+
38
+ /**
39
+ * Decode a base64 string to raw bytes — the inverse of {@link bytesToBase64}.
40
+ *
41
+ * The RPC platforms' `readFileBytes` answers base64 because their params and results are JSON, so
42
+ * this is where a JPEG becomes a JPEG again. `atob` yields one character per byte, and reading the
43
+ * code point back out is what keeps a byte above 0x7F from being re-encoded as UTF-8 — the exact
44
+ * corruption that makes `readFile` useless for an image.
45
+ *
46
+ * `Uint8Array.from` with a mapper rather than an index loop, because `atob`'s output is latin1: one
47
+ * code unit per byte, none of them a surrogate, so iterating characters and iterating bytes are the
48
+ * same walk and the index never has to be reasoned about.
49
+ */
50
+ export function base64ToBytes(base64: string): Uint8Array {
51
+ return Uint8Array.from(atob(base64), (char) => char.codePointAt(0) ?? 0);
52
+ }
@@ -5,7 +5,7 @@
5
5
  import { pureSchemeOf } from "@jxsuite/runtime";
6
6
 
7
7
  /**
8
- * True when a feature query participates in the forced-scheme contract (spec §9.5) — a pure
8
+ * True when a feature query participates in the forced-scheme contract (spec.md §9.5) — a pure
9
9
  * prefers-color-scheme query. These surface as the Auto/Light/Dark control, not generic toggles.
10
10
  *
11
11
  * @param {string} query
@@ -76,37 +76,6 @@ export function attrLabel(
76
76
  return camelToLabel(attr);
77
77
  }
78
78
 
79
- /**
80
- * Abbreviate a CSS value for button-group display
81
- *
82
- * @param {string} val
83
- * @returns {string}
84
- */
85
- export function abbreviateValue(val: string) {
86
- const map: Record<string, string> = {
87
- baseline: "base",
88
- column: "col",
89
- "column-reverse": "col-r",
90
- contents: "cnt",
91
- "flex-end": "end",
92
- "flex-start": "start",
93
- "flow-root": "flow",
94
- inline: "inl",
95
- "inline-block": "i-blk",
96
- "inline-flex": "i-flx",
97
- "inline-grid": "i-grd",
98
- normal: "norm",
99
- nowrap: "no-wr",
100
- "row-reverse": "row-r",
101
- "space-around": "arnd",
102
- "space-between": "betw",
103
- "space-evenly": "even",
104
- stretch: "str",
105
- "wrap-reverse": "wr-rev",
106
- };
107
- return map[val] || val;
108
- }
109
-
110
79
  /**
111
80
  * True when a schema `format` names a MEDIA reference.
112
81
  *
package/src/view.ts CHANGED
@@ -23,9 +23,10 @@ import type { Tab } from "./tabs/tab";
23
23
  * different modules, torn down by different events and written into by different continuations —
24
24
  * and each of those differences used to justify its own spelling of the same two ideas ("ignore the
25
25
  * change my own `setValue` is about to fire" and "cancel the work armed over this buffer"). The
26
- * function editor got a canceller in P8 and the source view did not, which is exactly how a 600ms
27
- * timer survived three disposal sites and stayed able to replace a page with an empty parse.
28
- * `services/monaco-buffer.ts` owns the rule; this is the storage it needs.
26
+ * function editor got a canceller in the shell redesign's panes phase and the source view did not,
27
+ * which is exactly how a 600ms timer survived three disposal sites and stayed able to replace a
28
+ * page with an empty parse. `services/monaco-buffer.ts` owns the rule; this is the storage it
29
+ * needs.
29
30
  */
30
31
  /**
31
32
  * A comparison's Monaco diff editor.
@@ -171,10 +171,10 @@ export const PRESET_LABELS: Readonly<Record<DerivePreset, string>> = {
171
171
  diff: "Diff vs HEAD",
172
172
  layout: "Layout",
173
173
  /* An UNFINISHED sentence, like `breakpoint`'s, because the row and the chip both finish it with
174
- the locale's own autonym — "Same page in français". Jx has no message catalogue (§13.3): a
175
- translation is a different file in a different directory, so this preset opens that file
176
- rather than re-rendering this one, and the label says "the same page" about the document
177
- rather than about the text on screen. */
174
+ the locale's own autonym — "Same page in français". Jx has no message catalogue
175
+ (site-architecture.md §13.3): a translation is a different file in a different directory, so
176
+ this preset opens that file rather than re-rendering this one, and the label says "the same
177
+ page" about the document rather than about the text on screen. */
178
178
  locale: "Same page in",
179
179
  };
180
180
 
@@ -1029,9 +1029,9 @@ export function presetRefusal(
1029
1029
  if (i18n === null || i18n.locales.length < 2) {
1030
1030
  return "a project that declares more than one locale — see Project Settings › Locales";
1031
1031
  }
1032
- /* A PATH, because a translation is a FILE (§13.3) rather than a rendering: an unsaved document
1033
- has nowhere for its sibling to be, and `translationPathFor` would answer null for it one
1034
- frame later with a sentence about locales instead of about saving. */
1032
+ /* A PATH, because a translation is a FILE (site-architecture.md §13.3) rather than a rendering:
1033
+ an unsaved document has nowhere for its sibling to be, and `translationPathFor` would answer
1034
+ null for it one frame later with a sentence about locales instead of about saving. */
1035
1035
  if (source.documentPath === null) {
1036
1036
  return "a document that has been saved";
1037
1037
  }
@@ -1082,10 +1082,11 @@ function derivationFor(
1082
1082
  media: string | null,
1083
1083
  locale: string | null = null,
1084
1084
  ): PaneDerivation {
1085
- /* A COMPANION, because the French copy of this page is a different FILE — §13.3 has no message
1086
- catalogue, so there is no rendering of this document that is the French one. A lens here would
1087
- be a second copy of the same page under a chip naming another language, which is the defect
1088
- this module's own header warns about: a projection that changes only the chip. */
1085
+ /* A COMPANION, because the French copy of this page is a different FILE — Jx has no message
1086
+ catalogue (site-architecture.md §13.3), so there is no rendering of this document that is the
1087
+ French one. A lens here would be a second copy of the same page under a chip naming another
1088
+ language, which is the defect this module's own header warns about: a projection that changes
1089
+ only the chip. */
1089
1090
  if (preset === "locale") {
1090
1091
  return {
1091
1092
  kind: "companion",
@@ -1213,8 +1214,8 @@ export function derivationCommands(deps: DerivationDeps): AnyCommand[] {
1213
1214
  enablement: () => deriveRefusal(activePane().id) === null,
1214
1215
  requires: DERIVE_REQUIRES,
1215
1216
  undo: "none",
1216
- /* No `aiTool`, by §12.4's first deletion rule: chrome — it arranges what the person is
1217
- looking at. */
1217
+ /* No `aiTool`, by studio-ui-guidelines.md §12.4's first deletion rule: chrome — it arranges
1218
+ what the person is looking at. */
1218
1219
  run: (_ctx, args) => {
1219
1220
  const preset = enumArg("pane.derive", args, "preset", DERIVE_PRESETS);
1220
1221
  /* `??`, not `||`. `optionalStringArg` answers `undefined` or a non-empty string — it is
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * Session.ts — what a project looked like when you left it, and how it looks that way again.
3
3
  *
4
- * Plan §4.4: "Session state persists **per project root** … open tabs and order per pane, active
5
- * tab, per-tab editor kind / view / zoom / rendering context." P3's "Newly possible" states the
6
- * consequence plainly: **the session survives a relaunch.** Neither shipped. The per-project record
7
- * held `layouts` and `activeLayout` and nothing else, its interface said "Session state (§4.4)
8
- * grows into this shape", and reopening a project landed on the home page with an empty strip — so
9
- * nine open documents, a split, and the breakpoint you were checking were lost every time.
4
+ * The shell redesign promised that session state persists **per project root** — open tabs and
5
+ * order per pane, active tab, per-tab editor kind / view / zoom / rendering context — and stated
6
+ * the consequence plainly: **the session survives a relaunch** (§14.8). Neither shipped. The
7
+ * per-project record held `layouts` and `activeLayout` and nothing else, its interface said session
8
+ * state would "grow into this shape", and reopening a project landed on the home page with an empty
9
+ * strip — so nine open documents, a split, and the breakpoint you were checking were lost every
10
+ * time.
10
11
  *
11
12
  * **Paths, not tab ids.** A tab id is minted per open and means nothing across a reload; the
12
13
  * document's path is the identity the workspace already dedupes on (`openFileInTab` scans
@@ -1,5 +1,6 @@
1
1
  import { computed, reactive, toRaw } from "../reactivity";
2
2
  import { commitTabBuffers } from "../services/monaco-buffer";
3
+ import { resetAiWrites } from "../services/ai-writes";
3
4
  import { ensureCollab, rekeyCollab } from "../collab/collab-session";
4
5
  import { createTab, disposeTab } from "../tabs/tab";
5
6
  import { projectState } from "../state";
@@ -12,7 +13,7 @@ import {
12
13
  } from "../commands/command-args";
13
14
  import { EDITOR_KIND_LABELS } from "../commands/context";
14
15
  import type { Tab, TabOrigin } from "../tabs/tab";
15
- import type { JsonLayout } from "../files/json-layout";
16
+ import type { JsonLayout } from "@jxsuite/schema/json-layout";
16
17
 
17
18
  import type { ComponentEntry } from "../files/components";
18
19
  import type { AnyCommand, CommandRegistry } from "../commands/registry";
@@ -35,11 +36,11 @@ export interface ClosedTabRecord {
35
36
  const CLOSED_TAB_LIMIT = 20;
36
37
 
37
38
  /**
38
- * One editor pane — the unit of split, focus and zoom (§4.1).
39
+ * One editor pane — the unit of split, focus and zoom (§18.1).
39
40
  *
40
41
  * A tab BELONGS to a pane, and the pane is what splits; the tab strip is per-pane for the same
41
- * reason. This is the minimal model P3 asks for: an ordered list and an active id, with no rules
42
- * for derived panes and no follow behaviour (those are P8).
42
+ * reason. The model is deliberately minimal, an ordered list and an active id; derived panes and
43
+ * their follow behaviour (§18.4) sit beside it in `derived` rather than in either.
43
44
  */
44
45
  export interface Pane {
45
46
  id: string;
@@ -357,10 +358,10 @@ export function focusOtherPane() {
357
358
  * `session.ui.canvasMode`), one picked the replacement kind, and two narrowed what the controls
358
359
  * offered so no dropdown could contain an entry the app would refuse.
359
360
  *
360
- * The cap existed because a second LIVE canvas host was unaffordable, and it is gone because
361
- * workstream 1 made it affordable — see {@link MAX_PANES}. Its LAST act was a bug: `capToPaneKind`
362
- * flipped a splitting Design tab to `source` before focus moved, so `⌘\` on a page you were
363
- * designing silently became "open this as Code somewhere else".
361
+ * The cap existed because a second LIVE canvas host was unaffordable, and it is gone because render
362
+ * fan-out (§18.2) made it affordable — see {@link MAX_PANES}. Its LAST act was a bug:
363
+ * `capToPaneKind` flipped a splitting Design tab to `source` before focus moved, so `⌘\` on a page
364
+ * you were designing silently became "open this as Code somewhere else".
364
365
  *
365
366
  * What replaces them is `tabs/tab.ts`'s `editorKindsOf` — the kinds a DOCUMENT declares — which is
366
367
  * the only narrowing left and the only one that was ever about the document rather than about what
@@ -371,7 +372,7 @@ export function focusOtherPane() {
371
372
  * Split the grid and move a tab into the new pane, which becomes focused.
372
373
  *
373
374
  * Moves rather than duplicates: one tab is one document with one undo history and one collab
374
- * session, and two strips claiming the same id is the duplicate-`repeat`-key bug §4.3 describes.
375
+ * session, and two strips claiming the same id is the duplicate-`repeat`-key bug §14.1 describes.
375
376
  *
376
377
  * The subject is `tabId` when given, else the focused pane's active tab. The drag resolver names
377
378
  * the tab it is carrying — the gesture points at a chip, not at whatever the keyboard is in — and
@@ -607,11 +608,11 @@ export function paneIsEmpty(pane: Pane): boolean {
607
608
  return pane.tabOrder.length === 0 && pane.derived === null;
608
609
  }
609
610
 
610
- /* No `togglePaneZoom`. Zoom is a view of a GRID, and there is no grid: §18.3 hands the one stage
611
- between panes, so the focused pane already fills the shell and the other one is not on screen to
612
- be zoomed away from. The state existed, both commands wrote it, and nothing that draws ever read
613
- it — a control whose only observable effect is on itself. It comes back with the second live
614
- host, and not one release earlier. */
611
+ /* No `togglePaneZoom`. It was removed when the shell still handed one stage between panes: the
612
+ focused pane already filled the shell, so the state existed, both commands wrote it, and nothing
613
+ that draws ever read it — a control whose only observable effect is on itself. There is a grid of
614
+ live panes now (§18.3), so a zoom is buildable, but no section of studio.md asks for one; it
615
+ comes back with the section that does, and not one release earlier. */
615
616
 
616
617
  /**
617
618
  * Remove a tab id from whichever pane holds it, without touching the tab itself.
@@ -696,6 +697,13 @@ function defaultSlot(pane: Pane, tabId: string): number {
696
697
  * @param {object | null} [config]
697
698
  */
698
699
  export function setWorkspaceProject(root: string | null, config: object | null = null) {
700
+ /* Leaving a project drops the assistant's record of what each turn changed (ai.md §3.2). It
701
+ names project-relative paths, and its Restore undoes the active tab, so carried into another
702
+ project it would list files that are not there and undo a document the turn never touched.
703
+ Adopting a project from none keeps it: a bootstrap conversation follows the project it made. */
704
+ if (workspace.projectRoot && root !== workspace.projectRoot) {
705
+ resetAiWrites();
706
+ }
699
707
  workspace.projectRoot = root;
700
708
  workspace.projectConfig = config;
701
709
  }
@@ -820,12 +828,12 @@ function setActiveTab(tabId: string, opts: { cycling?: boolean; focus?: boolean
820
828
  * keys. Callers that mean "show me this file" should use `files/files.ts`'s `openFileInTab`, which
821
829
  * activates the existing tab instead.
822
830
  *
823
- * `preview: true` opens a DISPOSABLE tab (§4.3): single-clicking through the tree or the palette is
824
- * browsing, and browsing must not litter, so the next preview open in the same pane takes its slot.
825
- * It is opt-in rather than the default because only the CALLER knows whether the author was
826
- * browsing or committing — an author who typed a new file name has committed, and silently
827
- * discarding that tab on their next click is the one failure a preview tab must never have.
828
- * {@link promoteTab} records a commitment made after the fact.
831
+ * `preview: true` opens a DISPOSABLE tab (docs/studio/interface/tabs.md): single-clicking through
832
+ * the tree or the palette is browsing, and browsing must not litter, so the next preview open in
833
+ * the same pane takes its slot. It is opt-in rather than the default because only the CALLER knows
834
+ * whether the author was browsing or committing — an author who typed a new file name has
835
+ * committed, and silently discarding that tab on their next click is the one failure a preview tab
836
+ * must never have. {@link promoteTab} records a commitment made after the fact.
829
837
  *
830
838
  * `paneId` says WHERE, and `focus` says whether the keyboard goes with it. Both default to today's
831
839
  * answer — the focused pane, and yes — because "open this document" has always meant "in front of
@@ -1383,12 +1391,12 @@ export function tabCommands(deps: TabCommandDeps): AnyCommand[] {
1383
1391
  "Use it when the user should SEE the page, or for iterative visual refinement after " +
1384
1392
  "creating a page or component.",
1385
1393
  name: "open_document",
1386
- /* The person's own read: `activeTab` is what the tab strip highlights, and `editor.kind`
1387
- is the fact the document-tree tier is gated on. Both are named so the model learns in one
1394
+ /* The person's own read: `activeTab` is what the tab strip highlights, and `editor.kind` is
1395
+ the fact the document-tree tier is gated on. Both are named so the model learns in one
1388
1396
  round whether the tree tools reach the file it opened — a `.csv` lands in the Grid, a
1389
1397
  `.png` in the Media viewer, and neither is an element tree. The `.value` read is module
1390
- state, which a projected `report` may close over (§12.4); `after` alone cannot say WHICH
1391
- document is active. */
1398
+ state, which a projected `report` may close over (studio-ui-guidelines.md §12.4); `after`
1399
+ alone cannot say WHICH document is active. */
1392
1400
  report: ({ after, args }) => {
1393
1401
  const path = stringArg("document.open", args, "path");
1394
1402
  const active = activeTab.value?.documentPath ?? null;
@@ -1402,11 +1410,12 @@ export function tabCommands(deps: TabCommandDeps): AnyCommand[] {
1402
1410
  return `"${path}" is the active document, ${where}.`;
1403
1411
  },
1404
1412
  },
1405
- /* Refuses by THROWING (§12.4's first review rule). `openFileInTab` reports a file it cannot
1406
- open as a Problem and returns normally — the right answer for a click in the Files tree,
1407
- where the toast is in front of the person — so the record has to look for the tab itself.
1408
- The hand tool this replaced read `getTab()` after the open and called whatever it found a
1409
- success, so a missing file left the PREVIOUS document active and reported "Switched to". */
1413
+ /* Refuses by THROWING (studio-ui-guidelines.md §12.4's first review rule). `openFileInTab`
1414
+ reports a file it cannot open as a Problem and returns normally — the right answer for a
1415
+ click in the Files tree, where the toast is in front of the person — so the record has to
1416
+ look for the tab itself. The hand tool this replaced read `getTab()` after the open and
1417
+ called whatever it found a success, so a missing file left the PREVIOUS document active and
1418
+ reported "Switched to". */
1410
1419
  run: async (_ctx, args) => {
1411
1420
  const path = stringArg("document.open", args, "path");
1412
1421
  await deps.openFile(path);
@@ -1435,8 +1444,9 @@ export function tabCommands(deps: TabCommandDeps): AnyCommand[] {
1435
1444
  group: "1_file",
1436
1445
  when: (ctx) => ctx.project.open,
1437
1446
  requires: "an open project",
1438
- /* No `aiTool`, by §12.4's first deletion rule: chrome — it arranges what the person is
1439
- looking at. No `undo`: opening a tab is not a state the undo stack owns. */
1447
+ /* No `aiTool`, by studio-ui-guidelines.md §12.4's first deletion rule: chrome — it arranges
1448
+ what the person is looking at. No `undo`: opening a tab is not a state the undo stack
1449
+ owns. */
1440
1450
  undo: "none",
1441
1451
  /* The non-drag equivalent of dragging a file row onto the other pane's strip (SC 2.5.7): the
1442
1452
  same `receivingPane` the drag resolver uses, so a file tree row and this command can never
@@ -1550,12 +1560,12 @@ export function tabCommands(deps: TabCommandDeps): AnyCommand[] {
1550
1560
  * The pane commands.
1551
1561
  *
1552
1562
  * They live beside the pane model for the same reason the tab commands do, and they are the whole
1553
- * user-facing surface of §4.1's Pane transport: split, focus, unsplit, zoom.
1563
+ * user-facing surface of the pane transport (§18.1): split, focus, unsplit, zoom.
1554
1564
  *
1555
1565
  * `⌘0` and `⌘⌥0` are a PAIR, and they are both bound. `⌘0` was `canvas.zoomReset`, which held it
1556
- * for the one verb of the zoom cluster that also has a button in the floating zoom pod (§3.2 ⑩) —
1557
- * so the chord was the second way to reach a control that is already on screen, while focusing a
1558
- * pane had no way at all. The zoom keeps its pod button and gives up the key.
1566
+ * for the one verb of the zoom cluster that also has a button in the floating zoom pod — so the
1567
+ * chord was the second way to reach a control that is already on screen, while focusing a pane had
1568
+ * no way at all. The zoom keeps its pod button and gives up the key.
1559
1569
  *
1560
1570
  * @param {TabCommandDeps} deps
1561
1571
  * @returns {AnyCommand[]}
@@ -1604,13 +1614,13 @@ export function paneCommands(deps: TabCommandDeps): AnyCommand[] {
1604
1614
  /* This is NOT `pane.derive`, and the distinction is the whole reason both exist. `derive`
1605
1615
  shows a projection OF THIS PANE — its Code, its diff, the same page at another breakpoint —
1606
1616
  and follows it. `compareWith` puts THAT DOCUMENT beside this one and then leaves it alone.
1607
- Collapsing them would give "open a second document in the other pane" two definition
1608
- sites, which is how the two names in §4.1 came to disagree in the first place. */
1617
+ Collapsing them would give "open a second document in the other pane" two definition sites,
1618
+ which is how the shell redesign's two names for it came to disagree in the first place. */
1609
1619
  enablement: () => workspace.activeTabId !== null,
1610
1620
  requires: "an open document",
1611
1621
  undo: "none",
1612
- /* No `aiTool`, by §12.4's first deletion rule: chrome — it arranges what the person is
1613
- looking at. */
1622
+ /* No `aiTool`, by studio-ui-guidelines.md §12.4's first deletion rule: chrome — it arranges
1623
+ what the person is looking at. */
1614
1624
  run: async (_ctx, args) => {
1615
1625
  const path = stringArg("pane.compareWith", args, "path");
1616
1626
  const here = activePane();
package/styles/canvas.css CHANGED
@@ -18,32 +18,6 @@
18
18
  min-height: 0;
19
19
  }
20
20
 
21
- /* ─── Document Header card ─────────────────────
22
- Region ⑧: the artefact's own header, drawn IN the stage. `#frontmatter-panel` — a fourth
23
- full-width band up to 40vh that appeared and vanished as a side effect of the canvas mode — is
24
- gone, grid row and all.
25
-
26
- Two placements, because the stage draws a page two ways. In Edit the card is `in-column`: a block
27
- of the document's own centered column that scrolls with it, so it is part of the artefact rather
28
- than a strip above it. In Design the artboards are under a pan/zoom transform, and a form drawn
29
- at 40% is a picture of a form — so the card is `pinned` above the surface at 1:1, and that is the
30
- one placement that still needs a cap. */
31
- .doc-header-host[hidden] {
32
- display: none;
33
- }
34
- .doc-header-host.in-column {
35
- display: block;
36
- padding-bottom: 16px;
37
- }
38
- .doc-header-host.pinned {
39
- flex-shrink: 0;
40
- max-height: 40vh;
41
- overflow-y: auto;
42
- }
43
- .doc-header-host.pinned .doc-header {
44
- border-width: 0 0 1px;
45
- border-radius: 0;
46
- }
47
21
  /* ─── Content edit mode canvas ─────────────── */
48
22
  .content-edit-canvas {
49
23
  width: 100%;
@@ -201,7 +175,7 @@
201
175
  .overlay-coselection-group {
202
176
  display: contents;
203
177
  }
204
- /* The other members of a multi-selection (§6.5). Same accent, thinner: one box is the primary —
178
+ /* The other members of a multi-selection (§6.7). Same accent, thinner: one box is the primary —
205
179
  what the Inspector, the block action bar and the status bar address — and the rest are along for
206
180
  the batch. A selection of one draws no co-selection boxes at all. */
207
181
  .overlay-coselection {