@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
@@ -27,7 +27,15 @@ import { collectionInfo } from "../grid/sources/content-source";
27
27
  import { projectState } from "../store";
28
28
  import { findContentTypeSchema } from "../utils/studio-utils";
29
29
  import type { ContentSectionEntry } from "../types";
30
- import type { ContentTypeSchema, ContentTypeSchemaField } from "@jxsuite/schema/types";
30
+ import { isNonNullObject, isString } from "@jxsuite/schema/guards";
31
+ import type {
32
+ ContentTypeSchema,
33
+ ContentTypeSchemaField,
34
+ ContentTypeSchemaValue,
35
+ JsonObject,
36
+ JsonValue,
37
+ } from "@jxsuite/schema/types";
38
+ import type { EntryFields } from "./entry-fields";
31
39
 
32
40
  // ─── Collections ─────────────────────────────────────────────────────────────
33
41
 
@@ -44,7 +52,7 @@ export interface EntryCollection {
44
52
 
45
53
  /** Every content type the project declares, in declaration order. */
46
54
  export function collectionNames(): string[] {
47
- return Object.keys((projectState?.projectConfig?.content ?? {}) as Record<string, unknown>);
55
+ return Object.keys(projectState?.projectConfig?.content ?? {});
48
56
  }
49
57
 
50
58
  /**
@@ -94,12 +102,13 @@ export function collectionOfPath(path: string | null): EntryCollection | null {
94
102
  // ─── Seeding a new entry ─────────────────────────────────────────────────────
95
103
 
96
104
  /** Deep copy of a schema `default`, so two entries seeded from one schema never share an object. */
97
- function cloneDefault(value: unknown): unknown {
98
- if (value === null || typeof value !== "object") {
105
+ function cloneDefault(value: ContentTypeSchemaValue): JsonValue {
106
+ if (value === null || !isNonNullObject(value)) {
99
107
  return value;
100
108
  }
109
+ // SAFETY: `JSON.parse` of what `JSON.stringify` just wrote is JSON by construction.
101
110
  // oxlint-disable-next-line unicorn/prefer-structured-clone
102
- return JSON.parse(JSON.stringify(value)) as unknown;
111
+ return JSON.parse(JSON.stringify(value)) as JsonValue;
103
112
  }
104
113
 
105
114
  /**
@@ -109,8 +118,8 @@ function cloneDefault(value: unknown): unknown {
109
118
  * and picking an arbitrary entry of the target collection on the author's behalf would be inventing
110
119
  * a relationship they never stated.
111
120
  */
112
- function emptyForField(field: ContentTypeSchemaField): unknown {
113
- if (typeof field.$ref === "string") {
121
+ function emptyForField(field: ContentTypeSchemaField): JsonValue {
122
+ if (isString(field.$ref)) {
114
123
  return "";
115
124
  }
116
125
  switch (field.type) {
@@ -125,7 +134,7 @@ function emptyForField(field: ContentTypeSchemaField): unknown {
125
134
  return [];
126
135
  }
127
136
  case "object": {
128
- return seedEntry(field as ContentTypeSchema);
137
+ return seedEntry(field);
129
138
  }
130
139
  default: {
131
140
  return "";
@@ -145,13 +154,13 @@ function emptyForField(field: ContentTypeSchemaField): unknown {
145
154
  * author's file full of keys they never asked for, and "the schema mentions it" is not a reason for
146
155
  * it to exist on disk — the form still draws a row for it, because the form draws the SCHEMA.
147
156
  */
148
- export function seedEntry(schema: ContentTypeSchema | null | undefined): Record<string, unknown> {
157
+ export function seedEntry(schema: ContentTypeSchema | null | undefined): JsonObject {
149
158
  const properties = schema?.properties;
150
159
  if (!properties) {
151
160
  return {};
152
161
  }
153
- const required = new Set(Array.isArray(schema?.required) ? schema.required : []);
154
- const seed: Record<string, unknown> = {};
162
+ const required = new Set(Array.isArray(schema.required) ? schema.required : []);
163
+ const seed: JsonObject = {};
155
164
  for (const [name, field] of Object.entries(properties)) {
156
165
  if (field.default !== undefined) {
157
166
  seed[name] = cloneDefault(field.default);
@@ -168,7 +177,7 @@ export function seedEntry(schema: ContentTypeSchema | null | undefined): Record<
168
177
  */
169
178
  export function missingRequired(
170
179
  schema: ContentTypeSchema | null | undefined,
171
- value: Record<string, unknown>,
180
+ value: EntryFields,
172
181
  ): string[] {
173
182
  const required = Array.isArray(schema?.required) ? schema.required : [];
174
183
  return required.filter((name) => value[name] === undefined);
@@ -15,7 +15,6 @@ import { dismissContextMenu, showContextMenu } from "./context-menu";
15
15
  import { bubbleInlinePath } from "../canvas/canvas-helpers";
16
16
  import { activeTab } from "../workspace/workspace";
17
17
  import type { CanvasContextMenuHandler } from "../canvas/iframe-host";
18
- import type { JxPath } from "../state";
19
18
 
20
19
  /**
21
20
  * Build the handler.
@@ -34,7 +33,7 @@ export function makeCanvasContextMenuHandler(): CanvasContextMenuHandler {
34
33
  }
35
34
  // Lift inline-element paths (a <strong> inside a <p>) to their block parent — right-clicking
36
35
  // Formatted text should act on the block, matching the legacy handler.
37
- const bubbled = bubbleInlinePath(activeTab.value?.doc.document, path as JxPath);
36
+ const bubbled = bubbleInlinePath(activeTab.value?.doc.document, path);
38
37
  // ShowContextMenu reads only preventDefault/clientX/clientY from the event, so a synthetic
39
38
  // MouseEvent carries the converted coords safely.
40
39
  const evt = new MouseEvent("contextmenu", { clientX, clientY });
@@ -18,11 +18,8 @@ export const canvasSlashHandler: CanvasSlashHandler = {
18
18
  opened. Typing "/" needs none — the author keeps typing into the iframe's contenteditable and
19
19
  the engine re-posts `slashShow` with the updated filter. A menu opened BY NAME
20
20
  (`insert.openSlashMenu`) has no "/…" run to type into, so without the field the only way past
21
- fifteen blocks is scrolling. */
21
+ fifteen blocks is scrolling. The menu reads `showFilter` for truthiness alone, so `false` and an
22
+ absent key are one state to it. */
22
23
  show: ({ rect, filter, showFilter, onSelect, onDismiss }) =>
23
- showSlashMenuAtRect(rect, filter, {
24
- onDismiss,
25
- onSelect,
26
- ...(showFilter === true ? { showFilter: true } : {}),
27
- }),
24
+ showSlashMenuAtRect(rect, filter, { onDismiss, onSelect, showFilter: showFilter === true }),
28
25
  };
@@ -14,11 +14,18 @@
14
14
  * @docs studio/editing/writing
15
15
  */
16
16
 
17
+ import { isString } from "@jxsuite/schema/guards";
17
18
  import type { JxMutableNode } from "@jxsuite/schema/types";
18
19
 
19
20
  /** A block's content as a children array — the form both storage shapes normalize to. */
20
21
  export type Content = (JxMutableNode | string)[];
21
22
 
23
+ /** A block's content in the form it is stored: `children` for markup, folded `textContent` else. */
24
+ export interface StoredContent {
25
+ children?: Content;
26
+ textContent?: string;
27
+ }
28
+
22
29
  /**
23
30
  * The content of `node` as a children array, whichever way it is stored.
24
31
  *
@@ -30,18 +37,16 @@ export function contentOf(node: JxMutableNode | undefined | null): Content {
30
37
  return [];
31
38
  }
32
39
  if (Array.isArray(node.children)) {
33
- return node.children as Content;
40
+ return node.children;
34
41
  }
35
- return typeof node.textContent === "string" && node.textContent.length > 0
36
- ? [node.textContent]
37
- : [];
42
+ return isString(node.textContent) && node.textContent.length > 0 ? [node.textContent] : [];
38
43
  }
39
44
 
40
45
  /** The rendered text length of a content array. */
41
46
  export function contentLength(children: Content): number {
42
47
  let n = 0;
43
48
  for (const child of children) {
44
- n += typeof child === "string" ? child.length : contentLength(contentOf(child));
49
+ n += isString(child) ? child.length : contentLength(contentOf(child));
45
50
  }
46
51
  return n;
47
52
  }
@@ -60,7 +65,7 @@ export function sliceContent(children: Content, start: number, end: number): Con
60
65
  if (pos >= end) {
61
66
  break;
62
67
  }
63
- if (typeof child === "string") {
68
+ if (isString(child)) {
64
69
  const childEnd = pos + child.length;
65
70
  if (childEnd > start) {
66
71
  const text = child.slice(
@@ -102,7 +107,7 @@ function withContent(node: JxMutableNode, children: Content): JxMutableNode {
102
107
  if (children.length === 0) {
103
108
  return { ...rest, textContent: "" };
104
109
  }
105
- if (children.length === 1 && typeof children[0] === "string") {
110
+ if (children.length === 1 && isString(children[0])) {
106
111
  return { ...rest, textContent: children[0] };
107
112
  }
108
113
  return { ...rest, children };
@@ -115,12 +120,12 @@ function withContent(node: JxMutableNode, children: Content): JxMutableNode {
115
120
  export function normalizeContent(children: Content): Content {
116
121
  const out: Content = [];
117
122
  for (const child of children) {
118
- if (typeof child === "string") {
123
+ if (isString(child)) {
119
124
  if (child.length === 0) {
120
125
  continue;
121
126
  }
122
127
  const prev = out.at(-1);
123
- if (typeof prev === "string") {
128
+ if (isString(prev)) {
124
129
  out[out.length - 1] = prev + child;
125
130
  continue;
126
131
  }
@@ -152,15 +157,12 @@ export function spliceAcross(
152
157
  }
153
158
 
154
159
  /** `{ children }` when the content carries markup, else the folded `{ textContent }` form. */
155
- export function toStored(children: Content): {
156
- children?: Content;
157
- textContent?: string;
158
- } {
160
+ export function toStored(children: Content): StoredContent {
159
161
  const normalized = normalizeContent(children);
160
162
  if (normalized.length === 0) {
161
163
  return { textContent: "" };
162
164
  }
163
- if (normalized.length === 1 && typeof normalized[0] === "string") {
165
+ if (normalized.length === 1 && isString(normalized[0])) {
164
166
  return { textContent: normalized[0] };
165
167
  }
166
168
  return { children: normalized };
@@ -21,10 +21,11 @@
21
21
  * is left here is the projection: the menu itself is the `menu` surface (`surfaces/menu.json`), a
22
22
  * `jx-menu` the kit gives its keyboard contract, its popover and its dismissal.
23
23
  */
24
- import { displayTagName } from "@jxsuite/schema/guards";
24
+ import { getNodeAtPath } from "@jxsuite/schema/doc-ops";
25
+ import { displayTagName, isNodeObject, isNonNullObject, isString } from "@jxsuite/schema/guards";
25
26
  import { jsonClone } from "../utils/studio-utils";
26
27
  import { htmlToJx } from "@jxsuite/markup/html-to-jx";
27
- import { childIndex, getNodeAtPath, parentElementPath } from "../store";
28
+ import { childIndex, parentElementPath } from "../store";
28
29
  import { activeTab, workspace } from "../workspace/workspace";
29
30
  import { isSelected, isSpliceablePath, primarySelection } from "../tabs/selection";
30
31
  import {
@@ -58,17 +59,15 @@ import type { AnyCommand, CommandRegistry } from "../commands/registry";
58
59
  import type { CommandContext } from "../commands/context";
59
60
  import type { Placement } from "../commands/levels";
60
61
  import type { JxPath } from "../state";
61
- import type { JxMutableNode, JxStyle } from "@jxsuite/schema/types";
62
-
63
- type JxNode = JxMutableNode;
62
+ import type { JxAttributeValue, JxMutableNode, JxStyle } from "@jxsuite/schema/types";
64
63
 
65
64
  // ─── Clipboard helpers ───────────────────────────────────────────────────────
66
65
 
67
- const JX_MIME = "web application/jx+json";
66
+ export const JX_MIME = "web application/jx+json";
68
67
 
69
- /** @param {JxNode | string} node */
70
- function nodeToHtml(node: JxNode | string): string {
71
- if (typeof node === "string") {
68
+ /** @param {JxMutableNode | string} node */
69
+ function nodeToHtml(node: JxMutableNode | string): string {
70
+ if (isString(node)) {
72
71
  return node;
73
72
  }
74
73
  /* A chosen tag cannot be serialised to one HTML string, so the copy takes the branch the
@@ -77,9 +76,10 @@ function nodeToHtml(node: JxNode | string): string {
77
76
  const tag = displayTagName(node.tagName).split("|")[0] || "div";
78
77
  let attrs = "";
79
78
  if (node.attributes) {
80
- for (const [k, v] of Object.entries(node.attributes)) {
79
+ // A document's attribute value is read as possibly JSON `null`, which is skipped with the refs.
80
+ for (const [k, v] of Object.entries<JxAttributeValue | null>(node.attributes)) {
81
81
  // Bound ($ref) attribute values have no static HTML representation — skip.
82
- if (typeof v === "object") {
82
+ if (v === null || isNonNullObject(v)) {
83
83
  continue;
84
84
  }
85
85
  attrs += v === "" ? ` ${k}` : ` ${k}="${String(v).replaceAll('"', "&quot;")}"`;
@@ -94,7 +94,7 @@ function nodeToHtml(node: JxNode | string): string {
94
94
  }
95
95
  }
96
96
  let inner = "";
97
- if (typeof node.textContent === "string") {
97
+ if (isString(node.textContent)) {
98
98
  inner = node.textContent;
99
99
  } else if (Array.isArray(node.children)) {
100
100
  inner = node.children.map((c) => nodeToHtml(c)).join("");
@@ -105,9 +105,9 @@ function nodeToHtml(node: JxNode | string): string {
105
105
  /**
106
106
  * Write a Jx node to the system clipboard with both jx+json and text/html types.
107
107
  *
108
- * @param {object} json
108
+ * @param {JxMutableNode} json
109
109
  */
110
- async function writeToClipboard(json: Record<string, unknown>) {
110
+ async function writeToClipboard(json: JxMutableNode) {
111
111
  workspace.clipboard = json;
112
112
  try {
113
113
  const jxBlob = new Blob([JSON.stringify(json)], { type: JX_MIME });
@@ -131,7 +131,7 @@ async function writeToClipboard(json: Record<string, unknown>) {
131
131
  /**
132
132
  * Read from the system clipboard. Returns Jx node(s) or null.
133
133
  *
134
- * @returns {Promise<JxNode[] | null>}
134
+ * @returns {Promise<JxMutableNode[] | null>}
135
135
  */
136
136
  async function readFromClipboard() {
137
137
  try {
@@ -139,16 +139,18 @@ async function readFromClipboard() {
139
139
  for (const item of items) {
140
140
  if (item.types.includes(JX_MIME)) {
141
141
  const blob = await item.getType(JX_MIME);
142
- const json = JSON.parse(await blob.text()) as JxNode;
142
+ /* SAFETY: `web application/jx+json` is Jx's own clipboard type, and `writeToClipboard` puts a
143
+ node there serialized with JSON.stringify; malformed text throws into the fallback below. */
144
+ const json = JSON.parse(await blob.text()) as JxMutableNode;
143
145
  return [json];
144
146
  }
145
147
  if (item.types.includes("text/html")) {
146
148
  const blob = await item.getType("text/html");
147
149
  const htmlStr = await blob.text();
148
150
  const nodes = htmlToJx(htmlStr);
149
- const jxNodes = nodes.map((n) =>
150
- typeof n === "string" ? { tagName: "p", textContent: n } : n,
151
- ) as JxNode[];
151
+ const jxNodes: JxMutableNode[] = nodes.map((n) =>
152
+ isString(n) ? { tagName: "p", textContent: n } : n,
153
+ );
152
154
  if (jxNodes.length > 0) {
153
155
  return jxNodes;
154
156
  }
@@ -158,8 +160,8 @@ async function readFromClipboard() {
158
160
  const text = await blob.text();
159
161
  // Try parsing as Jx JSON
160
162
  try {
161
- const parsed = JSON.parse(text) as JxNode;
162
- if (parsed && parsed.tagName) {
163
+ const parsed: unknown = JSON.parse(text);
164
+ if (isNodeObject(parsed) && parsed.tagName) {
163
165
  return [parsed];
164
166
  }
165
167
  } catch {
@@ -241,7 +243,12 @@ export async function pasteNode() {
241
243
  }
242
244
 
243
245
  if (selected && selected.length >= 2) {
246
+ // SAFETY: parentElementPath returns a path for any path of two or more segments, checked above.
244
247
  const pp = parentElementPath(selected) as JxPath;
248
+ /* SAFETY: a selection with a sibling position ends in `"children", <index>`, so this is the
249
+ numeric child index. A selected repeater template (`…, "map"`) or `$switch` case also has two
250
+ or more segments; callers do not exclude that case yet, so it reaches here with a string
251
+ segment exactly as before this cast. */
245
252
  const idx = childIndex(selected) as number;
246
253
  transactDoc(tab, (t) => {
247
254
  for (let i = 0; i < nodes.length; i++) {
@@ -408,7 +415,10 @@ export function elementCommands(deps: ElementCommandDeps): AnyCommand[] {
408
415
  if (!target || !nodes || nodes.length === 0) {
409
416
  return;
410
417
  }
418
+ /* SAFETY: this record runs only when enabled, and `spliceable` admits a target whose path
419
+ ends in `"children", <index>` — so it has a parent element path. */
411
420
  const parent = parentElementPath(target.path) as JxPath;
421
+ // SAFETY: the same `spliceable` check proved the last segment is a numeric child index.
412
422
  const idx = childIndex(target.path) as number;
413
423
  transactDoc(activeTab.value, (t) => {
414
424
  for (const [offset, node] of nodes.entries()) {
@@ -576,7 +586,10 @@ function insertSiblingParagraph(target: ElementMenuTarget | null, offset: number
576
586
  if (!target) {
577
587
  return;
578
588
  }
589
+ /* SAFETY: both callers are records enabled only by `spliceable`, which admits a target whose path
590
+ ends in `"children", <index>` — so it has a parent element path. */
579
591
  const parent = parentElementPath(target.path) as JxPath;
592
+ // SAFETY: the same `spliceable` check proved the last segment is a numeric child index.
580
593
  const idx = childIndex(target.path) as number;
581
594
  transactDoc(activeTab.value, (t) =>
582
595
  mutateInsertNode(t, parent, idx + offset, { children: [], tagName: "p" }),
@@ -681,7 +694,7 @@ function commandTarget(): ElementMenuTarget | null {
681
694
  if (!tab || !path) {
682
695
  return null;
683
696
  }
684
- const node = getNodeAtPath(tab.doc.document, path) as JxMutableNode | undefined;
697
+ const node = getNodeAtPath(tab.doc.document, path);
685
698
  return node ? { node, path } : null;
686
699
  }
687
700
 
@@ -1,34 +1,33 @@
1
1
  import elementsMeta from "../../data/elements-meta.json";
2
2
 
3
- export interface SlashCommand {
4
- label: string;
5
- tag: string;
6
- description: string;
7
- }
3
+ import { ELEMENT_DEFS } from "./editable-tags";
4
+ import type { SlashCommand } from "./inline-edit";
5
+
6
+ export type { SlashCommand } from "./inline-edit";
8
7
 
9
- const TAG_LABELS = {
10
- article: "Article",
11
- aside: "Aside",
12
- blockquote: "Blockquote",
13
- div: "Div",
14
- footer: "Footer",
15
- h1: "Heading 1",
16
- h2: "Heading 2",
17
- h3: "Heading 3",
18
- h4: "Heading 4",
19
- h5: "Heading 5",
20
- h6: "Heading 6",
21
- header: "Header",
22
- main: "Main",
23
- nav: "Nav",
24
- ol: "Numbered List",
25
- p: "Paragraph",
26
- search: "Search",
27
- section: "Section",
28
- ul: "Bulleted List",
29
- } as Record<string, string>;
8
+ const TAG_LABELS = new Map<string, string>([
9
+ ["article", "Article"],
10
+ ["aside", "Aside"],
11
+ ["blockquote", "Blockquote"],
12
+ ["div", "Div"],
13
+ ["footer", "Footer"],
14
+ ["h1", "Heading 1"],
15
+ ["h2", "Heading 2"],
16
+ ["h3", "Heading 3"],
17
+ ["h4", "Heading 4"],
18
+ ["h5", "Heading 5"],
19
+ ["h6", "Heading 6"],
20
+ ["header", "Header"],
21
+ ["main", "Main"],
22
+ ["nav", "Nav"],
23
+ ["ol", "Numbered List"],
24
+ ["p", "Paragraph"],
25
+ ["search", "Search"],
26
+ ["section", "Section"],
27
+ ["ul", "Bulleted List"],
28
+ ]);
30
29
 
31
- const groups = (elementsMeta.$convertGroups || {}) as Record<string, string[]>;
30
+ const groups: Readonly<Record<string, string[]>> = elementsMeta.$convertGroups;
32
31
 
33
32
  /**
34
33
  * Get the list of tags the current element can be converted to.
@@ -37,18 +36,14 @@ const groups = (elementsMeta.$convertGroups || {}) as Record<string, string[]>;
37
36
  * @param {boolean} isEmpty
38
37
  * @returns {SlashCommand[]}
39
38
  */
40
- export function getConvertTargets(currentTag: string, isEmpty: boolean) {
41
- const def = (elementsMeta.$defs as Record<string, Record<string, unknown>>)?.[currentTag] as
42
- | Record<string, unknown>
43
- | undefined;
39
+ export function getConvertTargets(currentTag: string, isEmpty: boolean): SlashCommand[] {
40
+ const def = ELEMENT_DEFS[currentTag];
44
41
  if (!def?.$convertTo) {
45
42
  return [];
46
43
  }
47
44
 
48
45
  const groupNames =
49
- isEmpty && def.$convertToWhenEmpty
50
- ? (def.$convertToWhenEmpty as string[])
51
- : [def.$convertTo as string];
46
+ isEmpty && def.$convertToWhenEmpty ? def.$convertToWhenEmpty : [def.$convertTo];
52
47
 
53
48
  const tags = new Set<string>();
54
49
  for (const name of groupNames) {
@@ -61,7 +56,7 @@ export function getConvertTargets(currentTag: string, isEmpty: boolean) {
61
56
 
62
57
  return [...tags].map((tag) => ({
63
58
  description: "",
64
- label: TAG_LABELS[tag] || tag.charAt(0).toUpperCase() + tag.slice(1),
59
+ label: TAG_LABELS.get(tag) || tag.charAt(0).toUpperCase() + tag.slice(1),
65
60
  tag,
66
61
  }));
67
62
  }
@@ -17,9 +17,10 @@
17
17
  *
18
18
  * @docs studio/design/components
19
19
  */
20
- import { displayTagName } from "@jxsuite/schema/guards";
20
+ import { displayTagName, isNonNullObject } from "@jxsuite/schema/guards";
21
21
  import { errorMessage } from "@jxsuite/schema/parse";
22
- import { childIndex, getNodeAtPath, parentElementPath } from "../store";
22
+ import { getNodeAtPath } from "@jxsuite/schema/doc-ops";
23
+ import { childIndex, parentElementPath } from "../store";
23
24
  import { primarySelection } from "../tabs/selection";
24
25
  import { activeTab } from "../workspace/workspace";
25
26
  import { transact } from "../tabs/transact";
@@ -71,10 +72,16 @@ export async function convertToComponent() {
71
72
  const converted = transact(tab, (doc) => {
72
73
  // Navigate to parent's children array and replace the node
73
74
  const pp = parentElementPath(selectionPath) ?? [];
75
+ /* SAFETY: an element with a sibling position ends in `"children", <index>`, so this is the
76
+ numeric child index. A selected repeater template (`…, "map"`) or `$switch` case also passes
77
+ the two-segment check above; callers do not exclude that case yet, so it reaches here with a
78
+ string segment exactly as before this cast. */
74
79
  const idx = childIndex(selectionPath) as number;
75
80
  let parent = doc;
76
- // Paths address nodes by construction (same contract as getNodeAtPath).
77
81
  for (const seg of pp) {
82
+ /* SAFETY: `pp` is the parent element's path, and a `JxPath` alternates node keys with
83
+ `"children"` and an index, so walking it from the root lands on a node (the same contract as
84
+ getNodeAtPath). */
78
85
  parent = parent[seg] as JxMutableNode;
79
86
  }
80
87
  if (!Array.isArray(parent.children)) {
@@ -87,8 +94,7 @@ export async function convertToComponent() {
87
94
  doc.$elements = [];
88
95
  }
89
96
  const alreadyReferenced = doc.$elements.some(
90
- (/** @type {JxMutableNode | string | { $ref: string }} */ el) =>
91
- el && typeof el === "object" && "$ref" in el && el.$ref === refPath,
97
+ (el) => isNonNullObject(el) && "$ref" in el && el.$ref === refPath,
92
98
  );
93
99
  if (!alreadyReferenced) {
94
100
  doc.$elements.push({ $ref: refPath });
@@ -12,7 +12,9 @@
12
12
  *
13
13
  * @docs studio/design/repeaters
14
14
  */
15
- import { childIndex, childList, getNodeAtPath, parentElementPath } from "../store";
15
+ import { getNodeAtPath } from "@jxsuite/schema/doc-ops";
16
+ import { isJsonObject, isPrototypeDef } from "@jxsuite/schema/guards";
17
+ import { childIndex, childList, parentElementPath } from "../store";
16
18
  import { primarySelection } from "../tabs/selection";
17
19
  import { activeTab } from "../workspace/workspace";
18
20
  import { transactDoc } from "../tabs/transact";
@@ -22,15 +24,18 @@ import { fetchPluginSchema } from "../services/code-services";
22
24
  import { CREATE_NEW_SOURCE, openConvertRepeaterSurface } from "../surfaces/convert-repeater";
23
25
 
24
26
  import type { ConvertRepeaterView } from "../surfaces/convert-repeater";
25
- import type { JxMutableNode } from "@jxsuite/schema/types";
27
+ import type { JxMutableNode, JxRef, JxStateDefinition } from "@jxsuite/schema/types";
26
28
 
27
29
  /** What a name has to look like to become a state definition. */
28
30
  const IDENTIFIER = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
29
31
 
32
+ /** A document's state definitions, by name. */
33
+ type StateDefs = Readonly<Record<string, JxStateDefinition>>;
34
+
30
35
  interface RepeaterConfig {
31
- items: { $ref: string } | unknown[];
32
- filter?: { $ref: string };
33
- sort?: { $ref: string };
36
+ items: JxRef;
37
+ filter?: JxRef;
38
+ sort?: JxRef;
34
39
  newDef?: { name: string };
35
40
  }
36
41
 
@@ -65,14 +70,18 @@ export async function convertToRepeater() {
65
70
  if (!pp) {
66
71
  return;
67
72
  }
73
+ /* SAFETY: the command is enabled only when every selected path is spliceable
74
+ (`structurallyEditable` in commands/defaults.ts), so `path` ends in `"children", <index>`. */
68
75
  const idx = childIndex(path) as number;
69
76
  const parent = getNodeAtPath(doc, pp);
70
77
  if (!parent?.children) {
71
78
  return;
72
79
  }
73
- const element = childList(parent)[idx];
80
+ /* SAFETY: a selection addresses an element node (the canvas selects elements, and an outline
81
+ text row is not a selectable item), and this reads the selected slot of its parent. */
82
+ const element = childList(parent)[idx] as JxMutableNode;
74
83
 
75
- const repeater: Record<string, unknown> = {
84
+ const repeater: JxMutableNode = {
76
85
  $prototype: "Array",
77
86
  items: config.items,
78
87
  map: element,
@@ -84,9 +93,11 @@ export async function convertToRepeater() {
84
93
  repeater.sort = config.sort;
85
94
  }
86
95
 
87
- // Replace the selected element in place with the array pseudo-element — no wrapper div. The
88
- // Repeated items render directly among the original parent's children.
89
- (parent.children as (string | JxMutableNode)[])[idx] = repeater as unknown as JxMutableNode;
96
+ /* Replace the selected element in place with the array pseudo-element — no wrapper div. The
97
+ repeated items render directly among the original parent's children.
98
+ SAFETY: a document is normalized to the member form when it loads into a tab
99
+ (`normalizeArrayChildren`), so a present `children` is an array, never a bare repeater. */
100
+ (parent.children as (string | JxMutableNode)[])[idx] = repeater;
90
101
  });
91
102
  }
92
103
 
@@ -98,18 +109,19 @@ export async function convertToRepeater() {
98
109
  * returns in its schema — so it is asked for once per definition, and only for the ones the first
99
110
  * three did not already claim.
100
111
  */
101
- async function arraySourceNames(defs: Record<string, unknown>): Promise<string[]> {
112
+ async function arraySourceNames(defs: StateDefs): Promise<string[]> {
102
113
  const named = Object.entries(defs)
103
- .filter(([, d]) => {
104
- const def = d as Record<string, unknown> | null;
105
- return def?.type === "array" || Array.isArray(def?.default) || def?.$prototype === "Array";
106
- })
114
+ .filter(
115
+ ([, def]) =>
116
+ isJsonObject(def) &&
117
+ (def.type === "array" || Array.isArray(def.default) || def.$prototype === "Array"),
118
+ )
107
119
  .map(([name]) => name);
108
120
 
109
121
  const docPath = activeTab.value?.documentPath;
110
- for (const [name, d] of Object.entries(defs)) {
111
- const def = d as Record<string, unknown> | null;
112
- if (!def?.$prototype || def.$prototype === "Function" || def.$prototype === "Array") {
122
+ for (const [name, def] of Object.entries(defs)) {
123
+ // Only a named, non-Function, non-Array prototype can be a plugin whose schema says it returns one.
124
+ if (!isPrototypeDef(def) || !def.$prototype || def.$prototype === "Array") {
113
125
  continue;
114
126
  }
115
127
  if (named.includes(name)) {
@@ -117,8 +129,8 @@ async function arraySourceNames(defs: Record<string, unknown>): Promise<string[]
117
129
  }
118
130
  const schema = await fetchPluginSchema(
119
131
  {
120
- ...(def.$src != null && { $src: def.$src as string }),
121
- $prototype: def.$prototype as string,
132
+ ...(def.$src != null && { $src: def.$src }),
133
+ $prototype: def.$prototype,
122
134
  },
123
135
  { ...(docPath != null && { documentPath: docPath }) },
124
136
  );
@@ -136,10 +148,10 @@ async function arraySourceNames(defs: Record<string, unknown>): Promise<string[]
136
148
  * name keeps the dialog open with the reason under the field — the flow's state machine, which is
137
149
  * the half the surface deliberately does not have.
138
150
  *
139
- * @param {Record<string, unknown>} defs
151
+ * @param {StateDefs} defs
140
152
  * @returns {Promise<RepeaterConfig | null>}
141
153
  */
142
- async function promptRepeaterConfig(defs: Record<string, unknown>): Promise<RepeaterConfig | null> {
154
+ async function promptRepeaterConfig(defs: StateDefs): Promise<RepeaterConfig | null> {
143
155
  const sources = await arraySourceNames(defs);
144
156
  const functions = Object.entries(defs)
145
157
  .filter(([, d]) => defCategory(d) === "function")
@@ -21,11 +21,26 @@
21
21
  */
22
22
 
23
23
  import elementsMeta from "../../data/elements-meta.json";
24
+ import type { InlineAction } from "./inline-edit";
24
25
 
25
- interface ElementDef {
26
+ /** The fields of one `elements-meta.json` `$defs` entry the editor reads. */
27
+ export interface ElementMeta {
28
+ /** Tags this element accepts as inline children; containers declare `[]`. */
26
29
  $inlineChildren?: string[];
30
+ /** The formatting verbs the block action bar offers, or the tag whose list this one shares. */
31
+ $inlineActions?: InlineAction[] | string;
32
+ /** The `$convertGroups` group this element converts within. */
33
+ $convertTo?: string;
34
+ /** The groups offered instead of `$convertTo` while the element is empty. */
35
+ $convertToWhenEmpty?: string[];
27
36
  }
28
37
 
38
+ /**
39
+ * The studio's element metadata by tag. The JSON is bundled with the studio, so the compiler checks
40
+ * every entry against {@link ElementMeta} here, once, instead of each reader asserting it.
41
+ */
42
+ export const ELEMENT_DEFS: Readonly<Record<string, ElementMeta>> = elementsMeta.$defs;
43
+
29
44
  /**
30
45
  * Tags the studio's own metadata says accept inline children.
31
46
  *
@@ -36,7 +51,7 @@ interface ElementDef {
36
51
  const EXCLUDED = new Set(["pre"]);
37
52
 
38
53
  export const BUILTIN_EDITABLE_TAGS: ReadonlySet<string> = new Set(
39
- Object.entries(elementsMeta.$defs as Record<string, ElementDef>)
54
+ Object.entries(ELEMENT_DEFS)
40
55
  .filter(([tag, def]) => !EXCLUDED.has(tag) && (def.$inlineChildren?.length ?? 0) > 0)
41
56
  .map(([tag]) => tag),
42
57
  );