@jxsuite/studio 4.1.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (612) hide show
  1. package/README.md +1 -1
  2. package/data/elements-meta.json +5 -15
  3. package/data/html-meta.json +51 -6
  4. package/dist/chunks/{2020-n6tfwnda.js → 2020-g79n01v9.js} +14 -14
  5. package/dist/chunks/action-group-sp6tna8v.js +118 -0
  6. package/dist/chunks/adapter-nj7tnjfn.js +13 -0
  7. package/dist/chunks/cf-account-picker-qwkm9zrf.js +495 -0
  8. package/dist/chunks/cf-settings-71bv5zn9.js +20 -0
  9. package/dist/chunks/color-area-v17df7b8.js +154 -0
  10. package/dist/chunks/color-field-anwybnkn.js +208 -0
  11. package/dist/chunks/color-slider-44ez96fc.js +79 -0
  12. package/dist/chunks/color-xgg3kfw7.js +35 -0
  13. package/dist/chunks/combobox-5e7jev23.js +236 -0
  14. package/dist/chunks/components-vj72gqtq.js +20 -0
  15. package/dist/chunks/content-source-5mex4x1h.js +46 -0
  16. package/dist/chunks/convert-to-repeater-n64h8znc.js +639 -0
  17. package/dist/chunks/dialog-s890b918.js +85 -0
  18. package/dist/chunks/editor-3qhbg51x.js +35 -0
  19. package/dist/chunks/entry-commands-234hjnqw.js +42 -0
  20. package/dist/chunks/extension-sections-3hpcambp.js +651 -0
  21. package/dist/chunks/file-ops-8yhd83xr.js +47 -0
  22. package/dist/chunks/files-hnxqxtsg.js +72 -0
  23. package/dist/chunks/format-host-779sfp2n.js +40 -0
  24. package/dist/chunks/fs-events-b88z7g4h.js +18 -0
  25. package/dist/chunks/git-panel-xwj8fpab.js +49 -0
  26. package/dist/chunks/icons-yy10wk9r.js +402 -0
  27. package/dist/chunks/iframe-entry-15g3n6xv.js +114 -0
  28. package/dist/chunks/iframe-entry-b27vq81n.js +203 -0
  29. package/dist/chunks/iframe-entry-nbyxee1y.js +154 -0
  30. package/dist/chunks/iframe-host-syr0f9sb.js +115 -0
  31. package/dist/chunks/{index-wcnt3r9g.js → index-mdmyx6s2.js} +11 -11
  32. package/dist/chunks/index-n4j7qxyr.js +109 -0
  33. package/dist/chunks/{javascript-q8kpmfah.js → javascript-xvrfrpkb.js} +8 -9
  34. package/dist/chunks/{jsonMode-dkr040ga.js → jsonMode-tyx2kj6p.js} +526 -527
  35. package/dist/chunks/jx-validate-j0wxk72w.js +15 -0
  36. package/dist/chunks/layers-ff81wthn.js +30 -0
  37. package/dist/chunks/layers-panel-pnnghw12.js +40 -0
  38. package/dist/chunks/listbox-6v2ac08j.js +44 -0
  39. package/dist/chunks/lit-html-m244w9nh.js +17 -0
  40. package/dist/chunks/media-picker-cqhhxhhe.js +33 -0
  41. package/dist/chunks/menu-9p2w46gj.js +15 -0
  42. package/dist/chunks/menu-q99sy9rf.js +285 -0
  43. package/dist/chunks/{monaco-binding-rj6xpx9y.js → monaco-binding-sw6en4dw.js} +12 -15
  44. package/dist/chunks/monaco-buffer-bhy3hb6m.js +24 -0
  45. package/dist/chunks/{monaco-setup-vx9bhp1d.js → monaco-setup-8r62fj6y.js} +27 -37
  46. package/dist/chunks/notify-628t0anh.js +21 -0
  47. package/dist/chunks/number-field-5cndyft8.js +72 -0
  48. package/dist/chunks/parse-0zx14y15.js +10 -0
  49. package/dist/chunks/platform-ts9cg1a4.js +15 -0
  50. package/dist/chunks/popover-47z3ytva.js +25 -0
  51. package/dist/chunks/publish-panel-5zt840sy.js +27 -0
  52. package/dist/chunks/quick-search-3748564r.js +45 -0
  53. package/dist/chunks/references-qvvxej7h.js +26 -0
  54. package/dist/chunks/right-panel-eccz92qt.js +45 -0
  55. package/dist/chunks/section-registry-5pq32t0q.js +25 -0
  56. package/dist/chunks/select-2r16ez41.js +49 -0
  57. package/dist/chunks/split-g7nm1rgb.js +191 -0
  58. package/dist/chunks/studio-0hqbpbd0.js +62 -0
  59. package/dist/chunks/{studio-kryvsw6j.js → studio-1k325pwf.js} +48 -48
  60. package/dist/chunks/{studio-b55y6ewx.js → studio-1knfqgy7.js} +58 -58
  61. package/dist/chunks/{studio-2b946z26.js → studio-1z1kcjv4.js} +3628 -6095
  62. package/dist/chunks/studio-3773086z.js +111 -0
  63. package/dist/chunks/{studio-4r1pv6zf.js → studio-38emantv.js} +22 -5
  64. package/dist/chunks/{studio-x813zp48.js → studio-3rz5md06.js} +30 -30
  65. package/dist/chunks/{studio-ynxwkrzd.js → studio-4wbb9ct0.js} +1953 -1785
  66. package/dist/chunks/studio-5te5tqng.js +81 -0
  67. package/dist/chunks/studio-5wt9nvhd.js +4763 -0
  68. package/dist/chunks/{studio-p4ngc046.js → studio-6ewqv390.js} +9 -9
  69. package/dist/chunks/{studio-9ab8gqsm.js → studio-6nzaktvy.js} +7 -7
  70. package/dist/chunks/studio-6xa21ej9.js +28 -0
  71. package/dist/chunks/{studio-5ehhackm.js → studio-727xh0e3.js} +19 -19
  72. package/dist/chunks/{studio-gcye7qka.js → studio-7e11jpa9.js} +21 -21
  73. package/dist/chunks/{studio-q7v10cg2.js → studio-7r42c6ca.js} +17 -7
  74. package/dist/chunks/studio-7xnmgv66.js +13615 -0
  75. package/dist/chunks/studio-8scdew0e.js +258 -0
  76. package/dist/chunks/{studio-fbf92ckf.js → studio-93zafjcm.js} +275 -163
  77. package/dist/chunks/{studio-dg7zyt5x.js → studio-98b3z07n.js} +1 -1
  78. package/dist/chunks/studio-a79k90n9.js +1162 -0
  79. package/dist/chunks/studio-aj37eer3.js +422 -0
  80. package/dist/chunks/studio-b0k5tqgp.js +2432 -0
  81. package/dist/chunks/studio-b4s9eykd.js +2811 -0
  82. package/dist/chunks/{studio-f3wqjtk5.js → studio-d0300636.js} +5076 -18
  83. package/dist/chunks/{studio-b9b6t1dg.js → studio-e1f24eff.js} +5664 -5666
  84. package/dist/chunks/studio-e1pgga3k.js +839 -0
  85. package/dist/chunks/{studio-g1wdche0.js → studio-esrxgwh6.js} +80 -80
  86. package/dist/chunks/{studio-q43a5y9h.js → studio-g3jevyjt.js} +16 -16
  87. package/dist/chunks/studio-gb13095a.js +2806 -0
  88. package/dist/chunks/{studio-3n4ndrzh.js → studio-h4nvnjvz.js} +303 -303
  89. package/dist/chunks/{studio-dm19q43r.js → studio-hhvs4sdg.js} +10 -10
  90. package/dist/chunks/{studio-x4qkdat5.js → studio-js8nv53n.js} +1749 -2481
  91. package/dist/chunks/{studio-qjy2z9bv.js → studio-jxp12rnj.js} +32 -137
  92. package/dist/chunks/{studio-trxrabqf.js → studio-k75n7vhp.js} +527 -283
  93. package/dist/chunks/{studio-q9jd1a60.js → studio-kcgff4r0.js} +15 -18
  94. package/dist/chunks/studio-kkqkcp3d.js +17668 -0
  95. package/dist/chunks/studio-mhhyamxf.js +763 -0
  96. package/dist/chunks/{studio-cft9r4r7.js → studio-nq5yf5mp.js} +22 -22
  97. package/dist/chunks/{studio-685s0ytq.js → studio-nqgawfks.js} +572 -572
  98. package/dist/chunks/studio-nzzpvzx9.js +185 -0
  99. package/dist/chunks/{studio-xrapxwwc.js → studio-p1dw8wfw.js} +55 -29
  100. package/dist/chunks/{studio-3bqpr0zh.js → studio-pxbgypmg.js} +299 -110
  101. package/dist/chunks/studio-q0a3yk4n.js +33 -0
  102. package/dist/chunks/{studio-wcy8trfx.js → studio-q3x06vmj.js} +23 -22
  103. package/dist/chunks/{studio-6yw4y3my.js → studio-qhhm6hbt.js} +8 -8
  104. package/dist/chunks/studio-qk4bfsq2.js +362 -0
  105. package/dist/chunks/{studio-msd847zn.js → studio-qk90fgqm.js} +361 -90
  106. package/dist/chunks/{studio-jkftxyvz.js → studio-r6nvacqs.js} +5861 -2154
  107. package/dist/chunks/{studio-53n8bp04.js → studio-r6xmmtfh.js} +1 -1
  108. package/dist/chunks/{studio-tvgyw9xw.js → studio-rnyf3f8e.js} +83 -250
  109. package/dist/chunks/studio-rs76k67r.js +12 -0
  110. package/dist/chunks/studio-sbjyc8bt.js +954 -0
  111. package/dist/chunks/{studio-1wagb550.js → studio-syg2tadj.js} +11 -11
  112. package/dist/chunks/{studio-t5wqwzdh.js → studio-tjjk7jvk.js} +1 -1
  113. package/dist/chunks/studio-tqgryxwg.js +1171 -0
  114. package/dist/chunks/studio-try13h8c.js +8448 -0
  115. package/dist/chunks/studio-vfq8qpjt.js +1219 -0
  116. package/dist/chunks/studio-vhv5tgxm.js +51 -0
  117. package/dist/chunks/{studio-ahn3pdgx.js → studio-vw7mzp6z.js} +52 -38
  118. package/dist/chunks/studio-wqpngpsk.js +174 -0
  119. package/dist/chunks/studio-wqz6dggm.js +1356 -0
  120. package/dist/chunks/{studio-92ab2jqe.js → studio-y9zedyjn.js} +14 -14
  121. package/dist/chunks/studio-yxa81f45.js +12 -0
  122. package/dist/chunks/studio-zdn2g6dt.js +148 -0
  123. package/dist/chunks/swatch-group-t0n6h2rn.js +139 -0
  124. package/dist/chunks/tabs-wbp1cdws.js +156 -0
  125. package/dist/chunks/textfield-abk6qyee.js +73 -0
  126. package/dist/chunks/toast-780pvfbv.js +27 -0
  127. package/dist/chunks/toast-host-3sqv46sn.js +116 -0
  128. package/dist/chunks/toolbar-xbh5bhtr.js +194 -0
  129. package/dist/chunks/tooltip-eptb614w.js +369 -0
  130. package/dist/chunks/tree-a73p0pqm.js +367 -0
  131. package/dist/chunks/{tsMode-76kr53q0.js → tsMode-jy4m86rv.js} +83 -85
  132. package/dist/chunks/workspace-t55jdjbx.js +69 -0
  133. package/dist/chunks/{ws-client-6shc5rze.js → ws-client-ny4zc51r.js} +20 -22
  134. package/dist/iframe-entry.js +1873 -673
  135. package/dist/studio.js +52757 -36535
  136. package/dist/workers/editor.worker.js +1040 -1085
  137. package/dist/workers/json.worker.js +1946 -1991
  138. package/dist/workers/ts.worker.js +4960 -5005
  139. package/index.html +1 -0
  140. package/package.json +18 -52
  141. package/src/about/about-modal.ts +72 -123
  142. package/src/browse/library-commands.ts +20 -49
  143. package/src/browse/library-layouts.ts +87 -218
  144. package/src/browse/library-pane.ts +406 -438
  145. package/src/canvas/canvas-helpers.ts +0 -49
  146. package/src/canvas/canvas-live-render.ts +25 -2
  147. package/src/canvas/canvas-render.ts +666 -231
  148. package/src/canvas/canvas-surface.ts +0 -1
  149. package/src/canvas/canvas-utils.ts +333 -113
  150. package/src/canvas/dialog-path.ts +76 -0
  151. package/src/canvas/dialog-state.ts +67 -0
  152. package/src/canvas/diff-marks.ts +252 -0
  153. package/src/canvas/diff-toolbar.ts +349 -0
  154. package/src/canvas/diff-view.ts +123 -0
  155. package/src/canvas/edit-width-drag.ts +187 -0
  156. package/src/canvas/edit-width.ts +150 -0
  157. package/src/canvas/iframe-entry.ts +125 -8
  158. package/src/canvas/iframe-host.ts +271 -15
  159. package/src/canvas/iframe-interaction.ts +133 -13
  160. package/src/canvas/iframe-patch.ts +7 -1
  161. package/src/canvas/iframe-position.ts +5 -6
  162. package/src/canvas/iframe-protocol.ts +106 -5
  163. package/src/canvas/iframe-render.ts +373 -5
  164. package/src/canvas/iframe-slash.ts +2 -2
  165. package/src/canvas/iframe-subtree.ts +5 -8
  166. package/src/canvas/path-mapping.ts +25 -3
  167. package/src/canvas/popover-path.ts +106 -0
  168. package/src/canvas/popover-state.ts +155 -0
  169. package/src/canvas/surface-registry.ts +21 -10
  170. package/src/collab/presence-chips.ts +51 -73
  171. package/src/commands/app-commands.ts +10 -0
  172. package/src/commands/budget.ts +12 -0
  173. package/src/commands/command-args.ts +404 -6
  174. package/src/commands/defaults.ts +44 -14
  175. package/src/commands/live-context.ts +11 -5
  176. package/src/commands/reference.ts +12 -2
  177. package/src/commands/registry.ts +177 -10
  178. package/src/commands/run-reported.ts +120 -0
  179. package/src/content/entry-commands.ts +16 -23
  180. package/src/content/entry-editor.ts +144 -101
  181. package/src/editor/canvas-slash-bridge.ts +2 -2
  182. package/src/editor/context-menu.ts +49 -200
  183. package/src/editor/convert-to-component.ts +42 -82
  184. package/src/editor/convert-to-repeater.ts +132 -173
  185. package/src/editor/inline-edit.ts +11 -0
  186. package/src/editor/shortcuts.ts +76 -49
  187. package/src/editor/slash-menu.ts +150 -157
  188. package/src/files/components.ts +39 -0
  189. package/src/files/file-ops.ts +117 -11
  190. package/src/files/files.ts +944 -660
  191. package/src/files/json-layout.ts +474 -0
  192. package/src/files/serialize-document.ts +8 -1
  193. package/src/format/convert-file.ts +16 -10
  194. package/src/format/format-host.ts +50 -1
  195. package/src/github/github-auth.ts +71 -29
  196. package/src/github/github-publish.ts +57 -65
  197. package/src/grid/cell-editors.ts +46 -4
  198. package/src/grid/cell-popovers.ts +65 -62
  199. package/src/grid/grid-open.ts +86 -64
  200. package/src/grid/grid-panel.ts +390 -469
  201. package/src/grid/redirects-grid.ts +40 -44
  202. package/src/hosting/layout.ts +2 -0
  203. package/src/i18n/i18n-commands.ts +137 -62
  204. package/src/media/media-pane.ts +83 -133
  205. package/src/new-project/add-repo-modal.ts +125 -176
  206. package/src/new-project/import-tab.ts +181 -252
  207. package/src/new-project/location-fields.ts +93 -160
  208. package/src/new-project/new-project-modal.ts +376 -367
  209. package/src/packages/jxsuite-update.ts +76 -18
  210. package/src/packages/pull-package-sync.ts +5 -1
  211. package/src/panels/activity-panel.ts +137 -104
  212. package/src/panels/ai-chat/chat-markdown.ts +15 -13
  213. package/src/panels/ai-chat/chat-view.ts +307 -472
  214. package/src/panels/ai-chat/composer.ts +175 -163
  215. package/src/panels/ai-chat/sessions-view.ts +31 -62
  216. package/src/panels/ai-panel.ts +213 -185
  217. package/src/panels/block-action-bar.ts +444 -645
  218. package/src/panels/bottom-dock.ts +112 -47
  219. package/src/panels/chat-panel.ts +19 -14
  220. package/src/panels/data-explorer.ts +141 -102
  221. package/src/panels/data-grid.ts +183 -150
  222. package/src/panels/dnd.ts +75 -20
  223. package/src/panels/editors.ts +26 -19
  224. package/src/panels/elements-panel.ts +243 -186
  225. package/src/panels/empty-state.ts +26 -42
  226. package/src/panels/events-panel.ts +950 -729
  227. package/src/panels/formula-workspace.ts +434 -207
  228. package/src/panels/frontmatter-fields.ts +185 -153
  229. package/src/panels/frontmatter-panel.ts +411 -187
  230. package/src/panels/git-diff-open.ts +148 -0
  231. package/src/panels/git-panel.ts +679 -605
  232. package/src/panels/head-panel.ts +617 -435
  233. package/src/panels/i18n-panel.ts +148 -135
  234. package/src/panels/imports-panel.ts +388 -414
  235. package/src/panels/jump-bar.ts +175 -147
  236. package/src/panels/layers-panel.ts +785 -671
  237. package/src/panels/left-panel.ts +166 -94
  238. package/src/panels/navigator-panels.ts +1 -1
  239. package/src/panels/pane-context.ts +529 -677
  240. package/src/panels/pane-grid.ts +265 -241
  241. package/src/panels/panel-registry.ts +15 -4
  242. package/src/panels/problems-panel.ts +181 -99
  243. package/src/panels/properties-panel.ts +1525 -966
  244. package/src/panels/provenance.ts +31 -113
  245. package/src/panels/quick-search.ts +223 -184
  246. package/src/panels/right-panel.ts +135 -169
  247. package/src/panels/seo-modal.ts +346 -167
  248. package/src/panels/settings-menu.ts +114 -661
  249. package/src/panels/settings-pane.ts +102 -86
  250. package/src/panels/signals-panel.ts +1318 -1172
  251. package/src/panels/statement-editor.ts +556 -428
  252. package/src/panels/style-panel.ts +1367 -630
  253. package/src/panels/style-utils.ts +36 -32
  254. package/src/panels/stylebook-doc.ts +30 -73
  255. package/src/panels/stylebook-layers-panel.ts +202 -109
  256. package/src/panels/stylebook-panel.ts +124 -88
  257. package/src/panels/tab-strip.ts +386 -288
  258. package/src/platforms/cloud.ts +106 -16
  259. package/src/platforms/devserver.ts +13 -0
  260. package/src/publish/deploy-checklist.ts +124 -78
  261. package/src/publish/publish-commands.ts +4 -12
  262. package/src/publish/publish-panel.ts +134 -328
  263. package/src/services/a11y-report.ts +100 -43
  264. package/src/services/ai-command-tools.ts +538 -0
  265. package/src/services/ai-project-tools.ts +8 -2
  266. package/src/services/ai-session-store.ts +6 -0
  267. package/src/services/ai-system-prompt.ts +112 -24
  268. package/src/services/ai-tools.ts +36 -212
  269. package/src/services/ai-write-report.ts +174 -0
  270. package/src/services/document-assistant.ts +59 -8
  271. package/src/services/idle.ts +16 -14
  272. package/src/services/live-surfaces.ts +237 -0
  273. package/src/services/model-uri.ts +75 -0
  274. package/src/services/notify.ts +63 -6
  275. package/src/services/popover-report.ts +325 -0
  276. package/src/services/surface-registry.ts +56 -0
  277. package/src/services/tool-executor.ts +6 -0
  278. package/src/settings/contexts-section.ts +301 -299
  279. package/src/settings/contributed-section.ts +361 -222
  280. package/src/settings/css-vars-editor.ts +320 -325
  281. package/src/settings/defs-editor.ts +572 -535
  282. package/src/settings/dependencies-editor.ts +79 -107
  283. package/src/settings/extension-commands.ts +379 -0
  284. package/src/settings/extension-rows.ts +113 -0
  285. package/src/settings/extensions-section.ts +287 -0
  286. package/src/settings/general-settings.ts +130 -181
  287. package/src/settings/head-editor.ts +10 -305
  288. package/src/settings/locales-section.ts +146 -196
  289. package/src/settings/preferences-accounts.ts +1 -1
  290. package/src/settings/preferences-dialog.ts +350 -330
  291. package/src/settings/project-sections.ts +108 -181
  292. package/src/settings/schema-field-ui.ts +11 -399
  293. package/src/settings/section-registry.ts +8 -1
  294. package/src/settings/settings-document.ts +31 -29
  295. package/src/shell/tree.ts +42 -87
  296. package/src/shell.ts +48 -36
  297. package/src/studio.ts +104 -69
  298. package/src/style/token-ref.ts +0 -39
  299. package/src/surfaces/about.json +263 -0
  300. package/src/surfaces/about.ts +163 -0
  301. package/src/surfaces/add-repo.json +486 -0
  302. package/src/surfaces/add-repo.ts +236 -0
  303. package/src/surfaces/ai-chat.json +2185 -0
  304. package/src/surfaces/ai-chat.ts +549 -0
  305. package/src/surfaces/ai-credentials-form.json +332 -0
  306. package/src/surfaces/ai-credentials-form.ts +151 -0
  307. package/src/surfaces/ai-managed-connect.json +108 -0
  308. package/src/surfaces/ai-managed-connect.ts +123 -0
  309. package/src/surfaces/ai-model-picker.json +114 -0
  310. package/src/surfaces/ai-model-picker.ts +139 -0
  311. package/src/surfaces/block-action-bar.json +640 -0
  312. package/src/surfaces/block-action-bar.ts +368 -0
  313. package/src/surfaces/bottom-dock.json +154 -0
  314. package/src/surfaces/bottom-dock.ts +125 -0
  315. package/src/surfaces/canvas-stage.json +444 -0
  316. package/src/surfaces/canvas-stage.ts +253 -0
  317. package/src/surfaces/cf-account-picker.json +300 -0
  318. package/src/surfaces/cf-account-picker.ts +214 -0
  319. package/src/surfaces/commandbar.json +985 -0
  320. package/src/{panels/toolbar.ts → surfaces/commandbar.ts} +417 -391
  321. package/src/surfaces/convert-repeater.json +309 -0
  322. package/src/surfaces/convert-repeater.ts +276 -0
  323. package/src/surfaces/data-actions.json +157 -0
  324. package/src/surfaces/data-actions.ts +115 -0
  325. package/src/surfaces/dialog.json +289 -0
  326. package/src/surfaces/dialog.ts +343 -0
  327. package/src/surfaces/diff-toolbar.json +266 -0
  328. package/src/surfaces/diff-toolbar.ts +139 -0
  329. package/src/surfaces/doc-header.json +739 -0
  330. package/src/surfaces/doc-header.ts +223 -0
  331. package/src/surfaces/empty-state.json +152 -0
  332. package/src/surfaces/empty-state.ts +173 -0
  333. package/src/surfaces/entry-editor.json +281 -0
  334. package/src/surfaces/entry-editor.ts +191 -0
  335. package/src/surfaces/expression-editor.json +977 -0
  336. package/src/surfaces/expression-editor.ts +266 -0
  337. package/src/surfaces/files-panel.json +592 -0
  338. package/src/surfaces/files-panel.ts +301 -0
  339. package/src/surfaces/formula-palette.json +364 -0
  340. package/src/surfaces/formula-palette.ts +340 -0
  341. package/src/surfaces/git-panel.json +1822 -0
  342. package/src/surfaces/git-panel.ts +272 -0
  343. package/src/surfaces/github-auth.json +112 -0
  344. package/src/surfaces/github-auth.ts +139 -0
  345. package/src/surfaces/github-publish.json +206 -0
  346. package/src/surfaces/github-publish.ts +169 -0
  347. package/src/surfaces/grid-cell.json +218 -0
  348. package/src/surfaces/grid-cell.ts +120 -0
  349. package/src/surfaces/grid-open.json +147 -0
  350. package/src/surfaces/grid-open.ts +87 -0
  351. package/src/surfaces/grid-panel.json +706 -0
  352. package/src/surfaces/grid-panel.ts +206 -0
  353. package/src/surfaces/grid-replace.json +179 -0
  354. package/src/surfaces/grid-replace.ts +89 -0
  355. package/src/surfaces/grid-views.json +446 -0
  356. package/src/surfaces/grid-views.ts +172 -0
  357. package/src/surfaces/inspector-dock.json +187 -0
  358. package/src/surfaces/inspector-dock.ts +161 -0
  359. package/src/surfaces/jump-bar.json +243 -0
  360. package/src/surfaces/jump-bar.ts +129 -0
  361. package/src/surfaces/jxsuite-update.json +146 -0
  362. package/src/surfaces/jxsuite-update.ts +149 -0
  363. package/src/surfaces/library-pane.json +1509 -0
  364. package/src/surfaces/library-pane.ts +363 -0
  365. package/src/surfaces/logic-panel.json +1949 -0
  366. package/src/surfaces/logic-panel.ts +369 -0
  367. package/src/surfaces/logic-workspace.json +685 -0
  368. package/src/surfaces/logic-workspace.ts +238 -0
  369. package/src/surfaces/media-browser.json +303 -0
  370. package/src/surfaces/media-browser.ts +145 -0
  371. package/src/surfaces/media-field.json +163 -0
  372. package/src/surfaces/media-field.ts +116 -0
  373. package/src/surfaces/media-pane.json +513 -0
  374. package/src/surfaces/media-pane.ts +197 -0
  375. package/src/surfaces/menu.json +232 -0
  376. package/src/surfaces/menu.ts +375 -0
  377. package/src/surfaces/navigator-dock.json +113 -0
  378. package/src/surfaces/navigator-dock.ts +130 -0
  379. package/src/surfaces/new-project.json +1654 -0
  380. package/src/surfaces/new-project.ts +411 -0
  381. package/src/surfaces/palette.json +535 -0
  382. package/src/surfaces/pane-context.json +1497 -0
  383. package/src/surfaces/pane-context.ts +513 -0
  384. package/src/surfaces/pane-grid.json +188 -0
  385. package/src/surfaces/pane-grid.ts +250 -0
  386. package/src/surfaces/panel-activity.json +563 -0
  387. package/src/surfaces/panel-activity.ts +187 -0
  388. package/src/surfaces/panel-data.json +141 -0
  389. package/src/surfaces/panel-data.ts +156 -0
  390. package/src/surfaces/panel-deploy-checklist.json +283 -0
  391. package/src/surfaces/panel-deploy-checklist.ts +175 -0
  392. package/src/surfaces/panel-elements.json +367 -0
  393. package/src/surfaces/panel-elements.ts +175 -0
  394. package/src/surfaces/panel-i18n.json +569 -0
  395. package/src/surfaces/panel-i18n.ts +212 -0
  396. package/src/surfaces/panel-imports.json +780 -0
  397. package/src/surfaces/panel-imports.ts +209 -0
  398. package/src/surfaces/panel-outline.json +734 -0
  399. package/src/surfaces/panel-outline.ts +326 -0
  400. package/src/surfaces/panel-page.json +1017 -0
  401. package/src/surfaces/panel-page.ts +240 -0
  402. package/src/surfaces/panel-problems.json +497 -0
  403. package/src/surfaces/panel-problems.ts +157 -0
  404. package/src/surfaces/panel-signals.json +1973 -0
  405. package/src/surfaces/panel-signals.ts +436 -0
  406. package/src/surfaces/panel-stylebook-layers.json +317 -0
  407. package/src/surfaces/panel-stylebook-layers.ts +155 -0
  408. package/src/surfaces/preferences.json +850 -0
  409. package/src/surfaces/preferences.ts +332 -0
  410. package/src/surfaces/progress-modal.json +110 -0
  411. package/src/surfaces/progress-modal.ts +150 -0
  412. package/src/surfaces/properties-panel.json +1578 -0
  413. package/src/surfaces/properties-panel.ts +337 -0
  414. package/src/surfaces/publish.json +945 -0
  415. package/src/surfaces/publish.ts +392 -0
  416. package/src/surfaces/push-plan.json +110 -0
  417. package/src/surfaces/push-plan.ts +87 -0
  418. package/src/surfaces/rail.json +255 -0
  419. package/src/surfaces/rail.ts +206 -0
  420. package/src/surfaces/reference-field.json +191 -0
  421. package/src/surfaces/reference-field.ts +168 -0
  422. package/src/surfaces/schema-builder.json +1011 -0
  423. package/src/surfaces/schema-builder.ts +181 -0
  424. package/src/surfaces/schema-form.json +1013 -0
  425. package/src/surfaces/schema-form.ts +287 -0
  426. package/src/surfaces/secret-field.json +60 -0
  427. package/src/surfaces/secret-field.ts +146 -0
  428. package/src/surfaces/seo.json +932 -0
  429. package/src/surfaces/seo.ts +334 -0
  430. package/src/surfaces/settings-contexts.json +538 -0
  431. package/src/surfaces/settings-contexts.ts +196 -0
  432. package/src/surfaces/settings-contributed.json +489 -0
  433. package/src/surfaces/settings-contributed.ts +199 -0
  434. package/src/surfaces/settings-css-vars.json +820 -0
  435. package/src/surfaces/settings-css-vars.ts +199 -0
  436. package/src/surfaces/settings-defs.json +1363 -0
  437. package/src/surfaces/settings-defs.ts +232 -0
  438. package/src/surfaces/settings-deploy.json +108 -0
  439. package/src/surfaces/settings-deploy.ts +135 -0
  440. package/src/surfaces/settings-extensions.json +486 -0
  441. package/src/surfaces/settings-extensions.ts +201 -0
  442. package/src/surfaces/settings-head.json +779 -0
  443. package/src/surfaces/settings-head.ts +397 -0
  444. package/src/surfaces/settings-locales.json +420 -0
  445. package/src/surfaces/settings-locales.ts +137 -0
  446. package/src/surfaces/settings-overview.json +423 -0
  447. package/src/surfaces/settings-overview.ts +218 -0
  448. package/src/surfaces/settings-packages.json +549 -0
  449. package/src/surfaces/settings-packages.ts +158 -0
  450. package/src/surfaces/settings-pane.json +147 -0
  451. package/src/surfaces/settings-pane.ts +150 -0
  452. package/src/surfaces/settings-rawjson.json +104 -0
  453. package/src/surfaces/settings-rawjson.ts +107 -0
  454. package/src/surfaces/shell.json +99 -0
  455. package/src/surfaces/shell.ts +30 -0
  456. package/src/surfaces/slash-menu.json +180 -0
  457. package/src/surfaces/slash-menu.ts +272 -0
  458. package/src/surfaces/statements.json +607 -0
  459. package/src/surfaces/statements.ts +238 -0
  460. package/src/surfaces/statusbar.json +94 -0
  461. package/src/{panels → surfaces}/statusbar.ts +164 -90
  462. package/src/surfaces/style-panel.json +1643 -0
  463. package/src/surfaces/style-panel.ts +338 -0
  464. package/src/surfaces/stylebook-chrome.json +122 -0
  465. package/src/surfaces/stylebook-chrome.ts +122 -0
  466. package/src/surfaces/tab-strip.json +448 -0
  467. package/src/surfaces/tab-strip.ts +200 -0
  468. package/src/surfaces/target-line.json +419 -0
  469. package/src/surfaces/target-line.ts +349 -0
  470. package/src/surfaces/toasts.json +189 -0
  471. package/src/surfaces/welcome.json +571 -0
  472. package/src/surfaces/welcome.ts +338 -0
  473. package/src/tabs/project-config.ts +111 -21
  474. package/src/tabs/tab.ts +52 -1
  475. package/src/types.ts +22 -0
  476. package/src/ui/ai-credentials-form.ts +104 -100
  477. package/src/ui/ai-managed-connect.ts +54 -36
  478. package/src/ui/ai-model-picker.ts +75 -47
  479. package/src/ui/cf-account-picker.ts +76 -141
  480. package/src/ui/color-selector.ts +80 -301
  481. package/src/ui/dynamic-slot.ts +43 -283
  482. package/src/ui/expression-editor.ts +797 -576
  483. package/src/ui/field-input.ts +8 -181
  484. package/src/ui/form-controls.ts +838 -366
  485. package/src/ui/formula-chips.ts +80 -85
  486. package/src/ui/kit.ts +33 -0
  487. package/src/ui/layers.ts +296 -593
  488. package/src/ui/media-picker.ts +203 -284
  489. package/src/ui/panel-resize.ts +251 -64
  490. package/src/ui/popover-surface.ts +153 -0
  491. package/src/ui/progress-modal.ts +22 -60
  492. package/src/ui/regions.ts +13 -7
  493. package/src/ui/schema-form.ts +906 -440
  494. package/src/ui/surface.ts +122 -0
  495. package/src/ui/unit-selector.ts +15 -131
  496. package/src/ui/value-source.ts +14 -0
  497. package/src/ui/virtual-window.ts +88 -9
  498. package/src/utils/canvas-media.ts +72 -0
  499. package/src/utils/edit-display.ts +7 -2
  500. package/src/utils/element-selectors.ts +69 -0
  501. package/src/utils/link-target.ts +2 -2
  502. package/src/utils/studio-utils.ts +0 -10
  503. package/src/view.ts +15 -0
  504. package/src/workspace/pane-derive.ts +36 -14
  505. package/src/workspace/workspace.ts +75 -6
  506. package/styles/canvas.css +72 -467
  507. package/styles/forced-colors.css +48 -82
  508. package/styles/forced-colors.json +41 -0
  509. package/styles/inspector.css +2 -1301
  510. package/styles/overlays.css +96 -2823
  511. package/styles/panels.css +6 -1423
  512. package/styles/shell-frame.css +272 -0
  513. package/styles/shell-frame.json +227 -0
  514. package/styles/shell.css +0 -1568
  515. package/styles/tokens.css +65 -136
  516. package/styles/tokens.json +84 -0
  517. package/dist/chunks/Overlay.dev-etqjwyf9.js +0 -23
  518. package/dist/chunks/adapter-jkcyhgtr.js +0 -14
  519. package/dist/chunks/cf-account-picker-en86v7x7.js +0 -163
  520. package/dist/chunks/cf-settings-45fgyx8y.js +0 -21
  521. package/dist/chunks/components-zmx10t62.js +0 -21
  522. package/dist/chunks/content-source-48xg86wj.js +0 -49
  523. package/dist/chunks/convert-to-repeater-43tfn7cs.js +0 -268
  524. package/dist/chunks/editor-cptq5jp1.js +0 -37
  525. package/dist/chunks/entry-commands-e63f1xc0.js +0 -46
  526. package/dist/chunks/extension-sections-txv08xxg.js +0 -311
  527. package/dist/chunks/file-ops-wbh4hhsx.js +0 -48
  528. package/dist/chunks/files-3kfkwhk4.js +0 -93
  529. package/dist/chunks/flow-qbfgrpeh.js +0 -634
  530. package/dist/chunks/focus-trap.esm-9t2drp0g.js +0 -1025
  531. package/dist/chunks/focus-visible-6eg70z18.js +0 -167
  532. package/dist/chunks/format-host-w16q81tf.js +0 -43
  533. package/dist/chunks/fs-events-hrrg328g.js +0 -21
  534. package/dist/chunks/git-panel-532whr1y.js +0 -59
  535. package/dist/chunks/index-zqxmqxmh.js +0 -109
  536. package/dist/chunks/jx-validate-8er6gbxy.js +0 -19
  537. package/dist/chunks/layers-panel-07hm7g5j.js +0 -67
  538. package/dist/chunks/layers-q75tj2mf.js +0 -45
  539. package/dist/chunks/lit-html-j055mkh8.js +0 -22
  540. package/dist/chunks/monaco-buffer-dqw0xxpx.js +0 -25
  541. package/dist/chunks/notify-yjm79n4s.js +0 -30
  542. package/dist/chunks/parse-24d0gay3.js +0 -19
  543. package/dist/chunks/platform-ba1j1gbh.js +0 -16
  544. package/dist/chunks/publish-panel-v9st96ja.js +0 -29
  545. package/dist/chunks/quick-search-zccjp6gv.js +0 -66
  546. package/dist/chunks/references-3qx8fkyp.js +0 -29
  547. package/dist/chunks/right-panel-vm3b6kwy.js +0 -46
  548. package/dist/chunks/section-registry-sx1ykvkw.js +0 -30
  549. package/dist/chunks/sp-overlay.dev-qgvhnxf4.js +0 -18
  550. package/dist/chunks/sp-popover.dev-n2etmqdp.js +0 -9
  551. package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +0 -15
  552. package/dist/chunks/sp-tooltip.dev-4sastdnw.js +0 -15
  553. package/dist/chunks/sp-tray.dev-tkbscm06.js +0 -199
  554. package/dist/chunks/studio-0a5qrh01.js +0 -3
  555. package/dist/chunks/studio-0bq6eh7v.js +0 -12
  556. package/dist/chunks/studio-18j3bmcz.js +0 -14
  557. package/dist/chunks/studio-1ngbejft.js +0 -181
  558. package/dist/chunks/studio-4b5z1x43.js +0 -424
  559. package/dist/chunks/studio-4n2b5gzb.js +0 -357
  560. package/dist/chunks/studio-4rzyrtjz.js +0 -426
  561. package/dist/chunks/studio-5fx3g9jg.js +0 -4325
  562. package/dist/chunks/studio-5myknm0a.js +0 -1903
  563. package/dist/chunks/studio-7afd21nw.js +0 -67
  564. package/dist/chunks/studio-7nte2hj4.js +0 -613
  565. package/dist/chunks/studio-8jpzqk5s.js +0 -128
  566. package/dist/chunks/studio-8q517dfr.js +0 -24
  567. package/dist/chunks/studio-950k0dx6.js +0 -6939
  568. package/dist/chunks/studio-9n4mrmnb.js +0 -184
  569. package/dist/chunks/studio-a0tswy0w.js +0 -58
  570. package/dist/chunks/studio-ahnzd9rg.js +0 -23
  571. package/dist/chunks/studio-bgxt93ga.js +0 -62
  572. package/dist/chunks/studio-cmb03bg5.js +0 -13890
  573. package/dist/chunks/studio-d1ks410h.js +0 -175
  574. package/dist/chunks/studio-ef49pnzx.js +0 -387
  575. package/dist/chunks/studio-eqq1w638.js +0 -113
  576. package/dist/chunks/studio-ew0djmhg.js +0 -341
  577. package/dist/chunks/studio-gfgb8pe6.js +0 -1652
  578. package/dist/chunks/studio-h4rxscfc.js +0 -10
  579. package/dist/chunks/studio-jc994dnr.js +0 -1066
  580. package/dist/chunks/studio-kyh9avvv.js +0 -28
  581. package/dist/chunks/studio-m3hwn7y1.js +0 -120
  582. package/dist/chunks/studio-mtfnaqs9.js +0 -659
  583. package/dist/chunks/studio-n6j9t10q.js +0 -318
  584. package/dist/chunks/studio-n8b9wqwy.js +0 -291
  585. package/dist/chunks/studio-qcy05dp8.js +0 -1743
  586. package/dist/chunks/studio-qq6jym04.js +0 -53
  587. package/dist/chunks/studio-rdx8jcxy.js +0 -83
  588. package/dist/chunks/studio-rn9ta2mz.js +0 -111
  589. package/dist/chunks/studio-t7qb9db8.js +0 -665
  590. package/dist/chunks/studio-tmc5emrw.js +0 -8
  591. package/dist/chunks/studio-z6nw2ek2.js +0 -18
  592. package/dist/chunks/studio-zczps70q.js +0 -378
  593. package/dist/chunks/studio-zqazsct2.js +0 -1823
  594. package/dist/chunks/workspace-fjx52ryv.js +0 -89
  595. package/src/panels/activity-bar.ts +0 -251
  596. package/src/panels/panel-scheduler.ts +0 -219
  597. package/src/panels/style-inputs.ts +0 -237
  598. package/src/panels/target-line.ts +0 -274
  599. package/src/panels/welcome-screen.ts +0 -346
  600. package/src/ui/button-group.ts +0 -102
  601. package/src/ui/choice-field.ts +0 -72
  602. package/src/ui/field-row.ts +0 -249
  603. package/src/ui/formula-palette.ts +0 -213
  604. package/src/ui/icons.ts +0 -52
  605. package/src/ui/jx-theme.ts +0 -130
  606. package/src/ui/spectrum.ts +0 -340
  607. package/src/ui/value-selector.ts +0 -174
  608. package/src/ui/widgets.ts +0 -172
  609. /package/dist/chunks/{javascript-s56a8dpk.css → javascript-4abzjcy9.css} +0 -0
  610. /package/dist/chunks/{jsonMode-zr9sw0c7.css → jsonMode-vvmpmk34.css} +0 -0
  611. /package/dist/chunks/{monaco-setup-pav68602.css → monaco-setup-8vbyqnhr.css} +0 -0
  612. /package/dist/chunks/{tsMode-ef187txx.css → tsMode-hnxxb73h.css} +0 -0
@@ -10,44 +10,60 @@
10
10
  * by whichever pane had focus, releasing the loser's artboards on the way past. This module is what
11
11
  * that scaffolding was standing in for, and the handover is deleted with it.
12
12
  *
13
- * **Lit owns the frame; imperative code owns the leaves.** `panels/bottom-dock.ts` is the
14
- * precedent. The cells, their four boxes and the splitter between them are a keyed `repeat` over
15
- * `workspace.panes`; everything a cell CONTAINS arrives through a `ref()` and is built by the
16
- * module that owns it — `canvas/canvas-render.ts` is the manual lit render root of `.pane-stage`
17
- * and this template puts nothing inside it, `panels/jump-bar.ts` and `panels/pane-context.ts` are
18
- * handed `.pane-jump` and `.pane-chrome`, and `panels/tab-strip.ts` is handed `.pane-strip`.
13
+ * **The document owns the frame; this module owns the decisions.** `surfaces/pane-grid.json` is the
14
+ * cells, the four boxes inside each one, the splitter between two of them and every rule that lays
15
+ * them out; `surfaces/pane-grid.ts` is the mount. What is left here is the flow: which panes there
16
+ * are, what a cell's stage is furnished with, in what ORDER a departing cell is taken apart, and
17
+ * what the grid's own tracks are. Everything a cell CONTAINS still arrives through the module that
18
+ * owns it — `canvas/canvas-render.ts` is the render root of the stage and this document puts
19
+ * nothing inside it, `panels/jump-bar.ts` and `panels/pane-context.ts` are handed the jump and
20
+ * chrome boxes, and `panels/tab-strip.ts` is handed the strip.
19
21
  *
20
- * This was an imperative reconciler until the splitter proved why it could not be. `layout()` runs
21
- * on every `shell.paneSplit` write — every `pointermove` of a drag — and it re-ran
22
- * `cells[1].root.before(splitter)`, which on an already-positioned node is a REMOVE plus an insert:
23
- * Chrome fires `lostpointercapture` on move #1, the rest of the gesture goes to whatever is under
24
- * the cursor, and a drag asking for +0.20 lands +0.03. The old file carried "never touches an
25
- * existing cell's children" as a comment; a **keyed** `repeat` makes it structural — lit MOVES an
26
- * existing cell rather than recreating it, and re-parenting is not a move for the things inside a
27
- * stage: an `<iframe>` that changes parent reloads, dropping its `iframe-channel` connection, its
28
- * shadow document and every `ready` panel with it.
22
+ * **Three properties survived the conversion, and each is structural rather than remembered:**
29
23
  *
30
- * **§18.1's three rules, structurally:**
24
+ * 1. _A pane is complete before it is published._ The runtime builds a `$map` row inside its own
25
+ * effect scope and inserts it afterwards, so the four `cellPart` calls below — the surface
26
+ * record, the gestures, the two bars' mounts, the pane-focus listener — all happen while the
27
+ * cell is still detached. There is no frame in which a cell exists with no stage inside it.
28
+ * 2. _A pane's node is never re-parented._ The repeater is keyed on the pane id and the runtime's
29
+ * forward-cursor pass only moves rows that are out of place. `workspace.panes[0]` is always the
30
+ * primary and `MAX_PANES` is 2, so no row ever moves — which matters because re-parenting is not
31
+ * a move for an `<iframe>`: it reloads, dropping its `iframe-channel` connection, its shadow
32
+ * document and every `ready` panel with it.
33
+ * 3. _A drag moves a track edge and never a node._ {@link layout} writes ONE inline property on the
34
+ * HOST, which is outside the document, and the projection is skipped entirely when the pane set
35
+ * has not changed — so the five `shell.paneSplit` writes a drag makes reach no markup at all.
36
+ * The predecessor re-ran `cells[1].root.before(splitter)` from here, and `.before()` on an
37
+ * already-positioned node is a REMOVE plus an insert: Chrome fires `lostpointercapture` on move
38
+ * #1, the rest of the gesture goes to whatever is under the cursor, and a drag asking for +0.20
39
+ * lands +0.03.
31
40
  *
32
- * 1. _A pane is complete before it is published._ lit clones a template into a `DocumentFragment`,
33
- * commits every part into it — which is where the `ref()` callbacks below run — and only then
34
- * inserts the fragment. So the stamping, the surface record and the gestures all happen while
35
- * the cell is still detached, exactly as the hand-built fragment used to. There is no frame in
36
- * which a `.pane` exists with no stage inside it.
37
- * 2. _A pane is never observable without existing._ One effect over `workspace.panes` is the only
38
- * writer of pane DOM. Vue batches, so no intermediate state is samplable.
39
- * 3. _A pane with nothing in it is a hole in the grid._ Enforced in `workspace/workspace.ts`, where
40
- * the tabs are. This module ASSERTS rather than repairs: repairing inside a reactive effect that
41
- * writes `workspace.panes` is an effect that triggers itself.
41
+ * **The splitter is `jx-split` (ui.md §5.5), and the drag left this module with it.** It used to be
42
+ * a bare div carrying `role="separator"` and a `setupHandle` call from here — which meant the one
43
+ * dock-sized thing in Studio that no keyboard could move: no tab stop, no `aria-valuenow`, no
44
+ * arrows, and a double click that reset the split as the only gesture a pointer could reach without
45
+ * dragging. What is left here is the two lines a host actually owns: {@link setPaneSplit} on every
46
+ * move and {@link persistDocks} on every commit. The element measures the grid itself, so the 320px
47
+ * floor is handed over in pixels and converted against the box it is actually dividing — and the
48
+ * `scale`, `min` and `max` closures that read `clientWidth` on every `pointermove` are gone, along
49
+ * with the window-resize staleness they were paying for.
50
+ *
51
+ * The fourth rule of §18.1 — _a pane with nothing in it is a hole in the grid_ — is enforced in
52
+ * `workspace/workspace.ts`, where the tabs are. This module never repairs: repairing inside a
53
+ * reactive effect that writes `workspace.panes` is an effect that triggers itself.
42
54
  */
43
55
 
44
- import { html, render as litRender, nothing } from "lit-html";
45
- import { repeat } from "lit-html/directives/repeat.js";
46
- import { ref } from "lit-html/directives/ref.js";
47
56
  import { effect, effectScope } from "../reactivity";
48
- import { persistDocks, registerShellSurface, setPaneSplit, shell } from "../shell";
49
57
  import {
50
- STAGE_CLASS,
58
+ DEFAULT_PANE_SPLIT,
59
+ PANE_SPLIT_MAX,
60
+ PANE_SPLIT_MIN,
61
+ persistDocks,
62
+ registerShellSurface,
63
+ setPaneSplit,
64
+ shell,
65
+ } from "../shell";
66
+ import {
51
67
  createPaneSurface,
52
68
  disposePaneSurface,
53
69
  registerCanvasSurface,
@@ -59,10 +75,10 @@ import { paneRegion, paneStripRegion } from "../ui/regions";
59
75
  import { attachJumpBarHost } from "./jump-bar";
60
76
  import { attachPaneChromeHost } from "./pane-context";
61
77
  import { focusPane, workspace } from "../workspace/workspace";
62
- import { setupHandle } from "../ui/panel-resize";
78
+ import { mountPaneGridSurface } from "../surfaces/pane-grid";
63
79
  import type { EffectScope } from "@vue/reactivity";
64
80
  import type { CanvasSurface } from "../canvas/canvas-surface";
65
- import type { TemplateResult } from "lit-html";
81
+ import type { PaneCellPart, PaneGridRow, PaneGridSurface } from "../surfaces/pane-grid";
66
82
 
67
83
  /** One drawn pane: its root and the four surfaces inside it. */
68
84
  export interface PaneCell {
@@ -75,30 +91,23 @@ export interface PaneCell {
75
91
  surface: CanvasSurface;
76
92
  }
77
93
 
78
- /**
79
- * A cell's record and the five `ref` callbacks that fill it in.
80
- *
81
- * The callbacks are MEMOISED per pane id, and that is not an optimisation. lit's `ref` directive
82
- * compares the callback by identity: hand it a fresh closure on a re-render and it calls the old
83
- * one with `undefined` and the new one with the element — a detach and a re-attach of a live host,
84
- * on every pass. Stable callbacks mean an unchanged cell's refs are never called again at all.
85
- */
86
- interface CellRefs extends PaneCell {
87
- onRoot: (el?: Element) => void;
88
- onStrip: (el?: Element) => void;
89
- onJump: (el?: Element) => void;
90
- onChrome: (el?: Element) => void;
91
- onStage: (el?: Element) => void;
92
- /** Capture-phase `pointerdown` on the cell root — see {@link cellRefs}. */
93
- onPointerDown: { handleEvent: () => void; capture: true };
94
- /** Stage-gesture disposer, live between the stage's attach and its detach. */
94
+ /** A cell's record, plus the disposer its stage gestures live behind. */
95
+ interface CellState extends PaneCell {
96
+ /** Stage-gesture disposer, live between the stage's creation and the cell's disposal. */
95
97
  releaseGestures: (() => void) | null;
96
98
  }
97
99
 
98
- const _cells = new Map<string, CellRefs>();
100
+ const _cells = new Map<string, CellState>();
99
101
 
100
102
  let _grid: HTMLElement | null = null;
101
103
 
104
+ let _surface: PaneGridSurface | null = null;
105
+
106
+ let _ready: Promise<void> | null = null;
107
+
108
+ /** The pane ids the document has been asked to draw. The projection's dirty check. */
109
+ let _drawn: string[] = [];
110
+
102
111
  let _scope: EffectScope | null = null;
103
112
 
104
113
  /** The cell a pane is drawn in, or null. Test-visible, and the bootstrap's handle on the primary. */
@@ -106,108 +115,34 @@ export function cellForPane(paneId: string): PaneCell | null {
106
115
  return _cells.get(paneId) ?? null;
107
116
  }
108
117
 
109
- /** Every drawn cell, in grid order. */
110
- export function paneCells(): PaneCell[] {
111
- return workspace.panes
112
- .map((pane) => _cells.get(pane.id))
113
- .filter((cell): cell is CellRefs => cell !== undefined);
118
+ /**
119
+ * Settles once the first projection is in the document.
120
+ *
121
+ * The mount is asynchronous — the runtime waits for the kit to be defined and renders one microtask
122
+ * after that — so `mountShell()` starting the grid is not the same event as the grid existing. The
123
+ * bootstrap reads the primary cell's four boxes on the line after it, exactly as `mountShellTree()`
124
+ * made every caller await the frame for the same reason.
125
+ */
126
+ export function paneGridReady(): Promise<void> {
127
+ return _ready ?? Promise.resolve();
114
128
  }
115
129
 
116
130
  /**
117
- * The record for a pane, created on first render and reused for the life of the cell.
131
+ * The record for a pane, created when its cell's outermost box is, and reused for the life of the
132
+ * cell.
118
133
  *
119
134
  * Every element field is typed non-null and starts null, the same bargain `CanvasSurface` makes:
120
- * lit commits the refs inside `litRender`, synchronously, before any caller can hold the record.
135
+ * the runtime creates a row's nodes depth-first inside one synchronous render pass, so the record
136
+ * is complete before anything outside that pass can hold it.
121
137
  */
122
- function cellRefs(paneId: string): CellRefs {
138
+ function cellState(paneId: string): CellState {
123
139
  const existing = _cells.get(paneId);
124
140
  if (existing) {
125
141
  return existing;
126
142
  }
127
- /* Declared in ONE literal, callbacks and all, closing over the very binding being declared. The
128
- bodies only run when lit commits a part, so there is no temporal-dead-zone read — and a record
129
- that is complete on the line it is created cannot be half-wired by an early return later. */
130
- const cell: CellRefs = {
143
+ const cell: CellState = {
131
144
  chrome: null as unknown as HTMLElement,
132
145
  jump: null as unknown as HTMLElement,
133
- /* The two bars are HANDED their host rather than resolving a region, the same way
134
- `panels/frontmatter-panel.ts` is handed the stage. They cannot resolve one the way the tab
135
- strip does: the strip's host carries `pane.<id>/tabs` and nothing inside it re-stamps that
136
- id, while the jump bar and the context bar both stamp `pane.<id>/jump` and
137
- `pane.<id>/context` on markup they render INSIDE these two divs — so a region on the wrapper
138
- as well would put the same id on two nested elements, and `resolveRegion` takes the LAST
139
- match. Sixty shots crop `pane.primary/context`; a second, larger element carrying it is a
140
- silently widened crop. */
141
- onChrome: (el?: Element) => {
142
- if (el) {
143
- cell.chrome = el as HTMLElement;
144
- }
145
- attachPaneChromeHost(paneId, (el as HTMLElement | undefined) ?? null);
146
- },
147
- onJump: (el?: Element) => {
148
- if (el) {
149
- cell.jump = el as HTMLElement;
150
- }
151
- attachJumpBarHost(paneId, (el as HTMLElement | undefined) ?? null);
152
- },
153
- /**
154
- * **A pointer landing anywhere in this cell puts the keyboard in this pane.**
155
- *
156
- * Before this, `panels/tab-strip.ts`'s strip row was the ONLY thing in the app that moved
157
- * `workspace.activePaneId` by pointer, so a click on the side pane's canvas, its context bar,
158
- * its Library, its Code editor or its entry form selected and edited that pane's document while
159
- * every keyboard command, the Inspector, the block action bar and the overlay effect went on
160
- * answering for the other one.
161
- *
162
- * ONE listener, on the cell, because the cell is the only thing that knows which pane a click
163
- * is IN. The alternative — a `focusPane` call in each of the seven surfaces a pane can contain
164
- * — is a list that a new surface joins by being remembered.
165
- *
166
- * Three properties it needs, and where each comes from:
167
- *
168
- * 1. _It must not disturb a control mid-interaction._ It moves the pane focus, never the DOM
169
- * focus, so a text field in the context bar keeps its caret and its selection; and it fires
170
- * on `pointerdown`, before any gesture has begun. The pane SPLITTER is a sibling of the
171
- * cells rather than a child of one, so a splitter drag never reaches this at all.
172
- * 2. _It must reach clicks inside the canvas._ It cannot: those land in a cross-origin iframe and
173
- * never surface as a parent-realm pointer event. `canvas/iframe-host.ts`'s `hit` /
174
- * `layoutHit` handlers are that seam and call {@link focusPane} themselves, from the pane
175
- * that mounted the artboard.
176
- * 3. _It must cost nothing when the pane is already focused._ {@link focusPane} returns early in
177
- * that case — the guard is in the module that owns focus, because a function handed a
178
- * `paneId` may not read the focus (`scripts/check-pane-singletons.ts` rule 4).
179
- *
180
- * CAPTURE phase, so a surface inside the cell that stops propagation cannot silently take the
181
- * pane's focus with it. `studio.ts`'s commit-on-parent-click listener is on `document`, so it
182
- * still runs first and the outgoing inline-edit session is committed before focus moves.
183
- */
184
- onPointerDown: {
185
- capture: true,
186
- handleEvent: () => {
187
- focusPane(paneId);
188
- },
189
- },
190
- /* Every element field is written on ATTACH and left alone on detach. A caller holding the
191
- record when its pane goes away — `studio.ts`'s primary cell, three tests — asks it what the
192
- cell WAS, and `root.isConnected === false` is the honest answer to that where `root === null`
193
- is a `TypeError`. `_cells` forgetting the pane is what makes the cell gone. */
194
- onRoot: (el?: Element) => {
195
- if (el) {
196
- cell.root = el as HTMLElement;
197
- }
198
- },
199
- onStage: (el?: Element) => {
200
- if (el) {
201
- attachStage(cell, el as HTMLElement);
202
- return;
203
- }
204
- detachStage(cell);
205
- },
206
- onStrip: (el?: Element) => {
207
- if (el) {
208
- cell.strip = el as HTMLElement;
209
- }
210
- },
211
146
  paneId,
212
147
  releaseGestures: null,
213
148
  root: null as unknown as HTMLElement,
@@ -219,15 +154,61 @@ function cellRefs(paneId: string): CellRefs {
219
154
  return cell;
220
155
  }
221
156
 
157
+ /**
158
+ * **A pointer landing anywhere in this cell puts the keyboard in this pane.**
159
+ *
160
+ * Before this, `panels/tab-strip.ts`'s strip row was the ONLY thing in the app that moved
161
+ * `workspace.activePaneId` by pointer, so a click on the side pane's canvas, its context bar, its
162
+ * Library, its Code editor or its entry form selected and edited that pane's document while every
163
+ * keyboard command, the Inspector, the block action bar and the overlay effect went on answering
164
+ * for the other one.
165
+ *
166
+ * ONE listener, on the cell, because the cell is the only thing that knows which pane a click is
167
+ * IN. The alternative — a `focusPane` call in each of the seven surfaces a pane can contain — is a
168
+ * list that a new surface joins by being remembered.
169
+ *
170
+ * Three properties it needs, and where each comes from:
171
+ *
172
+ * 1. _It must not disturb a control mid-interaction._ It moves the pane focus, never the DOM focus, so
173
+ * a text field in the context bar keeps its caret and its selection; and it fires on
174
+ * `pointerdown`, before any gesture has begun. The pane SPLITTER is a sibling of the cells
175
+ * rather than a child of one, so a splitter drag never reaches this at all.
176
+ * 2. _It must reach clicks inside the canvas._ It cannot: those land in a cross-origin iframe and
177
+ * never surface as a parent-realm pointer event. `canvas/iframe-host.ts`'s `hit` / `layoutHit`
178
+ * handlers are that seam and call {@link focusPane} themselves, from the pane that mounted the
179
+ * artboard.
180
+ * 3. _It must cost nothing when the pane is already focused._ {@link focusPane} returns early in that
181
+ * case — the guard is in the module that owns focus, because a function handed a `paneId` may
182
+ * not read the focus (`scripts/check-pane-singletons.ts` rule 4).
183
+ *
184
+ * CAPTURE phase, so a surface inside the cell that stops propagation cannot silently take the
185
+ * pane's focus with it. `studio.ts`'s commit-on-parent-click listener is on `document`, so it still
186
+ * runs first and the outgoing inline-edit session is committed before focus moves.
187
+ *
188
+ * **It is attached here rather than declared in the document** because a document's `on*` key binds
189
+ * through `addEventListener` with no options, so capture cannot be expressed in a Jx document at
190
+ * all. The node's lifetime is the row's, so there is nothing to remove: the listener goes when the
191
+ * repeater drops the node.
192
+ */
193
+ function watchPaneFocus(paneId: string, root: HTMLElement): void {
194
+ root.addEventListener(
195
+ "pointerdown",
196
+ () => {
197
+ focusPane(paneId);
198
+ },
199
+ { capture: true },
200
+ );
201
+ }
202
+
222
203
  /**
223
204
  * Furnish a pane's stage: its surface record, its host registration and its gestures.
224
205
  *
225
- * Runs while the cell is still inside lit's fragment (§18.1 rule 1). The render is SCHEDULED here
226
- * rather than by the caller because nothing else is keyed on a pane appearing — both canvas effects
227
- * key on the active TAB, which a split does not change — and by the time the frame runs, the cell
228
- * is in the document.
206
+ * Runs while the cell is still inside the runtime's render pass (§18.1 rule 1). The render is
207
+ * SCHEDULED here rather than by the caller because nothing else is keyed on a pane appearing — both
208
+ * canvas effects key on the active TAB, which a split does not change — and by the time the frame
209
+ * runs, the cell is in the document.
229
210
  */
230
- function attachStage(cell: CellRefs, stage: HTMLElement): void {
211
+ function attachStage(cell: CellState, stage: HTMLElement): void {
231
212
  cell.stage = stage;
232
213
  cell.surface = createPaneSurface(cell.paneId);
233
214
  registerCanvasSurface(cell.paneId, stage);
@@ -236,110 +217,135 @@ function attachStage(cell: CellRefs, stage: HTMLElement): void {
236
217
  }
237
218
 
238
219
  /**
239
- * Take a pane's stage apart, in the one order that works.
220
+ * One of a cell's five boxes has been created. Fill the record in, and wire what belongs to it.
240
221
  *
241
- * The frames go BEFORE the DOM does. lit notifies a part of its disconnection through
242
- * `_$notifyConnectionChanged` and only then removes the nodes, so `stage` still has its children
243
- * here — which is the whole reason `releaseCanvasHosts` can find them. A frame detached first is
244
- * otherwise noticed only by whichever lazy `liveHosts` walk runs next, so its channel's `window`
245
- * "message" listener and its overlay would outlive the pane. `disposePaneSurface` comes after,
246
- * because it is what stops the artboards' render scopes and clears the record the hosts resolve
247
- * through.
222
+ * The two bars are HANDED their host rather than resolving a region, the same way
223
+ * `panels/frontmatter-panel.ts` is handed the stage. They cannot resolve one the way the tab strip
224
+ * does: the strip's host carries `pane.<id>/tabs` and nothing inside it re-stamps that id, while
225
+ * the jump bar and the context bar both stamp `pane.<id>/jump` and `pane.<id>/context` on markup
226
+ * they render INSIDE their two boxes — so a region on the wrapper as well would put the same id on
227
+ * two nested elements, and `resolveRegion` takes the LAST match. Sixty shots crop
228
+ * `pane.primary/context`; a second, larger element carrying it is a silently widened crop.
248
229
  */
249
- function detachStage(cell: CellRefs): void {
230
+ function cellPart(paneId: string, part: PaneCellPart, element: HTMLElement): void {
231
+ const cell = cellState(paneId);
232
+ if (part === "pane") {
233
+ cell.root = element;
234
+ watchPaneFocus(paneId, element);
235
+ return;
236
+ }
237
+ if (part === "strip") {
238
+ cell.strip = element;
239
+ return;
240
+ }
241
+ if (part === "jump") {
242
+ cell.jump = element;
243
+ attachJumpBarHost(paneId, element);
244
+ return;
245
+ }
246
+ if (part === "chrome") {
247
+ cell.chrome = element;
248
+ attachPaneChromeHost(paneId, element);
249
+ return;
250
+ }
251
+ attachStage(cell, element);
252
+ }
253
+
254
+ /**
255
+ * Take a pane's cell apart, in the one order that works, BEFORE the repeater removes it.
256
+ *
257
+ * The lit predecessor got this ordering from a framework: a part is notified of its disconnection
258
+ * and only then are its nodes removed, so a `ref` detach ran while the stage still had its children
259
+ * in it. A document has no detach counterpart to `onNodeCreated`, so the order is stated here
260
+ * instead — which is the stronger arrangement, because it no longer depends on when a renderer
261
+ * chooses to tell anybody.
262
+ *
263
+ * The order is the document's own, and each step needs the one before it:
264
+ *
265
+ * - The two bars go first, each disposing a mount whose DOM is inside this cell — the runtime that
266
+ * owns it is about to be unreachable.
267
+ * - `releaseCanvasHosts` goes before `disposePaneSurface` and while `stage` still CONTAINS its
268
+ * frames, which is the whole reason it can find them. A frame released later is noticed only by
269
+ * whichever lazy `liveHosts` walk runs next, so its channel's `window` "message" listener and its
270
+ * overlay would outlive the pane.
271
+ * - `disposePaneSurface` comes last, because it is what stops the artboards' render scopes and clears
272
+ * the record the hosts resolve through.
273
+ */
274
+ function disposeCell(cell: CellState): void {
275
+ attachJumpBarHost(cell.paneId, null);
276
+ attachPaneChromeHost(cell.paneId, null);
250
277
  cell.releaseGestures?.();
251
278
  cell.releaseGestures = null;
252
279
  if (cell.stage) {
253
280
  releaseCanvasHosts(cell.stage);
254
281
  }
255
282
  disposePaneSurface(cell.paneId);
283
+ /* Every element field is left alone. A caller holding the record when its pane goes away —
284
+ `studio.ts`'s primary cell, three tests — asks it what the cell WAS, and `root.isConnected ===
285
+ false` is the honest answer to that where `root === null` is a `TypeError`. `_cells` forgetting
286
+ the pane is what makes the cell gone. */
256
287
  _cells.delete(cell.paneId);
257
288
  }
258
289
 
259
- /**
260
- * One pane's cell.
261
- *
262
- * `pane.<id>` goes on the STAGE, not on the cell root. Nine shots crop `pane.primary` meaning "the
263
- * canvas"; moving the id up one level would silently widen all nine to include the strip and the
264
- * two bars, and no gate can tell a wider crop from an intended one.
265
- *
266
- * Nothing lit renders may go INSIDE `.pane-stage`: the canvas host mounts iframes there and
267
- * `canvas/canvas-render.ts` is that element's lit render root — a node cannot have two owners of
268
- * its child list.
269
- */
270
- function cellTpl(paneId: string): TemplateResult {
271
- const cell = cellRefs(paneId);
272
- return html`
273
- <div class="pane" data-pane-id=${paneId} @pointerdown=${cell.onPointerDown} ${ref(cell.onRoot)}>
274
- <div class="pane-strip" data-jx-region=${paneStripRegion(paneId)} ${ref(cell.onStrip)}></div>
275
- <div class="pane-jump" ${ref(cell.onJump)}></div>
276
- <div class="pane-chrome" ${ref(cell.onChrome)}></div>
277
- <div class=${STAGE_CLASS} data-jx-region=${paneRegion(paneId)} ${ref(cell.onStage)}></div>
278
- </div>
279
- `;
290
+ /** `workspace.panes`, as the rows the document draws. */
291
+ function rows(): PaneGridRow[] {
292
+ return workspace.panes.map((pane, index) => ({
293
+ id: pane.id,
294
+ region: paneRegion(pane.id),
295
+ split: index > 0 ? "shown" : "hidden",
296
+ stripRegion: paneStripRegion(pane.id),
297
+ }));
280
298
  }
281
299
 
282
- /**
283
- * Remember the splitter and wire it, once.
284
- *
285
- * A module-level function, so lit's `ref` sees the same callback on every pass and never re-runs
286
- * it. That is the whole of defect S2's fix: a node the template owns is created when the second
287
- * cell appears and removed when it goes, and NOTHING in between re-inserts it — so a pointer
288
- * capture taken on `pointerdown` survives the drag that writes `shell.paneSplit` five times.
289
- */
290
- const onSplitter = (el?: Element): void => {
291
- if (!el) {
292
- return;
293
- }
294
- setupHandle(el as HTMLElement, {
295
- axis: "x",
296
- // The floor is a pane, not a sliver: 320px is the narrowest an Inspector-less editor is
297
- // Usable at, and the same number on both sides is what makes a drag symmetrical.
298
- max: () => Math.max(0.2, 1 - 320 / Math.max(1, gridWidth())),
299
- min: () => Math.min(0.8, 320 / Math.max(1, gridWidth())),
300
- read: () => shell.paneSplit,
301
- reset: () => 0.5,
302
- // A drag is px against the measured grid; the stored value is a RATIO, so the same layout
303
- // Survives a window resize.
304
- scale: () => 1 / Math.max(1, gridWidth()),
305
- settle: () => persistDocks(),
306
- write: (value) => setPaneSplit(value),
307
- });
308
- };
309
-
310
- /** The splitter, as a template. */
311
- function splitterTpl(): TemplateResult {
312
- return html`<div class="resize-handle pane-splitter" ${ref(onSplitter)}></div>`;
300
+ /** Whether two id lists name the same panes in the same order. */
301
+ function sameOrder(a: readonly string[], b: readonly string[]): boolean {
302
+ return a.length === b.length && a.every((id, index) => id === b[index]);
313
303
  }
314
304
 
315
305
  /**
316
- * The grid: every pane's cell, keyed by pane id, with a splitter before every cell but the first.
306
+ * The floor: a pane, not a sliver.
317
307
  *
318
- * Keyed on `pane.id` because the key is what makes lit MOVE a cell instead of rebuilding it. Every
319
- * item renders the SAME template — the splitter slot is a child value that is `nothing` at index 0
320
- * — so a reordering could only ever swap two values, never re-commit a different template over a
321
- * live cell. `workspace.panes[0]` is always the primary and `MAX_PANES` is 2, so in practice no
322
- * item ever moves.
308
+ * 320px is the narrowest an Inspector-less editor is usable at, and the same number on both sides
309
+ * is what makes a drag symmetrical. It is handed to `jx-split` in PIXELS and converted there
310
+ * against the track the element measures for itself, which is the whole reason nothing in this
311
+ * module has to notice a window resize: a fractional floor computed from a width here would be
312
+ * wrong the moment the width changed, and wrong in the direction that lets a pane go below it.
323
313
  */
324
- function gridTemplate(): TemplateResult {
325
- return html`${repeat(
326
- workspace.panes,
327
- (pane) => pane.id,
328
- (pane, index) => html`${index > 0 ? splitterTpl() : nothing}${cellTpl(pane.id)}`,
329
- )}`;
330
- }
314
+ const MIN_PANE_PX = 320;
331
315
 
332
316
  /**
333
317
  * Bring the drawn cells into line with `workspace.panes`. Idempotent by construction.
334
318
  *
319
+ * The pane set is compared before anything is written, and that dirty check is load-bearing rather
320
+ * than thrifty: the effect below re-runs on every `shell.paneSplit` write — which is every
321
+ * `pointermove` of a splitter drag — and a projection that reached the document on each of them
322
+ * would re-commit every binding in every row mid-gesture.
323
+ *
335
324
  * Exported for the tests, which need to drive it without a reactive tick.
336
325
  */
337
326
  export function reconcile(): void {
338
- if (!_grid) {
327
+ const grid = _grid;
328
+ if (!grid || !_surface) {
339
329
  return;
340
330
  }
341
- litRender(gridTemplate(), _grid);
342
- layout();
331
+ const next = rows();
332
+ const ids = next.map((row) => row.id);
333
+ if (!sameOrder(ids, _drawn)) {
334
+ /* BEFORE the projection, so the teardown runs while the cell is still standing. See
335
+ {@link disposeCell}: the repeater removes a departed row's node without telling anybody, so
336
+ the order the frames come down in is this module's to state. */
337
+ for (const paneId of _drawn) {
338
+ if (!ids.includes(paneId)) {
339
+ const cell = _cells.get(paneId);
340
+ if (cell) {
341
+ disposeCell(cell);
342
+ }
343
+ }
344
+ }
345
+ _drawn = ids;
346
+ _surface.update(next, shell.paneSplit);
347
+ }
348
+ layout(grid);
343
349
  }
344
350
 
345
351
  /**
@@ -349,23 +355,21 @@ export function reconcile(): void {
349
355
  * `workspace.panes.length`, and the shell effect must not read the workspace — a dock resize would
350
356
  * then re-run on every tab change. The shell owns `--pane-split`; the grid owns what it means.
351
357
  *
352
- * It writes ONE style property and touches no child, which is the other half of S2: a `pointermove`
353
- * that lands here can move a track edge but can never move a node.
358
+ * It writes ONE style property, on the HOST rather than on anything the document drew, and touches
359
+ * no child: a `pointermove` that lands here can move a track edge but can never move a node. It
360
+ * counts PANES rather than drawn cells, because the document's rows land one microtask after the
361
+ * model does and a track count that lagged them would flash a full-width primary on every split.
362
+ *
363
+ * The host is a PARAMETER rather than a module read, because {@link reconcile} has already answered
364
+ * "is there a grid" and a second null check here is a branch nothing can reach.
354
365
  */
355
- function layout(): void {
356
- if (!_grid) {
357
- return;
358
- }
359
- if (paneCells().length < 2) {
360
- _grid.style.gridTemplateColumns = "minmax(0, 1fr)";
366
+ function layout(grid: HTMLElement): void {
367
+ if (workspace.panes.length < 2) {
368
+ grid.style.gridTemplateColumns = "minmax(0, 1fr)";
361
369
  return;
362
370
  }
363
371
  const split = shell.paneSplit;
364
- _grid.style.gridTemplateColumns = `minmax(0, ${split}fr) 5px minmax(0, ${1 - split}fr)`;
365
- }
366
-
367
- function gridWidth(): number {
368
- return _grid?.clientWidth ?? 0;
372
+ grid.style.gridTemplateColumns = `minmax(0, ${split}fr) 5px minmax(0, ${1 - split}fr)`;
369
373
  }
370
374
 
371
375
  /** Mount the grid. Called by `shell.ts`'s `mountShell()`, like every other shell surface. */
@@ -377,6 +381,21 @@ export function mount(): void {
377
381
  if (!_grid) {
378
382
  return;
379
383
  }
384
+ const first = rows();
385
+ _drawn = first.map((row) => row.id);
386
+ _surface = mountPaneGridSurface(
387
+ _grid,
388
+ first,
389
+ {
390
+ collapse: DEFAULT_PANE_SPLIT,
391
+ gap: MIN_PANE_PX,
392
+ max: PANE_SPLIT_MAX,
393
+ min: PANE_SPLIT_MIN,
394
+ value: shell.paneSplit,
395
+ },
396
+ { cellPart, move: setPaneSplit, settle: persistDocks },
397
+ );
398
+ _ready = _surface.ready;
380
399
  _scope = effectScope();
381
400
  _scope.run(() => {
382
401
  effect(() => {
@@ -393,12 +412,17 @@ export function mount(): void {
393
412
  export function unmount(): void {
394
413
  _scope?.stop();
395
414
  _scope = null;
396
- if (_grid) {
397
- /* One statement takes every cell apart. Rendering `nothing` disconnects the template's parts —
398
- which runs every `ref` detach above, in document order, while the nodes are still in place —
399
- and then removes them. Doing it by hand would be a second teardown path to keep in step. */
400
- litRender(nothing, _grid);
415
+ /* Every cell is taken apart by the same ordered teardown a departing one gets, and BEFORE the
416
+ document goes: doing it afterwards would hand `releaseCanvasHosts` a stage whose frames the
417
+ runtime had already dropped. */
418
+ const standing = [..._cells.values()];
419
+ for (const cell of standing) {
420
+ disposeCell(cell);
401
421
  }
422
+ _surface?.dispose();
423
+ _surface = null;
424
+ _ready = null;
425
+ _drawn = [];
402
426
  _cells.clear();
403
427
  _grid = null;
404
428
  }