@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
@@ -29,9 +29,16 @@
29
29
 
30
30
  import { effect, effectScope } from "../reactivity";
31
31
  import { paneRegion } from "../ui/regions";
32
+ import { localDay } from "../utils/local-day";
32
33
  import { mediaSiteUrl, previewFileSrc } from "../files/media-paths";
33
- import { formatBytes, loadMediaMeta, peekMediaMeta, recordImageSize } from "../files/media-meta";
34
- import { extensionOf, mediaKind } from "../files/media-upload";
34
+ import {
35
+ formatBytes,
36
+ loadMediaMeta,
37
+ mediaMetaSettled,
38
+ peekMediaMeta,
39
+ recordImageSize,
40
+ } from "../files/media-meta";
41
+ import { FONT_EXTENSIONS, extensionOf, mediaKind } from "../files/media-upload";
35
42
  import { loadMediaUsages, mediaUsageHeadline, peekMediaUsages } from "../files/media-usage";
36
43
  import { mountMediaSurface } from "../surfaces/media-pane";
37
44
  import type { MediaStage, MediaSurfaceHandle, MediaView } from "../surfaces/media-pane";
@@ -43,9 +50,6 @@ import type { CanvasSurface } from "../canvas/canvas-surface";
43
50
  /** The `canvasMode` the media viewer draws under. */
44
51
  export const MEDIA_MODE = "media";
45
52
 
46
- /** Extensions the viewer renders as a font specimen rather than as a file it cannot show. */
47
- const FONT_EXTENSIONS = new Set([".woff", ".woff2", ".ttf", ".otf"]);
48
-
49
53
  /** The pangram the font specimen is set in — short, and every letter of the alphabet once. */
50
54
  const SPECIMEN = "The quick brown fox jumps over the lazy dog";
51
55
 
@@ -97,9 +101,11 @@ export function detachMediaPane(paneId: string): void {
97
101
  * Which element the stage should draw this file in.
98
102
  *
99
103
  * A font is decided by extension before anything else: `mediaKind` calls a `.woff2` a plain file,
100
- * and a file the browser cannot render is the last answer here rather than the first.
104
+ * and a file the browser cannot render is the last answer here rather than the first. Exported
105
+ * because the Media workspace's detail pane (`media/media-library.ts`) asks the same question, and
106
+ * two answers to it would drift.
101
107
  */
102
- function stageFor(path: string): MediaStage {
108
+ export function mediaStageFor(path: string): MediaStage {
103
109
  const ext = extensionOf(path);
104
110
  if (FONT_EXTENSIONS.has(ext)) {
105
111
  return "font";
@@ -112,7 +118,7 @@ function stageFor(path: string): MediaStage {
112
118
  }
113
119
 
114
120
  /** Kind, dimensions, size and modified time — each omitted rather than zeroed when unknown. */
115
- function factsLine(path: string, meta: MediaMeta | null): string {
121
+ export function factsLine(path: string, meta: MediaMeta | null): string {
116
122
  const facts: string[] = [mediaKind({ name: path })];
117
123
  if (meta?.width != null && meta.height != null) {
118
124
  facts.push(`${meta.width} × ${meta.height}`);
@@ -121,7 +127,8 @@ function factsLine(path: string, meta: MediaMeta | null): string {
121
127
  facts.push(formatBytes(meta.bytes));
122
128
  }
123
129
  if (meta?.modified) {
124
- facts.push(`modified ${meta.modified.slice(0, 10)}`);
130
+ // The local day it was modified on (`utils/local-day.ts`), not the UTC one.
131
+ facts.push(`modified ${localDay(new Date(meta.modified))}`);
125
132
  }
126
133
  return facts.join(" · ");
127
134
  }
@@ -131,8 +138,30 @@ function specimenFamily(path: string): string {
131
138
  return `jx-specimen-${path.replaceAll(/[^a-zA-Z0-9]/g, "-")}`;
132
139
  }
133
140
 
141
+ /** What a font specimen draws: its scoped face, the custom property naming it, and the pangram. */
142
+ export interface FontSpecimen {
143
+ /** A `@font-face` rule for the generated family, loading the file from its preview src. */
144
+ fontFace: string;
145
+ /** `--jx-specimen-family: "<family>"`, the one inline declaration a specimen element carries. */
146
+ specimenFamily: string;
147
+ specimen: string;
148
+ }
149
+
150
+ /**
151
+ * The specimen for a font file, shared by the viewer and the Media workspace's detail pane, so a
152
+ * font is shown by being USED in both and the face is scoped the same way in both.
153
+ */
154
+ export function specimenOf(path: string): FontSpecimen {
155
+ const family = specimenFamily(path);
156
+ return {
157
+ fontFace: `@font-face { font-family: "${family}"; src: url("${previewFileSrc(path)}"); }`,
158
+ specimen: SPECIMEN,
159
+ specimenFamily: `--jx-specimen-family: "${family}"`,
160
+ };
161
+ }
162
+
134
163
  /** Which documents reference this file, or the honest reason there is no list. */
135
- function usageOf(
164
+ export function usageOf(
136
165
  usage: UsageState | null,
137
166
  ): Pick<MediaView, "usageState" | "usageHeadline" | "usageFiles"> {
138
167
  const headline = mediaUsageHeadline(usage);
@@ -179,11 +208,10 @@ export function renderMediaMode(surface: CanvasSurface, tab: Tab): void {
179
208
  detachMediaPane(paneId);
180
209
 
181
210
  const path = tab.documentPath ?? "";
182
- const family = specimenFamily(path);
183
211
  const src = previewFileSrc(path);
184
212
  const ref = mediaSiteUrl(path);
185
213
  const ext = extensionOf(path);
186
- const kind = stageFor(path);
214
+ const kind = mediaStageFor(path);
187
215
 
188
216
  let panel: ActiveMediaPane | null = null;
189
217
  const redraw = () => {
@@ -197,25 +225,25 @@ export function renderMediaMode(surface: CanvasSurface, tab: Tab): void {
197
225
  const usage = peekMediaUsages(path);
198
226
  /* Asked on the first paint that finds it missing, never on every paint: both loaders cache and
199
227
  de-duplicate in flight, and both repaint through `redraw` when they land. */
200
- if (meta === null) {
228
+ if (!mediaMetaSettled(path)) {
201
229
  void loadMediaMeta(path).then(redraw);
202
230
  }
203
231
  if (usage === null || usage.status === "pending") {
204
232
  void loadMediaUsages(path).then(redraw);
205
233
  }
234
+ /* The face is scoped to the generated family name, so two specimens open side by side cannot
235
+ claim the same one. Written only for a font: every other stage carries an empty sheet. */
236
+ const specimen = specimenOf(path);
206
237
  return {
207
238
  extLabel: ext || "this file",
208
239
  facts: factsLine(path, meta),
209
- /* The face is scoped to the generated family name, so two specimens open side by side cannot
210
- claim the same one. Written only for a font: every other stage carries an empty sheet. */
211
- fontFace:
212
- kind === "font" ? `@font-face { font-family: "${family}"; src: url("${src}"); }` : "",
240
+ fontFace: kind === "font" ? specimen.fontFace : "",
213
241
  kind,
214
242
  name: path.split("/").pop() ?? "",
215
243
  ref,
216
244
  region: paneRegion(paneId, "media"),
217
- specimen: SPECIMEN,
218
- specimenFamily: `--jx-specimen-family: "${family}"`,
245
+ specimen: specimen.specimen,
246
+ specimenFamily: specimen.specimenFamily,
219
247
  src,
220
248
  ...usageOf(usage),
221
249
  };
@@ -346,7 +346,7 @@ export async function autoSyncProjectOnOpen(): Promise<void> {
346
346
  const platform = getPlatform();
347
347
  try {
348
348
  /* The status is host wire data. A host may leave `remotes` out (as `deploy-checklist.ts` and
349
- `git-panel.ts` also allow), which reads as "nothing to sync"; a host that sends no status at
349
+ `publish-changes.ts` also allow), which reads as "nothing to sync"; a host that sends no status at
350
350
  all throws on the first read and is caught below the same way, so a malformed status never
351
351
  aborts the project open that awaits this. */
352
352
  const status = await platform.gitStatus();
@@ -43,6 +43,7 @@ import { reactive } from "../reactivity";
43
43
  import { now } from "../services/clock";
44
44
  import { notify } from "../services/notify";
45
45
  import type { NotifyOptions } from "../services/notify";
46
+ import type { CommandArgValues } from "../commands/command-args";
46
47
  import { registerPanel } from "./panel-registry";
47
48
  import type { PanelBody } from "./panel-registry";
48
49
  import type {
@@ -180,7 +181,10 @@ export interface ActivityHandle {
180
181
  * The log goes into the notification's `detail`, so the persistent, inspectable error view §16.4
181
182
  * asks for is the Problems row, not a modal that has to stay open to be read.
182
183
  */
183
- fail: (message: string, opts?: { action?: string; path?: string }) => void;
184
+ fail: (
185
+ message: string,
186
+ opts?: { action?: string; actionArgs?: CommandArgValues; path?: string },
187
+ ) => void;
184
188
  }
185
189
 
186
190
  /**
@@ -237,7 +241,10 @@ export function beginActivity(options: ActivityOptions): ActivityHandle {
237
241
  finish("done", status);
238
242
  },
239
243
  entry: live,
240
- fail(message: string, opts: { action?: string; path?: string } = {}) {
244
+ fail(
245
+ message: string,
246
+ opts: { action?: string; actionArgs?: CommandArgValues; path?: string } = {},
247
+ ) {
241
248
  // An operation that has already ended does not get to raise a Problem: the first outcome is
242
249
  // The one that happened, and a late `fail()` after a successful `done()` would put a failure
243
250
  // On a list whose whole promise is that everything on it still needs fixing.
@@ -253,6 +260,9 @@ export function beginActivity(options: ActivityOptions): ActivityHandle {
253
260
  if (opts.action !== undefined) {
254
261
  notice.action = opts.action;
255
262
  }
263
+ if (opts.actionArgs !== undefined) {
264
+ notice.actionArgs = opts.actionArgs;
265
+ }
256
266
  if (opts.path !== undefined) {
257
267
  notice.path = opts.path;
258
268
  }
@@ -381,8 +391,14 @@ function stepView(step: ActivityStep, index: number): ActivityStepView {
381
391
  };
382
392
  }
383
393
 
384
- /** One operation, as the document reads it. */
385
- function rowView(entry: ActivityEntry): ActivityRowView {
394
+ /**
395
+ * One operation, as a document reads it.
396
+ *
397
+ * Exported because the Activity feed is not its only reader: Publish › Deploys draws a running
398
+ * deploy's steps from the same record (studio.md §22.8), and a second projection of one activity
399
+ * would be a second set of glyphs for the same states.
400
+ */
401
+ export function activityRowView(entry: ActivityEntry): ActivityRowView {
386
402
  const lines = entry.log.length;
387
403
  return {
388
404
  cancellable: entry.cancellable,
@@ -423,7 +439,7 @@ export function activityView(): ActivityView {
423
439
  clearLabel: `Clear ${finished} finished`,
424
440
  hasFinished: finished > 0,
425
441
  hasRows: activities.length > 0,
426
- rows: activities.map((entry) => rowView(entry)),
442
+ rows: activities.map((entry) => activityRowView(entry)),
427
443
  };
428
444
  }
429
445
 
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * Chat-markdown.ts — memoized markdown rendering for assistant chat messages.
3
3
  *
4
- * Wraps @jxsuite/markup/md-html (sanitized markdown → HTML) with a per-message cache
5
- * keyed by message id + content length, so re-renders during streaming only re-parse
6
- * the message that actually grew.
4
+ * Wraps @jxsuite/markup/md-html (sanitized markdown → HTML) with a per-message cache keyed by
5
+ * message id + content length, so re-renders during streaming only re-parse the message that
6
+ * actually grew.
7
7
  *
8
- * The HTML ends up in `[part="md"]`, the assistant surface's island — this app's ONE injection
9
- * sink — and it goes through the Trusted Types policy on the way THERE, in
10
- * `surfaces/ai-chat.ts`. md-html already sanitizes — raw HTML dropped, javascript: URLs
11
- * stripped — and the policy asserts that it did, rather than taking a comment's word for it. A
12
- * `createHTML` that passed its input through unchanged would satisfy the API and defend nothing.
8
+ * The HTML ends up in `[part="md"]`, the assistant surface's island — this app's ONE injection sink
9
+ * — and it goes through the Trusted Types policy on the way THERE, in `surfaces/ai-chat.ts`.
10
+ * md-html already sanitizes — raw HTML dropped, javascript: URLs stripped — and the policy asserts
11
+ * that it did, rather than taking a comment's word for it. A `createHTML` that passed its input
12
+ * through unchanged would satisfy the API and defend nothing.
13
13
  *
14
14
  * **It returns markup, not a template.** It used to hand back a `TemplateResult` wrapping
15
15
  * `unsafeHTML` (which stringified the trusted value straight back again); the assistant is a Jx
@@ -230,11 +230,11 @@ function barPosition(anchor: {
230
230
  "the stage showing the active tab", which was the same answer only while a tab could be on
231
231
  screen in one place; with a document displayed in two panes it picked whichever came first in
232
232
  the grid, and clipped the bar against a stage the author is not typing in.
233
- …and against the box that SCROLLS, where the stage has one. Edit docks the Document Header
234
- card above its scroller, inside the same stage cell, so a node scrolled up under the band is
235
- still inside the stage while nobody can see it — and the bar would float over the card,
236
- pointing at a node the card is covering. Everywhere else there is no scroller and the stage is
237
- the viewport, as it always was. */
233
+ …and against the box that SCROLLS, where the stage has one: Edit's page scroller. Over a
234
+ collection entry the writing banner stands above it in the same stage cell, so a node
235
+ scrolled up under the banner is still inside the stage while nobody can see it — and the bar
236
+ would float over the banner, pointing at a node it covers. Everywhere else there is no
237
+ scroller and the stage is the viewport, as it always was. */
238
238
  const surface = activeCanvasSurface();
239
239
  const viewport = panelOfSurface(surface)?.scrollContainer ?? stageOf(surface);
240
240
  const box = viewport ? rectOf(viewport) : null;
@@ -244,9 +244,9 @@ function barPosition(anchor: {
244
244
  /* ABOVE the anchor where the bar fits above it INSIDE that viewport, and BELOW it where it does
245
245
  not. The flip used to be decided against a flat 80 in WINDOW coordinates, the app's own top
246
246
  chrome, which is the same answer only while the viewport starts near the top of the window.
247
- Edit's scroller starts under a band, so an anchor whose top had scrolled under the card while
248
- its bottom was still on screen put the bar ON the card — over the fields it covers, taking
249
- their clicks. Flipped is better than hidden: the element is visible, so its bar should be, and
247
+ Edit's scroller can start under the writing banner, so an anchor whose top had scrolled under
248
+ it while its bottom was still on screen put the bar ON the banner, taking its clicks. Flipped
249
+ is better than hidden: the element is visible, so its bar should be, and
250
250
  past the clip above the anchor's bottom edge is inside the viewport, so the flipped bar is too.
251
251
  The fallback is that same 80px of chrome, for a stage no pass has measured yet. */
252
252
  const ceiling = box ? box.top : 80 - BAR_CLEARANCE;
@@ -257,7 +257,7 @@ function barPosition(anchor: {
257
257
  /* Below it, and kept INSIDE the viewport: an element taller than what is left of the scroller has
258
258
  its bottom edge off screen, and the bar would follow it there — over the status bar, or over
259
259
  whatever the pane below is showing. Clamped, it stands on that element's last visible strip,
260
- still pointing at it and still off the card. */
260
+ still pointing at it and still inside the scroller. */
261
261
  const below = anchor.top + anchor.height + 4;
262
262
  return {
263
263
  left: anchor.left,
@@ -1273,8 +1273,8 @@ function hideBar(): void {
1273
1273
  * The document + selection the bar is currently suppressed FOR, or null while it is free to draw.
1274
1274
  *
1275
1275
  * **Why a key and not a boolean.** The bar is `position: fixed` and clamped into the window, so it
1276
- * can sit over the Document Header card, the pane context bar and the docks; an author working in
1277
- * the Inspector's Logic tab reported it "blocking a part of the interface that the user needs to
1276
+ * can sit over the pane context bar, the Inspector and the docks; an author working in the
1277
+ * Inspector's Logic tab reported it "blocking a part of the interface that the user needs to
1278
1278
  * utilize". Hiding it on a chrome pointerdown is half an answer — {@link dismissBlockActionBar}
1279
1279
  * alone would flash back on the very next repaint, because the bar is re-rendered from the
1280
1280
  * `toolbarRefresh` seam on every selection snapshot and from `renderOnly("overlays")` on zoom/pan.
@@ -25,7 +25,7 @@
25
25
  */
26
26
 
27
27
  import { monitorForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
28
- import { displayTagName } from "@jxsuite/schema/guards";
28
+ import { displayTagName, isString } from "@jxsuite/schema/guards";
29
29
  import {
30
30
  beginDragSession,
31
31
  clearDropIndicator,
@@ -101,7 +101,10 @@ function cursorOf(location: MonitorDragArgs["location"]): ViewportPoint {
101
101
  */
102
102
  function dragSrcOf(data: ElementDragPayload["data"]): DragSrcKind | null {
103
103
  if (data.type === "block") {
104
- return { type: "block" };
104
+ /* SAFETY: every minter of a "block" payload sets `fragment` to the JxMutableNode it inserts;
105
+ only a string tag is read off it, and anything else crosses as no tag (placed as a block). */
106
+ const tag = (data.fragment as JxMutableNode | undefined)?.tagName;
107
+ return isString(tag) ? { tag, type: "block" } : { type: "block" };
105
108
  }
106
109
  if (data.type === "tree-node" && Array.isArray(data.path)) {
107
110
  /* SAFETY: both minters of a "tree-node" payload, an Outline row's `getInitialData` and the block
@@ -19,12 +19,12 @@
19
19
  *
20
20
  * **The assistant's SURFACE document is what this module defers.** Mounting `surfaces/ai-chat.ts`
21
21
  * costs a traced 100–290 ms of connectedCallback at boot (its island projections; see
22
- * `scripts/perf/REPORT.md`), paid on every window open although the document is invisible until
23
- * the tab is picked. Binding is the same seam (`bindAiPanelHost`) and it is now deferred to the
24
- * first time the Assistant tab is actually shown or revealed: `renderAiPanel` no-ops while the
25
- * surface is null, and the surface's own watcher projects whatever the machinery wrote before that
26
- * point, so a prompt seeded into a boot the reader never spends in the tab is not lost — it is one
27
- * more message waiting when the body first appears.
22
+ * `scripts/perf/REPORT.md`), paid on every window open although the document is invisible until the
23
+ * tab is picked. Binding is the same seam (`bindAiPanelHost`) and it is now deferred to the first
24
+ * time the Assistant tab is actually shown or revealed: `renderAiPanel` no-ops while the surface is
25
+ * null, and the surface's own watcher projects whatever the machinery wrote before that point, so a
26
+ * prompt seeded into a boot the reader never spends in the tab is not lost — it is one more message
27
+ * waiting when the body first appears.
28
28
  *
29
29
  * **This module is the SEAM, and it has no markup of its own.** The assistant is a Jx document
30
30
  * (`surfaces/ai-chat.json`), and a document CLEARS the host it is given — so it needs a container
@@ -0,0 +1,306 @@
1
+ /// <reference lib="dom" />
2
+ /**
3
+ * The Collections panel — the Content workspace's Navigator panel (`specs/studio.md` §22.5).
4
+ *
5
+ * Two lists, chosen by what the focused pane shows:
6
+ *
7
+ * - **The collections.** The built-in Pages and every collection `project.json` declares, in
8
+ * declaration order, each with its entry count (from the Content home's scan,
9
+ * `browse/library-pane.ts`) and its saved grid views beneath it (`grid/grid-layout.ts`), and **+
10
+ * New collection** at the foot. Choosing one shows it in the Content home
11
+ * (`content.showCollection`); choosing a saved view opens the collection's grid on it.
12
+ * - **One collection's entries**, while an entry of it is the focused document: thumbnail, title and
13
+ * status (`content/entry-summaries.ts`), under **‹ <Collection>** to go back, with **+ New
14
+ * <item>** at the foot. Choosing one opens it where it is written (`content/entry-open.ts`).
15
+ *
16
+ * The markup is `surfaces/collections-panel.json`; this module is the flow. It is registered with
17
+ * no rail button: Content has no rail, and the workspace's frame opens the Navigator on it
18
+ * (`shell.ts`'s `WORKSPACE_PRESETS`).
19
+ *
20
+ * @docs studio/projects/browse
21
+ */
22
+
23
+ import { nothing } from "lit-html";
24
+ import { projectState, renderOnly } from "../store";
25
+ import { activeTab } from "../workspace/workspace";
26
+ import { runActiveReported } from "../commands/run-reported";
27
+ import {
28
+ PAGES_COLLECTION,
29
+ collectionKeyOfPath,
30
+ contentCollection,
31
+ contentCollections,
32
+ } from "../content/content-collections";
33
+ import {
34
+ collectionCounts,
35
+ collectionEntryPaths,
36
+ collectionView,
37
+ shownCollectionKey,
38
+ } from "../browse/library-pane";
39
+ import { localeLabel, localeOfPath, resolveI18n } from "@jxsuite/schema/locale";
40
+ import { entryLocale } from "../content/collection-pages";
41
+ import { onEntrySummariesRead, peekEntrySummaries } from "../content/entry-summaries";
42
+ import { openEntry } from "../content/entry-open";
43
+ import { workspaceHomeTabId } from "../workspace/workspace-ids";
44
+ import { makeGridTabId, parseGridTabId } from "../grid/grid-source";
45
+ import { applySavedView, listSavedViews } from "../grid/grid-layout";
46
+ import {
47
+ emptyCollectionsPanelValues,
48
+ mountCollectionsPanelSurface,
49
+ } from "../surfaces/collections-panel";
50
+ import { registerPanel } from "./panel-registry";
51
+ import type {
52
+ CollectionEntryRow,
53
+ CollectionRow,
54
+ CollectionsPanelActions,
55
+ CollectionsPanelSurfaceHandle,
56
+ CollectionsPanelValues,
57
+ } from "../surfaces/collections-panel";
58
+ import type { Tab } from "../tabs/tab";
59
+
60
+ /** The `source` every command run from here reports under. */
61
+ const SOURCE = "Collections";
62
+
63
+ /** The grid tab id a collection's saved views are stored under. */
64
+ export function collectionGridId(key: string): string {
65
+ return key === PAGES_COLLECTION
66
+ ? makeGridTabId({ kind: "pages" })
67
+ : makeGridTabId({ kind: "collection", name: key });
68
+ }
69
+
70
+ /**
71
+ * The declared collection an entry tab is writing, or `null`: the focused document is a file a
72
+ * declared collection owns. Pages are Design's, so a page tab lists the collections. A file-backed
73
+ * collection's file is no entry either: its grid lists its records, so the panel lists the
74
+ * collections with that one selected.
75
+ */
76
+ export function writingCollection(tab: Tab | null): string | null {
77
+ const key = collectionKeyOfPath(tab?.documentPath);
78
+ return key !== null && key !== PAGES_COLLECTION && !contentCollection(key)?.fileBacked
79
+ ? key
80
+ : null;
81
+ }
82
+
83
+ /** The file-backed collection whose file a tab holds, or `null`. */
84
+ function fileCollectionOf(tab: Tab): string | null {
85
+ const key = collectionKeyOfPath(tab.documentPath);
86
+ return key !== null && contentCollection(key)?.fileBacked === true ? key : null;
87
+ }
88
+
89
+ /** The collection the focused pane is showing, for the list's selected row, or `null`. */
90
+ function shownKey(tab: Tab | null): string | null {
91
+ if (!tab) {
92
+ return null;
93
+ }
94
+ if (tab.id === workspaceHomeTabId("content")) {
95
+ return shownCollectionKey();
96
+ }
97
+ const grid = parseGridTabId(tab.id);
98
+ if (grid?.kind === "pages") {
99
+ return PAGES_COLLECTION;
100
+ }
101
+ return grid?.kind === "collection" ? grid.name : fileCollectionOf(tab);
102
+ }
103
+
104
+ /** The collection list. */
105
+ function collectionRows(tab: Tab | null): CollectionRow[] {
106
+ const counts = collectionCounts();
107
+ const selected = shownKey(tab);
108
+ return contentCollections().map((collection) => {
109
+ const count = counts?.get(collection.key);
110
+ return {
111
+ count: counts === null ? "" : String(count ?? 0),
112
+ key: collection.key,
113
+ selected: collection.key === selected ? "true" : "false",
114
+ title: collection.title,
115
+ views: listSavedViews(collectionGridId(collection.key)).map((view) => ({
116
+ key: `${collection.key}\n${view.name}`,
117
+ name: view.name,
118
+ })),
119
+ };
120
+ });
121
+ }
122
+
123
+ /** Everything the panel draws right now. */
124
+ export function collectionsPanelValues(): CollectionsPanelValues {
125
+ const values = emptyCollectionsPanelValues();
126
+ if (!projectState) {
127
+ return values;
128
+ }
129
+ const tab = activeTab.value;
130
+ const writing = writingCollection(tab);
131
+ if (writing === null) {
132
+ return { ...values, body: "list", collections: collectionRows(tab) };
133
+ }
134
+ const collection = contentCollection(writing);
135
+ const counts = collectionCounts();
136
+ const paths = collectionEntryPaths(writing);
137
+ const summaries = counts === null ? null : peekEntrySummaries(writing, paths);
138
+ /* A multilingual collection's entries say which language each is in, and the list keeps to the
139
+ language the collection's view is filtered to: three translations of one exhibition are three
140
+ rows, and an unlabelled list of them reads as one entry listed thrice. */
141
+ const filter = collectionView(writing).locale;
142
+ const entries: CollectionEntryRow[] = (summaries ?? []).flatMap((entry) => {
143
+ const locale = entryLocale(entry.path) ?? "";
144
+ if (filter !== "" && locale !== filter) {
145
+ return [];
146
+ }
147
+ return [
148
+ {
149
+ art: entry.thumb === "" ? "glyph" : "image",
150
+ lang: locale,
151
+ locale: locale === "" ? "" : localeLabel(locale),
152
+ path: entry.path,
153
+ selected: entry.path === tab?.documentPath ? "true" : "false",
154
+ status: entry.status,
155
+ thumb: entry.thumb,
156
+ title: entry.title,
157
+ },
158
+ ];
159
+ });
160
+ return {
161
+ ...values,
162
+ backTitle: collection?.title ?? writing,
163
+ backCount: counts === null ? "" : String(paths.length),
164
+ body: "entries",
165
+ entries,
166
+ entriesState: summaries === null ? "loading" : entries.length === 0 ? "empty" : "listed",
167
+ newLabel: `New ${collection?.item ?? "entry"}`,
168
+ };
169
+ }
170
+
171
+ // ─── Actions ─────────────────────────────────────────────────────────────────
172
+
173
+ /**
174
+ * Open a collection's grid on a saved view.
175
+ *
176
+ * The view is written onto the grid's working layout first, so a grid opened now loads it. A grid
177
+ * already on screen has loaded its layout, so it is told through `grid.applyView`, the command the
178
+ * grid's own picker runs.
179
+ */
180
+ async function applyCollectionView(key: string, name: string): Promise<void> {
181
+ const gridId = collectionGridId(key);
182
+ const showing = activeTab.value?.id === gridId;
183
+ applySavedView(gridId, name);
184
+ if (showing) {
185
+ await runActiveReported("grid.applyView", { name }, SOURCE);
186
+ return;
187
+ }
188
+ const { openCollectionGrid, openPagesGrid } = await import("../grid/grid-open");
189
+ if (key === PAGES_COLLECTION) {
190
+ openPagesGrid();
191
+ } else {
192
+ openCollectionGrid(key);
193
+ }
194
+ }
195
+
196
+ const ACTIONS: CollectionsPanelActions = {
197
+ applyView: (key, name) => {
198
+ const [collection = ""] = key.split("\n");
199
+ void applyCollectionView(collection, name);
200
+ },
201
+ back: () => {
202
+ const writing = writingCollection(activeTab.value);
203
+ if (writing !== null) {
204
+ void runActiveReported("content.showCollection", { collection: writing }, SOURCE);
205
+ }
206
+ },
207
+ newCollection: () => {
208
+ void runActiveReported("file.new", { kind: "collection" }, SOURCE);
209
+ },
210
+ newEntry: () => {
211
+ const writing = writingCollection(activeTab.value);
212
+ void runActiveReported(
213
+ "file.new",
214
+ writing === null
215
+ ? { kind: "entry" }
216
+ : { collection: writing, kind: "entry", ...newEntryLocale(writing) },
217
+ SOURCE,
218
+ );
219
+ },
220
+ openEntry: (path) => {
221
+ void openEntry(path);
222
+ },
223
+ showCollection: (key) => {
224
+ void runActiveReported("content.showCollection", { collection: key }, SOURCE);
225
+ },
226
+ };
227
+
228
+ /**
229
+ * The language a new entry starts in from the entries list: the one the entry being written is in,
230
+ * else the one the collection's view is filtered to. Nothing when neither says, which leaves the
231
+ * dialog on the site's default.
232
+ */
233
+ export function newEntryLocale(collection: string): { locale?: string } {
234
+ const { i18n } = resolveI18n(projectState?.projectConfig ?? {});
235
+ const path = activeTab.value?.documentPath ?? null;
236
+ const own = path === null ? null : localeOfPath(path, i18n);
237
+ const locale = own ?? collectionView(collection).locale;
238
+ return locale === "" ? {} : { locale };
239
+ }
240
+
241
+ // ─── Mounting ────────────────────────────────────────────────────────────────
242
+
243
+ interface StandingPanel {
244
+ host: HTMLElement;
245
+ handle: CollectionsPanelSurfaceHandle;
246
+ }
247
+
248
+ let _standing: StandingPanel | null = null;
249
+ let _unlisten: (() => void) | null = null;
250
+
251
+ /** Repaint the Navigator, which repaints this panel when it is the one showing. */
252
+ function repaint(): void {
253
+ renderOnly("leftPanel");
254
+ }
255
+
256
+ /** Mount the surface into `host`, or bring the one already there up to date. */
257
+ function mountInto(host: HTMLElement): void {
258
+ _unlisten ??= onEntrySummariesRead(repaint);
259
+ if (_standing && (_standing.host !== host || !_standing.handle.connected())) {
260
+ _standing.handle.dispose();
261
+ _standing = null;
262
+ }
263
+ if (_standing) {
264
+ _standing.handle.update(collectionsPanelValues());
265
+ return;
266
+ }
267
+ _standing = {
268
+ handle: mountCollectionsPanelSurface(host, collectionsPanelValues(), ACTIONS),
269
+ host,
270
+ };
271
+ }
272
+
273
+ /**
274
+ * The Navigator's content area inside a panel body, where a mounted document goes (the reason is
275
+ * `panels/elements-panel.ts`'s: the body is lit's part, and its child is the box a panel owns).
276
+ */
277
+ function contentArea(body: HTMLElement): HTMLElement {
278
+ return body.querySelector<HTMLElement>(".panel-content") ?? body;
279
+ }
280
+
281
+ /**
282
+ * Contribute the Collections panel: project level (it lists the project's collections), no rail
283
+ * button (Content has no rail), the Content workspace's Navigator.
284
+ */
285
+ export function registerCollectionsPanel(): void {
286
+ registerPanel({
287
+ id: "collections",
288
+ title: "Collections",
289
+ level: "project",
290
+ dock: "navigator",
291
+ icon: "stack",
292
+ rail: false,
293
+ render: () => nothing,
294
+ afterRender: (_ctx, host) => {
295
+ mountInto(contentArea(host));
296
+ },
297
+ });
298
+ }
299
+
300
+ /** Forget the mounted panel — tests, and a project switch. */
301
+ export function resetCollectionsPanel(): void {
302
+ _standing?.handle.dispose();
303
+ _standing = null;
304
+ _unlisten?.();
305
+ _unlisten = null;
306
+ }