@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
@@ -25,6 +25,7 @@ import {
25
25
  setCanvasAssetResolver,
26
26
  setSkipServerFunctions,
27
27
  } from "@jxsuite/runtime";
28
+ import { isFunction } from "@jxsuite/schema/guards";
28
29
  import { assetContextFor, hostAssetDeclarations, resolveAssetRef } from "../canvas/asset-refs";
29
30
  import { assetVersionsSnapshot } from "../files/asset-versions";
30
31
  import { componentRegistry } from "../files/components";
@@ -74,12 +75,13 @@ export function createPreviewCache(limit: number = PREVIEW_CACHE_LIMIT): Preview
74
75
  const entries = new Map<string, HTMLElement>();
75
76
 
76
77
  function evict() {
77
- // `size > limit` guarantees a first key, so the iterator result is never `done` here.
78
- while (entries.size > limit) {
79
- const oldest = entries.keys().next().value as string;
80
- const stale = entries.get(oldest);
78
+ // Insertion order is LRU order, and deleting the entry being visited is safe mid-iteration.
79
+ for (const [oldest, stale] of entries) {
80
+ if (entries.size <= limit) {
81
+ break;
82
+ }
81
83
  entries.delete(oldest);
82
- stale?.remove();
84
+ stale.remove();
83
85
  }
84
86
  }
85
87
 
@@ -127,8 +129,13 @@ export async function renderDocPreview(filePath: string): Promise<HTMLElement |
127
129
  let document_: JxDocument;
128
130
  if (formatForPath(filePath)) {
129
131
  const parsed = await parseSourceForPath(filePath, content);
132
+ /* SAFETY: a format class's parser returns its file as a Jx element tree (the format contract),
133
+ and the split hands that same tree back as `document`; a tree renderNode cannot draw throws
134
+ into the catch below, which is "no preview". */
130
135
  document_ = parsed.document as JxDocument;
131
136
  } else {
137
+ /* SAFETY: a `.json` file in the Library is a project document by the project layout; malformed
138
+ JSON or a shape renderNode cannot draw throws into the catch below, which is "no preview". */
132
139
  document_ = JSON.parse(content) as JxDocument;
133
140
  }
134
141
  const scope = await buildScope(document_, {}, location.href);
@@ -166,9 +173,7 @@ export async function previewFor(
166
173
  let rendered: HTMLElement | null | undefined;
167
174
  try {
168
175
  const component = componentRegistry.find((c: ComponentEntry) => c.path === path);
169
- rendered = component
170
- ? ((await renderComponentPreview(component)) as HTMLElement | undefined)
171
- : await renderDocPreview(path);
176
+ rendered = component ? await renderComponentPreview(component) : await renderDocPreview(path);
172
177
  } catch {
173
178
  return undefined;
174
179
  }
@@ -219,7 +224,7 @@ export interface PreviewObserver {
219
224
  * retained on that path, so there is nothing for the sweep to find.
220
225
  */
221
226
  export function createPreviewObserver(onVisible: (element: Element) => void): PreviewObserver {
222
- if (typeof IntersectionObserver !== "function") {
227
+ if (!isFunction(globalThis.IntersectionObserver)) {
223
228
  return {
224
229
  destroy() {
225
230
  /* Nothing to disconnect. */
@@ -93,7 +93,7 @@ export function createLibrarySource(dirs?: () => readonly string[]): LibrarySour
93
93
  let scan: LibraryScan | null = null;
94
94
  let inFlight: Promise<LibraryScan> | null = null;
95
95
 
96
- const projectDirs = dirs ?? (() => (projectState?.projectDirs ?? []) as readonly string[]);
96
+ const projectDirs = dirs ?? (() => projectState?.projectDirs ?? []);
97
97
 
98
98
  async function ensureScan(): Promise<LibraryScan> {
99
99
  if (scan) {
@@ -53,6 +53,9 @@ export function assetContextFor(
53
53
  const space = over?.space ?? "site";
54
54
  const mount = contentMountFor(
55
55
  documentPath,
56
+ /* SAFETY: `content` is the parser extension's opaque project section, which its project
57
+ fragment schema defines as a map of section entries; contentMountFor reads only each entry's
58
+ optional `source` and skips an entry without one. */
56
59
  projectState?.projectConfig?.content as Record<string, ContentSectionEntry> | undefined,
57
60
  projectLocales(),
58
61
  );
@@ -61,13 +64,16 @@ export function assetContextFor(
61
64
  if (space === "site" && !mount) {
62
65
  return null;
63
66
  }
64
- return {
67
+ const context: AssetContext = {
65
68
  documentDir: dirOfPath(documentPath ?? ""),
66
- ...(over?.fileBaseUrl === undefined ? {} : { fileBaseUrl: over.fileBaseUrl }),
67
69
  lanes: BUILD_LANES,
68
70
  mounts: mount ? [mount] : [],
69
71
  space,
70
72
  };
73
+ if (over?.fileBaseUrl !== undefined) {
74
+ context.fileBaseUrl = over.fileBaseUrl;
75
+ }
76
+ return context;
71
77
  }
72
78
 
73
79
  /**
@@ -99,6 +105,15 @@ export function previewAssetSrc(value: string): string {
99
105
  return loopbackAssetSrc(resolveAssetRef(value, versioned) ?? value);
100
106
  }
101
107
 
108
+ /**
109
+ * The registered host's media declarations: its asset space, and the base project files serve
110
+ * under.
111
+ */
112
+ export interface HostAssetDeclarations {
113
+ space?: "site" | "repo" | undefined;
114
+ fileBaseUrl: string;
115
+ }
116
+
102
117
  /**
103
118
  * What the registered host says about media — the platform's `assetSpace`, and the base project
104
119
  * files are served under.
@@ -108,14 +123,14 @@ export function previewAssetSrc(value: string): string {
108
123
  * The canvas pipeline builds the same two values from the same two places; the difference is only
109
124
  * that it already has the document in hand.
110
125
  *
111
- * @returns {{ space?: "site" | "repo"; fileBaseUrl: string }} The host's declarations
126
+ * @returns {HostAssetDeclarations} The host's declarations
112
127
  */
113
- export function hostAssetDeclarations(): {
114
- space?: "site" | "repo" | undefined;
115
- fileBaseUrl: string;
116
- } {
117
- return {
128
+ export function hostAssetDeclarations(): HostAssetDeclarations {
129
+ const declarations: HostAssetDeclarations = {
118
130
  fileBaseUrl: documentBase(projectState?.projectRoot),
119
- ...(hasPlatform() ? { space: getPlatform().assetSpace } : {}),
120
131
  };
132
+ if (hasPlatform()) {
133
+ declarations.space = getPlatform().assetSpace;
134
+ }
135
+ return declarations;
121
136
  }
@@ -45,29 +45,24 @@
45
45
 
46
46
  import {
47
47
  assetUrlFor,
48
+ CONTENT_SECTION_KEY,
48
49
  encodeProjectPath,
49
50
  isNonRelativeRef,
50
51
  joinProjectPath,
52
+ LOCALE_PLACEHOLDER,
51
53
  projectPathForRef,
54
+ SAFE_CONTENT_TYPE_NAME,
52
55
  splitRefSuffix,
53
56
  } from "@jxsuite/schema/asset-paths";
54
57
  import type { AssetLane, AssetMount } from "@jxsuite/schema/asset-paths";
58
+ import { isString } from "@jxsuite/schema/guards";
55
59
  import type { ContentSectionEntry } from "../types";
56
60
 
57
- /** Content type names safe in a URL path segment. Mirrors `SAFE_TYPE_NAME` in the content loader. */
58
- const SAFE_TYPE_NAME = /^[\w.~-]+$/;
59
-
60
- /** The project.json section key content types live under; also their URL namespace. */
61
- const SECTION_KEY = "content";
62
-
63
- /**
64
- * The placeholder a content type's `source` may carry to say "one directory per locale".
65
- *
66
- * Held here rather than imported out of the parser extension's content loader ({@link
67
- * file://../../../../extensions/parser/src/content-loader.ts}): that module reads the filesystem
68
- * and this one runs in a browser. It is also an extension, and core may not import one.
69
- */
70
- const LOCALE_PLACEHOLDER = "{locale}";
61
+ /* The content namespace, the URL-safe name test and the `{locale}` token come from
62
+ `@jxsuite/schema/asset-paths`, the one module both this browser-side resolver and the parser
63
+ extension's content loader may import: the loader reads the filesystem, and it is an extension,
64
+ which core may not import. Re-exported so Studio's content modules keep one local import. */
65
+ export { LOCALE_PLACEHOLDER } from "@jxsuite/schema/asset-paths";
71
66
 
72
67
  /**
73
68
  * Everything the canvas needs to resolve one reference, as PLAIN DATA.
@@ -127,7 +122,7 @@ export interface AssetContext {
127
122
  * @returns {string} The versioned URL
128
123
  */
129
124
  export function withVersion(url: string, version: string | undefined): string {
130
- if (typeof version !== "string" || version === "") {
125
+ if (!isString(version) || version === "") {
131
126
  return url;
132
127
  }
133
128
  const hashAt = url.indexOf("#");
@@ -172,7 +167,7 @@ function ownVersion(
172
167
  return undefined;
173
168
  }
174
169
  const version: unknown = versions[path];
175
- return typeof version === "string" ? version : undefined;
170
+ return isString(version) ? version : undefined;
176
171
  }
177
172
 
178
173
  /**
@@ -184,13 +179,14 @@ function ownVersion(
184
179
  * file tree cannot reach in the first place.
185
180
  *
186
181
  * @param {string | null | undefined} documentPath - Project-relative path of the open document
187
- * @param {Record<string, ContentSectionEntry> | null | undefined} content - Project.json `content`
182
+ * @param {Record<string, ContentSectionEntry | null | undefined> | null | undefined} content -
183
+ * Project.json `content`
188
184
  * @param {readonly string[]} [locales] - Canonical locale tags, for `{locale}` sources
189
185
  * @returns {AssetMount | null} The mount, project-relative
190
186
  */
191
187
  export function contentMountFor(
192
188
  documentPath: string | null | undefined,
193
- content: Record<string, ContentSectionEntry> | null | undefined,
189
+ content: Record<string, ContentSectionEntry | null | undefined> | null | undefined,
194
190
  locales: readonly string[] = [],
195
191
  ): AssetMount | null {
196
192
  if (!documentPath || !content) {
@@ -204,7 +200,7 @@ export function contentMountFor(
204
200
  }
205
201
  };
206
202
  for (const [name, def] of Object.entries(content)) {
207
- if (!def?.source || !SAFE_TYPE_NAME.test(name)) {
203
+ if (!def?.source || !SAFE_CONTENT_TYPE_NAME.test(name)) {
208
204
  continue;
209
205
  }
210
206
  const dir = normalizeSource(def.source);
@@ -222,7 +218,7 @@ export function contentMountFor(
222
218
  if (/\.[a-z0-9]+$/i.test(dir)) {
223
219
  continue;
224
220
  }
225
- consider(dir, `/${SECTION_KEY}/${name}`);
221
+ consider(dir, `/${CONTENT_SECTION_KEY}/${name}`);
226
222
  }
227
223
  return best;
228
224
  }
@@ -256,15 +252,15 @@ function localeMountFor(
256
252
  return null;
257
253
  }
258
254
  const segment = path.slice(head.length).split("/")[0] ?? "";
259
- if (segment === "" || !SAFE_TYPE_NAME.test(segment)) {
255
+ if (segment === "" || !SAFE_CONTENT_TYPE_NAME.test(segment)) {
260
256
  return null;
261
257
  }
262
258
  const canonical = locales.find((tag) => tag.toLowerCase() === segment.toLowerCase());
263
- if (canonical === undefined || !SAFE_TYPE_NAME.test(canonical)) {
259
+ if (canonical === undefined || !SAFE_CONTENT_TYPE_NAME.test(canonical)) {
264
260
  return null;
265
261
  }
266
262
  const dir = `${head}${segment}${tail}`.replace(/\/+$/, "");
267
- return { dir, urlPrefix: `/${SECTION_KEY}/${name}/${canonical}` };
263
+ return { dir, urlPrefix: `/${CONTENT_SECTION_KEY}/${name}/${canonical}` };
268
264
  }
269
265
 
270
266
  /** Strip `./` and any trailing slash from a configured `source`, the way collectionDirs does. */
@@ -5,7 +5,8 @@
5
5
  * multiple canvas-related modules: element lookup, zoom, panel resolution, inline bubbling.
6
6
  */
7
7
 
8
- import { getNodeAtPath, parentElementPath } from "../store";
8
+ import { getNodeAtPath } from "@jxsuite/schema/doc-ops";
9
+ import { parentElementPath } from "../store";
9
10
  import { displayTagName } from "@jxsuite/schema/guards";
10
11
  import { activeCanvasSurface, activeMediaOfPane } from "./canvas-surface";
11
12
  import { isInlineInContext } from "../editor/inline-edit";
@@ -46,7 +47,7 @@ export function panelOfSurface(surface: CanvasSurface): CanvasPanel | null {
46
47
  }
47
48
  const activeMedia = activeMediaOfPane(surface.paneId);
48
49
  for (const p of canvasPanels) {
49
- if (activeMedia === null && (p.mediaName === "base" || p.mediaName === null)) {
50
+ if (activeMedia === null && p.mediaName === "base") {
50
51
  return p;
51
52
  }
52
53
  if (p.mediaName === activeMedia) {
@@ -78,10 +79,10 @@ export function bubbleInlinePath(doc: JxMutableNode | undefined, path: JxPath) {
78
79
  return path;
79
80
  }
80
81
  let currentPath = path;
81
- while (currentPath.length >= 2) {
82
+ // `parentElementPath` is null exactly when the path is too short to have a parent element.
83
+ for (let pPath = parentElementPath(currentPath); pPath; pPath = parentElementPath(currentPath)) {
82
84
  const node = getNodeAtPath(doc, currentPath);
83
- const pPath = parentElementPath(currentPath);
84
- const parentNode = pPath ? getNodeAtPath(doc, pPath) : null;
85
+ const parentNode = getNodeAtPath(doc, pPath);
85
86
  if (!node || !parentNode) {
86
87
  break;
87
88
  }
@@ -90,7 +91,7 @@ export function bubbleInlinePath(doc: JxMutableNode | undefined, path: JxPath) {
90
91
  if (!isInlineInContext(childTag, parentTag)) {
91
92
  break;
92
93
  }
93
- currentPath = pPath as JxPath;
94
+ currentPath = pPath;
94
95
  }
95
96
  return currentPath;
96
97
  }
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { projectState, stripEventHandlers } from "../store";
10
- import { displayTagName } from "@jxsuite/schema/guards";
10
+ import { displayTagName, isMappedArray, isNonNullObject, isString } from "@jxsuite/schema/guards";
11
11
  import { canvasModeOfTab } from "./canvas-surface";
12
12
  import { toRaw } from "../reactivity";
13
13
  import {
@@ -20,7 +20,6 @@ import {
20
20
  resolveLayoutDoc,
21
21
  } from "../site-context";
22
22
  import { documentBase } from "./canvas-origin";
23
- import { getPlatform, hasPlatform } from "../platform";
24
23
  import { componentRegistry, computeRelativePath } from "../files/components";
25
24
  import { prepareForEditMode } from "../utils/edit-display";
26
25
  import {
@@ -29,10 +28,10 @@ import {
29
28
  substitutePreviewParams,
30
29
  } from "../page-params";
31
30
  import { isComponentDoc, substitutePreviewProps } from "../component-props";
32
- import { assetContextFor } from "./asset-refs";
31
+ import { assetContextFor, hostAssetDeclarations } from "./asset-refs";
33
32
 
34
33
  import type { AssetContext } from "./asset-refs";
35
- import type { JxElement, JxMutableNode, JxPath } from "@jxsuite/schema/types";
34
+ import type { JxElement, JxMutableNode, JxPath, JxStyle } from "@jxsuite/schema/types";
36
35
  import type { ComponentEntry } from "../files/components.js";
37
36
  import type { LayoutMarker } from "./path-mapping";
38
37
  import type { Tab } from "../tabs/tab";
@@ -55,19 +54,20 @@ function findPageContentPrefix(
55
54
  node: JxMutableNode,
56
55
  path: (string | number)[] = [],
57
56
  ): { prefix: (string | number)[]; offset: number } | null {
58
- if (!node || typeof node !== "object") {
57
+ // Document JSON can hold a null or scalar where a node belongs; neither has page content.
58
+ if (!isNonNullObject(node)) {
59
59
  return null;
60
60
  }
61
61
  if (Array.isArray(node.children)) {
62
62
  const firstPageIdx = node.children.findIndex(
63
- (child) => child && typeof child === "object" && !child.$__layout,
63
+ (child) => isNonNullObject(child) && !child.$__layout,
64
64
  );
65
65
  if (firstPageIdx !== -1) {
66
66
  return { offset: firstPageIdx, prefix: [...path, "children"] };
67
67
  }
68
68
  for (let i = 0; i < node.children.length; i++) {
69
69
  const child = node.children[i];
70
- if (child && typeof child === "object" && child.$__layout) {
70
+ if (isNonNullObject(child) && child.$__layout) {
71
71
  const found = findPageContentPrefix(child, [...path, "children", i]);
72
72
  if (found) {
73
73
  return found;
@@ -87,13 +87,14 @@ function findPageContentPrefix(
87
87
  * editor can open the layout with that node selected. See {@link LayoutMarker}.
88
88
  */
89
89
  export function markLayoutNodes(node: JxMutableNode, file: string, path: JxPath = []) {
90
- if (!node || typeof node !== "object") {
90
+ // Layout JSON can hold a null or scalar where a node belongs; there is nothing to mark on it.
91
+ if (!isNonNullObject(node)) {
91
92
  return;
92
93
  }
93
94
  node.$__layout = { file, path } satisfies LayoutMarker;
94
95
  if (Array.isArray(node.children)) {
95
96
  for (const [i, child] of node.children.entries()) {
96
- if (typeof child === "string") {
97
+ if (isString(child)) {
97
98
  continue;
98
99
  }
99
100
  markLayoutNodes(child, file, [...path, "children", i]);
@@ -101,7 +102,10 @@ export function markLayoutNodes(node: JxMutableNode, file: string, path: JxPath
101
102
  }
102
103
  if (Array.isArray(node.$elements)) {
103
104
  for (const [i, el] of node.$elements.entries()) {
104
- if (typeof el !== "string") {
105
+ if (!isString(el)) {
106
+ /* SAFETY: a non-string `$elements` entry is an inline element definition or a `{ $ref }`
107
+ import. markLayoutNodes stamps `$__layout` and walks `children` / `$elements`, which a
108
+ `$ref` entry simply lacks, so it is marked as a childless node. */
105
109
  markLayoutNodes(el as JxMutableNode, file, [...path, "$elements", i]);
106
110
  }
107
111
  }
@@ -157,7 +161,7 @@ export async function resolveCanvasDocument(
157
161
  /** How the canvas must address project media, or null when its origin serves the site URL space. */
158
162
  assets: AssetContext | null;
159
163
  mapperCtx: WireMapperCtx;
160
- siteStyle: Record<string, unknown> | null;
164
+ siteStyle: JxStyle | null;
161
165
  }> {
162
166
  const S = { documentPath: tab?.documentPath, mode: tab?.doc.mode };
163
167
  const canvasMode = modeOverride ?? canvasModeOfTab(tab);
@@ -180,7 +184,7 @@ export async function resolveCanvasDocument(
180
184
  if (isPage && tab?.session.ui.showLayout !== false) {
181
185
  const layoutPath = getEffectiveLayoutPath(doc.$layout);
182
186
  if (layoutPath) {
183
- const layoutDoc = (await resolveLayoutDoc(layoutPath)) as JxMutableNode | null;
187
+ const layoutDoc = await resolveLayoutDoc(layoutPath);
184
188
  if (layoutDoc) {
185
189
  // The SAME normalization resolveLayoutDoc reads the file with, so the marker's `file` is a
186
190
  // Path the editor can hand straight to navigateToComponent when the author asks to open it.
@@ -210,10 +214,7 @@ export async function resolveCanvasDocument(
210
214
  * — design and edit swap every bound src for a transparent pixel, so they never reached the
211
215
  * network to fail.
212
216
  */
213
- const assets = assetContextFor(S.documentPath, {
214
- fileBaseUrl: fileBase,
215
- ...(hasPlatform() ? { space: getPlatform().assetSpace } : {}),
216
- });
217
+ const assets = assetContextFor(S.documentPath, hostAssetDeclarations());
217
218
 
218
219
  // Substitute chosen dynamic route params ({$ref: "#/$params/x"} → literal), inject state.$page,
219
220
  // And bake the substituted class-prototype state entries via the backend resolver — in every
@@ -222,10 +223,8 @@ export async function resolveCanvasDocument(
222
223
  // (posted as shadowDoc), whose $refs must survive for editing/serialization.
223
224
  if (isPage && tab) {
224
225
  const { previewParams } = tab.session.ui;
225
- if (previewParams && Object.keys(previewParams).length > 0) {
226
- const boundKeys = paramBoundStateKeys(
227
- (doc as { state?: Record<string, unknown> }).state ?? null,
228
- );
226
+ if (Object.keys(previewParams).length > 0) {
227
+ const boundKeys = paramBoundStateKeys(doc.state ?? null);
229
228
  renderDoc = substitutePreviewParams(renderDoc, previewParams, S.documentPath);
230
229
  await resolveParamBoundState(renderDoc, boundKeys, docBase);
231
230
  }
@@ -253,30 +252,29 @@ export async function resolveCanvasDocument(
253
252
  const arrayPaths = new Set<string>();
254
253
  if (canvasMode !== "preview") {
255
254
  (function findArrayPaths(node: JxMutableNode, path: (string | number)[]) {
256
- if (!node || typeof node !== "object") {
255
+ // Document JSON can hold a null or scalar where a node belongs; it holds no repeater.
256
+ if (!isNonNullObject(node)) {
257
257
  return;
258
258
  }
259
- if ((node as unknown as { $prototype?: string }).$prototype === "Array") {
259
+ if (node.$prototype === "Array") {
260
260
  arrayPaths.add(path.join("/"));
261
- const mapDef = (node as JxMutableNode).map;
262
- if (mapDef && typeof mapDef === "object") {
261
+ const mapDef = node.map;
262
+ if (isNonNullObject(mapDef)) {
263
263
  findArrayPaths(mapDef, [...path, "map"]);
264
264
  }
265
265
  return;
266
266
  }
267
267
  if (Array.isArray(node.children)) {
268
- for (let i = 0; i < node.children.length; i++) {
269
- const child = node.children[i];
270
- if (typeof child === "string") {
268
+ for (const [i, child] of node.children.entries()) {
269
+ if (isString(child)) {
271
270
  continue;
272
271
  }
273
- findArrayPaths(child!, [...path, "children", i]);
272
+ findArrayPaths(child, [...path, "children", i]);
274
273
  }
275
- } else if (
276
- node.children &&
277
- typeof node.children === "object" &&
278
- (node.children as unknown as { $prototype?: string }).$prototype === "Array"
279
- ) {
274
+ } else if (isMappedArray(node.children)) {
275
+ /* SAFETY: the bare mapped-array slot is a repeater node in all but its declared type, and
276
+ the walk reads only what isMappedArray just established (`$prototype`) and its `map`
277
+ template, an element definition. */
280
278
  findArrayPaths(node.children as JxMutableNode, [...path, "children"]);
281
279
  }
282
280
  if (node.$switch && node.cases) {
@@ -295,14 +293,19 @@ export async function resolveCanvasDocument(
295
293
  const isLayoutDoc =
296
294
  S.documentPath != null &&
297
295
  (S.documentPath.startsWith("layouts/") || S.documentPath.startsWith("./layouts/"));
296
+ /* SAFETY: a document's `$elements` entries are tag-name strings, `{ $ref }` imports or inline
297
+ element definitions, each a JxElement or a string; getEffectiveElements only dedups them by the
298
+ string itself or by `$ref`. */
298
299
  const effectiveElements = getEffectiveElements(renderDoc.$elements as (JxElement | string)[]);
299
300
  if ((S.mode === "content" || layoutWrapped || isLayoutDoc) && componentRegistry.length > 0) {
300
301
  const existingRefs = new Set(
301
- effectiveElements.map((e: JxElement | string) => (typeof e === "string" ? e : e?.$ref)),
302
+ // A null entry in a document's `$elements` names nothing, so it contributes no ref.
303
+ effectiveElements.map((e: JxElement | string) => (isNonNullObject(e) ? e.$ref : e)),
302
304
  );
303
305
  const collectTags = (node: JxMutableNode): Set<string> => {
304
306
  const tags = new Set<string>();
305
- if (!node || typeof node !== "object") {
307
+ // Document JSON can hold a null or scalar where a node belongs; it carries no tag.
308
+ if (!isNonNullObject(node)) {
306
309
  return tags;
307
310
  }
308
311
  if (node.tagName) {
@@ -310,7 +313,7 @@ export async function resolveCanvasDocument(
310
313
  }
311
314
  if (Array.isArray(node.children)) {
312
315
  for (const child of node.children) {
313
- if (typeof child !== "string") {
316
+ if (!isString(child)) {
314
317
  for (const t of collectTags(child)) {
315
318
  tags.add(t);
316
319
  }
@@ -331,24 +334,12 @@ export async function resolveCanvasDocument(
331
334
  }
332
335
  }
333
336
  if (effectiveElements.length > 0) {
334
- renderDoc.$elements = effectiveElements as (string | JxMutableNode | { $ref: string })[];
337
+ renderDoc.$elements = effectiveElements;
335
338
  }
336
339
  renderDoc.imports = getEffectiveImports(renderDoc.imports);
337
- // The effective-media/head getters return the merged value or undefined. Optional properties
338
- // Cannot be assigned undefined under exactOptionalPropertyTypes, so clear them by deleting the key
339
- // When the getter yields nothing.
340
- const media = getEffectiveMedia(renderDoc.$media);
341
- if (media === undefined) {
342
- delete renderDoc.$media;
343
- } else {
344
- renderDoc.$media = media as NonNullable<JxMutableNode["$media"]>;
345
- }
346
- const head = getEffectiveHead(renderDoc.$head);
347
- if (head === undefined) {
348
- delete renderDoc.$head;
349
- } else {
350
- renderDoc.$head = head as NonNullable<JxMutableNode["$head"]>;
351
- }
340
+ // The effective-media/head getters always answer a value: the merge, or an empty one.
341
+ renderDoc.$media = getEffectiveMedia(renderDoc.$media);
342
+ renderDoc.$head = getEffectiveHead(renderDoc.$head);
352
343
 
353
344
  return {
354
345
  assets,
@@ -361,6 +352,6 @@ export async function resolveCanvasDocument(
361
352
  pageContentPrefix,
362
353
  },
363
354
  renderDoc,
364
- siteStyle: (projectState?.projectConfig?.style as Record<string, unknown>) ?? null,
355
+ siteStyle: projectState?.projectConfig?.style ?? null,
365
356
  };
366
357
  }
@@ -18,7 +18,9 @@
18
18
  * edit typed into the first, and how one failed patch would rebuild both.
19
19
  */
20
20
 
21
- import { getNodeAtPath, parentElementPath } from "../store";
21
+ import { getNodeAtPath } from "@jxsuite/schema/doc-ops";
22
+ import { isString } from "@jxsuite/schema/guards";
23
+ import { parentElementPath } from "../store";
22
24
  import { canvasModeOfPane, surfacesShowingTab } from "./canvas-surface";
23
25
  import { toRaw } from "../reactivity";
24
26
  import { postPatchToHosts } from "./iframe-host";
@@ -29,6 +31,13 @@ import type { CanvasSurface } from "./surface-registry";
29
31
  import type { JxPatchOp, TransactionRecord } from "../tabs/patch-ops";
30
32
  import type { Tab } from "../tabs/tab.js";
31
33
  import type { JxPath } from "../state";
34
+ import type { JxMutableNode } from "@jxsuite/schema/types";
35
+
36
+ /** Whether a batch can be applied surgically, and why not when it cannot. */
37
+ export interface PatchVerdict {
38
+ patchable: boolean;
39
+ reason: string;
40
+ }
32
41
 
33
42
  /** Render-side callbacks injected at init so this module stays free of heavy canvas imports. */
34
43
  interface CanvasPatcherCtx {
@@ -62,10 +71,17 @@ export function initCanvasPatcher(ctx: CanvasPatcherCtx) {
62
71
  apply: applyPatchBatch,
63
72
  classify: classifyOps,
64
73
  escalate: escalateToFullRender,
65
- markConsumed: (docRef: object, tab: Tab) => {
66
- markConsumed(
67
- docRef,
68
- patchableSurfaces(tab).map((surface) => surface.paneId),
74
+ /* Records that `docRef`'s change has been applied surgically in each patchable pane.
75
+
76
+ No empty-list guard, because the list cannot be empty here: `transactDoc` marks only a batch
77
+ {@link classifyOps} called patchable, and that function rejects `patchableSurfaces(tab).length
78
+ === 0` before it can say so. A guard on an unreachable value is a branch no test can enter (the
79
+ gate's own rule), and `_consumed` is a `WeakMap` keyed on a reference `transactDoc` mints fresh
80
+ per transaction, so there is nothing for one to protect either. */
81
+ markConsumed: (docRef, tab) => {
82
+ _consumed.set(
83
+ toRaw(docRef),
84
+ new Set(patchableSurfaces(tab).map((surface) => surface.paneId)),
69
85
  );
70
86
  },
71
87
  });
@@ -96,32 +112,16 @@ function patchableSurfaces(tab: Tab | null): CanvasSurface[] {
96
112
  });
97
113
  }
98
114
 
99
- /**
100
- * Record that `docRef`'s change has been applied surgically in each of `paneIds`.
101
- *
102
- * No empty-list guard, because the list cannot be empty here: `transactDoc` marks only a batch
103
- * {@link classifyOps} called patchable, and that function rejects `patchableSurfaces(tab).length
104
- * === 0` before it can say so. A guard on an unreachable value is a branch no test can enter — the
105
- * gate's own rule — and `_consumed` is a `WeakMap` keyed on a reference `transactDoc` mints fresh
106
- * per transaction, so there is nothing for one to protect either.
107
- *
108
- * @param {object} docRef
109
- * @param {readonly string[]} paneIds
110
- */
111
- function markConsumed(docRef: object, paneIds: readonly string[]) {
112
- _consumed.set(toRaw(docRef) as object, new Set(paneIds));
113
- }
114
-
115
115
  /**
116
116
  * One-shot check used by ONE PANE's canvas doc-effect: returns true (and clears that pane's mark)
117
117
  * when the given document reference was already applied to that pane's stage surgically, so its
118
118
  * full render can be skipped. One-shot so tab switches and repeat triggers still render fully.
119
119
  *
120
- * @param {object} doc
120
+ * @param {JxMutableNode} doc
121
121
  * @param {string} paneId
122
122
  */
123
- export function consumePatchedDocument(doc: object, paneId: string): boolean {
124
- const raw = toRaw(doc) as object;
123
+ export function consumePatchedDocument(doc: JxMutableNode, paneId: string): boolean {
124
+ const raw = toRaw(doc);
125
125
  const panes = _consumed.get(raw);
126
126
  if (!panes?.has(paneId)) {
127
127
  return false;
@@ -221,7 +221,7 @@ function containerVerdict(tab: Tab, parentPath: JxPath, requireArray = true): st
221
221
  * definition escalated to a full render — in a component-heavy project, the most common authoring
222
222
  * action there is.
223
223
  */
224
- if (parentPath.length > 0 && typeof parent.tagName === "string" && parent.tagName.includes("-")) {
224
+ if (parentPath.length > 0 && isString(parent.tagName) && parent.tagName.includes("-")) {
225
225
  return "structure-in-custom-element";
226
226
  }
227
227
  if (parent.innerHTML) {
@@ -253,7 +253,7 @@ function replaceVerdict(tab: Tab, path: JxPath): string | null {
253
253
  if (pathHasDynamicSegment(path)) {
254
254
  return "replace-on-cases-path";
255
255
  }
256
- const parentPath = parentElementPath(path) as JxPath | null;
256
+ const parentPath = parentElementPath(path);
257
257
  if (!parentPath) {
258
258
  return "replace-no-parent";
259
259
  }
@@ -295,7 +295,7 @@ function opVerdict(tab: Tab, op: JxPatchOp): string | null {
295
295
  if (!node) {
296
296
  return "node-not-found";
297
297
  }
298
- if (typeof node.tagName === "string" && node.tagName.includes("-")) {
298
+ if (isString(node.tagName) && node.tagName.includes("-")) {
299
299
  return "text-on-custom-element";
300
300
  }
301
301
  if (node.innerHTML) {
@@ -320,14 +320,14 @@ function opVerdict(tab: Tab, op: JxPatchOp): string | null {
320
320
  return containerVerdict(tab, op.parentPath);
321
321
  }
322
322
  case "remove": {
323
- const parentPath = parentElementPath(op.path) as JxPath | null;
323
+ const parentPath = parentElementPath(op.path);
324
324
  return parentPath === null ? "remove-no-parent" : containerVerdict(tab, parentPath);
325
325
  }
326
326
  case "move": {
327
327
  if (pathHasDynamicSegment(op.fromPath)) {
328
328
  return "structure-on-cases-path";
329
329
  }
330
- const fromParentPath = parentElementPath(op.fromPath) as JxPath | null;
330
+ const fromParentPath = parentElementPath(op.fromPath);
331
331
  if (fromParentPath === null) {
332
332
  return "move-no-parent";
333
333
  }
@@ -347,7 +347,7 @@ function opVerdict(tab: Tab, op: JxPatchOp): string | null {
347
347
  * @param {Tab} tab
348
348
  * @param {JxPatchOp[]} ops
349
349
  */
350
- export function classifyOps(tab: Tab, ops: JxPatchOp[]): { patchable: boolean; reason: string } {
350
+ export function classifyOps(tab: Tab, ops: JxPatchOp[]): PatchVerdict {
351
351
  const reject = (reason: string) => {
352
352
  recordEscalation(reason);
353
353
  return { patchable: false, reason };