@jxsuite/studio 6.1.2 → 8.0.1

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 (522) hide show
  1. package/README.md +5 -5
  2. package/dist/chunks/2020-5wtzce5g.js +4 -0
  3. package/dist/chunks/adapter-5tphg7xe.js +4 -0
  4. package/dist/chunks/add-new-flow-psym14sn.js +4 -0
  5. package/dist/chunks/cf-account-picker-haqazq5r.js +4 -0
  6. package/dist/chunks/cf-settings-h2wrh6hq.js +4 -0
  7. package/dist/chunks/{color-area-0fj8a4ga.js → color-area-6rbra5dy.js} +2 -2
  8. package/dist/chunks/{color-field-xch8kx2g.js → color-field-gy56c94g.js} +3 -3
  9. package/dist/chunks/color-m7r1vk54.js +4 -0
  10. package/dist/chunks/{color-slider-jxd8v350.js → color-slider-sqqdyf6c.js} +2 -2
  11. package/dist/chunks/{combobox-p0efwjke.js → combobox-72h32ejk.js} +3 -3
  12. package/dist/chunks/components-r22xca09.js +4 -0
  13. package/dist/chunks/content-source-mmppmcyg.js +4 -0
  14. package/dist/chunks/convert-to-repeater-he70fc2b.js +4 -0
  15. package/dist/chunks/editor-ccd9jwsa.js +4 -0
  16. package/dist/chunks/entry-commands-6qxz9e8p.js +4 -0
  17. package/dist/chunks/extension-sections-0egb88dm.js +4 -0
  18. package/dist/chunks/file-ops-qnab36ek.js +4 -0
  19. package/dist/chunks/files-rcn56qvm.js +4 -0
  20. package/dist/chunks/format-host-yxz0kjjs.js +4 -0
  21. package/dist/chunks/fs-events-d1qxq4z4.js +4 -0
  22. package/dist/chunks/grid-open-4z1g062t.js +4 -0
  23. package/dist/chunks/{grid-view-d2wq9ezg.js → grid-view-qg1b5eqx.js} +25 -25
  24. package/dist/chunks/{icons-pnfz478q.js → icons-63bjtbs7.js} +2 -2
  25. package/dist/chunks/{iframe-entry-jdcktez8.js → iframe-entry-2zggzajv.js} +4 -4
  26. package/dist/chunks/{iframe-entry-zap785cw.js → iframe-entry-76j8vwwx.js} +4 -4
  27. package/dist/chunks/iframe-entry-99nr5zfb.js +5 -0
  28. package/dist/chunks/{iframe-entry-x0ygf2yr.js → iframe-entry-pkgmcgp0.js} +4 -4
  29. package/dist/chunks/iframe-host-ssv7kyj0.js +4 -0
  30. package/dist/chunks/{index-zpjmzqfk.js → index-32eqzv1k.js} +3 -3
  31. package/dist/chunks/index-f0jn7nnp.js +4 -0
  32. package/dist/chunks/{javascript-m54mnwp6.js → javascript-pjp734q4.js} +3 -3
  33. package/dist/chunks/jsonMode-njkvsa02.js +13 -0
  34. package/dist/chunks/jx-validate-ttasrbh7.js +4 -0
  35. package/dist/chunks/layers-bgyr11tr.js +4 -0
  36. package/dist/chunks/layers-panel-7ez66zeq.js +4 -0
  37. package/dist/chunks/{listbox-e74qjyq6.js → listbox-c421cf8c.js} +2 -2
  38. package/dist/chunks/lit-html-v9gh687x.js +4 -0
  39. package/dist/chunks/media-library-sq468wts.js +4 -0
  40. package/dist/chunks/media-picker-shfe59pt.js +4 -0
  41. package/dist/chunks/{menu-n6yzswmy.js → menu-bdpzbfvs.js} +2 -2
  42. package/dist/chunks/{monaco-binding-7gx83x3r.js → monaco-binding-vq1hgnn0.js} +3 -3
  43. package/dist/chunks/monaco-buffer-3afgwvbp.js +4 -0
  44. package/dist/chunks/monaco-setup-xrd2cbkp.js +4 -0
  45. package/dist/chunks/notify-kzrjr3fv.js +4 -0
  46. package/dist/chunks/parse-v77xtwv7.js +4 -0
  47. package/dist/chunks/platform-7v4rtd7m.js +4 -0
  48. package/dist/chunks/popover-b89sfej7.js +4 -0
  49. package/dist/chunks/publish-panel-2z9gw8ee.js +4 -0
  50. package/dist/chunks/publish-quick-qx2455cj.js +4 -0
  51. package/dist/chunks/publish-workspace-wrc1dgwx.js +4 -0
  52. package/dist/chunks/quick-search-n0t0eqxb.js +4 -0
  53. package/dist/chunks/references-9547rg5s.js +4 -0
  54. package/dist/chunks/right-panel-wsgnr9pn.js +4 -0
  55. package/dist/chunks/source-control-kwktr9y6.js +4 -0
  56. package/dist/chunks/{split-n2gfpm5m.js → split-c9zfdgxf.js} +2 -2
  57. package/dist/chunks/studio-03ct6yfv.js +9 -0
  58. package/dist/chunks/studio-07rcfje3.js +5 -0
  59. package/dist/chunks/studio-3e54qh39.js +7 -0
  60. package/dist/chunks/studio-3exxhz5g.js +5 -0
  61. package/dist/chunks/studio-3q40e3rj.js +5 -0
  62. package/dist/chunks/studio-3qeh9t0h.js +6 -0
  63. package/dist/chunks/studio-3qqjeep5.js +6 -0
  64. package/dist/chunks/studio-43sd2mam.js +4 -0
  65. package/dist/chunks/studio-4hp8pkyt.js +5 -0
  66. package/dist/chunks/studio-58xg6j2q.js +9 -0
  67. package/dist/chunks/studio-5g0rprdt.js +7 -0
  68. package/dist/chunks/{studio-2j1cb95x.js → studio-6m380wp7.js} +4 -4
  69. package/dist/chunks/studio-80jyh1xh.js +4 -0
  70. package/dist/chunks/studio-83mk4bgg.js +9 -0
  71. package/dist/chunks/{studio-v0jzqdjm.js → studio-881z1xrx.js} +4 -4
  72. package/dist/chunks/studio-8m0y2qe7.js +5 -0
  73. package/dist/chunks/{studio-d81xfh1g.js → studio-8tr6cfrc.js} +4 -4
  74. package/dist/chunks/studio-8vcvfs7r.js +6 -0
  75. package/dist/chunks/studio-9tmebf0s.js +5 -0
  76. package/dist/chunks/studio-a2bvaptz.js +5 -0
  77. package/dist/chunks/studio-b7y5mr30.js +5 -0
  78. package/dist/chunks/studio-bavb31r0.js +5 -0
  79. package/dist/chunks/{studio-dgz8d0c1.js → studio-bbe4c90a.js} +4 -4
  80. package/dist/chunks/studio-c05wdnt3.js +5 -0
  81. package/dist/chunks/studio-ccv2svc6.js +5 -0
  82. package/dist/chunks/studio-d5grtb9e.js +16 -0
  83. package/dist/chunks/studio-df0baea9.js +5 -0
  84. package/dist/chunks/studio-dgk4dqtx.js +7 -0
  85. package/dist/chunks/studio-e1g64q43.js +5 -0
  86. package/dist/chunks/studio-ed1hp6zf.js +6 -0
  87. package/dist/chunks/studio-ed3xx40h.js +7 -0
  88. package/dist/chunks/studio-f14e1zdj.js +5 -0
  89. package/dist/chunks/studio-fbhxxc5f.js +5 -0
  90. package/dist/chunks/studio-fyh0jc96.js +5 -0
  91. package/dist/chunks/studio-gcxk20n7.js +5 -0
  92. package/dist/chunks/studio-jaaz8ce9.js +5 -0
  93. package/dist/chunks/{studio-90pq9km1.js → studio-jcnsdaep.js} +4 -4
  94. package/dist/chunks/studio-jsgf2q5k.js +7 -0
  95. package/dist/chunks/{studio-0q988f9w.js → studio-k3xxbagr.js} +3 -3
  96. package/dist/chunks/{studio-5ve6bbep.js → studio-k7evxqh4.js} +4 -4
  97. package/dist/chunks/studio-kt3pjkwd.js +5 -0
  98. package/dist/chunks/studio-m8g16bgz.js +5 -0
  99. package/dist/chunks/studio-mn0476ag.js +5 -0
  100. package/dist/chunks/studio-mx1smyq0.js +5 -0
  101. package/dist/chunks/studio-n0c5h6mf.js +5 -0
  102. package/dist/chunks/{studio-485046te.js → studio-nh6d342n.js} +7 -7
  103. package/dist/chunks/studio-nqxqf8p2.js +4 -0
  104. package/dist/chunks/studio-p4nrkvqr.js +5 -0
  105. package/dist/chunks/{studio-1fxkaaea.js → studio-pbxa5fpc.js} +4 -4
  106. package/dist/chunks/studio-pgmvdv7z.js +7 -0
  107. package/dist/chunks/studio-ph84avkf.js +5 -0
  108. package/dist/chunks/{studio-tq0rtfwj.js → studio-q0mvbaq6.js} +4 -4
  109. package/dist/chunks/studio-q0tccd43.js +5 -0
  110. package/dist/chunks/studio-q7rtx6zq.js +14 -0
  111. package/dist/chunks/{studio-a3g98wjr.js → studio-qgh13jtz.js} +4 -4
  112. package/dist/chunks/studio-qmn1xc24.js +5 -0
  113. package/dist/chunks/studio-qwdqwkj0.js +5 -0
  114. package/dist/chunks/studio-r5qy30vx.js +5 -0
  115. package/dist/chunks/studio-rj9eamf8.js +5 -0
  116. package/dist/chunks/studio-rptgsk96.js +5 -0
  117. package/dist/chunks/studio-rthyg42z.js +928 -0
  118. package/dist/chunks/studio-s19tp1xr.js +5 -0
  119. package/dist/chunks/studio-s32578pn.js +5 -0
  120. package/dist/chunks/studio-svb7yyve.js +5 -0
  121. package/dist/chunks/studio-syreb644.js +5 -0
  122. package/dist/chunks/studio-tf9jryb1.js +10 -0
  123. package/dist/chunks/studio-tfkznv39.js +5 -0
  124. package/dist/chunks/studio-tft6r105.js +5 -0
  125. package/dist/chunks/studio-txf2kysx.js +5 -0
  126. package/dist/chunks/studio-v8867ddv.js +12 -0
  127. package/dist/chunks/studio-vxq7fcvv.js +5 -0
  128. package/dist/chunks/studio-w4rz9hdk.js +5 -0
  129. package/dist/chunks/studio-wbcjbhy1.js +9 -0
  130. package/dist/chunks/studio-wc649wss.js +5 -0
  131. package/dist/chunks/studio-wys23mxz.js +5 -0
  132. package/dist/chunks/studio-x1x167jh.js +5 -0
  133. package/dist/chunks/studio-x9hsze61.js +5 -0
  134. package/dist/chunks/studio-y0n7yxea.js +9 -0
  135. package/dist/chunks/studio-y3ahhdpx.js +5 -0
  136. package/dist/chunks/studio-z97c48fk.js +5 -0
  137. package/dist/chunks/studio-zat62m0c.js +5 -0
  138. package/dist/chunks/studio-zcd2e7yz.js +5 -0
  139. package/dist/chunks/studio-zz9qe3rw.js +64 -0
  140. package/dist/chunks/toast-1gv0h53h.js +4 -0
  141. package/dist/chunks/{toast-host-ke5z4ng7.js → toast-host-1yg5ar8d.js} +3 -3
  142. package/dist/chunks/{tooltip-5v8ngwj2.js → tooltip-yypz28gq.js} +3 -3
  143. package/dist/chunks/{tree-mdq3v5cw.js → tree-9xa4hm7n.js} +2 -2
  144. package/dist/chunks/tsMode-epyfz1hv.js +14 -0
  145. package/dist/chunks/workspace-dz7nxfhv.js +4 -0
  146. package/dist/chunks/workspace-home-xsd7hkjw.js +4 -0
  147. package/dist/chunks/{ws-client-nze0k6mv.js → ws-client-bv552sfx.js} +3 -3
  148. package/dist/iframe-entry.js +20 -16
  149. package/dist/manifest.json +77 -63
  150. package/dist/studio.js +103 -104
  151. package/dist/workers/ts.worker.js +3 -3
  152. package/package.json +12 -11
  153. package/src/browse/component-examples.ts +272 -0
  154. package/src/browse/library-commands.ts +64 -104
  155. package/src/browse/library-layouts.ts +263 -49
  156. package/src/browse/library-model.ts +181 -133
  157. package/src/browse/library-pane.ts +738 -446
  158. package/src/browse/library-preview.ts +149 -19
  159. package/src/browse/library-source.ts +308 -41
  160. package/src/browse/preview-root.ts +78 -0
  161. package/src/browse/preview-style.ts +155 -0
  162. package/src/canvas/asset-refs.ts +16 -1
  163. package/src/canvas/canvas-live-render.ts +222 -30
  164. package/src/canvas/canvas-origin.ts +37 -12
  165. package/src/canvas/canvas-page-context.ts +112 -0
  166. package/src/canvas/canvas-render.ts +113 -107
  167. package/src/canvas/canvas-utils.ts +57 -12
  168. package/src/canvas/iframe-drop.ts +107 -0
  169. package/src/canvas/iframe-entry.ts +24 -12
  170. package/src/canvas/iframe-host.ts +134 -62
  171. package/src/canvas/iframe-keys.ts +11 -0
  172. package/src/canvas/iframe-patch.ts +8 -7
  173. package/src/canvas/iframe-protocol.ts +24 -5
  174. package/src/canvas/iframe-render.ts +48 -1
  175. package/src/canvas/iframe-subtree.ts +5 -2
  176. package/src/canvas/npm-modules.ts +22 -0
  177. package/src/canvas/preview-navigate.ts +16 -0
  178. package/src/canvas/repeater-sources.ts +200 -0
  179. package/src/canvas/stage-scrollbar.ts +5 -1
  180. package/src/canvas/surface-registry.ts +6 -0
  181. package/src/commands/app-commands.ts +16 -6
  182. package/src/commands/budget.ts +10 -8
  183. package/src/commands/context.ts +35 -10
  184. package/src/commands/defaults.ts +69 -45
  185. package/src/commands/keymap.ts +9 -0
  186. package/src/commands/levels.ts +11 -5
  187. package/src/commands/live-context.ts +2 -0
  188. package/src/commands/registry.ts +10 -1
  189. package/src/content/collection-match.ts +6 -5
  190. package/src/content/collection-pages.ts +412 -0
  191. package/src/content/content-collections.ts +270 -0
  192. package/src/content/entry-commands.ts +51 -60
  193. package/src/content/entry-editor.ts +111 -18
  194. package/src/content/entry-fields.ts +13 -1
  195. package/src/content/entry-inspector.ts +225 -0
  196. package/src/content/entry-model.ts +17 -3
  197. package/src/content/entry-open.ts +80 -0
  198. package/src/content/entry-summaries.ts +440 -0
  199. package/src/content/file-record-ids.ts +80 -0
  200. package/src/content/file-records.ts +125 -0
  201. package/src/content/page-summaries.ts +129 -0
  202. package/src/content/record-entry.ts +201 -0
  203. package/src/content/writing-banner.ts +177 -0
  204. package/src/create/add-new-commands.ts +185 -0
  205. package/src/create/add-new-flow.ts +1116 -0
  206. package/src/create/add-new.ts +1014 -0
  207. package/src/create/element-name.ts +121 -0
  208. package/src/editor/convert-to-component.ts +67 -43
  209. package/src/editor/file-drop-action.ts +9 -1
  210. package/src/editor/inline-edit-apply.ts +44 -0
  211. package/src/editor/inline-edit.ts +10 -0
  212. package/src/editor/insert-component.ts +91 -0
  213. package/src/editor/insert-zone-action.ts +13 -4
  214. package/src/editor/shortcuts.ts +44 -22
  215. package/src/editor/slash-menu.ts +51 -8
  216. package/src/files/components.ts +74 -10
  217. package/src/files/file-ops.ts +15 -5
  218. package/src/files/files.ts +164 -150
  219. package/src/files/fs-events.ts +18 -7
  220. package/src/files/media-meta.ts +95 -1
  221. package/src/files/media-types.ts +55 -0
  222. package/src/files/media-upload.ts +209 -52
  223. package/src/files/new-file-name.ts +156 -0
  224. package/src/files/serialize-document.ts +13 -3
  225. package/src/format/format-host.ts +15 -0
  226. package/src/github/github-auth.ts +2 -2
  227. package/src/github/github-publish.ts +4 -3
  228. package/src/grid/grid-controller.ts +5 -1
  229. package/src/grid/grid-open.ts +0 -27
  230. package/src/grid/grid-panel.ts +54 -2
  231. package/src/grid/grid-reveal.ts +34 -0
  232. package/src/grid/grid-source.ts +3 -25
  233. package/src/grid/grid-view.ts +31 -6
  234. package/src/grid/sources/content-source.ts +75 -7
  235. package/src/grid/tabulator-tables.d.ts +6 -0
  236. package/src/hosting/document.ts +2 -2
  237. package/src/i18n/i18n-commands.ts +2 -1
  238. package/src/media/media-commands.ts +181 -0
  239. package/src/media/media-library-model.ts +232 -0
  240. package/src/media/media-library.ts +1283 -0
  241. package/src/media/media-pane.ts +47 -19
  242. package/src/packages/pull-package-sync.ts +1 -1
  243. package/src/panels/activity-panel.ts +21 -5
  244. package/src/panels/ai-chat/chat-markdown.ts +8 -8
  245. package/src/panels/block-action-bar.ts +11 -11
  246. package/src/panels/canvas-dnd-bridge.ts +5 -2
  247. package/src/panels/chat-panel.ts +6 -6
  248. package/src/panels/collections-panel.ts +306 -0
  249. package/src/panels/component-preview.ts +35 -13
  250. package/src/panels/data-explorer.ts +1 -1
  251. package/src/panels/data-grid.ts +4 -0
  252. package/src/panels/dnd.ts +17 -30
  253. package/src/panels/elements-panel.ts +9 -2
  254. package/src/panels/empty-state.ts +2 -2
  255. package/src/panels/formula-workspace.ts +2 -3
  256. package/src/panels/frontmatter-fields.ts +9 -9
  257. package/src/panels/git-diff-open.ts +9 -5
  258. package/src/panels/head-panel.ts +369 -103
  259. package/src/panels/i18n-panel.ts +11 -10
  260. package/src/panels/imports-panel.ts +2 -0
  261. package/src/panels/jump-bar.ts +27 -2
  262. package/src/panels/layers-panel.ts +36 -30
  263. package/src/panels/left-panel.ts +22 -8
  264. package/src/panels/navigator-panels.ts +23 -11
  265. package/src/panels/pane-context.ts +111 -29
  266. package/src/panels/pane-grid.ts +6 -6
  267. package/src/panels/panel-registry.ts +14 -9
  268. package/src/panels/properties-panel.ts +7 -3
  269. package/src/panels/right-panel.ts +199 -43
  270. package/src/panels/seo-modal.ts +41 -108
  271. package/src/panels/settings-menu.ts +52 -50
  272. package/src/panels/settings-pane.ts +268 -162
  273. package/src/panels/signals-panel.ts +3 -4
  274. package/src/panels/site-model.ts +367 -0
  275. package/src/panels/site-panel.ts +638 -0
  276. package/src/panels/style-panel.ts +37 -3
  277. package/src/panels/stylebook-doc.ts +142 -10
  278. package/src/panels/stylebook-layers-panel.ts +39 -80
  279. package/src/panels/stylebook-panel.ts +47 -10
  280. package/src/panels/styles-panel.ts +327 -0
  281. package/src/panels/tab-strip.ts +109 -25
  282. package/src/platform.ts +1 -1
  283. package/src/platforms/cloud.ts +28 -1
  284. package/src/platforms/default-platform.ts +4 -4
  285. package/src/platforms/devserver.ts +51 -25
  286. package/src/publish/deploy-checklist.ts +12 -7
  287. package/src/publish/pages-service.ts +38 -14
  288. package/src/publish/publish-changes.ts +491 -0
  289. package/src/publish/publish-commands.ts +83 -18
  290. package/src/publish/publish-ids.ts +49 -0
  291. package/src/publish/publish-quick.ts +148 -0
  292. package/src/publish/publish-workspace.ts +926 -0
  293. package/src/publish/source-control.ts +907 -0
  294. package/src/services/automation.ts +16 -20
  295. package/src/services/cf-settings.ts +4 -4
  296. package/src/services/connection.ts +2 -2
  297. package/src/services/directory-picker.ts +2 -2
  298. package/src/services/references.ts +0 -0
  299. package/src/services/render-critic.ts +21 -2
  300. package/src/services/settings/definitions.ts +25 -14
  301. package/src/settings/contributed-section.ts +49 -10
  302. package/src/settings/css-vars-editor.ts +93 -27
  303. package/src/settings/developer-panels.ts +43 -0
  304. package/src/settings/extension-sections.ts +20 -2
  305. package/src/settings/general-settings.ts +91 -21
  306. package/src/settings/preferences-dialog.ts +8 -1
  307. package/src/settings/project-sections.ts +27 -10
  308. package/src/settings/schema-field-ui.ts +3 -1
  309. package/src/settings/section-registry.ts +116 -11
  310. package/src/settings/settings-dialog.ts +192 -0
  311. package/src/settings/settings-document.ts +182 -162
  312. package/src/shell.ts +467 -431
  313. package/src/site-context.ts +64 -10
  314. package/src/state.ts +28 -9
  315. package/src/studio.ts +194 -41
  316. package/src/style/project-styles.ts +34 -0
  317. package/src/style/stylebook-specimens.ts +89 -0
  318. package/src/style/stylebook-target.ts +26 -0
  319. package/src/style/tag-inheritance.ts +105 -0
  320. package/src/style/variables-focus.ts +61 -0
  321. package/src/style/variables-view.ts +235 -0
  322. package/src/surfaces/add-new.json +1482 -0
  323. package/src/surfaces/add-new.ts +325 -0
  324. package/src/surfaces/ai-chat.ts +2 -2
  325. package/src/surfaces/boot-failure.json +4 -3
  326. package/src/surfaces/bottom-dock.json +1 -1
  327. package/src/surfaces/canvas-stage.json +30 -19
  328. package/src/surfaces/canvas-stage.ts +5 -5
  329. package/src/surfaces/collections-panel.json +615 -0
  330. package/src/surfaces/collections-panel.ts +168 -0
  331. package/src/surfaces/commandbar.json +241 -96
  332. package/src/surfaces/commandbar.ts +134 -99
  333. package/src/surfaces/empty-state.json +1 -1
  334. package/src/surfaces/entry-editor.json +136 -0
  335. package/src/surfaces/entry-editor.ts +21 -0
  336. package/src/surfaces/entry-inspector.json +371 -0
  337. package/src/surfaces/entry-inspector.ts +143 -0
  338. package/src/surfaces/inspector-dock.json +162 -14
  339. package/src/surfaces/inspector-dock.ts +46 -19
  340. package/src/surfaces/library-pane.json +670 -437
  341. package/src/surfaces/library-pane.ts +94 -59
  342. package/src/surfaces/media-browser.json +62 -6
  343. package/src/surfaces/media-browser.ts +15 -1
  344. package/src/surfaces/media-field.json +1 -1
  345. package/src/surfaces/media-library.json +1586 -0
  346. package/src/surfaces/media-library.ts +282 -0
  347. package/src/surfaces/media-pick-dialog.json +71 -0
  348. package/src/surfaces/media-pick-dialog.ts +132 -0
  349. package/src/surfaces/menu.ts +18 -0
  350. package/src/surfaces/pane-context.json +144 -77
  351. package/src/surfaces/pane-context.ts +24 -0
  352. package/src/surfaces/panel-deploy-checklist.json +12 -6
  353. package/src/surfaces/panel-deploy-checklist.ts +3 -1
  354. package/src/surfaces/panel-page.json +72 -2
  355. package/src/surfaces/panel-page.ts +38 -7
  356. package/src/surfaces/panel-signals.json +1 -1
  357. package/src/surfaces/panel-signals.ts +5 -5
  358. package/src/surfaces/panel-stylebook-layers.json +71 -204
  359. package/src/surfaces/panel-stylebook-layers.ts +0 -21
  360. package/src/surfaces/preferences.json +120 -48
  361. package/src/surfaces/preferences.ts +8 -0
  362. package/src/surfaces/publish-quick.json +428 -0
  363. package/src/surfaces/publish-quick.ts +165 -0
  364. package/src/surfaces/publish-workspace.json +2892 -0
  365. package/src/surfaces/publish-workspace.ts +391 -0
  366. package/src/surfaces/rail.json +1 -159
  367. package/src/surfaces/rail.ts +18 -75
  368. package/src/surfaces/schema-form.json +10 -2
  369. package/src/surfaces/schema-form.ts +2 -0
  370. package/src/surfaces/seo.json +1 -1
  371. package/src/surfaces/settings-contributed.json +5 -5
  372. package/src/surfaces/settings-contributed.ts +18 -2
  373. package/src/surfaces/settings-css-vars.json +44 -36
  374. package/src/surfaces/settings-css-vars.ts +77 -4
  375. package/src/surfaces/settings-deploy.json +2 -15
  376. package/src/surfaces/settings-deploy.ts +1 -1
  377. package/src/surfaces/settings-dialog.json +51 -0
  378. package/src/surfaces/settings-dialog.ts +169 -0
  379. package/src/surfaces/settings-overview.json +3 -3
  380. package/src/surfaces/settings-overview.ts +9 -2
  381. package/src/surfaces/settings-pane.json +269 -46
  382. package/src/surfaces/settings-pane.ts +85 -24
  383. package/src/surfaces/shell.json +2 -1
  384. package/src/surfaces/site-panel.json +1029 -0
  385. package/src/surfaces/site-panel.ts +233 -0
  386. package/src/surfaces/slash-menu.json +63 -21
  387. package/src/surfaces/slash-menu.ts +23 -0
  388. package/src/surfaces/statusbar.ts +39 -15
  389. package/src/surfaces/styles-panel.json +735 -0
  390. package/src/surfaces/styles-panel.ts +195 -0
  391. package/src/surfaces/styles-variables.json +149 -0
  392. package/src/surfaces/styles-variables.ts +145 -0
  393. package/src/surfaces/tab-strip.json +4 -4
  394. package/src/surfaces/welcome.ts +1 -1
  395. package/src/surfaces/writing-banner.json +94 -0
  396. package/src/surfaces/writing-banner.ts +80 -0
  397. package/src/tabs/bare-repeaters.ts +101 -0
  398. package/src/tabs/tab.ts +47 -7
  399. package/src/types.ts +21 -26
  400. package/src/ui/form-controls.ts +47 -8
  401. package/src/ui/layers.ts +13 -0
  402. package/src/ui/media-picker.ts +284 -40
  403. package/src/ui/regions.ts +7 -6
  404. package/src/ui/schema-form.ts +259 -27
  405. package/src/ui/virtual-window.ts +26 -1
  406. package/src/utils/base64.ts +3 -3
  407. package/src/utils/edit-display.ts +322 -67
  408. package/src/utils/local-day.ts +18 -0
  409. package/src/workspace/pane-derive.ts +4 -4
  410. package/src/workspace/session.ts +28 -7
  411. package/src/workspace/tab-workspace.ts +137 -0
  412. package/src/workspace/workspace-back.ts +189 -0
  413. package/src/workspace/workspace-home.ts +133 -0
  414. package/src/workspace/workspace-ids.ts +129 -0
  415. package/src/workspace/workspace-switch.ts +372 -0
  416. package/src/workspace/workspace.ts +125 -11
  417. package/styles/shell-frame.css +30 -7
  418. package/styles/shell-frame.json +15 -3
  419. package/dist/chunks/2020-f2gkpy8d.js +0 -4
  420. package/dist/chunks/adapter-4912g339.js +0 -4
  421. package/dist/chunks/cf-account-picker-wccaenh6.js +0 -4
  422. package/dist/chunks/cf-settings-br6105tr.js +0 -4
  423. package/dist/chunks/color-28ksk2qm.js +0 -4
  424. package/dist/chunks/components-db68vsn7.js +0 -4
  425. package/dist/chunks/content-source-1449qesq.js +0 -4
  426. package/dist/chunks/convert-to-repeater-5h457egj.js +0 -4
  427. package/dist/chunks/editor-x9jn86a7.js +0 -4
  428. package/dist/chunks/entry-commands-may24cpt.js +0 -4
  429. package/dist/chunks/extension-sections-pvr14vxr.js +0 -4
  430. package/dist/chunks/file-ops-x9v54pcj.js +0 -4
  431. package/dist/chunks/files-j3knqf0s.js +0 -4
  432. package/dist/chunks/format-host-tyre2trb.js +0 -4
  433. package/dist/chunks/fs-events-64ma9htc.js +0 -4
  434. package/dist/chunks/git-panel-84ps5dex.js +0 -4
  435. package/dist/chunks/iframe-entry-fvbk89gm.js +0 -5
  436. package/dist/chunks/iframe-host-ck6vejt5.js +0 -4
  437. package/dist/chunks/index-b8xw3vy9.js +0 -4
  438. package/dist/chunks/jsonMode-w15cmrcm.js +0 -13
  439. package/dist/chunks/jx-validate-at3zn8wa.js +0 -4
  440. package/dist/chunks/layers-h4xp1pss.js +0 -4
  441. package/dist/chunks/layers-panel-m5hw1d7g.js +0 -4
  442. package/dist/chunks/lit-html-8t8t3dnr.js +0 -4
  443. package/dist/chunks/media-picker-2cweexc5.js +0 -4
  444. package/dist/chunks/menu-ynw13y0s.js +0 -4
  445. package/dist/chunks/monaco-buffer-qzxn567r.js +0 -4
  446. package/dist/chunks/monaco-setup-gxf6656g.js +0 -4
  447. package/dist/chunks/notify-cj5zbrs2.js +0 -4
  448. package/dist/chunks/parse-b7ddhr8m.js +0 -4
  449. package/dist/chunks/platform-3dfws9js.js +0 -4
  450. package/dist/chunks/popover-jt2gxmzs.js +0 -4
  451. package/dist/chunks/publish-panel-1gvxbj21.js +0 -4
  452. package/dist/chunks/quick-search-0dajrwqg.js +0 -4
  453. package/dist/chunks/references-49qgt404.js +0 -4
  454. package/dist/chunks/right-panel-jm1bwe53.js +0 -4
  455. package/dist/chunks/section-registry-6kwc4fky.js +0 -4
  456. package/dist/chunks/studio-0rt52vpq.js +0 -5
  457. package/dist/chunks/studio-18vgbhs5.js +0 -5
  458. package/dist/chunks/studio-24mccdrj.js +0 -5
  459. package/dist/chunks/studio-2eh507zk.js +0 -9
  460. package/dist/chunks/studio-2hcbccfv.js +0 -5
  461. package/dist/chunks/studio-2jbs3rxv.js +0 -5
  462. package/dist/chunks/studio-4h8k4dw9.js +0 -5
  463. package/dist/chunks/studio-5f6n18kx.js +0 -5
  464. package/dist/chunks/studio-5gthpky6.js +0 -5
  465. package/dist/chunks/studio-6ep3js6b.js +0 -9
  466. package/dist/chunks/studio-6wkmf3wz.js +0 -9
  467. package/dist/chunks/studio-841rf3cg.js +0 -5
  468. package/dist/chunks/studio-8gmwgheg.js +0 -5
  469. package/dist/chunks/studio-8qm0ep50.js +0 -5
  470. package/dist/chunks/studio-924p1tm1.js +0 -6
  471. package/dist/chunks/studio-a3shpz9y.js +0 -5
  472. package/dist/chunks/studio-amsbaaar.js +0 -6
  473. package/dist/chunks/studio-at5spjbc.js +0 -7
  474. package/dist/chunks/studio-b2xv945e.js +0 -5
  475. package/dist/chunks/studio-bn5nkafw.js +0 -5
  476. package/dist/chunks/studio-bw6q5xjf.js +0 -9
  477. package/dist/chunks/studio-c7q0t9x1.js +0 -5
  478. package/dist/chunks/studio-camnhc96.js +0 -6
  479. package/dist/chunks/studio-cffqvnzd.js +0 -5
  480. package/dist/chunks/studio-efvfrsf8.js +0 -5
  481. package/dist/chunks/studio-er30bpcx.js +0 -5
  482. package/dist/chunks/studio-ezscgr64.js +0 -5
  483. package/dist/chunks/studio-ffrrncvz.js +0 -5
  484. package/dist/chunks/studio-fpphgn8y.js +0 -10
  485. package/dist/chunks/studio-j1kvhdjk.js +0 -4
  486. package/dist/chunks/studio-j430n16g.js +0 -7
  487. package/dist/chunks/studio-jjkg84nj.js +0 -5
  488. package/dist/chunks/studio-m0q32ars.js +0 -4
  489. package/dist/chunks/studio-m6bdst11.js +0 -5
  490. package/dist/chunks/studio-nv2cptd4.js +0 -64
  491. package/dist/chunks/studio-nwfrc98n.js +0 -5
  492. package/dist/chunks/studio-p1wgvp7r.js +0 -5
  493. package/dist/chunks/studio-pag2wncm.js +0 -5
  494. package/dist/chunks/studio-qvanvzfd.js +0 -5
  495. package/dist/chunks/studio-qwa64kt7.js +0 -5
  496. package/dist/chunks/studio-rk1zc4x3.js +0 -928
  497. package/dist/chunks/studio-se2r3ptc.js +0 -5
  498. package/dist/chunks/studio-sg2a0dh0.js +0 -5
  499. package/dist/chunks/studio-tjzjhe68.js +0 -5
  500. package/dist/chunks/studio-v2w7q8r1.js +0 -7
  501. package/dist/chunks/studio-v5vjq1h0.js +0 -4
  502. package/dist/chunks/studio-v8aga3vp.js +0 -14
  503. package/dist/chunks/studio-vfcdrfmv.js +0 -5
  504. package/dist/chunks/studio-vmfkgdjf.js +0 -5
  505. package/dist/chunks/studio-wt1816jj.js +0 -5
  506. package/dist/chunks/studio-wz73q5kq.js +0 -4
  507. package/dist/chunks/studio-y04qtxvp.js +0 -7
  508. package/dist/chunks/studio-y21g8zzg.js +0 -5
  509. package/dist/chunks/studio-y64hrt8p.js +0 -12
  510. package/dist/chunks/studio-ynn90f58.js +0 -16
  511. package/dist/chunks/studio-yxz682sx.js +0 -5
  512. package/dist/chunks/studio-zp7snvkq.js +0 -5
  513. package/dist/chunks/studio-zq4z6tm6.js +0 -5
  514. package/dist/chunks/toast-c9fn075p.js +0 -4
  515. package/dist/chunks/tsMode-sf49ee9x.js +0 -14
  516. package/dist/chunks/workspace-jtfahpee.js +0 -4
  517. package/src/panels/frontmatter-panel.ts +0 -711
  518. package/src/panels/git-panel.ts +0 -1108
  519. package/src/surfaces/doc-header.json +0 -776
  520. package/src/surfaces/doc-header.ts +0 -222
  521. package/src/surfaces/git-panel.json +0 -1880
  522. package/src/surfaces/git-panel.ts +0 -264
@@ -528,15 +528,15 @@ function cycleRegion(direction: 1 | -1): void {
528
528
  }
529
529
  }
530
530
 
531
- // ─── Panel and inspector focus (⌘1–8, ⌘⇧1–4) ─────────────────────────────────
531
+ // ─── Panel and inspector focus (`panel.focus.*`, ⌘⇧1–4) ──────────────────────
532
532
 
533
533
  /**
534
534
  * Toggle-FOCUS, not toggle-visible.
535
535
  *
536
- * ⌘1 from the canvas reveals Files and puts the caret in it; ⌘1 again — with Files already focused
537
- * — collapses the dock and hands the caret back to the pane. The distinction matters because the
538
- * common case is "take me there", and a toggle-visible binding makes that a coin flip on whichever
539
- * panel happened to be showing.
536
+ * `Show Files` from the canvas reveals Files and puts the caret in it; running it again — with
537
+ * Files already focused — collapses the dock and hands the caret back to the pane. The distinction
538
+ * matters because the common case is "take me there", and a toggle-visible binding makes that a
539
+ * coin flip on whichever panel happened to be showing.
540
540
  *
541
541
  * Every panel this can be handed lives in the Navigator. The roster is the rail plus the rail-less
542
542
  * NAVIGATOR panels (`panels/navigator-panels.ts`), so a Bottom-dock record cannot reach here — the
@@ -546,14 +546,14 @@ function cycleRegion(direction: 1 | -1): void {
546
546
  */
547
547
  function focusPanel(panelId: string): void {
548
548
  /* THE DOOR. `shell.ts`'s `NAVIGATOR_PANEL_IDS` docstring says the command is what refuses an
549
- undeclared id, "at the one door a caller comes through" — this is that door for ⌘1–8, and it
549
+ undeclared id, "at the one door a caller comes through" — this is that door for `panel.focus.*`, and it
550
550
  was the only one of three doors that had no lock. `setActivityTab` now takes a
551
551
  `NavigatorPanelId`, so the roster's `string` has to be narrowed somewhere; narrowing HERE keeps
552
552
  `commands/defaults.ts` free of shell types, which is the property that lets three CI checks
553
553
  load the command set in a bare Bun process.
554
554
 
555
555
  A throw rather than a silent return: `tests/navigator-panels.test.ts` asserts the enum and the
556
- registry agree, so this is unreachable while that passes, and a chord that quietly does nothing
556
+ registry agree, so this is unreachable while that passes, and a command that quietly does nothing
557
557
  is precisely the defect this change is closing. */
558
558
  const id = requireNavigatorPanelId(panelId, "panel.focus");
559
559
  const alreadyThere =
@@ -990,7 +990,6 @@ export function initShortcuts(registry: CommandRegistry, stageContext: StageCont
990
990
  * scrolled with the wheel at all.
991
991
  *
992
992
  * - `grid` — a Tabulator viewport with its own virtual scroller.
993
- * - `manage` — the Library's ordinary overflow container.
994
993
  * - `settings` — the Project Settings document (`panels/settings-pane.ts`): an `overflow-y: auto`
995
994
  * section column, plus the Raw JSON `<pre>`'s own box inside it.
996
995
  * - `entry` — the Entry editor's `overflow-y: auto` form (`content/entry-editor.ts`).
@@ -1000,28 +999,30 @@ export function initShortcuts(registry: CommandRegistry, stageContext: StageCont
1000
999
  * there is no artboard here to zoom, either.
1001
1000
  */
1002
1001
  const SELF_SCROLLING_MODES: ReadonlySet<string> = new Set([
1002
+ /* The Content home — `browse/library-pane.ts`. Its own scrolling list, nothing to zoom. */
1003
+ "content-home",
1003
1004
  "entry",
1004
1005
  "grid",
1005
- "manage",
1006
1006
  /* `media` — the Media viewer's own scrolling column (`media/media-pane.ts`). There is no artboard
1007
1007
  here either: a wheel over an image is a scroll down the page it sits on, not a zoom. */
1008
1008
  "media",
1009
+ /* A workspace's home — `workspace/workspace-home.ts`. A centred column with nothing to zoom. */
1010
+ "media-home",
1009
1011
  "preview",
1010
- "settings",
1012
+ "publish-home",
1011
1013
  ]);
1012
1014
 
1013
1015
  /**
1014
1016
  * Can a box between `target` and the stage still answer this wheel itself?
1015
1017
  *
1016
- * Edit forwards every wheel to the page's scroller (see the branch below), which was the whole
1017
- * story while the stage cell held nothing but the page. It also holds the Document Header band now,
1018
- * a capped scroll container standing beside the scroller — and a `preventDefault` from an ANCESTOR
1019
- * listener cancels the native scroll of whatever the pointer is over, so a form taller than the cap
1020
- * could not be wheeled at all while the page scrolled underneath it instead. So the stage takes the
1021
- * wheel only when nothing inside it wants one: the walk stops at the stage cell, and it steps over
1022
- * the page's scroller because that box is what the forwarding is FOR — a wheel forwarded out of the
1023
- * cross-origin frame is dispatched on the cell itself and reaches no scrollable element on the way,
1024
- * and letting the browser scroll the page natively would chain out of it into the shell.
1018
+ * Edit forwards every wheel to the page's scroller (see the branch below). A `preventDefault` from
1019
+ * an ANCESTOR listener cancels the native scroll of whatever the pointer is over, so any box in the
1020
+ * stage cell that scrolls itself (a sideways strip, a capped list) would otherwise be unwheelable
1021
+ * while the page scrolled underneath it instead. So the stage takes the wheel only when nothing
1022
+ * inside it wants one: the walk stops at the stage cell, and it steps over the page's scroller
1023
+ * because that box is what the forwarding is FOR — a wheel forwarded out of the cross-origin frame
1024
+ * is dispatched on the cell itself and reaches no scrollable element on the way, and letting the
1025
+ * browser scroll the page natively would chain out of it into the shell.
1025
1026
  *
1026
1027
  * @param {WheelEvent} e
1027
1028
  * @param {HTMLElement} stage The cell the listener is on — the walk's upper bound, exclusive.
@@ -1146,8 +1147,8 @@ export function installStageGestures(surface: CanvasSurface): () => void {
1146
1147
  /* The stage's own scroller, by the `part` the stage document draws it with — the same
1147
1148
  attribute every other consumer of the canvas's chrome now addresses it by. */
1148
1149
  const sc = canvasWrap.querySelector<HTMLElement>('[part="edit-canvas"]');
1149
- /* …unless the pointer is over a box in this cell that scrolls itself — the Document Header
1150
- band, which Edit docks beside the scroller. See {@link wheelBelongsInside}. */
1150
+ /* …unless the pointer is over a box in this cell that scrolls itself. See
1151
+ {@link wheelBelongsInside}. */
1151
1152
  if (sc && !wheelBelongsInside(e, canvasWrap, sc)) {
1152
1153
  e.preventDefault();
1153
1154
  sc.scrollTop += e.deltaY;
@@ -1231,7 +1232,7 @@ export function installStageGestures(surface: CanvasSurface): () => void {
1231
1232
  canvasWrap.addEventListener(
1232
1233
  "click",
1233
1234
  (e: MouseEvent) => {
1234
- if (e.target !== canvasWrap && e.target !== surface.panzoomWrap) {
1235
+ if (!isStageBackground(e.target, canvasWrap, surface.panzoomWrap)) {
1235
1236
  return;
1236
1237
  }
1237
1238
  const tab = tabOfPane(surface.paneId);
@@ -1249,6 +1250,27 @@ export function installStageGestures(surface: CanvasSurface): () => void {
1249
1250
  };
1250
1251
  }
1251
1252
 
1253
+ /**
1254
+ * Whether a click landed on a stage's BACKGROUND rather than on anything drawn in it: the stage
1255
+ * itself, the pan/zoom surface, or the frame box every view draws its artboards in.
1256
+ *
1257
+ * The frame box is the one that matters in Edit. Edit has no pan/zoom surface; its frame is the
1258
+ * scrolling `[part="edit-canvas"]` that fills the stage around the page column, so the grey margin
1259
+ * beside the page IS that box, and a test that knew only the stage and the pan/zoom surface never
1260
+ * saw a click there. The frame is recognised by its static `data-slot`, which the stage document
1261
+ * stamps on it in every view.
1262
+ */
1263
+ export function isStageBackground(
1264
+ target: EventTarget | null,
1265
+ stage: Element,
1266
+ panzoom: Element | null,
1267
+ ): boolean {
1268
+ if (target === stage || (panzoom !== null && target === panzoom)) {
1269
+ return true;
1270
+ }
1271
+ return target instanceof HTMLElement && target.dataset.slot === "frame" && stage.contains(target);
1272
+ }
1273
+
1252
1274
  /** The context a stage answers with before the bootstrap has published a reader. */
1253
1275
  function stageless(): ShortcutPointerContext {
1254
1276
  return {
@@ -27,8 +27,14 @@
27
27
  */
28
28
 
29
29
  import { openSlashMenuSurface } from "../surfaces/slash-menu";
30
+ import { componentSlashCommands } from "./insert-component";
30
31
  import { rectOf } from "../utils/geometry";
31
- import type { SlashMenuRow, SlashMenuSurface, SlashMenuView } from "../surfaces/slash-menu";
32
+ import type {
33
+ SlashMenuGroup,
34
+ SlashMenuRow,
35
+ SlashMenuSurface,
36
+ SlashMenuView,
37
+ } from "../surfaces/slash-menu";
32
38
  import type { SlashCommand } from "./inline-edit";
33
39
 
34
40
  // ─── Commands ─────────────────────────────────────────────────────────────────
@@ -115,9 +121,43 @@ export function showSlashMenuAtRect(
115
121
  showAt(rect, filter, cbs);
116
122
  }
117
123
 
124
+ /**
125
+ * The standard set: the HTML elements, then the project's components under their own heading
126
+ * (§5.7), read from the registry at the moment the menu opens so a component saved a moment ago is
127
+ * on offer.
128
+ */
129
+ function standardCommands(): SlashCommand[] {
130
+ return [...SLASH_COMMANDS, ...componentSlashCommands()];
131
+ }
132
+
118
133
  /** Everything on offer right now: what the caller named, or the standard set. */
119
134
  function source(): SlashCommand[] {
120
- return callbacks?.commands || SLASH_COMMANDS;
135
+ return callbacks?.commands || standardCommands();
136
+ }
137
+
138
+ /**
139
+ * The rows as the panel groups them: consecutive offers with the same heading share a group, and
140
+ * the first, unheaded group is the HTML elements. Each row keeps its index in the FLAT list, which
141
+ * is the one the keyboard, the highlight and a pick count in.
142
+ */
143
+ function groupsOf(rows: readonly SlashMenuRow[], items: readonly SlashCommand[]): SlashMenuGroup[] {
144
+ const groups: SlashMenuGroup[] = [];
145
+ for (const [index, row] of rows.entries()) {
146
+ const label = items[index]?.group ?? "";
147
+ const last = groups.at(-1);
148
+ if (last?.label === label) {
149
+ last.rows.push(row);
150
+ } else {
151
+ groups.push({
152
+ hasLabel: label !== "",
153
+ key: label || "elements",
154
+ label,
155
+ name: label || "Elements",
156
+ rows: [row],
157
+ });
158
+ }
159
+ }
160
+ return groups;
121
161
  }
122
162
 
123
163
  /** The offers a filter leaves standing. An empty filter leaves all of them. */
@@ -132,14 +172,17 @@ function matching(filter: string, from: SlashCommand[]): SlashCommand[] {
132
172
  /** What the panel should be showing, from the state above. */
133
173
  function view(): SlashMenuView {
134
174
  const rect = _anchorRect;
175
+ const rows = filteredItems.map((cmd, index): SlashMenuRow => ({
176
+ description: cmd.description,
177
+ index,
178
+ key: cmd.tag,
179
+ label: cmd.label,
180
+ }));
135
181
  return {
136
182
  activeIndex: activeIdx,
137
183
  filter: filterText,
138
- rows: filteredItems.map((cmd): SlashMenuRow => ({
139
- description: cmd.description,
140
- key: cmd.tag,
141
- label: cmd.label,
142
- })),
184
+ groups: groupsOf(rows, filteredItems),
185
+ rows,
143
186
  showFilter,
144
187
  // The gap between the anchor and the panel is the `--jx-popover-offset` token the element
145
188
  // Already applies, so this is the anchor's own edge rather than an edge plus a guess.
@@ -163,7 +206,7 @@ function showAt(
163
206
  callbacks = cbs;
164
207
  _anchorRect = rect;
165
208
 
166
- filteredItems = matching(filter, cbs.commands || SLASH_COMMANDS);
209
+ filteredItems = matching(filter, cbs.commands || standardCommands());
167
210
 
168
211
  if (filteredItems.length === 0 && !cbs.showFilter) {
169
212
  dismissSlashMenu();
@@ -18,14 +18,39 @@ export interface ComponentEntry extends Omit<ComponentMeta, "path"> {
18
18
  export let componentRegistry: ComponentEntry[] = [];
19
19
  export let _componentRegistryLoaded = false;
20
20
 
21
- export async function loadComponentRegistry() {
22
- try {
23
- const platform = getPlatform();
24
- componentRegistry = await platform.discoverComponents(projectState?.projectRoot || undefined);
25
- _componentRegistryLoaded = true;
26
- } catch {
21
+ /** Bumped by every load, so only the newest scan may write the registry. */
22
+ let _generation = 0;
23
+ /** The newest scan, which a superseded one waits on so its caller still sees a loaded registry. */
24
+ let _newest: Promise<void> = Promise.resolve();
25
+
26
+ /**
27
+ * Scan the project's components into the registry.
28
+ *
29
+ * Scans can overlap (the project-open path and the Site panel each start one), and a scan bound to
30
+ * the project being closed may resolve after the next project's. So the newest scan wins: an older
31
+ * one discards what it read and resolves when the newest has written the registry.
32
+ */
33
+ export function loadComponentRegistry(): Promise<void> {
34
+ _generation += 1;
35
+ const generation = _generation;
36
+ const scan = (async () => {
37
+ let found: ComponentEntry[] | null = null;
38
+ try {
39
+ found = await getPlatform().discoverComponents(projectState?.projectRoot || undefined);
40
+ } catch {
41
+ found = null;
42
+ }
43
+ if (generation !== _generation) {
44
+ await _newest;
45
+ return;
46
+ }
47
+ if (found) {
48
+ componentRegistry = found;
49
+ }
27
50
  _componentRegistryLoaded = true;
28
- }
51
+ })();
52
+ _newest = scan;
53
+ return scan;
29
54
  }
30
55
 
31
56
  /**
@@ -107,18 +132,57 @@ export function buildComponentInstance(comp: ComponentEntry): JxMutableNode {
107
132
  }
108
133
 
109
134
  /**
135
+ * Walk `parts` the way a path resolver does: `.` stays, `..` climbs, the rest descend. A climb
136
+ * above the start is kept as a leading `..`, which is how a project-relative path names a file
137
+ * outside the project.
138
+ */
139
+ function normalizeParts(parts: readonly string[]): string[] {
140
+ const out: string[] = [];
141
+ for (const part of parts) {
142
+ if (part === ".") {
143
+ continue;
144
+ }
145
+ if (part === ".." && out.length > 0 && out.at(-1) !== "..") {
146
+ out.pop();
147
+ } else {
148
+ out.push(part);
149
+ }
150
+ }
151
+ return out;
152
+ }
153
+
154
+ /**
155
+ * The `$ref` a document at `fromDocPath` writes to reach `toCompPath`, both project-relative.
156
+ *
157
+ * A document can sit OUTSIDE the project: jxsuite.com's docs collection is `../../docs`, so an
158
+ * entry is `../../docs/extending/x.md`. Climbing from there back into the project needs the names
159
+ * of the folders the project sits in, which a project-relative path does not carry; counting its
160
+ * segments gave five `../` and a ref into a folder that does not exist, so every component a docs
161
+ * layout imports failed to register on the canvas. Such a path is resolved against `projectRoot`
162
+ * (the open project's, by default), the way the browser resolves the ref.
163
+ *
110
164
  * @param {string | null} fromDocPath
111
165
  * @param {string} toCompPath
166
+ * @param {string | null} [projectRoot]
112
167
  */
113
- export function computeRelativePath(fromDocPath: string | null, toCompPath: string) {
168
+ export function computeRelativePath(
169
+ fromDocPath: string | null,
170
+ toCompPath: string,
171
+ projectRoot: string | null = projectState?.projectRoot ?? null,
172
+ ) {
114
173
  if (!fromDocPath) {
115
174
  return `./${toCompPath}`;
116
175
  }
117
176
  const from = fromDocPath.replaceAll("\\", "/");
118
177
  const to = toCompPath.replaceAll("\\", "/");
119
178
  const fromDir = from.includes("/") ? from.slice(0, from.lastIndexOf("/")) : "";
120
- const fromParts = fromDir.split("/").filter(Boolean);
121
- const toParts = to.split("/").filter(Boolean);
179
+ let fromParts = normalizeParts(fromDir.split("/").filter(Boolean));
180
+ let toParts = normalizeParts(to.split("/").filter(Boolean));
181
+ if (fromParts[0] === ".." && projectRoot) {
182
+ const rootParts = projectRoot.replaceAll("\\", "/").split("/").filter(Boolean);
183
+ fromParts = normalizeParts([...rootParts, ...fromParts]);
184
+ toParts = normalizeParts([...rootParts, ...toParts]);
185
+ }
122
186
  let common = 0;
123
187
  while (
124
188
  common < fromParts.length &&
@@ -105,6 +105,8 @@ export async function parseSourceForPath(path: string, source: string) {
105
105
  export const parseCollabSource: CollabParser = async (tab, text) => {
106
106
  if (tab.documentPath && formatForPath(tab.documentPath)) {
107
107
  const parsed = await parseSourceForPath(tab.documentPath, text);
108
+ // The peer's text is what this client saves against now, as `layout` is for a JSON file.
109
+ tab.doc.source = text;
108
110
  return { document: parsed.document, frontmatter: parsed.frontmatter };
109
111
  }
110
112
  const parsed = parseJsonDocument(text);
@@ -195,6 +197,7 @@ export async function openFile() {
195
197
  fileHandle: handle,
196
198
  frontmatter,
197
199
  id: name,
200
+ source: text,
198
201
  sourceFormat: format.name,
199
202
  });
200
203
  } else if (name.endsWith(".json")) {
@@ -254,26 +257,27 @@ export async function openFile() {
254
257
  * **By injection**, the idiom `setSurfaceTeardown` and `setDiffRepaint` already use. The one
255
258
  * listener today is source control: a comparison is two texts read once, so it does not notice a
256
259
  * save on its own, and the file an author is most likely to save is the one they are reviewing. A
257
- * direct import would put `panels/git-panel.ts` into this module's graph, which is a load error for
258
- * every suite that mocks the canvas host out from under it.
260
+ * direct import would put `publish/source-control.ts` and the comparison opener behind it into this
261
+ * module's graph, which is a load error for every suite that mocks the canvas host out from under
262
+ * it.
259
263
  *
260
264
  * The listener is handed the document as well as the path, for the second listener: the live chrome
261
265
  * lane (`services/live-surfaces.ts`) re-mounts a saved surface from the document the tab just wrote
262
266
  * rather than reading the file back, which is what keeps the shipped app from ever reading its
263
267
  * chrome from disk.
264
268
  */
265
- let _onDocumentSaved: (path: string | null, doc: JxMutableNode) => void = () => {};
269
+ let _onDocumentSaved: (path: string | null, doc: JxMutableNode, tab: Tab) => void = () => {};
266
270
 
267
271
  /** Register the save listener. Called once, from the bootstrap. */
268
272
  export function setDocumentSavedListener(
269
- listener: (path: string | null, doc: JxMutableNode) => void,
273
+ listener: (path: string | null, doc: JxMutableNode, tab: Tab) => void,
270
274
  ): void {
271
275
  _onDocumentSaved = listener;
272
276
  }
273
277
 
274
278
  function reportSaved(tab: Tab) {
275
279
  const doc = tab.doc.document;
276
- _onDocumentSaved(tab.documentPath, doc);
280
+ _onDocumentSaved(tab.documentPath, doc, tab);
277
281
  const warning =
278
282
  isString(doc.tagName) && doc.tagName.includes("-") ? validateComponentSlots(doc) : null;
279
283
  if (warning) {
@@ -398,6 +402,9 @@ export async function saveFile(tab: Tab | null = activeTab.value): Promise<boole
398
402
  const platform = getPlatform();
399
403
  await platform.writeFile(tab.documentPath, output);
400
404
  tab.doc.dirty = false;
405
+ if (tab.doc.sourceFormat !== null) {
406
+ tab.doc.source = output;
407
+ }
401
408
  /* Retract the unsaved bytes NOW rather than a debounce later. The write also reaches the
402
409
  backend's filesystem watcher, and a live preview coalesces both into one reload only if
403
410
  they arrive together — otherwise a save costs two. */
@@ -418,6 +425,9 @@ export async function saveFile(tab: Tab | null = activeTab.value): Promise<boole
418
425
  await writable.write(output);
419
426
  await writable.close();
420
427
  tab.doc.dirty = false;
428
+ if (tab.doc.sourceFormat !== null) {
429
+ tab.doc.source = output;
430
+ }
421
431
  reportSaved(tab);
422
432
  return true;
423
433
  }