@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
@@ -0,0 +1,78 @@
1
+ /// <reference lib="dom" />
2
+ /**
3
+ * A live preview's root: the shadow root an island shows a rendered document or component in (the
4
+ * Site panel's component cards and the Content home's cards, `specs/studio.md` §5.7 and §9.1.2).
5
+ *
6
+ * A module of its own, apart from `library-preview.ts`, because what a preview IS (the render, the
7
+ * cache, the visibility gate) and where it is SHOWN are separate questions, and the second is plain
8
+ * DOM that every surface hosting a preview answers the same way.
9
+ *
10
+ * @docs studio/interface
11
+ */
12
+
13
+ import { isFunction } from "@jxsuite/schema/guards";
14
+ import { projectPreviewSheets } from "./preview-style";
15
+
16
+ /**
17
+ * The one rule a preview needs that its box cannot give it from outside: an image whose prop
18
+ * defaults to nothing draws a broken-image icon and its alt text, which in a thumbnail is noise,
19
+ * not the component. Everything else about the box (its size, its scale, its light surface) is the
20
+ * surface document's, on the island itself.
21
+ */
22
+ const PREVIEW_RULES = 'img:not([src]), img[src=""] { visibility: hidden; }';
23
+
24
+ const _previewSheets = new WeakMap<Document, CSSStyleSheet>();
25
+
26
+ /** The preview rules as a sheet of `doc`'s own (a constructed sheet is adoptable only there). */
27
+ function previewSheetFor(doc: Document): CSSStyleSheet | null {
28
+ const known = _previewSheets.get(doc);
29
+ if (known) {
30
+ return known;
31
+ }
32
+ const Sheet = doc.defaultView?.CSSStyleSheet;
33
+ if (!isFunction(Sheet)) {
34
+ return null;
35
+ }
36
+ const sheet = new Sheet();
37
+ sheet.replaceSync(PREVIEW_RULES);
38
+ _previewSheets.set(doc, sheet);
39
+ return sheet;
40
+ }
41
+
42
+ /**
43
+ * Show a rendered preview in its island, inside a shadow root of the island's own.
44
+ *
45
+ * **A preview is a page's part, rendered in Studio's window, and the shadow root is what keeps it a
46
+ * guest there.** It used to be appended straight into the island, so a component's own ids joined
47
+ * Studio's: Elite Equipment Rental's `<h2 id="equipment">` was the document's `#equipment`, an
48
+ * `aria-labelledby` or a fragment link anywhere in the chrome could resolve to a thumbnail, and two
49
+ * cards of one component made the id a duplicate. Ids, `getElementById` and Studio's linked
50
+ * stylesheets all stop at the root's boundary; inherited properties (the island's text colour and
51
+ * size) still cross it, which is what the light document surface relies on.
52
+ *
53
+ * The runtime's rules are not lost: the Jx runtime writes every element's style into sheets it
54
+ * adopts on the document (`openSink` in `@jxsuite/runtime`), keyed on each element's `data-jx`
55
+ * hash, and those sheets are adopted into the root too, so a component draws as it did. They are
56
+ * read at each fill, so a sheet the runtime opened since the last one is included.
57
+ *
58
+ * @param slot The island the surface document drew empty.
59
+ * @param rendered The preview, from `previewFor` (`library-preview.ts`).
60
+ */
61
+ export function mountPreview(slot: HTMLElement, rendered: HTMLElement): void {
62
+ const root = slot.shadowRoot ?? slot.attachShadow({ mode: "open" });
63
+ const own = previewSheetFor(slot.ownerDocument);
64
+ /* The project's sheets first, the runtime's after them: on the page, the site's stylesheet is
65
+ in the document and the runtime's rules are adopted after it, so a component's own rule wins
66
+ over a site rule of equal specificity there, and must here. */
67
+ root.adoptedStyleSheets = [
68
+ ...projectPreviewSheets(),
69
+ ...slot.ownerDocument.adoptedStyleSheets,
70
+ ...(own ? [own] : []),
71
+ ];
72
+ root.replaceChildren(rendered);
73
+ }
74
+
75
+ /** The preview an island is showing, or null while it shows none. */
76
+ export function shownPreview(slot: Element): Element | null {
77
+ return slot.shadowRoot?.firstElementChild ?? null;
78
+ }
@@ -0,0 +1,155 @@
1
+ /// <reference lib="dom" />
2
+ /**
3
+ * The project's stylesheet, as a live preview's root adopts it.
4
+ *
5
+ * A component card or a page card in the Site panel and the Content home draws a part of a page in
6
+ * Studio's own window, inside a shadow root of its box (`preview-root.ts`). It drew that part with
7
+ * the runtime's rules alone: none of the site's tokens, typography or linked stylesheets, which is
8
+ * where most of a component's look lives, so a museum card or a site header drew as an empty white
9
+ * box. A page draws with two things this module supplies: `project.json`'s `style`, emitted by the
10
+ * builder the canvas and the build share (`@jxsuite/site/site-style`), and the stylesheets the
11
+ * project's `$head` links.
12
+ *
13
+ * Both address the page's `:root` and `body`, which a shadow root does not have: inside it the box
14
+ * the preview is drawn in is `:host`. The project block is emitted with `:host` as its root and
15
+ * body, and a linked sheet's own `:root`, `html` and `body` selectors are re-pointed at `:host`
16
+ * through the CSSOM, rule by rule, so a site's custom properties and body typography reach the
17
+ * preview the way they reach the page. A web font is the one thing that does not: `@font-face` is
18
+ * not honoured inside a shadow root, so a preview draws in the fallback face.
19
+ *
20
+ * Built once per project configuration and shared by every preview of it.
21
+ *
22
+ * @docs studio/interface
23
+ */
24
+
25
+ import { buildSiteStyleCSS } from "@jxsuite/site/site-style";
26
+ import { isFunction, isNonNullObject, isString } from "@jxsuite/schema/guards";
27
+ import { previewAssetSrcFor } from "../canvas/asset-refs";
28
+ import { projectState } from "../store";
29
+ import type { JxHeadEntry, ProjectConfig } from "@jxsuite/schema/types";
30
+
31
+ /** The preview box, as a project sheet names the page's root and body inside a shadow root. */
32
+ const HOST_ROOTS = { body: ":host", root: ":host" };
33
+
34
+ /** Every sheet built for a configuration, settled or in flight. */
35
+ const _sheets = new WeakMap<ProjectConfig, Promise<CSSStyleSheet[]>>();
36
+ /** The settled answers, for the synchronous mount. */
37
+ const _settled = new WeakMap<ProjectConfig, CSSStyleSheet[]>();
38
+
39
+ /** A selector of the page's root or body re-pointed at the preview box; `null` when unchanged. */
40
+ export function hostSelector(selector: string): string | null {
41
+ const rewritten = selector
42
+ .replaceAll(/:root\b/g, ":host")
43
+ .replaceAll(/(^|[\s,>+~(])(?:html|body)(?![\w-])/g, "$1:host")
44
+ .replaceAll(/:host(?:\s*>?\s*:host)+/g, ":host");
45
+ return rewritten === selector ? null : rewritten;
46
+ }
47
+
48
+ /** What holds rules: a sheet, or a grouping rule (`@media`, `@supports`, a nesting style rule). */
49
+ type RuleContainer = CSSStyleSheet | CSSGroupingRule;
50
+
51
+ /**
52
+ * Re-point every style rule under `container` (at any depth of grouping or nesting) at the box.
53
+ *
54
+ * A rule is REPLACED by its own text under the new selector rather than assigned a new
55
+ * `selectorText`: the assignment is settable in browsers but not in every CSSOM, and replacing it
56
+ * keeps its declarations, its nested rules and its place in source order either way. A rewritten
57
+ * selector the engine cannot parse keeps the rule as it was.
58
+ */
59
+ function rehostRules(container: RuleContainer): void {
60
+ for (let index = 0; index < container.cssRules.length; index += 1) {
61
+ const rule = container.cssRules[index];
62
+ const rewritten = rule instanceof CSSStyleRule ? hostSelector(rule.selectorText) : null;
63
+ if (rule instanceof CSSStyleRule && rewritten !== null) {
64
+ const text = `${rewritten}${rule.cssText.slice(rule.cssText.indexOf("{"))}`;
65
+ try {
66
+ container.insertRule(text, index);
67
+ container.deleteRule(index + 1);
68
+ } catch {
69
+ // Unparseable under the new selector: the original stays.
70
+ }
71
+ }
72
+ const current = container.cssRules[index];
73
+ if (current instanceof CSSGroupingRule) {
74
+ rehostRules(current);
75
+ }
76
+ }
77
+ }
78
+
79
+ /** The stylesheet `href`s the project's `$head` links, in order. */
80
+ export function headStylesheetHrefs(head: readonly JxHeadEntry[] | undefined): string[] {
81
+ const hrefs: string[] = [];
82
+ for (const entry of head ?? []) {
83
+ if (!isNonNullObject(entry) || String(entry.tagName).toLowerCase() !== "link") {
84
+ continue;
85
+ }
86
+ const { href, rel } = entry.attributes ?? {};
87
+ if (isString(href) && href !== "" && isString(rel) && rel.split(/\s+/).includes("stylesheet")) {
88
+ hrefs.push(href);
89
+ }
90
+ }
91
+ return hrefs;
92
+ }
93
+
94
+ /** One linked stylesheet as a preview sheet, or null when it cannot be read. */
95
+ async function linkedSheet(href: string): Promise<CSSStyleSheet | null> {
96
+ const url = previewAssetSrcFor(href, "project.json");
97
+ try {
98
+ const response = await fetch(url);
99
+ if (!response.ok) {
100
+ return null;
101
+ }
102
+ const absolute = new URL(url, location.href).href;
103
+ // Its own URL as the base, so the sheet's relative `url()` references resolve as they do on the
104
+ // Page and not against Studio's address.
105
+ const sheet = new CSSStyleSheet({ baseURL: absolute });
106
+ sheet.replaceSync(await response.text());
107
+ rehostRules(sheet);
108
+ return sheet;
109
+ } catch {
110
+ return null;
111
+ }
112
+ }
113
+
114
+ /** Build every sheet for one configuration: the linked sheets in `$head` order, then the block. */
115
+ async function buildSheets(config: ProjectConfig): Promise<CSSStyleSheet[]> {
116
+ const linked = await Promise.all(
117
+ headStylesheetHrefs(config.$head).map((href) => linkedSheet(href)),
118
+ );
119
+ const sheets = linked.filter((sheet): sheet is CSSStyleSheet => sheet !== null);
120
+ const { style } = config;
121
+ if (isNonNullObject(style)) {
122
+ const block = new CSSStyleSheet();
123
+ block.replaceSync(
124
+ buildSiteStyleCSS(style, config.$media ?? {}, (value) => value, undefined, HOST_ROOTS),
125
+ );
126
+ sheets.push(block);
127
+ }
128
+ return sheets;
129
+ }
130
+
131
+ /**
132
+ * Read the open project's preview sheets, building them on first ask. A preview awaits this before
133
+ * it is shown, so {@link projectPreviewSheets} has the answer by the time a box is filled.
134
+ */
135
+ export function loadProjectPreviewSheets(): Promise<CSSStyleSheet[]> {
136
+ const config = projectState?.projectConfig;
137
+ if (!config || !isFunction(globalThis.CSSStyleSheet)) {
138
+ return Promise.resolve([]);
139
+ }
140
+ let pending = _sheets.get(config);
141
+ if (!pending) {
142
+ pending = buildSheets(config).then((sheets) => {
143
+ _settled.set(config, sheets);
144
+ return sheets;
145
+ });
146
+ _sheets.set(config, pending);
147
+ }
148
+ return pending;
149
+ }
150
+
151
+ /** The open project's preview sheets as far as they are built: none until the first read lands. */
152
+ export function projectPreviewSheets(): readonly CSSStyleSheet[] {
153
+ const config = projectState?.projectConfig;
154
+ return (config && _settled.get(config)) ?? [];
155
+ }
@@ -97,10 +97,25 @@ export function assetContextFor(
97
97
  * @returns {string} A src the parent realm can load
98
98
  */
99
99
  export function previewAssetSrc(value: string): string {
100
+ return previewAssetSrcFor(value, activeTab.value?.documentPath);
101
+ }
102
+
103
+ /**
104
+ * {@link previewAssetSrc} for a value authored in a NAMED document rather than the focused one.
105
+ *
106
+ * A project-level surface (Project Settings › Overview previewing the `$head` favicon of
107
+ * `project.json`) is about the project whatever tab is in front, so it names its document instead
108
+ * of asking which one has the focus.
109
+ *
110
+ * @param {string} value - The reference as the author wrote it
111
+ * @param {string | null | undefined} documentPath - Project-relative path of the authoring document
112
+ * @returns {string} A src the parent realm can load
113
+ */
114
+ export function previewAssetSrcFor(value: string, documentPath: string | null | undefined): string {
100
115
  if (!value) {
101
116
  return value;
102
117
  }
103
- const ctx = assetContextFor(activeTab.value?.documentPath, hostAssetDeclarations());
118
+ const ctx = assetContextFor(documentPath, hostAssetDeclarations());
104
119
  const versioned = ctx && { ...ctx, versions: assetVersionsSnapshot() };
105
120
  return loopbackAssetSrc(resolveAssetRef(value, versioned) ?? value);
106
121
  }
@@ -7,13 +7,20 @@
7
7
  */
8
8
 
9
9
  import { projectState, stripEventHandlers } from "../store";
10
- import { displayTagName, isMappedArray, isNonNullObject, isString } from "@jxsuite/schema/guards";
10
+ import {
11
+ displayTagName,
12
+ isJsonObject,
13
+ isMappedArray,
14
+ isNonNullObject,
15
+ isString,
16
+ } from "@jxsuite/schema/guards";
11
17
  import { canvasModeOfTab } from "./canvas-surface";
12
18
  import { toRaw } from "../reactivity";
13
19
  import {
14
20
  distributePageIntoLayout,
15
21
  getEffectiveElements,
16
22
  getEffectiveHead,
23
+ extensionClassPaths,
17
24
  getEffectiveImports,
18
25
  getEffectiveLayoutPath,
19
26
  getEffectiveMedia,
@@ -21,6 +28,9 @@ import {
21
28
  } from "../site-context";
22
29
  import { documentBase } from "./canvas-origin";
23
30
  import { componentRegistry, computeRelativePath } from "../files/components";
31
+ import { collectUsedTags } from "@jxsuite/schema/component-meta";
32
+ import { injectContext } from "@jxsuite/site/context";
33
+ import { documentUrlPattern } from "@jxsuite/site/routes";
24
34
  import { prepareForEditMode } from "../utils/edit-display";
25
35
  import {
26
36
  paramBoundStateKeys,
@@ -29,9 +39,22 @@ import {
29
39
  } from "../page-params";
30
40
  import { isComponentDoc, substitutePreviewProps } from "../component-props";
31
41
  import { assetContextFor, hostAssetDeclarations } from "./asset-refs";
42
+ import { collectionForFile } from "../content/collection-match";
43
+ import { writesEntryOnCanvas } from "../content/content-collections";
44
+ import { collectionPage, entryLayoutPath, entryLocale } from "../content/collection-pages";
45
+ import { pageContextFor } from "./canvas-page-context";
46
+ import { loadExtensions } from "../format/format-host";
47
+ import { bakeRepeaterSources, extensionClassKeys, repeaterSourceKeys } from "./repeater-sources";
32
48
 
33
49
  import type { AssetContext } from "./asset-refs";
34
- import type { JxElement, JxMutableNode, JxPath, JxStyle } from "@jxsuite/schema/types";
50
+ import type {
51
+ JsonValue,
52
+ JxDocument,
53
+ JxElement,
54
+ JxMutableNode,
55
+ JxPath,
56
+ JxStyle,
57
+ } from "@jxsuite/schema/types";
35
58
  import type { ComponentEntry } from "../files/components.js";
36
59
  import type { LayoutMarker } from "./path-mapping";
37
60
  import type { Tab } from "../tabs/tab";
@@ -112,6 +135,115 @@ export function markLayoutNodes(node: JxMutableNode, file: string, path: JxPath
112
135
  }
113
136
  }
114
137
 
138
+ /**
139
+ * A layout as the canvas wraps a document in it: read, its nodes marked with the file they come
140
+ * from, and itself wrapped in the layout ITS `$layout` names, all the way up.
141
+ *
142
+ * A layout can sit in another layout (`layouts/post.json` → `layouts/base.json`), and the build
143
+ * resolves the whole chain (`@jxsuite/site` `resolveLayout`), so a page drawn in the inner layout
144
+ * alone lost the outer one's nav and footer on the canvas and nowhere else. Each level is merged
145
+ * the way the build merges it, and each node keeps the marker of the file it was authored in, so
146
+ * opening a locked node still opens the layout that holds it. A cycle stops at the first repeat
147
+ * rather than recursing forever, and a parent that cannot be read leaves the chain where it is.
148
+ *
149
+ * @param {string} layoutPath - The `$layout` reference, project-relative as authored
150
+ * @param {Set<string>} [seen] - Files already on the chain
151
+ * @returns {Promise<JxMutableNode | null>} The merged, marked layout, or null when unreadable
152
+ */
153
+ export async function resolveCanvasLayout(
154
+ layoutPath: string,
155
+ seen = new Set<string>(),
156
+ ): Promise<JxMutableNode | null> {
157
+ const file = layoutPath.replace(/^\.\//, "");
158
+ if (seen.has(file)) {
159
+ return null;
160
+ }
161
+ seen.add(file);
162
+ const layoutDoc = await resolveLayoutDoc(layoutPath);
163
+ if (!layoutDoc) {
164
+ return null;
165
+ }
166
+ // The SAME normalization resolveLayoutDoc reads the file with, so the marker's `file` is a path
167
+ // The editor can hand straight to navigateToComponent when the author asks to open it.
168
+ markLayoutNodes(layoutDoc, file);
169
+ const parentRef = layoutDoc.$layout;
170
+ if (!isString(parentRef) || !parentRef) {
171
+ return layoutDoc;
172
+ }
173
+ const parent = await resolveCanvasLayout(parentRef, seen);
174
+ if (!parent) {
175
+ return layoutDoc;
176
+ }
177
+ const merged = distributePageIntoLayout(parent, layoutDoc);
178
+ delete merged.$layout;
179
+ return merged;
180
+ }
181
+
182
+ /**
183
+ * Re-address a layout's `$elements` imports from the layout's file to the document being drawn.
184
+ *
185
+ * A layout's `{ $ref: "../components/site-nav.json" }` is relative to the LAYOUT, but once the
186
+ * layout is merged around a document the canvas resolves every `$ref` against the document's own
187
+ * base. A page in `pages/` sits as deep as `layouts/`, so the two answers agreed by coincidence; an
188
+ * entry in `content/menu/` or a page in `pages/blog/` resolved the same ref to
189
+ * `content/components/…`, which 404s and leaves the nav and footer unregistered. Each entry is
190
+ * re-addressed from the file its `$__layout` marker names, which is the file it was authored in at
191
+ * every level of a layout chain. Bare specifiers and URLs are left alone, and so is an entry with
192
+ * no marker (the document's own).
193
+ *
194
+ * @param {(JxElement | string)[] | undefined} elements - The merged document's `$elements`
195
+ * @param {string} documentPath - The project-relative path of the document being drawn
196
+ */
197
+ export function rebaseLayoutElementRefs(
198
+ elements: readonly (JxElement | string | null)[] | undefined,
199
+ documentPath: string,
200
+ ) {
201
+ for (const el of elements ?? []) {
202
+ if (!isNonNullObject(el) || !isString(el.$ref) || !/^\.\.?\//.test(el.$ref)) {
203
+ continue;
204
+ }
205
+ const marker = el["$__layout"];
206
+ if (!isJsonObject(marker) || !isString(marker.file)) {
207
+ continue;
208
+ }
209
+ const target = new URL(el.$ref, `jx-project:/${marker.file}`).pathname.replace(/^\//, "");
210
+ el.$ref = computeRelativePath(documentPath, target);
211
+ }
212
+ }
213
+
214
+ /**
215
+ * A document's own layout override: its root's `$layout`, or a Markdown document's frontmatter
216
+ * `$layout` (which the build reads, and which the Inspector's Page tab writes); `undefined` when it
217
+ * names none.
218
+ */
219
+ export function pageLayout(doc: JxMutableNode, tab: Tab | null): string | false | undefined {
220
+ if (doc.$layout !== undefined) {
221
+ return doc.$layout;
222
+ }
223
+ const fromFrontmatter = tab?.doc.content.frontmatter.$layout;
224
+ return isString(fromFrontmatter) || fromFrontmatter === false ? fromFrontmatter : undefined;
225
+ }
226
+
227
+ /**
228
+ * The layout a collection entry is drawn in (studio.md §22.5): the entry's own `$layout` (on its
229
+ * document, or in its frontmatter for a Markdown entry, {@link pageLayout}), else its collection's
230
+ * template page's `$layout`, else the project's `defaults.layout`. `null` when none applies.
231
+ *
232
+ * The template PAGE's own body is not spliced around the entry: a page that binds the entry into a
233
+ * wrapper of its own shows that wrapper only in Preview of the page itself, because the edit
234
+ * transforms cannot map the entry's paths through the page's bound node.
235
+ */
236
+ export async function entryLayout(
237
+ doc: JxMutableNode,
238
+ tab: Tab | null,
239
+ path: string | null | undefined,
240
+ ): Promise<string | null> {
241
+ const own = pageLayout(doc, tab);
242
+ const match = path ? collectionForFile(path) : null;
243
+ const page = match ? await collectionPage(match.name, entryLocale(path)) : null;
244
+ return entryLayoutPath(own, page);
245
+ }
246
+
115
247
  /**
116
248
  * Resolve a document into the form the iframe canvas renders: layout-distributed, edit-transformed,
117
249
  * with components/imports/$media/$head merged in, plus the path-mapper context and site style. This
@@ -176,24 +308,35 @@ export async function resolveCanvasDocument(
176
308
  S.documentPath &&
177
309
  projectState?.isSiteProject &&
178
310
  (S.documentPath.startsWith("pages/") || S.documentPath.startsWith("./pages/"));
311
+ /* A collection entry written on the canvas is drawn the way a page is: inside a layout, with the
312
+ layout's nodes locked and the entry's own tree the editable region (studio.md §22.5). */
313
+ const isEntry =
314
+ !isPage && Boolean(projectState?.isSiteProject) && writesEntryOnCanvas(S.documentPath);
179
315
  let pageContentPrefix: (string | number)[] | null = null;
180
316
  let pageContentOffset = 0;
181
317
 
182
318
  // Layout wrapping obeys the tab-bar's "show layout elements" toggle (default on); with it off,
183
319
  // The page renders alone — the unwrapped path is identical to a non-layout page.
184
- if (isPage && tab?.session.ui.showLayout !== false) {
185
- const layoutPath = getEffectiveLayoutPath(doc.$layout);
320
+ if ((isPage || isEntry) && tab?.session.ui.showLayout !== false) {
321
+ const layoutPath = isEntry
322
+ ? await entryLayout(doc, tab, S.documentPath)
323
+ : getEffectiveLayoutPath(pageLayout(doc, tab));
186
324
  if (layoutPath) {
187
- const layoutDoc = await resolveLayoutDoc(layoutPath);
325
+ const layoutDoc = await resolveCanvasLayout(layoutPath);
188
326
  if (layoutDoc) {
189
- // The SAME normalization resolveLayoutDoc reads the file with, so the marker's `file` is a
190
- // Path the editor can hand straight to navigateToComponent when the author asks to open it.
191
- markLayoutNodes(layoutDoc, layoutPath.replace(/^\.\//, ""));
192
327
  const pageForSlots = canvasMode === "preview" ? structuredClone(toRaw(doc)) : renderDoc;
193
328
  const merged = distributePageIntoLayout(layoutDoc, pageForSlots);
194
329
  renderDoc =
195
330
  canvasMode === "preview" ? merged : prepareForEditMode(stripEventHandlers(merged));
196
331
  layoutWrapped = true;
332
+ if (S.documentPath) {
333
+ /* SAFETY: a document's `$elements` entries are tag-name strings, `{ $ref }` imports or
334
+ inline element definitions (a hand-edited null among them is skipped by the walk). */
335
+ rebaseLayoutElementRefs(
336
+ renderDoc.$elements as (JxElement | string | null)[] | undefined,
337
+ S.documentPath,
338
+ );
339
+ }
197
340
  const pageContent = findPageContentPrefix(merged);
198
341
  pageContentPrefix = pageContent?.prefix ?? null;
199
342
  pageContentOffset = pageContent?.offset ?? 0;
@@ -201,6 +344,24 @@ export async function resolveCanvasDocument(
201
344
  }
202
345
  }
203
346
 
347
+ /* `$site` / `$page`, and the project's own state, exactly as the build injects them — a layout
348
+ that binds `${state.$page.url}` (a header marking its current link) threw on the canvas, and in
349
+ Preview, where templates are live, that one TypeError blanked the whole page. Onto COPIES of
350
+ `state` and `imports`: the edit-mode render doc shares both with the tab's source document,
351
+ which must not gain injected keys it would then save. */
352
+ const projectConfig = projectState?.projectConfig;
353
+ if (isPage && projectConfig) {
354
+ renderDoc = { ...renderDoc, state: { ...renderDoc.state } };
355
+ if (renderDoc.imports) {
356
+ renderDoc.imports = { ...renderDoc.imports };
357
+ }
358
+ /* SAFETY: a page render document is a JxDocument in all but its declared (mutable) type;
359
+ `injectContext` writes `state`, `$media` and `imports`, all of which the copy above owns. */
360
+ injectContext(renderDoc as JxDocument, projectConfig, {
361
+ urlPattern: documentUrlPattern(S.documentPath),
362
+ });
363
+ }
364
+
204
365
  const fileBase = documentBase(projectState?.projectRoot);
205
366
  const docBase = S.documentPath ? new URL(S.documentPath, fileBase).href : undefined;
206
367
  /*
@@ -230,6 +391,40 @@ export async function resolveCanvasDocument(
230
391
  }
231
392
  }
232
393
 
394
+ // `$site` and `$page`, as the build injects them (canvas-page-context.ts), in every mode: Preview
395
+ // Draws the page as it ships, and edit mode evaluates a component's bound props live. For a page
396
+ // This refines the pair injected above with the chosen route params and the project's i18n, and
397
+ // It is the only injection a collection entry gets.
398
+ if ((isPage || isEntry) && S.documentPath) {
399
+ const context = await pageContextFor(
400
+ {
401
+ documentPath: S.documentPath,
402
+ fields: isEntry ? (tab?.doc.content.frontmatter ?? null) : null,
403
+ isEntry,
404
+ lang: doc.$lang,
405
+ params: tab?.session.ui.previewParams ?? {},
406
+ title: doc.title,
407
+ },
408
+ projectState?.projectConfig,
409
+ );
410
+ if (context) {
411
+ // A rebuild, not a write: the render doc shares nodes with the tab's source document.
412
+ renderDoc = { ...renderDoc, state: { ...renderDoc.state, ...context } };
413
+ }
414
+ }
415
+
416
+ /* Class-backed state resolved before the render rather than by it (repeater-sources.ts): in every
417
+ mode, an entry naming an enabled extension's class by a bare `$prototype`, as the build
418
+ resolves it; in Edit and Design, also the collections a repeater reads, whose first item draws
419
+ the repeater's template. */
420
+ const extensions = await loadExtensions();
421
+ const classes = extensionClassPaths(extensions);
422
+ const bakeKeys = extensionClassKeys(renderDoc, classes);
423
+ if (canvasMode !== "preview") {
424
+ bakeKeys.push(...repeaterSourceKeys(doc));
425
+ }
426
+ renderDoc = await bakeRepeaterSources(renderDoc, [...new Set(bakeKeys)], docBase, classes);
427
+
233
428
  // Component definition docs (non-page): seed chosen test-prop values into the render doc's state
234
429
  // So a non-instantiated component previews with real data — templates, dataScope snapshots, and
235
430
  // Live/snapshot expression previews all see the values (M6, the previewParams mirror). Pure
@@ -302,28 +497,25 @@ export async function resolveCanvasDocument(
302
497
  // A null entry in a document's `$elements` names nothing, so it contributes no ref.
303
498
  effectiveElements.map((e: JxElement | string) => (isNonNullObject(e) ? e.$ref : e)),
304
499
  );
305
- const collectTags = (node: JxMutableNode): Set<string> => {
306
- const tags = new Set<string>();
307
- // Document JSON can hold a null or scalar where a node belongs; it carries no tag.
308
- if (!isNonNullObject(node)) {
309
- return tags;
310
- }
311
- if (node.tagName) {
312
- tags.add(displayTagName(node.tagName));
313
- }
314
- if (Array.isArray(node.children)) {
315
- for (const child of node.children) {
316
- if (!isString(child)) {
317
- for (const t of collectTags(child)) {
318
- tags.add(t);
319
- }
320
- }
321
- }
500
+ /* Every tag the render can reach: the document's own, then — through the registry's `uses` —
501
+ each component's, transitively. Components address one another by bare `tagName` and declare
502
+ no `$elements` (the build finds them project-wide), so a tag that only appears INSIDE a
503
+ component (a card's panel, a header's theme toggle) was never registered here and rendered as
504
+ an empty, unstyled element. */
505
+ const byTag = new Map(componentRegistry.map((c: ComponentEntry) => [c.tagName, c]));
506
+ /* SAFETY: a render document is JSON by construction (it crosses into the canvas realm by
507
+ `postMessage`); the walk only reads `tagName`, `children`, `map` and `cases` off it. */
508
+ const reachable = collectUsedTags(renderDoc as JsonValue);
509
+ if (renderDoc.tagName) {
510
+ reachable.add(displayTagName(renderDoc.tagName));
511
+ }
512
+ for (const tag of reachable) {
513
+ for (const used of byTag.get(tag)?.uses ?? []) {
514
+ reachable.add(used);
322
515
  }
323
- return tags;
324
- };
325
- for (const tag of collectTags(renderDoc)) {
326
- const comp = componentRegistry.find((c: ComponentEntry) => c.tagName === tag);
516
+ }
517
+ for (const tag of reachable) {
518
+ const comp = byTag.get(tag);
327
519
  if (comp && comp.source !== "npm" && comp.path) {
328
520
  const relPath = computeRelativePath(S.documentPath ?? null, comp.path);
329
521
  if (!existingRefs.has(relPath)) {
@@ -336,7 +528,7 @@ export async function resolveCanvasDocument(
336
528
  if (effectiveElements.length > 0) {
337
529
  renderDoc.$elements = effectiveElements;
338
530
  }
339
- renderDoc.imports = getEffectiveImports(renderDoc.imports);
531
+ renderDoc.imports = getEffectiveImports(renderDoc.imports, extensions);
340
532
  // The effective-media/head getters always answer a value: the merge, or an empty one.
341
533
  renderDoc.$media = getEffectiveMedia(renderDoc.$media);
342
534
  renderDoc.$head = getEffectiveHead(renderDoc.$head);
@@ -1,20 +1,20 @@
1
1
  /// <reference lib="dom" />
2
2
  /**
3
- * Canvas origin derivation. The canvas iframe may load same-origin (dev server / chromium /
4
- * electrobun gate-off, where the platform canvasUrl is a relative path) or cross-origin (electrobun
5
- * loopback, where canvasUrl is an absolute http://127.0.0.1:<port> URL).
3
+ * Canvas origin derivation. The canvas iframe may load same-origin (dev server / chromium / cloud,
4
+ * where the platform canvasUrl is a relative path) or cross-origin (a host whose canvasUrl is an
5
+ * absolute http://127.0.0.1:<port> URL).
6
6
  *
7
7
  * CanvasBaseOrigin() returns the origin the canvas document lives on, so callers that build
8
8
  * absolute asset/doc URLs target the right server. For a relative canvasUrl this resolves to
9
- * location.origin — IDENTITY for the same-origin paths, so dev/chromium/electrobun-gate-off are
10
- * byte-identical to before.
9
+ * location.origin — IDENTITY for the same-origin paths, so dev/chromium/cloud are byte-identical to
10
+ * before.
11
11
  */
12
12
 
13
13
  import { getPlatform, hasPlatform } from "../platform";
14
14
 
15
15
  /**
16
- * The origin serving the canvas document. When the platform registers an absolute canvasUrl
17
- * (electrobun loopback), this is that URL's origin; otherwise it falls back to the parent's own
16
+ * The origin serving the canvas document. When the platform registers an absolute canvasUrl (an
17
+ * absolute loopback URL), this is that URL's origin; otherwise it falls back to the parent's own
18
18
  * origin (a relative canvasUrl resolves against location.href to location.origin).
19
19
  */
20
20
  export function canvasBaseOrigin(): string {
@@ -25,6 +25,31 @@ export function canvasBaseOrigin(): string {
25
25
  return location.origin;
26
26
  }
27
27
 
28
+ /**
29
+ * The dev-proxy token for a render in the SHELL's own realm, or null when it has none to use.
30
+ *
31
+ * A desktop server gates `/__jx_resolve__` and `/__jx_server__` on its rpcToken, and the canvas
32
+ * iframe authenticates by reading it off its own URL (`?rpcToken=`). The shell renders documents
33
+ * too (the Library's previews, the assistant's render check) through its own copy of the runtime,
34
+ * and that copy was never told: every `$src` class on a previewed page posted without the token and
35
+ * drew a 403. The token is read off the same canvas URL rather than a second channel, so there is
36
+ * one place it is handed out.
37
+ *
38
+ * Same-origin only. A cross-origin canvas (an absolute loopback canvasUrl) is not where the shell's
39
+ * relative dev-proxy fetches go, so its token would authenticate nothing and is better not spread
40
+ * around.
41
+ *
42
+ * @returns {string | null} The token, or null on a host with no token or a cross-origin canvas.
43
+ */
44
+ export function hostResolveToken(): string | null {
45
+ const canvasUrl = hasPlatform() ? getPlatform().canvasUrl : undefined;
46
+ if (!canvasUrl) {
47
+ return null;
48
+ }
49
+ const url = new URL(canvasUrl, location.href);
50
+ return url.origin === location.origin ? url.searchParams.get("rpcToken") : null;
51
+ }
52
+
28
53
  /**
29
54
  * The base the canvas fetches PROJECT FILES against — component `$ref`s, `$src` modules, images.
30
55
  *
@@ -69,11 +94,11 @@ export function documentBase(projectRoot?: string): string {
69
94
  * the desktop MutationObserver only rewrites asynchronously. Rendering the src loopback-absolute up
70
95
  * front eliminates that race for direct-parent <img> sites.
71
96
  *
72
- * Guarded: this ONLY rewrites when a real cross-origin http(s) loopback is registered (electrobun
73
- * loopback, canvasUrl set to an absolute http URL) whose origin differs from location.origin.
74
- * Otherwise — dev server, chromium, electrobun gate-off, and the pre-activate window — it returns
75
- * the ORIGINAL relative path unchanged, keeping those cases byte-identical (an absolute
76
- * ${location.origin}/... on the shell would just reintroduce views://... — the exact bug).
97
+ * Guarded: this ONLY rewrites when a real cross-origin http(s) loopback is registered (canvasUrl
98
+ * set to an absolute http URL) whose origin differs from location.origin. Otherwise — dev server,
99
+ * chromium, cloud, and the pre-activate window — it returns the ORIGINAL relative path unchanged,
100
+ * keeping those cases byte-identical (an absolute ${location.origin}/... on the shell would just
101
+ * reintroduce views://... — the exact bug).
77
102
  *
78
103
  * Only for PROJECT asset srcs; NEVER call it for shell-packaged views:// assets (sp-icons, bundled
79
104
  * chrome). Already-absolute inputs (data:/blob:/http/views://) are returned untouched, so it is