@jxsuite/studio 6.1.1 → 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 (637) hide show
  1. package/dist/chunks/{2020-va2wwra4.js → 2020-f2gkpy8d.js} +3 -3
  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-2veqmnhr.js → index-zpjmzqfk.js} +3 -3
  32. package/dist/chunks/{javascript-8nbvs12s.js → javascript-m54mnwp6.js} +3 -3
  33. package/dist/chunks/{jsonMode-s8kt9dsj.js → jsonMode-w15cmrcm.js} +8 -8
  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-1q1sh2ax.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-h46vtzk8.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-rv11kfwa.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-8grwxtqk.js → studio-90pq9km1.js} +4 -4
  77. package/dist/chunks/studio-924p1tm1.js +6 -0
  78. package/dist/chunks/{studio-pre0skq6.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-vrmcjf9g.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-wszjyh0j.js → studio-tq0rtfwj.js} +4 -4
  111. package/dist/chunks/{studio-kggey1jt.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-zey7zsdw.js → studio-y64hrt8p.js} +7 -7
  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-2frah6h1.js → tsMode-sf49ee9x.js} +7 -7
  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 +97 -97
  140. package/package.json +6 -6
  141. package/src/about/about-modal.ts +4 -3
  142. package/src/account/action-flow.ts +45 -38
  143. package/src/account-status.ts +7 -2
  144. package/src/browse/library-layouts.ts +8 -5
  145. package/src/browse/library-model.ts +18 -6
  146. package/src/browse/library-pane.ts +37 -21
  147. package/src/browse/library-preview.ts +14 -9
  148. package/src/browse/library-source.ts +1 -1
  149. package/src/canvas/asset-refs.ts +24 -9
  150. package/src/canvas/asset-resolve.ts +19 -23
  151. package/src/canvas/canvas-helpers.ts +7 -6
  152. package/src/canvas/canvas-live-render.ts +44 -53
  153. package/src/canvas/canvas-patcher.ts +30 -30
  154. package/src/canvas/canvas-perf.ts +11 -5
  155. package/src/canvas/canvas-render.ts +62 -41
  156. package/src/canvas/canvas-surface.ts +5 -3
  157. package/src/canvas/canvas-utils.ts +90 -49
  158. package/src/canvas/dialog-path.ts +9 -4
  159. package/src/canvas/dialog-state.ts +1 -2
  160. package/src/canvas/diff-marks.ts +9 -16
  161. package/src/canvas/diff-toolbar.ts +8 -7
  162. package/src/canvas/edit-width-drag.ts +4 -1
  163. package/src/canvas/edit-width.ts +1 -3
  164. package/src/canvas/iframe-channel.ts +27 -13
  165. package/src/canvas/iframe-drop.ts +13 -12
  166. package/src/canvas/iframe-editable-root.ts +29 -16
  167. package/src/canvas/iframe-entry.ts +80 -63
  168. package/src/canvas/iframe-eval.ts +73 -49
  169. package/src/canvas/iframe-host.ts +135 -96
  170. package/src/canvas/iframe-inline-edit.ts +49 -30
  171. package/src/canvas/iframe-insert.ts +5 -4
  172. package/src/canvas/iframe-interaction.ts +30 -25
  173. package/src/canvas/iframe-overlay.ts +1 -1
  174. package/src/canvas/iframe-patch.ts +18 -15
  175. package/src/canvas/iframe-position.ts +12 -6
  176. package/src/canvas/iframe-protocol.ts +12 -16
  177. package/src/canvas/iframe-render.ts +36 -34
  178. package/src/canvas/iframe-slash.ts +24 -11
  179. package/src/canvas/iframe-subtree.ts +6 -2
  180. package/src/canvas/path-mapping.ts +19 -12
  181. package/src/canvas/popover-path.ts +7 -3
  182. package/src/canvas/popover-state.ts +1 -2
  183. package/src/canvas/serialize-scope.ts +13 -5
  184. package/src/canvas/stage-scrollbar.ts +2 -1
  185. package/src/canvas/surface-registry.ts +21 -3
  186. package/src/collab/collab-commands.ts +7 -2
  187. package/src/collab/collab-session.ts +84 -45
  188. package/src/collab/collab-state.ts +4 -3
  189. package/src/collab/monaco-binding.ts +16 -13
  190. package/src/collab/monaco-cursors.ts +5 -2
  191. package/src/commands/budget.ts +3 -3
  192. package/src/commands/command-args.ts +103 -71
  193. package/src/commands/context.ts +22 -20
  194. package/src/commands/keymap.ts +47 -43
  195. package/src/commands/levels.ts +3 -3
  196. package/src/commands/live-context.ts +7 -7
  197. package/src/commands/reference.ts +2 -1
  198. package/src/commands/registry.ts +12 -10
  199. package/src/commands/run-reported.ts +15 -10
  200. package/src/component-props.ts +46 -30
  201. package/src/content/collection-match.ts +5 -4
  202. package/src/content/draft-state.ts +4 -3
  203. package/src/content/entry-commands.ts +3 -2
  204. package/src/content/entry-editor.ts +29 -17
  205. package/src/content/entry-fields.ts +12 -7
  206. package/src/content/entry-model.ts +21 -12
  207. package/src/editor/canvas-context-menu.ts +1 -2
  208. package/src/editor/canvas-slash-bridge.ts +3 -6
  209. package/src/editor/content-slice.ts +16 -14
  210. package/src/editor/context-menu.ts +35 -22
  211. package/src/editor/convert-targets.ts +30 -35
  212. package/src/editor/convert-to-component.ts +11 -5
  213. package/src/editor/convert-to-repeater.ts +34 -22
  214. package/src/editor/editable-tags.ts +17 -2
  215. package/src/editor/file-drop-action.ts +21 -22
  216. package/src/editor/inline-edit-apply.ts +35 -17
  217. package/src/editor/inline-edit.ts +66 -50
  218. package/src/editor/inline-format.ts +59 -56
  219. package/src/editor/inline-link.ts +40 -15
  220. package/src/editor/insert-zone-action.ts +1 -1
  221. package/src/editor/merge-tags.ts +58 -19
  222. package/src/editor/repeater-scope.ts +39 -28
  223. package/src/editor/shortcuts.ts +32 -20
  224. package/src/editor/slash-menu.ts +1 -6
  225. package/src/files/asset-versions.ts +4 -4
  226. package/src/files/components.ts +15 -10
  227. package/src/files/elements.ts +5 -6
  228. package/src/files/file-ops.ts +77 -36
  229. package/src/files/files.ts +90 -95
  230. package/src/files/media-meta.ts +2 -1
  231. package/src/files/media-paths.ts +3 -0
  232. package/src/files/serialize-document.ts +2 -2
  233. package/src/format/constraints.ts +1 -1
  234. package/src/format/convert-file.ts +27 -13
  235. package/src/format/format-choices.ts +2 -1
  236. package/src/format/format-host.ts +76 -50
  237. package/src/github/github-auth-status.ts +27 -3
  238. package/src/github/github-auth.ts +19 -9
  239. package/src/github/github-publish.ts +24 -20
  240. package/src/grid/cell-editors.ts +46 -31
  241. package/src/grid/cell-popovers.ts +5 -3
  242. package/src/grid/edit-buffer.ts +10 -9
  243. package/src/grid/grid-controller.ts +20 -22
  244. package/src/grid/grid-layout.ts +21 -5
  245. package/src/grid/grid-open.ts +3 -6
  246. package/src/grid/grid-panel.ts +11 -4
  247. package/src/grid/grid-source.ts +8 -8
  248. package/src/grid/grid-view.ts +14 -17
  249. package/src/grid/redirects-grid.ts +14 -5
  250. package/src/grid/redirects.ts +16 -14
  251. package/src/grid/schema-columns.ts +25 -19
  252. package/src/grid/sources/connector-source.ts +47 -20
  253. package/src/grid/sources/content-source.ts +25 -24
  254. package/src/grid/sources/csv-file-source.ts +2 -2
  255. package/src/grid/tabulator-tables.d.ts +55 -34
  256. package/src/hosting/stage.ts +7 -7
  257. package/src/i18n/i18n-commands.ts +1 -2
  258. package/src/media/media-pane.ts +4 -1
  259. package/src/new-project/add-repo-modal.ts +20 -19
  260. package/src/new-project/import-tab.ts +10 -14
  261. package/src/new-project/location-fields.ts +2 -1
  262. package/src/new-project/new-project-modal.ts +12 -9
  263. package/src/packages/jxsuite-update.ts +4 -3
  264. package/src/packages/pull-package-sync.ts +26 -25
  265. package/src/page-params.ts +97 -68
  266. package/src/panels/activity-panel.ts +17 -9
  267. package/src/panels/ai-chat/attached-context.ts +13 -7
  268. package/src/panels/ai-chat/chat-view.ts +80 -73
  269. package/src/panels/ai-chat/composer.ts +30 -33
  270. package/src/panels/ai-chat/sessions-view.ts +1 -2
  271. package/src/panels/ai-panel.ts +23 -20
  272. package/src/panels/block-action-bar.ts +46 -35
  273. package/src/panels/bottom-dock.ts +5 -3
  274. package/src/panels/canvas-dnd-bridge.ts +31 -24
  275. package/src/panels/data-explorer.ts +62 -25
  276. package/src/panels/data-grid.ts +14 -11
  277. package/src/panels/dnd.ts +108 -118
  278. package/src/panels/editors.ts +52 -21
  279. package/src/panels/elements-panel.ts +10 -3
  280. package/src/panels/events-panel.ts +84 -69
  281. package/src/panels/formula-workspace.ts +76 -51
  282. package/src/panels/frontmatter-fields.ts +24 -19
  283. package/src/panels/frontmatter-panel.ts +44 -34
  284. package/src/panels/git-diff-open.ts +4 -4
  285. package/src/panels/git-panel.ts +49 -61
  286. package/src/panels/head-panel.ts +131 -111
  287. package/src/panels/i18n-panel.ts +10 -3
  288. package/src/panels/imports-panel.ts +23 -47
  289. package/src/panels/jump-bar.ts +48 -29
  290. package/src/panels/layers-panel.ts +97 -74
  291. package/src/panels/left-panel.ts +3 -3
  292. package/src/panels/pane-context.ts +31 -22
  293. package/src/panels/pane-grid.ts +49 -20
  294. package/src/panels/panel-registry.ts +22 -37
  295. package/src/panels/properties-panel.ts +175 -121
  296. package/src/panels/quick-search.ts +53 -31
  297. package/src/panels/right-panel.ts +3 -3
  298. package/src/panels/seo-modal.ts +13 -17
  299. package/src/panels/settings-menu.ts +15 -15
  300. package/src/panels/signals-panel.ts +153 -131
  301. package/src/panels/statement-editor.ts +230 -140
  302. package/src/panels/style-panel.ts +218 -205
  303. package/src/panels/style-utils.ts +114 -51
  304. package/src/panels/stylebook-doc.ts +57 -54
  305. package/src/panels/stylebook-layers-panel.ts +9 -9
  306. package/src/panels/stylebook-panel.ts +13 -17
  307. package/src/panels/tab-drop.ts +27 -11
  308. package/src/panels/tab-strip.ts +36 -26
  309. package/src/platform-errors.ts +57 -43
  310. package/src/platform.ts +58 -30
  311. package/src/platforms/cloud.ts +345 -175
  312. package/src/platforms/default-platform.ts +10 -4
  313. package/src/platforms/devserver.ts +129 -117
  314. package/src/platforms/read-batcher.ts +16 -8
  315. package/src/project-list.ts +2 -1
  316. package/src/publish/deploy-checklist.ts +8 -2
  317. package/src/publish/pages-service.ts +28 -13
  318. package/src/publish/publish-commands.ts +5 -4
  319. package/src/publish/publish-panel.ts +10 -6
  320. package/src/recent-projects.ts +15 -8
  321. package/src/resize-edges.ts +26 -36
  322. package/src/services/a11y-report.ts +26 -19
  323. package/src/services/agent-seed.ts +23 -11
  324. package/src/services/ai-ask.ts +9 -12
  325. package/src/services/ai-command-tools.ts +68 -62
  326. package/src/services/ai-import-tools.ts +80 -67
  327. package/src/services/ai-models.ts +17 -13
  328. package/src/services/ai-project-tools.ts +68 -32
  329. package/src/services/ai-session-store.ts +19 -10
  330. package/src/services/ai-system-prompt.ts +46 -20
  331. package/src/services/ai-tools.ts +71 -27
  332. package/src/services/ai-write-report.ts +20 -12
  333. package/src/services/ai-writes.ts +4 -4
  334. package/src/services/automation.ts +58 -31
  335. package/src/services/cem-export.ts +149 -58
  336. package/src/services/clock.ts +6 -1
  337. package/src/services/code-services.ts +37 -31
  338. package/src/services/context-manager.ts +14 -10
  339. package/src/services/context-resolver.ts +20 -8
  340. package/src/services/data-service.ts +19 -15
  341. package/src/services/directory-picker.ts +18 -12
  342. package/src/services/document-assistant.ts +39 -19
  343. package/src/services/gated-registry.ts +10 -6
  344. package/src/services/harness/chat-reducer.ts +23 -1
  345. package/src/services/import-client.ts +91 -38
  346. package/src/services/import-seed.ts +8 -2
  347. package/src/services/jx-validate.ts +28 -31
  348. package/src/services/live-preview.ts +0 -0
  349. package/src/services/live-surfaces.ts +6 -3
  350. package/src/services/model-uri.ts +10 -2
  351. package/src/services/module-preloads.ts +12 -8
  352. package/src/services/monaco-buffer.ts +43 -24
  353. package/src/services/monaco-lazy.ts +7 -8
  354. package/src/services/monaco-setup.ts +23 -16
  355. package/src/services/notify.ts +44 -17
  356. package/src/services/popover-report.ts +25 -10
  357. package/src/services/preview-eval.ts +20 -8
  358. package/src/services/profile.ts +13 -6
  359. package/src/services/project-path.ts +13 -10
  360. package/src/services/references.ts +0 -0
  361. package/src/services/render-critic.ts +8 -7
  362. package/src/services/settings/kernel.ts +13 -4
  363. package/src/services/settings/write-queue.ts +9 -9
  364. package/src/services/token-lint.ts +12 -10
  365. package/src/services/tool-executor.ts +17 -5
  366. package/src/services/tool-outcomes.ts +44 -24
  367. package/src/services/trusted-types.ts +24 -7
  368. package/src/settings/contexts-section.ts +11 -8
  369. package/src/settings/contributed-section.ts +79 -60
  370. package/src/settings/css-vars-editor.ts +37 -16
  371. package/src/settings/defs-editor.ts +38 -35
  372. package/src/settings/extension-rows.ts +16 -8
  373. package/src/settings/extension-sections.ts +23 -13
  374. package/src/settings/extensions-section.ts +4 -1
  375. package/src/settings/general-settings.ts +11 -8
  376. package/src/settings/locales-section.ts +20 -12
  377. package/src/settings/preferences-dialog.ts +9 -6
  378. package/src/settings/preferences-keymap.ts +7 -5
  379. package/src/settings/preferences-sections.ts +11 -5
  380. package/src/settings/project-sections.ts +1 -1
  381. package/src/settings/schema-field-ui.ts +9 -0
  382. package/src/settings/settings-document.ts +5 -5
  383. package/src/shell/tree.ts +6 -5
  384. package/src/shell.ts +104 -81
  385. package/src/site-context.ts +37 -22
  386. package/src/state.ts +41 -49
  387. package/src/store.ts +54 -34
  388. package/src/studio.ts +25 -75
  389. package/src/style/live-preview.ts +1 -1
  390. package/src/style/project-styles.ts +6 -7
  391. package/src/style/token-ref.ts +5 -4
  392. package/src/surfaces/about.ts +18 -19
  393. package/src/surfaces/add-repo.ts +17 -19
  394. package/src/surfaces/ai-chat.ts +31 -36
  395. package/src/surfaces/ai-credentials-form.ts +6 -16
  396. package/src/surfaces/ai-managed-connect.ts +3 -4
  397. package/src/surfaces/ai-model-picker.ts +3 -5
  398. package/src/surfaces/block-action-bar.ts +22 -11
  399. package/src/surfaces/boot-failure.ts +4 -5
  400. package/src/surfaces/bottom-dock.ts +9 -18
  401. package/src/surfaces/canvas-stage.ts +12 -11
  402. package/src/surfaces/cf-account-picker.ts +17 -18
  403. package/src/surfaces/commandbar.ts +14 -9
  404. package/src/surfaces/convert-repeater.ts +17 -19
  405. package/src/surfaces/data-actions.ts +9 -7
  406. package/src/surfaces/dialog.ts +26 -28
  407. package/src/surfaces/diff-toolbar.ts +9 -7
  408. package/src/surfaces/doc-header.ts +10 -19
  409. package/src/surfaces/empty-state.ts +7 -10
  410. package/src/surfaces/entry-editor.ts +14 -11
  411. package/src/surfaces/expression-editor.ts +11 -25
  412. package/src/surfaces/files-panel.ts +27 -14
  413. package/src/surfaces/formula-palette.ts +4 -5
  414. package/src/surfaces/git-panel.ts +7 -15
  415. package/src/surfaces/github-auth.ts +16 -20
  416. package/src/surfaces/github-publish.ts +17 -22
  417. package/src/surfaces/grid-cell.ts +30 -31
  418. package/src/surfaces/grid-open.ts +10 -14
  419. package/src/surfaces/grid-panel.ts +13 -9
  420. package/src/surfaces/grid-replace.ts +13 -13
  421. package/src/surfaces/grid-views.ts +15 -18
  422. package/src/surfaces/inspector-dock.ts +13 -12
  423. package/src/surfaces/jump-bar.ts +5 -6
  424. package/src/surfaces/jxsuite-update.ts +16 -22
  425. package/src/surfaces/library-pane.ts +19 -20
  426. package/src/surfaces/logic-panel.ts +17 -30
  427. package/src/surfaces/logic-workspace.ts +13 -15
  428. package/src/surfaces/media-browser.ts +11 -10
  429. package/src/surfaces/media-field.ts +4 -5
  430. package/src/surfaces/media-pane.ts +8 -7
  431. package/src/surfaces/menu.ts +29 -23
  432. package/src/surfaces/navigator-dock.ts +11 -12
  433. package/src/surfaces/new-project.ts +27 -26
  434. package/src/surfaces/pane-context.ts +20 -25
  435. package/src/surfaces/pane-grid.ts +15 -10
  436. package/src/surfaces/panel-activity.ts +5 -6
  437. package/src/surfaces/panel-data.ts +3 -4
  438. package/src/surfaces/panel-deploy-checklist.ts +5 -6
  439. package/src/surfaces/panel-elements.ts +5 -7
  440. package/src/surfaces/panel-i18n.ts +5 -7
  441. package/src/surfaces/panel-imports.ts +6 -13
  442. package/src/surfaces/panel-outline.ts +25 -19
  443. package/src/surfaces/panel-page.ts +11 -18
  444. package/src/surfaces/panel-problems.ts +5 -7
  445. package/src/surfaces/panel-signals.ts +20 -34
  446. package/src/surfaces/panel-stylebook-layers.ts +5 -8
  447. package/src/surfaces/preferences.ts +21 -11
  448. package/src/surfaces/progress-modal.ts +15 -9
  449. package/src/surfaces/properties-panel.json +1 -1
  450. package/src/surfaces/properties-panel.ts +20 -42
  451. package/src/surfaces/publish.ts +18 -18
  452. package/src/surfaces/push-plan.ts +7 -8
  453. package/src/surfaces/rail.ts +13 -13
  454. package/src/surfaces/reference-field.ts +11 -15
  455. package/src/surfaces/schema-builder.ts +16 -22
  456. package/src/surfaces/schema-form.ts +15 -25
  457. package/src/surfaces/secret-field.ts +9 -6
  458. package/src/surfaces/seo.ts +21 -15
  459. package/src/surfaces/settings-contexts.ts +9 -7
  460. package/src/surfaces/settings-contributed.ts +14 -10
  461. package/src/surfaces/settings-css-vars.ts +5 -12
  462. package/src/surfaces/settings-defs.json +2 -2
  463. package/src/surfaces/settings-defs.ts +17 -21
  464. package/src/surfaces/settings-deploy.ts +8 -15
  465. package/src/surfaces/settings-extensions.ts +5 -6
  466. package/src/surfaces/settings-head.ts +33 -39
  467. package/src/surfaces/settings-locales.ts +8 -20
  468. package/src/surfaces/settings-overview.ts +5 -6
  469. package/src/surfaces/settings-packages.ts +8 -7
  470. package/src/surfaces/settings-pane.ts +9 -18
  471. package/src/surfaces/settings-rawjson.ts +5 -6
  472. package/src/surfaces/shell.ts +1 -2
  473. package/src/surfaces/slash-menu.ts +19 -9
  474. package/src/surfaces/statements.ts +13 -18
  475. package/src/surfaces/statusbar.ts +17 -14
  476. package/src/surfaces/style-panel.ts +18 -31
  477. package/src/surfaces/stylebook-chrome.ts +3 -5
  478. package/src/surfaces/tab-strip.ts +14 -13
  479. package/src/surfaces/target-line.ts +6 -7
  480. package/src/surfaces/welcome.ts +5 -7
  481. package/src/tabs/patch-ops.ts +4 -3
  482. package/src/tabs/project-config.ts +32 -21
  483. package/src/tabs/selection.ts +36 -8
  484. package/src/tabs/tab.ts +52 -41
  485. package/src/tabs/transact.ts +98 -53
  486. package/src/types.ts +32 -17
  487. package/src/ui/ai-credentials-form.ts +10 -13
  488. package/src/ui/ai-model-picker.ts +3 -3
  489. package/src/ui/cf-account-picker.ts +7 -9
  490. package/src/ui/color-selector.ts +9 -5
  491. package/src/ui/dynamic-slot.ts +7 -8
  492. package/src/ui/expression-editor.ts +112 -105
  493. package/src/ui/form-controls.ts +57 -48
  494. package/src/ui/formula-catalog.ts +239 -230
  495. package/src/ui/formula-chips.ts +33 -18
  496. package/src/ui/layers.ts +44 -31
  497. package/src/ui/media-picker.ts +18 -9
  498. package/src/ui/popover-surface.ts +20 -12
  499. package/src/ui/progress-modal.ts +13 -8
  500. package/src/ui/regions.ts +7 -6
  501. package/src/ui/schema-form.ts +113 -70
  502. package/src/ui/surface.ts +71 -8
  503. package/src/ui/timing.ts +9 -0
  504. package/src/ui/value-source.ts +44 -31
  505. package/src/ui/virtual-window.ts +3 -2
  506. package/src/utils/base64.ts +3 -1
  507. package/src/utils/edit-display.ts +73 -73
  508. package/src/utils/element-selectors.ts +3 -2
  509. package/src/utils/google-fonts.ts +15 -14
  510. package/src/utils/inherited-style.ts +18 -20
  511. package/src/utils/link-target.ts +1 -1
  512. package/src/utils/preview-format.ts +25 -3
  513. package/src/utils/strip-events.ts +49 -22
  514. package/src/utils/studio-utils.ts +29 -14
  515. package/src/view.ts +2 -3
  516. package/src/workspace/pane-derive.ts +10 -12
  517. package/src/workspace/session.ts +53 -43
  518. package/src/workspace/workspace.ts +40 -36
  519. package/dist/chunks/adapter-f93pafd7.js +0 -4
  520. package/dist/chunks/cf-account-picker-af1h381v.js +0 -4
  521. package/dist/chunks/cf-settings-125tdm0w.js +0 -4
  522. package/dist/chunks/color-area-jqhna8zy.js +0 -4
  523. package/dist/chunks/color-field-embq5ctg.js +0 -4
  524. package/dist/chunks/color-fpezvnkt.js +0 -4
  525. package/dist/chunks/color-slider-1qmw7wqy.js +0 -4
  526. package/dist/chunks/combobox-773hdzyj.js +0 -4
  527. package/dist/chunks/components-ax2f1fnn.js +0 -4
  528. package/dist/chunks/content-source-108aqtk1.js +0 -4
  529. package/dist/chunks/convert-to-repeater-ry5rmppr.js +0 -4
  530. package/dist/chunks/dialog-mtm9fq5t.js +0 -4
  531. package/dist/chunks/editor-pfgdmkb0.js +0 -4
  532. package/dist/chunks/entry-commands-t0k1q0kx.js +0 -4
  533. package/dist/chunks/extension-sections-6mwh2hf8.js +0 -4
  534. package/dist/chunks/file-ops-meej5mqf.js +0 -4
  535. package/dist/chunks/files-44vp8a2s.js +0 -4
  536. package/dist/chunks/format-host-7q1petb9.js +0 -4
  537. package/dist/chunks/fs-events-h2mwnxaz.js +0 -4
  538. package/dist/chunks/git-panel-zngbymvd.js +0 -4
  539. package/dist/chunks/grid-view-sx9vynsp.js +0 -51
  540. package/dist/chunks/icons-p44xhjf0.js +0 -4
  541. package/dist/chunks/iframe-entry-f7mqjwpm.js +0 -5
  542. package/dist/chunks/iframe-entry-haw7jkvz.js +0 -5
  543. package/dist/chunks/iframe-entry-z6qrytv3.js +0 -5
  544. package/dist/chunks/iframe-host-qk3ycq87.js +0 -4
  545. package/dist/chunks/index-wkyfjeqz.js +0 -4
  546. package/dist/chunks/jx-validate-psgd7vsx.js +0 -4
  547. package/dist/chunks/layers-panel-1kq2jyfk.js +0 -4
  548. package/dist/chunks/layers-xd92fg8d.js +0 -4
  549. package/dist/chunks/listbox-s8fkmnq8.js +0 -4
  550. package/dist/chunks/lit-html-ac4cwwr4.js +0 -4
  551. package/dist/chunks/media-picker-0svzkdyb.js +0 -4
  552. package/dist/chunks/menu-37d07z9m.js +0 -4
  553. package/dist/chunks/menu-5c55ea3d.js +0 -4
  554. package/dist/chunks/monaco-binding-z6r70pdt.js +0 -4
  555. package/dist/chunks/monaco-buffer-fz6gq5y7.js +0 -4
  556. package/dist/chunks/monaco-setup-q4axkgj7.js +0 -4
  557. package/dist/chunks/notify-0xgtn8q1.js +0 -4
  558. package/dist/chunks/number-field-gw7sq8rn.js +0 -4
  559. package/dist/chunks/parse-897fvqah.js +0 -4
  560. package/dist/chunks/platform-v1qctbe6.js +0 -4
  561. package/dist/chunks/popover-8htra4er.js +0 -4
  562. package/dist/chunks/publish-panel-ym1fmgq5.js +0 -4
  563. package/dist/chunks/quick-search-a54e6t5n.js +0 -4
  564. package/dist/chunks/references-gmqp82fs.js +0 -4
  565. package/dist/chunks/right-panel-v7t9awbk.js +0 -4
  566. package/dist/chunks/section-registry-vhyf3zsr.js +0 -4
  567. package/dist/chunks/select-n3v7gwas.js +0 -4
  568. package/dist/chunks/split-pff8f3ze.js +0 -4
  569. package/dist/chunks/studio-0bhx5hz6.js +0 -5
  570. package/dist/chunks/studio-0k1ddnyy.js +0 -6
  571. package/dist/chunks/studio-1b04pd0j.js +0 -64
  572. package/dist/chunks/studio-4dwbj6gb.js +0 -5
  573. package/dist/chunks/studio-4tanh828.js +0 -5
  574. package/dist/chunks/studio-55h4mstj.js +0 -4
  575. package/dist/chunks/studio-5tnvjkz4.js +0 -5
  576. package/dist/chunks/studio-60dqgb0c.js +0 -5
  577. package/dist/chunks/studio-61dp09qg.js +0 -9
  578. package/dist/chunks/studio-63epcrj2.js +0 -5
  579. package/dist/chunks/studio-74k29dy9.js +0 -5
  580. package/dist/chunks/studio-7vg6jcd3.js +0 -5
  581. package/dist/chunks/studio-7zx31knw.js +0 -9
  582. package/dist/chunks/studio-8bxk97km.js +0 -5
  583. package/dist/chunks/studio-933e30n4.js +0 -5
  584. package/dist/chunks/studio-98wnwhbg.js +0 -5
  585. package/dist/chunks/studio-9mccjwjh.js +0 -14
  586. package/dist/chunks/studio-a2ke1e4t.js +0 -5
  587. package/dist/chunks/studio-e3a2a6v3.js +0 -4
  588. package/dist/chunks/studio-e81q1vb9.js +0 -5
  589. package/dist/chunks/studio-epgrmqqm.js +0 -5
  590. package/dist/chunks/studio-ery42sqr.js +0 -4
  591. package/dist/chunks/studio-g1shz58g.js +0 -5
  592. package/dist/chunks/studio-gf0fz03c.js +0 -5
  593. package/dist/chunks/studio-gjtpe62q.js +0 -6
  594. package/dist/chunks/studio-gkskhg7m.js +0 -5
  595. package/dist/chunks/studio-gx4dnjzd.js +0 -5
  596. package/dist/chunks/studio-j03zga3y.js +0 -9
  597. package/dist/chunks/studio-jhpxeckz.js +0 -5
  598. package/dist/chunks/studio-k01r7ykp.js +0 -5
  599. package/dist/chunks/studio-kk7h30ne.js +0 -7
  600. package/dist/chunks/studio-kt1zqney.js +0 -5
  601. package/dist/chunks/studio-m3cwhvye.js +0 -5
  602. package/dist/chunks/studio-mcayt6hz.js +0 -5
  603. package/dist/chunks/studio-myjcghv3.js +0 -5
  604. package/dist/chunks/studio-n8bxwc3y.js +0 -4
  605. package/dist/chunks/studio-ngm8gvpb.js +0 -5
  606. package/dist/chunks/studio-nj5a01jy.js +0 -928
  607. package/dist/chunks/studio-pcgz8heb.js +0 -5
  608. package/dist/chunks/studio-qcsq0t15.js +0 -5
  609. package/dist/chunks/studio-qkstvve9.js +0 -5
  610. package/dist/chunks/studio-qv1pwd3c.js +0 -5
  611. package/dist/chunks/studio-rc6dvxjb.js +0 -7
  612. package/dist/chunks/studio-s8dmmz66.js +0 -5
  613. package/dist/chunks/studio-sp5xzbj4.js +0 -6
  614. package/dist/chunks/studio-szsjbj0g.js +0 -5
  615. package/dist/chunks/studio-t1xzawhr.js +0 -16
  616. package/dist/chunks/studio-t6b6qe31.js +0 -5
  617. package/dist/chunks/studio-tdcprbqb.js +0 -5
  618. package/dist/chunks/studio-vyc0b4he.js +0 -10
  619. package/dist/chunks/studio-w0cgf6jg.js +0 -5
  620. package/dist/chunks/studio-w3hskzqk.js +0 -5
  621. package/dist/chunks/studio-xgrgcdyh.js +0 -9
  622. package/dist/chunks/studio-xz2yxfcb.js +0 -5
  623. package/dist/chunks/studio-y6n42tgs.js +0 -5
  624. package/dist/chunks/studio-yb560vxr.js +0 -5
  625. package/dist/chunks/studio-yn9f8vtx.js +0 -5
  626. package/dist/chunks/studio-yw05w8f0.js +0 -7
  627. package/dist/chunks/studio-z16sgavc.js +0 -5
  628. package/dist/chunks/studio-zyqn40w4.js +0 -5
  629. package/dist/chunks/swatch-group-8qh9a3bm.js +0 -4
  630. package/dist/chunks/tabs-jwthg40w.js +0 -4
  631. package/dist/chunks/textfield-spy3hc77.js +0 -4
  632. package/dist/chunks/toast-4d35zd64.js +0 -4
  633. package/dist/chunks/toast-host-8sjmfed3.js +0 -4
  634. package/dist/chunks/tooltip-fd6g9xy3.js +0 -4
  635. package/dist/chunks/tree-h19v1jfz.js +0 -4
  636. package/dist/chunks/workspace-zt81g1ms.js +0 -4
  637. package/dist/chunks/ws-client-n2bh4hkf.js +0 -4
@@ -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,19 +67,70 @@ import type {
66
67
  SerializableRect,
67
68
  SerializedKey,
68
69
  SyncedChord,
69
- WireDocOp,
70
70
  } from "./iframe-protocol";
71
+ import type { JxDocOp } from "../tabs/patch-ops";
71
72
  import type { IframeChannel } from "./iframe-channel";
72
73
  import type { OverlayLayer, OverlayPlacement } from "./iframe-overlay";
73
74
  import type { SlashCommand } from "../editor/inline-edit";
74
75
  import type { Tab } from "../tabs/tab";
75
- import type { JxExpressionNode, JxMutableNode } from "@jxsuite/schema/types";
76
+ import type { JxExpressionNode, JxMutableNode, JxStyle } from "@jxsuite/schema/types";
76
77
  import { bundleUrl } from "../services/bundle-base";
77
78
 
78
- /** A rect in PARENT coordinates (overlay-local from {@link canvasRectToParent}, same field shape). */
79
- type ParentRect = OverlayPlacement;
79
+ /**
80
+ * A plain-data copy of a value bound for `postMessage`.
81
+ *
82
+ * A JSON round trip rather than `structuredClone`, which throws on exactly what this exists to
83
+ * drop: reactive-proxy internals and stray functions riding on a value read off a live store.
84
+ */
85
+ function plainCopy<T>(value: T): T {
86
+ /* SAFETY: Every value posted through here is document or config data, which is JSON by contract,
87
+ so the round trip returns the same shape minus only what the realm boundary cannot carry. */
88
+ // oxlint-disable-next-line unicorn/prefer-structured-clone
89
+ return JSON.parse(JSON.stringify(value)) as T;
90
+ }
91
+
92
+ /** A deferred re-entry into inline editing, waiting for the host's DOM to reflect a split/insert. */
93
+ interface PendingEnterEdit {
94
+ path: (string | number)[];
95
+ minGen: number;
96
+ offset?: number;
97
+ /** Set when the deferred re-entry is a PROP host rather than a block (see deferEnterEdit). */
98
+ prop?: string;
99
+ }
100
+
101
+ /** The `enterEdit` message a host posts to re-enter inline editing. */
102
+ type EnterEditMsg = Extract<ParentToIframe, { kind: "enterEdit" }>;
103
+
104
+ /** A full-render message, as one host receives it. */
105
+ type RenderMsg = Extract<ParentToIframe, { kind: "render" }>;
106
+
107
+ /**
108
+ * The source data a drag session retains for its drop, exactly what {@link applyDropInstruction}
109
+ * consumes.
110
+ */
111
+ type RetainedDragData = Parameters<typeof applyDropInstruction>[2];
112
+
113
+ /** The empirical zoom of a host's iframe and the iframe's parent-viewport offset. */
114
+ export interface DragGeometry {
115
+ scale: number;
116
+ rect: { left: number; top: number };
117
+ }
118
+
119
+ /** The current edit session's editing flag + latest selection snapshot, for the parent toolbar. */
120
+ export interface EditSnapshotState {
121
+ editing: boolean;
122
+ editingProp: string | null;
123
+ snapshot: SelectionSnapshot | null;
124
+ }
80
125
 
81
- interface HostState {
126
+ /**
127
+ * One artboard's iframe, overlay, selection and edit session.
128
+ *
129
+ * Exported for the drag coordinator, which holds one as an opaque handle and passes it back to the
130
+ * session API ({@link beginDragSession}/{@link hostDragGeometry}/{@link postDragMessage}) without
131
+ * reading its fields.
132
+ */
133
+ export interface HostState {
82
134
  iframe: HTMLIFrameElement;
83
135
  /** The `.canvas-panel-canvas` element hosting the iframe + overlay (the WeakMap key). */
84
136
  canvasEl: HTMLElement;
@@ -101,7 +153,7 @@ interface HostState {
101
153
  /** Primary selected path (mirrors `session.selection`'s last entry), for hover de-dupe. */
102
154
  selectionPath: (string | number)[] | null;
103
155
  /** The whole selection this host last measured, so a re-measure redraws every box. */
104
- selectionPaths: JxPath[];
156
+ selectionPaths: readonly JxPath[];
105
157
  /** Id of the most recent selection `measure` request, so stale `geometry` replies are dropped. */
106
158
  selReqId: number;
107
159
  /**
@@ -157,7 +209,7 @@ interface HostState {
157
209
  /** Highest snapshot `seq` seen, so stale (re-ordered) snapshots are dropped. */
158
210
  lastSnapshotSeq: number;
159
211
  /** The last non-null selection rect drawn (parent/overlay coords) — toolbar position fallback. */
160
- lastSelectionRect: ParentRect | null;
212
+ lastSelectionRect: OverlayPlacement | null;
161
213
  /** The node the pointer last hovered (iframe-viewport rect), so a zoom can hold it still. */
162
214
  lastHover: NodeHit | null;
163
215
  /**
@@ -228,13 +280,7 @@ interface HostState {
228
280
  * render acks (`renderComplete`) at a bumped one — both satisfy `gen >= minGen`. An immediate
229
281
  * `enterEdit` would race the escalated ASYNC render and silently fail to find the element.
230
282
  */
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;
283
+ pendingEnterEdit: PendingEnterEdit | null;
238
284
  /**
239
285
  * Stylebook capability (set by {@link mountStylebookCanvas}, cleared by page mounts). The specimen
240
286
  * doc's paths decode to TAGS, not tab-document paths: hits route to the injected stylebook
@@ -250,13 +296,6 @@ interface HostState {
250
296
  panReqId: number;
251
297
  }
252
298
 
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
299
  const hosts = new WeakMap<HTMLElement, HostState>();
261
300
 
262
301
  /** Every live host, so the selection watcher can re-measure each one when selection changes. */
@@ -271,7 +310,7 @@ const liveHosts = new Set<HostState>();
271
310
  let currentDragSeq = 0;
272
311
 
273
312
  /** Parent-retained source data keyed by `dragSeq` (the block fragment never crosses the boundary). */
274
- const retainedSrcData = new Map<number, Record<string, unknown>>();
313
+ const retainedSrcData = new Map<number, RetainedDragData>();
275
314
 
276
315
  /**
277
316
  * The last session whose cursor-carrying `dragOver` arrived FROM an iframe, and when. While a
@@ -421,9 +460,10 @@ export function requestCanvasEval(
421
460
  }
422
461
  evalReqId += 1;
423
462
  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 }[];
463
+ /* Expression nodes come off the reactive document — only plain values may cross postMessage.
464
+ SAFETY: A request's nodes are expression nodes by the caller's contract, and the frame re-checks
465
+ `operator` before evaluating one (`iframe-eval.ts`), reporting anything else per expression. */
466
+ const wireExprs = plainCopy(exprs) as { id: string; node: JxExpressionNode }[];
427
467
  target.channel.post({
428
468
  contextPath: contextPath ? [...contextPath] : null,
429
469
  exprs: wireExprs,
@@ -541,7 +581,7 @@ export function liveDragHostAt(cursor: { x: number; y: number }): HostState | nu
541
581
  export function beginDragSession(
542
582
  host: HostState,
543
583
  src: DragSrcKind,
544
- srcData: Record<string, unknown>,
584
+ srcData: RetainedDragData,
545
585
  ): number {
546
586
  currentDragSeq += 1;
547
587
  retainedSrcData.set(currentDragSeq, srcData);
@@ -569,7 +609,7 @@ export function beginDragSession(
569
609
  export function adoptDragSession(
570
610
  _host: HostState,
571
611
  _src: DragSrcKind,
572
- srcData: Record<string, unknown>,
612
+ srcData: RetainedDragData,
573
613
  seq: number,
574
614
  ): number {
575
615
  currentDragSeq = seq;
@@ -587,10 +627,7 @@ export function currentDragSession(): number {
587
627
  * read FRESH so a pan/zoom mid-drag is reflected. NOT `effectiveZoom()` (a separate path that can
588
628
  * desync). The matching iframe parent-viewport rect is returned so the cursor map cancels the pan.
589
629
  */
590
- export function hostDragGeometry(host: HostState): {
591
- scale: number;
592
- rect: { left: number; top: number };
593
- } {
630
+ export function hostDragGeometry(host: HostState): DragGeometry {
594
631
  const rect = rectOf(host.iframe);
595
632
  const scale = host.iframe.clientWidth > 0 ? rect.width / host.iframe.clientWidth : 1;
596
633
  return { rect, scale };
@@ -1317,7 +1354,7 @@ export function postDialogOpen(tab: { id: string }, path: JxPath | null): void {
1317
1354
  * `host` is deliberately not a pane-scoped parameter name (see `PANE_PARAM_NAMES`), and the body
1318
1355
  * reads no focus: `panelHostingCanvas` resolves the stage from the host's own element.
1319
1356
  */
1320
- export function postPatchToHosts(forwardOps: WireDocOp[], tabId: string | null): number {
1357
+ export function postPatchToHosts(forwardOps: JxDocOp[], tabId: string | null): number {
1321
1358
  let posted = 0;
1322
1359
  for (const host of liveHosts) {
1323
1360
  if (!host.iframe.isConnected) {
@@ -1457,14 +1494,14 @@ function hostTab(state: HostState): Tab | null {
1457
1494
  }
1458
1495
 
1459
1496
  /**
1460
- * The tab a drag TARGET is showing — the one public reader on the opaque {@link DragHost} handle.
1497
+ * The tab a drag TARGET is showing — the one public reader on the opaque {@link HostState} handle.
1461
1498
  *
1462
1499
  * `panels/canvas-dnd-bridge.ts` binds a session to the host under the cursor and then had nothing
1463
1500
  * to ask it, so the one fact it needed about the drag — what the dragged node is CALLED — came from
1464
1501
  * `activeTab`. The bridge is not supposed to read the handle's fields (that is what "opaque"
1465
1502
  * means), so the answer is a function rather than a widened type.
1466
1503
  */
1467
- export function dragHostTab(host: DragHost): Tab | null {
1504
+ export function dragHostTab(host: HostState): Tab | null {
1468
1505
  return hostTab(host);
1469
1506
  }
1470
1507
 
@@ -1499,7 +1536,7 @@ function shownSelections(): Map<string, readonly JxPath[]> {
1499
1536
  if (!tab) {
1500
1537
  continue;
1501
1538
  }
1502
- const sel = (tab.session.selection ?? []) as readonly JxPath[];
1539
+ const sel: readonly JxPath[] = tab.session.selection;
1503
1540
  // Track the selection deeply enough that a change WITHIN the set re-measures: the effect
1504
1541
  // Reads every path, not just the array reference.
1505
1542
  void sel.map((path) => path.join("/")).join("|");
@@ -1698,7 +1735,7 @@ function requestSelection(host: HostState, sel: readonly JxPath[]): void {
1698
1735
  }
1699
1736
  const primary = primarySelection(sel);
1700
1737
  host.selectionPath = primary;
1701
- host.selectionPaths = sel as JxPath[];
1738
+ host.selectionPaths = sel;
1702
1739
  if (!host.iframe.isConnected) {
1703
1740
  liveHosts.delete(host);
1704
1741
  return;
@@ -2017,7 +2054,7 @@ function hideInsertZoneNow(state: HostState): void {
2017
2054
  */
2018
2055
  function insertButtonHovered(state: HostState): boolean {
2019
2056
  const btn = state.overlay.insertButton;
2020
- return state.insertHover || (typeof btn.matches === "function" && btn.matches(":hover"));
2057
+ return state.insertHover || btn.matches(":hover");
2021
2058
  }
2022
2059
 
2023
2060
  /**
@@ -2173,7 +2210,8 @@ function handleMessage(state: HostState, msg: IframeToParent): void {
2173
2210
  behind that message with it. The null check is the same guard for a `targetPath` an edit
2174
2211
  has since invalidated, which threw for the same reason. */
2175
2212
  const target = getNodeAtPath(tab.doc.document, msg.targetPath);
2176
- if (!target || typeof target !== "object") {
2213
+ // A path can resolve to a string child or to nothing at all, whatever the declared type says.
2214
+ if (!isNodeObject(target)) {
2177
2215
  return;
2178
2216
  }
2179
2217
  if (POPOVER_COMMANDS.has(msg.command)) {
@@ -2370,7 +2408,7 @@ function handleMessage(state: HostState, msg: IframeToParent): void {
2370
2408
  // The iframe could not resolve is simply absent from `hits`.
2371
2409
  const primaryKey = JSON.stringify(state.selectionPath);
2372
2410
  const co: OverlayPlacement[] = [];
2373
- let rect: ParentRect | null = null;
2411
+ let rect: OverlayPlacement | null = null;
2374
2412
  if (state.stylebook) {
2375
2413
  const [hit] = msg.hits;
2376
2414
  rect = hit ? canvasRectToParent(hit.rect) : null;
@@ -2762,9 +2800,8 @@ function handleMessage(state: HostState, msg: IframeToParent): void {
2762
2800
  const { rect: ifr, scale } = hostDragGeometry(state);
2763
2801
  const left = msg.rect.x * scale + ifr.left;
2764
2802
  const top = msg.rect.y * scale + ifr.top;
2765
- canvasSlashHandler?.show({
2803
+ const request: CanvasSlashRequest = {
2766
2804
  filter: msg.filter,
2767
- ...(msg.showFilter === true ? { showFilter: true } : {}),
2768
2805
  onDismiss: () => state.channel.post({ kind: "slashDismissed" }),
2769
2806
  onSelect: (cmd) => state.channel.post({ cmd: { ...cmd }, kind: "slashSelect" }),
2770
2807
  rect: {
@@ -2774,7 +2811,11 @@ function handleMessage(state: HostState, msg: IframeToParent): void {
2774
2811
  top,
2775
2812
  width: msg.rect.width * scale,
2776
2813
  },
2777
- });
2814
+ };
2815
+ if (msg.showFilter === true) {
2816
+ request.showFilter = true;
2817
+ }
2818
+ canvasSlashHandler?.show(request);
2778
2819
  return;
2779
2820
  }
2780
2821
  case "slashNav": {
@@ -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
  /**
@@ -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,18 +3229,12 @@ 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",
3197
3240
  // Read at POST time for the same reason `colorScheme` is: a render replaces the DOM, so a panel
@@ -3199,6 +3242,15 @@ export async function mountIframeCanvas(
3199
3242
  popoverOpen: viewTab?.session.ui.openPopover ?? null,
3200
3243
  dialogOpen: viewTab?.session.ui.openDialog ?? null,
3201
3244
  };
3245
+ /* Diff marks are per-ARTBOARD, and post-time for a sharper version of the colour scheme's
3246
+ reason. `preparePassRender` memoizes on the document and hands the SAME payload object to every
3247
+ host in the pass, and a git-diff pass has two hosts holding two documents — but marks are a
3248
+ function of BOTH sides, not of the document this host draws, so they are not derivable from
3249
+ that cache key at all. Baking them in would make the two artboards race for whose marks the
3250
+ pass kept. */
3251
+ if (diffMarks) {
3252
+ message.diffMarks = diffMarks;
3253
+ }
3202
3254
  state.popoverOpen = message.popoverOpen ?? null;
3203
3255
  state.dialogOpen = message.dialogOpen ?? null;
3204
3256
  // Preview is the fidelity view: no editing messages are honoured from it, no overlay is painted
@@ -3250,10 +3302,8 @@ export function mountStylebookCanvas(
3250
3302
  state.iframe.style.width = widthPx ? `${widthPx}px` : "100%";
3251
3303
  // Two independent plain clones: the iframe renders `doc` and folds styleUpdates into `shadowDoc`
3252
3304
  // (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;
3305
+ const cloneableDoc = plainCopy(generated.doc);
3306
+ const cloneableShadow = plainCopy(generated.doc);
3257
3307
  const message: ParentToIframe = {
3258
3308
  // The specimen carries no tab identity, but the STAGE it is mounted into does: a stylebook is
3259
3309
  // Opened as a tab like anything else, and its `previewColorScheme` is that tab's.
@@ -3371,8 +3421,7 @@ export function postLocaleToLiveHosts(locale: string | null, root?: HTMLElement
3371
3421
  * off a tab's reactive record is a proxy, and a proxy cannot be structured-cloned.
3372
3422
  */
3373
3423
  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;
3424
+ const plain = plainCopy(doc);
3376
3425
  let posted = 0;
3377
3426
  for (const host of liveHosts) {
3378
3427
  if (!host.iframe.isConnected) {
@@ -3398,13 +3447,8 @@ export function postSiteStyleToLiveHosts(): void {
3398
3447
  return;
3399
3448
  }
3400
3449
  // 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>;
3450
+ const siteStyle = plainCopy(config.style ?? null);
3451
+ const media = plainCopy(config.$media ?? {});
3408
3452
  for (const host of liveHosts) {
3409
3453
  if (!host.iframe.isConnected) {
3410
3454
  liveHosts.delete(host);
@@ -3422,11 +3466,10 @@ export function postSiteStyleToLiveHosts(): void {
3422
3466
  * received it — zero means no stylebook iframe is live yet and the caller should fall through to a
3423
3467
  * full render. Each post is followed by a selection re-measure so the box tracks the reflow.
3424
3468
  */
3425
- export function postStyleUpdateToStylebookHosts(style: Record<string, unknown>): number {
3469
+ export function postStyleUpdateToStylebookHosts(style: JxStyle): number {
3426
3470
  let posted = 0;
3427
3471
  // 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>;
3472
+ const cloneable = plainCopy(style);
3430
3473
  for (const host of liveHosts) {
3431
3474
  if (!host.iframe.isConnected) {
3432
3475
  liveHosts.delete(host);
@@ -3462,12 +3505,8 @@ export function panToStylebookTag(tag: string): void {
3462
3505
 
3463
3506
  // ─── Format-toolbar bridge (Phase 4b-2) ─────────────────────────────────────────
3464
3507
 
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
- } {
3508
+ /** The current edit session's state, for the parent toolbar. */
3509
+ export function getEditSnapshot(): EditSnapshotState {
3471
3510
  /* The same connectivity guard {@link isCaretActive} has, and for the same reason: a session
3472
3511
  belongs to a frame, and a frame that has left the document has no caret in it. `releaseHost`
3473
3512
  clears `activeEditHost`, so this is the second line rather than the first — it covers the frame
@@ -3578,7 +3617,7 @@ export function commitActiveEditSession(): void {
3578
3617
  /** The live host backing the active panel's canvas (for non-edit selection-bar positioning). */
3579
3618
  function hostForActivePanel(): HostState | null {
3580
3619
  const panel = getActivePanel();
3581
- return panel ? hostForCanvas(panel.canvas as HTMLElement) : null;
3620
+ return panel ? hostForCanvas(panel.canvas) : null;
3582
3621
  }
3583
3622
 
3584
3623
  /**
@@ -3592,7 +3631,7 @@ function hostForActivePanel(): HostState | null {
3592
3631
  * (where it evaluates to exactly `editZoom` — the iframe's layout width is `renderWidth / editZoom`
3593
3632
  * while its rendered width is `renderWidth`).
3594
3633
  */
3595
- export function getEditBarAnchorRect(): ParentRect | null {
3634
+ export function getEditBarAnchorRect(): OverlayPlacement | null {
3596
3635
  // The bar rests on the SELECTED ELEMENT's box, edit session or not. It used to prefer the caret's
3597
3636
  // Snapshot rect while editing, so it hopped line to line with the caret and sat over the text the
3598
3637
  // Author was typing; the snapshot still feeds the format state, never the position. Only an edit
@@ -51,11 +51,21 @@ import type {
51
51
  SerializableRect,
52
52
  } from "./iframe-protocol";
53
53
  import type { DocPos } from "./iframe-position";
54
+ import { isNonNullObject, isString } from "@jxsuite/schema/guards";
54
55
  import type { JxPath } from "../state";
55
56
  import type { JxMutableNode } from "@jxsuite/schema/types";
56
57
 
58
+ /** The commit a block session posts when its text reaches the document. */
59
+ type EditCommitMsg = Extract<IframeToParent, { kind: "editCommit" }>;
60
+
61
+ /** The commit a prop-bound session posts for its instance's `$props` value. */
62
+ type EditCommitPropMsg = Extract<IframeToParent, { kind: "editCommitProp" }>;
63
+
64
+ /** The toolbar commands that toggle an inline tag (every intent but `link` and `insertData`). */
65
+ type InlineFormatCommand = Exclude<ApplyFormatIntent["command"], "link" | "insertData">;
66
+
57
67
  /** Toolbar command → inline tag (mirrors the parent's old `cmdToTag`, now iframe-side). */
58
- const CMD_TO_TAG: Record<string, string> = {
68
+ const CMD_TO_TAG = {
59
69
  bold: "strong",
60
70
  code: "code",
61
71
  italic: "em",
@@ -63,7 +73,19 @@ const CMD_TO_TAG: Record<string, string> = {
63
73
  subscript: "sub",
64
74
  superscript: "sup",
65
75
  underline: "u",
66
- };
76
+ } satisfies Record<InlineFormatCommand, string>;
77
+
78
+ /**
79
+ * The CSS Custom Highlight API as this realm may or may not provide it. lib.dom declares both
80
+ * members unconditionally, but happy-dom and non-Chromium engines ship neither.
81
+ */
82
+ interface HighlightRealm {
83
+ Highlight?: typeof Highlight;
84
+ CSS?: { highlights?: HighlightRegistry };
85
+ }
86
+
87
+ /** The global object, read through the optional shape above so the feature probes stay honest. */
88
+ const highlightRealm: HighlightRealm = globalThis;
67
89
 
68
90
  /** Inline tags whose active state the snapshot reports (everything except `a`, handled via link). */
69
91
  const ACTIVE_TAG_PROBES = ["strong", "em", "u", "del", "sub", "sup", "code"];
@@ -125,19 +147,19 @@ function deliveredOutsideProps(node: JxMutableNode | undefined, prop: string): b
125
147
  if (!node) {
126
148
  return false;
127
149
  }
128
- const attrs = node.attributes as Record<string, unknown> | undefined;
150
+ const attrs = node.attributes;
129
151
  if (attrs) {
130
152
  if (attrs[`props.${prop}`] !== undefined) {
131
153
  return true;
132
154
  }
133
155
  const reflected = new Set([prop.toLowerCase(), camelToKebab(prop).toLowerCase()]);
134
- for (const [name, val] of Object.entries(attrs)) {
135
- if (val !== undefined && reflected.has(name.toLowerCase())) {
156
+ for (const name of Object.keys(attrs)) {
157
+ if (attrs[name] !== undefined && reflected.has(name.toLowerCase())) {
136
158
  return true;
137
159
  }
138
160
  }
139
161
  const payload = attrs["data-jx-props"];
140
- if (typeof payload === "string" && payloadNames(payload, prop)) {
162
+ if (isString(payload) && payloadNames(payload, prop)) {
141
163
  return true;
142
164
  }
143
165
  }
@@ -145,14 +167,14 @@ function deliveredOutsideProps(node: JxMutableNode | undefined, prop: string): b
145
167
  if (RESERVED_KEYS.has(prop) || prop.startsWith("$")) {
146
168
  return false;
147
169
  }
148
- return (node as Record<string, unknown>)[prop] !== undefined;
170
+ return node[prop] !== undefined;
149
171
  }
150
172
 
151
173
  /** Whether a `data-jx-props` JSON payload carries `prop`. Unparseable payloads deliver nothing. */
152
174
  function payloadNames(payload: string, prop: string): boolean {
153
175
  try {
154
176
  const parsed: unknown = JSON.parse(payload);
155
- return typeof parsed === "object" && parsed !== null && prop in parsed;
177
+ return isNonNullObject(parsed) && prop in parsed;
156
178
  } catch {
157
179
  return false;
158
180
  }
@@ -238,11 +260,8 @@ export function startIframeInlineEdit(
238
260
  * `Highlight`/`CSS.highlights`, so this is a silent no-op there (and in non-Chromium engines).
239
261
  */
240
262
  const updateHighlight = () => {
241
- const HighlightCtor = (globalThis as { Highlight?: unknown }).Highlight as
242
- | (new (range: Range) => unknown)
243
- | undefined;
244
- const highlights = (globalThis.CSS as unknown as { highlights?: Map<string, unknown> })
245
- ?.highlights;
263
+ const HighlightCtor = highlightRealm.Highlight;
264
+ const highlights = highlightRealm.CSS?.highlights;
246
265
  if (!HighlightCtor || !highlights) {
247
266
  return;
248
267
  }
@@ -256,9 +275,7 @@ export function startIframeInlineEdit(
256
275
 
257
276
  /** Tear down the Custom Highlight (on teardown / real editEnd). */
258
277
  const clearHighlight = () => {
259
- const highlights = (globalThis.CSS as unknown as { highlights?: Map<string, unknown> })
260
- ?.highlights;
261
- highlights?.delete(HIGHLIGHT_NAME);
278
+ highlightRealm.CSS?.highlights?.delete(HIGHLIGHT_NAME);
262
279
  };
263
280
 
264
281
  /**
@@ -321,13 +338,11 @@ export function startIframeInlineEdit(
321
338
  // Optional, so omitting it keeps the message shape unchanged for every non-tick commit.
322
339
  onCommit: (p, children, textContent, inPlace) => {
323
340
  inputAnnounced = false;
324
- channel.post({
325
- children,
326
- kind: "editCommit",
327
- path: p,
328
- textContent,
329
- ...(inPlace ? { inPlace: true } : {}),
330
- });
341
+ const commit: EditCommitMsg = { children, kind: "editCommit", path: p, textContent };
342
+ if (inPlace) {
343
+ commit.inPlace = true;
344
+ }
345
+ channel.post(commit);
331
346
  },
332
347
  onEnd: () => {
333
348
  clearHighlight();
@@ -356,11 +371,11 @@ export function startIframeInlineEdit(
356
371
  if (!shadowDoc) {
357
372
  return true;
358
373
  }
359
- const node = getNodeAtPath(shadowDoc, hostPath) as JxMutableNode | undefined;
374
+ const node = getNodeAtPath(shadowDoc, hostPath);
360
375
  if (deliveredOutsideProps(node, prop)) {
361
376
  return false;
362
377
  }
363
- const raw = (node?.$props as Record<string, unknown> | undefined)?.[prop];
378
+ const raw = node?.$props?.[prop];
364
379
  if (raw == null) {
365
380
  return true; // Unset — editing ADDS the prop, overriding the definition default.
366
381
  }
@@ -371,7 +386,7 @@ export function startIframeInlineEdit(
371
386
  * Objects were already excluded; the scalar cases were not, and are the ones a component
372
387
  * actually declares. They stay editable in the properties panel, which knows their type.
373
388
  */
374
- if (typeof raw !== "string") {
389
+ if (!isString(raw)) {
375
390
  return false;
376
391
  }
377
392
  return !raw.includes("${");
@@ -390,13 +405,16 @@ export function startIframeInlineEdit(
390
405
  {
391
406
  onCommit: (p, _children, textContent, inPlace) => {
392
407
  inputAnnounced = false;
393
- channel.post({
408
+ const commit: EditCommitPropMsg = {
394
409
  kind: "editCommitProp",
395
410
  path: p,
396
411
  prop,
397
412
  value: textContent ?? "",
398
- ...(inPlace ? { inPlace: true } : {}),
399
- });
413
+ };
414
+ if (inPlace) {
415
+ commit.inPlace = true;
416
+ }
417
+ channel.post(commit);
400
418
  },
401
419
  onEnd: () => {
402
420
  clearHighlight();
@@ -519,6 +537,7 @@ export function startIframeInlineEdit(
519
537
  if (start === -1 || end === -1 || end <= start) {
520
538
  return false;
521
539
  }
540
+ // SAFETY: `blocksInOrder` selects `[data-jx-path]`, so every block it returns carries the key.
522
541
  const between = blocks
523
542
  .slice(start + 1, end)
524
543
  .map((el) => parseJxPath(el.dataset.jxPath as string));
@@ -573,7 +592,7 @@ export function startIframeInlineEdit(
573
592
  owns what a slash MEANS (start of a block or after a space; anywhere else it is punctuation).
574
593
  The matching `input` half re-filters the open menu, for the same reason: a contenteditable's
575
594
  `input` targets its host, not the block the caret happens to be in. */
576
- const onSlashKey = (e: Event) => handleSlashTrigger(e as KeyboardEvent);
595
+ const onSlashKey = (e: KeyboardEvent) => handleSlashTrigger(e);
577
596
  const onSlashInput = () => {
578
597
  if (isSlashActive()) {
579
598
  refreshSlashMenu();