@jxsuite/studio 6.1.2 → 8.0.0

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,112 @@
1
+ /**
2
+ * The `$site` and `$page` a page or a collection entry is drawn with on the canvas.
3
+ *
4
+ * A built page reads `${$page.locale}`, `${$page.url}` and `${$site.name}` from the state the build
5
+ * injects (site-architecture.md §10, §13.4). The canvas injected none of it, so every one of those
6
+ * bindings was undefined there: a component handed `$page.locale` as its locale threw a
7
+ * `RangeError` and was not rendered, and Preview of a multilingual page drew no list at all. The
8
+ * injection is `@jxsuite/site/context`'s, the one the build calls, because a host that improvised
9
+ * its own subset would differ from the build in whichever field it forgot.
10
+ *
11
+ * The route is the document's own: a page's URL pattern from its file, with the route parameters
12
+ * chosen for the preview; an entry's URL from its collection's template page in the entry's own
13
+ * language (studio.md §22.5), with the entry filling the page's parameter.
14
+ *
15
+ * @docs studio/interface/canvas
16
+ */
17
+
18
+ import { injectContext } from "@jxsuite/site/context";
19
+ import { documentUrlPattern } from "@jxsuite/site/routes";
20
+ import { isString } from "@jxsuite/schema/guards";
21
+ import { resolveI18n } from "@jxsuite/schema/locale";
22
+ import { collectionForFile } from "../content/collection-match";
23
+ import { collectionPage, entryLocale, entryUrl } from "../content/collection-pages";
24
+ import type { SiteRoute } from "@jxsuite/site/context";
25
+ import type {
26
+ JxDocument,
27
+ JxDocumentValue,
28
+ JxStateDefinition,
29
+ ProjectConfig,
30
+ } from "@jxsuite/schema/types";
31
+
32
+ /** The two injected state entries. */
33
+ export interface PageContext {
34
+ $site: JxStateDefinition;
35
+ $page: JxStateDefinition;
36
+ }
37
+
38
+ /** What {@link pageContextFor} reads off the document being drawn. */
39
+ export interface PageContextSource {
40
+ /** The project-relative path of the page or entry. */
41
+ documentPath: string;
42
+ /** Whether it is a collection entry rather than a page. */
43
+ isEntry: boolean;
44
+ /** The document's own `title` and `$lang`, which win over the route's. */
45
+ title?: JxDocumentValue | undefined;
46
+ lang?: JxDocumentValue | undefined;
47
+ /** An entry's fields (Markdown frontmatter), which may fill its URL and title. */
48
+ fields?: Readonly<Record<string, JxDocumentValue | undefined>> | null;
49
+ /** The route parameters chosen for a dynamic page's preview. */
50
+ params?: Readonly<Record<string, string>>;
51
+ }
52
+
53
+ /** The parameters `url` fills in `pattern`, by position: `/blog/:slug` and `/blog/a` → `{slug: a}`. */
54
+ export function routeParams(pattern: string, url: string): NonNullable<SiteRoute["_pathParams"]> {
55
+ const values = url.split("/");
56
+ const out: NonNullable<SiteRoute["_pathParams"]> = {};
57
+ for (const [i, name] of pattern.split("/").entries()) {
58
+ const value = values[i];
59
+ if (name.startsWith(":") && value !== undefined) {
60
+ out[name.slice(1)] = value;
61
+ }
62
+ }
63
+ return out;
64
+ }
65
+
66
+ /** The route a document is served at, as the build would serve it. */
67
+ async function routeOf(source: PageContextSource): Promise<SiteRoute> {
68
+ const { documentPath, fields, isEntry, params } = source;
69
+ if (!isEntry) {
70
+ return {
71
+ _pathParams: { ...params },
72
+ sourcePath: documentPath,
73
+ urlPattern: documentUrlPattern(documentPath),
74
+ };
75
+ }
76
+ const match = collectionForFile(documentPath);
77
+ const page = match ? await collectionPage(match.name, entryLocale(documentPath)) : null;
78
+ const url = entryUrl(page, documentPath, fields);
79
+ if (!page || url === null) {
80
+ return { _pathParams: {}, sourcePath: documentPath, urlPattern: page?.pattern ?? "/" };
81
+ }
82
+ return { _pathParams: routeParams(page.pattern, url), sourcePath: page.path, urlPattern: url };
83
+ }
84
+
85
+ /**
86
+ * The `$site` and `$page` for one document, injected exactly as the build injects them, or `null`
87
+ * when there is no project configuration to inject from.
88
+ */
89
+ export async function pageContextFor(
90
+ source: PageContextSource,
91
+ projectConfig: ProjectConfig | null | undefined,
92
+ ): Promise<PageContext | null> {
93
+ if (!projectConfig) {
94
+ return null;
95
+ }
96
+ const route = await routeOf(source);
97
+ const fieldTitle = source.fields?.title;
98
+ const title = isString(source.title) ? source.title : isString(fieldTitle) ? fieldTitle : null;
99
+ // A scratch document: the injection writes project state, `$media`, imports and `$elements` onto
100
+ // The document it is handed, and the canvas composes those its own way. Only the two context
101
+ // Entries are taken from it.
102
+ const doc: JxDocument = { state: {} };
103
+ if (title !== null) {
104
+ doc.title = title;
105
+ }
106
+ if (isString(source.lang)) {
107
+ doc.$lang = source.lang;
108
+ }
109
+ const scratch = injectContext(doc, projectConfig, route, null, resolveI18n(projectConfig).i18n);
110
+ const { $page, $site } = scratch.state ?? {};
111
+ return $page === undefined || $site === undefined ? null : { $page, $site };
112
+ }
@@ -1,7 +1,7 @@
1
1
  /// <reference lib="dom" />
2
2
  /**
3
3
  * Canvas render — extracted from studio.js (Phase 4o). Multi-mode canvas rendering orchestrator:
4
- * dispatches to manage/settings/source/edit/design/preview rendering paths.
4
+ * dispatches to source/edit/design/preview rendering paths.
5
5
  */
6
6
 
7
7
  import { render, nothing } from "lit-html";
@@ -49,14 +49,20 @@ import { parseSourceForPath } from "../files/file-ops";
49
49
  import { serializeDocument } from "../files/serialize-document";
50
50
  import { parseJsonDocument, serializeJson } from "@jxsuite/schema/json-layout";
51
51
  import { detachGridPanel, gridPanelMounted, renderGridMode } from "../grid/grid-panel";
52
- import { detachLibraryPane, libraryPaneMounted, renderLibraryMode } from "../browse/library-pane";
53
52
  import { detachEntryPane, entryPaneMounted, renderEntryMode } from "../content/entry-editor";
54
53
  import { detachMediaPane, mediaPaneMounted, renderMediaMode } from "../media/media-pane";
55
54
  import {
56
- detachSettingsPane,
57
- renderSettingsPane,
58
- settingsPaneMounted,
59
- } from "../panels/settings-pane";
55
+ detachVariablesView,
56
+ renderVariablesMode,
57
+ variablesViewMounted,
58
+ } from "../style/variables-view";
59
+ import { PROJECT_VARIABLES_VIEW } from "../style/project-styles";
60
+ import {
61
+ detachWorkspaceHome,
62
+ isWorkspaceHomeMode,
63
+ renderWorkspaceHome,
64
+ workspaceHomeMounted,
65
+ } from "../workspace/workspace-home";
60
66
  import { formatByName, formatForPath } from "../format/format-host";
61
67
  import {
62
68
  BUFFER_COMMIT,
@@ -66,10 +72,16 @@ import {
66
72
  commitBufferWrites,
67
73
  } from "../services/monaco-buffer";
68
74
  import { noteTyping } from "../tabs/typing";
75
+ import { normalizeDocument } from "../tabs/bare-repeaters";
69
76
  import { diffModelUrisFor, modelUriFor, monacoLangForPath } from "../services/model-uri";
70
77
  import { renderWelcome } from "../surfaces/welcome";
71
78
  import { emptyState } from "../surfaces/empty-state";
72
- import { attachDocumentHeaderHost, hasDocumentHeader } from "../panels/frontmatter-panel";
79
+ import {
80
+ attachWritingBanner,
81
+ detachWritingBanner,
82
+ renderWritingBanner,
83
+ } from "../content/writing-banner";
84
+ import { writesEntryOnCanvas } from "../content/content-collections";
73
85
  import { mountCanvasStage } from "../surfaces/canvas-stage";
74
86
  import type {
75
87
  CanvasStageActions,
@@ -238,7 +250,7 @@ function hardClearCanvasWrap(canvasWrap: HTMLElement) {
238
250
  * the two width handles apart: `data-side`, the one part the stage document draws twice).
239
251
  *
240
252
  * Per STAGE rather than per module, for the reason every other field on `CanvasSurface` is: two
241
- * panes each have a Monaco host and a card slot, and one slot would hand the second pane's stage
253
+ * panes each have a Monaco host and a banner slot, and one slot would hand the second pane's stage
242
254
  * the first pane's editor.
243
255
  */
244
256
  const _stageHosts = new WeakMap<CanvasSurface, Map<string, HTMLElement>>();
@@ -282,8 +294,8 @@ function stageActions(surface: CanvasSurface): CanvasStageActions {
282
294
  stageHosts(surface).set(detail ? `${part}:${detail}` : part, element);
283
295
  if (part === "panzoom") {
284
296
  surface.panzoomWrap = element;
285
- } else if (part === "doc-header") {
286
- attachDocumentHeaderHost(surface.paneId, element);
297
+ } else if (part === "writing-banner") {
298
+ attachWritingBanner(surface.paneId, element);
287
299
  } else if (part === "diff-toolbar") {
288
300
  setDiffToolbarHost(surface.paneId, element);
289
301
  }
@@ -319,10 +331,10 @@ function drawStage(
319
331
  entries: CanvasPanelEntry[],
320
332
  ): Promise<CanvasStageHandle | null> {
321
333
  _stageEntries.set(surface, entries);
322
- /* There is no card-host release here, and there must not be one: `renderCanvasImpl` already
323
- releases it for EVERY mode that draws no header, one branch above this one. A second answer to
334
+ /* There is no banner-host release here, and there must not be one: `renderCanvasImpl` already
335
+ releases it for EVERY mode that draws no banner, one branch above this one. A second answer to
324
336
  the same question is how the two drift — and this one would answer only for the frames that
325
- reach a stage, leaving grid, library, entry, media and settings to the first. */
337
+ reach a stage, leaving grid, the homes, entry and media to the first. */
326
338
  const standing = surface.stage;
327
339
  const stage = standing ?? mountCanvasStage(surface.wrap, view, stageActions(surface));
328
340
  surface.stage = stage;
@@ -364,9 +376,8 @@ function editorStageView(frame: "source" | "code"): CanvasStageView {
364
376
  * Release this pane's stage document.
365
377
  *
366
378
  * Called on a real mode transition and on a full reset — the two moments the structure standing on
367
- * the stage stops being the stage's. The two hosts that are handed to another module go WITH it: a
368
- * card slot or a Compare bar pointing at a detached node is a surface that draws into nothing and
369
- * never says so.
379
+ * the stage stops being the stage's. The host handed to another module goes WITH it: a Compare bar
380
+ * pointing at a detached node is a surface that draws into nothing and never says so.
370
381
  *
371
382
  * @param {CanvasSurface} surface
372
383
  */
@@ -378,7 +389,6 @@ function detachCanvasStage(surface: CanvasSurface): void {
378
389
  surface.stage = null;
379
390
  _stageHosts.delete(surface);
380
391
  _stageEntries.delete(surface);
381
- attachDocumentHeaderHost(surface.paneId, null);
382
392
  setDiffToolbarHost(surface.paneId, null);
383
393
  }
384
394
 
@@ -616,10 +626,11 @@ function resetCanvasView(surface: CanvasSurface) {
616
626
  const canvasWrap = surface.wrap;
617
627
  const { paneId } = surface;
618
628
  detachGridPanel(paneId);
619
- detachLibraryPane(paneId);
620
629
  detachEntryPane(paneId);
630
+ detachWritingBanner(paneId);
621
631
  detachMediaPane(paneId);
622
- detachSettingsPane(paneId);
632
+ detachVariablesView(paneId);
633
+ detachWorkspaceHome(paneId);
623
634
  disposeSourceCollab(surface);
624
635
  disposeSourceEditor(surface);
625
636
  disposeDiffEditor(surface);
@@ -841,6 +852,8 @@ async function mountSourceEditor(
841
852
  }
842
853
  tab.doc.document = document;
843
854
  tab.doc.content.frontmatter = frontmatter;
855
+ // The buffer is the author's own spelling now, so it is what the next save keeps.
856
+ tab.doc.source = sourceEditor.getValue();
844
857
  tab.doc.dirty = true;
845
858
  writes.markSettled();
846
859
  } catch {
@@ -854,7 +867,10 @@ async function mountSourceEditor(
854
867
  const parsed = parseJsonDocument(sourceEditor.getValue());
855
868
  /* SAFETY: a JSON source tab holds a Jx document, whose schema makes the top-level object
856
869
  parseJsonDocument returns a node; text that does not parse throws into the catch. */
857
- tab.doc.document = parsed.document as JxMutableNode;
870
+ const document = parsed.document as JxMutableNode;
871
+ // The member form every edit assumes, and the record of the bare repeaters the text wrote.
872
+ tab.doc.bareRepeaters = normalizeDocument(document);
873
+ tab.doc.document = document;
858
874
  tab.doc.layout = parsed.layout;
859
875
  tab.doc.dirty = true;
860
876
  writes.markSettled();
@@ -979,7 +995,6 @@ function renderCanvasImpl(surface: CanvasSurface) {
979
995
  // No active tab — reset every piece of canvas view state so reopening a file can never inherit
980
996
  // A stale Lit part, a dead Monaco editor, or a mismatched prevCanvasMode (the toxic states that
981
997
  // Previously left the canvas unrenderable until a full reload).
982
- attachDocumentHeaderHost(surface.paneId, null);
983
998
  resetCanvasView(surface);
984
999
  /* A DERIVED pane with nothing to draw explains itself. It is not the welcome screen and it is
985
1000
  not a blank stage: it is a standing rule that has not resolved — "Select an element inside a
@@ -1009,7 +1024,6 @@ function renderCanvasImpl(surface: CanvasSurface) {
1009
1024
  follow has run and a stage that blanked on that would flicker on every retarget. */
1010
1025
  const staleDerivation = derivationOfPane(surface.paneId);
1011
1026
  if (staleDerivation?.status === "unavailable" && staleDerivation.reason) {
1012
- attachDocumentHeaderHost(surface.paneId, null);
1013
1027
  resetCanvasView(surface);
1014
1028
  /* …and for a COMPANION, what it is still holding. `tab` here is the pane's OWN document for a
1015
1029
  companion and the SOURCE pane's for a lens — the hop `tabOfPane` takes — so the kind decides
@@ -1035,28 +1049,19 @@ function renderCanvasImpl(surface: CanvasSurface) {
1035
1049
  // Re-rendering the previous mode's panels in place.
1036
1050
  const canvasMode = canvasModeOfPane(surface.paneId);
1037
1051
 
1038
- /* The Document Header card is drawn by the STAGE, and only by Edit's. It is docked
1039
- above the page's scroller, a band across the pane at 1:1: its width is the PANE's rather than
1040
- the breakpoint's, and the page scrolls under it rather than taking it along. It used to go
1041
- INSIDE Edit's column, where it was exactly as wide as whatever breakpoint or dragged width the
1042
- column had and scrolled off with the first screen of the page, and it was pinned above Design's
1043
- artboards as well. Design omits it now, so the artboards get the whole pane, and nothing is
1044
- lost by that: the Navigator's Page panel carries the same fields in every mode.
1045
- `hasDocumentHeader` is the other predicate, and it is a fact about the DOCUMENT. It used to
1046
- carry two more clauses, suppressing the card while a function body or a formula was open on
1047
- the grounds that those sub-editors took the whole stage. They open in the dock's Logic tab now
1048
- (§16.3) and the page is still on screen behind it, so those clauses only DETACHED the visible
1049
- card: it stopped re-rendering and quietly showed frontmatter from before the edit. */
1050
- /* …and NOT in a lens. The Document Header is an editing surface — Title, Route, SEO, the raw
1051
- frontmatter — over a document this pane does not own, and two cards editing one document's
1052
- frontmatter side by side is two writers for one field. A lens is a view; the card belongs to
1053
- the pane that owns the tab. */
1054
- const wantsDocHeader =
1052
+ /* Edit draws the page and nothing above it (§4.2): a document's settings — title, URL, layout,
1053
+ frontmatter, description, Search appearance — are the Inspector's Page tab's (§6), in every
1054
+ mode, so the stage carries no card of its own and the page starts at the top of the pane.
1055
+ A collection entry written in Edit carries the writing-mode banner (§22.5): the canvas wraps it
1056
+ in its template's layout, and the banner says the layout is locked. Not in a lens: the
1057
+ banner's door belongs to the pane that owns the tab, and a lens is a view. */
1058
+ const wantsWritingBanner =
1055
1059
  canvasMode === "edit" &&
1056
1060
  derivationOfPane(surface.paneId)?.kind !== "lens" &&
1057
- hasDocumentHeader(tab);
1058
- if (!wantsDocHeader) {
1059
- attachDocumentHeaderHost(surface.paneId, null);
1061
+ Boolean(projectState?.isSiteProject) &&
1062
+ writesEntryOnCanvas(tab.documentPath);
1063
+ if (!wantsWritingBanner) {
1064
+ detachWritingBanner(surface.paneId);
1060
1065
  }
1061
1066
 
1062
1067
  /* Advance THIS stage's render generation so stale async renders from its previous pass bail out.
@@ -1165,12 +1170,6 @@ function renderCanvasImpl(surface: CanvasSurface) {
1165
1170
  return;
1166
1171
  }
1167
1172
 
1168
- // Library fast-path, same shape: the Library pane owns its own reactivity (view state, scan,
1169
- // Window), so a same-tab re-render while it is mounted needs nothing from the canvas pipeline.
1170
- if (canvasMode === "manage" && libraryPaneMounted(surface.paneId, tab) && !modeChanged) {
1171
- return;
1172
- }
1173
-
1174
1173
  // Entry fast-path, same shape: the entry form owns its own effect over the tab's frontmatter, so
1175
1174
  // A field commit repaints the form and nothing reaches the canvas pipeline.
1176
1175
  if (canvasMode === "entry" && entryPaneMounted(surface.paneId, tab) && !modeChanged) {
@@ -1183,9 +1182,21 @@ function renderCanvasImpl(surface: CanvasSurface) {
1183
1182
  return;
1184
1183
  }
1185
1184
 
1186
- // Settings fast-path, same shape: the Project Settings editor subscribes to the section registry
1187
- // And to its own chosen section, so a mounted editor needs nothing from the canvas pipeline.
1188
- if (canvasMode === "settings" && settingsPaneMounted(surface) && !modeChanged) {
1185
+ // Variables fast-path, same shape: the view follows the document through its own effect.
1186
+ if (
1187
+ canvasMode === PROJECT_VARIABLES_VIEW &&
1188
+ variablesViewMounted(surface.paneId, tab) &&
1189
+ !modeChanged
1190
+ ) {
1191
+ return;
1192
+ }
1193
+
1194
+ // Workspace-home fast-path, same shape: a home is a standing document with nothing to re-render.
1195
+ if (
1196
+ isWorkspaceHomeMode(canvasMode) &&
1197
+ workspaceHomeMounted(surface.paneId, tab) &&
1198
+ !modeChanged
1199
+ ) {
1189
1200
  return;
1190
1201
  }
1191
1202
 
@@ -1202,7 +1213,8 @@ function renderCanvasImpl(surface: CanvasSurface) {
1202
1213
  const curCustomized = shell.stylebook.customizedOnly;
1203
1214
  const filterChanged =
1204
1215
  curFilter !== surface.prevStylebookFilter ||
1205
- curCustomized !== surface.prevStylebookCustomizedOnly;
1216
+ curCustomized !== surface.prevStylebookCustomizedOnly ||
1217
+ activeMediaOfPane(surface.paneId) !== surface.prevStylebookMedia;
1206
1218
  if (!filterChanged) {
1207
1219
  const style = transposeStylebookStyle(getEffectiveStyle(tab.doc.document.style));
1208
1220
  if (postStyleUpdateToStylebookHosts(style) > 0) {
@@ -1249,18 +1261,18 @@ function renderCanvasImpl(surface: CanvasSurface) {
1249
1261
  // Destroy the grid panel if switching away from grid mode
1250
1262
  detachGridPanel(surface.paneId);
1251
1263
 
1252
- // The Library holds an IntersectionObserver and an LRU of live runtime subtrees; leaving the
1253
- // Mode without releasing them is exactly the unbounded retention its LRU cap exists to remove.
1254
- detachLibraryPane(surface.paneId);
1255
-
1256
- // Same for the entry form, whose effect scope subscribes to the document's frontmatter
1264
+ // The entry form's effect scope subscribes to the document's frontmatter
1257
1265
  detachEntryPane(surface.paneId);
1258
1266
 
1259
1267
  // And for the media viewer, whose effect scope subscribes to the tab's path
1260
1268
  detachMediaPane(surface.paneId);
1261
1269
 
1262
- // Same for the Project Settings editor, which holds a registry subscription
1263
- detachSettingsPane(surface.paneId);
1270
+ // And for the Variables view, whose effect scope subscribes to the document's style
1271
+ detachVariablesView(surface.paneId);
1272
+
1273
+ // And for a workspace home, whose surface holds a mounted document (the Content home's
1274
+ // IntersectionObserver and preview LRU among them)
1275
+ detachWorkspaceHome(surface.paneId);
1264
1276
 
1265
1277
  // Dispose Monaco editor if switching away from source mode
1266
1278
  disposeSourceCollab(surface);
@@ -1293,6 +1305,7 @@ function renderCanvasImpl(surface: CanvasSurface) {
1293
1305
  if (canvasMode === "stylebook") {
1294
1306
  surface.prevStylebookFilter = shell.stylebook.filter;
1295
1307
  surface.prevStylebookCustomizedOnly = shell.stylebook.customizedOnly;
1308
+ surface.prevStylebookMedia = activeMediaOfPane(surface.paneId);
1296
1309
  void renderStylebookMode(surface, {
1297
1310
  applyTransform,
1298
1311
  canvasPanelEntry,
@@ -1371,29 +1384,8 @@ function renderCanvasImpl(surface: CanvasSurface) {
1371
1384
  return;
1372
1385
  }
1373
1386
 
1374
- /* Settings mode: the Project Settings document (§17.1). Same non-iframe-editor pattern as
1375
- grid and stylebook — the panel owns its own reactivity from here — and the reason the seven
1376
- settings screenshots now crop `pane.primary` instead of `overlay.dialog:settings` without one
1377
- manifest step changing. */
1378
- if (canvasMode === "settings") {
1379
- canvasWrap.style.padding = "0";
1380
- canvasWrap.style.display = "block";
1381
- renderSettingsPane(surface);
1382
- return;
1383
- }
1384
-
1385
- /* Library mode: the project's own contents, over a GridSource, in one of five layouts
1386
- (`browse/library-pane.ts`). `commands/context.ts` already mapped `manage` to the `library`
1387
- editor kind — this is the surface that map was pointing at. */
1388
- if (canvasMode === "manage") {
1389
- canvasWrap.style.padding = "0";
1390
- canvasWrap.style.display = "block";
1391
- renderLibraryMode(surface, tab);
1392
- return;
1393
- }
1394
-
1395
1387
  /* Entry mode: one content entry's fields, typed by its collection's schema
1396
- (`content/entry-editor.ts`). Same non-iframe-editor pattern as the Library and Settings — and
1388
+ (`content/entry-editor.ts`). Same non-iframe-editor pattern as the grid and the homes — and
1397
1389
  the reason `entry` is in `commands/context.ts`'s mode map, without which a form over a
1398
1390
  frontmatter record would have resolved into the CANVAS key scope. */
1399
1391
  if (canvasMode === "entry") {
@@ -1412,6 +1404,22 @@ function renderCanvasImpl(surface: CanvasSurface) {
1412
1404
  return;
1413
1405
  }
1414
1406
 
1407
+ /* The Styles workspace's Variables view: `project.json`'s CSS custom properties, edited in a
1408
+ form (`style/variables-view.ts`). Like a home, it writes nothing onto the stage: its own style
1409
+ block fills it (`surfaces/styles-variables.json`). */
1410
+ if (canvasMode === PROJECT_VARIABLES_VIEW) {
1411
+ renderVariablesMode(surface, tab);
1412
+ return;
1413
+ }
1414
+
1415
+ /* A workspace's home: the surface a workspace opens on when none of its own tabs is open
1416
+ (`workspace/workspace-home.ts`). Unlike the five above it writes nothing onto the stage: the
1417
+ home's own style block fills it (`surfaces/publish-workspace.json`, for one). */
1418
+ if (isWorkspaceHomeMode(canvasMode)) {
1419
+ renderWorkspaceHome(surface, tab);
1420
+ return;
1421
+ }
1422
+
1415
1423
  // Grid mode: spreadsheet editor over the tab's grid source (mirrors the Monaco/stylebook
1416
1424
  // Non-iframe-editor pattern; the panel owns its own reactivity from here).
1417
1425
  if (canvasMode === "grid") {
@@ -1653,28 +1661,23 @@ function renderCanvasImpl(surface: CanvasSurface) {
1653
1661
  const rootTag = S.document.tagName;
1654
1662
  const isComponentDoc = isString(rootTag) && rootTag.includes("-");
1655
1663
  const { featureToggles: editToggles } = S.ui;
1656
- /* The stage stacks when it carries the card: the band first, the page's scroller below it.
1657
- Set on every render rather than on the transition, because whether a document HAS a header
1658
- changes with the tab and not with the mode. `editors.ts` and `formula-workspace.ts` already
1659
- claim the column this way — `#canvas-wrap` is a row by default and each surface states its
1660
- own axis. Stretched, because the band is as wide as the PANE: that is the whole difference
1661
- between docking the card here and drawing it in the column, whose width is the breakpoint's
1662
- or the drag's.
1663
- And FLUSH: the cell's own 24px gap (`pane-grid.json`) between two side-by-side surfaces is
1664
- dead space between a docked band and the box below it. The scroller clips its content at its
1665
- top edge, so the gap put that edge 24px below the band's border with nothing drawn in
1666
- between — the page ended at an invisible line and the band read as a hairline over one
1667
- continuous surface. At 0 the clip happens AT the border, which is what makes the border read
1668
- as the band's edge; the scroller's own 32px of top padding is the breathing room. */
1669
- canvasWrap.style.flexDirection = wantsDocHeader ? "column" : "";
1670
- canvasWrap.style.alignItems = wantsDocHeader ? "stretch" : "";
1671
- canvasWrap.style.gap = wantsDocHeader ? "0px" : "";
1664
+ /* The stage stacks when it carries the writing banner: the band first, the page's scroller
1665
+ below it. Set on every render rather than on the transition, because whether a document is
1666
+ a collection entry changes with the tab and not with the mode. `editors.ts` and
1667
+ `formula-workspace.ts` already claim the column this way — `#canvas-wrap` is a row by
1668
+ default and each surface states its own axis. Stretched, because the band is as wide as the
1669
+ PANE rather than the breakpoint's column. And FLUSH: the cell's own 24px gap
1670
+ (`pane-grid.json`) between two side-by-side surfaces would be dead space between the band and
1671
+ the scroller below it, whose own 32px of top padding is the breathing room. */
1672
+ const stacks = wantsWritingBanner;
1673
+ canvasWrap.style.flexDirection = stacks ? "column" : "";
1674
+ canvasWrap.style.alignItems = stacks ? "stretch" : "";
1675
+ canvasWrap.style.gap = stacks ? "0px" : "";
1672
1676
  void drawStage(
1673
1677
  surface,
1674
1678
  {
1675
- /* The card LEADS the frame (`tests/canvas-render.test.ts`, "Edit docks it above the page's
1676
- scroller"): a sibling of the scroller rather than a block of the column, so the page
1677
- scrolls under it and the breakpoint does not size it. The two handles are not in the
1679
+ /* The banner LEADS the frame: a sibling of the scroller rather than a block of the column,
1680
+ so the page scrolls under it and the breakpoint does not size it. The two handles are not in the
1678
1681
  column either: they are `jx-split`s standing beside it in the canvas's row, because the
1679
1682
  canvas is the track a splitter has to measure and the column is the thing being
1680
1683
  resized. */
@@ -1684,7 +1687,7 @@ function renderCanvasImpl(surface: CanvasSurface) {
1684
1687
  handles: "shown",
1685
1688
  hug: isComponentDoc,
1686
1689
  innerPart: "edit-column",
1687
- lead: wantsDocHeader ? "header" : "none",
1690
+ lead: wantsWritingBanner ? "banner" : "none",
1688
1691
  panels: [entry.item],
1689
1692
  },
1690
1693
  [entry],
@@ -1692,6 +1695,9 @@ function renderCanvasImpl(surface: CanvasSurface) {
1692
1695
  if (!stage) {
1693
1696
  return;
1694
1697
  }
1698
+ if (wantsWritingBanner) {
1699
+ renderWritingBanner(surface.paneId, tab);
1700
+ }
1695
1701
  const editColumn = stageHost(surface, "edit-column");
1696
1702
  const scroller = stageHost(surface, "edit-canvas");
1697
1703
  /* SAFETY: the edit stage view always draws its `edit-canvas` part, and this runs only once
@@ -1733,9 +1739,9 @@ function renderCanvasImpl(surface: CanvasSurface) {
1733
1739
  canvasWrap.style.padding = "0";
1734
1740
  canvasWrap.style.overflow = "hidden";
1735
1741
  }
1736
- /* No Document Header here, so the stage cell stays the row it is by default: the card is Edit's
1737
- alone (see `wantsDocHeader`), and the transition into this mode has already cleared the two
1738
- inline writes Edit makes to stack it. */
1742
+ /* Nothing leads Design's frame, so the stage cell stays the row it is by default: the writing
1743
+ banner is Edit's alone (see `wantsWritingBanner`), and the transition into this mode has
1744
+ already cleared the inline writes Edit makes to stack it. */
1739
1745
 
1740
1746
  const {
1741
1747
  sizeBreakpoints,
@@ -1995,11 +2001,11 @@ setSurfaceTeardown((surface) => {
1995
2001
  clearDiffView(surface.paneId);
1996
2002
  setDiffToolbarHost(surface.paneId, null);
1997
2003
  detachGridPanel(surface.paneId);
1998
- detachLibraryPane(surface.paneId);
1999
2004
  detachEntryPane(surface.paneId);
2000
2005
  detachMediaPane(surface.paneId);
2001
- detachSettingsPane(surface.paneId);
2002
- attachDocumentHeaderHost(surface.paneId, null);
2006
+ detachVariablesView(surface.paneId);
2007
+ detachWorkspaceHome(surface.paneId);
2008
+ detachWritingBanner(surface.paneId);
2003
2009
  disposeSourceCollab(surface);
2004
2010
  disposeSourceEditor(surface);
2005
2011
  surface.centerObserver?.disconnect();
@@ -257,8 +257,10 @@ export function centerCanvas(surface: CanvasSurface) {
257
257
  const wrapWidth = surface.wrap.clientWidth;
258
258
  const contentWidth = surface.panzoomWrap.scrollWidth;
259
259
  const zoom = zoomOf(surface);
260
- const scaledWidth = contentWidth * zoom;
261
- surface.panX = Math.max(16, (wrapWidth - scaledWidth) / 2);
260
+ const free = wrapWidth - contentWidth * zoom;
261
+ /* Content that fits is centred, however little room is left over: a 16px floor on a fitted page
262
+ pushed its right edge 16px past the pane. Content wider than the pane starts 16px in. */
263
+ surface.panX = free >= 0 ? free / 2 : 16;
262
264
  surface.panY = 0;
263
265
  }
264
266
 
@@ -483,6 +485,14 @@ export function requestEditZoom(
483
485
  );
484
486
  }
485
487
 
488
+ /** The screen pixels a stage reserves above its boards (`--boards-inset`), or 0 for none. */
489
+ export function boardsInset(panzoomWrap: HTMLElement): number {
490
+ // The inset is declared in px (`--boards-inset:40px`); unset reads as "", which is no inset.
491
+ const declared = getComputedStyle(panzoomWrap).getPropertyValue("--boards-inset").trim();
492
+ const inset = Number(declared.replace(/px$/, ""));
493
+ return Number.isFinite(inset) ? inset : 0;
494
+ }
495
+
486
496
  /**
487
497
  * Compute and apply a geometric fit. Pure arithmetic — it declares nothing.
488
498
  *
@@ -495,7 +505,9 @@ function applyGeometricFit(surface: CanvasSurface, axis: "width" | "page", maxZo
495
505
  return;
496
506
  }
497
507
  const wrapWidth = surface.wrap.clientWidth;
498
- const wrapHeight = surface.wrap.clientHeight;
508
+ // The boards' own top clearance (`--boards-inset`, the Project Styles bar) is screen space the
509
+ // Artboards cannot use, so the page is fitted into what is left below it.
510
+ const wrapHeight = surface.wrap.clientHeight - boardsInset(surface.panzoomWrap);
499
511
  const gap = 24;
500
512
  const padding = 32;
501
513
  let totalPanelWidth = 0;
@@ -763,11 +775,12 @@ export function resetZoom(surface: CanvasSurface = activeCanvasSurface()) {
763
775
  * hands a rect back), so one arithmetic serves a rect measured from a parent DOM element and a rect
764
776
  * measured inside a canvas iframe alike.
765
777
  *
766
- * **The viewport is the box that SCROLLS, not the stage.** Edit docks the Document Header card
767
- * above its scroller, inside the same stage cell, so there the stage is the band plus the page and
768
- * its centre is half a band too high: a reveal would land the node that far above the middle of
769
- * what the author can actually see. {@link revealScroller} is that box wherever there is one, and
770
- * where there is none (Design, Stylebook, a comparison) the stage is still the viewport.
778
+ * **The viewport is the box that SCROLLS, not the stage.** Over a collection entry Edit stands the
779
+ * writing banner above its scroller, inside the same stage cell, so there the stage is the banner
780
+ * plus the page and its centre is half a banner too high: a reveal would land the node that far
781
+ * above the middle of what the author can actually see. {@link revealScroller} is that box wherever
782
+ * there is one, and where there is none (Design, Stylebook, a comparison) the stage is still the
783
+ * viewport.
771
784
  */
772
785
  function centeringOffset(surface: CanvasSurface, rect: { top: number; height: number }): number {
773
786
  const viewport = rectOf(revealScroller(surface) ?? surface.wrap);
@@ -840,6 +853,37 @@ export function panToParentRect(
840
853
  revealBy(surface, centeringOffset(surface, rect), false);
841
854
  }
842
855
 
856
+ /**
857
+ * Bring a PARENT-VIEWPORT rect into view on a pan/zoom stage, moving as little as it must: not at
858
+ * all when the rect is already wholly in view, and never so far that the boards' top edge comes
859
+ * down past where it stands now.
860
+ *
861
+ * {@link panToParentRect} always centres, which is right for a jump to a diff change or a node the
862
+ * author asked to see. The stylebook's Outline is a list the author clicks DOWN, and centring its
863
+ * first rows meant the h1 at the very top of a fitted catalogue pulled every board half a pane
864
+ * down, leaving an empty band above them and no specimen that had moved into view.
865
+ */
866
+ export function revealParentRect(
867
+ rect: { top: number; height: number },
868
+ surface: CanvasSurface = activeCanvasSurface(),
869
+ ) {
870
+ const viewport = rectOf(surface.wrap);
871
+ if (rect.top >= viewport.top && rect.top + rect.height <= viewport.top + viewport.height) {
872
+ return;
873
+ }
874
+ const offset = centeringOffset(surface, rect);
875
+ const ceiling = Math.max(surface.panY, 0) - surface.panY;
876
+ const step = Math.min(offset, ceiling);
877
+ if (step !== 0) {
878
+ /* A reveal is a pan somebody asked for, so the entry's centring stands down as a user's pan
879
+ makes it: a frame still growing to its height would otherwise re-centre the stage (top
880
+ aligned) under the card just brought on screen, which is what a reveal on a fresh stylebook
881
+ canvas did. */
882
+ surface.needsCenter = false;
883
+ animatePanBy(surface, step);
884
+ }
885
+ }
886
+
843
887
  /** Animate a stage's `panY` by `offsetY` with the shared 250ms ease-out. */
844
888
  function animatePanBy(surface: CanvasSurface, offsetY: number) {
845
889
  const startY = surface.panY;
@@ -874,6 +918,7 @@ export const CANVAS_MODES = [
874
918
  "preview",
875
919
  "source",
876
920
  "stylebook",
921
+ "tokens",
877
922
  "grid",
878
923
  "git-diff",
879
924
  ] as const;
@@ -1144,7 +1189,7 @@ export function canvasViewCommands(deps: CanvasCommandDeps): AnyCommand[] {
1144
1189
  description:
1145
1190
  "Switch the active document's editor mode. edit and design put its element tree on " +
1146
1191
  "the canvas, where the document tools work; preview renders it as a visitor sees it; " +
1147
- "source shows the JSON; stylebook, grid and git-diff open other editors. Use it to " +
1192
+ "source shows the JSON; stylebook, tokens, grid and git-diff open other editors. Use it to " +
1148
1193
  "return a document to edit or design when the tree tools are refused.",
1149
1194
  name: "set_canvas_mode",
1150
1195
  /* `after.editor.kind` rather than the tab's own field: it is the gate's read (`treeEditable`
@@ -1411,9 +1456,9 @@ export function canvasViewCommands(deps: CanvasCommandDeps): AnyCommand[] {
1411
1456
  * preset owns it. What is left over is genuinely a rendering context, and it is the half of
1412
1457
  * site-architecture.md §13.4 the canvas can show today: the artboard root's `lang` and `dir`.
1413
1458
  * That is why this is a verb and not a label — an RTL locale mirrors the layout on screen.
1414
- * (That section's other half, `$page.locale` in the rendered state, is injected by the
1415
- * compiler and not by the canvas's `substitutePreviewParams`; previewing it is a separate
1416
- * change to the render path.)
1459
+ * (That section's other half, `$page.locale`, is injected into the canvas render from the
1460
+ * page's route, as the build injects it (studio.md §4.1). This verb leaves it alone: the
1461
+ * pane's language is a rendering context, not the page's (§20.2).)
1417
1462
  *
1418
1463
  * It lives in this file rather than in `i18n/i18n-commands.ts` because `contextTab`, `paneArg`
1419
1464
  * and `repaint` are local closures here, and every rendering-context verb needs all three: the