@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,11 +1,18 @@
1
1
  /// <reference lib="dom" />
2
2
  /**
3
- * Media Picker — the project's media, and the two surfaces that offer them.
3
+ * Media Picker — the project's media, and the compact surfaces that offer them.
4
4
  *
5
5
  * The FIELD (`surfaces/media-field.json`) is a control a row draws: a thumbnail of what the value
6
6
  * names, the value itself, an Upload button that adds a new file to the project and assigns it, and
7
- * a Browse button. The BROWSER (`surfaces/media-browser.json`) is the panel Browse opens: a search
8
- * box over the files under `public/`, with thumbnail previews for images.
7
+ * a Browse button. The BROWSER (`surfaces/media-browser.json`) is the compact panel Browse opens: a
8
+ * search box over the project's media, with thumbnail previews for images, and a last row, **Browse
9
+ * all media…**, that opens the Media workspace's own surface in a dialog in pick mode
10
+ * (`media/media-library.ts`, studio.md §9.3). One picker, two sizes.
11
+ *
12
+ * **What media the project has is answered here, once.** {@link projectMediaFiles} walks
13
+ * `public/**` and every collection's `content/<collection>/images/**`, and the Media workspace, the
14
+ * Site panel's Media section, both pickers and the search-appearance dialog all read that one
15
+ * listing, so "what media does this project have" cannot have two answers in two choosers.
9
16
  *
10
17
  * **This module is the flow, not the markup.** Both surfaces are Jx documents now; what is left
11
18
  * here is which files count as media, what a row's caption says, how long a keystroke waits before
@@ -20,13 +27,13 @@
20
27
  * `<img>` has finished by the time `load` fires, so the caption is a read of work already done
21
28
  * rather than a second fetch per row. What is NOT here is a usage count: `findReferences` sweeps
22
29
  * every document in the project per query, and fifty of those to caption a list is the wrong trade.
23
- * Usage is asked once, about one file, where it changes a decision — the delete confirmation
24
- * (`files/media-usage.ts`).
30
+ * Usage is asked about one file where it changes a decision (the delete confirmation, the Media
31
+ * workspace's detail pane), and about every file only when the reader asks for **Unused**.
25
32
  *
26
33
  * @docs studio/projects/media
27
34
  */
28
35
 
29
- import { getPlatform } from "../platform";
36
+ import { getPlatform, hasPlatform } from "../platform";
30
37
  import { debouncedStyleCommit, renderOnly } from "../store";
31
38
  import { mountMediaFieldSurface } from "../surfaces/media-field";
32
39
  import { openMediaBrowserSurface } from "../surfaces/media-browser";
@@ -35,12 +42,15 @@ import { previewAssetSrc } from "../canvas/asset-refs";
35
42
  import {
36
43
  IMAGE_EXTENSIONS,
37
44
  MEDIA_EXTENSIONS,
38
- uploadAccept,
39
45
  extensionOf,
46
+ mediaFileKind,
47
+ uploadAccept,
40
48
  uploadAssets,
41
49
  } from "../files/media-upload";
42
50
  import {
43
51
  invalidateMediaMeta,
52
+ loadMediaMeta,
53
+ mediaMetaSettled,
44
54
  mediaMetaSummary,
45
55
  peekMediaMeta,
46
56
  recordImageSize,
@@ -48,28 +58,51 @@ import {
48
58
  } from "../files/media-meta";
49
59
  import { mediaSiteUrl, previewFileSrc } from "../files/media-paths";
50
60
  import { notedListing } from "../files/asset-versions";
61
+ import type { MediaFileKind } from "../files/media-upload";
51
62
  import type {
52
63
  MediaBrowserRow,
53
64
  MediaBrowserSurfaceHandle,
54
65
  MediaBrowserView,
55
66
  } from "../surfaces/media-browser";
56
67
  import type { MediaFieldHandle, MediaFieldView } from "../surfaces/media-field";
68
+ import type * as MediaLibraryModule from "../media/media-library";
57
69
 
58
70
  // ─── Media file cache ────────────────────────────────────────────────────────
59
71
 
60
- /** One browsable media file: the ref a pick writes, and the file that ref names. */
61
- interface MediaEntry {
62
- /** The value committed to the field — the site URL, which is the authored form. */
63
- path: string;
64
- /** Project-relative path on disk, the key everything in `media-meta` is stored under. */
72
+ /** One media file of the project, as every browsing surface lists it. */
73
+ export interface ProjectMediaFile {
74
+ /** The project-relative path on disk, the key everything in `media-meta` is stored under. */
65
75
  file: string;
66
76
  name: string;
77
+ /** The site URL a document authors for it, which is what a pick commits. */
78
+ ref: string;
67
79
  isImage: boolean;
80
+ /** What the file is, as the Media workspace's filters name it. */
81
+ kind: MediaFileKind;
68
82
  }
69
83
 
70
- let mediaCache: MediaEntry[] = [];
84
+ let mediaCache: ProjectMediaFile[] = [];
71
85
  let mediaCacheLoaded = false;
72
86
 
87
+ /**
88
+ * The directory every collection co-locates its uploads in (`files/media-upload.ts`
89
+ * `uploadDirFor`).
90
+ */
91
+ const COLLECTION_IMAGES = "images";
92
+
93
+ /**
94
+ * Whether a project path is inside the media scan: under `public/`, or under a collection's
95
+ * `content/<collection>/images/`. A filesystem batch that touches neither cannot change what
96
+ * {@link projectMediaFiles} lists, so the listing is kept.
97
+ */
98
+ export function isMediaScanPath(path: string): boolean {
99
+ const normalized = path.replaceAll("\\", "/");
100
+ return (
101
+ normalized.startsWith("public/") ||
102
+ new RegExp(`^content/[^/]+/${COLLECTION_IMAGES}/`).test(normalized)
103
+ );
104
+ }
105
+
73
106
  /**
74
107
  * Recursively collect media files from a directory, seeding {@link seedMediaMeta} with the listing
75
108
  * on the way past — the size of every row is already in the response that enumerated it.
@@ -77,8 +110,8 @@ let mediaCacheLoaded = false;
77
110
  async function collectMedia(
78
111
  dir: string,
79
112
  platform: ReturnType<typeof getPlatform>,
80
- ): Promise<MediaEntry[]> {
81
- const results: MediaEntry[] = [];
113
+ ): Promise<ProjectMediaFile[]> {
114
+ const results: ProjectMediaFile[] = [];
82
115
  try {
83
116
  const entries = await notedListing(() => platform.listDirectory(dir));
84
117
  seedMediaMeta(entries);
@@ -92,10 +125,11 @@ async function collectMedia(
92
125
  results.push({
93
126
  file: entry.path,
94
127
  isImage: IMAGE_EXTENSIONS.has(ext),
128
+ kind: mediaFileKind(entry.name),
95
129
  name: entry.name,
96
130
  // The site URL is what production serves and therefore what a document should say.
97
- // Media-paths holds the one definition of that mapping.
98
- path: mediaSiteUrl(entry.path),
131
+ // Media-paths holds the one definition of that mapping, a collection's mount included.
132
+ ref: mediaSiteUrl(entry.path),
99
133
  });
100
134
  }
101
135
  }
@@ -106,16 +140,141 @@ async function collectMedia(
106
140
  return results;
107
141
  }
108
142
 
109
- async function loadMediaCache() {
143
+ /**
144
+ * Every collection's co-located `images/` folder: `content/<collection>/images/**`.
145
+ *
146
+ * Listed from `content/` itself rather than from `project.json`'s declarations, because the folder
147
+ * is where an upload from an entry lands whatever its collection's `source` says, and a picker that
148
+ * could not see it could not choose a cover image the author had just uploaded to an entry.
149
+ */
150
+ async function collectCollectionImages(
151
+ platform: ReturnType<typeof getPlatform>,
152
+ ): Promise<ProjectMediaFile[]> {
153
+ let collections: string[];
154
+ try {
155
+ const entries = await platform.listDirectory("content");
156
+ collections = entries.filter((entry) => entry.type === "directory").map((entry) => entry.path);
157
+ } catch {
158
+ return [];
159
+ }
160
+ const results: ProjectMediaFile[] = [];
161
+ for (const dir of collections) {
162
+ /* Descend only into an `images/` the collection actually holds: most collections have none,
163
+ and asking the backend to list a directory that does not exist is a 404 in the console. */
164
+ let holdsImages = false;
165
+ try {
166
+ const entries = await platform.listDirectory(dir);
167
+ holdsImages = entries.some(
168
+ (entry) => entry.type === "directory" && entry.name === COLLECTION_IMAGES,
169
+ );
170
+ } catch {
171
+ // An unreadable collection folder has no images to offer.
172
+ }
173
+ if (holdsImages) {
174
+ results.push(...(await collectMedia(`${dir}/${COLLECTION_IMAGES}`, platform)));
175
+ }
176
+ }
177
+ return results;
178
+ }
179
+
180
+ /**
181
+ * Which of the two scan roots, `public` and `content`, the project root holds as folders. A root
182
+ * listing that fails says nothing either way, so both are then walked, each tolerating its own
183
+ * absence as it always has.
184
+ */
185
+ async function projectScanRoots(platform: ReturnType<typeof getPlatform>): Promise<Set<string>> {
186
+ try {
187
+ const entries = await platform.listDirectory(".");
188
+ return new Set(
189
+ entries
190
+ .filter((entry) => entry.type === "directory")
191
+ .map((entry) => entry.name)
192
+ .filter((name) => name === "public" || name === "content"),
193
+ );
194
+ } catch {
195
+ return new Set(["public", "content"]);
196
+ }
197
+ }
198
+
199
+ /** The walk in flight, so every caller that asks while it runs shares it instead of starting one. */
200
+ let _mediaLoading: Promise<void> | null = null;
201
+ /** Bumped by {@link invalidateMediaCache}, so a walk that started before it cannot land after it. */
202
+ let _mediaEpoch = 0;
203
+
204
+ /** Surfaces that own their own repaint and want to hear when the listing moves. */
205
+ const _listeners = new Set<() => void>();
206
+
207
+ /**
208
+ * Hear about every change to the listing: a walk landing, or an invalidation dropping it.
209
+ *
210
+ * For a surface that is not one of the named panels {@link loadMediaCache} repaints — the Media
211
+ * workspace's library, which owns its own reactivity the way the Library pane does.
212
+ *
213
+ * @returns The unsubscribe.
214
+ */
215
+ export function onProjectMediaChange(listener: () => void): () => void {
216
+ _listeners.add(listener);
217
+ return () => {
218
+ _listeners.delete(listener);
219
+ };
220
+ }
221
+
222
+ function announce(): void {
223
+ for (const listener of _listeners) {
224
+ listener();
225
+ }
226
+ }
227
+
228
+ function loadMediaCache(): Promise<void> {
110
229
  if (mediaCacheLoaded) {
111
- return;
230
+ return Promise.resolve();
231
+ }
232
+ if (_mediaLoading) {
233
+ return _mediaLoading;
234
+ }
235
+ if (!hasPlatform()) {
236
+ // No backend to list (a window still booting): nothing is known, and nothing is claimed.
237
+ return Promise.resolve();
238
+ }
239
+ const epoch = _mediaEpoch;
240
+ const walk = (async () => {
241
+ const platform = getPlatform();
242
+ /* Descend only into the scan roots the project actually has: a project with no `content/` (a
243
+ site of pages alone) or no `public/` would otherwise ask the backend to list a folder that
244
+ does not exist, which is a 404 in the console on every visit to Media. */
245
+ const roots = await projectScanRoots(platform);
246
+ const [published, collections] = await Promise.all([
247
+ roots.has("public") ? collectMedia("public", platform) : [],
248
+ roots.has("content") ? collectCollectionImages(platform) : [],
249
+ ]);
250
+ if (epoch !== _mediaEpoch) {
251
+ // Superseded: this listing is older than the invalidation, and a newer walk owns the cache.
252
+ return;
253
+ }
254
+ mediaCache = [...published, ...collections];
255
+ mediaCacheLoaded = true;
256
+ _mediaLoading = null;
257
+ // Re-render the host panels so the browse panel has entries to show once the async listing
258
+ // Resolves. Mirrors loadLayoutEntries()'s renderOnly() in head-panel.
259
+ renderOnly("leftPanel", "rightPanel");
260
+ announce();
261
+ })();
262
+ _mediaLoading = walk;
263
+ return walk;
264
+ }
265
+
266
+ /**
267
+ * The project's media, or `null` while the listing is still being read — the same cache the picker
268
+ * browses, so the Site panel's Media section, the Media workspace and the image field cannot
269
+ * disagree about what exists. Asking starts the read; it repaints the panels that read it when it
270
+ * lands, and tells every {@link onProjectMediaChange} listener.
271
+ */
272
+ export function projectMediaFiles(): readonly ProjectMediaFile[] | null {
273
+ if (!mediaCacheLoaded) {
274
+ void loadMediaCache();
275
+ return null;
112
276
  }
113
- const platform = getPlatform();
114
- mediaCache = await collectMedia("public", platform);
115
- mediaCacheLoaded = true;
116
- // Re-render the host panels so the browse panel has entries to show once the async listing
117
- // Resolves. Mirrors loadLayoutEntries()'s renderOnly() in head-panel.
118
- renderOnly("leftPanel", "rightPanel", "frontmatterPanel");
277
+ return mediaCache;
119
278
  }
120
279
 
121
280
  /**
@@ -127,30 +286,74 @@ async function loadMediaCache() {
127
286
  export function invalidateMediaCache() {
128
287
  mediaCache = [];
129
288
  mediaCacheLoaded = false;
289
+ _mediaEpoch += 1;
290
+ _mediaLoading = null;
130
291
  invalidateMediaMeta();
292
+ announce();
293
+ }
294
+
295
+ // ─── The full-size picker ────────────────────────────────────────────────────
296
+
297
+ /** What a chooser offers: every media file, or only images, for a field whose value is a picture. */
298
+ export interface MediaPickOptions {
299
+ images?: boolean;
300
+ }
301
+
302
+ /**
303
+ * The library flow, imported on the first **Browse all media…**. One cached promise, because two
304
+ * dynamic imports of a module in flight at once is the shape that loses its coverage record, and a
305
+ * field nobody opened the big picker from should not load the workspace's surface.
306
+ */
307
+ let _library: Promise<typeof MediaLibraryModule> | null = null;
308
+
309
+ function mediaLibrary(): Promise<typeof MediaLibraryModule> {
310
+ _library ??= import("../media/media-library");
311
+ return _library;
312
+ }
313
+
314
+ /**
315
+ * Open the Media workspace's surface in a dialog, in pick mode, and commit what is chosen.
316
+ *
317
+ * @param {(val: string) => void} onCommit - What a pick writes: the file's site URL
318
+ * @param {MediaPickOptions} options - Images only, for a field that holds a picture
319
+ */
320
+ export async function openFullMediaPicker(
321
+ onCommit: (val: string) => void,
322
+ options: MediaPickOptions = {},
323
+ ): Promise<void> {
324
+ const library = await mediaLibrary();
325
+ library.openMediaPickDialog({ images: options.images === true, onPick: onCommit });
131
326
  }
132
327
 
133
328
  // ─── The browser ─────────────────────────────────────────────────────────────
134
329
 
135
- /** The most rows the panel lists. Everything past it is counted rather than drawn. */
330
+ /**
331
+ * How many rows the panel lists at first, and how many more each **Show N more** adds. Past the cap
332
+ * the rows are counted, and the count is the control that lists them, so every file the Media
333
+ * workspace has can be reached from here: the cap bounds the first paint, never the answer.
334
+ */
136
335
  const BROWSE_LIMIT = 50;
137
336
 
138
337
  /** The open panel, and what a pick in it commits. One at a time. */
139
338
  let _browser: MediaBrowserSurfaceHandle | null = null;
140
339
  let _browserCommit: ((value: string) => void) | null = null;
141
340
  let _browserFilter = "";
341
+ /** How many rows the open panel lists; raised by Show more, and never lowered while it is open. */
342
+ let _browserLimit = BROWSE_LIMIT;
343
+ let _browserOptions: MediaPickOptions = {};
142
344
  /** Where the panel was opened, kept so a filter keystroke does not move it. */
143
345
  let _browserOrigin = { x: 0, y: 0 };
144
346
 
145
347
  /** What the panel shows for the filter that stands now. */
146
348
  function browserView(): MediaBrowserView {
147
349
  const query = _browserFilter.toLowerCase();
350
+ const offered = _browserOptions.images ? mediaCache.filter((m) => m.isImage) : mediaCache;
148
351
  const filtered = query
149
- ? mediaCache.filter(
150
- (m) => m.path.toLowerCase().includes(query) || m.name.toLowerCase().includes(query),
352
+ ? offered.filter(
353
+ (m) => m.ref.toLowerCase().includes(query) || m.name.toLowerCase().includes(query),
151
354
  )
152
- : mediaCache;
153
- const listed = filtered.slice(0, BROWSE_LIMIT);
355
+ : offered;
356
+ const listed = filtered.slice(0, _browserLimit);
154
357
  const rows: MediaBrowserRow[] = listed.map((m) => {
155
358
  const caption = mediaMetaSummary(peekMediaMeta(m.file));
156
359
  return {
@@ -159,16 +362,24 @@ function browserView(): MediaBrowserView {
159
362
  file: m.file,
160
363
  key: m.file,
161
364
  name: m.name,
162
- path: m.path,
365
+ path: m.ref,
163
366
  thumbSrc: m.isImage ? previewFileSrc(m.file) : "",
164
367
  thumbState: m.isImage ? "shown" : "hidden",
165
368
  };
166
369
  });
167
370
  return {
168
- emptyLabel: "No matches",
371
+ browseAllLabel: _browserOptions.images ? "Browse all images…" : "Browse all media…",
372
+ /* An empty project says so, rather than "No matches" over a search nobody typed: the Upload
373
+ beside the field and the row below are what to do about it. */
374
+ emptyLabel:
375
+ offered.length > 0
376
+ ? "No matches"
377
+ : _browserOptions.images
378
+ ? "No images in this project yet"
379
+ : "No media in this project yet",
169
380
  filter: _browserFilter,
170
- moreLabel: `…${filtered.length - BROWSE_LIMIT} more`,
171
- moreState: filtered.length > BROWSE_LIMIT ? "shown" : "hidden",
381
+ moreLabel: `Show ${Math.min(filtered.length - _browserLimit, BROWSE_LIMIT)} more`,
382
+ moreState: filtered.length > _browserLimit ? "shown" : "hidden",
172
383
  rows,
173
384
  rowsState: rows.length > 0 ? "listed" : "empty",
174
385
  x: _browserOrigin.x,
@@ -182,6 +393,7 @@ function dismissMediaPickerPopover() {
182
393
  _browser = null;
183
394
  _browserCommit = null;
184
395
  _browserFilter = "";
396
+ _browserLimit = BROWSE_LIMIT;
185
397
  open?.close();
186
398
  }
187
399
 
@@ -198,7 +410,14 @@ let _sizeRepaint = 0;
198
410
  * because the measurement has not changed.
199
411
  */
200
412
  function noteImageSize(file: string, width: number, height: number) {
201
- if (!recordImageSize(file, width, height) || _sizeRepaint !== 0) {
413
+ if (!recordImageSize(file, width, height)) {
414
+ if (!mediaMetaSettled(file)) {
415
+ // An SVG's decode is not its size: the file says what it is, read once.
416
+ void loadMediaMeta(file).then(() => _browser?.update(browserView()));
417
+ }
418
+ return;
419
+ }
420
+ if (_sizeRepaint !== 0) {
202
421
  return;
203
422
  }
204
423
  _sizeRepaint = requestAnimationFrame(() => {
@@ -212,14 +431,23 @@ function noteImageSize(file: string, width: number, height: number) {
212
431
  *
213
432
  * Exported because the panel is a SURFACE OF ITS OWN — it renders into the popover layer, not into
214
433
  * the field that opened it — so a converted surface can offer the same browser without
215
- * re-implementing it. `surfaces/doc-header.ts`'s Browse button is the first such caller.
434
+ * re-implementing it. The Inspector's Page tab's Browse button is one such caller, and the
435
+ * search-appearance dialog's image rows another: the panel is a top-layer `jx-popover`, so it
436
+ * paints over the modal that opened it.
216
437
  *
217
438
  * @param {HTMLElement} anchorEl
218
439
  * @param {(val: string) => void} onCommit
440
+ * @param {MediaPickOptions} options - Images only, for a field that holds a picture
219
441
  */
220
- export function showMediaPickerPopover(anchorEl: HTMLElement, onCommit: (val: string) => void) {
442
+ export function showMediaPickerPopover(
443
+ anchorEl: HTMLElement,
444
+ onCommit: (val: string) => void,
445
+ options: MediaPickOptions = {},
446
+ ) {
221
447
  dismissMediaPickerPopover();
448
+ void loadMediaCache().then(() => _browser?.update(browserView()));
222
449
  _browserCommit = onCommit;
450
+ _browserOptions = options;
223
451
  const box = rectOf(anchorEl);
224
452
  /* Where it opens, once. `jx-popover` clamps a freshly shown panel into the viewport itself, so
225
453
  nothing here measures the panel or flips it above a low anchor — that was the layer-div
@@ -228,10 +456,19 @@ export function showMediaPickerPopover(anchorEl: HTMLElement, onCommit: (val: st
228
456
  _browser = openMediaBrowserSurface(
229
457
  browserView(),
230
458
  {
459
+ browseAll: () => {
460
+ const commit = _browserCommit;
461
+ const chosen = _browserOptions;
462
+ dismissMediaPickerPopover();
463
+ if (commit) {
464
+ void openFullMediaPicker(commit, chosen);
465
+ }
466
+ },
231
467
  dismissed: () => {
232
468
  _browser = null;
233
469
  _browserCommit = null;
234
470
  _browserFilter = "";
471
+ _browserLimit = BROWSE_LIMIT;
235
472
  },
236
473
  measure: noteImageSize,
237
474
  pick: (path: string) => {
@@ -239,6 +476,10 @@ export function showMediaPickerPopover(anchorEl: HTMLElement, onCommit: (val: st
239
476
  dismissMediaPickerPopover();
240
477
  commit?.(path);
241
478
  },
479
+ showMore: () => {
480
+ _browserLimit += BROWSE_LIMIT;
481
+ _browser?.update(browserView());
482
+ },
242
483
  setFilter: (value: string) => {
243
484
  _browserFilter = value;
244
485
  _browser?.update(browserView());
@@ -301,6 +542,7 @@ const FIELD_DEBOUNCE_MS = 400;
301
542
  interface FieldBinding {
302
543
  prop: string;
303
544
  commit: (value: string) => void;
545
+ options: MediaPickOptions;
304
546
  }
305
547
 
306
548
  interface FieldEntry extends FieldBinding {
@@ -339,12 +581,14 @@ function fieldView(prop: string, value: string): MediaFieldView {
339
581
  * @param {string} prop - The attribute or prop being edited; the field's accessible name
340
582
  * @param {string} value - What it holds now
341
583
  * @param {(val: string) => void} onCommit - What a pick or a settled keystroke writes
584
+ * @param {MediaPickOptions} options - Images only, for a field whose value is a picture
342
585
  */
343
586
  export function mountMediaPicker(
344
587
  host: HTMLElement,
345
588
  prop: string,
346
589
  value: string,
347
590
  onCommit: (val: string) => void,
591
+ options: MediaPickOptions = {},
348
592
  ): void {
349
593
  // Kick off async load (won't block the mount)
350
594
  void loadMediaCache();
@@ -352,14 +596,14 @@ export function mountMediaPicker(
352
596
  if (existing) {
353
597
  existing.prop = prop;
354
598
  existing.commit = onCommit;
599
+ existing.options = options;
355
600
  existing.handle.update(fieldView(prop, value));
356
601
  return;
357
602
  }
358
- const entry: FieldBinding = { commit: onCommit, prop };
603
+ const entry: FieldBinding = { commit: onCommit, options, prop };
359
604
  const handle = mountMediaFieldSurface(host, fieldView(prop, value), {
360
605
  browse: (anchor: HTMLElement) => {
361
- void loadMediaCache();
362
- showMediaPickerPopover(anchor, (val: string) => entry.commit(val));
606
+ showMediaPickerPopover(anchor, (val: string) => entry.commit(val), entry.options);
363
607
  },
364
608
  /* Debounced under the prop's own key, so the canvas follows the typing without a document write
365
609
  per keystroke and two fields never share a timer. */
package/src/ui/regions.ts CHANGED
@@ -14,8 +14,9 @@
14
14
  *
15
15
  * ```text
16
16
  * navigator the Navigator dock
17
- * navigator/panel:git the Source Control panel inside it
18
- * navigator/panel:git/commit a leaf within that panel
17
+ * navigator/panel:site the Site panel inside it
18
+ * pane.primary/publish the Publish home in the primary pane
19
+ * pane.primary/publish/commit a leaf within it (Review changes' destination card)
19
20
  * inspector/tab:style the Style tab's body
20
21
  * inspector/field:href a field row (the Inspector's documents already emit `data-prop`)
21
22
  * pane.primary/tabs the primary pane's own tab strip
@@ -27,7 +28,7 @@
27
28
  * inspector stamps `inspector/tab:${key}` from its own tab records; `getLayerSlot()` stamps
28
29
  * `overlay.<kind>:<id>` from the key it already builds. A panel rename then propagates to the
29
30
  * region automatically, and a stale id fails the shot contract instead of photographing the wrong
30
- * box. Only leaves — `navigator/panel:git/commit` — are hand-stamped, and those are counted.
31
+ * box. Only leaves — `pane.primary/publish/commit` — are hand-stamped, and those are counted.
31
32
  *
32
33
  * Resolution reads the live DOM rather than a registration table, for the same reason
33
34
  * {@link import("./layers").isModalOpen} does: whatever is on screen is the answer, and there is no
@@ -61,7 +62,7 @@ export interface ParsedRegionId {
61
62
  surface: RegionSurface;
62
63
  /** `primary` in `pane.primary`, `dialog` in `overlay.dialog:settings`. */
63
64
  instance?: string;
64
- /** Everything after the first `/` — `panel:git/commit` in `navigator/panel:git/commit`. */
65
+ /** Everything after the first `/` — `publish/commit` in `pane.primary/publish/commit`. */
65
66
  part?: string;
66
67
  }
67
68
 
@@ -159,8 +160,8 @@ const DERIVED_RESOLVERS: readonly {
159
160
  * external hook it has (`ui.md` §3.1), which is exactly what a derived region wants.
160
161
  *
161
162
  * `ui/media-picker.ts` used to STAMP this id on the button itself, and the id was a lie about
162
- * where the button is: the same picker draws the Document Header card's Icon and og:image fields,
163
- * and that card is rendered inside a PANE's stage. So `inspector/field:icon/browse` resolved to
163
+ * where the button is: the same picker drew the Document Header card's Icon and og:image fields,
164
+ * and that card (since removed, studio.md §4.2) was rendered inside a PANE's stage. So `inspector/field:icon/browse` resolved to
164
165
  * two elements the moment the grid split — one per pane, neither of them in the Inspector — and
165
166
  * `resolveRegion` takes the LAST, which is the side pane's. `paneRegion` could never have reached
166
167
  * it: an `inspector/…` id on an element outside the Inspector is not a pane-scoping problem, it