@jxsuite/studio 6.1.0 → 6.1.2

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 (645) hide show
  1. package/dist/chunks/2020-f2gkpy8d.js +4 -0
  2. package/dist/chunks/{action-group-2mqby73c.js → action-group-4aan7g6g.js} +3 -3
  3. package/dist/chunks/adapter-4912g339.js +4 -0
  4. package/dist/chunks/cf-account-picker-wccaenh6.js +4 -0
  5. package/dist/chunks/cf-settings-br6105tr.js +4 -0
  6. package/dist/chunks/color-28ksk2qm.js +4 -0
  7. package/dist/chunks/color-area-0fj8a4ga.js +4 -0
  8. package/dist/chunks/color-field-xch8kx2g.js +4 -0
  9. package/dist/chunks/color-slider-jxd8v350.js +4 -0
  10. package/dist/chunks/combobox-p0efwjke.js +4 -0
  11. package/dist/chunks/components-db68vsn7.js +4 -0
  12. package/dist/chunks/content-source-1449qesq.js +4 -0
  13. package/dist/chunks/convert-to-repeater-5h457egj.js +4 -0
  14. package/dist/chunks/dialog-c4x05vyx.js +4 -0
  15. package/dist/chunks/editor-x9jn86a7.js +4 -0
  16. package/dist/chunks/entry-commands-may24cpt.js +4 -0
  17. package/dist/chunks/extension-sections-pvr14vxr.js +4 -0
  18. package/dist/chunks/file-ops-x9v54pcj.js +4 -0
  19. package/dist/chunks/files-j3knqf0s.js +4 -0
  20. package/dist/chunks/format-host-tyre2trb.js +4 -0
  21. package/dist/chunks/fs-events-64ma9htc.js +4 -0
  22. package/dist/chunks/git-panel-84ps5dex.js +4 -0
  23. package/dist/chunks/grid-view-d2wq9ezg.js +51 -0
  24. package/dist/chunks/icons-pnfz478q.js +4 -0
  25. package/dist/chunks/iframe-entry-fvbk89gm.js +5 -0
  26. package/dist/chunks/iframe-entry-jdcktez8.js +5 -0
  27. package/dist/chunks/iframe-entry-x0ygf2yr.js +5 -0
  28. package/dist/chunks/iframe-entry-zap785cw.js +5 -0
  29. package/dist/chunks/iframe-host-ck6vejt5.js +4 -0
  30. package/dist/chunks/index-b8xw3vy9.js +4 -0
  31. package/dist/chunks/{index-gybvv7a6.js → index-zpjmzqfk.js} +3 -3
  32. package/dist/chunks/{javascript-rnvdp3k4.js → javascript-m54mnwp6.js} +3 -3
  33. package/dist/chunks/jsonMode-w15cmrcm.js +13 -0
  34. package/dist/chunks/jx-validate-at3zn8wa.js +4 -0
  35. package/dist/chunks/layers-h4xp1pss.js +4 -0
  36. package/dist/chunks/layers-panel-m5hw1d7g.js +4 -0
  37. package/dist/chunks/listbox-e74qjyq6.js +4 -0
  38. package/dist/chunks/lit-html-8t8t3dnr.js +4 -0
  39. package/dist/chunks/media-picker-2cweexc5.js +4 -0
  40. package/dist/chunks/menu-n6yzswmy.js +4 -0
  41. package/dist/chunks/menu-ynw13y0s.js +4 -0
  42. package/dist/chunks/monaco-binding-7gx83x3r.js +4 -0
  43. package/dist/chunks/monaco-buffer-qzxn567r.js +4 -0
  44. package/dist/chunks/monaco-setup-gxf6656g.js +4 -0
  45. package/dist/chunks/notify-cj5zbrs2.js +4 -0
  46. package/dist/chunks/number-field-y62xq0ty.js +4 -0
  47. package/dist/chunks/parse-b7ddhr8m.js +4 -0
  48. package/dist/chunks/platform-3dfws9js.js +4 -0
  49. package/dist/chunks/popover-jt2gxmzs.js +4 -0
  50. package/dist/chunks/publish-panel-1gvxbj21.js +4 -0
  51. package/dist/chunks/quick-search-0dajrwqg.js +4 -0
  52. package/dist/chunks/references-49qgt404.js +4 -0
  53. package/dist/chunks/right-panel-jm1bwe53.js +4 -0
  54. package/dist/chunks/section-registry-6kwc4fky.js +4 -0
  55. package/dist/chunks/select-rzds841e.js +4 -0
  56. package/dist/chunks/split-n2gfpm5m.js +4 -0
  57. package/dist/chunks/{studio-5g0ag7c0.js → studio-0q988f9w.js} +3 -3
  58. package/dist/chunks/studio-0rt52vpq.js +5 -0
  59. package/dist/chunks/studio-18vgbhs5.js +5 -0
  60. package/dist/chunks/{studio-pxg4ap1e.js → studio-1fxkaaea.js} +4 -4
  61. package/dist/chunks/studio-24mccdrj.js +5 -0
  62. package/dist/chunks/studio-2eh507zk.js +9 -0
  63. package/dist/chunks/studio-2hcbccfv.js +5 -0
  64. package/dist/chunks/studio-2j1cb95x.js +5 -0
  65. package/dist/chunks/studio-2jbs3rxv.js +5 -0
  66. package/dist/chunks/{studio-qbj760tx.js → studio-485046te.js} +7 -7
  67. package/dist/chunks/studio-4h8k4dw9.js +5 -0
  68. package/dist/chunks/studio-5f6n18kx.js +5 -0
  69. package/dist/chunks/studio-5gthpky6.js +5 -0
  70. package/dist/chunks/studio-5ve6bbep.js +5 -0
  71. package/dist/chunks/studio-6ep3js6b.js +9 -0
  72. package/dist/chunks/studio-6wkmf3wz.js +9 -0
  73. package/dist/chunks/studio-841rf3cg.js +5 -0
  74. package/dist/chunks/studio-8gmwgheg.js +5 -0
  75. package/dist/chunks/studio-8qm0ep50.js +5 -0
  76. package/dist/chunks/{studio-anyrgzvb.js → studio-90pq9km1.js} +4 -4
  77. package/dist/chunks/studio-924p1tm1.js +6 -0
  78. package/dist/chunks/{studio-v2bvmbjj.js → studio-a3g98wjr.js} +4 -4
  79. package/dist/chunks/studio-a3shpz9y.js +5 -0
  80. package/dist/chunks/studio-amsbaaar.js +6 -0
  81. package/dist/chunks/studio-at5spjbc.js +7 -0
  82. package/dist/chunks/studio-b2xv945e.js +5 -0
  83. package/dist/chunks/studio-bn5nkafw.js +5 -0
  84. package/dist/chunks/studio-bw6q5xjf.js +9 -0
  85. package/dist/chunks/studio-c7q0t9x1.js +5 -0
  86. package/dist/chunks/studio-camnhc96.js +6 -0
  87. package/dist/chunks/studio-cffqvnzd.js +5 -0
  88. package/dist/chunks/studio-d81xfh1g.js +5 -0
  89. package/dist/chunks/studio-dgz8d0c1.js +5 -0
  90. package/dist/chunks/studio-efvfrsf8.js +5 -0
  91. package/dist/chunks/studio-er30bpcx.js +5 -0
  92. package/dist/chunks/studio-ezscgr64.js +5 -0
  93. package/dist/chunks/studio-ffrrncvz.js +5 -0
  94. package/dist/chunks/studio-fpphgn8y.js +10 -0
  95. package/dist/chunks/studio-j1kvhdjk.js +4 -0
  96. package/dist/chunks/{studio-txjjzrsm.js → studio-j430n16g.js} +5 -5
  97. package/dist/chunks/studio-jjkg84nj.js +5 -0
  98. package/dist/chunks/studio-m0q32ars.js +4 -0
  99. package/dist/chunks/studio-m6bdst11.js +5 -0
  100. package/dist/chunks/studio-nv2cptd4.js +64 -0
  101. package/dist/chunks/studio-nwfrc98n.js +5 -0
  102. package/dist/chunks/studio-p1wgvp7r.js +5 -0
  103. package/dist/chunks/studio-pag2wncm.js +5 -0
  104. package/dist/chunks/studio-qvanvzfd.js +5 -0
  105. package/dist/chunks/studio-qwa64kt7.js +5 -0
  106. package/dist/chunks/studio-rk1zc4x3.js +928 -0
  107. package/dist/chunks/studio-se2r3ptc.js +5 -0
  108. package/dist/chunks/studio-sg2a0dh0.js +5 -0
  109. package/dist/chunks/studio-tjzjhe68.js +5 -0
  110. package/dist/chunks/{studio-0x3jyb8r.js → studio-tq0rtfwj.js} +4 -4
  111. package/dist/chunks/{studio-cpxkgjrj.js → studio-v0jzqdjm.js} +4 -4
  112. package/dist/chunks/studio-v2w7q8r1.js +7 -0
  113. package/dist/chunks/studio-v5vjq1h0.js +4 -0
  114. package/dist/chunks/studio-v8aga3vp.js +14 -0
  115. package/dist/chunks/studio-vfcdrfmv.js +5 -0
  116. package/dist/chunks/studio-vmfkgdjf.js +5 -0
  117. package/dist/chunks/studio-wt1816jj.js +5 -0
  118. package/dist/chunks/studio-wz73q5kq.js +4 -0
  119. package/dist/chunks/studio-y04qtxvp.js +7 -0
  120. package/dist/chunks/studio-y21g8zzg.js +5 -0
  121. package/dist/chunks/studio-y64hrt8p.js +12 -0
  122. package/dist/chunks/studio-ynn90f58.js +16 -0
  123. package/dist/chunks/studio-yxz682sx.js +5 -0
  124. package/dist/chunks/studio-zp7snvkq.js +5 -0
  125. package/dist/chunks/studio-zq4z6tm6.js +5 -0
  126. package/dist/chunks/swatch-group-ewh24n1f.js +4 -0
  127. package/dist/chunks/tabs-f5cw3tgp.js +4 -0
  128. package/dist/chunks/textfield-wewc8rq2.js +4 -0
  129. package/dist/chunks/toast-c9fn075p.js +4 -0
  130. package/dist/chunks/toast-host-ke5z4ng7.js +4 -0
  131. package/dist/chunks/{toolbar-2nnzqhrj.js → toolbar-44cm5cta.js} +3 -3
  132. package/dist/chunks/tooltip-5v8ngwj2.js +4 -0
  133. package/dist/chunks/tree-mdq3v5cw.js +4 -0
  134. package/dist/chunks/tsMode-sf49ee9x.js +14 -0
  135. package/dist/chunks/workspace-jtfahpee.js +4 -0
  136. package/dist/chunks/ws-client-nze0k6mv.js +4 -0
  137. package/dist/iframe-entry.js +16 -16
  138. package/dist/manifest.json +63 -63
  139. package/dist/studio.js +98 -98
  140. package/dist/workers/ts.worker.js +3 -3
  141. package/package.json +11 -11
  142. package/src/about/about-modal.ts +4 -3
  143. package/src/account/action-flow.ts +45 -38
  144. package/src/account-status.ts +7 -2
  145. package/src/browse/library-layouts.ts +8 -5
  146. package/src/browse/library-model.ts +18 -6
  147. package/src/browse/library-pane.ts +37 -21
  148. package/src/browse/library-preview.ts +14 -9
  149. package/src/browse/library-source.ts +1 -1
  150. package/src/canvas/asset-refs.ts +24 -9
  151. package/src/canvas/asset-resolve.ts +19 -23
  152. package/src/canvas/canvas-helpers.ts +7 -6
  153. package/src/canvas/canvas-live-render.ts +44 -53
  154. package/src/canvas/canvas-patcher.ts +30 -30
  155. package/src/canvas/canvas-perf.ts +11 -5
  156. package/src/canvas/canvas-render.ts +66 -42
  157. package/src/canvas/canvas-surface.ts +5 -3
  158. package/src/canvas/canvas-utils.ts +163 -64
  159. package/src/canvas/dialog-path.ts +9 -4
  160. package/src/canvas/dialog-state.ts +1 -2
  161. package/src/canvas/diff-marks.ts +9 -16
  162. package/src/canvas/diff-toolbar.ts +8 -7
  163. package/src/canvas/edit-width-drag.ts +4 -1
  164. package/src/canvas/edit-width.ts +1 -3
  165. package/src/canvas/iframe-channel.ts +27 -13
  166. package/src/canvas/iframe-drop.ts +13 -12
  167. package/src/canvas/iframe-editable-root.ts +29 -16
  168. package/src/canvas/iframe-entry.ts +80 -63
  169. package/src/canvas/iframe-eval.ts +73 -49
  170. package/src/canvas/iframe-host.ts +203 -118
  171. package/src/canvas/iframe-inline-edit.ts +69 -32
  172. package/src/canvas/iframe-insert.ts +5 -4
  173. package/src/canvas/iframe-interaction.ts +37 -26
  174. package/src/canvas/iframe-overlay.ts +1 -1
  175. package/src/canvas/iframe-patch.ts +18 -15
  176. package/src/canvas/iframe-position.ts +12 -6
  177. package/src/canvas/iframe-protocol.ts +15 -16
  178. package/src/canvas/iframe-render.ts +41 -36
  179. package/src/canvas/iframe-slash.ts +24 -11
  180. package/src/canvas/iframe-subtree.ts +6 -2
  181. package/src/canvas/path-mapping.ts +19 -12
  182. package/src/canvas/popover-path.ts +7 -3
  183. package/src/canvas/popover-state.ts +1 -2
  184. package/src/canvas/serialize-scope.ts +13 -5
  185. package/src/canvas/stage-scrollbar.ts +2 -1
  186. package/src/canvas/surface-registry.ts +21 -3
  187. package/src/collab/collab-commands.ts +7 -2
  188. package/src/collab/collab-session.ts +84 -45
  189. package/src/collab/collab-state.ts +4 -3
  190. package/src/collab/monaco-binding.ts +16 -13
  191. package/src/collab/monaco-cursors.ts +5 -2
  192. package/src/commands/budget.ts +3 -3
  193. package/src/commands/command-args.ts +103 -71
  194. package/src/commands/context.ts +22 -20
  195. package/src/commands/defaults.ts +4 -1
  196. package/src/commands/keymap.ts +47 -43
  197. package/src/commands/levels.ts +3 -3
  198. package/src/commands/live-context.ts +10 -8
  199. package/src/commands/reference.ts +2 -1
  200. package/src/commands/registry.ts +12 -10
  201. package/src/commands/run-reported.ts +15 -10
  202. package/src/component-props.ts +46 -30
  203. package/src/content/collection-match.ts +5 -4
  204. package/src/content/draft-state.ts +4 -3
  205. package/src/content/entry-commands.ts +3 -2
  206. package/src/content/entry-editor.ts +29 -17
  207. package/src/content/entry-fields.ts +12 -7
  208. package/src/content/entry-model.ts +21 -12
  209. package/src/editor/canvas-context-menu.ts +1 -2
  210. package/src/editor/canvas-slash-bridge.ts +3 -6
  211. package/src/editor/content-slice.ts +16 -14
  212. package/src/editor/context-menu.ts +35 -22
  213. package/src/editor/convert-targets.ts +30 -35
  214. package/src/editor/convert-to-component.ts +11 -5
  215. package/src/editor/convert-to-repeater.ts +34 -22
  216. package/src/editor/editable-tags.ts +17 -2
  217. package/src/editor/file-drop-action.ts +21 -22
  218. package/src/editor/inline-edit-apply.ts +35 -17
  219. package/src/editor/inline-edit.ts +66 -50
  220. package/src/editor/inline-format.ts +59 -56
  221. package/src/editor/inline-link.ts +40 -15
  222. package/src/editor/insert-zone-action.ts +1 -1
  223. package/src/editor/merge-tags.ts +58 -19
  224. package/src/editor/repeater-scope.ts +39 -28
  225. package/src/editor/shortcuts.ts +87 -47
  226. package/src/editor/slash-menu.ts +1 -6
  227. package/src/files/asset-versions.ts +4 -4
  228. package/src/files/components.ts +15 -10
  229. package/src/files/elements.ts +5 -6
  230. package/src/files/file-ops.ts +77 -36
  231. package/src/files/files.ts +90 -95
  232. package/src/files/media-meta.ts +2 -1
  233. package/src/files/media-paths.ts +6 -1
  234. package/src/files/serialize-document.ts +2 -2
  235. package/src/format/constraints.ts +1 -1
  236. package/src/format/convert-file.ts +27 -13
  237. package/src/format/format-choices.ts +2 -1
  238. package/src/format/format-host.ts +76 -50
  239. package/src/github/github-auth-status.ts +27 -3
  240. package/src/github/github-auth.ts +19 -9
  241. package/src/github/github-publish.ts +24 -20
  242. package/src/grid/cell-editors.ts +46 -31
  243. package/src/grid/cell-popovers.ts +5 -3
  244. package/src/grid/edit-buffer.ts +10 -9
  245. package/src/grid/grid-controller.ts +20 -22
  246. package/src/grid/grid-layout.ts +21 -5
  247. package/src/grid/grid-open.ts +3 -6
  248. package/src/grid/grid-panel.ts +11 -4
  249. package/src/grid/grid-source.ts +8 -8
  250. package/src/grid/grid-view.ts +14 -17
  251. package/src/grid/redirects-grid.ts +14 -5
  252. package/src/grid/redirects.ts +16 -14
  253. package/src/grid/schema-columns.ts +25 -19
  254. package/src/grid/sources/connector-source.ts +47 -20
  255. package/src/grid/sources/content-source.ts +25 -24
  256. package/src/grid/sources/csv-file-source.ts +2 -2
  257. package/src/grid/tabulator-tables.d.ts +55 -34
  258. package/src/hosting/stage.ts +7 -7
  259. package/src/i18n/i18n-commands.ts +1 -2
  260. package/src/media/media-pane.ts +4 -1
  261. package/src/new-project/add-repo-modal.ts +20 -19
  262. package/src/new-project/import-tab.ts +10 -14
  263. package/src/new-project/location-fields.ts +2 -1
  264. package/src/new-project/new-project-modal.ts +12 -9
  265. package/src/packages/jxsuite-update.ts +4 -3
  266. package/src/packages/pull-package-sync.ts +26 -25
  267. package/src/page-params.ts +97 -68
  268. package/src/panels/activity-panel.ts +17 -9
  269. package/src/panels/ai-chat/attached-context.ts +13 -7
  270. package/src/panels/ai-chat/chat-view.ts +80 -73
  271. package/src/panels/ai-chat/composer.ts +30 -33
  272. package/src/panels/ai-chat/sessions-view.ts +1 -2
  273. package/src/panels/ai-panel.ts +23 -20
  274. package/src/panels/block-action-bar.ts +46 -35
  275. package/src/panels/bottom-dock.ts +5 -3
  276. package/src/panels/canvas-dnd-bridge.ts +31 -24
  277. package/src/panels/data-explorer.ts +62 -25
  278. package/src/panels/data-grid.ts +14 -11
  279. package/src/panels/dnd.ts +108 -118
  280. package/src/panels/editors.ts +55 -22
  281. package/src/panels/elements-panel.ts +10 -3
  282. package/src/panels/events-panel.ts +84 -69
  283. package/src/panels/formula-workspace.ts +76 -51
  284. package/src/panels/frontmatter-fields.ts +24 -19
  285. package/src/panels/frontmatter-panel.ts +44 -34
  286. package/src/panels/git-diff-open.ts +4 -4
  287. package/src/panels/git-panel.ts +49 -61
  288. package/src/panels/head-panel.ts +131 -111
  289. package/src/panels/i18n-panel.ts +10 -3
  290. package/src/panels/imports-panel.ts +23 -47
  291. package/src/panels/jump-bar.ts +48 -29
  292. package/src/panels/layers-panel.ts +97 -74
  293. package/src/panels/left-panel.ts +3 -3
  294. package/src/panels/pane-context.ts +31 -22
  295. package/src/panels/pane-grid.ts +49 -20
  296. package/src/panels/panel-registry.ts +22 -37
  297. package/src/panels/properties-panel.ts +175 -121
  298. package/src/panels/quick-search.ts +53 -31
  299. package/src/panels/right-panel.ts +3 -3
  300. package/src/panels/seo-modal.ts +13 -17
  301. package/src/panels/settings-menu.ts +15 -15
  302. package/src/panels/signals-panel.ts +153 -131
  303. package/src/panels/statement-editor.ts +230 -140
  304. package/src/panels/style-panel.ts +254 -216
  305. package/src/panels/style-utils.ts +114 -51
  306. package/src/panels/stylebook-doc.ts +57 -54
  307. package/src/panels/stylebook-layers-panel.ts +9 -9
  308. package/src/panels/stylebook-panel.ts +13 -17
  309. package/src/panels/tab-drop.ts +27 -11
  310. package/src/panels/tab-strip.ts +36 -26
  311. package/src/platform-errors.ts +57 -43
  312. package/src/platform.ts +58 -30
  313. package/src/platforms/cloud.ts +345 -175
  314. package/src/platforms/default-platform.ts +10 -4
  315. package/src/platforms/devserver.ts +174 -127
  316. package/src/platforms/read-batcher.ts +16 -8
  317. package/src/project-list.ts +2 -1
  318. package/src/publish/deploy-checklist.ts +8 -2
  319. package/src/publish/pages-service.ts +28 -13
  320. package/src/publish/publish-commands.ts +5 -4
  321. package/src/publish/publish-panel.ts +10 -6
  322. package/src/recent-projects.ts +15 -8
  323. package/src/resize-edges.ts +26 -36
  324. package/src/services/a11y-report.ts +26 -19
  325. package/src/services/agent-seed.ts +23 -11
  326. package/src/services/ai-ask.ts +9 -12
  327. package/src/services/ai-command-tools.ts +68 -62
  328. package/src/services/ai-import-tools.ts +80 -67
  329. package/src/services/ai-models.ts +17 -13
  330. package/src/services/ai-project-tools.ts +68 -32
  331. package/src/services/ai-session-store.ts +19 -10
  332. package/src/services/ai-system-prompt.ts +46 -20
  333. package/src/services/ai-tools.ts +71 -27
  334. package/src/services/ai-write-report.ts +20 -12
  335. package/src/services/ai-writes.ts +4 -4
  336. package/src/services/automation.ts +58 -31
  337. package/src/services/cem-export.ts +149 -58
  338. package/src/services/clock.ts +6 -1
  339. package/src/services/code-services.ts +37 -31
  340. package/src/services/context-manager.ts +14 -10
  341. package/src/services/context-resolver.ts +20 -8
  342. package/src/services/data-service.ts +19 -15
  343. package/src/services/directory-picker.ts +18 -12
  344. package/src/services/document-assistant.ts +39 -19
  345. package/src/services/gated-registry.ts +10 -6
  346. package/src/services/harness/chat-reducer.ts +23 -1
  347. package/src/services/import-client.ts +91 -38
  348. package/src/services/import-seed.ts +8 -2
  349. package/src/services/jx-validate.ts +28 -31
  350. package/src/services/live-preview.ts +0 -0
  351. package/src/services/live-surfaces.ts +6 -3
  352. package/src/services/model-uri.ts +10 -2
  353. package/src/services/module-preloads.ts +12 -8
  354. package/src/services/monaco-buffer.ts +43 -24
  355. package/src/services/monaco-lazy.ts +7 -8
  356. package/src/services/monaco-setup.ts +23 -16
  357. package/src/services/notify.ts +44 -17
  358. package/src/services/popover-report.ts +25 -10
  359. package/src/services/preview-eval.ts +20 -8
  360. package/src/services/profile.ts +13 -6
  361. package/src/services/project-path.ts +13 -10
  362. package/src/services/references.ts +0 -0
  363. package/src/services/render-critic.ts +8 -7
  364. package/src/services/settings/kernel.ts +13 -4
  365. package/src/services/settings/write-queue.ts +9 -9
  366. package/src/services/token-lint.ts +12 -10
  367. package/src/services/tool-executor.ts +17 -5
  368. package/src/services/tool-outcomes.ts +44 -24
  369. package/src/services/trusted-types.ts +24 -7
  370. package/src/settings/contexts-section.ts +11 -8
  371. package/src/settings/contributed-section.ts +79 -60
  372. package/src/settings/css-vars-editor.ts +37 -16
  373. package/src/settings/defs-editor.ts +38 -35
  374. package/src/settings/extension-rows.ts +16 -8
  375. package/src/settings/extension-sections.ts +23 -13
  376. package/src/settings/extensions-section.ts +4 -1
  377. package/src/settings/general-settings.ts +11 -8
  378. package/src/settings/locales-section.ts +20 -12
  379. package/src/settings/preferences-dialog.ts +9 -6
  380. package/src/settings/preferences-keymap.ts +7 -5
  381. package/src/settings/preferences-sections.ts +11 -5
  382. package/src/settings/project-sections.ts +1 -1
  383. package/src/settings/schema-field-ui.ts +9 -0
  384. package/src/settings/settings-document.ts +5 -5
  385. package/src/shell/tree.ts +6 -5
  386. package/src/shell.ts +104 -81
  387. package/src/site-context.ts +37 -22
  388. package/src/state.ts +41 -49
  389. package/src/store.ts +54 -34
  390. package/src/studio.ts +39 -77
  391. package/src/style/live-preview.ts +1 -1
  392. package/src/style/project-styles.ts +6 -7
  393. package/src/style/token-ref.ts +5 -4
  394. package/src/surfaces/about.ts +18 -19
  395. package/src/surfaces/add-repo.ts +17 -19
  396. package/src/surfaces/ai-chat.ts +31 -36
  397. package/src/surfaces/ai-credentials-form.ts +6 -16
  398. package/src/surfaces/ai-managed-connect.ts +3 -4
  399. package/src/surfaces/ai-model-picker.ts +3 -5
  400. package/src/surfaces/block-action-bar.ts +22 -11
  401. package/src/surfaces/boot-failure.ts +4 -5
  402. package/src/surfaces/bottom-dock.ts +9 -18
  403. package/src/surfaces/canvas-stage.ts +12 -11
  404. package/src/surfaces/cf-account-picker.ts +17 -18
  405. package/src/surfaces/commandbar.ts +14 -9
  406. package/src/surfaces/convert-repeater.ts +17 -19
  407. package/src/surfaces/data-actions.ts +9 -7
  408. package/src/surfaces/dialog.ts +26 -28
  409. package/src/surfaces/diff-toolbar.ts +9 -7
  410. package/src/surfaces/doc-header.ts +10 -19
  411. package/src/surfaces/empty-state.ts +7 -10
  412. package/src/surfaces/entry-editor.ts +14 -11
  413. package/src/surfaces/expression-editor.ts +11 -25
  414. package/src/surfaces/files-panel.ts +27 -14
  415. package/src/surfaces/formula-palette.ts +4 -5
  416. package/src/surfaces/git-panel.ts +7 -15
  417. package/src/surfaces/github-auth.ts +16 -20
  418. package/src/surfaces/github-publish.ts +17 -22
  419. package/src/surfaces/grid-cell.ts +30 -31
  420. package/src/surfaces/grid-open.ts +10 -14
  421. package/src/surfaces/grid-panel.ts +13 -9
  422. package/src/surfaces/grid-replace.ts +13 -13
  423. package/src/surfaces/grid-views.ts +15 -18
  424. package/src/surfaces/inspector-dock.ts +13 -12
  425. package/src/surfaces/jump-bar.ts +5 -6
  426. package/src/surfaces/jxsuite-update.ts +16 -22
  427. package/src/surfaces/library-pane.ts +25 -20
  428. package/src/surfaces/logic-panel.ts +17 -30
  429. package/src/surfaces/logic-workspace.ts +13 -15
  430. package/src/surfaces/media-browser.ts +11 -10
  431. package/src/surfaces/media-field.ts +4 -5
  432. package/src/surfaces/media-pane.ts +8 -7
  433. package/src/surfaces/menu.ts +29 -23
  434. package/src/surfaces/navigator-dock.ts +11 -12
  435. package/src/surfaces/new-project.ts +27 -26
  436. package/src/surfaces/pane-context.ts +20 -25
  437. package/src/surfaces/pane-grid.ts +15 -10
  438. package/src/surfaces/panel-activity.ts +5 -6
  439. package/src/surfaces/panel-data.ts +3 -4
  440. package/src/surfaces/panel-deploy-checklist.ts +5 -6
  441. package/src/surfaces/panel-elements.ts +5 -7
  442. package/src/surfaces/panel-i18n.ts +5 -7
  443. package/src/surfaces/panel-imports.ts +6 -13
  444. package/src/surfaces/panel-outline.ts +25 -19
  445. package/src/surfaces/panel-page.ts +11 -18
  446. package/src/surfaces/panel-problems.ts +5 -7
  447. package/src/surfaces/panel-signals.ts +20 -34
  448. package/src/surfaces/panel-stylebook-layers.ts +5 -8
  449. package/src/surfaces/preferences.ts +21 -11
  450. package/src/surfaces/progress-modal.ts +15 -9
  451. package/src/surfaces/properties-panel.json +1 -1
  452. package/src/surfaces/properties-panel.ts +20 -42
  453. package/src/surfaces/publish.ts +18 -18
  454. package/src/surfaces/push-plan.ts +7 -8
  455. package/src/surfaces/rail.json +126 -73
  456. package/src/surfaces/rail.ts +38 -12
  457. package/src/surfaces/reference-field.ts +11 -15
  458. package/src/surfaces/schema-builder.ts +16 -22
  459. package/src/surfaces/schema-form.ts +15 -25
  460. package/src/surfaces/secret-field.ts +9 -6
  461. package/src/surfaces/seo.ts +21 -15
  462. package/src/surfaces/settings-contexts.ts +9 -7
  463. package/src/surfaces/settings-contributed.ts +14 -10
  464. package/src/surfaces/settings-css-vars.ts +5 -12
  465. package/src/surfaces/settings-defs.json +2 -2
  466. package/src/surfaces/settings-defs.ts +17 -21
  467. package/src/surfaces/settings-deploy.ts +8 -15
  468. package/src/surfaces/settings-extensions.ts +5 -6
  469. package/src/surfaces/settings-head.ts +33 -39
  470. package/src/surfaces/settings-locales.ts +8 -20
  471. package/src/surfaces/settings-overview.ts +5 -6
  472. package/src/surfaces/settings-packages.ts +8 -7
  473. package/src/surfaces/settings-pane.ts +9 -18
  474. package/src/surfaces/settings-rawjson.ts +5 -6
  475. package/src/surfaces/shell.ts +1 -2
  476. package/src/surfaces/slash-menu.ts +19 -9
  477. package/src/surfaces/statements.ts +13 -18
  478. package/src/surfaces/statusbar.ts +17 -14
  479. package/src/surfaces/style-panel.ts +18 -31
  480. package/src/surfaces/stylebook-chrome.ts +3 -5
  481. package/src/surfaces/tab-strip.ts +14 -13
  482. package/src/surfaces/target-line.ts +6 -7
  483. package/src/surfaces/welcome.ts +5 -7
  484. package/src/tabs/patch-ops.ts +4 -3
  485. package/src/tabs/project-config.ts +32 -21
  486. package/src/tabs/selection.ts +36 -8
  487. package/src/tabs/tab.ts +52 -41
  488. package/src/tabs/transact.ts +98 -53
  489. package/src/tabs/typing.ts +31 -0
  490. package/src/types.ts +32 -17
  491. package/src/ui/ai-credentials-form.ts +10 -13
  492. package/src/ui/ai-model-picker.ts +3 -3
  493. package/src/ui/cf-account-picker.ts +7 -9
  494. package/src/ui/color-selector.ts +9 -5
  495. package/src/ui/dynamic-slot.ts +7 -8
  496. package/src/ui/expression-editor.ts +112 -105
  497. package/src/ui/form-controls.ts +57 -48
  498. package/src/ui/formula-catalog.ts +239 -230
  499. package/src/ui/formula-chips.ts +33 -18
  500. package/src/ui/layers.ts +44 -31
  501. package/src/ui/media-picker.ts +18 -9
  502. package/src/ui/popover-surface.ts +20 -12
  503. package/src/ui/progress-modal.ts +13 -8
  504. package/src/ui/regions.ts +7 -6
  505. package/src/ui/schema-form.ts +113 -70
  506. package/src/ui/surface.ts +71 -8
  507. package/src/ui/timing.ts +9 -0
  508. package/src/ui/value-source.ts +44 -31
  509. package/src/ui/virtual-window.ts +3 -2
  510. package/src/utils/base64.ts +3 -1
  511. package/src/utils/edit-display.ts +103 -74
  512. package/src/utils/element-selectors.ts +3 -2
  513. package/src/utils/google-fonts.ts +15 -14
  514. package/src/utils/inherited-style.ts +18 -20
  515. package/src/utils/link-target.ts +1 -1
  516. package/src/utils/preview-format.ts +25 -3
  517. package/src/utils/strip-events.ts +49 -22
  518. package/src/utils/studio-utils.ts +29 -14
  519. package/src/view.ts +2 -3
  520. package/src/workspace/pane-derive.ts +10 -12
  521. package/src/workspace/session.ts +53 -43
  522. package/src/workspace/workspace.ts +47 -36
  523. package/dist/chunks/2020-nkxms0ay.js +0 -4
  524. package/dist/chunks/adapter-m948ab5f.js +0 -4
  525. package/dist/chunks/cf-account-picker-n8hr38v7.js +0 -4
  526. package/dist/chunks/cf-settings-356xdtne.js +0 -4
  527. package/dist/chunks/color-area-jqhna8zy.js +0 -4
  528. package/dist/chunks/color-field-embq5ctg.js +0 -4
  529. package/dist/chunks/color-fpezvnkt.js +0 -4
  530. package/dist/chunks/color-slider-1qmw7wqy.js +0 -4
  531. package/dist/chunks/combobox-773hdzyj.js +0 -4
  532. package/dist/chunks/components-yk3t844e.js +0 -4
  533. package/dist/chunks/content-source-bawmyc09.js +0 -4
  534. package/dist/chunks/convert-to-repeater-7wns8kf1.js +0 -4
  535. package/dist/chunks/dialog-mtm9fq5t.js +0 -4
  536. package/dist/chunks/editor-6sc8nacq.js +0 -4
  537. package/dist/chunks/entry-commands-8ay9fh81.js +0 -4
  538. package/dist/chunks/extension-sections-na7rj3nr.js +0 -4
  539. package/dist/chunks/file-ops-gye0qw56.js +0 -4
  540. package/dist/chunks/files-dmc2xvcq.js +0 -4
  541. package/dist/chunks/format-host-t76b53f8.js +0 -4
  542. package/dist/chunks/fs-events-cg4tpz06.js +0 -4
  543. package/dist/chunks/git-panel-yef7bshy.js +0 -4
  544. package/dist/chunks/grid-view-7tt4bxw1.js +0 -51
  545. package/dist/chunks/icons-qbh8akb1.js +0 -4
  546. package/dist/chunks/iframe-entry-f7mqjwpm.js +0 -5
  547. package/dist/chunks/iframe-entry-haw7jkvz.js +0 -5
  548. package/dist/chunks/iframe-entry-z6qrytv3.js +0 -5
  549. package/dist/chunks/iframe-host-tmz30gfp.js +0 -4
  550. package/dist/chunks/index-nag78a9h.js +0 -4
  551. package/dist/chunks/jsonMode-zhwsprqk.js +0 -13
  552. package/dist/chunks/jx-validate-3gbp5nkn.js +0 -4
  553. package/dist/chunks/layers-fvz99859.js +0 -4
  554. package/dist/chunks/layers-panel-gchmbf9p.js +0 -4
  555. package/dist/chunks/listbox-s8fkmnq8.js +0 -4
  556. package/dist/chunks/lit-html-9agdspkt.js +0 -4
  557. package/dist/chunks/media-picker-rhjvyy00.js +0 -4
  558. package/dist/chunks/menu-2e4y9j7b.js +0 -4
  559. package/dist/chunks/menu-37d07z9m.js +0 -4
  560. package/dist/chunks/monaco-binding-2jnhjjvr.js +0 -4
  561. package/dist/chunks/monaco-buffer-rp21359g.js +0 -4
  562. package/dist/chunks/monaco-setup-vhh7gayr.js +0 -4
  563. package/dist/chunks/notify-h3www560.js +0 -4
  564. package/dist/chunks/number-field-gw7sq8rn.js +0 -4
  565. package/dist/chunks/parse-897fvqah.js +0 -4
  566. package/dist/chunks/platform-hz34wcd7.js +0 -4
  567. package/dist/chunks/popover-8htra4er.js +0 -4
  568. package/dist/chunks/publish-panel-exsmjzyj.js +0 -4
  569. package/dist/chunks/quick-search-pgaqeh0m.js +0 -4
  570. package/dist/chunks/references-2cgdxnb8.js +0 -4
  571. package/dist/chunks/right-panel-jf856w5w.js +0 -4
  572. package/dist/chunks/section-registry-jnf8r5gy.js +0 -4
  573. package/dist/chunks/select-n3v7gwas.js +0 -4
  574. package/dist/chunks/split-pff8f3ze.js +0 -4
  575. package/dist/chunks/studio-0f55h9ye.js +0 -5
  576. package/dist/chunks/studio-0sqdhnew.js +0 -12
  577. package/dist/chunks/studio-19x1c75w.js +0 -5
  578. package/dist/chunks/studio-2sqcndak.js +0 -5
  579. package/dist/chunks/studio-3aey7dd3.js +0 -5
  580. package/dist/chunks/studio-3bpdkq2s.js +0 -5
  581. package/dist/chunks/studio-47y6fbpf.js +0 -5
  582. package/dist/chunks/studio-4m3826v2.js +0 -5
  583. package/dist/chunks/studio-4nx2dj1g.js +0 -4
  584. package/dist/chunks/studio-4xv5s47r.js +0 -9
  585. package/dist/chunks/studio-5g4t8v6w.js +0 -5
  586. package/dist/chunks/studio-5py436sf.js +0 -5
  587. package/dist/chunks/studio-77kt594t.js +0 -5
  588. package/dist/chunks/studio-8f87n763.js +0 -10
  589. package/dist/chunks/studio-8mb00b84.js +0 -5
  590. package/dist/chunks/studio-8p51gq2h.js +0 -6
  591. package/dist/chunks/studio-954ds834.js +0 -5
  592. package/dist/chunks/studio-9ft45c83.js +0 -9
  593. package/dist/chunks/studio-a2138x5k.js +0 -7
  594. package/dist/chunks/studio-a9yzstqj.js +0 -5
  595. package/dist/chunks/studio-acdksxx6.js +0 -5
  596. package/dist/chunks/studio-ar8jcf2y.js +0 -5
  597. package/dist/chunks/studio-bmn4mn9j.js +0 -7
  598. package/dist/chunks/studio-e7xxa0sj.js +0 -5
  599. package/dist/chunks/studio-ecaz25vk.js +0 -5
  600. package/dist/chunks/studio-ery42sqr.js +0 -4
  601. package/dist/chunks/studio-fzdywr6p.js +0 -9
  602. package/dist/chunks/studio-g5fmf0c1.js +0 -5
  603. package/dist/chunks/studio-g7nse7zb.js +0 -5
  604. package/dist/chunks/studio-geq1ea6k.js +0 -14
  605. package/dist/chunks/studio-ghkb32r8.js +0 -5
  606. package/dist/chunks/studio-ghrhy2y7.js +0 -5
  607. package/dist/chunks/studio-grawhey1.js +0 -5
  608. package/dist/chunks/studio-h0xgswew.js +0 -5
  609. package/dist/chunks/studio-h9c4p2tx.js +0 -5
  610. package/dist/chunks/studio-jyzj5cxs.js +0 -5
  611. package/dist/chunks/studio-mgx8yhnz.js +0 -64
  612. package/dist/chunks/studio-mpb04y1d.js +0 -6
  613. package/dist/chunks/studio-n17mty64.js +0 -5
  614. package/dist/chunks/studio-nwh2eqsk.js +0 -6
  615. package/dist/chunks/studio-q1h883bt.js +0 -16
  616. package/dist/chunks/studio-q4tv82h7.js +0 -9
  617. package/dist/chunks/studio-qpqa7vk3.js +0 -5
  618. package/dist/chunks/studio-qqz8e5gk.js +0 -5
  619. package/dist/chunks/studio-qvasen3f.js +0 -5
  620. package/dist/chunks/studio-rggdb3hm.js +0 -5
  621. package/dist/chunks/studio-sghy936h.js +0 -4
  622. package/dist/chunks/studio-skprc6ef.js +0 -5
  623. package/dist/chunks/studio-tt2nactf.js +0 -5
  624. package/dist/chunks/studio-tyasmc39.js +0 -5
  625. package/dist/chunks/studio-vk9ezads.js +0 -5
  626. package/dist/chunks/studio-vzacdfsc.js +0 -4
  627. package/dist/chunks/studio-w4c0xmrw.js +0 -5
  628. package/dist/chunks/studio-wfjqhhbk.js +0 -5
  629. package/dist/chunks/studio-wgh5kxpj.js +0 -5
  630. package/dist/chunks/studio-xasj0796.js +0 -5
  631. package/dist/chunks/studio-y78s8r2n.js +0 -5
  632. package/dist/chunks/studio-z8w183sh.js +0 -5
  633. package/dist/chunks/studio-zscd9ty4.js +0 -928
  634. package/dist/chunks/studio-zxp13sss.js +0 -7
  635. package/dist/chunks/studio-zygnmm01.js +0 -5
  636. package/dist/chunks/swatch-group-8qh9a3bm.js +0 -4
  637. package/dist/chunks/tabs-jwthg40w.js +0 -4
  638. package/dist/chunks/textfield-spy3hc77.js +0 -4
  639. package/dist/chunks/toast-4d35zd64.js +0 -4
  640. package/dist/chunks/toast-host-8sjmfed3.js +0 -4
  641. package/dist/chunks/tooltip-fd6g9xy3.js +0 -4
  642. package/dist/chunks/tree-h19v1jfz.js +0 -4
  643. package/dist/chunks/tsMode-gr0m4dzr.js +0 -14
  644. package/dist/chunks/workspace-m15qtcqp.js +0 -4
  645. package/dist/chunks/ws-client-nf61frr1.js +0 -4
@@ -32,6 +32,7 @@ import {
32
32
  setDiffView,
33
33
  stepDiff,
34
34
  } from "./diff-view";
35
+ import { isString } from "@jxsuite/schema/guards";
35
36
  import type { DiffView } from "./diff-view";
36
37
  import type { ChangeStep } from "./diff-marks";
37
38
  import { panToParentRect } from "./canvas-utils";
@@ -158,7 +159,7 @@ function describeStep(step: ChangeStep, index: number, total: number): string {
158
159
  * it is, so a reveal that could not measure leaves a correct toolbar over an unmoved canvas.
159
160
  */
160
161
  function onStepClick(paneId: string, delta: 1 | -1): void {
161
- stepDiffAndReveal(paneId, delta).catch((error: unknown) => {
162
+ stepDiffAndReveal(paneId, delta).catch((error) => {
162
163
  console.warn("stepDiffAndReveal:", error);
163
164
  });
164
165
  }
@@ -180,8 +181,8 @@ export async function stepDiffAndReveal(paneId: string, delta: 1 | -1): Promise<
180
181
  renderDiffToolbar(paneId);
181
182
  const step = diffChangeMapOf(paneId)?.steps[landed];
182
183
  const { panels } = surfaceForPane(paneId);
183
- const original = panels[0]?.canvas as HTMLElement | undefined;
184
- const current = panels[1]?.canvas as HTMLElement | undefined;
184
+ const original = panels[0]?.canvas;
185
+ const current = panels[1]?.canvas;
185
186
  if (!step) {
186
187
  return;
187
188
  }
@@ -252,10 +253,10 @@ export function diffToolbarView(paneId: string): DiffToolbarView {
252
253
  return {
253
254
  codeChecked: showing === "code" ? "true" : "false",
254
255
  countLabel,
255
- degraded: !code && map?.degraded ? "shown" : "hidden",
256
+ degraded: !code && map.degraded ? "shown" : "hidden",
256
257
  nextDisabled: index >= total - 1,
257
258
  prevDisabled: index <= 0,
258
- rootKeys: !code && map?.rootKeys.length ? "shown" : "hidden",
259
+ rootKeys: !code && map.rootKeys.length > 0 ? "shown" : "hidden",
259
260
  rootKeysTitle: `Changed: ${map?.rootKeys.join(", ") ?? ""}`,
260
261
  stepper: total === 0 ? "hidden" : "shown",
261
262
  // A file the canvas cannot render arrives with a null map: there is no visual half to offer.
@@ -266,8 +267,8 @@ export function diffToolbarView(paneId: string): DiffToolbarView {
266
267
 
267
268
  /** The pane a diff verb addresses: named, or the focused one. */
268
269
  function paneOfArgs(args: CommandArgValues): string {
269
- const { pane } = args as { pane?: unknown };
270
- return typeof pane === "string" ? pane : workspace.activePaneId;
270
+ const { pane } = args;
271
+ return isString(pane) ? pane : workspace.activePaneId;
271
272
  }
272
273
 
273
274
  const paneProperty = stringProperty("Which pane to act on. Defaults to the focused one.");
@@ -58,7 +58,7 @@ export function applyEditWidth(surface: CanvasSurface, column: HTMLElement, widt
58
58
  return;
59
59
  }
60
60
  column.style.maxWidth = `${Math.round(width)}px`;
61
- if ((tab.session.ui.editZoom ?? 1) !== 1) {
61
+ if (tab.session.ui.editZoom !== 1) {
62
62
  applyEditZoom(surface);
63
63
  }
64
64
  const rendered = rectOf(column).width || width;
@@ -183,5 +183,8 @@ export function mountEditWidthHandle(
183
183
  if (!handle || !column || _wired.has(handle)) {
184
184
  return;
185
185
  }
186
+ /* SAFETY: Every caller passes one of the two `jx-split` slots `surfaces/canvas-stage.json` draws
187
+ beside the column, and a `jx-split` carries the `value`/`min`/`max`/`collapse` share
188
+ properties `SplitElement` names; `bindSplit` only writes them. */
186
189
  _wired.set(handle, bindSplit(handle as SplitElement, editWidthTarget(surface, column, grow)));
187
190
  }
@@ -78,9 +78,7 @@ const _editWidths = new Map<string, EditWidthRecord>();
78
78
 
79
79
  /** The size breakpoints and base width a tab renders against, site context included. */
80
80
  function mediaEntriesOfTab(tab: Tab) {
81
- return parseMediaEntries(
82
- getEffectiveMedia(tab.doc.document?.$media as Record<string, string> | undefined),
83
- );
81
+ return parseMediaEntries(getEffectiveMedia(tab.doc.document.$media));
84
82
  }
85
83
 
86
84
  /**
@@ -9,6 +9,8 @@
9
9
  * plus an in-memory `fakeChannelPair` so cross-frame logic is unit-testable without a live iframe.
10
10
  */
11
11
 
12
+ import { isNonNullObject } from "@jxsuite/schema/guards";
13
+
12
14
  /**
13
15
  * A typed, bidirectional message channel. `TOut` is what this side sends; `TIn` is what it
14
16
  * receives. Implementations: a real `postMessage` channel over an `<iframe>` (added with the iframe
@@ -26,8 +28,14 @@ export interface IframeChannel<TOut, TIn> {
26
28
  /** Envelope key that tags every cross-frame message so foreign `message` events are ignored. */
27
29
  const ENVELOPE = "jx:canvas";
28
30
 
29
- interface PostMessageTarget {
30
- postMessage: (message: unknown, targetOrigin: string) => void;
31
+ /** What crosses the frame boundary: the session token beside the typed message it authenticates. */
32
+ export interface CanvasEnvelope<T> {
33
+ [ENVELOPE]: string;
34
+ payload: T;
35
+ }
36
+
37
+ interface PostMessageTarget<TOut> {
38
+ postMessage: (message: CanvasEnvelope<TOut>, targetOrigin: string) => void;
31
39
  }
32
40
 
33
41
  interface MessageSource {
@@ -46,7 +54,7 @@ interface MessageSource {
46
54
  * origin.
47
55
  */
48
56
  export function postMessageChannel<TOut, TIn>(opts: {
49
- target: PostMessageTarget;
57
+ target: PostMessageTarget<TOut>;
50
58
  source: MessageSource;
51
59
  targetOrigin: string;
52
60
  acceptOrigin: string;
@@ -59,12 +67,15 @@ export function postMessageChannel<TOut, TIn>(opts: {
59
67
  if (acceptOrigin !== "*" && event.origin !== acceptOrigin) {
60
68
  return;
61
69
  }
62
- const data = event.data as { [ENVELOPE]?: string; payload?: TIn } | null;
63
- if (!data || typeof data !== "object" || data[ENVELOPE] !== token) {
70
+ const envelope: unknown = event.data;
71
+ if (!isNonNullObject(envelope) || !(ENVELOPE in envelope) || envelope[ENVELOPE] !== token) {
64
72
  return;
65
73
  }
74
+ /* SAFETY: The envelope carries this session's secret token, which only the peer channel holds,
75
+ and that peer's `post` is typed to send exactly `TIn`; a foreign page cannot forge the token. */
76
+ const payload = ("payload" in envelope ? envelope.payload : undefined) as TIn;
66
77
  for (const handler of handlers) {
67
- handler(data.payload as TIn);
78
+ handler(payload);
68
79
  }
69
80
  };
70
81
  source.addEventListener("message", listener);
@@ -84,19 +95,22 @@ export function postMessageChannel<TOut, TIn>(opts: {
84
95
  };
85
96
  }
86
97
 
98
+ /** Both ends of an in-memory channel, and the controls that deliver what they posted. */
99
+ export interface FakeChannelPair<ParentOut, IframeOut> {
100
+ parent: IframeChannel<ParentOut, IframeOut>;
101
+ iframe: IframeChannel<IframeOut, ParentOut>;
102
+ flush: () => void;
103
+ /** Number of messages waiting to be delivered (both directions). */
104
+ pending: () => number;
105
+ }
106
+
87
107
  /**
88
108
  * Two in-memory channels wired to each other for tests. A message `parent.post(x)` is delivered to
89
109
  * the `iframe` side's handlers (and vice versa) only when `flush()` is called — so tests drive the
90
110
  * inherently-async postMessage ordering deterministically. Messages posted _during_ a flush are
91
111
  * queued for the next flush, never delivered re-entrantly.
92
112
  */
93
- export function fakeChannelPair<ParentOut, IframeOut>(): {
94
- parent: IframeChannel<ParentOut, IframeOut>;
95
- iframe: IframeChannel<IframeOut, ParentOut>;
96
- flush: () => void;
97
- /** Number of messages waiting to be delivered (both directions). */
98
- pending: () => number;
99
- } {
113
+ export function fakeChannelPair<ParentOut, IframeOut>(): FakeChannelPair<ParentOut, IframeOut> {
100
114
  let toIframe: ParentOut[] = [];
101
115
  let toParent: IframeOut[] = [];
102
116
  const iframeHandlers = new Set<(m: ParentOut) => void>();
@@ -17,7 +17,7 @@
17
17
  */
18
18
 
19
19
  import { parseJxPath } from "./path-mapping";
20
- import { displayTagName } from "@jxsuite/schema/guards";
20
+ import { displayTagName, isNonNullObject } from "@jxsuite/schema/guards";
21
21
  import { rectOf, elementAtPoint } from "../utils/geometry";
22
22
  import { getNodeAtPath, isAncestor, pathsEqual } from "../state";
23
23
  import type { DragSrcKind, DropPreview } from "./iframe-protocol";
@@ -52,9 +52,11 @@ const VOID_TAGS = new Set([
52
52
  * reuse it. Returns null when no addressable node is found.
53
53
  */
54
54
  function nearestPathEl(start: Element | null): HTMLElement | null {
55
- let el = start instanceof Element ? (start as HTMLElement) : null;
55
+ // Only an `HTMLElement` is ever stamped (`makeStamper` skips every other node), so the walk
56
+ // Passes over SVG and other foreign elements on its way up.
57
+ let el = start;
56
58
  while (el) {
57
- if (el.dataset?.jxPath) {
59
+ if (el instanceof HTMLElement && el.dataset.jxPath) {
58
60
  return el;
59
61
  }
60
62
  el = el.parentElement;
@@ -94,15 +96,15 @@ export function computeDropInstruction(
94
96
  shadowDoc: JxMutableNode,
95
97
  src: DragSrcKind,
96
98
  ): DropPreview | null {
97
- const serialized = targetEl.dataset?.jxPath;
99
+ const serialized = targetEl.dataset.jxPath;
98
100
  if (serialized == null) {
99
101
  return null;
100
102
  }
101
- const targetPath = parseJxPath(serialized) as JxPath;
103
+ const targetPath = parseJxPath(serialized);
102
104
 
103
105
  // Root: pick the nearest child edge among the element's element children.
104
106
  if (targetPath.length === 0) {
105
- const children = [...targetEl.children] as HTMLElement[];
107
+ const children = [...targetEl.children];
106
108
  if (children.length === 0) {
107
109
  return canDrop(src, targetPath)
108
110
  ? {
@@ -120,11 +122,10 @@ export function computeDropInstruction(
120
122
  return null;
121
123
  }
122
124
 
123
- const node = getNodeAtPath(shadowDoc, targetPath) as JxMutableNode | undefined;
125
+ const node = getNodeAtPath(shadowDoc, targetPath);
124
126
  const tag = (displayTagName(node?.tagName) || "div").toLowerCase();
125
127
  const hasElementChildren =
126
- Array.isArray(node?.children) &&
127
- node.children.some((c: unknown) => c != null && typeof c === "object");
128
+ Array.isArray(node?.children) && node.children.some((c) => isNonNullObject(c));
128
129
  const isLeaf = VOID_TAGS.has(tag) || !hasElementChildren;
129
130
 
130
131
  const rect = rectFor(targetEl);
@@ -150,7 +151,7 @@ export function computeDropInstruction(
150
151
  * parentElementPath(slice(0,-2)) / childIndex(at(-1)) read the same parent + index.
151
152
  */
152
153
  function nearestChildEdge(
153
- children: HTMLElement[],
154
+ children: Element[],
154
155
  cursorY: number,
155
156
  parentPath: JxPath,
156
157
  src: DragSrcKind,
@@ -175,7 +176,7 @@ function nearestChildEdge(
175
176
  }
176
177
  }
177
178
 
178
- const childPath = [...parentPath, "children", closestIdx] as JxPath;
179
+ const childPath: JxPath = [...parentPath, "children", closestIdx];
179
180
  if (!canDrop(src, childPath)) {
180
181
  return null;
181
182
  }
@@ -192,7 +193,7 @@ function canDrop(src: DragSrcKind, targetPath: JxPath): boolean {
192
193
  if (src.type !== "tree-node") {
193
194
  return true;
194
195
  }
195
- const srcPath = src.path as JxPath;
196
+ const srcPath = src.path;
196
197
  if (pathsEqual(srcPath, targetPath)) {
197
198
  return false;
198
199
  }
@@ -35,6 +35,7 @@ import {
35
35
  toDomPosition,
36
36
  } from "./iframe-position";
37
37
  import { serializeJxPath } from "./path-mapping";
38
+ import { isFunction } from "@jxsuite/schema/guards";
38
39
  import type { DocPos, DocRange, EditablePredicate } from "./iframe-position";
39
40
  import type { JxPath } from "../state";
40
41
 
@@ -167,6 +168,14 @@ export interface EditableRootHandle {
167
168
  isComposing: () => boolean;
168
169
  }
169
170
 
171
+ /**
172
+ * Whether an element carries `dataset`. HTML, SVG and MathML elements all mix in
173
+ * `HTMLOrSVGElement`; an element from any other namespace has no `dataset` at all.
174
+ */
175
+ function hasDataset(el: Element): el is Element & HTMLOrSVGElement {
176
+ return "dataset" in el;
177
+ }
178
+
170
179
  /** Wire the editing host on `container`. */
171
180
  export function startEditableRoot(
172
181
  container: HTMLElement,
@@ -183,13 +192,13 @@ export function startEditableRoot(
183
192
  return mode === undefined || mode === "design" || mode === "edit";
184
193
  };
185
194
 
186
- /** Pending idle-commit timer, or 0 when none is armed. */
187
- let commitTimer = 0;
195
+ /** Pending idle-commit timer, or null when none is armed. */
196
+ let commitTimer: ReturnType<typeof setTimeout> | null = null;
188
197
 
189
198
  const cancelCommitTick = () => {
190
- if (commitTimer) {
199
+ if (commitTimer !== null) {
191
200
  clearTimeout(commitTimer);
192
- commitTimer = 0;
201
+ commitTimer = null;
193
202
  }
194
203
  };
195
204
 
@@ -202,7 +211,7 @@ export function startEditableRoot(
202
211
  * idle commit invisible — without it, typing would jump the caret every time you paused.
203
212
  */
204
213
  const commitTick = () => {
205
- commitTimer = 0;
214
+ commitTimer = null;
206
215
  if (!activeEl || !deps.onCommitTick) {
207
216
  return;
208
217
  }
@@ -233,7 +242,7 @@ export function startEditableRoot(
233
242
  return;
234
243
  }
235
244
  cancelCommitTick();
236
- commitTimer = setTimeout(commitTick, deps.commitDelayMs ?? COMMIT_IDLE_MS) as unknown as number;
245
+ commitTimer = setTimeout(commitTick, deps.commitDelayMs ?? COMMIT_IDLE_MS);
237
246
  };
238
247
 
239
248
  const onCompositionStart = () => {
@@ -349,7 +358,8 @@ export function startEditableRoot(
349
358
  const affectedRange = (
350
359
  e: InputEvent,
351
360
  ): { from: DocPos; to: DocPos; endEl: HTMLElement } | null => {
352
- const [target] = typeof e.getTargetRanges === "function" ? e.getTargetRanges() : [];
361
+ // Happy-dom ships no `getTargetRanges` at all, so the method's presence is probed, not assumed.
362
+ const [target] = isFunction(e.getTargetRanges) ? e.getTargetRanges() : [];
353
363
  const sel = doc.getSelection();
354
364
  const useSelection = sel && sel.rangeCount > 0 && (sel.isCollapsed || !target);
355
365
  let startNode: Node | null;
@@ -370,8 +380,7 @@ export function startEditableRoot(
370
380
  return from && to && endBlock ? { endEl: endBlock.el, from, to } : null;
371
381
  };
372
382
 
373
- const onBeforeInput = (event: Event) => {
374
- const e = event as InputEvent;
383
+ const onBeforeInput = (e: InputEvent) => {
375
384
  if (!editingAllowed()) {
376
385
  return;
377
386
  }
@@ -435,21 +444,25 @@ export function startEditableRoot(
435
444
  if (!editingAllowed() || !deps.onPropActivate) {
436
445
  return;
437
446
  }
438
- let el = e.target instanceof Element ? (e.target as HTMLElement) : null;
447
+ let el = e.target instanceof Element ? e.target : null;
439
448
  while (el && el !== container) {
440
- if (el.dataset?.jxBoundProp) {
441
- if (el !== activeEl) {
449
+ if (hasDataset(el) && el.dataset.jxBoundProp) {
450
+ /* SAFETY: The runtime builds SVG and MathML nodes as `createElementNS(...) as HTMLElement`
451
+ and stamps `data-jx-bound-prop` through that same `HTMLElement`-typed path, so a
452
+ foreign-namespace prop host already travels downstream as an `HTMLElement`. */
453
+ const host = el as HTMLElement;
454
+ if (host !== activeEl) {
442
455
  deactivate();
443
- if (deps.onPropActivate(el)) {
456
+ if (deps.onPropActivate(host)) {
444
457
  // Adopt the nested host so the caret landing inside it is not read as a block change.
445
- activeEl = el;
458
+ activeEl = host;
446
459
  activeKey = null;
447
460
  }
448
461
  }
449
462
  return;
450
463
  }
451
464
  // A stamped page block first means this is ordinary page DOM, not component internals.
452
- if (el.dataset?.jxPath) {
465
+ if (hasDataset(el) && el.dataset.jxPath) {
453
466
  return;
454
467
  }
455
468
  el = el.parentElement;
@@ -493,7 +506,7 @@ export function startEditableRoot(
493
506
  if (composing) {
494
507
  return;
495
508
  }
496
- if (commitTimer) {
509
+ if (commitTimer !== null) {
497
510
  cancelCommitTick();
498
511
  commitTick();
499
512
  }
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { postMessageChannel } from "./iframe-channel";
9
9
  import { rectOf } from "../utils/geometry";
10
- import { displayTagName } from "@jxsuite/schema/guards";
10
+ import { displayTagName, isNonNullObject } from "@jxsuite/schema/guards";
11
11
  import {
12
12
  applyCanvasDialogOpen,
13
13
  applyCanvasPopoverOpen,
@@ -59,16 +59,17 @@ import {
59
59
  import { resolveAssetRef } from "./asset-resolve";
60
60
  import type { AssetContext } from "./asset-resolve";
61
61
  import { KIT_LOADERS } from "@jxsuite/ui/loaders";
62
- import type { IframeChannel } from "./iframe-channel";
62
+ import type { CanvasEnvelope, IframeChannel } from "./iframe-channel";
63
63
  import type {
64
64
  CanvasMode,
65
+ DragOverMsg,
65
66
  DragSrcKind,
66
67
  FileDropHit,
67
68
  IframeToParent,
68
69
  LayoutHit,
69
70
  ParentToIframe,
70
71
  } from "./iframe-protocol";
71
- import type { JxDocument, JxMutableNode, JxStyle } from "@jxsuite/schema/types";
72
+ import type { JxDocument, JxMutableNode } from "@jxsuite/schema/types";
72
73
  import type { IframeRenderCtx, RenderHandle } from "./iframe-render";
73
74
  import { setBundleBase } from "../services/bundle-base";
74
75
 
@@ -153,12 +154,12 @@ export function fileDropHitFor(
153
154
  targetEl: HTMLElement,
154
155
  shadowDoc: JxMutableNode | null,
155
156
  ): FileDropHit | null {
156
- const serialized = targetEl.dataset?.jxPath;
157
+ const serialized = targetEl.dataset.jxPath;
157
158
  if (serialized == null) {
158
159
  return null;
159
160
  }
160
161
  const path = parseJxPath(serialized);
161
- const node = shadowDoc ? (getNodeAtPath(shadowDoc, path as JxPath) as JxMutableNode) : undefined;
162
+ const node = shadowDoc ? getNodeAtPath(shadowDoc, path) : undefined;
162
163
  return {
163
164
  path,
164
165
  rect: rectFor(targetEl),
@@ -186,7 +187,8 @@ export function layoutHitFor(target: EventTarget | null): LayoutHit | null {
186
187
  return null;
187
188
  }
188
189
  return {
189
- className: typeof el.className === "string" ? el.className : "",
190
+ // An `HTMLElement`'s `className` is always the string form (only SVG's is an animated value).
191
+ className: el.className,
190
192
  layoutFile: el.dataset.jxLayoutFile ?? "",
191
193
  layoutPath: parseJxPath(el.dataset.jxLayoutPath ?? "[]"),
192
194
  rect: rectFor(el),
@@ -194,6 +196,34 @@ export function layoutHitFor(target: EventTarget | null): LayoutHit | null {
194
196
  };
195
197
  }
196
198
 
199
+ /**
200
+ * The text a failed patch or render reports: the thrown value's `message` when it has one, the
201
+ * value itself otherwise.
202
+ */
203
+ function failureText(cause: unknown): string {
204
+ const message = isNonNullObject(cause) && "message" in cause ? cause.message : undefined;
205
+ return String(message ?? cause);
206
+ }
207
+
208
+ /** The edge-hold auto-scroll loop's last cursor, and the drag it belongs to. */
209
+ interface AutoScrollCursor {
210
+ x: number;
211
+ y: number;
212
+ dragSeq: number;
213
+ withCursor: boolean;
214
+ src?: DragSrcKind;
215
+ gen?: number;
216
+ }
217
+
218
+ /**
219
+ * The two document APIs the `idle` sample reads, as a frame may or may not provide them. lib.dom
220
+ * declares both unconditionally; happy-dom implements neither.
221
+ */
222
+ interface IdleProbeDocument {
223
+ getAnimations?: Document["getAnimations"];
224
+ fonts?: FontFaceSet;
225
+ }
226
+
197
227
  /** Set-key keys the patcher applies IN PLACE (never a subtree re-render) — safe under a live edit. */
198
228
  const IN_PLACE_KEYS = new Set(["style", "textContent"]);
199
229
 
@@ -317,14 +347,7 @@ export function startCanvasIframe(opts: {
317
347
  // (the parent has no pointer of its own then). Parent-driven flows (1/2/4) leave `src` undefined
318
348
  // And `withCursor` false — the tick falls back to `dragSrc`/`dragGen` and the parent moves the
319
349
  // Ghost from its own raw pointer.
320
- let autoScrollCursor: {
321
- x: number;
322
- y: number;
323
- dragSeq: number;
324
- withCursor: boolean;
325
- src?: DragSrcKind;
326
- gen?: number;
327
- } | null = null;
350
+ let autoScrollCursor: AutoScrollCursor | null = null;
328
351
  const win = container.ownerDocument.defaultView;
329
352
 
330
353
  function stopAutoScroll(): void {
@@ -353,16 +376,17 @@ export function startCanvasIframe(opts: {
353
376
  return;
354
377
  }
355
378
  const preview = previewAt(autoScrollCursor, src, shadowDoc, container.ownerDocument);
356
- channel.post({
357
- // Flow 3 (withCursor) re-posts the cursor so the parent keeps tracking the ghost at an edge-hold.
358
- ...(autoScrollCursor.withCursor
359
- ? { cursor: { x: autoScrollCursor.x, y: autoScrollCursor.y } }
360
- : {}),
379
+ const over: DragOverMsg = {
361
380
  dragSeq: autoScrollCursor.dragSeq,
362
381
  gen: autoScrollCursor.gen ?? dragGen,
363
382
  kind: "dragOver",
364
383
  preview,
365
- });
384
+ };
385
+ // Flow 3 (withCursor) re-posts the cursor so the parent keeps tracking the ghost at an edge-hold.
386
+ if (autoScrollCursor.withCursor) {
387
+ over.cursor = { x: autoScrollCursor.x, y: autoScrollCursor.y };
388
+ }
389
+ channel.post(over);
366
390
  autoScrollFrame = win.requestAnimationFrame(autoScrollTick);
367
391
  }
368
392
 
@@ -381,13 +405,12 @@ export function startCanvasIframe(opts: {
381
405
  stopAutoScroll();
382
406
  return;
383
407
  }
384
- autoScrollCursor = {
385
- dragSeq,
386
- withCursor: flow3 != null,
387
- x: cursor.x,
388
- y: cursor.y,
389
- ...(flow3 ? { gen: flow3.gen, src: flow3.src } : {}),
390
- };
408
+ const next: AutoScrollCursor = { dragSeq, withCursor: flow3 != null, x: cursor.x, y: cursor.y };
409
+ if (flow3) {
410
+ next.gen = flow3.gen;
411
+ next.src = flow3.src;
412
+ }
413
+ autoScrollCursor = next;
391
414
  if (!autoScrollFrame) {
392
415
  autoScrollFrame = win.requestAnimationFrame(autoScrollTick);
393
416
  }
@@ -522,13 +545,11 @@ export function startCanvasIframe(opts: {
522
545
  }
523
546
 
524
547
  function sampleIdle(): Extract<IframeToParent, { kind: "idle" }> {
525
- const running =
526
- typeof frameDoc.getAnimations === "function"
527
- ? frameDoc.getAnimations().filter((a) => a.playState === "running").length
528
- : 0;
548
+ const probe: IdleProbeDocument = frameDoc;
549
+ const running = probe.getAnimations?.().filter((a) => a.playState === "running").length ?? 0;
529
550
  return {
530
551
  animations: running,
531
- fonts: frameDoc.fonts ? frameDoc.fonts.status === "loaded" : true,
552
+ fonts: probe.fonts ? probe.fonts.status === "loaded" : true,
532
553
  gen: renderedGen,
533
554
  images: pendingImageRetries(),
534
555
  kind: "idle",
@@ -750,12 +771,7 @@ export function startCanvasIframe(opts: {
750
771
  // The wrong scope); the parent additionally gen-gates the reply against its own state.
751
772
  const results =
752
773
  msg.gen === renderedGen && renderCtx && shadowDoc
753
- ? evaluateLiveExprs(
754
- msg.exprs,
755
- renderCtx.defs as Record<string, unknown>,
756
- shadowDoc,
757
- msg.contextPath,
758
- )
774
+ ? evaluateLiveExprs(msg.exprs, renderCtx.defs, shadowDoc, msg.contextPath)
759
775
  : [];
760
776
  channel.post({ gen: msg.gen, kind: "evalResult", reqId: msg.reqId, results });
761
777
  return;
@@ -820,11 +836,7 @@ export function startCanvasIframe(opts: {
820
836
  channel.post({ gen, kind: "patchComplete" });
821
837
  armIdleWatch();
822
838
  } catch (error) {
823
- channel.post({
824
- gen,
825
- kind: "patchError",
826
- message: String((error as Error)?.message ?? error),
827
- });
839
+ channel.post({ gen, kind: "patchError", message: failureText(error) });
828
840
  }
829
841
  return;
830
842
  }
@@ -835,14 +847,10 @@ export function startCanvasIframe(opts: {
835
847
  if (msg.gen !== renderedGen || !shadowDoc) {
836
848
  return;
837
849
  }
838
- (shadowDoc as { style?: JxStyle }).style = msg.style as JxStyle;
850
+ shadowDoc.style = msg.style;
839
851
  const rootEl = container.firstElementChild;
840
852
  if (rootEl instanceof HTMLElement) {
841
- reapplyStyle(
842
- rootEl,
843
- msg.style as JxStyle,
844
- (shadowDoc as { $media?: Record<string, string> }).$media ?? {},
845
- );
853
+ reapplyStyle(rootEl, msg.style, shadowDoc.$media ?? {});
846
854
  }
847
855
  return;
848
856
  }
@@ -945,7 +953,10 @@ export function startCanvasIframe(opts: {
945
953
  if (msg.kind === "redefineElement") {
946
954
  /* The canvas half of embedding.md §7. The definition is replaced in THIS realm; instances
947
955
  already on the canvas keep the old one until the host's next render replaces them. */
948
- void redefineElement(msg.doc as never, msg.base);
956
+ /* SAFETY: The host posts only the component definition a save just wrote
957
+ (`redefineElementOnCanvases`), a whole `.json` element document, and `redefineElement`
958
+ itself rejects one whose `tagName` is not a hyphenated literal. */
959
+ void redefineElement(msg.doc as JxDocument, msg.base);
949
960
  return;
950
961
  }
951
962
  if (msg.kind === "setLocale") {
@@ -988,6 +999,8 @@ export function startCanvasIframe(opts: {
988
999
  // A document with no format, where the studio's own element metadata answers on its own.
989
1000
  setEditableVerdicts(msg.editableTags ?? null);
990
1001
  const { gen, mapperCtx } = msg;
1002
+ /* SAFETY: The host posts `shadowDoc` as a JSON round-trip of the tab's raw page document (or of
1003
+ the stylebook specimen), so it is a `JxMutableNode` in the forward-op coordinate space. */
991
1004
  const rawDoc = msg.shadowDoc as JxMutableNode;
992
1005
  void (async () => {
993
1006
  try {
@@ -1002,9 +1015,11 @@ export function startCanvasIframe(opts: {
1002
1015
  than a theoretical one. It used to be exactly that, one orphaned `<style>` per styled
1003
1016
  element per render. */
1004
1017
  resetDocumentStyles(container.ownerDocument);
1005
- handle = await renderResolvedDocument({
1018
+ const renderOpts: Parameters<typeof renderResolvedDocument>[0] = {
1006
1019
  assets: versionedAssets(msg.assets ?? null),
1007
1020
  container,
1021
+ /* SAFETY: The host posts `doc` as a JSON round-trip of `resolveCanvasDocument`'s render
1022
+ document (or the stylebook specimen), which is the resolved `JxDocument` it renders. */
1008
1023
  doc: msg.doc as JxDocument,
1009
1024
  docBase: msg.docBase,
1010
1025
  mapperCtx: {
@@ -1019,8 +1034,11 @@ export function startCanvasIframe(opts: {
1019
1034
  popoverOpen: msg.popoverOpen ? serializeJxPath(msg.popoverOpen) : null,
1020
1035
  dialogOpen: msg.dialogOpen ? serializeJxPath(msg.dialogOpen) : null,
1021
1036
  siteStyle: msg.siteStyle,
1022
- ...(msg.allowAutoRequests ? { allowAutoRequests: true } : {}),
1023
- });
1037
+ };
1038
+ if (msg.allowAutoRequests) {
1039
+ renderOpts.allowAutoRequests = true;
1040
+ }
1041
+ handle = await renderResolvedDocument(renderOpts);
1024
1042
  if (gen === latestGen) {
1025
1043
  /* A render replaces the DOM, so the open attribute goes with it. The host sends the value
1026
1044
  on the render message for exactly this reason; a frame that kept only its own copy
@@ -1042,7 +1060,7 @@ export function startCanvasIframe(opts: {
1042
1060
  // BuildScope returns, and serializeDataScope reads defs[key], so the effect tracks those
1043
1061
  // Refs and RE-POSTS an updated snapshot when the data settles (the host gen-gates stale
1044
1062
  // Ones). Isolated in try/catch: a serialization failure never breaks the render ack above.
1045
- const defs = handle.ctx.defs as Record<string, unknown>;
1063
+ const { defs } = handle.ctx;
1046
1064
  stopDataScopeWatch = observeScope(() => {
1047
1065
  try {
1048
1066
  channel.post({ gen, kind: "dataScope", scope: serializeDataScope(defs) });
@@ -1054,11 +1072,7 @@ export function startCanvasIframe(opts: {
1054
1072
  postContentHeight();
1055
1073
  }
1056
1074
  } catch (error) {
1057
- channel.post({
1058
- gen,
1059
- kind: "renderError",
1060
- message: String((error as Error)?.message ?? error),
1061
- });
1075
+ channel.post({ gen, kind: "renderError", message: failureText(error) });
1062
1076
  }
1063
1077
  })();
1064
1078
  });
@@ -1094,7 +1108,9 @@ export function startCanvasIframe(opts: {
1094
1108
  interface BootWindow {
1095
1109
  location: { search: string };
1096
1110
  document: { querySelector: (selectors: string) => Element | null; body: HTMLElement };
1097
- parent: { postMessage: (message: unknown, targetOrigin: string) => void };
1111
+ parent: {
1112
+ postMessage: (message: CanvasEnvelope<IframeToParent>, targetOrigin: string) => void;
1113
+ };
1098
1114
  addEventListener: (type: "message", listener: (event: MessageEvent) => void) => void;
1099
1115
  removeEventListener: (type: "message", listener: (event: MessageEvent) => void) => void;
1100
1116
  }
@@ -1122,8 +1138,9 @@ export function bootCanvasIframe(win: BootWindow): () => void {
1122
1138
  "The parent origin did not round-trip; the channel relies on the shared token alone.",
1123
1139
  );
1124
1140
  }
1125
- const container = (win.document.querySelector("#jx-canvas-root") ??
1126
- win.document.body) as HTMLElement;
1141
+ // `canvas.html` draws `#jx-canvas-root` as a `<div>`; a page without it renders into `<body>`.
1142
+ const root = win.document.querySelector("#jx-canvas-root");
1143
+ const container = root instanceof HTMLElement ? root : win.document.body;
1127
1144
  const channel = postMessageChannel<IframeToParent, ParentToIframe>({
1128
1145
  acceptOrigin: parentOrigin,
1129
1146
  source: win,
@@ -1136,5 +1153,5 @@ export function bootCanvasIframe(win: BootWindow): () => void {
1136
1153
 
1137
1154
  // Boot only when actually loaded as the iframe document (has a real parent frame), never in tests.
1138
1155
  if (typeof window !== "undefined" && window.parent !== window) {
1139
- bootCanvasIframe(window as unknown as BootWindow);
1156
+ bootCanvasIframe(window);
1140
1157
  }