@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
@@ -43,8 +43,7 @@ import {
43
43
  } from "../shell";
44
44
  import type { DockId } from "../shell";
45
45
  import { effect } from "../reactivity";
46
- import { rectOf } from "../utils/geometry";
47
- import { splitModifiersOf } from "@jxsuite/ui/behaviors/split";
46
+ import { splitModifiersOf, trackOf } from "@jxsuite/ui/behaviors/split";
48
47
  import type { SplitModifiers } from "@jxsuite/ui/behaviors/split";
49
48
 
50
49
  /**
@@ -59,11 +58,11 @@ import type { SplitModifiers } from "@jxsuite/ui/behaviors/split";
59
58
  *
60
59
  * **There is no pointer path left in this module**, and it is worth recording why rather than
61
60
  * quietly dropping it: a handle driven by pointer events alone is unreachable from a keyboard,
62
- * which is an SC 2.1.1 failure however it is drawn. The pane split moved to `jx-split` first (ui.md
63
- * §5.5), the three docks followed, and the Edit column was last because its handle SNAPS — and the
64
- * element had no way to tell a host which modifiers a step carried. It has now: every `input`
65
- * carries them, so a snap lives in {@link bindSplit} rather than in a pointer handler of this
66
- * module's own.
61
+ * which is an SC 2.1.1 failure however it is drawn. The pane split moved to `jx-split` first
62
+ * (`ui.md` §5.5), the three docks followed, and the Edit column was last because its handle SNAPS —
63
+ * and the element had no way to tell a host which modifiers a step carried. It has now: every
64
+ * `input` carries them, so a snap lives in {@link bindSplit} rather than in a pointer handler of
65
+ * this module's own.
67
66
  */
68
67
  export interface ResizeTarget {
69
68
  /** Which coordinate the drag reads. */
@@ -160,25 +159,6 @@ export interface SplitElement extends HTMLElement {
160
159
  collapse: number;
161
160
  }
162
161
 
163
- /**
164
- * The length of the box a handle divides, along its axis — the same box `jx-split` measures at
165
- * pointerdown, read here so the share this adapter writes and the one the element drags against are
166
- * two readings of one number.
167
- */
168
- function trackOf(handle: HTMLElement, axis: "x" | "y"): number {
169
- for (let node = handle.parentElement; node; node = node.parentElement) {
170
- if (globalThis.getComputedStyle(node).display === "contents") {
171
- continue;
172
- }
173
- const box = rectOf(node);
174
- const length = axis === "x" ? box.width : box.height;
175
- if (length > 0) {
176
- return length;
177
- }
178
- }
179
- return 0;
180
- }
181
-
182
162
  /**
183
163
  * A target's pixel size as the leading side's share of the track, and back.
184
164
  *
@@ -231,7 +211,7 @@ function sizeOf(target: ResizeTarget, share: number, track: number): number {
231
211
  */
232
212
  export function bindSplit(handle: SplitElement, target: ResizeTarget): () => void {
233
213
  const sync = (): void => {
234
- const track = trackOf(handle, target.axis);
214
+ const track = trackOf(handle, target.axis === "x");
235
215
  if (track <= 0) {
236
216
  return;
237
217
  }
@@ -243,7 +223,7 @@ export function bindSplit(handle: SplitElement, target: ResizeTarget): () => voi
243
223
  handle.value = shareOf(target, target.read(), track);
244
224
  };
245
225
  const onInput = (event: Event): void => {
246
- const track = trackOf(handle, target.axis);
226
+ const track = trackOf(handle, target.axis === "x");
247
227
  if (track <= 0) {
248
228
  return;
249
229
  }
@@ -66,8 +66,8 @@ export interface OpenPopoverSurfaceOptions {
66
66
  /** The platform closed it — clicked outside, Escape, or a second press on the invoker. */
67
67
  onDismissed?: () => void;
68
68
  /**
69
- * A node the document made exists. The one seam an island reaches its host through (§9.4), passed
70
- * straight to the runtime.
69
+ * A node the document made exists. The one seam an island reaches its host through
70
+ * (studio-ui-guidelines.md §9.4), passed straight to the runtime.
71
71
  */
72
72
  onNodeCreated?: JxMountOptions["onNodeCreated"];
73
73
  }
@@ -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
@@ -300,6 +300,44 @@ export function nearestScroller(el: HTMLElement | null): HTMLElement | null {
300
300
  return null;
301
301
  }
302
302
 
303
+ /**
304
+ * The scroller that resolved for `list` last time, memoized while the tree still holds it.
305
+ *
306
+ * Every render pass asks {@link nearestScroller} the same question about the same list, and each
307
+ * ask is a walk whose reads (scrollHeight, clientHeight, computed overflow) land on a tree that is
308
+ * mid-paint — they force the flush the DevTools timeline charges to whatever function happened to
309
+ * read first. Profiling the program boot put 400 ms of style recalculation behind a handful of
310
+ * these walks (`trackOf` and this one together), and the answer did not change between any two
311
+ * repaints.
312
+ *
313
+ * Only a POSITIVE resolution is cached, and negative answers are never: a list that has not grown
314
+ * its rows yet has nothing to scroll (the first paint of every session), and a null answer memoized
315
+ * at that instant would never be revisited. Once a scroller is found it is kept while
316
+ * `cached.contains(list)` and both are connected — containment and connectivity are plain node
317
+ * walks that never read layout, so the cache invalidates itself on reparenting or teardown without
318
+ * paying for it.
319
+ *
320
+ * **The one semantic residue is deliberate**: the original walk additionally required the scroller
321
+ * to HAVE something to scroll right then (the "declares overflow but never scrolls" guard above). A
322
+ * memoized scroller that has since stopped scrolling stays answered — which is harmless for the
323
+ * window: `computeWindow` then yields the whole list (a list that fits has no window to cut), and
324
+ * the watch's own ResizeObserver drives a measure when the box moves so the empty answer of the
325
+ * next moment arrives without a walk.
326
+ */
327
+ export function scrollerFor(list: HTMLElement): HTMLElement | null {
328
+ const cached = scrollerCache.get(list);
329
+ if (cached && cached.isConnected && list.isConnected && cached.contains(list)) {
330
+ return cached;
331
+ }
332
+ const found = nearestScroller(list);
333
+ if (found) {
334
+ scrollerCache.set(list, found);
335
+ } else {
336
+ scrollerCache.delete(list);
337
+ }
338
+ return found;
339
+ }
340
+
303
341
  /**
304
342
  * How far `list`'s first row has scrolled above the top of `scroller`'s viewport.
305
343
  *
@@ -327,7 +365,7 @@ export function listWindow(
327
365
  list: HTMLElement | null,
328
366
  spec: { count: number; rowHeight: number; columns?: number; overscanRows?: number },
329
367
  ): WindowRange {
330
- const scroller = list?.isConnected === true ? nearestScroller(list) : null;
368
+ const scroller = list?.isConnected === true ? scrollerFor(list) : null;
331
369
  if (!scroller || !list) {
332
370
  const count = Math.max(0, Math.trunc(spec.count));
333
371
  return { end: count, padBottom: 0, padTop: 0, start: 0, totalRows: count };
@@ -377,7 +415,7 @@ export function scrollTopToReveal(spec: {
377
415
  * would paint twice for one keystroke.
378
416
  */
379
417
  export function revealListRow(list: HTMLElement | null, index: number, rowHeight: number): boolean {
380
- const scroller = list?.isConnected === true ? nearestScroller(list) : null;
418
+ const scroller = list?.isConnected === true ? scrollerFor(list) : null;
381
419
  if (!scroller || !list || index < 0 || !(rowHeight > 0)) {
382
420
  return false;
383
421
  }
@@ -437,9 +475,24 @@ export interface ListWindowWatch {
437
475
  window: VirtualWindow;
438
476
  }
439
477
 
478
+ const scrollerCache = new WeakMap<HTMLElement, HTMLElement>();
479
+
440
480
  /**
441
481
  * Bind — or keep — the scroll watch that repaints a windowed list.
442
482
  *
483
+ * Runs synchronously, on purpose. Deferring the resolve-and-bind to the next animation frame was
484
+ * tried and measured (`scripts/perf/REPORT.md`, "deferring the first measure a frame"): it moved
485
+ * the boot trace's forced-flush cost rather than removing it, since the layout work still runs on
486
+ * the frame's own pass either way, and it cost eight test-semantics changes in this file's test
487
+ * suite for the same totals — so it was reverted. What actually removed the repeated flush is
488
+ * `scrollerFor`'s identity memoization (alongside `trackOf`'s, in `split.ts`): once a scroller is
489
+ * resolved for a list, later calls skip the ancestor walk entirely instead of re-reading
490
+ * `scrollHeight`/`clientHeight` on a dirty tree every render.
491
+ *
492
+ * A steady state where the SAME list and scroller are bound hands back `previous` unchanged — no
493
+ * work happens when nothing changed. Only a rebind (new list, changed scroller, first bind) creates
494
+ * a new watch.
495
+ *
443
496
  * Called from the surface's `afterRender`, where the rows exist and the scroller can be resolved,
444
497
  * and handed back its own previous handle so re-binding is the exception rather than the rule: the
445
498
  * tree element survives every re-render (lit reuses it), so the steady state is "same list, same
@@ -450,14 +503,14 @@ export interface ListWindowWatch {
450
503
  * the element whose box moves when the ROWS change height, so it is what the window observes to
451
504
  * learn that `rowHeight()` now answers differently ({@link createVirtualWindow}).
452
505
  *
453
- * Returns the handle to keep, or null when nothing scrolls this list.
506
+ * @returns The handle to keep, or null when nothing scrolls this list.
454
507
  */
455
508
  export function watchListWindow(
456
509
  previous: ListWindowWatch | null,
457
510
  list: HTMLElement,
458
511
  spec: { count: () => number; rowHeight: () => number; onChange: () => void },
459
512
  ): ListWindowWatch | null {
460
- const scroller = nearestScroller(list);
513
+ const scroller = scrollerFor(list);
461
514
  if (previous && previous.list === list && previous.scroller === scroller) {
462
515
  return previous;
463
516
  }
@@ -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