@jxsuite/studio 6.1.1 → 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 (785) hide show
  1. package/README.md +5 -5
  2. package/dist/chunks/2020-5wtzce5g.js +4 -0
  3. package/dist/chunks/{action-group-2mqby73c.js → action-group-4aan7g6g.js} +3 -3
  4. package/dist/chunks/adapter-5tphg7xe.js +4 -0
  5. package/dist/chunks/add-new-flow-psym14sn.js +4 -0
  6. package/dist/chunks/cf-account-picker-haqazq5r.js +4 -0
  7. package/dist/chunks/cf-settings-h2wrh6hq.js +4 -0
  8. package/dist/chunks/color-area-6rbra5dy.js +4 -0
  9. package/dist/chunks/color-field-gy56c94g.js +4 -0
  10. package/dist/chunks/color-m7r1vk54.js +4 -0
  11. package/dist/chunks/color-slider-sqqdyf6c.js +4 -0
  12. package/dist/chunks/combobox-72h32ejk.js +4 -0
  13. package/dist/chunks/components-r22xca09.js +4 -0
  14. package/dist/chunks/content-source-mmppmcyg.js +4 -0
  15. package/dist/chunks/convert-to-repeater-he70fc2b.js +4 -0
  16. package/dist/chunks/dialog-c4x05vyx.js +4 -0
  17. package/dist/chunks/editor-ccd9jwsa.js +4 -0
  18. package/dist/chunks/entry-commands-6qxz9e8p.js +4 -0
  19. package/dist/chunks/extension-sections-0egb88dm.js +4 -0
  20. package/dist/chunks/file-ops-qnab36ek.js +4 -0
  21. package/dist/chunks/files-rcn56qvm.js +4 -0
  22. package/dist/chunks/format-host-yxz0kjjs.js +4 -0
  23. package/dist/chunks/fs-events-d1qxq4z4.js +4 -0
  24. package/dist/chunks/grid-open-4z1g062t.js +4 -0
  25. package/dist/chunks/grid-view-qg1b5eqx.js +51 -0
  26. package/dist/chunks/icons-63bjtbs7.js +4 -0
  27. package/dist/chunks/iframe-entry-2zggzajv.js +5 -0
  28. package/dist/chunks/iframe-entry-76j8vwwx.js +5 -0
  29. package/dist/chunks/iframe-entry-99nr5zfb.js +5 -0
  30. package/dist/chunks/iframe-entry-pkgmcgp0.js +5 -0
  31. package/dist/chunks/iframe-host-ssv7kyj0.js +4 -0
  32. package/dist/chunks/{index-2veqmnhr.js → index-32eqzv1k.js} +3 -3
  33. package/dist/chunks/index-f0jn7nnp.js +4 -0
  34. package/dist/chunks/{javascript-8nbvs12s.js → javascript-pjp734q4.js} +3 -3
  35. package/dist/chunks/jsonMode-njkvsa02.js +13 -0
  36. package/dist/chunks/jx-validate-ttasrbh7.js +4 -0
  37. package/dist/chunks/layers-bgyr11tr.js +4 -0
  38. package/dist/chunks/layers-panel-7ez66zeq.js +4 -0
  39. package/dist/chunks/listbox-c421cf8c.js +4 -0
  40. package/dist/chunks/lit-html-v9gh687x.js +4 -0
  41. package/dist/chunks/media-library-sq468wts.js +4 -0
  42. package/dist/chunks/media-picker-shfe59pt.js +4 -0
  43. package/dist/chunks/menu-bdpzbfvs.js +4 -0
  44. package/dist/chunks/monaco-binding-vq1hgnn0.js +4 -0
  45. package/dist/chunks/monaco-buffer-3afgwvbp.js +4 -0
  46. package/dist/chunks/monaco-setup-xrd2cbkp.js +4 -0
  47. package/dist/chunks/notify-kzrjr3fv.js +4 -0
  48. package/dist/chunks/number-field-y62xq0ty.js +4 -0
  49. package/dist/chunks/parse-v77xtwv7.js +4 -0
  50. package/dist/chunks/platform-7v4rtd7m.js +4 -0
  51. package/dist/chunks/popover-b89sfej7.js +4 -0
  52. package/dist/chunks/publish-panel-2z9gw8ee.js +4 -0
  53. package/dist/chunks/publish-quick-qx2455cj.js +4 -0
  54. package/dist/chunks/publish-workspace-wrc1dgwx.js +4 -0
  55. package/dist/chunks/quick-search-n0t0eqxb.js +4 -0
  56. package/dist/chunks/references-9547rg5s.js +4 -0
  57. package/dist/chunks/right-panel-wsgnr9pn.js +4 -0
  58. package/dist/chunks/select-rzds841e.js +4 -0
  59. package/dist/chunks/source-control-kwktr9y6.js +4 -0
  60. package/dist/chunks/split-c9zfdgxf.js +4 -0
  61. package/dist/chunks/studio-03ct6yfv.js +9 -0
  62. package/dist/chunks/studio-07rcfje3.js +5 -0
  63. package/dist/chunks/studio-3e54qh39.js +7 -0
  64. package/dist/chunks/studio-3exxhz5g.js +5 -0
  65. package/dist/chunks/studio-3q40e3rj.js +5 -0
  66. package/dist/chunks/studio-3qeh9t0h.js +6 -0
  67. package/dist/chunks/studio-3qqjeep5.js +6 -0
  68. package/dist/chunks/studio-43sd2mam.js +4 -0
  69. package/dist/chunks/studio-4hp8pkyt.js +5 -0
  70. package/dist/chunks/studio-58xg6j2q.js +9 -0
  71. package/dist/chunks/studio-5g0rprdt.js +7 -0
  72. package/dist/chunks/studio-6m380wp7.js +5 -0
  73. package/dist/chunks/studio-80jyh1xh.js +4 -0
  74. package/dist/chunks/studio-83mk4bgg.js +9 -0
  75. package/dist/chunks/{studio-kggey1jt.js → studio-881z1xrx.js} +4 -4
  76. package/dist/chunks/studio-8m0y2qe7.js +5 -0
  77. package/dist/chunks/studio-8tr6cfrc.js +5 -0
  78. package/dist/chunks/studio-8vcvfs7r.js +6 -0
  79. package/dist/chunks/studio-9tmebf0s.js +5 -0
  80. package/dist/chunks/studio-a2bvaptz.js +5 -0
  81. package/dist/chunks/studio-b7y5mr30.js +5 -0
  82. package/dist/chunks/studio-bavb31r0.js +5 -0
  83. package/dist/chunks/studio-bbe4c90a.js +5 -0
  84. package/dist/chunks/studio-c05wdnt3.js +5 -0
  85. package/dist/chunks/studio-ccv2svc6.js +5 -0
  86. package/dist/chunks/studio-d5grtb9e.js +16 -0
  87. package/dist/chunks/studio-df0baea9.js +5 -0
  88. package/dist/chunks/studio-dgk4dqtx.js +7 -0
  89. package/dist/chunks/studio-e1g64q43.js +5 -0
  90. package/dist/chunks/studio-ed1hp6zf.js +6 -0
  91. package/dist/chunks/studio-ed3xx40h.js +7 -0
  92. package/dist/chunks/studio-f14e1zdj.js +5 -0
  93. package/dist/chunks/studio-fbhxxc5f.js +5 -0
  94. package/dist/chunks/studio-fyh0jc96.js +5 -0
  95. package/dist/chunks/studio-gcxk20n7.js +5 -0
  96. package/dist/chunks/studio-jaaz8ce9.js +5 -0
  97. package/dist/chunks/{studio-8grwxtqk.js → studio-jcnsdaep.js} +4 -4
  98. package/dist/chunks/studio-jsgf2q5k.js +7 -0
  99. package/dist/chunks/{studio-1q1sh2ax.js → studio-k3xxbagr.js} +3 -3
  100. package/dist/chunks/studio-k7evxqh4.js +5 -0
  101. package/dist/chunks/studio-kt3pjkwd.js +5 -0
  102. package/dist/chunks/studio-m8g16bgz.js +5 -0
  103. package/dist/chunks/studio-mn0476ag.js +5 -0
  104. package/dist/chunks/studio-mx1smyq0.js +5 -0
  105. package/dist/chunks/studio-n0c5h6mf.js +5 -0
  106. package/dist/chunks/{studio-rv11kfwa.js → studio-nh6d342n.js} +7 -7
  107. package/dist/chunks/studio-nqxqf8p2.js +4 -0
  108. package/dist/chunks/studio-p4nrkvqr.js +5 -0
  109. package/dist/chunks/{studio-h46vtzk8.js → studio-pbxa5fpc.js} +4 -4
  110. package/dist/chunks/studio-pgmvdv7z.js +7 -0
  111. package/dist/chunks/studio-ph84avkf.js +5 -0
  112. package/dist/chunks/{studio-wszjyh0j.js → studio-q0mvbaq6.js} +4 -4
  113. package/dist/chunks/studio-q0tccd43.js +5 -0
  114. package/dist/chunks/studio-q7rtx6zq.js +14 -0
  115. package/dist/chunks/{studio-pre0skq6.js → studio-qgh13jtz.js} +4 -4
  116. package/dist/chunks/studio-qmn1xc24.js +5 -0
  117. package/dist/chunks/studio-qwdqwkj0.js +5 -0
  118. package/dist/chunks/{studio-t6b6qe31.js → studio-r5qy30vx.js} +4 -4
  119. package/dist/chunks/studio-rj9eamf8.js +5 -0
  120. package/dist/chunks/studio-rptgsk96.js +5 -0
  121. package/dist/chunks/studio-rthyg42z.js +928 -0
  122. package/dist/chunks/studio-s19tp1xr.js +5 -0
  123. package/dist/chunks/studio-s32578pn.js +5 -0
  124. package/dist/chunks/studio-svb7yyve.js +5 -0
  125. package/dist/chunks/studio-syreb644.js +5 -0
  126. package/dist/chunks/studio-tf9jryb1.js +10 -0
  127. package/dist/chunks/studio-tfkznv39.js +5 -0
  128. package/dist/chunks/studio-tft6r105.js +5 -0
  129. package/dist/chunks/studio-txf2kysx.js +5 -0
  130. package/dist/chunks/studio-v8867ddv.js +12 -0
  131. package/dist/chunks/studio-vxq7fcvv.js +5 -0
  132. package/dist/chunks/studio-w4rz9hdk.js +5 -0
  133. package/dist/chunks/studio-wbcjbhy1.js +9 -0
  134. package/dist/chunks/studio-wc649wss.js +5 -0
  135. package/dist/chunks/studio-wys23mxz.js +5 -0
  136. package/dist/chunks/studio-x1x167jh.js +5 -0
  137. package/dist/chunks/studio-x9hsze61.js +5 -0
  138. package/dist/chunks/studio-y0n7yxea.js +9 -0
  139. package/dist/chunks/studio-y3ahhdpx.js +5 -0
  140. package/dist/chunks/studio-z97c48fk.js +5 -0
  141. package/dist/chunks/studio-zat62m0c.js +5 -0
  142. package/dist/chunks/studio-zcd2e7yz.js +5 -0
  143. package/dist/chunks/studio-zz9qe3rw.js +64 -0
  144. package/dist/chunks/swatch-group-ewh24n1f.js +4 -0
  145. package/dist/chunks/tabs-f5cw3tgp.js +4 -0
  146. package/dist/chunks/textfield-wewc8rq2.js +4 -0
  147. package/dist/chunks/toast-1gv0h53h.js +4 -0
  148. package/dist/chunks/toast-host-1yg5ar8d.js +4 -0
  149. package/dist/chunks/{toolbar-2nnzqhrj.js → toolbar-44cm5cta.js} +3 -3
  150. package/dist/chunks/tooltip-yypz28gq.js +4 -0
  151. package/dist/chunks/tree-9xa4hm7n.js +4 -0
  152. package/dist/chunks/tsMode-epyfz1hv.js +14 -0
  153. package/dist/chunks/workspace-dz7nxfhv.js +4 -0
  154. package/dist/chunks/workspace-home-xsd7hkjw.js +4 -0
  155. package/dist/chunks/ws-client-bv552sfx.js +4 -0
  156. package/dist/iframe-entry.js +20 -16
  157. package/dist/manifest.json +77 -63
  158. package/dist/studio.js +106 -107
  159. package/dist/workers/ts.worker.js +3 -3
  160. package/package.json +17 -16
  161. package/src/about/about-modal.ts +4 -3
  162. package/src/account/action-flow.ts +45 -38
  163. package/src/account-status.ts +7 -2
  164. package/src/browse/component-examples.ts +272 -0
  165. package/src/browse/library-commands.ts +64 -104
  166. package/src/browse/library-layouts.ts +270 -53
  167. package/src/browse/library-model.ts +194 -134
  168. package/src/browse/library-pane.ts +748 -440
  169. package/src/browse/library-preview.ts +161 -26
  170. package/src/browse/library-source.ts +308 -41
  171. package/src/browse/preview-root.ts +78 -0
  172. package/src/browse/preview-style.ts +155 -0
  173. package/src/canvas/asset-refs.ts +40 -10
  174. package/src/canvas/asset-resolve.ts +19 -23
  175. package/src/canvas/canvas-helpers.ts +7 -6
  176. package/src/canvas/canvas-live-render.ts +260 -77
  177. package/src/canvas/canvas-origin.ts +37 -12
  178. package/src/canvas/canvas-page-context.ts +112 -0
  179. package/src/canvas/canvas-patcher.ts +30 -30
  180. package/src/canvas/canvas-perf.ts +11 -5
  181. package/src/canvas/canvas-render.ts +175 -148
  182. package/src/canvas/canvas-surface.ts +5 -3
  183. package/src/canvas/canvas-utils.ts +147 -61
  184. package/src/canvas/dialog-path.ts +9 -4
  185. package/src/canvas/dialog-state.ts +1 -2
  186. package/src/canvas/diff-marks.ts +9 -16
  187. package/src/canvas/diff-toolbar.ts +8 -7
  188. package/src/canvas/edit-width-drag.ts +4 -1
  189. package/src/canvas/edit-width.ts +1 -3
  190. package/src/canvas/iframe-channel.ts +27 -13
  191. package/src/canvas/iframe-drop.ts +120 -12
  192. package/src/canvas/iframe-editable-root.ts +29 -16
  193. package/src/canvas/iframe-entry.ts +104 -75
  194. package/src/canvas/iframe-eval.ts +73 -49
  195. package/src/canvas/iframe-host.ts +269 -158
  196. package/src/canvas/iframe-inline-edit.ts +49 -30
  197. package/src/canvas/iframe-insert.ts +5 -4
  198. package/src/canvas/iframe-interaction.ts +30 -25
  199. package/src/canvas/iframe-keys.ts +11 -0
  200. package/src/canvas/iframe-overlay.ts +1 -1
  201. package/src/canvas/iframe-patch.ts +25 -21
  202. package/src/canvas/iframe-position.ts +12 -6
  203. package/src/canvas/iframe-protocol.ts +36 -21
  204. package/src/canvas/iframe-render.ts +84 -35
  205. package/src/canvas/iframe-slash.ts +24 -11
  206. package/src/canvas/iframe-subtree.ts +10 -3
  207. package/src/canvas/npm-modules.ts +22 -0
  208. package/src/canvas/path-mapping.ts +19 -12
  209. package/src/canvas/popover-path.ts +7 -3
  210. package/src/canvas/popover-state.ts +1 -2
  211. package/src/canvas/preview-navigate.ts +16 -0
  212. package/src/canvas/repeater-sources.ts +200 -0
  213. package/src/canvas/serialize-scope.ts +13 -5
  214. package/src/canvas/stage-scrollbar.ts +7 -2
  215. package/src/canvas/surface-registry.ts +27 -3
  216. package/src/collab/collab-commands.ts +7 -2
  217. package/src/collab/collab-session.ts +84 -45
  218. package/src/collab/collab-state.ts +4 -3
  219. package/src/collab/monaco-binding.ts +16 -13
  220. package/src/collab/monaco-cursors.ts +5 -2
  221. package/src/commands/app-commands.ts +16 -6
  222. package/src/commands/budget.ts +13 -11
  223. package/src/commands/command-args.ts +103 -71
  224. package/src/commands/context.ts +55 -28
  225. package/src/commands/defaults.ts +69 -45
  226. package/src/commands/keymap.ts +56 -43
  227. package/src/commands/levels.ts +14 -8
  228. package/src/commands/live-context.ts +9 -7
  229. package/src/commands/reference.ts +2 -1
  230. package/src/commands/registry.ts +22 -11
  231. package/src/commands/run-reported.ts +15 -10
  232. package/src/component-props.ts +46 -30
  233. package/src/content/collection-match.ts +11 -9
  234. package/src/content/collection-pages.ts +412 -0
  235. package/src/content/content-collections.ts +270 -0
  236. package/src/content/draft-state.ts +4 -3
  237. package/src/content/entry-commands.ts +54 -62
  238. package/src/content/entry-editor.ts +137 -32
  239. package/src/content/entry-fields.ts +25 -8
  240. package/src/content/entry-inspector.ts +225 -0
  241. package/src/content/entry-model.ts +38 -15
  242. package/src/content/entry-open.ts +80 -0
  243. package/src/content/entry-summaries.ts +440 -0
  244. package/src/content/file-record-ids.ts +80 -0
  245. package/src/content/file-records.ts +125 -0
  246. package/src/content/page-summaries.ts +129 -0
  247. package/src/content/record-entry.ts +201 -0
  248. package/src/content/writing-banner.ts +177 -0
  249. package/src/create/add-new-commands.ts +185 -0
  250. package/src/create/add-new-flow.ts +1116 -0
  251. package/src/create/add-new.ts +1014 -0
  252. package/src/create/element-name.ts +121 -0
  253. package/src/editor/canvas-context-menu.ts +1 -2
  254. package/src/editor/canvas-slash-bridge.ts +3 -6
  255. package/src/editor/content-slice.ts +16 -14
  256. package/src/editor/context-menu.ts +35 -22
  257. package/src/editor/convert-targets.ts +30 -35
  258. package/src/editor/convert-to-component.ts +78 -48
  259. package/src/editor/convert-to-repeater.ts +34 -22
  260. package/src/editor/editable-tags.ts +17 -2
  261. package/src/editor/file-drop-action.ts +29 -22
  262. package/src/editor/inline-edit-apply.ts +79 -17
  263. package/src/editor/inline-edit.ts +76 -50
  264. package/src/editor/inline-format.ts +59 -56
  265. package/src/editor/inline-link.ts +40 -15
  266. package/src/editor/insert-component.ts +91 -0
  267. package/src/editor/insert-zone-action.ts +14 -5
  268. package/src/editor/merge-tags.ts +58 -19
  269. package/src/editor/repeater-scope.ts +39 -28
  270. package/src/editor/shortcuts.ts +76 -42
  271. package/src/editor/slash-menu.ts +52 -14
  272. package/src/files/asset-versions.ts +4 -4
  273. package/src/files/components.ts +89 -20
  274. package/src/files/elements.ts +5 -6
  275. package/src/files/file-ops.ts +92 -41
  276. package/src/files/files.ts +248 -239
  277. package/src/files/fs-events.ts +18 -7
  278. package/src/files/media-meta.ts +97 -2
  279. package/src/files/media-paths.ts +3 -0
  280. package/src/files/media-types.ts +55 -0
  281. package/src/files/media-upload.ts +209 -52
  282. package/src/files/new-file-name.ts +156 -0
  283. package/src/files/serialize-document.ts +15 -5
  284. package/src/format/constraints.ts +1 -1
  285. package/src/format/convert-file.ts +27 -13
  286. package/src/format/format-choices.ts +2 -1
  287. package/src/format/format-host.ts +91 -50
  288. package/src/github/github-auth-status.ts +27 -3
  289. package/src/github/github-auth.ts +21 -11
  290. package/src/github/github-publish.ts +28 -23
  291. package/src/grid/cell-editors.ts +46 -31
  292. package/src/grid/cell-popovers.ts +5 -3
  293. package/src/grid/edit-buffer.ts +10 -9
  294. package/src/grid/grid-controller.ts +25 -23
  295. package/src/grid/grid-layout.ts +21 -5
  296. package/src/grid/grid-open.ts +3 -33
  297. package/src/grid/grid-panel.ts +65 -6
  298. package/src/grid/grid-reveal.ts +34 -0
  299. package/src/grid/grid-source.ts +9 -31
  300. package/src/grid/grid-view.ts +45 -23
  301. package/src/grid/redirects-grid.ts +14 -5
  302. package/src/grid/redirects.ts +16 -14
  303. package/src/grid/schema-columns.ts +25 -19
  304. package/src/grid/sources/connector-source.ts +47 -20
  305. package/src/grid/sources/content-source.ts +100 -31
  306. package/src/grid/sources/csv-file-source.ts +2 -2
  307. package/src/grid/tabulator-tables.d.ts +61 -34
  308. package/src/hosting/document.ts +2 -2
  309. package/src/hosting/stage.ts +7 -7
  310. package/src/i18n/i18n-commands.ts +3 -3
  311. package/src/media/media-commands.ts +181 -0
  312. package/src/media/media-library-model.ts +232 -0
  313. package/src/media/media-library.ts +1283 -0
  314. package/src/media/media-pane.ts +51 -20
  315. package/src/new-project/add-repo-modal.ts +20 -19
  316. package/src/new-project/import-tab.ts +10 -14
  317. package/src/new-project/location-fields.ts +2 -1
  318. package/src/new-project/new-project-modal.ts +12 -9
  319. package/src/packages/jxsuite-update.ts +4 -3
  320. package/src/packages/pull-package-sync.ts +26 -25
  321. package/src/page-params.ts +97 -68
  322. package/src/panels/activity-panel.ts +38 -14
  323. package/src/panels/ai-chat/attached-context.ts +13 -7
  324. package/src/panels/ai-chat/chat-markdown.ts +8 -8
  325. package/src/panels/ai-chat/chat-view.ts +80 -73
  326. package/src/panels/ai-chat/composer.ts +30 -33
  327. package/src/panels/ai-chat/sessions-view.ts +1 -2
  328. package/src/panels/ai-panel.ts +23 -20
  329. package/src/panels/block-action-bar.ts +57 -46
  330. package/src/panels/bottom-dock.ts +5 -3
  331. package/src/panels/canvas-dnd-bridge.ts +36 -26
  332. package/src/panels/chat-panel.ts +6 -6
  333. package/src/panels/collections-panel.ts +306 -0
  334. package/src/panels/component-preview.ts +35 -13
  335. package/src/panels/data-explorer.ts +63 -26
  336. package/src/panels/data-grid.ts +18 -11
  337. package/src/panels/dnd.ts +115 -138
  338. package/src/panels/editors.ts +52 -21
  339. package/src/panels/elements-panel.ts +19 -5
  340. package/src/panels/empty-state.ts +2 -2
  341. package/src/panels/events-panel.ts +84 -69
  342. package/src/panels/formula-workspace.ts +78 -54
  343. package/src/panels/frontmatter-fields.ts +33 -28
  344. package/src/panels/git-diff-open.ts +10 -6
  345. package/src/panels/head-panel.ts +488 -202
  346. package/src/panels/i18n-panel.ts +20 -12
  347. package/src/panels/imports-panel.ts +25 -47
  348. package/src/panels/jump-bar.ts +75 -31
  349. package/src/panels/layers-panel.ts +130 -101
  350. package/src/panels/left-panel.ts +25 -11
  351. package/src/panels/navigator-panels.ts +23 -11
  352. package/src/panels/pane-context.ts +140 -49
  353. package/src/panels/pane-grid.ts +55 -26
  354. package/src/panels/panel-registry.ts +36 -46
  355. package/src/panels/properties-panel.ts +182 -124
  356. package/src/panels/quick-search.ts +53 -31
  357. package/src/panels/right-panel.ts +201 -45
  358. package/src/panels/seo-modal.ts +54 -125
  359. package/src/panels/settings-menu.ts +63 -61
  360. package/src/panels/settings-pane.ts +268 -162
  361. package/src/panels/signals-panel.ts +156 -135
  362. package/src/panels/site-model.ts +367 -0
  363. package/src/panels/site-panel.ts +638 -0
  364. package/src/panels/statement-editor.ts +230 -140
  365. package/src/panels/style-panel.ts +253 -206
  366. package/src/panels/style-utils.ts +114 -51
  367. package/src/panels/stylebook-doc.ts +195 -60
  368. package/src/panels/stylebook-layers-panel.ts +48 -89
  369. package/src/panels/stylebook-panel.ts +60 -27
  370. package/src/panels/styles-panel.ts +327 -0
  371. package/src/panels/tab-drop.ts +27 -11
  372. package/src/panels/tab-strip.ts +143 -49
  373. package/src/platform-errors.ts +57 -43
  374. package/src/platform.ts +59 -31
  375. package/src/platforms/cloud.ts +373 -176
  376. package/src/platforms/default-platform.ts +14 -8
  377. package/src/platforms/devserver.ts +180 -142
  378. package/src/platforms/read-batcher.ts +16 -8
  379. package/src/project-list.ts +2 -1
  380. package/src/publish/deploy-checklist.ts +20 -9
  381. package/src/publish/pages-service.ts +66 -27
  382. package/src/publish/publish-changes.ts +491 -0
  383. package/src/publish/publish-commands.ts +88 -22
  384. package/src/publish/publish-ids.ts +49 -0
  385. package/src/publish/publish-panel.ts +10 -6
  386. package/src/publish/publish-quick.ts +148 -0
  387. package/src/publish/publish-workspace.ts +926 -0
  388. package/src/publish/source-control.ts +907 -0
  389. package/src/recent-projects.ts +15 -8
  390. package/src/resize-edges.ts +26 -36
  391. package/src/services/a11y-report.ts +26 -19
  392. package/src/services/agent-seed.ts +23 -11
  393. package/src/services/ai-ask.ts +9 -12
  394. package/src/services/ai-command-tools.ts +68 -62
  395. package/src/services/ai-import-tools.ts +80 -67
  396. package/src/services/ai-models.ts +17 -13
  397. package/src/services/ai-project-tools.ts +68 -32
  398. package/src/services/ai-session-store.ts +19 -10
  399. package/src/services/ai-system-prompt.ts +46 -20
  400. package/src/services/ai-tools.ts +71 -27
  401. package/src/services/ai-write-report.ts +20 -12
  402. package/src/services/ai-writes.ts +4 -4
  403. package/src/services/automation.ts +74 -51
  404. package/src/services/cem-export.ts +149 -58
  405. package/src/services/cf-settings.ts +4 -4
  406. package/src/services/clock.ts +6 -1
  407. package/src/services/code-services.ts +37 -31
  408. package/src/services/connection.ts +2 -2
  409. package/src/services/context-manager.ts +14 -10
  410. package/src/services/context-resolver.ts +20 -8
  411. package/src/services/data-service.ts +19 -15
  412. package/src/services/directory-picker.ts +20 -14
  413. package/src/services/document-assistant.ts +39 -19
  414. package/src/services/gated-registry.ts +10 -6
  415. package/src/services/harness/chat-reducer.ts +23 -1
  416. package/src/services/import-client.ts +91 -38
  417. package/src/services/import-seed.ts +8 -2
  418. package/src/services/jx-validate.ts +28 -31
  419. package/src/services/live-preview.ts +0 -0
  420. package/src/services/live-surfaces.ts +6 -3
  421. package/src/services/model-uri.ts +10 -2
  422. package/src/services/module-preloads.ts +12 -8
  423. package/src/services/monaco-buffer.ts +43 -24
  424. package/src/services/monaco-lazy.ts +7 -8
  425. package/src/services/monaco-setup.ts +23 -16
  426. package/src/services/notify.ts +44 -17
  427. package/src/services/popover-report.ts +25 -10
  428. package/src/services/preview-eval.ts +20 -8
  429. package/src/services/profile.ts +13 -6
  430. package/src/services/project-path.ts +13 -10
  431. package/src/services/references.ts +0 -0
  432. package/src/services/render-critic.ts +29 -9
  433. package/src/services/settings/definitions.ts +25 -14
  434. package/src/services/settings/kernel.ts +13 -4
  435. package/src/services/settings/write-queue.ts +9 -9
  436. package/src/services/token-lint.ts +12 -10
  437. package/src/services/tool-executor.ts +17 -5
  438. package/src/services/tool-outcomes.ts +44 -24
  439. package/src/services/trusted-types.ts +24 -7
  440. package/src/settings/contexts-section.ts +11 -8
  441. package/src/settings/contributed-section.ts +128 -70
  442. package/src/settings/css-vars-editor.ts +119 -32
  443. package/src/settings/defs-editor.ts +38 -35
  444. package/src/settings/developer-panels.ts +43 -0
  445. package/src/settings/extension-rows.ts +16 -8
  446. package/src/settings/extension-sections.ts +42 -14
  447. package/src/settings/extensions-section.ts +4 -1
  448. package/src/settings/general-settings.ts +101 -28
  449. package/src/settings/locales-section.ts +20 -12
  450. package/src/settings/preferences-dialog.ts +17 -7
  451. package/src/settings/preferences-keymap.ts +7 -5
  452. package/src/settings/preferences-sections.ts +11 -5
  453. package/src/settings/project-sections.ts +28 -11
  454. package/src/settings/schema-field-ui.ts +12 -1
  455. package/src/settings/section-registry.ts +116 -11
  456. package/src/settings/settings-dialog.ts +192 -0
  457. package/src/settings/settings-document.ts +186 -166
  458. package/src/shell/tree.ts +6 -5
  459. package/src/shell.ts +536 -477
  460. package/src/site-context.ts +101 -32
  461. package/src/state.ts +68 -57
  462. package/src/store.ts +54 -34
  463. package/src/studio.ts +219 -116
  464. package/src/style/live-preview.ts +1 -1
  465. package/src/style/project-styles.ts +40 -7
  466. package/src/style/stylebook-specimens.ts +89 -0
  467. package/src/style/stylebook-target.ts +26 -0
  468. package/src/style/tag-inheritance.ts +105 -0
  469. package/src/style/token-ref.ts +5 -4
  470. package/src/style/variables-focus.ts +61 -0
  471. package/src/style/variables-view.ts +235 -0
  472. package/src/surfaces/about.ts +18 -19
  473. package/src/surfaces/add-new.json +1482 -0
  474. package/src/surfaces/add-new.ts +325 -0
  475. package/src/surfaces/add-repo.ts +17 -19
  476. package/src/surfaces/ai-chat.ts +33 -38
  477. package/src/surfaces/ai-credentials-form.ts +6 -16
  478. package/src/surfaces/ai-managed-connect.ts +3 -4
  479. package/src/surfaces/ai-model-picker.ts +3 -5
  480. package/src/surfaces/block-action-bar.ts +22 -11
  481. package/src/surfaces/boot-failure.json +4 -3
  482. package/src/surfaces/boot-failure.ts +4 -5
  483. package/src/surfaces/bottom-dock.json +1 -1
  484. package/src/surfaces/bottom-dock.ts +9 -18
  485. package/src/surfaces/canvas-stage.json +30 -19
  486. package/src/surfaces/canvas-stage.ts +17 -16
  487. package/src/surfaces/cf-account-picker.ts +17 -18
  488. package/src/surfaces/collections-panel.json +615 -0
  489. package/src/surfaces/collections-panel.ts +168 -0
  490. package/src/surfaces/commandbar.json +241 -96
  491. package/src/surfaces/commandbar.ts +145 -105
  492. package/src/surfaces/convert-repeater.ts +17 -19
  493. package/src/surfaces/data-actions.ts +9 -7
  494. package/src/surfaces/dialog.ts +26 -28
  495. package/src/surfaces/diff-toolbar.ts +9 -7
  496. package/src/surfaces/empty-state.json +1 -1
  497. package/src/surfaces/empty-state.ts +7 -10
  498. package/src/surfaces/entry-editor.json +136 -0
  499. package/src/surfaces/entry-editor.ts +35 -11
  500. package/src/surfaces/entry-inspector.json +371 -0
  501. package/src/surfaces/entry-inspector.ts +143 -0
  502. package/src/surfaces/expression-editor.ts +11 -25
  503. package/src/surfaces/files-panel.ts +27 -14
  504. package/src/surfaces/formula-palette.ts +4 -5
  505. package/src/surfaces/github-auth.ts +16 -20
  506. package/src/surfaces/github-publish.ts +17 -22
  507. package/src/surfaces/grid-cell.ts +30 -31
  508. package/src/surfaces/grid-open.ts +10 -14
  509. package/src/surfaces/grid-panel.ts +13 -9
  510. package/src/surfaces/grid-replace.ts +13 -13
  511. package/src/surfaces/grid-views.ts +15 -18
  512. package/src/surfaces/inspector-dock.json +162 -14
  513. package/src/surfaces/inspector-dock.ts +59 -31
  514. package/src/surfaces/jump-bar.ts +5 -6
  515. package/src/surfaces/jxsuite-update.ts +16 -22
  516. package/src/surfaces/library-pane.json +670 -437
  517. package/src/surfaces/library-pane.ts +111 -77
  518. package/src/surfaces/logic-panel.ts +17 -30
  519. package/src/surfaces/logic-workspace.ts +13 -15
  520. package/src/surfaces/media-browser.json +62 -6
  521. package/src/surfaces/media-browser.ts +26 -11
  522. package/src/surfaces/media-field.json +1 -1
  523. package/src/surfaces/media-field.ts +4 -5
  524. package/src/surfaces/media-library.json +1586 -0
  525. package/src/surfaces/media-library.ts +282 -0
  526. package/src/surfaces/media-pane.ts +8 -7
  527. package/src/surfaces/media-pick-dialog.json +71 -0
  528. package/src/surfaces/media-pick-dialog.ts +132 -0
  529. package/src/surfaces/menu.ts +47 -23
  530. package/src/surfaces/navigator-dock.ts +11 -12
  531. package/src/surfaces/new-project.ts +27 -26
  532. package/src/surfaces/pane-context.json +144 -77
  533. package/src/surfaces/pane-context.ts +44 -25
  534. package/src/surfaces/pane-grid.ts +15 -10
  535. package/src/surfaces/panel-activity.ts +5 -6
  536. package/src/surfaces/panel-data.ts +3 -4
  537. package/src/surfaces/panel-deploy-checklist.json +12 -6
  538. package/src/surfaces/panel-deploy-checklist.ts +8 -7
  539. package/src/surfaces/panel-elements.ts +5 -7
  540. package/src/surfaces/panel-i18n.ts +5 -7
  541. package/src/surfaces/panel-imports.ts +6 -13
  542. package/src/surfaces/panel-outline.ts +25 -19
  543. package/src/surfaces/panel-page.json +72 -2
  544. package/src/surfaces/panel-page.ts +49 -25
  545. package/src/surfaces/panel-problems.ts +5 -7
  546. package/src/surfaces/panel-signals.json +1 -1
  547. package/src/surfaces/panel-signals.ts +25 -39
  548. package/src/surfaces/panel-stylebook-layers.json +71 -204
  549. package/src/surfaces/panel-stylebook-layers.ts +5 -29
  550. package/src/surfaces/preferences.json +120 -48
  551. package/src/surfaces/preferences.ts +29 -11
  552. package/src/surfaces/progress-modal.ts +15 -9
  553. package/src/surfaces/properties-panel.json +1 -1
  554. package/src/surfaces/properties-panel.ts +20 -42
  555. package/src/surfaces/publish-quick.json +428 -0
  556. package/src/surfaces/publish-quick.ts +165 -0
  557. package/src/surfaces/publish-workspace.json +2892 -0
  558. package/src/surfaces/publish-workspace.ts +391 -0
  559. package/src/surfaces/publish.ts +18 -18
  560. package/src/surfaces/push-plan.ts +7 -8
  561. package/src/surfaces/rail.json +1 -159
  562. package/src/surfaces/rail.ts +22 -79
  563. package/src/surfaces/reference-field.ts +11 -15
  564. package/src/surfaces/schema-builder.ts +16 -22
  565. package/src/surfaces/schema-form.json +10 -2
  566. package/src/surfaces/schema-form.ts +17 -25
  567. package/src/surfaces/secret-field.ts +9 -6
  568. package/src/surfaces/seo.json +1 -1
  569. package/src/surfaces/seo.ts +21 -15
  570. package/src/surfaces/settings-contexts.ts +9 -7
  571. package/src/surfaces/settings-contributed.json +5 -5
  572. package/src/surfaces/settings-contributed.ts +32 -12
  573. package/src/surfaces/settings-css-vars.json +44 -36
  574. package/src/surfaces/settings-css-vars.ts +82 -16
  575. package/src/surfaces/settings-defs.json +2 -2
  576. package/src/surfaces/settings-defs.ts +17 -21
  577. package/src/surfaces/settings-deploy.json +2 -15
  578. package/src/surfaces/settings-deploy.ts +9 -16
  579. package/src/surfaces/settings-dialog.json +51 -0
  580. package/src/surfaces/settings-dialog.ts +169 -0
  581. package/src/surfaces/settings-extensions.ts +5 -6
  582. package/src/surfaces/settings-head.ts +33 -39
  583. package/src/surfaces/settings-locales.ts +8 -20
  584. package/src/surfaces/settings-overview.json +3 -3
  585. package/src/surfaces/settings-overview.ts +14 -8
  586. package/src/surfaces/settings-packages.ts +8 -7
  587. package/src/surfaces/settings-pane.json +269 -46
  588. package/src/surfaces/settings-pane.ts +88 -36
  589. package/src/surfaces/settings-rawjson.ts +5 -6
  590. package/src/surfaces/shell.json +2 -1
  591. package/src/surfaces/shell.ts +1 -2
  592. package/src/surfaces/site-panel.json +1029 -0
  593. package/src/surfaces/site-panel.ts +233 -0
  594. package/src/surfaces/slash-menu.json +63 -21
  595. package/src/surfaces/slash-menu.ts +42 -9
  596. package/src/surfaces/statements.ts +13 -18
  597. package/src/surfaces/statusbar.ts +56 -29
  598. package/src/surfaces/style-panel.ts +18 -31
  599. package/src/surfaces/stylebook-chrome.ts +3 -5
  600. package/src/surfaces/styles-panel.json +735 -0
  601. package/src/surfaces/styles-panel.ts +195 -0
  602. package/src/surfaces/styles-variables.json +149 -0
  603. package/src/surfaces/styles-variables.ts +145 -0
  604. package/src/surfaces/tab-strip.json +4 -4
  605. package/src/surfaces/tab-strip.ts +14 -13
  606. package/src/surfaces/target-line.ts +6 -7
  607. package/src/surfaces/welcome.ts +6 -8
  608. package/src/surfaces/writing-banner.json +94 -0
  609. package/src/surfaces/writing-banner.ts +80 -0
  610. package/src/tabs/bare-repeaters.ts +101 -0
  611. package/src/tabs/patch-ops.ts +4 -3
  612. package/src/tabs/project-config.ts +32 -21
  613. package/src/tabs/selection.ts +36 -8
  614. package/src/tabs/tab.ts +99 -48
  615. package/src/tabs/transact.ts +98 -53
  616. package/src/types.ts +53 -43
  617. package/src/ui/ai-credentials-form.ts +10 -13
  618. package/src/ui/ai-model-picker.ts +3 -3
  619. package/src/ui/cf-account-picker.ts +7 -9
  620. package/src/ui/color-selector.ts +9 -5
  621. package/src/ui/dynamic-slot.ts +7 -8
  622. package/src/ui/expression-editor.ts +112 -105
  623. package/src/ui/form-controls.ts +104 -56
  624. package/src/ui/formula-catalog.ts +239 -230
  625. package/src/ui/formula-chips.ts +33 -18
  626. package/src/ui/layers.ts +57 -31
  627. package/src/ui/media-picker.ts +300 -47
  628. package/src/ui/popover-surface.ts +20 -12
  629. package/src/ui/progress-modal.ts +13 -8
  630. package/src/ui/regions.ts +14 -12
  631. package/src/ui/schema-form.ts +372 -97
  632. package/src/ui/surface.ts +71 -8
  633. package/src/ui/timing.ts +9 -0
  634. package/src/ui/value-source.ts +44 -31
  635. package/src/ui/virtual-window.ts +29 -3
  636. package/src/utils/base64.ts +6 -4
  637. package/src/utils/edit-display.ts +366 -111
  638. package/src/utils/element-selectors.ts +3 -2
  639. package/src/utils/google-fonts.ts +15 -14
  640. package/src/utils/inherited-style.ts +18 -20
  641. package/src/utils/link-target.ts +1 -1
  642. package/src/utils/local-day.ts +18 -0
  643. package/src/utils/preview-format.ts +25 -3
  644. package/src/utils/strip-events.ts +49 -22
  645. package/src/utils/studio-utils.ts +29 -14
  646. package/src/view.ts +2 -3
  647. package/src/workspace/pane-derive.ts +14 -16
  648. package/src/workspace/session.ts +81 -50
  649. package/src/workspace/tab-workspace.ts +137 -0
  650. package/src/workspace/workspace-back.ts +189 -0
  651. package/src/workspace/workspace-home.ts +133 -0
  652. package/src/workspace/workspace-ids.ts +129 -0
  653. package/src/workspace/workspace-switch.ts +372 -0
  654. package/src/workspace/workspace.ts +165 -47
  655. package/styles/shell-frame.css +30 -7
  656. package/styles/shell-frame.json +15 -3
  657. package/dist/chunks/2020-va2wwra4.js +0 -4
  658. package/dist/chunks/adapter-f93pafd7.js +0 -4
  659. package/dist/chunks/cf-account-picker-af1h381v.js +0 -4
  660. package/dist/chunks/cf-settings-125tdm0w.js +0 -4
  661. package/dist/chunks/color-area-jqhna8zy.js +0 -4
  662. package/dist/chunks/color-field-embq5ctg.js +0 -4
  663. package/dist/chunks/color-fpezvnkt.js +0 -4
  664. package/dist/chunks/color-slider-1qmw7wqy.js +0 -4
  665. package/dist/chunks/combobox-773hdzyj.js +0 -4
  666. package/dist/chunks/components-ax2f1fnn.js +0 -4
  667. package/dist/chunks/content-source-108aqtk1.js +0 -4
  668. package/dist/chunks/convert-to-repeater-ry5rmppr.js +0 -4
  669. package/dist/chunks/dialog-mtm9fq5t.js +0 -4
  670. package/dist/chunks/editor-pfgdmkb0.js +0 -4
  671. package/dist/chunks/entry-commands-t0k1q0kx.js +0 -4
  672. package/dist/chunks/extension-sections-6mwh2hf8.js +0 -4
  673. package/dist/chunks/file-ops-meej5mqf.js +0 -4
  674. package/dist/chunks/files-44vp8a2s.js +0 -4
  675. package/dist/chunks/format-host-7q1petb9.js +0 -4
  676. package/dist/chunks/fs-events-h2mwnxaz.js +0 -4
  677. package/dist/chunks/git-panel-zngbymvd.js +0 -4
  678. package/dist/chunks/grid-view-sx9vynsp.js +0 -51
  679. package/dist/chunks/icons-p44xhjf0.js +0 -4
  680. package/dist/chunks/iframe-entry-f7mqjwpm.js +0 -5
  681. package/dist/chunks/iframe-entry-haw7jkvz.js +0 -5
  682. package/dist/chunks/iframe-entry-z6qrytv3.js +0 -5
  683. package/dist/chunks/iframe-host-qk3ycq87.js +0 -4
  684. package/dist/chunks/index-wkyfjeqz.js +0 -4
  685. package/dist/chunks/jsonMode-s8kt9dsj.js +0 -13
  686. package/dist/chunks/jx-validate-psgd7vsx.js +0 -4
  687. package/dist/chunks/layers-panel-1kq2jyfk.js +0 -4
  688. package/dist/chunks/layers-xd92fg8d.js +0 -4
  689. package/dist/chunks/listbox-s8fkmnq8.js +0 -4
  690. package/dist/chunks/lit-html-ac4cwwr4.js +0 -4
  691. package/dist/chunks/media-picker-0svzkdyb.js +0 -4
  692. package/dist/chunks/menu-37d07z9m.js +0 -4
  693. package/dist/chunks/menu-5c55ea3d.js +0 -4
  694. package/dist/chunks/monaco-binding-z6r70pdt.js +0 -4
  695. package/dist/chunks/monaco-buffer-fz6gq5y7.js +0 -4
  696. package/dist/chunks/monaco-setup-q4axkgj7.js +0 -4
  697. package/dist/chunks/notify-0xgtn8q1.js +0 -4
  698. package/dist/chunks/number-field-gw7sq8rn.js +0 -4
  699. package/dist/chunks/parse-897fvqah.js +0 -4
  700. package/dist/chunks/platform-v1qctbe6.js +0 -4
  701. package/dist/chunks/popover-8htra4er.js +0 -4
  702. package/dist/chunks/publish-panel-ym1fmgq5.js +0 -4
  703. package/dist/chunks/quick-search-a54e6t5n.js +0 -4
  704. package/dist/chunks/references-gmqp82fs.js +0 -4
  705. package/dist/chunks/right-panel-v7t9awbk.js +0 -4
  706. package/dist/chunks/section-registry-vhyf3zsr.js +0 -4
  707. package/dist/chunks/select-n3v7gwas.js +0 -4
  708. package/dist/chunks/split-pff8f3ze.js +0 -4
  709. package/dist/chunks/studio-0bhx5hz6.js +0 -5
  710. package/dist/chunks/studio-0k1ddnyy.js +0 -6
  711. package/dist/chunks/studio-1b04pd0j.js +0 -64
  712. package/dist/chunks/studio-4dwbj6gb.js +0 -5
  713. package/dist/chunks/studio-4tanh828.js +0 -5
  714. package/dist/chunks/studio-55h4mstj.js +0 -4
  715. package/dist/chunks/studio-5tnvjkz4.js +0 -5
  716. package/dist/chunks/studio-60dqgb0c.js +0 -5
  717. package/dist/chunks/studio-61dp09qg.js +0 -9
  718. package/dist/chunks/studio-63epcrj2.js +0 -5
  719. package/dist/chunks/studio-74k29dy9.js +0 -5
  720. package/dist/chunks/studio-7vg6jcd3.js +0 -5
  721. package/dist/chunks/studio-7zx31knw.js +0 -9
  722. package/dist/chunks/studio-8bxk97km.js +0 -5
  723. package/dist/chunks/studio-933e30n4.js +0 -5
  724. package/dist/chunks/studio-98wnwhbg.js +0 -5
  725. package/dist/chunks/studio-9mccjwjh.js +0 -14
  726. package/dist/chunks/studio-a2ke1e4t.js +0 -5
  727. package/dist/chunks/studio-e3a2a6v3.js +0 -4
  728. package/dist/chunks/studio-e81q1vb9.js +0 -5
  729. package/dist/chunks/studio-epgrmqqm.js +0 -5
  730. package/dist/chunks/studio-ery42sqr.js +0 -4
  731. package/dist/chunks/studio-g1shz58g.js +0 -5
  732. package/dist/chunks/studio-gf0fz03c.js +0 -5
  733. package/dist/chunks/studio-gjtpe62q.js +0 -6
  734. package/dist/chunks/studio-gkskhg7m.js +0 -5
  735. package/dist/chunks/studio-gx4dnjzd.js +0 -5
  736. package/dist/chunks/studio-j03zga3y.js +0 -9
  737. package/dist/chunks/studio-jhpxeckz.js +0 -5
  738. package/dist/chunks/studio-k01r7ykp.js +0 -5
  739. package/dist/chunks/studio-kk7h30ne.js +0 -7
  740. package/dist/chunks/studio-kt1zqney.js +0 -5
  741. package/dist/chunks/studio-m3cwhvye.js +0 -5
  742. package/dist/chunks/studio-mcayt6hz.js +0 -5
  743. package/dist/chunks/studio-myjcghv3.js +0 -5
  744. package/dist/chunks/studio-n8bxwc3y.js +0 -4
  745. package/dist/chunks/studio-ngm8gvpb.js +0 -5
  746. package/dist/chunks/studio-nj5a01jy.js +0 -928
  747. package/dist/chunks/studio-pcgz8heb.js +0 -5
  748. package/dist/chunks/studio-qcsq0t15.js +0 -5
  749. package/dist/chunks/studio-qkstvve9.js +0 -5
  750. package/dist/chunks/studio-qv1pwd3c.js +0 -5
  751. package/dist/chunks/studio-rc6dvxjb.js +0 -7
  752. package/dist/chunks/studio-s8dmmz66.js +0 -5
  753. package/dist/chunks/studio-sp5xzbj4.js +0 -6
  754. package/dist/chunks/studio-szsjbj0g.js +0 -5
  755. package/dist/chunks/studio-t1xzawhr.js +0 -16
  756. package/dist/chunks/studio-tdcprbqb.js +0 -5
  757. package/dist/chunks/studio-vrmcjf9g.js +0 -7
  758. package/dist/chunks/studio-vyc0b4he.js +0 -10
  759. package/dist/chunks/studio-w0cgf6jg.js +0 -5
  760. package/dist/chunks/studio-w3hskzqk.js +0 -5
  761. package/dist/chunks/studio-xgrgcdyh.js +0 -9
  762. package/dist/chunks/studio-xz2yxfcb.js +0 -5
  763. package/dist/chunks/studio-y6n42tgs.js +0 -5
  764. package/dist/chunks/studio-yb560vxr.js +0 -5
  765. package/dist/chunks/studio-yn9f8vtx.js +0 -5
  766. package/dist/chunks/studio-yw05w8f0.js +0 -7
  767. package/dist/chunks/studio-z16sgavc.js +0 -5
  768. package/dist/chunks/studio-zey7zsdw.js +0 -12
  769. package/dist/chunks/studio-zyqn40w4.js +0 -5
  770. package/dist/chunks/swatch-group-8qh9a3bm.js +0 -4
  771. package/dist/chunks/tabs-jwthg40w.js +0 -4
  772. package/dist/chunks/textfield-spy3hc77.js +0 -4
  773. package/dist/chunks/toast-4d35zd64.js +0 -4
  774. package/dist/chunks/toast-host-8sjmfed3.js +0 -4
  775. package/dist/chunks/tooltip-fd6g9xy3.js +0 -4
  776. package/dist/chunks/tree-h19v1jfz.js +0 -4
  777. package/dist/chunks/tsMode-2frah6h1.js +0 -14
  778. package/dist/chunks/workspace-zt81g1ms.js +0 -4
  779. package/dist/chunks/ws-client-n2bh4hkf.js +0 -4
  780. package/src/panels/frontmatter-panel.ts +0 -701
  781. package/src/panels/git-panel.ts +0 -1120
  782. package/src/surfaces/doc-header.json +0 -776
  783. package/src/surfaces/doc-header.ts +0 -231
  784. package/src/surfaces/git-panel.json +0 -1880
  785. package/src/surfaces/git-panel.ts +0 -272
@@ -237,11 +237,11 @@ export async function relistLoadedDirs(
237
237
  }
238
238
 
239
239
  /**
240
- * Modes only a tab whose `document` is a placeholder offers: the media viewer, a CSV grid, the
241
- * Library. Spelled out as strings, not imported, because each constant's module drags its whole
242
- * editor in behind it.
240
+ * Modes only a tab whose `document` is a placeholder offers: the media viewer and a CSV grid.
241
+ * Spelled out as strings, not imported, because each constant's module drags its whole editor in
242
+ * behind it. A workspace home has no file at all, so it never reaches the re-read.
243
243
  */
244
- const STUB_DOCUMENT_MODES = new Set(["media", "grid", "manage"]);
244
+ const STUB_DOCUMENT_MODES = new Set(["media", "grid"]);
245
245
 
246
246
  /**
247
247
  * Whether a tab's `document` is a placeholder rather than its parsed file. Such a tab reads its
@@ -293,8 +293,16 @@ export interface FsSyncContext {
293
293
  * and this module is imported BY the file layer those panels sit on top of — reaching up would be
294
294
  * a cycle. The bootstrap owns the list because the bootstrap is where the panels are already in
295
295
  * scope.
296
+ *
297
+ * Handed the project-relative paths the batch touched, so a cache that only one directory can
298
+ * change (the media listing, the component registry) can ignore the rest. `undefined` means the
299
+ * stream had a gap and any path may have changed.
300
+ *
301
+ * `structural` says whether the batch added, removed or renamed anything, as opposed to only
302
+ * rewriting files that already existed: a cache of the file SET can ignore a batch of plain
303
+ * `change` events, which is every save. It is `true` after a gap.
296
304
  */
297
- invalidateDerivedCaches?: () => void;
305
+ invalidateDerivedCaches?: (paths?: readonly string[], structural?: boolean) => void;
298
306
  /**
299
307
  * After a reconnect, the clean, non-collab open tabs whose files may have changed while the
300
308
  * stream was down. The handler re-reads them; it should leave a tab whose content did not change
@@ -363,7 +371,7 @@ export function startFsSync(ctx: FsSyncContext): () => void {
363
371
  );
364
372
  if (reason === "reconnect") {
365
373
  invalidateUsages();
366
- ctx.invalidateDerivedCaches?.();
374
+ ctx.invalidateDerivedCaches?.(undefined, true);
367
375
  await Promise.all([relist, reloadIgnoreCache()]);
368
376
  } else {
369
377
  await relist;
@@ -413,7 +421,10 @@ export function startFsSync(ctx: FsSyncContext): () => void {
413
421
  // Removed layout's `$head` to the open page, a `$paths` enumeration listing entries that are
414
422
  // Gone.
415
423
  invalidateUsages();
416
- ctx.invalidateDerivedCaches?.();
424
+ ctx.invalidateDerivedCaches?.(
425
+ events.map((event) => event.path),
426
+ events.some((event) => event.type !== "change"),
427
+ );
417
428
  // Content versions are derived state of the same kind, and Studio's own writes change bytes:
418
429
  // A version kept past its echo would let an immutable cache answer with the old file.
419
430
  forgetEventVersions(events);
@@ -11,6 +11,12 @@
11
11
  * renders anyway. {@link recordImageSize} is how that surface hands the number back, and until
12
12
  * one does, the dimensions are `null` and the summary simply omits them. Downloading the file a
13
13
  * second time to measure it would be a network request per thumbnail for a line of text.
14
+ * - **An SVG's dimensions are its own**, read from the file: `width`/`height` on the root element, or
15
+ * its `viewBox` when either is missing ({@link svgIntrinsicSize}). A vector image has no pixels
16
+ * to decode, and an `<img>` of one that declares no size reports the replaced element's default
17
+ * object size (150 × 150 in Chromium), a number about the browser rather than the file. So
18
+ * {@link recordImageSize} ignores SVGs, and {@link loadMediaMeta} reads the text instead, which
19
+ * for a vector is a few hundred bytes the thumbnail already fetched.
14
20
  *
15
21
  * A missing number is `null` everywhere, never `0`. `0 × 0` and `0 B` are things a real file can
16
22
  * almost be, so they must not double as "we did not find out" — the same rule
@@ -23,6 +29,7 @@
23
29
  * @docs studio/projects/media
24
30
  */
25
31
 
32
+ import { isNumber } from "@jxsuite/schema/guards";
26
33
  import { getPlatform } from "../platform";
27
34
  import { baseName, dirName, normalizeProjectPath } from "./media-paths";
28
35
  import { notedListing } from "./asset-versions";
@@ -62,12 +69,87 @@ const inFlight = new Map<string, Promise<MediaMeta>>();
62
69
  */
63
70
  const measured = new Map<string, { width: number; height: number }>();
64
71
 
72
+ /**
73
+ * SVG paths whose own size has been read (or found unreadable), so {@link loadMediaMeta} reads each
74
+ * file once rather than on every paint that finds no dimensions: most SVGs declare none at all.
75
+ */
76
+ const svgRead = new Set<string>();
77
+
78
+ function isSvg(path: string): boolean {
79
+ return extensionOf(path) === ".svg";
80
+ }
81
+
82
+ /** A `width`/`height` attribute in user units (`64`, `64px`), or null for anything relative. */
83
+ function svgLength(value: string | null): number | null {
84
+ const match = value === null ? null : /^\s*(\d+(?:\.\d+)?)\s*(?:px)?\s*$/.exec(value);
85
+ const length = match ? Number(match[1]) : Number.NaN;
86
+ return length > 0 ? length : null;
87
+ }
88
+
89
+ /**
90
+ * The size an SVG document declares for itself, or null when it declares none (the intrinsic
91
+ * dimensions of an outermost `<svg>` element, SVG 2 §8.2): `width` and `height` where both are
92
+ * absolute lengths; one of them with the `viewBox`'s aspect ratio supplying the other; or the
93
+ * `viewBox`'s own width and height when neither is given. A percentage or a font-relative length is
94
+ * a size relative to whatever embeds the file, which a file on its own does not have.
95
+ */
96
+ export function svgIntrinsicSize(text: string): { width: number; height: number } | null {
97
+ const root = new DOMParser().parseFromString(text, "image/svg+xml").documentElement;
98
+ if (root.localName !== "svg") {
99
+ return null;
100
+ }
101
+ const width = svgLength(root.getAttribute("width"));
102
+ const height = svgLength(root.getAttribute("height"));
103
+ const box = (root.getAttribute("viewBox") ?? "")
104
+ .trim()
105
+ .split(/[\s,]+/)
106
+ .map(Number);
107
+ const boxWidth = box[2] ?? Number.NaN;
108
+ const boxHeight = box[3] ?? Number.NaN;
109
+ const hasBox = box.length === 4 && boxWidth > 0 && boxHeight > 0;
110
+ let size: { width: number; height: number } | null = null;
111
+ if (width !== null && height !== null) {
112
+ size = { height, width };
113
+ } else if (hasBox && width !== null) {
114
+ size = { height: (width * boxHeight) / boxWidth, width };
115
+ } else if (hasBox && height !== null) {
116
+ size = { height, width: (height * boxWidth) / boxHeight };
117
+ } else if (hasBox) {
118
+ size = { height: boxHeight, width: boxWidth };
119
+ }
120
+ return size && { height: Math.round(size.height), width: Math.round(size.width) };
121
+ }
122
+
123
+ /** Read an SVG's declared size into {@link measured}, once per path. */
124
+ async function readSvgSize(path: string): Promise<void> {
125
+ svgRead.add(path);
126
+ try {
127
+ const size = svgIntrinsicSize(await getPlatform().readFile(path));
128
+ if (size && size.width > 0 && size.height > 0) {
129
+ measured.set(path, size);
130
+ }
131
+ } catch {
132
+ // Unreadable: the dimensions stay unknown, which the summary already says by omitting them.
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Whether {@link loadMediaMeta} has nothing left to learn about a file: its record is settled and,
138
+ * for an SVG, its own declared size has been read. A surface asks this, rather than whether
139
+ * {@link peekMediaMeta} is null, before starting a load: a listing seeds every SVG's record without
140
+ * the dimensions only the file itself can give.
141
+ */
142
+ export function mediaMetaSettled(path: string): boolean {
143
+ const key = normalizeProjectPath(path);
144
+ return settled.has(key) && (!isSvg(key) || svgRead.has(key));
145
+ }
146
+
65
147
  /** Build a meta record from a path and whatever the listing knew about it. */
66
148
  function metaFrom(path: string, entry: DirEntry | undefined): MediaMeta {
67
149
  const name = baseName(path);
68
150
  const dims = measured.get(path);
69
151
  return {
70
- bytes: typeof entry?.size === "number" ? entry.size : null,
152
+ bytes: isNumber(entry?.size) ? entry.size : null,
71
153
  ext: extensionOf(name),
72
154
  height: dims?.height ?? null,
73
155
  kind: mediaKind({ name }),
@@ -117,7 +199,7 @@ export function seedMediaMeta(entries: Iterable<DirEntry>): void {
117
199
  export async function loadMediaMeta(path: string): Promise<MediaMeta> {
118
200
  const key = normalizeProjectPath(path);
119
201
  const done = settled.get(key);
120
- if (done) {
202
+ if (done && mediaMetaSettled(key)) {
121
203
  return done;
122
204
  }
123
205
  const running = inFlight.get(key);
@@ -125,6 +207,14 @@ export async function loadMediaMeta(path: string): Promise<MediaMeta> {
125
207
  return running;
126
208
  }
127
209
  const pending = (async (): Promise<MediaMeta> => {
210
+ if (isSvg(key) && !svgRead.has(key)) {
211
+ await readSvgSize(key);
212
+ }
213
+ if (done) {
214
+ // Seeded by a listing already: only the SVG's own size was missing.
215
+ const dims = measured.get(key);
216
+ return { ...done, height: dims?.height ?? null, width: dims?.width ?? null };
217
+ }
128
218
  let entry: DirEntry | undefined;
129
219
  try {
130
220
  const listing = await notedListing(() => getPlatform().listDirectory(dirName(key)));
@@ -153,6 +243,10 @@ export function recordImageSize(path: string, width: number, height: number): bo
153
243
  return false; // A broken or still-decoding image reports 0 — that is not a measurement.
154
244
  }
155
245
  const key = normalizeProjectPath(path);
246
+ if (isSvg(key)) {
247
+ // The decode of a vector measures the browser's default object size, not the file.
248
+ return false;
249
+ }
156
250
  const known = measured.get(key);
157
251
  if (known && known.width === width && known.height === height) {
158
252
  return false;
@@ -169,6 +263,7 @@ export function recordImageSize(path: string, width: number, height: number): bo
169
263
  export function invalidateMediaMeta(): void {
170
264
  settled.clear();
171
265
  measured.clear();
266
+ svgRead.clear();
172
267
  }
173
268
 
174
269
  /** `84 KB`, `1.2 MB`, `903 B` — one significant fraction, because this is a caption. */
@@ -66,6 +66,9 @@ export function dirName(path: string): string {
66
66
  /** The open project's content sections, or null when no project (or no `content:`) is loaded. */
67
67
  function contentSections(): Record<string, ContentSectionEntry> | null {
68
68
  const content = projectState?.projectConfig?.content;
69
+ /* SAFETY: `content` is the parser extension's section, which the schema types leave opaque; its
70
+ Project fragment schema shapes every entry as a `ContentSectionEntry`, and `contentMountFor`
71
+ (the only reader) touches nothing but an entry's optional `source` behind `def?.source`. */
69
72
  return (content as Record<string, ContentSectionEntry> | undefined) ?? null;
70
73
  }
71
74
 
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Which files are project media, by extension — the classification alone, with nothing imported.
3
+ *
4
+ * `files/media-upload.ts` owns uploading and re-exports these; they live here so a module that only
5
+ * needs to ask "is this file media" (the collection matcher behind the Content workspace, which the
6
+ * canvas resolver reads too) does not pull the upload core, the platform and the reference index in
7
+ * behind the question.
8
+ *
9
+ * @docs studio/projects/media
10
+ */
11
+
12
+ /** Extensions rendered as a thumbnail (media picker, cards, file-tree icons). */
13
+ export const IMAGE_EXTENSIONS = new Set([
14
+ ".jpg",
15
+ ".jpeg",
16
+ ".png",
17
+ ".gif",
18
+ ".svg",
19
+ ".webp",
20
+ ".avif",
21
+ ".ico",
22
+ ]);
23
+
24
+ /** Extensions an upload places as a `<video>`. */
25
+ export const VIDEO_EXTENSIONS = new Set([".mp4", ".webm", ".mov", ".m4v"]);
26
+
27
+ /** Extensions an upload places as an `<audio>`. */
28
+ export const AUDIO_EXTENSIONS = new Set([".mp3", ".wav", ".ogg", ".m4a", ".flac"]);
29
+
30
+ /**
31
+ * Every extension Studio treats as project media: a superset of {@link IMAGE_EXTENSIONS} and of
32
+ * every extension classified as video or audio, so a file it classifies is one the media listing
33
+ * shows.
34
+ */
35
+ export const MEDIA_EXTENSIONS = new Set([
36
+ ...IMAGE_EXTENSIONS,
37
+ ...VIDEO_EXTENSIONS,
38
+ ...AUDIO_EXTENSIONS,
39
+ ".pdf",
40
+ ".woff",
41
+ ".woff2",
42
+ ".ttf",
43
+ ".otf",
44
+ ]);
45
+
46
+ /** Lowercased extension including the dot, or `""` when the name carries none. */
47
+ export function extensionOf(name: string): string {
48
+ const dot = name.lastIndexOf(".");
49
+ return dot > 0 ? name.slice(dot).toLowerCase() : "";
50
+ }
51
+
52
+ /** Whether a file extension (with or without the leading dot) is a previewable image. */
53
+ export function isImage(ext: string): boolean {
54
+ return IMAGE_EXTENSIONS.has(ext.startsWith(".") ? ext.toLowerCase() : `.${ext.toLowerCase()}`);
55
+ }
@@ -3,15 +3,18 @@
3
3
  * Media upload core — the single path every upload surface goes through.
4
4
  *
5
5
  * Four surfaces feed it: the image field's Upload button (ui/media-picker), a file dropped on the
6
- * canvas (editor/file-drop-action), a file dropped on the Files tree (files/files), and the Manage
7
- * view's drop zone / Upload button (browse/browse). They differ only in how the destination
8
- * directory is chosen; naming, collision handling, the platform call, and cache invalidation are
9
- * shared here.
6
+ * canvas (editor/file-drop-action), a file dropped on the Files tree (files/files), and the Media
7
+ * workspace's drop zone and Upload button (media/media-library, and Add new › Media). They differ
8
+ * only in how the destination directory is chosen; naming, collision handling, the platform call,
9
+ * and cache invalidation are shared here.
10
10
  *
11
- * Destination (specs/site-architecture.md §9.1/§9.3): a document inside a content collection keeps
12
- * its media co-located in `content/<collection>/images/` — the entry references it file-relatively
13
- * and the collection loader rewrites it to the asset mount. Everything else goes to `public/`,
14
- * which is served from the site root.
11
+ * Destination (specs/site-architecture.md §9.1/§9.4, studio.md §9.3): a document inside a content
12
+ * collection keeps its media co-located in `content/<collection>/images/` — the entry references it
13
+ * file-relatively and the collection loader rewrites it to the asset mount. Everything else goes to
14
+ * {@link DEFAULT_MEDIA_DIR}, `public/media/`, which is served from the site root as `/media/`.
15
+ *
16
+ * {@link replaceAsset} is the one door that writes over an existing file, and it is asked for by
17
+ * name: the Media workspace's **Replace** (studio.md §9.3).
15
18
  *
16
19
  * @docs studio/projects/media
17
20
  */
@@ -19,37 +22,22 @@
19
22
  import { errorMessage } from "@jxsuite/schema/parse";
20
23
  import { getPlatform, hasPlatform } from "../platform";
21
24
  import { notify } from "../services/notify";
25
+ import { invalidateUsages } from "../services/references";
22
26
  import { activeTab } from "../workspace/workspace";
23
27
  import { forgetVersions, notedListing } from "./asset-versions";
28
+ import {
29
+ AUDIO_EXTENSIONS,
30
+ IMAGE_EXTENSIONS,
31
+ MEDIA_EXTENSIONS,
32
+ VIDEO_EXTENSIONS,
33
+ extensionOf,
34
+ } from "./media-types";
24
35
 
25
36
  // ─── File classification ─────────────────────────────────────────────────────
26
37
 
27
- /** Extensions rendered as a thumbnail (media picker, Manage cards, file-tree icons). */
28
- export const IMAGE_EXTENSIONS = new Set([
29
- ".jpg",
30
- ".jpeg",
31
- ".png",
32
- ".gif",
33
- ".svg",
34
- ".webp",
35
- ".avif",
36
- ".ico",
37
- ]);
38
-
39
- /** Every extension Studio treats as project media. Superset of {@link IMAGE_EXTENSIONS}. */
40
- export const MEDIA_EXTENSIONS = new Set([
41
- ...IMAGE_EXTENSIONS,
42
- ".mp4",
43
- ".webm",
44
- ".mp3",
45
- ".wav",
46
- ".ogg",
47
- ".pdf",
48
- ".woff",
49
- ".woff2",
50
- ".ttf",
51
- ".otf",
52
- ]);
38
+ /* The classification lives in `media-types.ts`, which imports nothing; it is re-exported here so
39
+ every existing caller of the upload core keeps one import. */
40
+ export { IMAGE_EXTENSIONS, MEDIA_EXTENSIONS, extensionOf, isImage } from "./media-types";
53
41
 
54
42
  /** A byte count as a person reads it, for a refusal that names the limit. */
55
43
  function formatBytes(bytes: number): string {
@@ -104,25 +92,11 @@ export interface UploadedAsset {
104
92
  kind: AssetKind;
105
93
  }
106
94
 
107
- /** Lowercased extension including the dot, or `""` when the name carries none. */
108
- export function extensionOf(name: string): string {
109
- const dot = name.lastIndexOf(".");
110
- return dot > 0 ? name.slice(dot).toLowerCase() : "";
111
- }
112
-
113
- /** Whether a file extension (with or without the leading dot) is a previewable image. */
114
- export function isImage(ext: string): boolean {
115
- return IMAGE_EXTENSIONS.has(ext.startsWith(".") ? ext.toLowerCase() : `.${ext.toLowerCase()}`);
116
- }
117
-
118
95
  /** Whether a filename is project media. */
119
96
  export function isMediaFile(name: string): boolean {
120
97
  return MEDIA_EXTENSIONS.has(extensionOf(name));
121
98
  }
122
99
 
123
- const VIDEO_EXTENSIONS = new Set([".mp4", ".webm", ".mov", ".m4v"]);
124
- const AUDIO_EXTENSIONS = new Set([".mp3", ".wav", ".ogg", ".m4a", ".flac"]);
125
-
126
100
  /**
127
101
  * Classify a file by MIME type, falling back to its extension. A drop from the OS usually carries a
128
102
  * `type`, but it is empty for unrecognized extensions and for some Linux file managers.
@@ -151,18 +125,59 @@ export function mediaKind(file: { name: string; type?: string }): AssetKind {
151
125
  return "file";
152
126
  }
153
127
 
128
+ /** The font extensions, which a browser shows as a specimen rather than as a file. */
129
+ export const FONT_EXTENSIONS: ReadonlySet<string> = new Set([".woff", ".woff2", ".ttf", ".otf"]);
130
+
131
+ /**
132
+ * What a media file IS, as the Media workspace's filters name it: one of five kinds.
133
+ *
134
+ * Distinct from {@link AssetKind}, which answers "what element does this become" and so has no word
135
+ * for a font or a PDF (both become an `<a href>`). Decided by extension alone, because a file
136
+ * already on disk carries no MIME type: a font first, then the three a browser has an element for,
137
+ * and everything else (a PDF) is a document.
138
+ */
139
+ export type MediaFileKind = "image" | "video" | "audio" | "font" | "document";
140
+
141
+ /** The {@link MediaFileKind} of a file, by its name. */
142
+ export function mediaFileKind(name: string): MediaFileKind {
143
+ if (FONT_EXTENSIONS.has(extensionOf(name))) {
144
+ return "font";
145
+ }
146
+ const kind = mediaKind({ name });
147
+ return kind === "file" ? "document" : kind;
148
+ }
149
+
154
150
  // ─── Destination resolution ──────────────────────────────────────────────────
155
151
 
156
152
  /** Matches a path inside a content collection: `content/<collection>/…`. */
157
153
  const CONTENT_ENTRY = /^content\/([^/]+)\//;
158
154
 
155
+ /**
156
+ * Where media with no context lands: `public/media/`, referenced from the site root as `/media/…`.
157
+ *
158
+ * A default and not a rule (site-architecture.md §9.4): a file under any other `public/` path is
159
+ * published and listed exactly the same. Its one definition, so the Media workspace, Add new ›
160
+ * Media and the Library's All view cannot disagree about it.
161
+ */
162
+ export const DEFAULT_MEDIA_DIR = "public/media";
163
+
159
164
  /**
160
165
  * The directory an upload lands in when the caller doesn't name one. A document inside a content
161
- * collection gets `content/<collection>/images`; everything else gets `public`.
166
+ * collection gets `content/<collection>/images`; everything else gets {@link DEFAULT_MEDIA_DIR}.
162
167
  */
163
168
  export function uploadDirFor(documentPath: string | null): string {
164
169
  const match = documentPath ? CONTENT_ENTRY.exec(documentPath.replaceAll("\\", "/")) : null;
165
- return match ? `content/${match[1]}/images` : "public";
170
+ return match ? `content/${match[1]}/images` : DEFAULT_MEDIA_DIR;
171
+ }
172
+
173
+ /**
174
+ * Whether `dir` is a folder {@link uploadDirFor} answers: `public/media`, or a collection's
175
+ * `content/<collection>/images`. An upload creates these on its own, so they are the folders a
176
+ * media delete takes away again once it has emptied them (`media/media-library.ts`).
177
+ */
178
+ export function isUploadDestination(dir: string): boolean {
179
+ const normalized = dir.replaceAll("\\", "/");
180
+ return normalized === DEFAULT_MEDIA_DIR || /^content\/[^/]+\/images$/.test(normalized);
166
181
  }
167
182
 
168
183
  /**
@@ -223,8 +238,61 @@ export function setMediaChangedHandler(fn: ((dir: string) => void | Promise<void
223
238
  mediaChangedHandler = fn;
224
239
  }
225
240
 
241
+ /**
242
+ * Say that the media in `dir` changed by some door other than {@link uploadAssets} — a replace, or
243
+ * the Media workspace's delete — so every cache keyed on the listing is dropped the same way.
244
+ */
245
+ export async function announceMediaChanged(dir: string): Promise<void> {
246
+ await mediaChangedHandler?.(dir);
247
+ }
248
+
249
+ /** The directory part of a project path, `.` for a file at the root. */
250
+ function parentDir(path: string): string {
251
+ const normalized = path.replaceAll("\\", "/");
252
+ const slash = normalized.lastIndexOf("/");
253
+ return slash > 0 ? normalized.slice(0, slash) : ".";
254
+ }
255
+
226
256
  // ─── Upload ──────────────────────────────────────────────────────────────────
227
257
 
258
+ /**
259
+ * What a media surface may upload: any file the media listing shows (`media`), or only pictures
260
+ * (`image`, a picker for a field that holds one).
261
+ *
262
+ * A file input's `accept` narrows only the chooser; a drop carries whatever the OS hands over, and
263
+ * `image/*` admits types (`.heic`, `.bmp`) no listing names. A surface that selects what it just
264
+ * uploaded must not upload a file it will then fail to show.
265
+ */
266
+ export type UploadOnly = "media" | "image";
267
+
268
+ /** The files `only` admits; the rest are named in one warning rather than written unseen. */
269
+ function keepListable(files: File[], only: UploadOnly): File[] {
270
+ const admits = (file: File): boolean =>
271
+ only === "image" ? IMAGE_EXTENSIONS.has(extensionOf(file.name)) : isMediaFile(file.name);
272
+ const kept: File[] = [];
273
+ const skipped: string[] = [];
274
+ for (const file of files) {
275
+ if (admits(file)) {
276
+ kept.push(file);
277
+ } else {
278
+ skipped.push(file.name);
279
+ }
280
+ }
281
+ if (skipped.length > 0) {
282
+ notify.warn(
283
+ skipped.length === 1 ? `Skipped ${skipped[0]}` : `Skipped ${skipped.length} files`,
284
+ {
285
+ detail:
286
+ only === "image"
287
+ ? `Only images can be used here: ${skipped.join(", ")}.`
288
+ : `Studio lists images, video, audio, PDFs and fonts as media, so ${skipped.join(", ")} would not appear in it.`,
289
+ source: "Media",
290
+ },
291
+ );
292
+ }
293
+ return kept;
294
+ }
295
+
228
296
  /**
229
297
  * Upload files into the project and report what landed.
230
298
  *
@@ -235,9 +303,9 @@ export function setMediaChangedHandler(fn: ((dir: string) => void | Promise<void
235
303
  */
236
304
  export async function uploadAssets(
237
305
  files: Iterable<File>,
238
- opts: { dir?: string } = {},
306
+ opts: { dir?: string; only?: UploadOnly } = {},
239
307
  ): Promise<UploadedAsset[]> {
240
- const list = [...files];
308
+ const list = opts.only === undefined ? [...files] : keepListable([...files], opts.only);
241
309
  if (list.length === 0) {
242
310
  return [];
243
311
  }
@@ -297,3 +365,92 @@ export async function uploadAssets(
297
365
  }
298
366
  return uploaded;
299
367
  }
368
+
369
+ /** Extensions that name one format, so either may stand in for the other. */
370
+ const SAME_FORMAT: readonly ReadonlySet<string>[] = [
371
+ new Set([".jpg", ".jpeg"]),
372
+ new Set([".tif", ".tiff"]),
373
+ ];
374
+
375
+ /** Every extension that names the same format as `ext` (itself included). */
376
+ function formatSiblings(ext: string): ReadonlySet<string> {
377
+ return SAME_FORMAT.find((group) => group.has(ext)) ?? new Set([ext]);
378
+ }
379
+
380
+ /**
381
+ * The `accept` for a Replace of the file at `path`: its own format, and only that.
382
+ *
383
+ * The path is what every document names, and the server answers it with the type its EXTENSION
384
+ * implies, so bytes of another format under it are a broken picture on every page that uses it.
385
+ */
386
+ export function replaceAccept(path: string): string {
387
+ return [...formatSiblings(extensionOf(path))].join(",");
388
+ }
389
+
390
+ /** Whether `file` may replace the file at `path`: the same format, by extension. */
391
+ export function canReplaceWith(path: string, file: { name: string }): boolean {
392
+ return formatSiblings(extensionOf(path)).has(extensionOf(file.name));
393
+ }
394
+
395
+ /**
396
+ * Write `file`'s bytes over the existing media file at `path` — the Media workspace's **Replace**.
397
+ *
398
+ * The only overwrite in this module, and asked for by name (studio.md §9.3): every reference to
399
+ * `path` keeps resolving and now shows the new bytes. Nothing about the path tells a cache that the
400
+ * file changed, so this forgets the path's content version (§3.4) and announces the change, which
401
+ * drops the media listing, its metadata and the usage answers derived from it.
402
+ *
403
+ * A backend that reports a DIFFERENT path did not replace anything: it wrote a second file beside
404
+ * the first, and the references still name the old bytes. That is said rather than called success.
405
+ *
406
+ * @returns Whether the bytes now at `path` are the new ones.
407
+ */
408
+ export async function replaceAsset(path: string, file: File): Promise<boolean> {
409
+ const name = path.split("/").pop() ?? path;
410
+ /* An `accept` narrows only the chooser (and a backend's own picker may ignore it), so the format is
411
+ checked here too: same path, other format, is every reference broken at once. */
412
+ if (!canReplaceWith(path, file)) {
413
+ notify.error(`${file.name} cannot replace ${name}.`, {
414
+ detail: `${name} is ${extensionOf(path) || "a file with no extension"} and ${file.name} is ${extensionOf(file.name) || "a file with no extension"}. Every page that uses ${name} would get bytes its address does not describe; choose a file of the same type, or upload it as a new file.`,
415
+ path,
416
+ source: "Media",
417
+ });
418
+ return false;
419
+ }
420
+ const maxBytes = hasPlatform() ? getPlatform().assetCapabilities?.maxUploadBytes : undefined;
421
+ if (maxBytes !== undefined && file.size > maxBytes) {
422
+ notify.error(`${file.name} is too large.`, {
423
+ detail: `This backend accepts at most ${formatBytes(maxBytes)}; that file is ${formatBytes(file.size)}.`,
424
+ source: "Media",
425
+ });
426
+ return false;
427
+ }
428
+ let written: string;
429
+ try {
430
+ ({ path: written } = await getPlatform().uploadFile(path, file));
431
+ } catch (error) {
432
+ notify.error(`Could not replace ${name}.`, {
433
+ detail: errorMessage(error),
434
+ path,
435
+ source: "Media",
436
+ });
437
+ return false;
438
+ }
439
+ forgetVersions([path, written]);
440
+ // The answers were about the old bytes' place in the project; the place is the same, but a stale
441
+ // "Used on" is a list the reader acts on, so it is asked again rather than trusted.
442
+ invalidateUsages();
443
+ await announceMediaChanged(parentDir(path));
444
+ if (written !== path) {
445
+ notify.warn(`${name} was not replaced.`, {
446
+ detail: `The backend stored the new file as ${written}, so every reference still shows the old one.`,
447
+ path,
448
+ source: "Media",
449
+ // A Problem rather than a toast: two files now exist, and the reader has a decision to make.
450
+ tier: "problem",
451
+ });
452
+ return false;
453
+ }
454
+ notify.success(`Replaced ${name}`);
455
+ return true;
456
+ }