@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,372 @@
1
+ /**
2
+ * Switching workspaces — the tab half of `view.setWorkspace`, and the rule that keeps the workspace
3
+ * on screen and the tab on screen in agreement.
4
+ *
5
+ * Two invariants, both stated in `docs/studio/interface.md`, "Workspaces":
6
+ *
7
+ * 1. **Activating a tab enters its workspace.** Whatever activated it — a click in the Files tree,
8
+ * "Open" on a Content row, a search result, session restore — the workspace follows the tab, so
9
+ * opening a page from Content lands in Design without anybody asking for Design. This is an
10
+ * EFFECT over the active tab rather than a line in every opener, so no opener can forget it.
11
+ * 2. **Showing a workspace shows its tabs.** `view.setWorkspace` activates the workspace's most
12
+ * recently used tab, or opens its home when it has none: the Content, Media and Publish homes,
13
+ * `project.json` in its stylebook editor for Styles. Design has no home; with no design tab open
14
+ * its stage is empty, which is the home a canvas has.
15
+ *
16
+ * Every pane follows the switch, the side pane included: a pane shows its own most recent tab of
17
+ * the workspace, or none, and its strip draws only that workspace's tabs (`panels/tab-strip.ts`).
18
+ * Nothing is closed by a switch. A tab of another workspace keeps its place in its pane's strip and
19
+ * comes back when its workspace does.
20
+ *
21
+ * The rule holds for the panes the author is NOT in as well. A read into another pane (a session
22
+ * restore, `pane.compareWith`) can put a tab of another workspace on that pane's stage without
23
+ * activating anything the follow sees, and the pane would draw a document under a strip that hides
24
+ * it. A second effect re-points such a pane at its own tab of the workspace, or clears it
25
+ * ({@link alignHiddenPanes}); `pane.compareWith` asks for the comparison to be SHOWN, so it enters
26
+ * the compared document's workspace instead ({@link revealInPane}).
27
+ *
28
+ * @docs studio/interface
29
+ */
30
+
31
+ import { effect, effectScope, pauseTracking, resetTracking } from "../reactivity";
32
+ import {
33
+ WORKSPACE_PRESETS,
34
+ enterWorkspace,
35
+ presetInspectorOpen,
36
+ setDockCollapsed,
37
+ shell,
38
+ } from "../shell";
39
+ import {
40
+ activateTab,
41
+ activeTab,
42
+ clearPaneActiveTab,
43
+ closeTab,
44
+ paneById,
45
+ paneOfTab,
46
+ setCloseLanding,
47
+ setEmptyPaneFallback,
48
+ setTabScope,
49
+ workspace,
50
+ } from "./workspace";
51
+ import { homeWorkspaceOfTabId, isHomeWorkspace, workspaceHomeTabId } from "./workspace-ids";
52
+ import {
53
+ isTabInWorkspace,
54
+ lastTabOfWorkspace,
55
+ paneTabOfWorkspace,
56
+ workspaceOfTab,
57
+ writesOnCanvas,
58
+ } from "./tab-workspace";
59
+ import type { WorkspaceDeps } from "../shell";
60
+ import type { HomeWorkspaceId, WorkspaceId } from "./workspace-ids";
61
+ import type { EffectScope } from "@vue/reactivity";
62
+
63
+ /**
64
+ * What a switch needs that this module may not import: the opener of the home that belongs to
65
+ * another module (the configuration document), the generic homes' opener, and the Inspector's tab
66
+ * writer.
67
+ */
68
+ export interface WorkspaceSwitchDeps extends WorkspaceDeps {
69
+ /** Open (or reveal) `project.json` in its stylebook editor: the Styles workspace's home. */
70
+ openStyles: () => void;
71
+ /** Open (or activate) a generic home tab (`workspace/workspace-home.ts`). */
72
+ openHome: (id: HomeWorkspaceId) => void;
73
+ }
74
+
75
+ /** What the follow needs: the frame's deps, and the writers that keep a home within reach. */
76
+ export interface WorkspaceFollowDeps extends WorkspaceDeps {
77
+ /** Put a home in a pane's strip without showing it (`workspace-home.ts` `placeWorkspaceHome`). */
78
+ placeHome?: (id: HomeWorkspaceId, paneId: string) => void;
79
+ /** Reopen `project.json` in the Styles home view, when a close left Styles with nothing open. */
80
+ openStyles?: () => void;
81
+ }
82
+
83
+ /**
84
+ * Whether entering `id` on `tab` should put the workspace's home beside it: a workspace with a home
85
+ * and no tab strip, entered on a tab that is not that home. The pane context bar's Back control
86
+ * (`workspace/workspace-back.ts`) closes the view and lands on the home, so the home has to be
87
+ * there to land on: a view of such a workspace never stands in its pane alone.
88
+ */
89
+ export function wantsHomeBeside(id: WorkspaceId, tabId: string): id is HomeWorkspaceId {
90
+ return (
91
+ isHomeWorkspace(id) && !WORKSPACE_PRESETS[id].tabStrip && homeWorkspaceOfTabId(tabId) !== id
92
+ );
93
+ }
94
+
95
+ /**
96
+ * The views a newly shown view replaces in `paneId`: in a workspace with a home and no tab strip,
97
+ * every other view of that workspace in the pane that can go without losing anything (not its home,
98
+ * not pinned, nothing unsaved in it).
99
+ *
100
+ * A strip-less pane holds its home and ONE view beside it (`workspace/workspace-back.ts`). With no
101
+ * strip, a second viewer opened from the Site panel, or a viewer left by a workspace switch rather
102
+ * than by Back, would otherwise stay open where nothing draws it: reachable only by ⌃Tab, and the
103
+ * tab the next visit to the workspace landed on instead of its home.
104
+ */
105
+ export function viewsReplacedBy(
106
+ paneId: string,
107
+ tabId: string,
108
+ id: WorkspaceId = shell.workspace,
109
+ ): string[] {
110
+ const pane = paneById(paneId);
111
+ if (!pane || !wantsHomeBeside(id, tabId)) {
112
+ return [];
113
+ }
114
+ return pane.tabOrder.filter((other) => {
115
+ const tab = workspace.tabs.get(other);
116
+ return (
117
+ other !== tabId &&
118
+ tab !== undefined &&
119
+ homeWorkspaceOfTabId(other) !== id &&
120
+ workspaceOfTab(tab) === id &&
121
+ !tab.pinned &&
122
+ !tab.doc.dirty
123
+ );
124
+ });
125
+ }
126
+
127
+ /**
128
+ * Where closing a pane's active tab lands in a workspace with no tab strip: its home, when the home
129
+ * is in that pane. `null` everywhere else, which leaves the most recently used tab in charge.
130
+ */
131
+ export function stripLessLanding(paneId: string, id: WorkspaceId = shell.workspace): string | null {
132
+ if (!isHomeWorkspace(id) || WORKSPACE_PRESETS[id].tabStrip) {
133
+ return null;
134
+ }
135
+ const home = workspaceHomeTabId(id);
136
+ return paneById(paneId)?.tabOrder.includes(home) === true ? home : null;
137
+ }
138
+
139
+ /**
140
+ * Point every pane except `except` at the workspace: its own most recent tab there, or none.
141
+ *
142
+ * `except` is the pane whose active tab the caller has just chosen, so the pane being switched into
143
+ * is not second-guessed.
144
+ */
145
+ function alignPanes(id: WorkspaceId, except: string | null): void {
146
+ for (const pane of workspace.panes) {
147
+ if (pane.id === except || pane.derived) {
148
+ continue;
149
+ }
150
+ const target = paneTabOfWorkspace(pane, id);
151
+ if (target === pane.activeTabId) {
152
+ continue;
153
+ }
154
+ if (target) {
155
+ activateTab(target, { focus: false });
156
+ } else {
157
+ clearPaneActiveTab(pane.id);
158
+ }
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Point every pane the author is not in, whose active tab belongs to another workspace, at its own
164
+ * tab of the workspace on screen, or at none. The focused pane is the follow's: its active tab
165
+ * DECIDES the workspace rather than disagreeing with it. A derived pane follows its source.
166
+ */
167
+ function alignHiddenPanes(): void {
168
+ const id = shell.workspace;
169
+ const focused = workspace.activePaneId;
170
+ for (const pane of workspace.panes) {
171
+ const tabId = pane.activeTabId;
172
+ if (pane.id === focused || pane.derived || !tabId || isTabInWorkspace(tabId, id)) {
173
+ continue;
174
+ }
175
+ // The writes below are this effect's own correction, not something it should track.
176
+ pauseTracking();
177
+ try {
178
+ const target = paneTabOfWorkspace(pane, id);
179
+ if (target) {
180
+ activateTab(target, { focus: false });
181
+ } else {
182
+ clearPaneActiveTab(pane.id);
183
+ }
184
+ } finally {
185
+ resetTracking();
186
+ }
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Show what a read put into `paneId` at `path` when it belongs to another workspace: activate it
192
+ * (moving the keyboard there), which enters its workspace through the follow. A tab of the
193
+ * workspace on screen is left exactly as the read left it.
194
+ *
195
+ * `pane.compareWith` is the caller: comparing is a request to SEE the other document, and leaving
196
+ * it hidden in a strip that filters it out would be a command that visibly did nothing.
197
+ */
198
+ export function revealInPane(paneId: string, path: string): void {
199
+ const pane = paneById(paneId);
200
+ const tabId = pane?.tabOrder.find((id) => workspace.tabs.get(id)?.documentPath === path);
201
+ if (tabId && !isTabInWorkspace(tabId)) {
202
+ activateTab(tabId);
203
+ }
204
+ }
205
+
206
+ /** The active tab is being written on the canvas: what opens Content's Inspector. */
207
+ function activeWritesOnCanvas(): boolean {
208
+ const tab = activeTab.value;
209
+ return tab ? writesOnCanvas(tab) : false;
210
+ }
211
+
212
+ /** The deps a frame is laid out with: the caller's, plus the rule that reads the active tab. */
213
+ function frameDeps(deps: WorkspaceDeps): WorkspaceDeps {
214
+ return { ...deps, writingOnCanvas: activeWritesOnCanvas };
215
+ }
216
+
217
+ /** Open a workspace's home. Design has none, and answers `false`. */
218
+ function openHome(id: WorkspaceId, deps: WorkspaceSwitchDeps): boolean {
219
+ if (id === "styles") {
220
+ deps.openStyles();
221
+ return true;
222
+ }
223
+ if (isHomeWorkspace(id)) {
224
+ deps.openHome(id);
225
+ return true;
226
+ }
227
+ return false;
228
+ }
229
+
230
+ /**
231
+ * Show a workspace: enter its frame and put its tabs on screen.
232
+ *
233
+ * Idempotent. Showing the workspace already on screen re-activates its most recent tab, which is
234
+ * the tab already showing, so a second ⌘2 changes nothing.
235
+ */
236
+ export function switchWorkspace(id: WorkspaceId, deps: WorkspaceSwitchDeps): void {
237
+ /* The TAB first, the frame second. Activating the workspace's tab enters the workspace through
238
+ the follow below, with that tab already active, which is what lets the preset's Inspector tab
239
+ land on the document it is for: entering first would write Styles' "Style" tab onto whatever
240
+ Design document was showing. The explicit entry after it covers the two cases no activation
241
+ reports: a tab that was ALREADY active, and Design with nothing open. */
242
+ const recent = lastTabOfWorkspace(id);
243
+ if (recent) {
244
+ activateTab(recent);
245
+ } else {
246
+ openHome(id, deps);
247
+ }
248
+ enterWorkspace(id, frameDeps(deps));
249
+ const tab = activeTab.value;
250
+ const kept = tab && isTabInWorkspace(tab.id, id) ? (paneOfTab(tab.id)?.id ?? null) : null;
251
+ alignPanes(id, kept);
252
+ }
253
+
254
+ let _scope: EffectScope | null = null;
255
+
256
+ /**
257
+ * Start the rule that activating a tab enters its workspace. Idempotent; the bootstrap calls it
258
+ * once, after the shell has mounted. Returns the function that stops it again and drops the
259
+ * fallback scope, which a window never needs and a test does.
260
+ *
261
+ * It also scopes the panes' fallback (`workspace.ts`'s `setTabScope`), so closing a workspace's
262
+ * last tab never switches the workspace under the author: the pane is left empty, or, in a
263
+ * workspace with no tab strip, shows its home (`setEmptyPaneFallback`, {@link fallBackToHome}).
264
+ */
265
+ export function installWorkspaceFollow(deps: WorkspaceFollowDeps): () => void {
266
+ if (_scope) {
267
+ return stopWorkspaceFollow;
268
+ }
269
+ setTabScope((tabId) => isTabInWorkspace(tabId));
270
+ setEmptyPaneFallback((paneId) => fallBackToHome(paneId, deps));
271
+ setCloseLanding((paneId) => stripLessLanding(paneId));
272
+ const scope = effectScope();
273
+ _scope = scope;
274
+ const withTab = frameDeps(deps);
275
+ /* Whether the active tab was being written on the canvas at the last run, so a `whileWriting`
276
+ Inspector (Content's) opens and closes on the TRANSITION, from the Content home into an
277
+ entry and back, and an author's own toggle on the same tab is left alone. */
278
+ let writing: boolean | null = null;
279
+ scope.run(() => {
280
+ effect(() => {
281
+ const tab = activeTab.value;
282
+ if (!tab) {
283
+ return;
284
+ }
285
+ // What the tab IS is tracked — its identity, its editor, its path — because `project.json`
286
+ // Moves between Design and Styles with its mode. Nothing else is.
287
+ const id = workspaceOfTab(tab);
288
+ const nowWriting = writesOnCanvas(tab);
289
+ /* The entry runs UNTRACKED. It reads `shell.workspace` and the panes, and an effect that
290
+ depended on them would re-run the moment `switchWorkspace` entered a workspace and, finding
291
+ the OLD tab still active for one more statement, enter the old workspace straight back. */
292
+ pauseTracking();
293
+ try {
294
+ const paneId = paneOfTab(tab.id)?.id ?? null;
295
+ // The view on screen replaces the ones beside it nobody can see (a strip-less pane only).
296
+ if (paneId !== null) {
297
+ for (const replaced of viewsReplacedBy(paneId, tab.id, id)) {
298
+ closeTab(replaced);
299
+ }
300
+ }
301
+ if (enterWorkspace(id, withTab)) {
302
+ alignPanes(id, paneId);
303
+ if (paneId !== null && wantsHomeBeside(id, tab.id)) {
304
+ deps.placeHome?.(id, paneId);
305
+ }
306
+ } else if (
307
+ writing !== null &&
308
+ nowWriting !== writing &&
309
+ WORKSPACE_PRESETS[id].inspector.whileWriting === true
310
+ ) {
311
+ setDockCollapsed("right", !presetInspectorOpen(id, nowWriting));
312
+ }
313
+ } finally {
314
+ resetTracking();
315
+ }
316
+ writing = nowWriting;
317
+ });
318
+ /* The panes the author is not in. DEFERRED to a microtask, because a switch moves the active
319
+ tab and the focused pane in two writes, and an effect run between them would see the pane
320
+ being switched INTO as a hidden pane showing the wrong workspace, and point it straight
321
+ back. By the microtask every synchronous switch has finished. */
322
+ let queued = false;
323
+ const runner = effect(alignHiddenPanes, {
324
+ scheduler: () => {
325
+ if (queued) {
326
+ return;
327
+ }
328
+ queued = true;
329
+ queueMicrotask(() => {
330
+ queued = false;
331
+ if (_scope === scope) {
332
+ runner();
333
+ }
334
+ });
335
+ },
336
+ });
337
+ });
338
+ return stopWorkspaceFollow;
339
+ }
340
+
341
+ /**
342
+ * A close left `paneId` with no tab of the workspace on screen: in a workspace with no tab strip,
343
+ * show its home there. Media, Styles and Publish have no strip to reach another tab from, so an
344
+ * empty stage there is a dead end (closing the home itself, or the viewer beside a home closed
345
+ * earlier), where Design's empty stage is its home and Content's strip and Collections panel are
346
+ * the way back. Styles' home is `project.json` itself, reopened in the view it was left in.
347
+ */
348
+ function fallBackToHome(paneId: string, deps: WorkspaceFollowDeps): void {
349
+ const id = shell.workspace;
350
+ const pane = paneById(paneId);
351
+ if (!pane || pane.derived || WORKSPACE_PRESETS[id].tabStrip) {
352
+ return;
353
+ }
354
+ if (id === "styles") {
355
+ deps.openStyles?.();
356
+ return;
357
+ }
358
+ if (!deps.placeHome || !isHomeWorkspace(id)) {
359
+ return;
360
+ }
361
+ deps.placeHome(id, paneId);
362
+ activateTab(workspaceHomeTabId(id));
363
+ }
364
+
365
+ /** Stop the follow and drop the fallback scope. */
366
+ function stopWorkspaceFollow(): void {
367
+ _scope?.stop();
368
+ _scope = null;
369
+ setTabScope(null);
370
+ setEmptyPaneFallback(null);
371
+ setCloseLanding(null);
372
+ }
@@ -578,9 +578,15 @@ export function closePane(paneId: string) {
578
578
  for the side pane's: focused=primary showing=a became focused=primary showing=b, and the only
579
579
  way back was the tab strip. Nothing is lost either way, because the closing pane's tabs have
580
580
  just been moved into the survivor above; this only decides which of them is on screen. */
581
+ /* …unless it belongs to another workspace than the one on screen. A derived pane follows the
582
+ author into every workspace, still showing its source's document, so Close Side Pane run from
583
+ its own menu in Media would hand the survivor a Design document and carry the author off to
584
+ Design. The survivor's own tab of the workspace stays in front instead. */
585
+ const closingShows =
586
+ pane.activeTabId !== null && _tabScope(pane.activeTabId) ? pane.activeTabId : null;
581
587
  survivor.activeTabId =
582
588
  workspace.activePaneId === pane.id
583
- ? (pane.activeTabId ?? survivor.activeTabId)
589
+ ? (closingShows ?? survivor.activeTabId ?? pane.activeTabId)
584
590
  : (survivor.activeTabId ?? pane.activeTabId);
585
591
  workspace.activePaneId = survivor.id;
586
592
  /* Invariant D4, and it goes HERE — after the survivor has inherited the tabs and the focus, and
@@ -621,6 +627,72 @@ export function paneIsEmpty(pane: Pane): boolean {
621
627
  live panes now (§18.3), so a zoom is buildable, but no section of studio.md asks for one; it
622
628
  comes back with the section that does, and not one release earlier. */
623
629
 
630
+ /**
631
+ * Which tabs a fallback may land on: the tabs of the workspace on screen.
632
+ *
633
+ * When the active tab leaves a pane (closed, or moved), the pane falls back to another of its tabs.
634
+ * A fallback onto a tab of a DIFFERENT workspace would switch the workspace under the author, who
635
+ * closed a tab and did not ask to go anywhere — so the fallback is scoped, and a pane with no tab
636
+ * of the workspace left shows none. The predicate is INJECTED by `workspace/workspace-switch.ts`,
637
+ * because the rule that says which workspace a tab is in reads the project's collections, and this
638
+ * module sits below all of that. Unset, every tab qualifies, which is the model before workspaces.
639
+ */
640
+ let _tabScope: (tabId: string) => boolean = () => true;
641
+
642
+ /** Install the fallback scope. `workspace/workspace-switch.ts` calls it once at boot. */
643
+ export function setTabScope(scope: ((tabId: string) => boolean) | null): void {
644
+ _tabScope = scope ?? (() => true);
645
+ }
646
+
647
+ /**
648
+ * What a pane shows when a close leaves it no tab in scope, or nothing (the stage stays empty).
649
+ *
650
+ * Injected by `workspace/workspace-switch.ts` for the same reason {@link setTabScope} is: the answer
651
+ * (a Navigator-less workspace falls back to its home) is a workspace rule, and this module sits
652
+ * below the workspaces. Only {@link closeTab} asks, because only a close is the author emptying a
653
+ * pane; a project switch's {@link closeAllTabs} empties every pane on its way to a new project and
654
+ * must not plant a home in it.
655
+ */
656
+ let _emptyFallback: ((paneId: string) => void) | null = null;
657
+
658
+ /** Install the empty-pane fallback. `workspace/workspace-switch.ts` calls it once at boot. */
659
+ export function setEmptyPaneFallback(fallback: ((paneId: string) => void) | null): void {
660
+ _emptyFallback = fallback;
661
+ }
662
+
663
+ /**
664
+ * Where a close of the ACTIVE tab lands in a pane, ahead of the most recently used tab: a tab id,
665
+ * or `null` for no preference.
666
+ *
667
+ * Injected by `workspace/workspace-switch.ts`, for the reason {@link setTabScope} is. A workspace
668
+ * that draws no tab strip (Media, Publish) shows its home and one view opened beside it, and
669
+ * closing that view is the author going back to the home: the most recently used tab could be
670
+ * another view the strip-less pane is not drawing, which would put the author somewhere they did
671
+ * not ask to go and cannot see the way out of.
672
+ */
673
+ let _closeLanding: ((paneId: string) => string | null) | null = null;
674
+
675
+ /** Install the close landing. `workspace/workspace-switch.ts` calls it once at boot. */
676
+ export function setCloseLanding(landing: ((paneId: string) => string | null) | null): void {
677
+ _closeLanding = landing;
678
+ }
679
+
680
+ /** How many open tabs are in scope: the workspace on screen's, which is what ⌃Tab walks. */
681
+ export function scopedTabCount(): number {
682
+ let count = 0;
683
+ for (const id of workspace.tabs.keys()) {
684
+ if (_tabScope(id)) {
685
+ count += 1;
686
+ }
687
+ }
688
+ return count;
689
+ }
690
+
691
+ /** The rightmost tab of a pane that is in scope: the safe default when its active tab leaves. */
692
+ function rightmostInScope(pane: Pane): string | null {
693
+ return pane.tabOrder.findLast((id) => _tabScope(id)) ?? null;
694
+ }
695
+
624
696
  /**
625
697
  * Remove a tab id from whichever pane holds it, without touching the tab itself.
626
698
  *
@@ -636,7 +708,7 @@ export function detachTab(tabId: string) {
636
708
  }
637
709
  pane.tabOrder = pane.tabOrder.filter((id) => id !== tabId);
638
710
  if (pane.activeTabId === tabId) {
639
- pane.activeTabId = pane.tabOrder.at(-1) ?? null;
711
+ pane.activeTabId = rightmostInScope(pane);
640
712
  }
641
713
  /* The PRIMARY is exempt HERE and only here. `splitRight` moves the tab out of the source pane
642
714
  through this function, so a lone document split from the primary empties it — and that state
@@ -789,6 +861,22 @@ function syncTreeSelection(tab: Tab) {
789
861
  }
790
862
  }
791
863
 
864
+ /**
865
+ * Show no tab in a pane, leaving its strip exactly as it is.
866
+ *
867
+ * What a pane shows when the workspace on screen has no tab in it: its other tabs are another
868
+ * workspace's, and they come back when that workspace does. Written only by
869
+ * `workspace/workspace-switch.ts`; nothing about the tabs, the MRU order or the focus changes.
870
+ *
871
+ * @param {string} paneId
872
+ */
873
+ export function clearPaneActiveTab(paneId: string) {
874
+ const pane = paneById(paneId);
875
+ if (pane) {
876
+ pane.activeTabId = null;
877
+ }
878
+ }
879
+
792
880
  /**
793
881
  * Make `tabId` the active tab OF ITS OWN PANE. The one place activation happens.
794
882
  *
@@ -878,7 +966,28 @@ export function openTab(opts: {
878
966
  paneId?: string;
879
967
  focus?: boolean;
880
968
  layout?: JsonLayout | null;
969
+ /** The text a format-class file was read as (`createTab`'s `source`). */
970
+ source?: string | null;
971
+ /** The mode it opens in, when not its first declared one (`createTab`). */
972
+ canvasMode?: string;
973
+ /**
974
+ * Put a NEW tab in the pane's strip without activating it; a tab already open is left exactly as
975
+ * it is. For a tab that belongs beside the one on screen rather than in front of it: a
976
+ * workspace's home kept within reach of a document of that workspace.
977
+ */
978
+ background?: boolean;
881
979
  }) {
980
+ if (opts.background === true) {
981
+ const open = workspace.tabs.get(opts.id);
982
+ if (open) {
983
+ return open;
984
+ }
985
+ const tab = createTab({ ...opts, preview: false });
986
+ const pane = (opts.paneId === undefined ? undefined : paneById(opts.paneId)) ?? activePane();
987
+ insertIntoPane(pane, tab.id);
988
+ workspace.tabs.set(tab.id, tab);
989
+ return tab;
990
+ }
882
991
  const previous = workspace.tabs.get(opts.id);
883
992
  const preview = opts.preview === true && previous?.pinned !== true;
884
993
  const tab = createTab({ ...opts, preview });
@@ -1058,12 +1167,15 @@ export function closeTab(tabId: string) {
1058
1167
  // It, which is almost never the rightmost tab in the strip. `detachTab` has already put the
1059
1168
  // Rightmost there as the safe default, so this only ever improves on it.
1060
1169
  const survivor = activePane();
1170
+ const landing = _closeLanding?.(survivor.id) ?? null;
1061
1171
  const next =
1062
- workspace.mruOrder.find((id) => survivor.tabOrder.includes(id)) ??
1063
- survivor.tabOrder.at(-1) ??
1064
- null;
1172
+ (landing !== null && survivor.tabOrder.includes(landing) ? landing : null) ??
1173
+ workspace.mruOrder.find((id) => survivor.tabOrder.includes(id) && _tabScope(id)) ??
1174
+ rightmostInScope(survivor);
1065
1175
  if (next) {
1066
1176
  setActiveTab(next);
1177
+ } else {
1178
+ _emptyFallback?.(survivor.id);
1067
1179
  }
1068
1180
  }
1069
1181
 
@@ -1144,7 +1256,8 @@ export function takeClosedTabPath(): string | undefined {
1144
1256
  */
1145
1257
  export function cycleTab(step: number): string | undefined {
1146
1258
  if (!_cycleList) {
1147
- _cycleList = workspace.mruOrder.filter((id) => workspace.tabs.has(id));
1259
+ // The workspace on screen's tabs: ⌃Tab walks what the strips draw, never into another workspace.
1260
+ _cycleList = workspace.mruOrder.filter((id) => workspace.tabs.has(id) && _tabScope(id));
1148
1261
  _cycleIndex = 0;
1149
1262
  }
1150
1263
  const list = _cycleList;
@@ -1344,9 +1457,10 @@ export function registerTabCommands(registry: CommandRegistry, deps: TabCommandD
1344
1457
  * @returns {AnyCommand[]}
1345
1458
  */
1346
1459
  export function tabCommands(deps: TabCommandDeps): AnyCommand[] {
1347
- // The MRU list is workspace-wide, so cycling is available whenever a second document is open —
1348
- // In either pane, not just the focused one's strip.
1349
- const twoOrMoreTabs = () => workspace.tabs.size > 1;
1460
+ /* Cycling walks the workspace on screen's tabs, in either pane, not just the focused one's strip
1461
+ (`cycleTab`), so it is available when that workspace has a second tab open. In a workspace
1462
+ with no strip that is its home and a view opened beside it: ⌃Tab moves between the two. */
1463
+ const twoOrMoreTabs = () => scopedTabCount() > 1;
1350
1464
  return [
1351
1465
  {
1352
1466
  id: "document.nextTab",
@@ -1358,7 +1472,7 @@ export function tabCommands(deps: TabCommandDeps): AnyCommand[] {
1358
1472
  group: "2_navigate",
1359
1473
  when: (ctx) => ctx.document.open,
1360
1474
  enablement: twoOrMoreTabs,
1361
- requires: "a second open document",
1475
+ requires: "a second open document in this workspace",
1362
1476
  run: () => {
1363
1477
  cycleTab(1);
1364
1478
  },
@@ -1373,7 +1487,7 @@ export function tabCommands(deps: TabCommandDeps): AnyCommand[] {
1373
1487
  group: "2_navigate",
1374
1488
  when: (ctx) => ctx.document.open,
1375
1489
  enablement: twoOrMoreTabs,
1376
- requires: "a second open document",
1490
+ requires: "a second open document in this workspace",
1377
1491
  run: () => {
1378
1492
  cycleTab(-1);
1379
1493
  },
@@ -51,15 +51,17 @@
51
51
  container-type: inline-size;
52
52
  container-name: toolbar;
53
53
  }
54
- #toolbar.electrobun-webkit-app-region-drag {
54
+ /* A window handle under the frameless desktop window: the Command Bar (#toolbar) and the
55
+ boot-failure strip carry it, and the Electron shell drags the window by any area it covers.
56
+ Unscoped because the class name is self-describing. A no-op in a browser tab. */
57
+ .app-region-drag {
55
58
  -webkit-app-region: drag;
56
59
  }
57
- /* The mirror of #toolbar's own drag class above: every interactive control inside the Command Bar
58
- carries this (commandbar.json) so Electrobun's ancestry walk finds a working no-drag signal on
59
- the button itself instead of falling through to the toolbar's drag region. Unscoped, like
60
- .resize-edge below, because the class name is self-describing and used nowhere else. A no-op
61
- outside Electrobun. */
62
- .electrobun-webkit-app-region-no-drag {
60
+ /* The mirror of the drag class above: it marks an interactive control inside a drag region
61
+ (commandbar.json) so a press on the button reaches the button instead of dragging the window.
62
+ Unscoped, like .resize-edge below, because the class name is self-describing. A no-op outside a
63
+ frameless desktop window. */
64
+ .app-region-no-drag {
63
65
  -webkit-app-region: no-drag;
64
66
  }
65
67
  /* Resize edge zones */
@@ -201,6 +203,27 @@
201
203
  #app.bottom-collapsed #resize-bottom {
202
204
  display: none;
203
205
  }
206
+ /* The rail column, out of the grid. `src/shell.ts`'s applyDockLayout() stamps the class from the
207
+ active workspace's preset: the rail is Design's, and every other workspace gives its 56px to the
208
+ pane. The column is zero rather than removed so every named line below keeps its place. */
209
+ #app.rail-hidden {
210
+ grid-template-columns: [rail] 0px [nav] var(--panel-w-left) [pane] 1fr [insp] var(--panel-w-right);
211
+ }
212
+ #app.rail-hidden #activity-bar {
213
+ display: none;
214
+ }
215
+ /* Every pane's document tab strip, out of the cell. `src/shell.ts`'s applyDockLayout() stamps the
216
+ class from the active workspace's preset (`tabStrip`): Design and Content draw strips, and
217
+ Media, Styles and Publish draw their home surface alone, with the pane context bar's Back
218
+ control leading out of anything opened beside it. The strip stays mounted underneath, so
219
+ returning to a workspace with strips repaints nothing. A DERIVED pane's strip is the exception,
220
+ whenever it draws the derivation chip: the chip says what the side pane is a projection of, and
221
+ its trailing ✕ (Close Side Pane) is a lens's only visible exit, since a lens's context bar draws
222
+ neither the preset menu nor a Back control. Derived panes follow the author into every
223
+ workspace, so hiding that row would leave a pane nobody can name or close. */
224
+ #app.tabs-hidden #pane-grid [data-pane-id] > [part="strip"]:not(:has([part="derivation"])) {
225
+ display: none;
226
+ }
204
227
  /* ⑫ Status bar Three fields in the shell's own level order, separated by a rule rather than by a
205
228
  run of "|" characters: the boundary between PROJECT, DOCUMENT and SELECTION is structural, and
206
229
  the eye should be able to find it without reading. */
@@ -32,11 +32,12 @@
32
32
  "container-type": "inline-size",
33
33
  "container-name": "toolbar"
34
34
  },
35
- "#toolbar.electrobun-webkit-app-region-drag": {
35
+ ".app-region-drag": {
36
+ "$description": "A window handle under the frameless desktop window: the Command Bar (#toolbar) and the boot-failure strip carry it, and the Electron shell drags the window by any area it covers. Unscoped because the class name is self-describing. A no-op in a browser tab.",
36
37
  "-webkit-app-region": "drag"
37
38
  },
38
- ".electrobun-webkit-app-region-no-drag": {
39
- "$description": "The mirror of #toolbar's own drag class above: every interactive control inside the Command Bar carries this (commandbar.json) so Electrobun's ancestry walk finds a working no-drag signal on the button itself instead of falling through to the toolbar's drag region. Unscoped, like .resize-edge below, because the class name is self-describing and used nowhere else. A no-op outside Electrobun.",
39
+ ".app-region-no-drag": {
40
+ "$description": "The mirror of the drag class above: it marks an interactive control inside a drag region (commandbar.json) so a press on the button reaches the button instead of dragging the window. Unscoped, like .resize-edge below, because the class name is self-describing. A no-op outside a frameless desktop window.",
40
41
  "-webkit-app-region": "no-drag"
41
42
  },
42
43
  ".resize-edge": {
@@ -169,6 +170,17 @@
169
170
  "#app.bottom-collapsed #bottom-dock, #app.bottom-collapsed #resize-bottom": {
170
171
  "display": "none"
171
172
  },
173
+ "#app.rail-hidden": {
174
+ "$description": "The rail column, out of the grid. `src/shell.ts`'s applyDockLayout() stamps the class from the active workspace's preset: the rail is Design's, and every other workspace gives its 56px to the pane. The column is zero rather than removed so every named line below keeps its place.",
175
+ "grid-template-columns": "[rail] 0px [nav] var(--panel-w-left) [pane] 1fr [insp] var(--panel-w-right)"
176
+ },
177
+ "#app.rail-hidden #activity-bar": {
178
+ "display": "none"
179
+ },
180
+ "#app.tabs-hidden #pane-grid [data-pane-id] > [part=\"strip\"]:not(:has([part=\"derivation\"]))": {
181
+ "$description": "Every pane's document tab strip, out of the cell. `src/shell.ts`'s applyDockLayout() stamps the class from the active workspace's preset (`tabStrip`): Design and Content draw strips, and Media, Styles and Publish draw their home surface alone, with the pane context bar's Back control leading out of anything opened beside it. The strip stays mounted underneath, so returning to a workspace with strips repaints nothing. A DERIVED pane's strip is the exception, whenever it draws the derivation chip: the chip says what the side pane is a projection of, and its trailing ✕ (Close Side Pane) is a lens's only visible exit, since a lens's context bar draws neither the preset menu nor a Back control. Derived panes follow the author into every workspace, so hiding that row would leave a pane nobody can name or close.",
182
+ "display": "none"
183
+ },
172
184
  "#statusbar": {
173
185
  "$description": "\u246b Status bar Three fields in the shell's own level order, separated by a rule rather than by a run of \"|\" characters: the boundary between PROJECT, DOCUMENT and SELECTION is structural, and the eye should be able to find it without reading.",
174
186
  "grid-row": "4",