@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
@@ -1,17 +1,27 @@
1
1
  /// <reference lib="dom" />
2
2
  /**
3
- * Right panel — the Inspector dock: four tabs, one dock, one column.
3
+ * Right panel — the Inspector dock: three tabs, five views, one column (§6).
4
4
  *
5
- * **Content · Style · Logic · Assistant**, text-labelled. Three of those are renames of what was
6
- * here (Properties→Content, Events→Logic) and the fourth is the assistant, folded in from the fifth
7
- * grid column it used to own — which is the point of §3.1's "no assistant column": an inspector tab
8
- * costs zero additional width, so the canvas gets the ~300px back and the assistant is still one
9
- * key away.
5
+ * **Page · Element · Assistant**, text-labelled. Page is the open document's own settings (the
6
+ * former Navigator Page panel, re-hosted), titled for what the document is: Page, Template,
7
+ * Component, Entry or Document. Element is the selected node, and keeps the three views the strip
8
+ * used to spend three tabs on — Content, Style, Logic — behind a sub-switch in its body. The
9
+ * Assistant is folded in from the grid column it used to own, which is the point of §3.1's "no
10
+ * assistant column": an inspector tab costs zero additional width.
11
+ *
12
+ * The five LEAVES stay addressable (`shell.ts`'s `INSPECTOR_TAB_IDS`): `view.setRightTab { tab:
13
+ * "style" }` shows Element with Style chosen, and ⌘⇧1–5 follow the leaves left to right.
14
+ *
15
+ * **The strip follows the selection, and yields to the author.** When the selection goes from empty
16
+ * to non-empty while Page is showing, the strip switches to Element on the leaf last used there;
17
+ * when it becomes empty while Element is showing, it switches to Page. Only the TRANSITION
18
+ * switches: a tab the author clicks is kept until the selection next crosses empty, and the
19
+ * Assistant is never switched away from.
10
20
  *
11
21
  * **This file no longer draws anything.** The dock's markup, its ARIA and its style are
12
22
  * `surfaces/inspector-dock.json`, mounted through `surfaces/inspector-dock.ts`; what is left here
13
- * is the flow — which tab is selected, what the tab is pointed at, and which of the four seams each
14
- * body host is handed to. Two things came out of the move:
23
+ * is the flow — which leaf is selected, what the tab is pointed at, and which of the five seams
24
+ * each body host is handed to. Two things came out of the move:
15
25
  *
16
26
  * - **The strip is a real `tablist`.** It was `sp-tabs` with no `tabpanel` on the other end, so the
17
27
  * four bodies were anonymous scrolling divs: a screen reader was told a tab was selected and
@@ -31,17 +41,27 @@ import { getNodeAtPath, nodeLabel, requireShellRef, rightPanel } from "../store"
31
41
  import { effect, effectScope, reactive } from "../reactivity";
32
42
  import { activeTab } from "../workspace/workspace";
33
43
  import { primarySelection } from "../tabs/selection";
44
+ import { stylebookTarget } from "../style/stylebook-target";
34
45
  import { bindLogicPanelHost } from "./events-panel";
35
46
  import { bindContentHost } from "./properties-panel";
36
47
 
37
- import { DEFAULT_INSPECTOR_TAB, isInspectorTabId, shell } from "../shell";
38
- import { INSPECTOR_TABS } from "../commands/defaults";
48
+ import {
49
+ DEFAULT_ELEMENT_TAB,
50
+ DEFAULT_INSPECTOR_TAB,
51
+ isElementTabId,
52
+ isInspectorTabId,
53
+ shell,
54
+ } from "../shell";
55
+ import { INSPECTOR_STRIP, INSPECTOR_TABS } from "../commands/defaults";
56
+ import { bindPageHost, PAGE_TAB_EMPTY, pageTabTitle, renderPageTab } from "./head-panel";
39
57
  import { inspectorTabRegion } from "../ui/regions";
40
58
  import { bindStyleHost } from "./style-panel";
41
59
  import { mountInspectorDock } from "../surfaces/inspector-dock";
42
60
 
43
61
  import type { InspectorDockHandle, InspectorTabView } from "../surfaces/inspector-dock";
44
- import type { InspectorTabId } from "../shell";
62
+ import type { ElementTabId, InspectorTabId } from "../shell";
63
+ import type { InspectorStripId } from "../commands/defaults";
64
+ import type { Tab } from "../tabs/tab";
45
65
  import type { EffectScope } from "@vue/reactivity";
46
66
 
47
67
  interface RightPanelCtx {
@@ -74,7 +94,17 @@ let _dock: InspectorDockHandle | null = null;
74
94
  * reports whether the assistant is showing, and a plain field would leave that button lying on the
75
95
  * welcome screen — which is the whole class of bug `shell.ts` was split out to end.
76
96
  */
77
- const _detached = reactive({ tab: DEFAULT_INSPECTOR_TAB });
97
+ const _detached = reactive<{ tab: InspectorTabId; element: ElementTabId }>({
98
+ element: DEFAULT_ELEMENT_TAB,
99
+ tab: DEFAULT_INSPECTOR_TAB,
100
+ });
101
+
102
+ /**
103
+ * Whether each document had a selection the last time the dock looked, so the strip switches on a
104
+ * TRANSITION through empty rather than on every repaint (§6). Keyed on the tab, so focusing another
105
+ * document is not mistaken for its selection appearing.
106
+ */
107
+ const _hadSelection = new WeakMap<Tab, boolean>();
78
108
 
79
109
  /**
80
110
  * Hand one tab's body host to its owner, once, and never let that stop the dock.
@@ -95,15 +125,21 @@ function bindBody(ctx: RightPanelCtx, tabId: string, host: HTMLElement): void {
95
125
  }
96
126
 
97
127
  /**
98
- * The four seams, keyed by the tab whose body feeds them.
128
+ * The five seams, keyed by the leaf whose body feeds them.
99
129
  *
100
130
  * A table rather than a chain of `if`s so that the ONE thing this module still does with a body
101
- * host is stated in one place. Every entry is a binder another module published: three of them
102
- * mount a document of their own into the host, and the assistant's arrives from `studio.ts` because
103
- * a matching import here would be a cycle.
131
+ * host is stated in one place. Every entry is a binder another module published: four of them mount
132
+ * a document of their own into the host, and the assistant's arrives from `studio.ts` because a
133
+ * matching import here would be a cycle.
104
134
  */
105
135
  function bindBodyUnguarded(ctx: RightPanelCtx, tabId: string, host: HTMLElement): void {
106
136
  switch (tabId) {
137
+ case "page": {
138
+ /* The former Navigator Page panel, re-hosted unchanged: `head-panel.ts` draws it against the
139
+ focused document and empties it when nothing is open. */
140
+ bindPageHost(host);
141
+ break;
142
+ }
107
143
  case "assistant": {
108
144
  /* The assistant owns its container for the life of the window: it is the mount point for the
109
145
  Assistant's own Jx document, and rebuilding it would drop the transcript and the composer
@@ -127,7 +163,10 @@ function bindBodyUnguarded(ctx: RightPanelCtx, tabId: string, host: HTMLElement)
127
163
  /* The canvas mode is what the tab cannot read for itself — Stylebook edits a tag catalogue
128
164
  entry and Edit edits the selection — so it comes in from `studio.ts` the same way the
129
165
  Content tab's navigation door does. */
130
- bindStyleHost(host, { getCanvasMode: ctx.getCanvasMode });
166
+ bindStyleHost(host, {
167
+ getCanvasMode: ctx.getCanvasMode,
168
+ isShowing: () => inspectorTab() === "style",
169
+ });
131
170
  break;
132
171
  }
133
172
  default: {
@@ -139,16 +178,49 @@ function bindBodyUnguarded(ctx: RightPanelCtx, tabId: string, host: HTMLElement)
139
178
  }
140
179
  }
141
180
 
142
- /** The tab rows the document draws, with the selection resolved onto them. */
181
+ /** Which strip tab hosts a leaf. */
182
+ export function stripOf(leaf: InspectorTabId): InspectorStripId {
183
+ return INSPECTOR_TABS.find((tab) => tab.id === leaf)?.strip ?? "page";
184
+ }
185
+
186
+ /** The Page tab's title for the focused document (§6), or "Page" with nothing open. */
187
+ function pageTitle(): string {
188
+ const tab = activeTab.value;
189
+ return tab ? pageTabTitle(tab.documentPath) : "Page";
190
+ }
191
+
192
+ /** What a strip tab is called on screen: Page's title is the document's kind. */
193
+ function stripTitle(id: InspectorStripId): string {
194
+ return id === "page" ? pageTitle() : (INSPECTOR_STRIP.find((tab) => tab.id === id)?.title ?? id);
195
+ }
196
+
197
+ /** The tab rows the document draws, with the selected leaf resolved onto them. */
143
198
  function tabViews(selected: InspectorTabId): InspectorTabView[] {
144
- return INSPECTOR_TABS.map((tab) => ({
145
- active: tab.id === selected,
146
- key: tab.id,
147
- panelId: `inspector-panel-${tab.id}`,
148
- region: inspectorTabRegion(tab.id),
149
- tabId: `inspector-tab-${tab.id}`,
150
- title: tab.title,
151
- }));
199
+ const strip = stripOf(selected);
200
+ const noDocument = activeTab.value === null;
201
+ return INSPECTOR_STRIP.map((tab) => {
202
+ const leaves = INSPECTOR_TABS.filter((leaf) => leaf.strip === tab.id).map((leaf) => {
203
+ const empty = leaf.id === "page" && noDocument ? PAGE_TAB_EMPTY : "";
204
+ return {
205
+ active: leaf.id === selected,
206
+ empty,
207
+ hasEmpty: empty !== "",
208
+ key: leaf.id,
209
+ region: inspectorTabRegion(leaf.id),
210
+ shown: leaf.id === selected ? ("true" as const) : ("false" as const),
211
+ title: leaf.title,
212
+ };
213
+ });
214
+ return {
215
+ active: tab.id === strip,
216
+ hasLeaves: leaves.length > 1,
217
+ key: tab.id,
218
+ leaves,
219
+ panelId: `inspector-panel-${tab.id}`,
220
+ tabId: `inspector-tab-${tab.id}`,
221
+ title: stripTitle(tab.id),
222
+ };
223
+ });
152
224
  }
153
225
 
154
226
  /**
@@ -176,10 +248,15 @@ export function mount(ctx: RightPanelCtx) {
176
248
  if (tab) {
177
249
  // Track properties the right panel reads
178
250
  void tab.doc.document;
251
+ // The Page tab reads the realm a content page keeps its head in, and its kind.
252
+ void tab.doc.mode;
253
+ void tab.doc.content;
254
+ void tab.documentPath;
179
255
  // The whole SET, joined — a bare property read would not re-trigger when the selection
180
256
  // Changes WITHIN the array, and §6.7's helpers always replace it but nothing enforces that.
181
257
  void tab.session.selection.map((path) => path.join("/")).join("|");
182
258
  void tab.session.ui.rightTab;
259
+ followSelection(tab);
183
260
  }
184
261
  void _detached.tab;
185
262
  render();
@@ -197,33 +274,89 @@ export function unmount() {
197
274
  bindLogicPanelHost(null);
198
275
  bindContentHost(null);
199
276
  bindStyleHost(null);
277
+ bindPageHost(null);
200
278
  _detached.tab = DEFAULT_INSPECTOR_TAB;
279
+ _detached.element = DEFAULT_ELEMENT_TAB;
280
+ }
281
+
282
+ /**
283
+ * Switch the strip on a selection transition through empty (§6), and only then.
284
+ *
285
+ * Empty → non-empty while Page shows: Element, on the leaf last used there. Non-empty → empty while
286
+ * an Element leaf shows: Page. The first look at a document records its state and switches nothing,
287
+ * so focusing another tab is not a transition, and the Assistant is never left by this. Layout
288
+ * chrome counts as a selection: the Content view is what explains it. So does a Styles specimen's
289
+ * tag, which is a selection of the catalogue rather than of the document.
290
+ */
291
+ function followSelection(tab: Tab): void {
292
+ // A tag picked in the Styles workspace's Elements view is a selection too: it is what the Style
293
+ // Leaf is editing, and it leaves the document selection empty (`selectStylebookTag`).
294
+ const has =
295
+ tab.session.selection.length > 0 ||
296
+ shell.layoutSelection !== null ||
297
+ stylebookTarget(tab) !== null;
298
+ const had = _hadSelection.get(tab);
299
+ _hadSelection.set(tab, has);
300
+ if (had === undefined || had === has) {
301
+ return;
302
+ }
303
+ const current = inspectorTab();
304
+ if (has && current === "page") {
305
+ setInspectorTab(lastElementTab());
306
+ } else if (!has && isElementTabId(current)) {
307
+ setInspectorTab("page");
308
+ }
309
+ }
310
+
311
+ /**
312
+ * The tab the strip rests on when nothing chose one (§6): Page with nothing selected, otherwise the
313
+ * Element leaf last used. What closing the Assistant steps onto.
314
+ */
315
+ export function restingInspectorTab(): InspectorTabId {
316
+ const tab = activeTab.value;
317
+ const has = tab !== null && (tab.session.selection.length > 0 || shell.layoutSelection !== null);
318
+ return has ? lastElementTab() : "page";
319
+ }
320
+
321
+ /** The Element leaf last shown for the focused document, or with nothing open. */
322
+ function lastElementTab(): ElementTabId {
323
+ const tab = activeTab.value;
324
+ const stored: unknown = tab ? tab.session.ui.elementTab : _detached.element;
325
+ return isElementTabId(stored) ? stored : DEFAULT_ELEMENT_TAB;
201
326
  }
202
327
 
203
328
  /** The whole projection the document reads, built fresh from the state it names. */
204
329
  function values() {
205
330
  const tab = inspectorTab();
331
+ const strip = stripOf(tab);
206
332
  return {
207
- tab,
333
+ tab: strip,
208
334
  tabs: tabViews(tab),
209
- target: inspectorTarget(),
210
- title: INSPECTOR_TABS.find((t) => t.id === tab)?.title ?? tab,
335
+ target: inspectorTarget(tab),
336
+ title: stripTitle(strip),
211
337
  };
212
338
  }
213
339
 
214
340
  /**
215
341
  * Bring the dock up to date. Synchronous, and idempotent: the runtime skips a binding whose value
216
342
  * did not move, so an explicit caller can never take a control mid-interaction.
343
+ *
344
+ * The Page body is redrawn only while it is the leaf showing: it builds a merged-head preview per
345
+ * paint, and a hidden one would pay for that on every selection change. Showing it draws it.
217
346
  */
218
347
  export function render() {
219
- _dock?.update(values());
348
+ const view = values();
349
+ _dock?.update(view);
350
+ if (inspectorTab() === "page") {
351
+ renderPageTab();
352
+ }
220
353
  }
221
354
 
222
355
  /**
223
- * The tab showing right now — the stored value, coerced to a declared one.
356
+ * The leaf showing right now — the stored value, coerced to a declared one.
224
357
  *
225
358
  * Coercion rather than trust because the value is persisted session state: a build that spelled a
226
- * tab differently, or an automation step that guessed, must land on Content rather than on a blank
359
+ * tab differently, or an automation step that guessed, must land on Page rather than on a blank
227
360
  * dock with no tab selected.
228
361
  */
229
362
  export function inspectorTab(): InspectorTabId {
@@ -243,38 +376,51 @@ export function setInspectorTab(tab: InspectorTabId): void {
243
376
  const aTab = activeTab.value;
244
377
  if (aTab) {
245
378
  aTab.session.ui.rightTab = tab;
379
+ if (isElementTabId(tab)) {
380
+ aTab.session.ui.elementTab = tab;
381
+ }
246
382
  } else {
247
383
  _detached.tab = tab;
384
+ if (isElementTabId(tab)) {
385
+ _detached.element = tab;
386
+ }
248
387
  }
249
388
  render();
250
389
  }
251
390
 
252
391
  /**
253
- * A pick from the strip.
392
+ * A pick from the strip, or from Element's sub-switch.
254
393
  *
255
394
  * The strip states a value and this decides what it means, which is why the document dispatches an
256
- * id rather than writing one: an id the enum does not declare is refused here rather than becoming
257
- * a dock with no tab selected.
395
+ * id rather than writing one. `element` is a strip id with no leaf of its own: it means "the
396
+ * Element view I last used". Any other id must be a declared leaf, so an id the enum does not
397
+ * declare is refused here rather than becoming a dock with no tab selected.
258
398
  */
259
399
  function selectFromStrip(id: string): void {
260
- if (id !== "" && id !== inspectorTab() && isInspectorTabId(id)) {
261
- setInspectorTab(id);
400
+ const leaf = id === "element" ? lastElementTab() : id;
401
+ if (leaf !== "" && leaf !== inspectorTab() && isInspectorTabId(leaf)) {
402
+ setInspectorTab(leaf);
262
403
  }
263
404
  }
264
405
 
265
406
  /**
266
407
  * What the selected tab is pointed AT, in the fewest words that are true.
267
408
  *
268
- * A tag name when a node is selected, the document's own name when nothing is, and "no document"
269
- * when there is nothing open. The dock spent its whole life unable to answer this — three icon tabs
270
- * and no statement of target anywhere — which is how "why is this field disabled" became a question
271
- * with no on-screen answer.
409
+ * A tag name when a node is selected, the document's own name when nothing is or when the Page tab
410
+ * is showing (it edits the document, whatever is selected), and "no document" when there is nothing
411
+ * open. The dock spent its whole life unable to answer this — three icon tabs and no statement of
412
+ * target anywhere — which is how "why is this field disabled" became a question with no on-screen
413
+ * answer.
272
414
  */
273
- function inspectorTarget(): string {
415
+ function inspectorTarget(showing: InspectorTabId): string {
274
416
  const tab = activeTab.value;
275
417
  if (!tab) {
276
418
  return "no document";
277
419
  }
420
+ // The Page tab edits the document whatever is selected, so it is pointed at the document.
421
+ if (showing === "page") {
422
+ return documentName(tab.documentPath);
423
+ }
278
424
  // Layout chrome first: it is mutually exclusive with a document selection, and it is the one
279
425
  // Target whose name has to say where it came FROM — "<header> in layouts/base.json" is the whole
280
426
  // Answer to "why can I not edit this".
@@ -282,6 +428,12 @@ function inspectorTarget(): string {
282
428
  if (layout) {
283
429
  return `<${layout.tagName || "element"}> in ${layout.layoutFile || "the layout"}`;
284
430
  }
431
+ // The Styles workspace's Elements view styles a TAG: name it, as the Style body's `h1 · Base`
432
+ // Does, rather than whatever node the document selection happens to hold.
433
+ const tag = stylebookTarget(tab);
434
+ if (tag) {
435
+ return tag;
436
+ }
285
437
  const paths = tab.session.selection;
286
438
  const selection = primarySelection(paths);
287
439
  if (selection) {
@@ -291,6 +443,10 @@ function inspectorTarget(): string {
291
443
  ? `${paths.length} elements`
292
444
  : nodeLabel(getNodeAtPath(tab.doc.document, selection));
293
445
  }
294
- const path = tab.documentPath;
446
+ return documentName(tab.documentPath);
447
+ }
448
+
449
+ /** A document's own name in the header: its file name, or "document" for an unsaved one. */
450
+ function documentName(path: string | null | undefined): string {
295
451
  return path ? (path.split("/").at(-1) ?? "document") : "document";
296
452
  }
@@ -8,14 +8,14 @@
8
8
  * writing. A previewed SERP result is not a field; it is a picture you study, and studying it in a
9
9
  * strip above the canvas meant the card grew taller than the thing it describes.
10
10
  *
11
- * So it opens as a modal, from two places — the Document Header card and the Navigator's Page
12
- * panel. Two doors because the two are different moments: one while writing the page, one while
13
- * working on its head material. Both run `document.openSeo`, so there is a third door in the
14
- * palette and no surface owns the capability (one definition site per action, §13).
11
+ * So it opens as a modal, from the Inspector's Page tab (and, once, from the Document Header card
12
+ * Edit used to dock above the page, studio.md §4.2). The tab's button runs `document.openSeo`, so
13
+ * there is a second door in the palette and no surface owns the capability (one definition site per
14
+ * action, §13).
15
15
  *
16
- * **The card keeps the fields; the modal keeps the picture.** Title still lives on the card,
17
- * because it is the one head value you type while writing. Everything the modal holds is either a
18
- * rendering of what the build will emit or a field you set once and leave.
16
+ * **The Page tab keeps the fields; the modal keeps the picture.** Title lives on the tab, because
17
+ * it is the one head value you type while writing. Everything the modal holds is either a rendering
18
+ * of what the build will emit or a field you set once and leave.
19
19
  *
20
20
  * **This module is the flow; `surfaces/seo.json` is the markup.** What lives here is every
21
21
  * judgement the picture rests on: which realm a commit is written into, what each previewed line
@@ -24,9 +24,9 @@
24
24
  * decision — it holds the parts, the ARIA and the styling, and it branches on flags this module
25
25
  * hands it.
26
26
  *
27
- * The mutation path is the card's, unchanged: a markdown page commits through
28
- * `applyContentMutation` and a JSON one through `transact`, both taking the tab so the modal edits
29
- * the document it was opened over rather than whichever pane has focus.
27
+ * The mutation path is the Page tab's: a markdown page commits through `applyContentMutation` and a
28
+ * JSON one through `transact`, both taking the tab so the modal edits the document it was opened
29
+ * over rather than whichever pane has focus.
30
30
  *
31
31
  * @docs studio/editing/frontmatter
32
32
  */
@@ -34,21 +34,18 @@
34
34
  import { activeRegistry } from "../commands/active-registry";
35
35
  import { HEAD_EDIT_DEBOUNCE } from "../ui/timing";
36
36
  import { previewAssetSrc } from "../canvas/asset-refs";
37
- import { getPlatform } from "../platform";
38
37
  import { layerHost } from "../ui/layers";
39
- import { mediaSiteUrl } from "../files/media-paths";
40
- import { openMenu } from "../surfaces/menu";
41
38
  import { openSeoSurface } from "../surfaces/seo";
42
39
  import { provenanceTitle } from "./provenance";
43
- import { PUBLIC_DIR } from "@jxsuite/schema/asset-paths";
44
- import { scanLibrary } from "../browse/library-model";
45
40
  import { tabLabel } from "./tab-strip";
46
41
  import { transact } from "../tabs/transact";
47
42
  import { activeTab } from "../workspace/workspace";
48
- import { MEDIA_EXTENSIONS, uploadAccept, uploadAssets } from "../files/media-upload";
43
+ import { pickAndUpload, showMediaPickerPopover } from "../ui/media-picker";
49
44
  import {
50
45
  OG_FIELDS,
51
46
  PAGE_FIELDS,
47
+ pageDescription,
48
+ setPageDescription,
52
49
  applyContentMutation,
53
50
  buildHeadDoc,
54
51
  findLinkEntry,
@@ -226,6 +223,11 @@ function writeField(
226
223
  applyMutation((doc: JxMutableNode) => upsertLink(doc, "icon", trimmed));
227
224
  return;
228
225
  }
226
+ if (key === "description") {
227
+ // The page's one description, written where the build reads it.
228
+ applyMutation((doc: JxMutableNode) => setPageDescription(doc, trimmed));
229
+ return;
230
+ }
229
231
  const field = META_BY_KEY.get(key);
230
232
  if (!field) {
231
233
  return;
@@ -252,35 +254,6 @@ function cancelPending(key?: string): void {
252
254
  }
253
255
  }
254
256
 
255
- // ─── The project's media ──────────────────────────────────────────────────────
256
-
257
- /** The listing, once. Invalidated by an upload, which is the only thing that changes it here. */
258
- let _media: string[] | null = null;
259
-
260
- /**
261
- * Every media file under `public/`, as the site URLs a pick would write.
262
- *
263
- * `browse/library-model.ts`'s walker rather than a second one: it is the scanner the Library pane
264
- * already runs over the same tree, it records what it could not read instead of swallowing it, and
265
- * it skips the directories nobody wants walked. The site URL is the authored form — what production
266
- * serves — and `files/media-paths.ts` holds the one definition of that mapping.
267
- */
268
- async function mediaChoices(): Promise<string[]> {
269
- if (_media) {
270
- return _media;
271
- }
272
- const { files } = await scanLibrary([PUBLIC_DIR], getPlatform());
273
- _media = files
274
- .filter((file) => MEDIA_EXTENSIONS.has(file.ext))
275
- .map((file) => mediaSiteUrl(file.path));
276
- return _media;
277
- }
278
-
279
- /** Forget the listing: it was derived from a tree an upload has just changed. */
280
- function invalidateMediaChoices(): void {
281
- _media = null;
282
- }
283
-
284
257
  // ─── The modal ───────────────────────────────────────────────────────────────
285
258
 
286
259
  /** The open surface, or `null`. One at a time: it is about the focused document. */
@@ -359,9 +332,12 @@ function buildView(tab: Tab, headDoc: JxMutableNode, head: JxHeadEntry[]): SeoVi
359
332
  note: "",
360
333
  rows: [
361
334
  ...PAGE_FIELDS.map((field) =>
362
- editRow(field.key, field.label, metaValue(head, field.key), {
363
- multiline: field.multiline,
364
- }),
335
+ editRow(
336
+ field.key,
337
+ field.label,
338
+ field.key === "description" ? pageDescription(headDoc) : metaValue(head, field.key),
339
+ { multiline: field.multiline },
340
+ ),
365
341
  ),
366
342
  editRow(ICON_KEY, "Icon", iconHref, { media: true }),
367
343
  ],
@@ -409,69 +385,26 @@ function commitField(key: string, value: string): void {
409
385
  renderSeoModal();
410
386
  }
411
387
 
412
- /** Open the OS file picker for a media row, and assign the first file it returns. */
388
+ /**
389
+ * Upload into a media row: the image field's own Upload (`ui/media-picker.ts`), which opens the
390
+ * picker, uploads through the one core, and hands the first file's ref back. A commit that lands
391
+ * after the modal closed writes nothing, because {@link commitField} is about the open modal.
392
+ */
413
393
  function uploadInto(key: string): void {
414
- /* The input is created per click and discarded after: a hidden one kept in the document would be
415
- a node the surface owns outside its own document, which is exactly what a surface may not
416
- have. */
417
- const input = document.createElement("input");
418
- input.type = "file";
419
- input.multiple = true;
420
- input.accept = uploadAccept();
421
- input.addEventListener("change", () => {
422
- if (!input.files?.length) {
423
- return;
424
- }
425
- void uploadAssets([...input.files]).then((uploaded) => {
426
- invalidateMediaChoices();
427
- const [first] = uploaded;
428
- if (first) {
429
- commitField(key, first.ref);
430
- }
431
- });
432
- });
433
- input.click();
394
+ pickAndUpload((ref: string) => commitField(key, ref));
434
395
  }
435
396
 
436
397
  /**
437
- * Offer the project's media for one row.
398
+ * Offer the project's images for one row: the media picker's own compact browser, with its **Browse
399
+ * all images…** row opening the Media workspace's surface in pick mode.
438
400
  *
439
- * A kit menu rather than the Spectrum popover `ui/media-picker.ts` draws, and it has to be: a modal
440
- * `<dialog>` is in the top layer, so an overlay painted into a layer div renders UNDERNEATH the
441
- * dialog that opened it and is inert besides (`specs/ui.md` §7). The menu is shown with the Browse
442
- * button as its `source`, which is what puts it in the dialog's own top-layer hierarchy.
401
+ * Not a list of this module's own. It kept one (a walk of `public/` alone, and a kit menu of site
402
+ * URLs) because the old picker painted into a layer div UNDER the modal; the browser is a top-layer
403
+ * `jx-popover` now, so it paints over the modal that opened it, and the field and this dialog
404
+ * answer "what media does this project have" from the same listing (studio.md §9.3).
443
405
  */
444
406
  function browseFor(key: string, anchor: HTMLElement): void {
445
- void mediaChoices().then((choices) => {
446
- openMenu({
447
- label: "Project media",
448
- opener: anchor,
449
- region: "seo-media",
450
- rows:
451
- choices.length > 0
452
- ? choices.map((path) => ({
453
- destructive: false,
454
- disabled: false,
455
- dividerAbove: false,
456
- id: path,
457
- run: () => commitField(key, path),
458
- /* The site URL rather than the file name, because it is the string the pick writes
459
- AND the only one of the two that is unique: two `hero.jpg`s in two directories are
460
- one row twice over in a list that shows names. */
461
- title: path,
462
- }))
463
- : [
464
- {
465
- destructive: false,
466
- disabled: true,
467
- dividerAbove: false,
468
- id: "seo.media.empty",
469
- requires: "a file under public/",
470
- title: "No media in this project",
471
- },
472
- ],
473
- });
474
- });
407
+ showMediaPickerPopover(anchor, (ref: string) => commitField(key, ref), { images: true });
475
408
  }
476
409
 
477
410
  /** Open it over `tab`. Idempotent — opening it again re-points it at the current document. */
@@ -529,10 +462,10 @@ export function closeSeoModal(): void {
529
462
  /**
530
463
  * `document.openSeo` — the one capability behind both buttons.
531
464
  *
532
- * The Document Header card and the Page panel each render a control that runs this, so neither owns
533
- * it and the palette has it by name. A record rather than two click handlers, for the reason the
534
- * rendering-context axes became records: a surface that IS the capability is a capability the
535
- * palette, the assistant and `__jxAutomation` cannot reach.
465
+ * The Page tab renders a control that runs this, so it does not own it and the palette has it by
466
+ * name. A record rather than two click handlers, for the reason the rendering-context axes became
467
+ * records: a surface that IS the capability is a capability the palette, the assistant and
468
+ * `__jxAutomation` cannot reach.
536
469
  *
537
470
  * @returns {AnyCommand[]}
538
471
  */