@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
@@ -30,6 +30,7 @@
30
30
  * to the code that runs, so a chord and its behaviour cannot drift apart again.
31
31
  */
32
32
 
33
+ import { isFunction, isNonNullObject } from "@jxsuite/schema/guards";
33
34
  import { childIndex, childList, getNodeAtPath, parentElementPath, projectState } from "../store";
34
35
  import { activeTab, workspace } from "../workspace/workspace";
35
36
  import {
@@ -45,22 +46,22 @@ import {
45
46
  mutateDuplicateNodes,
46
47
  mutateInsertNode,
47
48
  mutateRemoveNodes,
48
- redo as tabRedo,
49
- undo as tabUndo,
49
+ redo,
50
+ undo,
50
51
  transactDoc,
51
52
  } from "../tabs/transact";
52
53
  import {
53
54
  applyEditZoom,
54
- markExplicitZoom,
55
55
  requestEditZoom,
56
56
  setEditZoom,
57
+ setUserZoom,
58
+ stageZoom,
57
59
  } from "../canvas/canvas-utils";
58
60
  import { openQuickSearch } from "../panels/quick-search";
59
61
  import { inspectorTab } from "../panels/right-panel";
60
62
  import { requestClose } from "../panels/tab-strip";
61
63
  import { layerHost } from "../ui/layers";
62
64
  import { openDialogSurface } from "../surfaces/dialog";
63
- import { rectOf } from "../utils/geometry";
64
65
  import {
65
66
  DOCK_IDS,
66
67
  requireNavigatorPanelId,
@@ -78,7 +79,10 @@ import { hasElementSelection, hasSelection, inCanvas, keyScopeStack } from "../c
78
79
  import { defaultCommands } from "../commands/defaults";
79
80
  import { setActiveRegistry } from "../commands/active-registry";
80
81
  import { runReported } from "../commands/run-reported";
82
+ import { pointerAnchor } from "../canvas/iframe-host";
83
+ import type { PointerAnchor } from "../canvas/iframe-host";
81
84
  import type { CommandContext } from "../commands/context";
85
+ import type { CommandArgValues } from "../commands/command-args";
82
86
  import type { DockId as CommandDockId } from "../commands/defaults";
83
87
  import type { AnyCommand, CommandRegistry } from "../commands/registry";
84
88
  import type { DockId, FocusRegion } from "../shell";
@@ -163,11 +167,14 @@ function navigateSelection(direction: -1 | 1): void {
163
167
  tab.session.selection = [[]];
164
168
  return;
165
169
  }
166
- if (selected.length < 2) {
170
+ // `parentElementPath` is null exactly when the path is the document element (length < 2).
171
+ const parentPath = parentElementPath(selected);
172
+ if (!parentPath) {
167
173
  return;
168
174
  }
169
- const parentPath = parentElementPath(selected) as JxPath;
170
175
  const parent = getNodeAtPath(tab.doc.document, parentPath);
176
+ // SAFETY: a selection path of length >= 2 addresses a child as `[..., "children", n]`, so its
177
+ // Last segment is the numeric index `n` (the same invariant `insertSiblingParagraph` relies on).
171
178
  const newIndex = (childIndex(selected) as number) + direction;
172
179
  if (newIndex >= 0 && newIndex < childList(parent).length) {
173
180
  tab.session.selection = [[...parentPath, "children", newIndex]];
@@ -188,14 +195,10 @@ function selectSiblings(): void {
188
195
  return;
189
196
  }
190
197
  const selected = primarySelection(tab.session.selection);
191
- const parentPath = (
192
- selected && selected.length >= 2 ? parentElementPath(selected) : []
193
- ) as JxPath;
198
+ const parentPath: JxPath = (selected ? parentElementPath(selected) : null) ?? [];
194
199
  const parent = getNodeAtPath(tab.doc.document, parentPath);
195
200
  const paths = childList(parent).flatMap((child, index) =>
196
- typeof child === "object" && child !== null
197
- ? [[...parentPath, "children", index] as JxPath]
198
- : [],
201
+ isNonNullObject(child) ? [[...parentPath, "children", index]] : [],
199
202
  );
200
203
  if (paths.length > 0) {
201
204
  tab.session.selection = paths;
@@ -240,10 +243,16 @@ function selectParent(): void {
240
243
  function insertSiblingParagraph(): void {
241
244
  const tab = activeTab.value;
242
245
  const selected = primarySelection(tab?.session.selection);
243
- if (!tab || !selected || selected.length < 2) {
246
+ if (!tab || !selected) {
247
+ return;
248
+ }
249
+ // Null exactly when the selection is the document element, which has no sibling slot.
250
+ const parentPath = parentElementPath(selected);
251
+ if (!parentPath) {
244
252
  return;
245
253
  }
246
- const parentPath = parentElementPath(selected) as JxPath;
254
+ // SAFETY: a selection path of length >= 2 addresses a child as `[..., "children", n]`, so its
255
+ // Last segment is the numeric child index `n`.
247
256
  const index = childIndex(selected) as number;
248
257
  const newPath = [...parentPath, "children", index + 1];
249
258
  transactDoc(tab, (t) => {
@@ -301,24 +310,28 @@ function closeDocument(): void {
301
310
  function undoDocument(): void {
302
311
  const tab = activeTab.value;
303
312
  if (tab) {
304
- tabUndo(tab);
313
+ undo(tab);
305
314
  }
306
315
  }
307
316
 
308
317
  function redoDocument(): void {
309
318
  const tab = activeTab.value;
310
319
  if (tab) {
311
- tabRedo(tab);
320
+ redo(tab);
312
321
  }
313
322
  }
314
323
 
315
324
  // ─── Canvas zoom ──────────────────────────────────────────────────────────────
316
325
 
317
- /** Design-mode zoom bounds. Edit mode clamps inside `canvas-utils`. */
318
- const ZOOM_MIN = 0.05;
319
- const ZOOM_MAX = 5;
326
+ /** One chord step. Both zooms clamp inside `canvas-utils`. */
320
327
  const ZOOM_STEP = 1.2;
321
328
 
329
+ /** How long after an Edit zoom step its anchor is measured again: the zoom's frame, then a reflow. */
330
+ const EDIT_ZOOM_SETTLE_MS = 34;
331
+
332
+ /** How long an Edit zoom burst must be idle before its anchor is let go. */
333
+ const EDIT_ZOOM_RELEASE_MS = 300;
334
+
322
335
  /** Whether the zoom verbs address the reflowing content zoom rather than the artboard transform. */
323
336
  function isContentZoom(ctx: CommandContext): boolean {
324
337
  return ctx.canvas.view === "edit";
@@ -343,11 +356,11 @@ function zoomBy(ctx: CommandContext, factor: number, redraw: () => void): void {
343
356
  return;
344
357
  }
345
358
  if (isContentZoom(ctx)) {
346
- setEditZoom((tab.session.ui.editZoom ?? 1) * factor);
359
+ setEditZoom(tab.session.ui.editZoom * factor);
347
360
  return;
348
361
  }
349
- const next = (tab.session.ui.zoom ?? 1) * factor;
350
- tab.session.ui.zoom = Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, next));
362
+ // Around the stage's middle, like the zoom pod's buttons.
363
+ setUserZoom(stageZoom() * factor);
351
364
  redraw();
352
365
  }
353
366
 
@@ -391,7 +404,7 @@ function toggleShellDock(registry: CommandRegistry, dock: CommandDockId): void {
391
404
  * a missing id must be inert rather than an exception thrown out of a keydown handler. A present
392
405
  * one that refuses its arguments is not inert: `commands/run-reported.ts` files it in Problems.
393
406
  */
394
- function runIfPresent(registry: CommandRegistry, id: string, args: Record<string, unknown>): void {
407
+ function runIfPresent(registry: CommandRegistry, id: string, args: CommandArgValues): void {
395
408
  if (registry.get(id) && registry.isEnabled(id)) {
396
409
  void runReported(registry, id, args, "Keyboard");
397
410
  }
@@ -641,8 +654,9 @@ function askWhereToOpen(openName: string): Promise<ProjectOpenTarget | "cancel">
641
654
  export async function openProjectFlow(hooks: StudioCommandHooks): Promise<void> {
642
655
  const platform = hasPlatform() ? getPlatform() : null;
643
656
  const canOpenElsewhere =
644
- typeof platform?.openProjectInNewWindow === "function" &&
645
- typeof platform?.pickProject === "function";
657
+ platform !== null &&
658
+ isFunction(platform.openProjectInNewWindow) &&
659
+ isFunction(platform.pickProject);
646
660
  if (!projectState || !canOpenElsewhere) {
647
661
  await hooks.openProject("thisWindow");
648
662
  return;
@@ -953,7 +967,11 @@ export function initShortcuts(registry: CommandRegistry, stageContext: StageCont
953
967
  document.addEventListener(
954
968
  "wheel",
955
969
  (e: WheelEvent) => {
956
- if ((e.ctrlKey || e.metaKey) && !(e.target as Element | null)?.closest(STAGE_SELECTOR)) {
970
+ const { target } = e;
971
+ if (
972
+ (e.ctrlKey || e.metaKey) &&
973
+ !(target instanceof Element && target.closest(STAGE_SELECTOR))
974
+ ) {
957
975
  e.preventDefault();
958
976
  }
959
977
  },
@@ -1016,7 +1034,7 @@ function wheelBelongsInside(
1016
1034
  ): boolean {
1017
1035
  const { target } = e;
1018
1036
  let el: Element | null =
1019
- target instanceof Element ? target : ((target as Node | null)?.parentElement ?? null);
1037
+ target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
1020
1038
  while (el && el !== stage) {
1021
1039
  if (el instanceof HTMLElement && el !== pageScroller && canScrollBy(el, e.deltaX, e.deltaY)) {
1022
1040
  return true;
@@ -1074,6 +1092,26 @@ let _stageContext: StageContext | null = null;
1074
1092
  */
1075
1093
  export function installStageGestures(surface: CanvasSurface): () => void {
1076
1094
  const canvasWrap = surface.wrap;
1095
+ /* An Edit zoom burst's anchor: the element under the cursor when the burst began. The frame
1096
+ reflows at every step, so after each one the element is measured again and the scroller takes
1097
+ back whatever it moved; the anchor is let go once the burst has been idle a while. */
1098
+ let zoomAnchor: PointerAnchor | null = null;
1099
+ let zoomRelease: ReturnType<typeof setTimeout> | undefined;
1100
+ let zoomCorrecting = false;
1101
+ const correctEditZoom = () => {
1102
+ const anchor = zoomAnchor;
1103
+ if (!anchor || zoomCorrecting) {
1104
+ return;
1105
+ }
1106
+ zoomCorrecting = true;
1107
+ void anchor.settle().then((top) => {
1108
+ zoomCorrecting = false;
1109
+ const sc = canvasWrap.querySelector<HTMLElement>('[part="edit-canvas"]');
1110
+ if (top !== null && sc) {
1111
+ sc.scrollTop += top - anchor.top;
1112
+ }
1113
+ });
1114
+ };
1077
1115
  const getContext = () => _stageContext?.(surface) ?? stageless();
1078
1116
  const controller = new AbortController();
1079
1117
  const { signal } = controller;
@@ -1093,7 +1131,16 @@ export function installStageGestures(surface: CanvasSurface): () => void {
1093
1131
  if (e.ctrlKey || e.metaKey) {
1094
1132
  e.preventDefault();
1095
1133
  const editZoom = paneTab?.session.ui.editZoom ?? 1;
1096
- requestEditZoom(editZoom * (1 + -e.deltaY * 0.005), surface);
1134
+ const canvasEl = surface.panels[0]?.canvas;
1135
+ zoomAnchor ??= canvasEl ? pointerAnchor(canvasEl) : null;
1136
+ requestEditZoom(editZoom * (1 + -e.deltaY * 0.005), surface, e.clientY);
1137
+ // Measured after the zoom's own frame has applied, and the frame has had one to reflow.
1138
+ setTimeout(correctEditZoom, EDIT_ZOOM_SETTLE_MS);
1139
+ clearTimeout(zoomRelease);
1140
+ zoomRelease = setTimeout(() => {
1141
+ correctEditZoom();
1142
+ zoomAnchor = null;
1143
+ }, EDIT_ZOOM_RELEASE_MS);
1097
1144
  return;
1098
1145
  }
1099
1146
  /* The stage's own scroller, by the `part` the stage document draws it with — the same
@@ -1125,24 +1172,14 @@ export function installStageGestures(surface: CanvasSurface): () => void {
1125
1172
  }
1126
1173
  e.preventDefault();
1127
1174
  if (e.ctrlKey || e.metaKey) {
1128
- // Zoom towards cursor
1129
- const rect = rectOf(canvasWrap);
1130
- const cursorX = e.clientX - rect.left;
1131
- const cursorY = e.clientY - rect.top;
1132
- /* THIS pane's tab, not the focused pane's. Both reads were `activeTab`, so a wheel over
1133
- the unfocused stage magnified a document on the other side of the splitter. */
1134
- const oldZoom = paneTab?.session.ui.zoom ?? 1;
1135
- const delta = -e.deltaY * 0.005;
1136
- const newZoom = Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, oldZoom * (1 + delta)));
1137
- const ratio = newZoom / oldZoom;
1138
- // Adjust pan so the point under cursor stays stationary
1139
- setPan(cursorX - (cursorX - panX) * ratio, cursorY - (cursorY - panY) * ratio);
1140
- if (paneTab) {
1141
- paneTab.session.ui.zoom = newZoom;
1142
- }
1143
- // The author chose this zoom, so re-entering Design keeps it instead of auto-fitting.
1144
- // On THIS stage: the declared fit is a fact about the document under the cursor.
1145
- markExplicitZoom(surface);
1175
+ /* Zoom toward the cursor, on THIS stage (a lens pane carries its own scale). The author
1176
+ chose this zoom, so it is declared as the document's fit and re-entering Design keeps
1177
+ it instead of auto-fitting. */
1178
+ setUserZoom(stageZoom(surface) * (1 + -e.deltaY * 0.005), surface, {
1179
+ clientX: e.clientX,
1180
+ clientY: e.clientY,
1181
+ });
1182
+ return;
1146
1183
  } else if (e.shiftKey) {
1147
1184
  // Shift+scroll = horizontal pan
1148
1185
  setPan(panX - e.deltaY, panY);
@@ -1206,7 +1243,10 @@ export function installStageGestures(surface: CanvasSurface): () => void {
1206
1243
  { signal },
1207
1244
  );
1208
1245
 
1209
- return () => controller.abort();
1246
+ return () => {
1247
+ clearTimeout(zoomRelease);
1248
+ controller.abort();
1249
+ };
1210
1250
  }
1211
1251
 
1212
1252
  /** The context a stage answers with before the bootstrap has published a reader. */
@@ -29,12 +29,7 @@
29
29
  import { openSlashMenuSurface } from "../surfaces/slash-menu";
30
30
  import { rectOf } from "../utils/geometry";
31
31
  import type { SlashMenuRow, SlashMenuSurface, SlashMenuView } from "../surfaces/slash-menu";
32
-
33
- interface SlashCommand {
34
- label: string;
35
- tag: string;
36
- description: string;
37
- }
32
+ import type { SlashCommand } from "./inline-edit";
38
33
 
39
34
  // ─── Commands ─────────────────────────────────────────────────────────────────
40
35
 
@@ -30,13 +30,14 @@
30
30
  * paths only — a `.json` or `.md` file is never loaded by URL from the canvas, and leaving them out
31
31
  * keeps that message as small as the media set.
32
32
  *
33
- * Pure module state — it imports nothing but path normalization and the reactivity re-export — so
34
- * the canvas host can import it without dragging in the file layer.
33
+ * Pure module state — it imports nothing but path normalization, the primitive guards and the
34
+ * reactivity re-export — so the canvas host can import it without dragging in the file layer.
35
35
  *
36
36
  * @docs extending/embedding/platform-adapter
37
37
  */
38
38
 
39
39
  import { normalizeProjectPath } from "@jxsuite/schema/asset-paths";
40
+ import { isString } from "@jxsuite/schema/guards";
40
41
  import { shallowRef } from "../reactivity";
41
42
  import type { DirEntry } from "../types";
42
43
 
@@ -161,8 +162,7 @@ export function noteListing(entries: readonly DirEntry[], since: number): void {
161
162
  if (path === "" || dirtiedSince(path, since)) {
162
163
  continue;
163
164
  }
164
- const version =
165
- typeof entry.version === "string" && entry.version !== "" ? entry.version : null;
165
+ const version = isString(entry.version) && entry.version !== "" ? entry.version : null;
166
166
  const held = versions.get(path);
167
167
  if (version === null) {
168
168
  if (held === undefined) {
@@ -4,7 +4,8 @@ import { getPlatform } from "../platform";
4
4
  import { projectState } from "../store";
5
5
  import type { ComponentMeta } from "../types";
6
6
  import { componentMetaFrom } from "@jxsuite/schema/component-meta";
7
- import type { JxMutableNode } from "@jxsuite/schema/types";
7
+ import { isNonNullObject, isString } from "@jxsuite/schema/guards";
8
+ import type { JsonValue, JxMutableNode } from "@jxsuite/schema/types";
8
9
 
9
10
  /** A discovered component: project components carry a file path; package components may not. */
10
11
  export interface ComponentEntry extends Omit<ComponentMeta, "path"> {
@@ -40,15 +41,17 @@ export async function loadComponentRegistry() {
40
41
  *
41
42
  * @param {string | null} path - The saved document's project-relative path; `null` for an unsaved
42
43
  * draft, which no scan would have seen either.
43
- * @param {unknown} doc - The saved document.
44
+ * @param {JxMutableNode} doc - The saved document.
44
45
  * @returns {boolean} Whether the registry changed, so a caller knows to repaint what reads it.
45
46
  */
46
- export function noteComponentSaved(path: string | null, doc: unknown): boolean {
47
+ export function noteComponentSaved(path: string | null, doc: JxMutableNode): boolean {
47
48
  if (!path) {
48
49
  return false;
49
50
  }
50
51
  const index = componentRegistry.findIndex((c) => c.path === path);
51
- const meta = componentMetaFrom(doc, path);
52
+ /* SAFETY: a saved tab's document was parsed from JSON and has just been written back as JSON
53
+ (`serializeDocument`), so every value it holds is a JSON value — the model the extractor reads. */
54
+ const meta = componentMetaFrom(doc as JsonValue, path);
52
55
  if (!meta) {
53
56
  if (index === -1) {
54
57
  return false;
@@ -56,6 +59,8 @@ export function noteComponentSaved(path: string | null, doc: unknown): boolean {
56
59
  componentRegistry.splice(index, 1);
57
60
  return true;
58
61
  }
62
+ /* SAFETY: `componentMetaFrom` is the extractor every backend's `discoverComponents` scan runs to
63
+ Produce the registry's entries, so its output has the shape the registry already holds. */
59
64
  const entry = meta as ComponentEntry;
60
65
  if (index === -1) {
61
66
  componentRegistry.push(entry);
@@ -84,7 +89,7 @@ export function buildComponentInstance(comp: ComponentEntry): JxMutableNode {
84
89
  for (const child of structuredClone(slot.fallback ?? [])) {
85
90
  if (!slot.name) {
86
91
  children.push(child);
87
- } else if (typeof child === "object" && child !== null && typeof child.tagName === "string") {
92
+ } else if (isNonNullObject(child) && isString(child.tagName)) {
88
93
  // Preserve an author-set slot attribute (slot forwarding inside fallback).
89
94
  child.attributes = { slot: slot.name, ...child.attributes };
90
95
  children.push(child);
@@ -94,11 +99,11 @@ export function buildComponentInstance(comp: ComponentEntry): JxMutableNode {
94
99
  }
95
100
  }
96
101
  }
97
- return {
98
- $props,
99
- tagName: comp.tagName,
100
- ...(children.length > 0 ? { children } : {}),
101
- };
102
+ const instance: JxMutableNode = { $props, tagName: comp.tagName };
103
+ if (children.length > 0) {
104
+ instance.children = children;
105
+ }
106
+ return instance;
102
107
  }
103
108
 
104
109
  /**
@@ -20,6 +20,7 @@
20
20
  * they must not differ on is WHICH entries the list should hold.
21
21
  */
22
22
 
23
+ import { isNonNullObject, isString } from "@jxsuite/schema/guards";
23
24
  import { computeRelativePath } from "./components";
24
25
  import type { ComponentEntry } from "./components";
25
26
  import type { JxElement } from "@jxsuite/schema/types";
@@ -29,9 +30,7 @@ export type ElementsEntry = string | JxElement | { $ref: string };
29
30
 
30
31
  /** The `$ref` of an entry, when it has one. */
31
32
  function refOf(entry: ElementsEntry): string | null {
32
- return typeof entry === "object" && entry !== null && typeof entry.$ref === "string"
33
- ? entry.$ref
34
- : null;
33
+ return isNonNullObject(entry) && isString(entry.$ref) ? entry.$ref : null;
35
34
  }
36
35
 
37
36
  /**
@@ -81,7 +80,7 @@ export function hasElement(
81
80
  const specifier = npmSpecifier(comp);
82
81
  if (specifier) {
83
82
  return elements.some(
84
- (entry) => typeof entry === "string" && (entry === specifier || entry === comp.package),
83
+ (entry) => isString(entry) && (entry === specifier || entry === comp.package),
85
84
  );
86
85
  }
87
86
  if (!comp.path) {
@@ -132,7 +131,7 @@ export function disableElement(
132
131
  if (specifier) {
133
132
  // Both spellings: a component ticked off must not stay imported through the package entry.
134
133
  return elements.filter(
135
- (entry) => !(typeof entry === "string" && (entry === specifier || entry === comp.package)),
134
+ (entry) => !(isString(entry) && (entry === specifier || entry === comp.package)),
136
135
  );
137
136
  }
138
137
  if (!comp.path) {
@@ -151,7 +150,7 @@ export function removePackageElements(
151
150
  pkg: string,
152
151
  ): ElementsEntry[] {
153
152
  return elements.filter(
154
- (entry) => typeof entry !== "string" || !(entry === pkg || entry.startsWith(`${pkg}/`)),
153
+ (entry) => !isString(entry) || !(entry === pkg || entry.startsWith(`${pkg}/`)),
155
154
  );
156
155
  }
157
156
 
@@ -28,6 +28,8 @@ import { showConfirmDialog } from "../ui/layers";
28
28
  import { locateDocument } from "../services/code-services";
29
29
  import { errorMessage } from "@jxsuite/schema/parse";
30
30
  import { notify } from "../services/notify";
31
+ import type { NotifyOptions } from "../services/notify";
32
+ import { isString } from "@jxsuite/schema/guards";
31
33
  import { validateComponentSlots } from "../services/cem-export";
32
34
  import { reportPopoverProblems } from "../services/popover-report";
33
35
  import { getPlatform } from "../platform";
@@ -103,19 +105,66 @@ export async function parseSourceForPath(path: string, source: string) {
103
105
  export const parseCollabSource: CollabParser = async (tab, text) => {
104
106
  if (tab.documentPath && formatForPath(tab.documentPath)) {
105
107
  const parsed = await parseSourceForPath(tab.documentPath, text);
106
- return { document: parsed.document as JxMutableNode, frontmatter: parsed.frontmatter };
108
+ return { document: parsed.document, frontmatter: parsed.frontmatter };
107
109
  }
108
110
  const parsed = parseJsonDocument(text);
109
111
  tab.doc.layout = parsed.layout;
112
+ /* SAFETY: a co-edited path no format class claims is a `.json` Jx document (every other path
113
+ `ensureCollab` attaches to is a format file, handled above), and that file's schema makes its
114
+ Top-level object a node — the object `parseJsonDocument` already states it returns. */
110
115
  return { document: parsed.document as JxMutableNode };
111
116
  };
112
117
 
118
+ /** One `accept` group of a File System Access picker: media type → extensions, with a label. */
119
+ interface FilePickerAcceptType {
120
+ accept: Record<string, string[]>;
121
+ description: string;
122
+ }
123
+
124
+ /**
125
+ * The File System Access pickers (WICG), which TypeScript's DOM lib does not declare. Only what
126
+ * this module passes and reads is spelled out.
127
+ */
128
+ interface FilePickerWindow {
129
+ showOpenFilePicker: (options: {
130
+ types: FilePickerAcceptType[];
131
+ }) => Promise<FileSystemFileHandle[]>;
132
+ /* Export's single extension is `format.extensions[0]`, absent for a format that declares none —
133
+ So the save picker is typed with the value it is actually handed. */
134
+ showSaveFilePicker: (options: {
135
+ suggestedName?: string | undefined;
136
+ types: { accept: Record<string, (string | undefined)[]>; description: string }[];
137
+ }) => Promise<FileSystemFileHandle>;
138
+ }
139
+
140
+ /** Whether this browser offers `showOpenFilePicker` (Chromium does; Firefox and Safari do not). */
141
+ function hasOpenFilePicker(
142
+ win: Window,
143
+ ): win is Window & Pick<FilePickerWindow, "showOpenFilePicker"> {
144
+ return "showOpenFilePicker" in win;
145
+ }
146
+
147
+ /** Whether this browser offers `showSaveFilePicker` (Chromium does; Firefox and Safari do not). */
148
+ function hasSaveFilePicker(
149
+ win: Window,
150
+ ): win is Window & Pick<FilePickerWindow, "showSaveFilePicker"> {
151
+ return "showSaveFilePicker" in win;
152
+ }
153
+
154
+ /** Attach the tab's file to a notification's options, when it has one. */
155
+ function atTabPath(tab: Tab, options: NotifyOptions): NotifyOptions {
156
+ if (tab.documentPath !== null) {
157
+ options.path = tab.documentPath;
158
+ }
159
+ return options;
160
+ }
161
+
113
162
  /** Open a file via the File System Access API (or fallback input). */
114
163
  export async function openFile() {
115
164
  try {
116
165
  await loadFormats();
117
166
  const formats = getFormats();
118
- const pickerTypes = [
167
+ const pickerTypes: FilePickerAcceptType[] = [
119
168
  {
120
169
  accept: { "application/json": [".json"] },
121
170
  description: "Jx Component",
@@ -160,12 +209,8 @@ export async function openFile() {
160
209
  // Trail; it does not need a message that erases itself.
161
210
  };
162
211
 
163
- if ("showOpenFilePicker" in window) {
164
- const [handle] = await (
165
- window as unknown as {
166
- showOpenFilePicker: (options?: unknown) => Promise<FileSystemFileHandle[]>;
167
- }
168
- ).showOpenFilePicker({ types: pickerTypes });
212
+ if (hasOpenFilePicker(window)) {
213
+ const [handle] = await window.showOpenFilePicker({ types: pickerTypes });
169
214
  const file = await handle!.getFile();
170
215
  await handleSource(handle!.name, await file.text(), handle!);
171
216
  } else {
@@ -230,16 +275,16 @@ function reportSaved(tab: Tab) {
230
275
  const doc = tab.doc.document;
231
276
  _onDocumentSaved(tab.documentPath, doc);
232
277
  const warning =
233
- typeof doc.tagName === "string" && doc.tagName.includes("-")
234
- ? validateComponentSlots(doc)
235
- : null;
278
+ isString(doc.tagName) && doc.tagName.includes("-") ? validateComponentSlots(doc) : null;
236
279
  if (warning) {
237
- notify.warn(warning, {
238
- key: `slots:${tab.documentPath ?? tab.id}`,
239
- ...(tab.documentPath === null ? {} : { path: tab.documentPath }),
240
- source: "Components",
241
- tier: "problem",
242
- });
280
+ notify.warn(
281
+ warning,
282
+ atTabPath(tab, {
283
+ key: `slots:${tab.documentPath ?? tab.id}`,
284
+ source: "Components",
285
+ tier: "problem",
286
+ }),
287
+ );
243
288
  }
244
289
  /* Popover correctness, beside the slot check and for the same reason it is here: a successful
245
290
  save is the one chokepoint every edit passes through, whichever surface made it. A render-time
@@ -311,12 +356,11 @@ export async function saveFile(tab: Tab | null = activeTab.value): Promise<boole
311
356
  notify.warn(
312
357
  `You have read access to this session — changes to ${tab.documentPath ?? "this document"} ` +
313
358
  `stay in your browser and cannot be saved.`,
314
- {
359
+ atTabPath(tab, {
315
360
  action: "file.save",
316
361
  key: `save.readOnly:${tab.documentPath ?? tab.id}`,
317
- ...(tab.documentPath === null ? {} : { path: tab.documentPath }),
318
362
  source: "Collaboration",
319
- },
363
+ }),
320
364
  );
321
365
  return false;
322
366
  }
@@ -327,11 +371,10 @@ export async function saveFile(tab: Tab | null = activeTab.value): Promise<boole
327
371
  if (!hasSerializableDocument(tab)) {
328
372
  notify.warn(
329
373
  `${tab.documentPath ?? "This file"} is a media file, so there is no document for Save to write.`,
330
- {
374
+ atTabPath(tab, {
331
375
  key: `save.notADocument:${tab.documentPath ?? tab.id}`,
332
- ...(tab.documentPath === null ? {} : { path: tab.documentPath }),
333
376
  source: "Save",
334
- },
377
+ }),
335
378
  );
336
379
  return false;
337
380
  }
@@ -381,13 +424,15 @@ export async function saveFile(tab: Tab | null = activeTab.value): Promise<boole
381
424
  notify.warn("This document has no save target — use Export.", { key: "save.noTarget" });
382
425
  } catch (error) {
383
426
  if (!(error instanceof Error && error.name === "AbortError")) {
384
- notify.error(`Could not save ${tab.documentPath ?? tab.id}.`, {
385
- action: "file.save",
386
- detail: errorMessage(error),
387
- key: `save:${tab.documentPath ?? tab.id}`,
388
- ...(tab.documentPath === null ? {} : { path: tab.documentPath }),
389
- source: "Save",
390
- });
427
+ notify.error(
428
+ `Could not save ${tab.documentPath ?? tab.id}.`,
429
+ atTabPath(tab, {
430
+ action: "file.save",
431
+ detail: errorMessage(error),
432
+ key: `save:${tab.documentPath ?? tab.id}`,
433
+ source: "Save",
434
+ }),
435
+ );
391
436
  }
392
437
  }
393
438
  return false;
@@ -418,13 +463,9 @@ export async function exportFile() {
418
463
  const description = format ? format.name : "Jx Component";
419
464
  const fallbackName = format ? `content${ext}` : "component.json";
420
465
 
421
- if ("showSaveFilePicker" in window) {
466
+ if (hasSaveFilePicker(window)) {
422
467
  const suggestedName = tab.documentPath ? tab.documentPath.split("/").pop() : fallbackName;
423
- const handle = await (
424
- window as unknown as {
425
- showSaveFilePicker: (options?: unknown) => Promise<FileSystemFileHandle>;
426
- }
427
- ).showSaveFilePicker({
468
+ const handle = await window.showSaveFilePicker({
428
469
  suggestedName,
429
470
  types: [{ accept: { [mimeType]: [ext] }, description }],
430
471
  });