@jxsuite/studio 6.1.1 → 8.0.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 (785) hide show
  1. package/README.md +5 -5
  2. package/dist/chunks/2020-5wtzce5g.js +4 -0
  3. package/dist/chunks/{action-group-2mqby73c.js → action-group-4aan7g6g.js} +3 -3
  4. package/dist/chunks/adapter-5tphg7xe.js +4 -0
  5. package/dist/chunks/add-new-flow-psym14sn.js +4 -0
  6. package/dist/chunks/cf-account-picker-haqazq5r.js +4 -0
  7. package/dist/chunks/cf-settings-h2wrh6hq.js +4 -0
  8. package/dist/chunks/color-area-6rbra5dy.js +4 -0
  9. package/dist/chunks/color-field-gy56c94g.js +4 -0
  10. package/dist/chunks/color-m7r1vk54.js +4 -0
  11. package/dist/chunks/color-slider-sqqdyf6c.js +4 -0
  12. package/dist/chunks/combobox-72h32ejk.js +4 -0
  13. package/dist/chunks/components-r22xca09.js +4 -0
  14. package/dist/chunks/content-source-mmppmcyg.js +4 -0
  15. package/dist/chunks/convert-to-repeater-he70fc2b.js +4 -0
  16. package/dist/chunks/dialog-c4x05vyx.js +4 -0
  17. package/dist/chunks/editor-ccd9jwsa.js +4 -0
  18. package/dist/chunks/entry-commands-6qxz9e8p.js +4 -0
  19. package/dist/chunks/extension-sections-0egb88dm.js +4 -0
  20. package/dist/chunks/file-ops-qnab36ek.js +4 -0
  21. package/dist/chunks/files-rcn56qvm.js +4 -0
  22. package/dist/chunks/format-host-yxz0kjjs.js +4 -0
  23. package/dist/chunks/fs-events-d1qxq4z4.js +4 -0
  24. package/dist/chunks/grid-open-4z1g062t.js +4 -0
  25. package/dist/chunks/grid-view-qg1b5eqx.js +51 -0
  26. package/dist/chunks/icons-63bjtbs7.js +4 -0
  27. package/dist/chunks/iframe-entry-2zggzajv.js +5 -0
  28. package/dist/chunks/iframe-entry-76j8vwwx.js +5 -0
  29. package/dist/chunks/iframe-entry-99nr5zfb.js +5 -0
  30. package/dist/chunks/iframe-entry-pkgmcgp0.js +5 -0
  31. package/dist/chunks/iframe-host-ssv7kyj0.js +4 -0
  32. package/dist/chunks/{index-2veqmnhr.js → index-32eqzv1k.js} +3 -3
  33. package/dist/chunks/index-f0jn7nnp.js +4 -0
  34. package/dist/chunks/{javascript-8nbvs12s.js → javascript-pjp734q4.js} +3 -3
  35. package/dist/chunks/jsonMode-njkvsa02.js +13 -0
  36. package/dist/chunks/jx-validate-ttasrbh7.js +4 -0
  37. package/dist/chunks/layers-bgyr11tr.js +4 -0
  38. package/dist/chunks/layers-panel-7ez66zeq.js +4 -0
  39. package/dist/chunks/listbox-c421cf8c.js +4 -0
  40. package/dist/chunks/lit-html-v9gh687x.js +4 -0
  41. package/dist/chunks/media-library-sq468wts.js +4 -0
  42. package/dist/chunks/media-picker-shfe59pt.js +4 -0
  43. package/dist/chunks/menu-bdpzbfvs.js +4 -0
  44. package/dist/chunks/monaco-binding-vq1hgnn0.js +4 -0
  45. package/dist/chunks/monaco-buffer-3afgwvbp.js +4 -0
  46. package/dist/chunks/monaco-setup-xrd2cbkp.js +4 -0
  47. package/dist/chunks/notify-kzrjr3fv.js +4 -0
  48. package/dist/chunks/number-field-y62xq0ty.js +4 -0
  49. package/dist/chunks/parse-v77xtwv7.js +4 -0
  50. package/dist/chunks/platform-7v4rtd7m.js +4 -0
  51. package/dist/chunks/popover-b89sfej7.js +4 -0
  52. package/dist/chunks/publish-panel-2z9gw8ee.js +4 -0
  53. package/dist/chunks/publish-quick-qx2455cj.js +4 -0
  54. package/dist/chunks/publish-workspace-wrc1dgwx.js +4 -0
  55. package/dist/chunks/quick-search-n0t0eqxb.js +4 -0
  56. package/dist/chunks/references-9547rg5s.js +4 -0
  57. package/dist/chunks/right-panel-wsgnr9pn.js +4 -0
  58. package/dist/chunks/select-rzds841e.js +4 -0
  59. package/dist/chunks/source-control-kwktr9y6.js +4 -0
  60. package/dist/chunks/split-c9zfdgxf.js +4 -0
  61. package/dist/chunks/studio-03ct6yfv.js +9 -0
  62. package/dist/chunks/studio-07rcfje3.js +5 -0
  63. package/dist/chunks/studio-3e54qh39.js +7 -0
  64. package/dist/chunks/studio-3exxhz5g.js +5 -0
  65. package/dist/chunks/studio-3q40e3rj.js +5 -0
  66. package/dist/chunks/studio-3qeh9t0h.js +6 -0
  67. package/dist/chunks/studio-3qqjeep5.js +6 -0
  68. package/dist/chunks/studio-43sd2mam.js +4 -0
  69. package/dist/chunks/studio-4hp8pkyt.js +5 -0
  70. package/dist/chunks/studio-58xg6j2q.js +9 -0
  71. package/dist/chunks/studio-5g0rprdt.js +7 -0
  72. package/dist/chunks/studio-6m380wp7.js +5 -0
  73. package/dist/chunks/studio-80jyh1xh.js +4 -0
  74. package/dist/chunks/studio-83mk4bgg.js +9 -0
  75. package/dist/chunks/{studio-kggey1jt.js → studio-881z1xrx.js} +4 -4
  76. package/dist/chunks/studio-8m0y2qe7.js +5 -0
  77. package/dist/chunks/studio-8tr6cfrc.js +5 -0
  78. package/dist/chunks/studio-8vcvfs7r.js +6 -0
  79. package/dist/chunks/studio-9tmebf0s.js +5 -0
  80. package/dist/chunks/studio-a2bvaptz.js +5 -0
  81. package/dist/chunks/studio-b7y5mr30.js +5 -0
  82. package/dist/chunks/studio-bavb31r0.js +5 -0
  83. package/dist/chunks/studio-bbe4c90a.js +5 -0
  84. package/dist/chunks/studio-c05wdnt3.js +5 -0
  85. package/dist/chunks/studio-ccv2svc6.js +5 -0
  86. package/dist/chunks/studio-d5grtb9e.js +16 -0
  87. package/dist/chunks/studio-df0baea9.js +5 -0
  88. package/dist/chunks/studio-dgk4dqtx.js +7 -0
  89. package/dist/chunks/studio-e1g64q43.js +5 -0
  90. package/dist/chunks/studio-ed1hp6zf.js +6 -0
  91. package/dist/chunks/studio-ed3xx40h.js +7 -0
  92. package/dist/chunks/studio-f14e1zdj.js +5 -0
  93. package/dist/chunks/studio-fbhxxc5f.js +5 -0
  94. package/dist/chunks/studio-fyh0jc96.js +5 -0
  95. package/dist/chunks/studio-gcxk20n7.js +5 -0
  96. package/dist/chunks/studio-jaaz8ce9.js +5 -0
  97. package/dist/chunks/{studio-8grwxtqk.js → studio-jcnsdaep.js} +4 -4
  98. package/dist/chunks/studio-jsgf2q5k.js +7 -0
  99. package/dist/chunks/{studio-1q1sh2ax.js → studio-k3xxbagr.js} +3 -3
  100. package/dist/chunks/studio-k7evxqh4.js +5 -0
  101. package/dist/chunks/studio-kt3pjkwd.js +5 -0
  102. package/dist/chunks/studio-m8g16bgz.js +5 -0
  103. package/dist/chunks/studio-mn0476ag.js +5 -0
  104. package/dist/chunks/studio-mx1smyq0.js +5 -0
  105. package/dist/chunks/studio-n0c5h6mf.js +5 -0
  106. package/dist/chunks/{studio-rv11kfwa.js → studio-nh6d342n.js} +7 -7
  107. package/dist/chunks/studio-nqxqf8p2.js +4 -0
  108. package/dist/chunks/studio-p4nrkvqr.js +5 -0
  109. package/dist/chunks/{studio-h46vtzk8.js → studio-pbxa5fpc.js} +4 -4
  110. package/dist/chunks/studio-pgmvdv7z.js +7 -0
  111. package/dist/chunks/studio-ph84avkf.js +5 -0
  112. package/dist/chunks/{studio-wszjyh0j.js → studio-q0mvbaq6.js} +4 -4
  113. package/dist/chunks/studio-q0tccd43.js +5 -0
  114. package/dist/chunks/studio-q7rtx6zq.js +14 -0
  115. package/dist/chunks/{studio-pre0skq6.js → studio-qgh13jtz.js} +4 -4
  116. package/dist/chunks/studio-qmn1xc24.js +5 -0
  117. package/dist/chunks/studio-qwdqwkj0.js +5 -0
  118. package/dist/chunks/{studio-t6b6qe31.js → studio-r5qy30vx.js} +4 -4
  119. package/dist/chunks/studio-rj9eamf8.js +5 -0
  120. package/dist/chunks/studio-rptgsk96.js +5 -0
  121. package/dist/chunks/studio-rthyg42z.js +928 -0
  122. package/dist/chunks/studio-s19tp1xr.js +5 -0
  123. package/dist/chunks/studio-s32578pn.js +5 -0
  124. package/dist/chunks/studio-svb7yyve.js +5 -0
  125. package/dist/chunks/studio-syreb644.js +5 -0
  126. package/dist/chunks/studio-tf9jryb1.js +10 -0
  127. package/dist/chunks/studio-tfkznv39.js +5 -0
  128. package/dist/chunks/studio-tft6r105.js +5 -0
  129. package/dist/chunks/studio-txf2kysx.js +5 -0
  130. package/dist/chunks/studio-v8867ddv.js +12 -0
  131. package/dist/chunks/studio-vxq7fcvv.js +5 -0
  132. package/dist/chunks/studio-w4rz9hdk.js +5 -0
  133. package/dist/chunks/studio-wbcjbhy1.js +9 -0
  134. package/dist/chunks/studio-wc649wss.js +5 -0
  135. package/dist/chunks/studio-wys23mxz.js +5 -0
  136. package/dist/chunks/studio-x1x167jh.js +5 -0
  137. package/dist/chunks/studio-x9hsze61.js +5 -0
  138. package/dist/chunks/studio-y0n7yxea.js +9 -0
  139. package/dist/chunks/studio-y3ahhdpx.js +5 -0
  140. package/dist/chunks/studio-z97c48fk.js +5 -0
  141. package/dist/chunks/studio-zat62m0c.js +5 -0
  142. package/dist/chunks/studio-zcd2e7yz.js +5 -0
  143. package/dist/chunks/studio-zz9qe3rw.js +64 -0
  144. package/dist/chunks/swatch-group-ewh24n1f.js +4 -0
  145. package/dist/chunks/tabs-f5cw3tgp.js +4 -0
  146. package/dist/chunks/textfield-wewc8rq2.js +4 -0
  147. package/dist/chunks/toast-1gv0h53h.js +4 -0
  148. package/dist/chunks/toast-host-1yg5ar8d.js +4 -0
  149. package/dist/chunks/{toolbar-2nnzqhrj.js → toolbar-44cm5cta.js} +3 -3
  150. package/dist/chunks/tooltip-yypz28gq.js +4 -0
  151. package/dist/chunks/tree-9xa4hm7n.js +4 -0
  152. package/dist/chunks/tsMode-epyfz1hv.js +14 -0
  153. package/dist/chunks/workspace-dz7nxfhv.js +4 -0
  154. package/dist/chunks/workspace-home-xsd7hkjw.js +4 -0
  155. package/dist/chunks/ws-client-bv552sfx.js +4 -0
  156. package/dist/iframe-entry.js +20 -16
  157. package/dist/manifest.json +77 -63
  158. package/dist/studio.js +106 -107
  159. package/dist/workers/ts.worker.js +3 -3
  160. package/package.json +17 -16
  161. package/src/about/about-modal.ts +4 -3
  162. package/src/account/action-flow.ts +45 -38
  163. package/src/account-status.ts +7 -2
  164. package/src/browse/component-examples.ts +272 -0
  165. package/src/browse/library-commands.ts +64 -104
  166. package/src/browse/library-layouts.ts +270 -53
  167. package/src/browse/library-model.ts +194 -134
  168. package/src/browse/library-pane.ts +748 -440
  169. package/src/browse/library-preview.ts +161 -26
  170. package/src/browse/library-source.ts +308 -41
  171. package/src/browse/preview-root.ts +78 -0
  172. package/src/browse/preview-style.ts +155 -0
  173. package/src/canvas/asset-refs.ts +40 -10
  174. package/src/canvas/asset-resolve.ts +19 -23
  175. package/src/canvas/canvas-helpers.ts +7 -6
  176. package/src/canvas/canvas-live-render.ts +260 -77
  177. package/src/canvas/canvas-origin.ts +37 -12
  178. package/src/canvas/canvas-page-context.ts +112 -0
  179. package/src/canvas/canvas-patcher.ts +30 -30
  180. package/src/canvas/canvas-perf.ts +11 -5
  181. package/src/canvas/canvas-render.ts +175 -148
  182. package/src/canvas/canvas-surface.ts +5 -3
  183. package/src/canvas/canvas-utils.ts +147 -61
  184. package/src/canvas/dialog-path.ts +9 -4
  185. package/src/canvas/dialog-state.ts +1 -2
  186. package/src/canvas/diff-marks.ts +9 -16
  187. package/src/canvas/diff-toolbar.ts +8 -7
  188. package/src/canvas/edit-width-drag.ts +4 -1
  189. package/src/canvas/edit-width.ts +1 -3
  190. package/src/canvas/iframe-channel.ts +27 -13
  191. package/src/canvas/iframe-drop.ts +120 -12
  192. package/src/canvas/iframe-editable-root.ts +29 -16
  193. package/src/canvas/iframe-entry.ts +104 -75
  194. package/src/canvas/iframe-eval.ts +73 -49
  195. package/src/canvas/iframe-host.ts +269 -158
  196. package/src/canvas/iframe-inline-edit.ts +49 -30
  197. package/src/canvas/iframe-insert.ts +5 -4
  198. package/src/canvas/iframe-interaction.ts +30 -25
  199. package/src/canvas/iframe-keys.ts +11 -0
  200. package/src/canvas/iframe-overlay.ts +1 -1
  201. package/src/canvas/iframe-patch.ts +25 -21
  202. package/src/canvas/iframe-position.ts +12 -6
  203. package/src/canvas/iframe-protocol.ts +36 -21
  204. package/src/canvas/iframe-render.ts +84 -35
  205. package/src/canvas/iframe-slash.ts +24 -11
  206. package/src/canvas/iframe-subtree.ts +10 -3
  207. package/src/canvas/npm-modules.ts +22 -0
  208. package/src/canvas/path-mapping.ts +19 -12
  209. package/src/canvas/popover-path.ts +7 -3
  210. package/src/canvas/popover-state.ts +1 -2
  211. package/src/canvas/preview-navigate.ts +16 -0
  212. package/src/canvas/repeater-sources.ts +200 -0
  213. package/src/canvas/serialize-scope.ts +13 -5
  214. package/src/canvas/stage-scrollbar.ts +7 -2
  215. package/src/canvas/surface-registry.ts +27 -3
  216. package/src/collab/collab-commands.ts +7 -2
  217. package/src/collab/collab-session.ts +84 -45
  218. package/src/collab/collab-state.ts +4 -3
  219. package/src/collab/monaco-binding.ts +16 -13
  220. package/src/collab/monaco-cursors.ts +5 -2
  221. package/src/commands/app-commands.ts +16 -6
  222. package/src/commands/budget.ts +13 -11
  223. package/src/commands/command-args.ts +103 -71
  224. package/src/commands/context.ts +55 -28
  225. package/src/commands/defaults.ts +69 -45
  226. package/src/commands/keymap.ts +56 -43
  227. package/src/commands/levels.ts +14 -8
  228. package/src/commands/live-context.ts +9 -7
  229. package/src/commands/reference.ts +2 -1
  230. package/src/commands/registry.ts +22 -11
  231. package/src/commands/run-reported.ts +15 -10
  232. package/src/component-props.ts +46 -30
  233. package/src/content/collection-match.ts +11 -9
  234. package/src/content/collection-pages.ts +412 -0
  235. package/src/content/content-collections.ts +270 -0
  236. package/src/content/draft-state.ts +4 -3
  237. package/src/content/entry-commands.ts +54 -62
  238. package/src/content/entry-editor.ts +137 -32
  239. package/src/content/entry-fields.ts +25 -8
  240. package/src/content/entry-inspector.ts +225 -0
  241. package/src/content/entry-model.ts +38 -15
  242. package/src/content/entry-open.ts +80 -0
  243. package/src/content/entry-summaries.ts +440 -0
  244. package/src/content/file-record-ids.ts +80 -0
  245. package/src/content/file-records.ts +125 -0
  246. package/src/content/page-summaries.ts +129 -0
  247. package/src/content/record-entry.ts +201 -0
  248. package/src/content/writing-banner.ts +177 -0
  249. package/src/create/add-new-commands.ts +185 -0
  250. package/src/create/add-new-flow.ts +1116 -0
  251. package/src/create/add-new.ts +1014 -0
  252. package/src/create/element-name.ts +121 -0
  253. package/src/editor/canvas-context-menu.ts +1 -2
  254. package/src/editor/canvas-slash-bridge.ts +3 -6
  255. package/src/editor/content-slice.ts +16 -14
  256. package/src/editor/context-menu.ts +35 -22
  257. package/src/editor/convert-targets.ts +30 -35
  258. package/src/editor/convert-to-component.ts +78 -48
  259. package/src/editor/convert-to-repeater.ts +34 -22
  260. package/src/editor/editable-tags.ts +17 -2
  261. package/src/editor/file-drop-action.ts +29 -22
  262. package/src/editor/inline-edit-apply.ts +79 -17
  263. package/src/editor/inline-edit.ts +76 -50
  264. package/src/editor/inline-format.ts +59 -56
  265. package/src/editor/inline-link.ts +40 -15
  266. package/src/editor/insert-component.ts +91 -0
  267. package/src/editor/insert-zone-action.ts +14 -5
  268. package/src/editor/merge-tags.ts +58 -19
  269. package/src/editor/repeater-scope.ts +39 -28
  270. package/src/editor/shortcuts.ts +76 -42
  271. package/src/editor/slash-menu.ts +52 -14
  272. package/src/files/asset-versions.ts +4 -4
  273. package/src/files/components.ts +89 -20
  274. package/src/files/elements.ts +5 -6
  275. package/src/files/file-ops.ts +92 -41
  276. package/src/files/files.ts +248 -239
  277. package/src/files/fs-events.ts +18 -7
  278. package/src/files/media-meta.ts +97 -2
  279. package/src/files/media-paths.ts +3 -0
  280. package/src/files/media-types.ts +55 -0
  281. package/src/files/media-upload.ts +209 -52
  282. package/src/files/new-file-name.ts +156 -0
  283. package/src/files/serialize-document.ts +15 -5
  284. package/src/format/constraints.ts +1 -1
  285. package/src/format/convert-file.ts +27 -13
  286. package/src/format/format-choices.ts +2 -1
  287. package/src/format/format-host.ts +91 -50
  288. package/src/github/github-auth-status.ts +27 -3
  289. package/src/github/github-auth.ts +21 -11
  290. package/src/github/github-publish.ts +28 -23
  291. package/src/grid/cell-editors.ts +46 -31
  292. package/src/grid/cell-popovers.ts +5 -3
  293. package/src/grid/edit-buffer.ts +10 -9
  294. package/src/grid/grid-controller.ts +25 -23
  295. package/src/grid/grid-layout.ts +21 -5
  296. package/src/grid/grid-open.ts +3 -33
  297. package/src/grid/grid-panel.ts +65 -6
  298. package/src/grid/grid-reveal.ts +34 -0
  299. package/src/grid/grid-source.ts +9 -31
  300. package/src/grid/grid-view.ts +45 -23
  301. package/src/grid/redirects-grid.ts +14 -5
  302. package/src/grid/redirects.ts +16 -14
  303. package/src/grid/schema-columns.ts +25 -19
  304. package/src/grid/sources/connector-source.ts +47 -20
  305. package/src/grid/sources/content-source.ts +100 -31
  306. package/src/grid/sources/csv-file-source.ts +2 -2
  307. package/src/grid/tabulator-tables.d.ts +61 -34
  308. package/src/hosting/document.ts +2 -2
  309. package/src/hosting/stage.ts +7 -7
  310. package/src/i18n/i18n-commands.ts +3 -3
  311. package/src/media/media-commands.ts +181 -0
  312. package/src/media/media-library-model.ts +232 -0
  313. package/src/media/media-library.ts +1283 -0
  314. package/src/media/media-pane.ts +51 -20
  315. package/src/new-project/add-repo-modal.ts +20 -19
  316. package/src/new-project/import-tab.ts +10 -14
  317. package/src/new-project/location-fields.ts +2 -1
  318. package/src/new-project/new-project-modal.ts +12 -9
  319. package/src/packages/jxsuite-update.ts +4 -3
  320. package/src/packages/pull-package-sync.ts +26 -25
  321. package/src/page-params.ts +97 -68
  322. package/src/panels/activity-panel.ts +38 -14
  323. package/src/panels/ai-chat/attached-context.ts +13 -7
  324. package/src/panels/ai-chat/chat-markdown.ts +8 -8
  325. package/src/panels/ai-chat/chat-view.ts +80 -73
  326. package/src/panels/ai-chat/composer.ts +30 -33
  327. package/src/panels/ai-chat/sessions-view.ts +1 -2
  328. package/src/panels/ai-panel.ts +23 -20
  329. package/src/panels/block-action-bar.ts +57 -46
  330. package/src/panels/bottom-dock.ts +5 -3
  331. package/src/panels/canvas-dnd-bridge.ts +36 -26
  332. package/src/panels/chat-panel.ts +6 -6
  333. package/src/panels/collections-panel.ts +306 -0
  334. package/src/panels/component-preview.ts +35 -13
  335. package/src/panels/data-explorer.ts +63 -26
  336. package/src/panels/data-grid.ts +18 -11
  337. package/src/panels/dnd.ts +115 -138
  338. package/src/panels/editors.ts +52 -21
  339. package/src/panels/elements-panel.ts +19 -5
  340. package/src/panels/empty-state.ts +2 -2
  341. package/src/panels/events-panel.ts +84 -69
  342. package/src/panels/formula-workspace.ts +78 -54
  343. package/src/panels/frontmatter-fields.ts +33 -28
  344. package/src/panels/git-diff-open.ts +10 -6
  345. package/src/panels/head-panel.ts +488 -202
  346. package/src/panels/i18n-panel.ts +20 -12
  347. package/src/panels/imports-panel.ts +25 -47
  348. package/src/panels/jump-bar.ts +75 -31
  349. package/src/panels/layers-panel.ts +130 -101
  350. package/src/panels/left-panel.ts +25 -11
  351. package/src/panels/navigator-panels.ts +23 -11
  352. package/src/panels/pane-context.ts +140 -49
  353. package/src/panels/pane-grid.ts +55 -26
  354. package/src/panels/panel-registry.ts +36 -46
  355. package/src/panels/properties-panel.ts +182 -124
  356. package/src/panels/quick-search.ts +53 -31
  357. package/src/panels/right-panel.ts +201 -45
  358. package/src/panels/seo-modal.ts +54 -125
  359. package/src/panels/settings-menu.ts +63 -61
  360. package/src/panels/settings-pane.ts +268 -162
  361. package/src/panels/signals-panel.ts +156 -135
  362. package/src/panels/site-model.ts +367 -0
  363. package/src/panels/site-panel.ts +638 -0
  364. package/src/panels/statement-editor.ts +230 -140
  365. package/src/panels/style-panel.ts +253 -206
  366. package/src/panels/style-utils.ts +114 -51
  367. package/src/panels/stylebook-doc.ts +195 -60
  368. package/src/panels/stylebook-layers-panel.ts +48 -89
  369. package/src/panels/stylebook-panel.ts +60 -27
  370. package/src/panels/styles-panel.ts +327 -0
  371. package/src/panels/tab-drop.ts +27 -11
  372. package/src/panels/tab-strip.ts +143 -49
  373. package/src/platform-errors.ts +57 -43
  374. package/src/platform.ts +59 -31
  375. package/src/platforms/cloud.ts +373 -176
  376. package/src/platforms/default-platform.ts +14 -8
  377. package/src/platforms/devserver.ts +180 -142
  378. package/src/platforms/read-batcher.ts +16 -8
  379. package/src/project-list.ts +2 -1
  380. package/src/publish/deploy-checklist.ts +20 -9
  381. package/src/publish/pages-service.ts +66 -27
  382. package/src/publish/publish-changes.ts +491 -0
  383. package/src/publish/publish-commands.ts +88 -22
  384. package/src/publish/publish-ids.ts +49 -0
  385. package/src/publish/publish-panel.ts +10 -6
  386. package/src/publish/publish-quick.ts +148 -0
  387. package/src/publish/publish-workspace.ts +926 -0
  388. package/src/publish/source-control.ts +907 -0
  389. package/src/recent-projects.ts +15 -8
  390. package/src/resize-edges.ts +26 -36
  391. package/src/services/a11y-report.ts +26 -19
  392. package/src/services/agent-seed.ts +23 -11
  393. package/src/services/ai-ask.ts +9 -12
  394. package/src/services/ai-command-tools.ts +68 -62
  395. package/src/services/ai-import-tools.ts +80 -67
  396. package/src/services/ai-models.ts +17 -13
  397. package/src/services/ai-project-tools.ts +68 -32
  398. package/src/services/ai-session-store.ts +19 -10
  399. package/src/services/ai-system-prompt.ts +46 -20
  400. package/src/services/ai-tools.ts +71 -27
  401. package/src/services/ai-write-report.ts +20 -12
  402. package/src/services/ai-writes.ts +4 -4
  403. package/src/services/automation.ts +74 -51
  404. package/src/services/cem-export.ts +149 -58
  405. package/src/services/cf-settings.ts +4 -4
  406. package/src/services/clock.ts +6 -1
  407. package/src/services/code-services.ts +37 -31
  408. package/src/services/connection.ts +2 -2
  409. package/src/services/context-manager.ts +14 -10
  410. package/src/services/context-resolver.ts +20 -8
  411. package/src/services/data-service.ts +19 -15
  412. package/src/services/directory-picker.ts +20 -14
  413. package/src/services/document-assistant.ts +39 -19
  414. package/src/services/gated-registry.ts +10 -6
  415. package/src/services/harness/chat-reducer.ts +23 -1
  416. package/src/services/import-client.ts +91 -38
  417. package/src/services/import-seed.ts +8 -2
  418. package/src/services/jx-validate.ts +28 -31
  419. package/src/services/live-preview.ts +0 -0
  420. package/src/services/live-surfaces.ts +6 -3
  421. package/src/services/model-uri.ts +10 -2
  422. package/src/services/module-preloads.ts +12 -8
  423. package/src/services/monaco-buffer.ts +43 -24
  424. package/src/services/monaco-lazy.ts +7 -8
  425. package/src/services/monaco-setup.ts +23 -16
  426. package/src/services/notify.ts +44 -17
  427. package/src/services/popover-report.ts +25 -10
  428. package/src/services/preview-eval.ts +20 -8
  429. package/src/services/profile.ts +13 -6
  430. package/src/services/project-path.ts +13 -10
  431. package/src/services/references.ts +0 -0
  432. package/src/services/render-critic.ts +29 -9
  433. package/src/services/settings/definitions.ts +25 -14
  434. package/src/services/settings/kernel.ts +13 -4
  435. package/src/services/settings/write-queue.ts +9 -9
  436. package/src/services/token-lint.ts +12 -10
  437. package/src/services/tool-executor.ts +17 -5
  438. package/src/services/tool-outcomes.ts +44 -24
  439. package/src/services/trusted-types.ts +24 -7
  440. package/src/settings/contexts-section.ts +11 -8
  441. package/src/settings/contributed-section.ts +128 -70
  442. package/src/settings/css-vars-editor.ts +119 -32
  443. package/src/settings/defs-editor.ts +38 -35
  444. package/src/settings/developer-panels.ts +43 -0
  445. package/src/settings/extension-rows.ts +16 -8
  446. package/src/settings/extension-sections.ts +42 -14
  447. package/src/settings/extensions-section.ts +4 -1
  448. package/src/settings/general-settings.ts +101 -28
  449. package/src/settings/locales-section.ts +20 -12
  450. package/src/settings/preferences-dialog.ts +17 -7
  451. package/src/settings/preferences-keymap.ts +7 -5
  452. package/src/settings/preferences-sections.ts +11 -5
  453. package/src/settings/project-sections.ts +28 -11
  454. package/src/settings/schema-field-ui.ts +12 -1
  455. package/src/settings/section-registry.ts +116 -11
  456. package/src/settings/settings-dialog.ts +192 -0
  457. package/src/settings/settings-document.ts +186 -166
  458. package/src/shell/tree.ts +6 -5
  459. package/src/shell.ts +536 -477
  460. package/src/site-context.ts +101 -32
  461. package/src/state.ts +68 -57
  462. package/src/store.ts +54 -34
  463. package/src/studio.ts +219 -116
  464. package/src/style/live-preview.ts +1 -1
  465. package/src/style/project-styles.ts +40 -7
  466. package/src/style/stylebook-specimens.ts +89 -0
  467. package/src/style/stylebook-target.ts +26 -0
  468. package/src/style/tag-inheritance.ts +105 -0
  469. package/src/style/token-ref.ts +5 -4
  470. package/src/style/variables-focus.ts +61 -0
  471. package/src/style/variables-view.ts +235 -0
  472. package/src/surfaces/about.ts +18 -19
  473. package/src/surfaces/add-new.json +1482 -0
  474. package/src/surfaces/add-new.ts +325 -0
  475. package/src/surfaces/add-repo.ts +17 -19
  476. package/src/surfaces/ai-chat.ts +33 -38
  477. package/src/surfaces/ai-credentials-form.ts +6 -16
  478. package/src/surfaces/ai-managed-connect.ts +3 -4
  479. package/src/surfaces/ai-model-picker.ts +3 -5
  480. package/src/surfaces/block-action-bar.ts +22 -11
  481. package/src/surfaces/boot-failure.json +4 -3
  482. package/src/surfaces/boot-failure.ts +4 -5
  483. package/src/surfaces/bottom-dock.json +1 -1
  484. package/src/surfaces/bottom-dock.ts +9 -18
  485. package/src/surfaces/canvas-stage.json +30 -19
  486. package/src/surfaces/canvas-stage.ts +17 -16
  487. package/src/surfaces/cf-account-picker.ts +17 -18
  488. package/src/surfaces/collections-panel.json +615 -0
  489. package/src/surfaces/collections-panel.ts +168 -0
  490. package/src/surfaces/commandbar.json +241 -96
  491. package/src/surfaces/commandbar.ts +145 -105
  492. package/src/surfaces/convert-repeater.ts +17 -19
  493. package/src/surfaces/data-actions.ts +9 -7
  494. package/src/surfaces/dialog.ts +26 -28
  495. package/src/surfaces/diff-toolbar.ts +9 -7
  496. package/src/surfaces/empty-state.json +1 -1
  497. package/src/surfaces/empty-state.ts +7 -10
  498. package/src/surfaces/entry-editor.json +136 -0
  499. package/src/surfaces/entry-editor.ts +35 -11
  500. package/src/surfaces/entry-inspector.json +371 -0
  501. package/src/surfaces/entry-inspector.ts +143 -0
  502. package/src/surfaces/expression-editor.ts +11 -25
  503. package/src/surfaces/files-panel.ts +27 -14
  504. package/src/surfaces/formula-palette.ts +4 -5
  505. package/src/surfaces/github-auth.ts +16 -20
  506. package/src/surfaces/github-publish.ts +17 -22
  507. package/src/surfaces/grid-cell.ts +30 -31
  508. package/src/surfaces/grid-open.ts +10 -14
  509. package/src/surfaces/grid-panel.ts +13 -9
  510. package/src/surfaces/grid-replace.ts +13 -13
  511. package/src/surfaces/grid-views.ts +15 -18
  512. package/src/surfaces/inspector-dock.json +162 -14
  513. package/src/surfaces/inspector-dock.ts +59 -31
  514. package/src/surfaces/jump-bar.ts +5 -6
  515. package/src/surfaces/jxsuite-update.ts +16 -22
  516. package/src/surfaces/library-pane.json +670 -437
  517. package/src/surfaces/library-pane.ts +111 -77
  518. package/src/surfaces/logic-panel.ts +17 -30
  519. package/src/surfaces/logic-workspace.ts +13 -15
  520. package/src/surfaces/media-browser.json +62 -6
  521. package/src/surfaces/media-browser.ts +26 -11
  522. package/src/surfaces/media-field.json +1 -1
  523. package/src/surfaces/media-field.ts +4 -5
  524. package/src/surfaces/media-library.json +1586 -0
  525. package/src/surfaces/media-library.ts +282 -0
  526. package/src/surfaces/media-pane.ts +8 -7
  527. package/src/surfaces/media-pick-dialog.json +71 -0
  528. package/src/surfaces/media-pick-dialog.ts +132 -0
  529. package/src/surfaces/menu.ts +47 -23
  530. package/src/surfaces/navigator-dock.ts +11 -12
  531. package/src/surfaces/new-project.ts +27 -26
  532. package/src/surfaces/pane-context.json +144 -77
  533. package/src/surfaces/pane-context.ts +44 -25
  534. package/src/surfaces/pane-grid.ts +15 -10
  535. package/src/surfaces/panel-activity.ts +5 -6
  536. package/src/surfaces/panel-data.ts +3 -4
  537. package/src/surfaces/panel-deploy-checklist.json +12 -6
  538. package/src/surfaces/panel-deploy-checklist.ts +8 -7
  539. package/src/surfaces/panel-elements.ts +5 -7
  540. package/src/surfaces/panel-i18n.ts +5 -7
  541. package/src/surfaces/panel-imports.ts +6 -13
  542. package/src/surfaces/panel-outline.ts +25 -19
  543. package/src/surfaces/panel-page.json +72 -2
  544. package/src/surfaces/panel-page.ts +49 -25
  545. package/src/surfaces/panel-problems.ts +5 -7
  546. package/src/surfaces/panel-signals.json +1 -1
  547. package/src/surfaces/panel-signals.ts +25 -39
  548. package/src/surfaces/panel-stylebook-layers.json +71 -204
  549. package/src/surfaces/panel-stylebook-layers.ts +5 -29
  550. package/src/surfaces/preferences.json +120 -48
  551. package/src/surfaces/preferences.ts +29 -11
  552. package/src/surfaces/progress-modal.ts +15 -9
  553. package/src/surfaces/properties-panel.json +1 -1
  554. package/src/surfaces/properties-panel.ts +20 -42
  555. package/src/surfaces/publish-quick.json +428 -0
  556. package/src/surfaces/publish-quick.ts +165 -0
  557. package/src/surfaces/publish-workspace.json +2892 -0
  558. package/src/surfaces/publish-workspace.ts +391 -0
  559. package/src/surfaces/publish.ts +18 -18
  560. package/src/surfaces/push-plan.ts +7 -8
  561. package/src/surfaces/rail.json +1 -159
  562. package/src/surfaces/rail.ts +22 -79
  563. package/src/surfaces/reference-field.ts +11 -15
  564. package/src/surfaces/schema-builder.ts +16 -22
  565. package/src/surfaces/schema-form.json +10 -2
  566. package/src/surfaces/schema-form.ts +17 -25
  567. package/src/surfaces/secret-field.ts +9 -6
  568. package/src/surfaces/seo.json +1 -1
  569. package/src/surfaces/seo.ts +21 -15
  570. package/src/surfaces/settings-contexts.ts +9 -7
  571. package/src/surfaces/settings-contributed.json +5 -5
  572. package/src/surfaces/settings-contributed.ts +32 -12
  573. package/src/surfaces/settings-css-vars.json +44 -36
  574. package/src/surfaces/settings-css-vars.ts +82 -16
  575. package/src/surfaces/settings-defs.json +2 -2
  576. package/src/surfaces/settings-defs.ts +17 -21
  577. package/src/surfaces/settings-deploy.json +2 -15
  578. package/src/surfaces/settings-deploy.ts +9 -16
  579. package/src/surfaces/settings-dialog.json +51 -0
  580. package/src/surfaces/settings-dialog.ts +169 -0
  581. package/src/surfaces/settings-extensions.ts +5 -6
  582. package/src/surfaces/settings-head.ts +33 -39
  583. package/src/surfaces/settings-locales.ts +8 -20
  584. package/src/surfaces/settings-overview.json +3 -3
  585. package/src/surfaces/settings-overview.ts +14 -8
  586. package/src/surfaces/settings-packages.ts +8 -7
  587. package/src/surfaces/settings-pane.json +269 -46
  588. package/src/surfaces/settings-pane.ts +88 -36
  589. package/src/surfaces/settings-rawjson.ts +5 -6
  590. package/src/surfaces/shell.json +2 -1
  591. package/src/surfaces/shell.ts +1 -2
  592. package/src/surfaces/site-panel.json +1029 -0
  593. package/src/surfaces/site-panel.ts +233 -0
  594. package/src/surfaces/slash-menu.json +63 -21
  595. package/src/surfaces/slash-menu.ts +42 -9
  596. package/src/surfaces/statements.ts +13 -18
  597. package/src/surfaces/statusbar.ts +56 -29
  598. package/src/surfaces/style-panel.ts +18 -31
  599. package/src/surfaces/stylebook-chrome.ts +3 -5
  600. package/src/surfaces/styles-panel.json +735 -0
  601. package/src/surfaces/styles-panel.ts +195 -0
  602. package/src/surfaces/styles-variables.json +149 -0
  603. package/src/surfaces/styles-variables.ts +145 -0
  604. package/src/surfaces/tab-strip.json +4 -4
  605. package/src/surfaces/tab-strip.ts +14 -13
  606. package/src/surfaces/target-line.ts +6 -7
  607. package/src/surfaces/welcome.ts +6 -8
  608. package/src/surfaces/writing-banner.json +94 -0
  609. package/src/surfaces/writing-banner.ts +80 -0
  610. package/src/tabs/bare-repeaters.ts +101 -0
  611. package/src/tabs/patch-ops.ts +4 -3
  612. package/src/tabs/project-config.ts +32 -21
  613. package/src/tabs/selection.ts +36 -8
  614. package/src/tabs/tab.ts +99 -48
  615. package/src/tabs/transact.ts +98 -53
  616. package/src/types.ts +53 -43
  617. package/src/ui/ai-credentials-form.ts +10 -13
  618. package/src/ui/ai-model-picker.ts +3 -3
  619. package/src/ui/cf-account-picker.ts +7 -9
  620. package/src/ui/color-selector.ts +9 -5
  621. package/src/ui/dynamic-slot.ts +7 -8
  622. package/src/ui/expression-editor.ts +112 -105
  623. package/src/ui/form-controls.ts +104 -56
  624. package/src/ui/formula-catalog.ts +239 -230
  625. package/src/ui/formula-chips.ts +33 -18
  626. package/src/ui/layers.ts +57 -31
  627. package/src/ui/media-picker.ts +300 -47
  628. package/src/ui/popover-surface.ts +20 -12
  629. package/src/ui/progress-modal.ts +13 -8
  630. package/src/ui/regions.ts +14 -12
  631. package/src/ui/schema-form.ts +372 -97
  632. package/src/ui/surface.ts +71 -8
  633. package/src/ui/timing.ts +9 -0
  634. package/src/ui/value-source.ts +44 -31
  635. package/src/ui/virtual-window.ts +29 -3
  636. package/src/utils/base64.ts +6 -4
  637. package/src/utils/edit-display.ts +366 -111
  638. package/src/utils/element-selectors.ts +3 -2
  639. package/src/utils/google-fonts.ts +15 -14
  640. package/src/utils/inherited-style.ts +18 -20
  641. package/src/utils/link-target.ts +1 -1
  642. package/src/utils/local-day.ts +18 -0
  643. package/src/utils/preview-format.ts +25 -3
  644. package/src/utils/strip-events.ts +49 -22
  645. package/src/utils/studio-utils.ts +29 -14
  646. package/src/view.ts +2 -3
  647. package/src/workspace/pane-derive.ts +14 -16
  648. package/src/workspace/session.ts +81 -50
  649. package/src/workspace/tab-workspace.ts +137 -0
  650. package/src/workspace/workspace-back.ts +189 -0
  651. package/src/workspace/workspace-home.ts +133 -0
  652. package/src/workspace/workspace-ids.ts +129 -0
  653. package/src/workspace/workspace-switch.ts +372 -0
  654. package/src/workspace/workspace.ts +165 -47
  655. package/styles/shell-frame.css +30 -7
  656. package/styles/shell-frame.json +15 -3
  657. package/dist/chunks/2020-va2wwra4.js +0 -4
  658. package/dist/chunks/adapter-f93pafd7.js +0 -4
  659. package/dist/chunks/cf-account-picker-af1h381v.js +0 -4
  660. package/dist/chunks/cf-settings-125tdm0w.js +0 -4
  661. package/dist/chunks/color-area-jqhna8zy.js +0 -4
  662. package/dist/chunks/color-field-embq5ctg.js +0 -4
  663. package/dist/chunks/color-fpezvnkt.js +0 -4
  664. package/dist/chunks/color-slider-1qmw7wqy.js +0 -4
  665. package/dist/chunks/combobox-773hdzyj.js +0 -4
  666. package/dist/chunks/components-ax2f1fnn.js +0 -4
  667. package/dist/chunks/content-source-108aqtk1.js +0 -4
  668. package/dist/chunks/convert-to-repeater-ry5rmppr.js +0 -4
  669. package/dist/chunks/dialog-mtm9fq5t.js +0 -4
  670. package/dist/chunks/editor-pfgdmkb0.js +0 -4
  671. package/dist/chunks/entry-commands-t0k1q0kx.js +0 -4
  672. package/dist/chunks/extension-sections-6mwh2hf8.js +0 -4
  673. package/dist/chunks/file-ops-meej5mqf.js +0 -4
  674. package/dist/chunks/files-44vp8a2s.js +0 -4
  675. package/dist/chunks/format-host-7q1petb9.js +0 -4
  676. package/dist/chunks/fs-events-h2mwnxaz.js +0 -4
  677. package/dist/chunks/git-panel-zngbymvd.js +0 -4
  678. package/dist/chunks/grid-view-sx9vynsp.js +0 -51
  679. package/dist/chunks/icons-p44xhjf0.js +0 -4
  680. package/dist/chunks/iframe-entry-f7mqjwpm.js +0 -5
  681. package/dist/chunks/iframe-entry-haw7jkvz.js +0 -5
  682. package/dist/chunks/iframe-entry-z6qrytv3.js +0 -5
  683. package/dist/chunks/iframe-host-qk3ycq87.js +0 -4
  684. package/dist/chunks/index-wkyfjeqz.js +0 -4
  685. package/dist/chunks/jsonMode-s8kt9dsj.js +0 -13
  686. package/dist/chunks/jx-validate-psgd7vsx.js +0 -4
  687. package/dist/chunks/layers-panel-1kq2jyfk.js +0 -4
  688. package/dist/chunks/layers-xd92fg8d.js +0 -4
  689. package/dist/chunks/listbox-s8fkmnq8.js +0 -4
  690. package/dist/chunks/lit-html-ac4cwwr4.js +0 -4
  691. package/dist/chunks/media-picker-0svzkdyb.js +0 -4
  692. package/dist/chunks/menu-37d07z9m.js +0 -4
  693. package/dist/chunks/menu-5c55ea3d.js +0 -4
  694. package/dist/chunks/monaco-binding-z6r70pdt.js +0 -4
  695. package/dist/chunks/monaco-buffer-fz6gq5y7.js +0 -4
  696. package/dist/chunks/monaco-setup-q4axkgj7.js +0 -4
  697. package/dist/chunks/notify-0xgtn8q1.js +0 -4
  698. package/dist/chunks/number-field-gw7sq8rn.js +0 -4
  699. package/dist/chunks/parse-897fvqah.js +0 -4
  700. package/dist/chunks/platform-v1qctbe6.js +0 -4
  701. package/dist/chunks/popover-8htra4er.js +0 -4
  702. package/dist/chunks/publish-panel-ym1fmgq5.js +0 -4
  703. package/dist/chunks/quick-search-a54e6t5n.js +0 -4
  704. package/dist/chunks/references-gmqp82fs.js +0 -4
  705. package/dist/chunks/right-panel-v7t9awbk.js +0 -4
  706. package/dist/chunks/section-registry-vhyf3zsr.js +0 -4
  707. package/dist/chunks/select-n3v7gwas.js +0 -4
  708. package/dist/chunks/split-pff8f3ze.js +0 -4
  709. package/dist/chunks/studio-0bhx5hz6.js +0 -5
  710. package/dist/chunks/studio-0k1ddnyy.js +0 -6
  711. package/dist/chunks/studio-1b04pd0j.js +0 -64
  712. package/dist/chunks/studio-4dwbj6gb.js +0 -5
  713. package/dist/chunks/studio-4tanh828.js +0 -5
  714. package/dist/chunks/studio-55h4mstj.js +0 -4
  715. package/dist/chunks/studio-5tnvjkz4.js +0 -5
  716. package/dist/chunks/studio-60dqgb0c.js +0 -5
  717. package/dist/chunks/studio-61dp09qg.js +0 -9
  718. package/dist/chunks/studio-63epcrj2.js +0 -5
  719. package/dist/chunks/studio-74k29dy9.js +0 -5
  720. package/dist/chunks/studio-7vg6jcd3.js +0 -5
  721. package/dist/chunks/studio-7zx31knw.js +0 -9
  722. package/dist/chunks/studio-8bxk97km.js +0 -5
  723. package/dist/chunks/studio-933e30n4.js +0 -5
  724. package/dist/chunks/studio-98wnwhbg.js +0 -5
  725. package/dist/chunks/studio-9mccjwjh.js +0 -14
  726. package/dist/chunks/studio-a2ke1e4t.js +0 -5
  727. package/dist/chunks/studio-e3a2a6v3.js +0 -4
  728. package/dist/chunks/studio-e81q1vb9.js +0 -5
  729. package/dist/chunks/studio-epgrmqqm.js +0 -5
  730. package/dist/chunks/studio-ery42sqr.js +0 -4
  731. package/dist/chunks/studio-g1shz58g.js +0 -5
  732. package/dist/chunks/studio-gf0fz03c.js +0 -5
  733. package/dist/chunks/studio-gjtpe62q.js +0 -6
  734. package/dist/chunks/studio-gkskhg7m.js +0 -5
  735. package/dist/chunks/studio-gx4dnjzd.js +0 -5
  736. package/dist/chunks/studio-j03zga3y.js +0 -9
  737. package/dist/chunks/studio-jhpxeckz.js +0 -5
  738. package/dist/chunks/studio-k01r7ykp.js +0 -5
  739. package/dist/chunks/studio-kk7h30ne.js +0 -7
  740. package/dist/chunks/studio-kt1zqney.js +0 -5
  741. package/dist/chunks/studio-m3cwhvye.js +0 -5
  742. package/dist/chunks/studio-mcayt6hz.js +0 -5
  743. package/dist/chunks/studio-myjcghv3.js +0 -5
  744. package/dist/chunks/studio-n8bxwc3y.js +0 -4
  745. package/dist/chunks/studio-ngm8gvpb.js +0 -5
  746. package/dist/chunks/studio-nj5a01jy.js +0 -928
  747. package/dist/chunks/studio-pcgz8heb.js +0 -5
  748. package/dist/chunks/studio-qcsq0t15.js +0 -5
  749. package/dist/chunks/studio-qkstvve9.js +0 -5
  750. package/dist/chunks/studio-qv1pwd3c.js +0 -5
  751. package/dist/chunks/studio-rc6dvxjb.js +0 -7
  752. package/dist/chunks/studio-s8dmmz66.js +0 -5
  753. package/dist/chunks/studio-sp5xzbj4.js +0 -6
  754. package/dist/chunks/studio-szsjbj0g.js +0 -5
  755. package/dist/chunks/studio-t1xzawhr.js +0 -16
  756. package/dist/chunks/studio-tdcprbqb.js +0 -5
  757. package/dist/chunks/studio-vrmcjf9g.js +0 -7
  758. package/dist/chunks/studio-vyc0b4he.js +0 -10
  759. package/dist/chunks/studio-w0cgf6jg.js +0 -5
  760. package/dist/chunks/studio-w3hskzqk.js +0 -5
  761. package/dist/chunks/studio-xgrgcdyh.js +0 -9
  762. package/dist/chunks/studio-xz2yxfcb.js +0 -5
  763. package/dist/chunks/studio-y6n42tgs.js +0 -5
  764. package/dist/chunks/studio-yb560vxr.js +0 -5
  765. package/dist/chunks/studio-yn9f8vtx.js +0 -5
  766. package/dist/chunks/studio-yw05w8f0.js +0 -7
  767. package/dist/chunks/studio-z16sgavc.js +0 -5
  768. package/dist/chunks/studio-zey7zsdw.js +0 -12
  769. package/dist/chunks/studio-zyqn40w4.js +0 -5
  770. package/dist/chunks/swatch-group-8qh9a3bm.js +0 -4
  771. package/dist/chunks/tabs-jwthg40w.js +0 -4
  772. package/dist/chunks/textfield-spy3hc77.js +0 -4
  773. package/dist/chunks/toast-4d35zd64.js +0 -4
  774. package/dist/chunks/toast-host-8sjmfed3.js +0 -4
  775. package/dist/chunks/tooltip-fd6g9xy3.js +0 -4
  776. package/dist/chunks/tree-h19v1jfz.js +0 -4
  777. package/dist/chunks/tsMode-2frah6h1.js +0 -14
  778. package/dist/chunks/workspace-zt81g1ms.js +0 -4
  779. package/dist/chunks/ws-client-n2bh4hkf.js +0 -4
  780. package/src/panels/frontmatter-panel.ts +0 -701
  781. package/src/panels/git-panel.ts +0 -1120
  782. package/src/surfaces/doc-header.json +0 -776
  783. package/src/surfaces/doc-header.ts +0 -231
  784. package/src/surfaces/git-panel.json +0 -1880
  785. package/src/surfaces/git-panel.ts +0 -272
@@ -23,7 +23,7 @@ import {
23
23
  import { canvasRectToParent, createOverlayLayer } from "./iframe-overlay";
24
24
  import { serializeJxPath } from "./path-mapping";
25
25
  import { getActivePanel, panelMediaToActiveMedia } from "./canvas-helpers";
26
- import { panToParentRect } from "./canvas-utils";
26
+ import { panToParentRect, revealParentRect } from "./canvas-utils";
27
27
  import { clearDragGhost, moveDragGhost } from "../panels/drag-ghost";
28
28
  import { applyDropInstruction } from "../panels/dnd";
29
29
  import { rectOf } from "../utils/geometry";
@@ -46,6 +46,7 @@ import { formatEditableVerdicts } from "../format/constraints";
46
46
  import { formatByName } from "../format/format-host";
47
47
  import { collabState } from "../collab/collab-state";
48
48
  import { DIALOG_COMMANDS, isDialog, POPOVER_COMMANDS } from "@jxsuite/schema/dialogs";
49
+ import { isNodeObject } from "@jxsuite/schema/guards";
49
50
  import { isPopover } from "@jxsuite/schema/overlays";
50
51
  import { localeDirection } from "@jxsuite/schema/locale";
51
52
  import { getPlatform, hasPlatform } from "../platform";
@@ -66,27 +67,79 @@ import type {
66
67
  SerializableRect,
67
68
  SerializedKey,
68
69
  SyncedChord,
69
- WireDocOp,
70
+ WireLocale,
70
71
  } from "./iframe-protocol";
72
+ import { getEffectiveRenderLocale } from "../site-context";
73
+ import type { JxDocOp } from "../tabs/patch-ops";
71
74
  import type { IframeChannel } from "./iframe-channel";
72
75
  import type { OverlayLayer, OverlayPlacement } from "./iframe-overlay";
73
76
  import type { SlashCommand } from "../editor/inline-edit";
74
77
  import type { Tab } from "../tabs/tab";
75
- import type { JxExpressionNode, JxMutableNode } from "@jxsuite/schema/types";
78
+ import type { JxExpressionNode, JxMutableNode, JxStyle } from "@jxsuite/schema/types";
76
79
  import { bundleUrl } from "../services/bundle-base";
77
80
 
78
- /** A rect in PARENT coordinates (overlay-local from {@link canvasRectToParent}, same field shape). */
79
- type ParentRect = OverlayPlacement;
81
+ /**
82
+ * A plain-data copy of a value bound for `postMessage`.
83
+ *
84
+ * A JSON round trip rather than `structuredClone`, which throws on exactly what this exists to
85
+ * drop: reactive-proxy internals and stray functions riding on a value read off a live store.
86
+ */
87
+ function plainCopy<T>(value: T): T {
88
+ /* SAFETY: Every value posted through here is document or config data, which is JSON by contract,
89
+ so the round trip returns the same shape minus only what the realm boundary cannot carry. */
90
+ // oxlint-disable-next-line unicorn/prefer-structured-clone
91
+ return JSON.parse(JSON.stringify(value)) as T;
92
+ }
93
+
94
+ /** A deferred re-entry into inline editing, waiting for the host's DOM to reflect a split/insert. */
95
+ interface PendingEnterEdit {
96
+ path: (string | number)[];
97
+ minGen: number;
98
+ offset?: number;
99
+ /** Set when the deferred re-entry is a PROP host rather than a block (see deferEnterEdit). */
100
+ prop?: string;
101
+ }
102
+
103
+ /** The `enterEdit` message a host posts to re-enter inline editing. */
104
+ type EnterEditMsg = Extract<ParentToIframe, { kind: "enterEdit" }>;
105
+
106
+ /** A full-render message, as one host receives it. */
107
+ type RenderMsg = Extract<ParentToIframe, { kind: "render" }>;
108
+
109
+ /**
110
+ * The source data a drag session retains for its drop, exactly what {@link applyDropInstruction}
111
+ * consumes.
112
+ */
113
+ type RetainedDragData = Parameters<typeof applyDropInstruction>[2];
114
+
115
+ /** The empirical zoom of a host's iframe and the iframe's parent-viewport offset. */
116
+ export interface DragGeometry {
117
+ scale: number;
118
+ rect: { left: number; top: number };
119
+ }
80
120
 
81
- interface HostState {
121
+ /** The current edit session's editing flag + latest selection snapshot, for the parent toolbar. */
122
+ export interface EditSnapshotState {
123
+ editing: boolean;
124
+ editingProp: string | null;
125
+ snapshot: SelectionSnapshot | null;
126
+ }
127
+
128
+ /**
129
+ * One artboard's iframe, overlay, selection and edit session.
130
+ *
131
+ * Exported for the drag coordinator, which holds one as an opaque handle and passes it back to the
132
+ * session API ({@link beginDragSession}/{@link hostDragGeometry}/{@link postDragMessage}) without
133
+ * reading its fields.
134
+ */
135
+ export interface HostState {
82
136
  iframe: HTMLIFrameElement;
83
137
  /** The `.canvas-panel-canvas` element hosting the iframe + overlay (the WeakMap key). */
84
138
  canvasEl: HTMLElement;
85
139
  channel: IframeChannel<ParentToIframe, IframeToParent>;
86
140
  /**
87
- * The resolved canvasUrl this iframe was built with — so a host built early with the default URL
88
- * is rebuilt once the platform's loopback canvasUrl becomes available (electrobun resolves it
89
- * async).
141
+ * The resolved canvasUrl this iframe was built with — so a host built with one URL is rebuilt if
142
+ * the platform later reports a different one.
90
143
  */
91
144
  canvasUrl: string;
92
145
  ready: boolean;
@@ -101,8 +154,22 @@ interface HostState {
101
154
  /** Primary selected path (mirrors `session.selection`'s last entry), for hover de-dupe. */
102
155
  selectionPath: (string | number)[] | null;
103
156
  /** The whole selection this host last measured, so a re-measure redraws every box. */
104
- selectionPaths: JxPath[];
105
- /** Id of the most recent selection `measure` request, so stale `geometry` replies are dropped. */
157
+ selectionPaths: readonly JxPath[];
158
+ /**
159
+ * The allocator every `measure` request this host posts draws its id from: selection, presence,
160
+ * pan-to-card and {@link measureCanvasPath}. One counter keeps the ids disjoint, so the `geometry`
161
+ * handler can dispatch on the id alone.
162
+ */
163
+ measureSeq: number;
164
+ /**
165
+ * Id of the most recent SELECTION `measure` request, so a stale selection reply is dropped.
166
+ *
167
+ * It names the request, not the counter. It used to be both, so any other measure posted while a
168
+ * selection measure was in flight superseded it: picking a tag in the Styles Outline posts the
169
+ * selection measure and then a pan-to-card measure in the same tick, the pan took the newer id,
170
+ * and the selection reply arrived to find itself stale. The specimen was centred and never
171
+ * outlined. A live peer's presence measure did the same to the page selection.
172
+ */
106
173
  selReqId: number;
107
174
  /**
108
175
  * The NON-primary selected paths the in-flight selection `measure` covers, serialized. The
@@ -110,7 +177,7 @@ interface HostState {
110
177
  * selected node leaves it empty, so nothing but the selection box is ever drawn.
111
178
  */
112
179
  coSelectionKeys: Set<string>;
113
- /** Id of the most recent presence `measure` request (allocated from the selReqId counter). */
180
+ /** Id of the most recent presence `measure` request (allocated from {@link HostState.measureSeq}). */
114
181
  presenceReqId: number;
115
182
  /** Serialized peer path → presence box meta for the in-flight presence measure. */
116
183
  presenceMeta: Map<string, { color: string; label: string }>;
@@ -157,7 +224,7 @@ interface HostState {
157
224
  /** Highest snapshot `seq` seen, so stale (re-ordered) snapshots are dropped. */
158
225
  lastSnapshotSeq: number;
159
226
  /** The last non-null selection rect drawn (parent/overlay coords) — toolbar position fallback. */
160
- lastSelectionRect: ParentRect | null;
227
+ lastSelectionRect: OverlayPlacement | null;
161
228
  /** The node the pointer last hovered (iframe-viewport rect), so a zoom can hold it still. */
162
229
  lastHover: NodeHit | null;
163
230
  /**
@@ -218,8 +285,8 @@ interface HostState {
218
285
  /**
219
286
  * Resolvers for {@link measureCanvasPath} requests, keyed by the `measure` reqId.
220
287
  *
221
- * Allocated from the same `selReqId` counter as the selection and presence measures, so the three
222
- * can never collide and the `geometry` handler can dispatch on the id alone.
288
+ * Allocated from the same `measureSeq` counter as the selection and presence measures, so the
289
+ * three can never collide and the `geometry` handler can dispatch on the id alone.
223
290
  */
224
291
  pendingMeasures: Map<number, (hits: NodeHit[]) => void>;
225
292
  /**
@@ -228,13 +295,7 @@ interface HostState {
228
295
  * render acks (`renderComplete`) at a bumped one — both satisfy `gen >= minGen`. An immediate
229
296
  * `enterEdit` would race the escalated ASYNC render and silently fail to find the element.
230
297
  */
231
- pendingEnterEdit: {
232
- path: (string | number)[];
233
- minGen: number;
234
- offset?: number;
235
- /** Set when the deferred re-entry is a PROP host rather than a block (see deferEnterEdit). */
236
- prop?: string;
237
- } | null;
298
+ pendingEnterEdit: PendingEnterEdit | null;
238
299
  /**
239
300
  * Stylebook capability (set by {@link mountStylebookCanvas}, cleared by page mounts). The specimen
240
301
  * doc's paths decode to TAGS, not tab-document paths: hits route to the injected stylebook
@@ -250,13 +311,6 @@ interface HostState {
250
311
  panReqId: number;
251
312
  }
252
313
 
253
- /**
254
- * Opaque drag-target host handle for the coordinator bridge. The bridge passes it back to the
255
- * session API ({@link beginDragSession}/{@link hostDragGeometry}/{@link postDragMessage}) without
256
- * reading its fields.
257
- */
258
- export type DragHost = HostState;
259
-
260
314
  const hosts = new WeakMap<HTMLElement, HostState>();
261
315
 
262
316
  /** Every live host, so the selection watcher can re-measure each one when selection changes. */
@@ -271,7 +325,7 @@ const liveHosts = new Set<HostState>();
271
325
  let currentDragSeq = 0;
272
326
 
273
327
  /** Parent-retained source data keyed by `dragSeq` (the block fragment never crosses the boundary). */
274
- const retainedSrcData = new Map<number, Record<string, unknown>>();
328
+ const retainedSrcData = new Map<number, RetainedDragData>();
275
329
 
276
330
  /**
277
331
  * The last session whose cursor-carrying `dragOver` arrived FROM an iframe, and when. While a
@@ -421,9 +475,10 @@ export function requestCanvasEval(
421
475
  }
422
476
  evalReqId += 1;
423
477
  const reqId = evalReqId;
424
- // Expression nodes come off the reactive document — only plain values may cross postMessage.
425
- // oxlint-disable-next-line unicorn/prefer-structured-clone
426
- const wireExprs = JSON.parse(JSON.stringify(exprs)) as { id: string; node: JxExpressionNode }[];
478
+ /* Expression nodes come off the reactive document — only plain values may cross postMessage.
479
+ SAFETY: A request's nodes are expression nodes by the caller's contract, and the frame re-checks
480
+ `operator` before evaluating one (`iframe-eval.ts`), reporting anything else per expression. */
481
+ const wireExprs = plainCopy(exprs) as { id: string; node: JxExpressionNode }[];
427
482
  target.channel.post({
428
483
  contextPath: contextPath ? [...contextPath] : null,
429
484
  exprs: wireExprs,
@@ -541,7 +596,7 @@ export function liveDragHostAt(cursor: { x: number; y: number }): HostState | nu
541
596
  export function beginDragSession(
542
597
  host: HostState,
543
598
  src: DragSrcKind,
544
- srcData: Record<string, unknown>,
599
+ srcData: RetainedDragData,
545
600
  ): number {
546
601
  currentDragSeq += 1;
547
602
  retainedSrcData.set(currentDragSeq, srcData);
@@ -569,7 +624,7 @@ export function beginDragSession(
569
624
  export function adoptDragSession(
570
625
  _host: HostState,
571
626
  _src: DragSrcKind,
572
- srcData: Record<string, unknown>,
627
+ srcData: RetainedDragData,
573
628
  seq: number,
574
629
  ): number {
575
630
  currentDragSeq = seq;
@@ -587,10 +642,7 @@ export function currentDragSession(): number {
587
642
  * read FRESH so a pan/zoom mid-drag is reflected. NOT `effectiveZoom()` (a separate path that can
588
643
  * desync). The matching iframe parent-viewport rect is returned so the cursor map cancels the pan.
589
644
  */
590
- export function hostDragGeometry(host: HostState): {
591
- scale: number;
592
- rect: { left: number; top: number };
593
- } {
645
+ export function hostDragGeometry(host: HostState): DragGeometry {
594
646
  const rect = rectOf(host.iframe);
595
647
  const scale = host.iframe.clientWidth > 0 ? rect.width / host.iframe.clientWidth : 1;
596
648
  return { rect, scale };
@@ -735,8 +787,8 @@ function measureIn(
735
787
  path: readonly (string | number)[],
736
788
  ): Promise<CanvasPoint | null> {
737
789
  return new Promise((resolve) => {
738
- host.selReqId += 1;
739
- const reqId = host.selReqId;
790
+ host.measureSeq += 1;
791
+ const reqId = host.measureSeq;
740
792
  const timer = setTimeout(() => {
741
793
  host.pendingMeasures.delete(reqId);
742
794
  resolve(null);
@@ -1317,7 +1369,7 @@ export function postDialogOpen(tab: { id: string }, path: JxPath | null): void {
1317
1369
  * `host` is deliberately not a pane-scoped parameter name (see `PANE_PARAM_NAMES`), and the body
1318
1370
  * reads no focus: `panelHostingCanvas` resolves the stage from the host's own element.
1319
1371
  */
1320
- export function postPatchToHosts(forwardOps: WireDocOp[], tabId: string | null): number {
1372
+ export function postPatchToHosts(forwardOps: JxDocOp[], tabId: string | null): number {
1321
1373
  let posted = 0;
1322
1374
  for (const host of liveHosts) {
1323
1375
  if (!host.iframe.isConnected) {
@@ -1457,14 +1509,14 @@ function hostTab(state: HostState): Tab | null {
1457
1509
  }
1458
1510
 
1459
1511
  /**
1460
- * The tab a drag TARGET is showing — the one public reader on the opaque {@link DragHost} handle.
1512
+ * The tab a drag TARGET is showing — the one public reader on the opaque {@link HostState} handle.
1461
1513
  *
1462
1514
  * `panels/canvas-dnd-bridge.ts` binds a session to the host under the cursor and then had nothing
1463
1515
  * to ask it, so the one fact it needed about the drag — what the dragged node is CALLED — came from
1464
1516
  * `activeTab`. The bridge is not supposed to read the handle's fields (that is what "opaque"
1465
1517
  * means), so the answer is a function rather than a widened type.
1466
1518
  */
1467
- export function dragHostTab(host: DragHost): Tab | null {
1519
+ export function dragHostTab(host: HostState): Tab | null {
1468
1520
  return hostTab(host);
1469
1521
  }
1470
1522
 
@@ -1499,7 +1551,7 @@ function shownSelections(): Map<string, readonly JxPath[]> {
1499
1551
  if (!tab) {
1500
1552
  continue;
1501
1553
  }
1502
- const sel = (tab.session.selection ?? []) as readonly JxPath[];
1554
+ const sel: readonly JxPath[] = tab.session.selection;
1503
1555
  // Track the selection deeply enough that a change WITHIN the set re-measures: the effect
1504
1556
  // Reads every path, not just the array reference.
1505
1557
  void sel.map((path) => path.join("/")).join("|");
@@ -1679,8 +1731,8 @@ function requestPresence(host: HostState): void {
1679
1731
  host.overlay.setPresence([]);
1680
1732
  return;
1681
1733
  }
1682
- host.selReqId += 1;
1683
- host.presenceReqId = host.selReqId;
1734
+ host.measureSeq += 1;
1735
+ host.presenceReqId = host.measureSeq;
1684
1736
  host.channel.post({ kind: "measure", paths, reqId: host.presenceReqId });
1685
1737
  }
1686
1738
 
@@ -1698,12 +1750,14 @@ function requestSelection(host: HostState, sel: readonly JxPath[]): void {
1698
1750
  }
1699
1751
  const primary = primarySelection(sel);
1700
1752
  host.selectionPath = primary;
1701
- host.selectionPaths = sel as JxPath[];
1753
+ host.selectionPaths = sel;
1702
1754
  if (!host.iframe.isConnected) {
1703
1755
  liveHosts.delete(host);
1704
1756
  return;
1705
1757
  }
1706
1758
  if (!primary) {
1759
+ // Nothing selected, so a reply still in flight answers a question nobody is asking any more.
1760
+ host.selReqId = -1;
1707
1761
  host.coSelectionKeys.clear();
1708
1762
  host.overlay.setSelection(null);
1709
1763
  host.overlay.setCoSelection([]);
@@ -1716,7 +1770,8 @@ function requestSelection(host: HostState, sel: readonly JxPath[]): void {
1716
1770
  // Cross the postMessage boundary.
1717
1771
  const others = cloneSelection(sel.slice(0, -1));
1718
1772
  host.coSelectionKeys = new Set(others.map((path) => JSON.stringify(path)));
1719
- host.selReqId += 1;
1773
+ host.measureSeq += 1;
1774
+ host.selReqId = host.measureSeq;
1720
1775
  host.channel.post({
1721
1776
  kind: "measure",
1722
1777
  paths: [[...primary], ...others],
@@ -1737,13 +1792,15 @@ function requestStylebookSelection(host: HostState): void {
1737
1792
  const tag = shell.stylebook.selection;
1738
1793
  const cardPath = tag ? host.stylebook?.tagToCardPath.get(tag) : undefined;
1739
1794
  if (!tag || !cardPath) {
1795
+ host.selReqId = -1;
1740
1796
  host.overlay.setSelection(null);
1741
1797
  return;
1742
1798
  }
1743
1799
  if (!host.ready) {
1744
1800
  return;
1745
1801
  }
1746
- host.selReqId += 1;
1802
+ host.measureSeq += 1;
1803
+ host.selReqId = host.measureSeq;
1747
1804
  host.channel.post({ kind: "measure", paths: [[...cardPath]], reqId: host.selReqId });
1748
1805
  }
1749
1806
 
@@ -1752,9 +1809,9 @@ function requestStylebookSelection(host: HostState): void {
1752
1809
  *
1753
1810
  * This was the literal `/packages/studio/canvas.html` — a repo dev-server path baked into a browser
1754
1811
  * bundle, correct on exactly one host out of four. It happened to work because every other host
1755
- * overrides `canvasUrl`: the chromium launcher builds one from its token, electrobun fetches one
1756
- * over RPC, and the cloud adapter hard-codes its own. So the default was never the default; it was
1757
- * the dev server's URL with nothing saying so.
1812
+ * overrides `canvasUrl`: the chromium launcher builds one from its token and the cloud adapter
1813
+ * hard-codes its own. So the default was never the default; it was the dev server's URL with
1814
+ * nothing saying so.
1758
1815
  *
1759
1816
  * `bundleUrl` resolves it against the ENTRY's directory, which is the one fact every host agrees on
1760
1817
  * (see services/bundle-base.ts), so the fallback is now correct everywhere rather than accidentally
@@ -1764,12 +1821,6 @@ function defaultCanvasUrl(): string {
1764
1821
  return bundleUrl("../canvas.html");
1765
1822
  }
1766
1823
 
1767
- /**
1768
- * Loads nothing, deliberately: what a platform that resolves its canvasUrl LATER gets in the
1769
- * meantime. See {@link StudioPlatform.canvasUrlDeferred}.
1770
- */
1771
- const DEFERRED_CANVAS_URL = "about:blank";
1772
-
1773
1824
  /**
1774
1825
  * Release one host: its channel, its overlay, its frame, and everything awaiting a reply from it.
1775
1826
  *
@@ -1815,27 +1866,12 @@ function releaseHost(host: HostState): void {
1815
1866
  liveHosts.delete(host);
1816
1867
  }
1817
1868
 
1818
- /**
1819
- * Which document this pane's canvas iframe should load.
1820
- *
1821
- * The `canvasUrlDeferred` branch is the one that needs saying. Electrobun resolves its canvasUrl
1822
- * asynchronously — it is this window's loopback port, fetched over RPC inside `activate()` — and
1823
- * until the fix above, the bundle-relative default resolved to nothing servable under `views://`,
1824
- * so an early frame simply failed and `ensureHost` rebuilt against the real URL when it landed. Now
1825
- * that default RESOLVES: `views://studio/canvas.html` is a document electrobun really does stage.
1826
- * An early frame would therefore boot the whole canvas bundle inside the SHELL's app-privileged
1827
- * origin, in a CEF instance running `disable-site-isolation-trials` — and the cross-origin loopback
1828
- * canvas exists precisely so that never happens.
1829
- */
1869
+ /** Which document this pane's canvas iframe should load. */
1830
1870
  function resolveCanvasUrl(): string {
1831
1871
  if (!hasPlatform()) {
1832
1872
  return defaultCanvasUrl();
1833
1873
  }
1834
- const platform = getPlatform();
1835
- if (platform.canvasUrl) {
1836
- return platform.canvasUrl;
1837
- }
1838
- return platform.canvasUrlDeferred ? DEFERRED_CANVAS_URL : defaultCanvasUrl();
1874
+ return getPlatform().canvasUrl || defaultCanvasUrl();
1839
1875
  }
1840
1876
 
1841
1877
  /**
@@ -1875,9 +1911,8 @@ function ensureHost(canvasEl: HTMLElement): HostState {
1875
1911
  if (existing.canvasUrl === canvasUrl) {
1876
1912
  return existing;
1877
1913
  }
1878
- // The platform's loopback canvasUrl arrived after this host was built with the default URL
1879
- // (electrobun resolves it async over RPC) — tear the early iframe down and rebuild against the
1880
- // Right cross-origin origin.
1914
+ // The platform now reports a different canvasUrl than this host was built with — tear the
1915
+ // Iframe down and rebuild against the right origin.
1881
1916
  releaseHost(existing);
1882
1917
  }
1883
1918
  // ParentOrigin is the parent's real origin, passed into the iframe URL so the cross-origin iframe
@@ -1885,8 +1920,8 @@ function ensureHost(canvasEl: HTMLElement): HostState {
1885
1920
  const parentOrigin = location.origin;
1886
1921
  const token = crypto.randomUUID();
1887
1922
  // IframeOrigin is the iframe's own origin. For a RELATIVE canvasUrl (dev / chromium) it resolves to
1888
- // The parent's location.origin (same-origin). For an absolute loopback canvasUrl (electrobun) it is
1889
- // The loopback origin, so the channel accepts/targets the right cross-origin peer.
1923
+ // The parent's location.origin (same-origin). For an absolute canvasUrl it is
1924
+ // That URL's origin, so the channel accepts/targets the right cross-origin peer.
1890
1925
  const iframeOrigin = new URL(canvasUrl, location.href).origin;
1891
1926
  const iframe = document.createElement("iframe");
1892
1927
  iframe.className = "jx-canvas-iframe";
@@ -1895,16 +1930,16 @@ function ensureHost(canvasEl: HTMLElement): HostState {
1895
1930
  iframe.title = "Canvas — live page render";
1896
1931
  iframe.style.cssText =
1897
1932
  "width:100%;min-height:480px;height:100%;border:0;display:block;background:#fff";
1898
- // Preserve any query already on canvasUrl (e.g. electrobun's ?win=7) and append the token (always)
1933
+ // Preserve any query already on canvasUrl (e.g. a per-window `?win=7`) and append the token (always)
1899
1934
  // Plus parentOrigin (conditionally — see below).
1900
1935
  const srcUrl = new URL(canvasUrl, location.href);
1901
1936
  srcUrl.searchParams.set("token", token);
1902
1937
  // Pass parentOrigin into the iframe src ONLY when the PARENT is served over http(s) (dev / chromium
1903
1938
  // — same-origin, so the origin round-trips and the iframe can keep its acceptOrigin STRICT). For a
1904
- // Non-http(s) parent (electrobun views://) OMIT it: a custom scheme may not surface as a postMessage
1939
+ // Non-http(s) parent (a custom scheme) OMIT it: a custom scheme may not surface as a postMessage
1905
1940
  // Origin (event.origin), so the iframe falls back to acceptOrigin '*' + the shared token (it logs
1906
1941
  // The warn) rather than silently stalling. The PARENT side here stays STRICT — acceptOrigin /
1907
- // TargetOrigin are the real (loopback) iframeOrigin below, a gateable origin.
1942
+ // TargetOrigin are the real iframeOrigin below, a gateable origin.
1908
1943
  if (location.protocol === "http:" || location.protocol === "https:") {
1909
1944
  srcUrl.searchParams.set("parentOrigin", parentOrigin);
1910
1945
  }
@@ -1950,6 +1985,7 @@ function ensureHost(canvasEl: HTMLElement): HostState {
1950
1985
  lastHover: null,
1951
1986
  lastSelectionRect: null,
1952
1987
  lastSnapshotSeq: 0,
1988
+ measureSeq: 0,
1953
1989
  overlay,
1954
1990
  panReqId: -1,
1955
1991
  pending: null,
@@ -1965,7 +2001,7 @@ function ensureHost(canvasEl: HTMLElement): HostState {
1965
2001
  ready: false,
1966
2002
  selectionPath: null,
1967
2003
  selectionPaths: [],
1968
- selReqId: 0,
2004
+ selReqId: -1,
1969
2005
  snapshot: null,
1970
2006
  stylebook: null,
1971
2007
  tabId: null,
@@ -2017,7 +2053,7 @@ function hideInsertZoneNow(state: HostState): void {
2017
2053
  */
2018
2054
  function insertButtonHovered(state: HostState): boolean {
2019
2055
  const btn = state.overlay.insertButton;
2020
- return state.insertHover || (typeof btn.matches === "function" && btn.matches(":hover"));
2056
+ return state.insertHover || btn.matches(":hover");
2021
2057
  }
2022
2058
 
2023
2059
  /**
@@ -2173,7 +2209,8 @@ function handleMessage(state: HostState, msg: IframeToParent): void {
2173
2209
  behind that message with it. The null check is the same guard for a `targetPath` an edit
2174
2210
  has since invalidated, which threw for the same reason. */
2175
2211
  const target = getNodeAtPath(tab.doc.document, msg.targetPath);
2176
- if (!target || typeof target !== "object") {
2212
+ // A path can resolve to a string child or to nothing at all, whatever the declared type says.
2213
+ if (!isNodeObject(target)) {
2177
2214
  return;
2178
2215
  }
2179
2216
  if (POPOVER_COMMANDS.has(msg.command)) {
@@ -2359,7 +2396,7 @@ function handleMessage(state: HostState, msg: IframeToParent): void {
2359
2396
  const [hit] = msg.hits;
2360
2397
  if (hit) {
2361
2398
  const { rect: ifr, scale } = hostDragGeometry(state);
2362
- panToParentRect({ height: hit.rect.height * scale, top: hit.rect.y * scale + ifr.top });
2399
+ revealParentRect({ height: hit.rect.height * scale, top: hit.rect.y * scale + ifr.top });
2363
2400
  }
2364
2401
  return;
2365
2402
  }
@@ -2370,7 +2407,7 @@ function handleMessage(state: HostState, msg: IframeToParent): void {
2370
2407
  // The iframe could not resolve is simply absent from `hits`.
2371
2408
  const primaryKey = JSON.stringify(state.selectionPath);
2372
2409
  const co: OverlayPlacement[] = [];
2373
- let rect: ParentRect | null = null;
2410
+ let rect: OverlayPlacement | null = null;
2374
2411
  if (state.stylebook) {
2375
2412
  const [hit] = msg.hits;
2376
2413
  rect = hit ? canvasRectToParent(hit.rect) : null;
@@ -2762,9 +2799,8 @@ function handleMessage(state: HostState, msg: IframeToParent): void {
2762
2799
  const { rect: ifr, scale } = hostDragGeometry(state);
2763
2800
  const left = msg.rect.x * scale + ifr.left;
2764
2801
  const top = msg.rect.y * scale + ifr.top;
2765
- canvasSlashHandler?.show({
2802
+ const request: CanvasSlashRequest = {
2766
2803
  filter: msg.filter,
2767
- ...(msg.showFilter === true ? { showFilter: true } : {}),
2768
2804
  onDismiss: () => state.channel.post({ kind: "slashDismissed" }),
2769
2805
  onSelect: (cmd) => state.channel.post({ cmd: { ...cmd }, kind: "slashSelect" }),
2770
2806
  rect: {
@@ -2774,7 +2810,11 @@ function handleMessage(state: HostState, msg: IframeToParent): void {
2774
2810
  top,
2775
2811
  width: msg.rect.width * scale,
2776
2812
  },
2777
- });
2813
+ };
2814
+ if (msg.showFilter === true) {
2815
+ request.showFilter = true;
2816
+ }
2817
+ canvasSlashHandler?.show(request);
2778
2818
  return;
2779
2819
  }
2780
2820
  case "slashNav": {
@@ -2843,6 +2883,7 @@ function onDomUpdated(state: HostState, gen: number): void {
2843
2883
  requestSelection(state, state.selectionPaths);
2844
2884
  requestPresence(state);
2845
2885
  hideInsertZoneNow(state);
2886
+ servePendingStylebookPan(state);
2846
2887
  if (state.pendingEnterEdit && gen >= state.pendingEnterEdit.minGen) {
2847
2888
  const { offset, path, prop } = state.pendingEnterEdit;
2848
2889
  state.pendingEnterEdit = null;
@@ -2871,12 +2912,14 @@ function deferEnterEdit(
2871
2912
  offset?: number,
2872
2913
  prop?: string,
2873
2914
  ): void {
2874
- state.pendingEnterEdit = {
2875
- minGen: state.lastRenderedGen,
2876
- path: [...path],
2877
- ...(offset === undefined ? {} : { offset }),
2878
- ...(prop === undefined ? {} : { prop }),
2879
- };
2915
+ const pending: PendingEnterEdit = { minGen: state.lastRenderedGen, path: [...path] };
2916
+ if (offset !== undefined) {
2917
+ pending.offset = offset;
2918
+ }
2919
+ if (prop !== undefined) {
2920
+ pending.prop = prop;
2921
+ }
2922
+ state.pendingEnterEdit = pending;
2880
2923
  }
2881
2924
 
2882
2925
  /** Ask the host's iframe to (re-)enter inline editing on `path` (a plain copy crosses the bridge). */
@@ -2886,12 +2929,14 @@ function reenterEdit(
2886
2929
  offset?: number,
2887
2930
  prop?: string,
2888
2931
  ): void {
2889
- state.channel.post({
2890
- kind: "enterEdit",
2891
- path: [...path],
2892
- ...(offset === undefined ? {} : { offset }),
2893
- ...(prop === undefined ? {} : { prop }),
2894
- });
2932
+ const enter: EnterEditMsg = { kind: "enterEdit", path: [...path] };
2933
+ if (offset !== undefined) {
2934
+ enter.offset = offset;
2935
+ }
2936
+ if (prop !== undefined) {
2937
+ enter.prop = prop;
2938
+ }
2939
+ state.channel.post(enter);
2895
2940
  }
2896
2941
 
2897
2942
  /**
@@ -2963,7 +3008,7 @@ function drawHover(state: HostState, hit: NodeHit | null): void {
2963
3008
  */
2964
3009
  type PreparedRender = Omit<
2965
3010
  Extract<ParentToIframe, { kind: "render" }>,
2966
- "colorScheme" | "gen" | "kind"
3011
+ "colorScheme" | "gen" | "kind" | "locale"
2967
3012
  >;
2968
3013
 
2969
3014
  /** One render pass's prepared payloads, by document IDENTITY. */
@@ -3087,11 +3132,9 @@ function preparePassRender(
3087
3132
  // So a JSON round-trip (NOT structuredClone, which would throw) drops residual functions /
3088
3133
  // Reactive proxy artifacts that would otherwise raise DataCloneError and silently drop the
3089
3134
  // Entire message.
3090
- // oxlint-disable-next-line unicorn/prefer-structured-clone
3091
- const cloneableDoc = JSON.parse(JSON.stringify(resolved.renderDoc)) as unknown;
3135
+ const cloneableDoc = plainCopy(resolved.renderDoc);
3092
3136
  // The RAW page doc (forward-op + data-jx-path coordinate space) crosses as the shadow doc.
3093
- // oxlint-disable-next-line unicorn/prefer-structured-clone
3094
- const cloneableShadow = JSON.parse(JSON.stringify(doc)) as unknown;
3137
+ const cloneableShadow = plainCopy(doc);
3095
3138
  // Which tags can hold a caret depends on the document's vocabulary, so the format's verdicts
3096
3139
  // Ride with the render rather than being baked into the frame. Absent for a native document,
3097
3140
  // Where the studio's own element metadata answers on its own.
@@ -3101,21 +3144,27 @@ function preparePassRender(
3101
3144
  const renderTab = tabId ? (workspace.tabs.get(tabId) ?? null) : null;
3102
3145
  const formatElements = formatByName(renderTab?.doc.sourceFormat)?.studio?.elements;
3103
3146
  const editableTags = formatElements ? formatEditableVerdicts(formatElements) : undefined;
3104
- return {
3147
+ const prepared: PreparedRender = {
3105
3148
  assets: resolved.assets,
3106
3149
  doc: cloneableDoc,
3107
3150
  docBase: resolved.docBase ?? `${canvasBaseOrigin()}/`,
3108
3151
  mapperCtx: resolved.mapperCtx,
3109
- /* Still a cast, but no longer a lie: `WireMapperCtx.canvasMode` is typed `string` (it mirrors
3110
- `PathMapCtx`, which carries the mode for `isEditableMode` and takes `CanvasMode | string`),
3111
- so this narrows a string to the union. It used to narrow it to a union that did not list
3112
- `git-diff` while git-diff renders went through here every day. */
3152
+ /* SAFETY: `WireMapperCtx.canvasMode` is typed `string` only because it mirrors `PathMapCtx`,
3153
+ which takes `CanvasMode | string`; `resolveCanvasDocument` fills it with the mode this host
3154
+ was asked to render (`modeOverride ?? canvasModeOfTab(tab)`), and only a canvas mode ever
3155
+ mounts a host. It used to narrow to a union that did not list `git-diff` while git-diff
3156
+ renders went through here every day. */
3113
3157
  mode: resolved.mapperCtx.canvasMode as CanvasMode,
3114
3158
  shadowDoc: cloneableShadow,
3115
3159
  siteStyle: resolved.siteStyle,
3116
- ...(editableTags ? { editableTags } : {}),
3117
- ...(allowAutoRequests ? { allowAutoRequests: true } : {}),
3118
3160
  };
3161
+ if (editableTags) {
3162
+ prepared.editableTags = editableTags;
3163
+ }
3164
+ if (allowAutoRequests) {
3165
+ prepared.allowAutoRequests = true;
3166
+ }
3167
+ return prepared;
3119
3168
  });
3120
3169
  byDoc.set(doc, { mode: modeOverride, payload, tabId, viewTabId });
3121
3170
  return payload;
@@ -3180,25 +3229,30 @@ export async function mountIframeCanvas(
3180
3229
  paneOfContainer(canvasEl),
3181
3230
  modeOverride,
3182
3231
  );
3183
- const message: ParentToIframe = {
3232
+ const message: RenderMsg = {
3184
3233
  ...prepared,
3185
3234
  // Per-TAB, and read at POST time so a scheme flip that raced the shared resolution is not
3186
3235
  // Baked into the payload every host shares. `viewTab` is this artboard's tab — defaulted from
3187
3236
  // `tabOfContainer(canvasEl)`, the same route the rest of this mount takes.
3188
3237
  colorScheme: schemeWireFor(viewTab),
3189
- /* Per-ARTBOARD, and post-time for a sharper version of the same reason. `preparePassRender`
3190
- memoizes on the document and hands the SAME payload object to every host in the pass, and a
3191
- git-diff pass has two hosts holding two documents — but marks are a function of BOTH sides,
3192
- not of the document this host draws, so they are not derivable from that cache key at all.
3193
- Baking them in would make the two artboards race for whose marks the pass kept. */
3194
- ...(diffMarks ? { diffMarks } : {}),
3195
3238
  gen,
3196
3239
  kind: "render",
3240
+ // The language this artboard draws in, per tab and at post time like the scheme.
3241
+ locale: localeWireFor(viewTab),
3197
3242
  // Read at POST time for the same reason `colorScheme` is: a render replaces the DOM, so a panel
3198
3243
  // The author opened before this pass would close under them without it.
3199
3244
  popoverOpen: viewTab?.session.ui.openPopover ?? null,
3200
3245
  dialogOpen: viewTab?.session.ui.openDialog ?? null,
3201
3246
  };
3247
+ /* Diff marks are per-ARTBOARD, and post-time for a sharper version of the colour scheme's
3248
+ reason. `preparePassRender` memoizes on the document and hands the SAME payload object to every
3249
+ host in the pass, and a git-diff pass has two hosts holding two documents — but marks are a
3250
+ function of BOTH sides, not of the document this host draws, so they are not derivable from
3251
+ that cache key at all. Baking them in would make the two artboards race for whose marks the
3252
+ pass kept. */
3253
+ if (diffMarks) {
3254
+ message.diffMarks = diffMarks;
3255
+ }
3202
3256
  state.popoverOpen = message.popoverOpen ?? null;
3203
3257
  state.dialogOpen = message.dialogOpen ?? null;
3204
3258
  // Preview is the fidelity view: no editing messages are honoured from it, no overlay is painted
@@ -3215,14 +3269,27 @@ export async function mountIframeCanvas(
3215
3269
  * until it says `ready`. Counted either way: the message is this pass's work for this host whether
3216
3270
  * or not the frame has finished loading.
3217
3271
  */
3218
- function deliverRender(state: HostState, message: ParentToIframe): void {
3272
+ function deliverRender(state: HostState, message: RenderMsg): void {
3219
3273
  canvasPerf.hostRenderPosts += 1;
3220
3274
  if (state.ready) {
3221
3275
  syncAssetVersions(state);
3222
3276
  state.channel.post(message);
3223
- } else {
3224
- state.pending = message;
3277
+ return;
3278
+ }
3279
+ /* The queue holds ONE render, and it must be the NEWEST, not the last to arrive. A page render
3280
+ awaits its own resolution, so two renders started in order can finish out of order: on a
3281
+ reload that restores two tabs, the frame is still booting, the first-opened tab's render (an
3282
+ older gen) can resolve after the active tab's, and overwriting the slot posted the stale one
3283
+ on `ready`. The frame drew the other tab's document, the ack adopted that tab's identity, and
3284
+ the active tab's render was lost. A ready frame drops a stale gen itself (`latestGen`); the
3285
+ queue has to make the same call. */
3286
+ const queued = state.pending;
3287
+ if (queued?.kind === "render" && queued.gen > message.gen) {
3288
+ // Never posted, so never acked: its identity must not wait for an ack that cannot come.
3289
+ state.pendingTabIds.delete(message.gen);
3290
+ return;
3225
3291
  }
3292
+ state.pending = message;
3226
3293
  }
3227
3294
 
3228
3295
  /**
@@ -3250,17 +3317,17 @@ export function mountStylebookCanvas(
3250
3317
  state.iframe.style.width = widthPx ? `${widthPx}px` : "100%";
3251
3318
  // Two independent plain clones: the iframe renders `doc` and folds styleUpdates into `shadowDoc`
3252
3319
  // (and fake test channels pass messages by reference, so sharing one object would alias them).
3253
- // oxlint-disable-next-line unicorn/prefer-structured-clone
3254
- const cloneableDoc = JSON.parse(JSON.stringify(generated.doc)) as unknown;
3255
- // oxlint-disable-next-line unicorn/prefer-structured-clone
3256
- const cloneableShadow = JSON.parse(JSON.stringify(generated.doc)) as unknown;
3257
- const message: ParentToIframe = {
3320
+ const cloneableDoc = plainCopy(generated.doc);
3321
+ const cloneableShadow = plainCopy(generated.doc);
3322
+ const message: RenderMsg = {
3258
3323
  // The specimen carries no tab identity, but the STAGE it is mounted into does: a stylebook is
3259
3324
  // Opened as a tab like anything else, and its `previewColorScheme` is that tab's.
3260
3325
  /* A stylebook specimen is generated, not a project document, so it references no project
3261
3326
  media and needs no resolution. */
3262
3327
  assets: null,
3263
3328
  colorScheme: schemeWireFor(tabOfContainer(canvasEl)),
3329
+ // And its language, for the same reason: a stylebook draws `:lang()` rules and `dir` too.
3330
+ locale: localeWireFor(tabOfContainer(canvasEl)),
3264
3331
  doc: cloneableDoc,
3265
3332
  docBase: `${canvasBaseOrigin()}/`,
3266
3333
  gen,
@@ -3342,6 +3409,21 @@ export function postColorSchemeToLiveHosts(
3342
3409
  * `dir` is computed here and sent with the tag: `localeDirection` is CLDR's answer through
3343
3410
  * `Intl.Locale`, and a frame deriving it again is a second copy of the RTL script list to keep.
3344
3411
  */
3412
+ /** A locale tag as the frame applies it, with its direction; null stays null. */
3413
+ function localeWire(locale: string | null): WireLocale | null {
3414
+ return locale === null ? null : { dir: localeDirection(locale), locale };
3415
+ }
3416
+
3417
+ /**
3418
+ * The language a TAB's artboard draws in, as wire data (studio.md §20.2): the one rule
3419
+ * `getEffectiveRenderLocale` answers, read at post time for `schemeWireFor`'s reason.
3420
+ */
3421
+ function localeWireFor(tab: Tab | null): WireLocale | null {
3422
+ return localeWire(
3423
+ tab ? getEffectiveRenderLocale(tab.session.ui.previewLocale, tab.documentPath) : null,
3424
+ );
3425
+ }
3426
+
3345
3427
  export function postLocaleToLiveHosts(locale: string | null, root?: HTMLElement | null): void {
3346
3428
  const dir = localeDirection(locale);
3347
3429
  for (const host of liveHosts) {
@@ -3354,6 +3436,12 @@ export function postLocaleToLiveHosts(locale: string | null, root?: HTMLElement
3354
3436
  }
3355
3437
  if (host.ready) {
3356
3438
  host.channel.post({ dir, kind: "setLocale", locale });
3439
+ } else if (host.pending?.kind === "render") {
3440
+ /* A host still booting draws the render it is waiting for, so THAT is what learns the
3441
+ language: a restored session writes `previewLocale` after its files have opened, by which
3442
+ time a pane's render may already be pending with the old value, and no render follows. A
3443
+ host with nothing pending is skipped; its render reads the value when it is posted. */
3444
+ host.pending = { ...host.pending, locale: localeWire(locale) };
3357
3445
  }
3358
3446
  }
3359
3447
  }
@@ -3371,8 +3459,7 @@ export function postLocaleToLiveHosts(locale: string | null, root?: HTMLElement
3371
3459
  * off a tab's reactive record is a proxy, and a proxy cannot be structured-cloned.
3372
3460
  */
3373
3461
  export function postRedefineElementToLiveHosts(doc: JxMutableNode, base: string): number {
3374
- // oxlint-disable-next-line unicorn/prefer-structured-clone
3375
- const plain = JSON.parse(JSON.stringify(doc)) as JxMutableNode;
3462
+ const plain = plainCopy(doc);
3376
3463
  let posted = 0;
3377
3464
  for (const host of liveHosts) {
3378
3465
  if (!host.iframe.isConnected) {
@@ -3398,13 +3485,8 @@ export function postSiteStyleToLiveHosts(): void {
3398
3485
  return;
3399
3486
  }
3400
3487
  // Site config comes off a reactive store — only plain values may cross postMessage.
3401
- // oxlint-disable-next-line unicorn/prefer-structured-clone
3402
- const siteStyle = JSON.parse(JSON.stringify(config.style ?? null)) as Record<
3403
- string,
3404
- unknown
3405
- > | null;
3406
- // oxlint-disable-next-line unicorn/prefer-structured-clone
3407
- const media = JSON.parse(JSON.stringify(config.$media ?? {})) as Record<string, string>;
3488
+ const siteStyle = plainCopy(config.style ?? null);
3489
+ const media = plainCopy(config.$media ?? {});
3408
3490
  for (const host of liveHosts) {
3409
3491
  if (!host.iframe.isConnected) {
3410
3492
  liveHosts.delete(host);
@@ -3422,11 +3504,10 @@ export function postSiteStyleToLiveHosts(): void {
3422
3504
  * received it — zero means no stylebook iframe is live yet and the caller should fall through to a
3423
3505
  * full render. Each post is followed by a selection re-measure so the box tracks the reflow.
3424
3506
  */
3425
- export function postStyleUpdateToStylebookHosts(style: Record<string, unknown>): number {
3507
+ export function postStyleUpdateToStylebookHosts(style: JxStyle): number {
3426
3508
  let posted = 0;
3427
3509
  // Style objects come off the reactive doc — only plain values may cross postMessage.
3428
- // oxlint-disable-next-line unicorn/prefer-structured-clone
3429
- const cloneable = JSON.parse(JSON.stringify(style)) as Record<string, unknown>;
3510
+ const cloneable = plainCopy(style);
3430
3511
  for (const host of liveHosts) {
3431
3512
  if (!host.iframe.isConnected) {
3432
3513
  liveHosts.delete(host);
@@ -3441,33 +3522,63 @@ export function postStyleUpdateToStylebookHosts(style: Record<string, unknown>):
3441
3522
  return posted;
3442
3523
  }
3443
3524
 
3525
+ /**
3526
+ * A pan asked for before any stylebook canvas could serve it: the tag, served by the next stylebook
3527
+ * render that draws its card while it is still the selection ({@link servePendingStylebookPan}).
3528
+ */
3529
+ let pendingStylebookPan: string | null = null;
3530
+
3531
+ /** Post the measure that pans `host` to `tag`'s card. `false` when the host cannot serve it yet. */
3532
+ function postStylebookPan(host: HostState | null, tag: string): boolean {
3533
+ if (!host?.stylebook || !host.ready) {
3534
+ return false;
3535
+ }
3536
+ const cardPath = host.stylebook.tagToCardPath.get(tag);
3537
+ if (!cardPath) {
3538
+ return false;
3539
+ }
3540
+ host.measureSeq += 1;
3541
+ host.panReqId = host.measureSeq;
3542
+ host.channel.post({ kind: "measure", paths: [[...cardPath]], reqId: host.panReqId });
3543
+ return true;
3544
+ }
3545
+
3444
3546
  /**
3445
3547
  * Pan the canvas so the selected tag's card is centered (layers-panel "locate" affordance). The
3446
3548
  * card lives inside the iframe, so it is measured over the bridge and the reply pans by the
3447
3549
  * converted parent-viewport rect (see the `geometry` handler's panReqId branch).
3550
+ *
3551
+ * A pan the canvas cannot serve yet is KEPT, not dropped. The Styles panel switches the pane to the
3552
+ * Elements view and selects a tag in one tick, from the Variables view, where no stylebook frame
3553
+ * exists: the frame that will draw the card has not been mounted. The request waits for it.
3448
3554
  */
3449
3555
  export function panToStylebookTag(tag: string): void {
3450
- const host = hostForActivePanel();
3451
- if (!host?.stylebook || !host.ready) {
3556
+ pendingStylebookPan = postStylebookPan(hostForActivePanel(), tag) ? null : tag;
3557
+ }
3558
+
3559
+ /**
3560
+ * Serve a kept pan on a stylebook render that has just landed: on the canvas the author is in, and
3561
+ * only while its tag is still the selection, so a request the author has since moved past is
3562
+ * forgotten rather than served late.
3563
+ */
3564
+ function servePendingStylebookPan(state: HostState): void {
3565
+ const tag = pendingStylebookPan;
3566
+ if (tag === null || !state.stylebook) {
3452
3567
  return;
3453
3568
  }
3454
- const cardPath = host.stylebook.tagToCardPath.get(tag);
3455
- if (!cardPath) {
3569
+ if (shell.stylebook.selection !== tag) {
3570
+ pendingStylebookPan = null;
3456
3571
  return;
3457
3572
  }
3458
- host.selReqId += 1;
3459
- host.panReqId = host.selReqId;
3460
- host.channel.post({ kind: "measure", paths: [[...cardPath]], reqId: host.selReqId });
3573
+ if (state === hostForActivePanel() && postStylebookPan(state, tag)) {
3574
+ pendingStylebookPan = null;
3575
+ }
3461
3576
  }
3462
3577
 
3463
3578
  // ─── Format-toolbar bridge (Phase 4b-2) ─────────────────────────────────────────
3464
3579
 
3465
- /** The current edit session's editing flag + latest selection snapshot, for the parent toolbar. */
3466
- export function getEditSnapshot(): {
3467
- editing: boolean;
3468
- editingProp: string | null;
3469
- snapshot: SelectionSnapshot | null;
3470
- } {
3580
+ /** The current edit session's state, for the parent toolbar. */
3581
+ export function getEditSnapshot(): EditSnapshotState {
3471
3582
  /* The same connectivity guard {@link isCaretActive} has, and for the same reason: a session
3472
3583
  belongs to a frame, and a frame that has left the document has no caret in it. `releaseHost`
3473
3584
  clears `activeEditHost`, so this is the second line rather than the first — it covers the frame
@@ -3578,7 +3689,7 @@ export function commitActiveEditSession(): void {
3578
3689
  /** The live host backing the active panel's canvas (for non-edit selection-bar positioning). */
3579
3690
  function hostForActivePanel(): HostState | null {
3580
3691
  const panel = getActivePanel();
3581
- return panel ? hostForCanvas(panel.canvas as HTMLElement) : null;
3692
+ return panel ? hostForCanvas(panel.canvas) : null;
3582
3693
  }
3583
3694
 
3584
3695
  /**
@@ -3592,7 +3703,7 @@ function hostForActivePanel(): HostState | null {
3592
3703
  * (where it evaluates to exactly `editZoom` — the iframe's layout width is `renderWidth / editZoom`
3593
3704
  * while its rendered width is `renderWidth`).
3594
3705
  */
3595
- export function getEditBarAnchorRect(): ParentRect | null {
3706
+ export function getEditBarAnchorRect(): OverlayPlacement | null {
3596
3707
  // The bar rests on the SELECTED ELEMENT's box, edit session or not. It used to prefer the caret's
3597
3708
  // Snapshot rect while editing, so it hopped line to line with the caret and sat over the text the
3598
3709
  // Author was typing; the snapshot still feeds the format state, never the position. Only an edit