@jxsuite/studio 6.1.2 → 8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (522) hide show
  1. package/README.md +5 -5
  2. package/dist/chunks/2020-5wtzce5g.js +4 -0
  3. package/dist/chunks/adapter-5tphg7xe.js +4 -0
  4. package/dist/chunks/add-new-flow-psym14sn.js +4 -0
  5. package/dist/chunks/cf-account-picker-haqazq5r.js +4 -0
  6. package/dist/chunks/cf-settings-h2wrh6hq.js +4 -0
  7. package/dist/chunks/{color-area-0fj8a4ga.js → color-area-6rbra5dy.js} +2 -2
  8. package/dist/chunks/{color-field-xch8kx2g.js → color-field-gy56c94g.js} +3 -3
  9. package/dist/chunks/color-m7r1vk54.js +4 -0
  10. package/dist/chunks/{color-slider-jxd8v350.js → color-slider-sqqdyf6c.js} +2 -2
  11. package/dist/chunks/{combobox-p0efwjke.js → combobox-72h32ejk.js} +3 -3
  12. package/dist/chunks/components-r22xca09.js +4 -0
  13. package/dist/chunks/content-source-mmppmcyg.js +4 -0
  14. package/dist/chunks/convert-to-repeater-he70fc2b.js +4 -0
  15. package/dist/chunks/editor-ccd9jwsa.js +4 -0
  16. package/dist/chunks/entry-commands-6qxz9e8p.js +4 -0
  17. package/dist/chunks/extension-sections-0egb88dm.js +4 -0
  18. package/dist/chunks/file-ops-qnab36ek.js +4 -0
  19. package/dist/chunks/files-rcn56qvm.js +4 -0
  20. package/dist/chunks/format-host-yxz0kjjs.js +4 -0
  21. package/dist/chunks/fs-events-d1qxq4z4.js +4 -0
  22. package/dist/chunks/grid-open-4z1g062t.js +4 -0
  23. package/dist/chunks/{grid-view-d2wq9ezg.js → grid-view-qg1b5eqx.js} +25 -25
  24. package/dist/chunks/{icons-pnfz478q.js → icons-63bjtbs7.js} +2 -2
  25. package/dist/chunks/{iframe-entry-jdcktez8.js → iframe-entry-2zggzajv.js} +4 -4
  26. package/dist/chunks/{iframe-entry-zap785cw.js → iframe-entry-76j8vwwx.js} +4 -4
  27. package/dist/chunks/iframe-entry-99nr5zfb.js +5 -0
  28. package/dist/chunks/{iframe-entry-x0ygf2yr.js → iframe-entry-pkgmcgp0.js} +4 -4
  29. package/dist/chunks/iframe-host-ssv7kyj0.js +4 -0
  30. package/dist/chunks/{index-zpjmzqfk.js → index-32eqzv1k.js} +3 -3
  31. package/dist/chunks/index-f0jn7nnp.js +4 -0
  32. package/dist/chunks/{javascript-m54mnwp6.js → javascript-pjp734q4.js} +3 -3
  33. package/dist/chunks/jsonMode-njkvsa02.js +13 -0
  34. package/dist/chunks/jx-validate-ttasrbh7.js +4 -0
  35. package/dist/chunks/layers-bgyr11tr.js +4 -0
  36. package/dist/chunks/layers-panel-7ez66zeq.js +4 -0
  37. package/dist/chunks/{listbox-e74qjyq6.js → listbox-c421cf8c.js} +2 -2
  38. package/dist/chunks/lit-html-v9gh687x.js +4 -0
  39. package/dist/chunks/media-library-sq468wts.js +4 -0
  40. package/dist/chunks/media-picker-shfe59pt.js +4 -0
  41. package/dist/chunks/{menu-n6yzswmy.js → menu-bdpzbfvs.js} +2 -2
  42. package/dist/chunks/{monaco-binding-7gx83x3r.js → monaco-binding-vq1hgnn0.js} +3 -3
  43. package/dist/chunks/monaco-buffer-3afgwvbp.js +4 -0
  44. package/dist/chunks/monaco-setup-xrd2cbkp.js +4 -0
  45. package/dist/chunks/notify-kzrjr3fv.js +4 -0
  46. package/dist/chunks/parse-v77xtwv7.js +4 -0
  47. package/dist/chunks/platform-7v4rtd7m.js +4 -0
  48. package/dist/chunks/popover-b89sfej7.js +4 -0
  49. package/dist/chunks/publish-panel-2z9gw8ee.js +4 -0
  50. package/dist/chunks/publish-quick-qx2455cj.js +4 -0
  51. package/dist/chunks/publish-workspace-wrc1dgwx.js +4 -0
  52. package/dist/chunks/quick-search-n0t0eqxb.js +4 -0
  53. package/dist/chunks/references-9547rg5s.js +4 -0
  54. package/dist/chunks/right-panel-wsgnr9pn.js +4 -0
  55. package/dist/chunks/source-control-kwktr9y6.js +4 -0
  56. package/dist/chunks/{split-n2gfpm5m.js → split-c9zfdgxf.js} +2 -2
  57. package/dist/chunks/studio-03ct6yfv.js +9 -0
  58. package/dist/chunks/studio-07rcfje3.js +5 -0
  59. package/dist/chunks/studio-3e54qh39.js +7 -0
  60. package/dist/chunks/studio-3exxhz5g.js +5 -0
  61. package/dist/chunks/studio-3q40e3rj.js +5 -0
  62. package/dist/chunks/studio-3qeh9t0h.js +6 -0
  63. package/dist/chunks/studio-3qqjeep5.js +6 -0
  64. package/dist/chunks/studio-43sd2mam.js +4 -0
  65. package/dist/chunks/studio-4hp8pkyt.js +5 -0
  66. package/dist/chunks/studio-58xg6j2q.js +9 -0
  67. package/dist/chunks/studio-5g0rprdt.js +7 -0
  68. package/dist/chunks/{studio-2j1cb95x.js → studio-6m380wp7.js} +4 -4
  69. package/dist/chunks/studio-80jyh1xh.js +4 -0
  70. package/dist/chunks/studio-83mk4bgg.js +9 -0
  71. package/dist/chunks/{studio-v0jzqdjm.js → studio-881z1xrx.js} +4 -4
  72. package/dist/chunks/studio-8m0y2qe7.js +5 -0
  73. package/dist/chunks/{studio-d81xfh1g.js → studio-8tr6cfrc.js} +4 -4
  74. package/dist/chunks/studio-8vcvfs7r.js +6 -0
  75. package/dist/chunks/studio-9tmebf0s.js +5 -0
  76. package/dist/chunks/studio-a2bvaptz.js +5 -0
  77. package/dist/chunks/studio-b7y5mr30.js +5 -0
  78. package/dist/chunks/studio-bavb31r0.js +5 -0
  79. package/dist/chunks/{studio-dgz8d0c1.js → studio-bbe4c90a.js} +4 -4
  80. package/dist/chunks/studio-c05wdnt3.js +5 -0
  81. package/dist/chunks/studio-ccv2svc6.js +5 -0
  82. package/dist/chunks/studio-d5grtb9e.js +16 -0
  83. package/dist/chunks/studio-df0baea9.js +5 -0
  84. package/dist/chunks/studio-dgk4dqtx.js +7 -0
  85. package/dist/chunks/studio-e1g64q43.js +5 -0
  86. package/dist/chunks/studio-ed1hp6zf.js +6 -0
  87. package/dist/chunks/studio-ed3xx40h.js +7 -0
  88. package/dist/chunks/studio-f14e1zdj.js +5 -0
  89. package/dist/chunks/studio-fbhxxc5f.js +5 -0
  90. package/dist/chunks/studio-fyh0jc96.js +5 -0
  91. package/dist/chunks/studio-gcxk20n7.js +5 -0
  92. package/dist/chunks/studio-jaaz8ce9.js +5 -0
  93. package/dist/chunks/{studio-90pq9km1.js → studio-jcnsdaep.js} +4 -4
  94. package/dist/chunks/studio-jsgf2q5k.js +7 -0
  95. package/dist/chunks/{studio-0q988f9w.js → studio-k3xxbagr.js} +3 -3
  96. package/dist/chunks/{studio-5ve6bbep.js → studio-k7evxqh4.js} +4 -4
  97. package/dist/chunks/studio-kt3pjkwd.js +5 -0
  98. package/dist/chunks/studio-m8g16bgz.js +5 -0
  99. package/dist/chunks/studio-mn0476ag.js +5 -0
  100. package/dist/chunks/studio-mx1smyq0.js +5 -0
  101. package/dist/chunks/studio-n0c5h6mf.js +5 -0
  102. package/dist/chunks/{studio-485046te.js → studio-nh6d342n.js} +7 -7
  103. package/dist/chunks/studio-nqxqf8p2.js +4 -0
  104. package/dist/chunks/studio-p4nrkvqr.js +5 -0
  105. package/dist/chunks/{studio-1fxkaaea.js → studio-pbxa5fpc.js} +4 -4
  106. package/dist/chunks/studio-pgmvdv7z.js +7 -0
  107. package/dist/chunks/studio-ph84avkf.js +5 -0
  108. package/dist/chunks/{studio-tq0rtfwj.js → studio-q0mvbaq6.js} +4 -4
  109. package/dist/chunks/studio-q0tccd43.js +5 -0
  110. package/dist/chunks/studio-q7rtx6zq.js +14 -0
  111. package/dist/chunks/{studio-a3g98wjr.js → studio-qgh13jtz.js} +4 -4
  112. package/dist/chunks/studio-qmn1xc24.js +5 -0
  113. package/dist/chunks/studio-qwdqwkj0.js +5 -0
  114. package/dist/chunks/studio-r5qy30vx.js +5 -0
  115. package/dist/chunks/studio-rj9eamf8.js +5 -0
  116. package/dist/chunks/studio-rptgsk96.js +5 -0
  117. package/dist/chunks/studio-rthyg42z.js +928 -0
  118. package/dist/chunks/studio-s19tp1xr.js +5 -0
  119. package/dist/chunks/studio-s32578pn.js +5 -0
  120. package/dist/chunks/studio-svb7yyve.js +5 -0
  121. package/dist/chunks/studio-syreb644.js +5 -0
  122. package/dist/chunks/studio-tf9jryb1.js +10 -0
  123. package/dist/chunks/studio-tfkznv39.js +5 -0
  124. package/dist/chunks/studio-tft6r105.js +5 -0
  125. package/dist/chunks/studio-txf2kysx.js +5 -0
  126. package/dist/chunks/studio-v8867ddv.js +12 -0
  127. package/dist/chunks/studio-vxq7fcvv.js +5 -0
  128. package/dist/chunks/studio-w4rz9hdk.js +5 -0
  129. package/dist/chunks/studio-wbcjbhy1.js +9 -0
  130. package/dist/chunks/studio-wc649wss.js +5 -0
  131. package/dist/chunks/studio-wys23mxz.js +5 -0
  132. package/dist/chunks/studio-x1x167jh.js +5 -0
  133. package/dist/chunks/studio-x9hsze61.js +5 -0
  134. package/dist/chunks/studio-y0n7yxea.js +9 -0
  135. package/dist/chunks/studio-y3ahhdpx.js +5 -0
  136. package/dist/chunks/studio-z97c48fk.js +5 -0
  137. package/dist/chunks/studio-zat62m0c.js +5 -0
  138. package/dist/chunks/studio-zcd2e7yz.js +5 -0
  139. package/dist/chunks/studio-zz9qe3rw.js +64 -0
  140. package/dist/chunks/toast-1gv0h53h.js +4 -0
  141. package/dist/chunks/{toast-host-ke5z4ng7.js → toast-host-1yg5ar8d.js} +3 -3
  142. package/dist/chunks/{tooltip-5v8ngwj2.js → tooltip-yypz28gq.js} +3 -3
  143. package/dist/chunks/{tree-mdq3v5cw.js → tree-9xa4hm7n.js} +2 -2
  144. package/dist/chunks/tsMode-epyfz1hv.js +14 -0
  145. package/dist/chunks/workspace-dz7nxfhv.js +4 -0
  146. package/dist/chunks/workspace-home-xsd7hkjw.js +4 -0
  147. package/dist/chunks/{ws-client-nze0k6mv.js → ws-client-bv552sfx.js} +3 -3
  148. package/dist/iframe-entry.js +20 -16
  149. package/dist/manifest.json +77 -63
  150. package/dist/studio.js +103 -104
  151. package/dist/workers/ts.worker.js +3 -3
  152. package/package.json +12 -11
  153. package/src/browse/component-examples.ts +272 -0
  154. package/src/browse/library-commands.ts +64 -104
  155. package/src/browse/library-layouts.ts +263 -49
  156. package/src/browse/library-model.ts +181 -133
  157. package/src/browse/library-pane.ts +738 -446
  158. package/src/browse/library-preview.ts +149 -19
  159. package/src/browse/library-source.ts +308 -41
  160. package/src/browse/preview-root.ts +78 -0
  161. package/src/browse/preview-style.ts +155 -0
  162. package/src/canvas/asset-refs.ts +16 -1
  163. package/src/canvas/canvas-live-render.ts +222 -30
  164. package/src/canvas/canvas-origin.ts +37 -12
  165. package/src/canvas/canvas-page-context.ts +112 -0
  166. package/src/canvas/canvas-render.ts +113 -107
  167. package/src/canvas/canvas-utils.ts +57 -12
  168. package/src/canvas/iframe-drop.ts +107 -0
  169. package/src/canvas/iframe-entry.ts +24 -12
  170. package/src/canvas/iframe-host.ts +134 -62
  171. package/src/canvas/iframe-keys.ts +11 -0
  172. package/src/canvas/iframe-patch.ts +8 -7
  173. package/src/canvas/iframe-protocol.ts +24 -5
  174. package/src/canvas/iframe-render.ts +48 -1
  175. package/src/canvas/iframe-subtree.ts +5 -2
  176. package/src/canvas/npm-modules.ts +22 -0
  177. package/src/canvas/preview-navigate.ts +16 -0
  178. package/src/canvas/repeater-sources.ts +200 -0
  179. package/src/canvas/stage-scrollbar.ts +5 -1
  180. package/src/canvas/surface-registry.ts +6 -0
  181. package/src/commands/app-commands.ts +16 -6
  182. package/src/commands/budget.ts +10 -8
  183. package/src/commands/context.ts +35 -10
  184. package/src/commands/defaults.ts +69 -45
  185. package/src/commands/keymap.ts +9 -0
  186. package/src/commands/levels.ts +11 -5
  187. package/src/commands/live-context.ts +2 -0
  188. package/src/commands/registry.ts +10 -1
  189. package/src/content/collection-match.ts +6 -5
  190. package/src/content/collection-pages.ts +412 -0
  191. package/src/content/content-collections.ts +270 -0
  192. package/src/content/entry-commands.ts +51 -60
  193. package/src/content/entry-editor.ts +111 -18
  194. package/src/content/entry-fields.ts +13 -1
  195. package/src/content/entry-inspector.ts +225 -0
  196. package/src/content/entry-model.ts +17 -3
  197. package/src/content/entry-open.ts +80 -0
  198. package/src/content/entry-summaries.ts +440 -0
  199. package/src/content/file-record-ids.ts +80 -0
  200. package/src/content/file-records.ts +125 -0
  201. package/src/content/page-summaries.ts +129 -0
  202. package/src/content/record-entry.ts +201 -0
  203. package/src/content/writing-banner.ts +177 -0
  204. package/src/create/add-new-commands.ts +185 -0
  205. package/src/create/add-new-flow.ts +1116 -0
  206. package/src/create/add-new.ts +1014 -0
  207. package/src/create/element-name.ts +121 -0
  208. package/src/editor/convert-to-component.ts +67 -43
  209. package/src/editor/file-drop-action.ts +9 -1
  210. package/src/editor/inline-edit-apply.ts +44 -0
  211. package/src/editor/inline-edit.ts +10 -0
  212. package/src/editor/insert-component.ts +91 -0
  213. package/src/editor/insert-zone-action.ts +13 -4
  214. package/src/editor/shortcuts.ts +44 -22
  215. package/src/editor/slash-menu.ts +51 -8
  216. package/src/files/components.ts +74 -10
  217. package/src/files/file-ops.ts +15 -5
  218. package/src/files/files.ts +164 -150
  219. package/src/files/fs-events.ts +18 -7
  220. package/src/files/media-meta.ts +95 -1
  221. package/src/files/media-types.ts +55 -0
  222. package/src/files/media-upload.ts +209 -52
  223. package/src/files/new-file-name.ts +156 -0
  224. package/src/files/serialize-document.ts +13 -3
  225. package/src/format/format-host.ts +15 -0
  226. package/src/github/github-auth.ts +2 -2
  227. package/src/github/github-publish.ts +4 -3
  228. package/src/grid/grid-controller.ts +5 -1
  229. package/src/grid/grid-open.ts +0 -27
  230. package/src/grid/grid-panel.ts +54 -2
  231. package/src/grid/grid-reveal.ts +34 -0
  232. package/src/grid/grid-source.ts +3 -25
  233. package/src/grid/grid-view.ts +31 -6
  234. package/src/grid/sources/content-source.ts +75 -7
  235. package/src/grid/tabulator-tables.d.ts +6 -0
  236. package/src/hosting/document.ts +2 -2
  237. package/src/i18n/i18n-commands.ts +2 -1
  238. package/src/media/media-commands.ts +181 -0
  239. package/src/media/media-library-model.ts +232 -0
  240. package/src/media/media-library.ts +1283 -0
  241. package/src/media/media-pane.ts +47 -19
  242. package/src/packages/pull-package-sync.ts +1 -1
  243. package/src/panels/activity-panel.ts +21 -5
  244. package/src/panels/ai-chat/chat-markdown.ts +8 -8
  245. package/src/panels/block-action-bar.ts +11 -11
  246. package/src/panels/canvas-dnd-bridge.ts +5 -2
  247. package/src/panels/chat-panel.ts +6 -6
  248. package/src/panels/collections-panel.ts +306 -0
  249. package/src/panels/component-preview.ts +35 -13
  250. package/src/panels/data-explorer.ts +1 -1
  251. package/src/panels/data-grid.ts +4 -0
  252. package/src/panels/dnd.ts +17 -30
  253. package/src/panels/elements-panel.ts +9 -2
  254. package/src/panels/empty-state.ts +2 -2
  255. package/src/panels/formula-workspace.ts +2 -3
  256. package/src/panels/frontmatter-fields.ts +9 -9
  257. package/src/panels/git-diff-open.ts +9 -5
  258. package/src/panels/head-panel.ts +369 -103
  259. package/src/panels/i18n-panel.ts +11 -10
  260. package/src/panels/imports-panel.ts +2 -0
  261. package/src/panels/jump-bar.ts +27 -2
  262. package/src/panels/layers-panel.ts +36 -30
  263. package/src/panels/left-panel.ts +22 -8
  264. package/src/panels/navigator-panels.ts +23 -11
  265. package/src/panels/pane-context.ts +111 -29
  266. package/src/panels/pane-grid.ts +6 -6
  267. package/src/panels/panel-registry.ts +14 -9
  268. package/src/panels/properties-panel.ts +7 -3
  269. package/src/panels/right-panel.ts +199 -43
  270. package/src/panels/seo-modal.ts +41 -108
  271. package/src/panels/settings-menu.ts +52 -50
  272. package/src/panels/settings-pane.ts +268 -162
  273. package/src/panels/signals-panel.ts +3 -4
  274. package/src/panels/site-model.ts +367 -0
  275. package/src/panels/site-panel.ts +638 -0
  276. package/src/panels/style-panel.ts +37 -3
  277. package/src/panels/stylebook-doc.ts +142 -10
  278. package/src/panels/stylebook-layers-panel.ts +39 -80
  279. package/src/panels/stylebook-panel.ts +47 -10
  280. package/src/panels/styles-panel.ts +327 -0
  281. package/src/panels/tab-strip.ts +109 -25
  282. package/src/platform.ts +1 -1
  283. package/src/platforms/cloud.ts +28 -1
  284. package/src/platforms/default-platform.ts +4 -4
  285. package/src/platforms/devserver.ts +51 -25
  286. package/src/publish/deploy-checklist.ts +12 -7
  287. package/src/publish/pages-service.ts +38 -14
  288. package/src/publish/publish-changes.ts +491 -0
  289. package/src/publish/publish-commands.ts +83 -18
  290. package/src/publish/publish-ids.ts +49 -0
  291. package/src/publish/publish-quick.ts +148 -0
  292. package/src/publish/publish-workspace.ts +926 -0
  293. package/src/publish/source-control.ts +907 -0
  294. package/src/services/automation.ts +16 -20
  295. package/src/services/cf-settings.ts +4 -4
  296. package/src/services/connection.ts +2 -2
  297. package/src/services/directory-picker.ts +2 -2
  298. package/src/services/references.ts +0 -0
  299. package/src/services/render-critic.ts +21 -2
  300. package/src/services/settings/definitions.ts +25 -14
  301. package/src/settings/contributed-section.ts +49 -10
  302. package/src/settings/css-vars-editor.ts +93 -27
  303. package/src/settings/developer-panels.ts +43 -0
  304. package/src/settings/extension-sections.ts +20 -2
  305. package/src/settings/general-settings.ts +91 -21
  306. package/src/settings/preferences-dialog.ts +8 -1
  307. package/src/settings/project-sections.ts +27 -10
  308. package/src/settings/schema-field-ui.ts +3 -1
  309. package/src/settings/section-registry.ts +116 -11
  310. package/src/settings/settings-dialog.ts +192 -0
  311. package/src/settings/settings-document.ts +182 -162
  312. package/src/shell.ts +467 -431
  313. package/src/site-context.ts +64 -10
  314. package/src/state.ts +28 -9
  315. package/src/studio.ts +194 -41
  316. package/src/style/project-styles.ts +34 -0
  317. package/src/style/stylebook-specimens.ts +89 -0
  318. package/src/style/stylebook-target.ts +26 -0
  319. package/src/style/tag-inheritance.ts +105 -0
  320. package/src/style/variables-focus.ts +61 -0
  321. package/src/style/variables-view.ts +235 -0
  322. package/src/surfaces/add-new.json +1482 -0
  323. package/src/surfaces/add-new.ts +325 -0
  324. package/src/surfaces/ai-chat.ts +2 -2
  325. package/src/surfaces/boot-failure.json +4 -3
  326. package/src/surfaces/bottom-dock.json +1 -1
  327. package/src/surfaces/canvas-stage.json +30 -19
  328. package/src/surfaces/canvas-stage.ts +5 -5
  329. package/src/surfaces/collections-panel.json +615 -0
  330. package/src/surfaces/collections-panel.ts +168 -0
  331. package/src/surfaces/commandbar.json +241 -96
  332. package/src/surfaces/commandbar.ts +134 -99
  333. package/src/surfaces/empty-state.json +1 -1
  334. package/src/surfaces/entry-editor.json +136 -0
  335. package/src/surfaces/entry-editor.ts +21 -0
  336. package/src/surfaces/entry-inspector.json +371 -0
  337. package/src/surfaces/entry-inspector.ts +143 -0
  338. package/src/surfaces/inspector-dock.json +162 -14
  339. package/src/surfaces/inspector-dock.ts +46 -19
  340. package/src/surfaces/library-pane.json +670 -437
  341. package/src/surfaces/library-pane.ts +94 -59
  342. package/src/surfaces/media-browser.json +62 -6
  343. package/src/surfaces/media-browser.ts +15 -1
  344. package/src/surfaces/media-field.json +1 -1
  345. package/src/surfaces/media-library.json +1586 -0
  346. package/src/surfaces/media-library.ts +282 -0
  347. package/src/surfaces/media-pick-dialog.json +71 -0
  348. package/src/surfaces/media-pick-dialog.ts +132 -0
  349. package/src/surfaces/menu.ts +18 -0
  350. package/src/surfaces/pane-context.json +144 -77
  351. package/src/surfaces/pane-context.ts +24 -0
  352. package/src/surfaces/panel-deploy-checklist.json +12 -6
  353. package/src/surfaces/panel-deploy-checklist.ts +3 -1
  354. package/src/surfaces/panel-page.json +72 -2
  355. package/src/surfaces/panel-page.ts +38 -7
  356. package/src/surfaces/panel-signals.json +1 -1
  357. package/src/surfaces/panel-signals.ts +5 -5
  358. package/src/surfaces/panel-stylebook-layers.json +71 -204
  359. package/src/surfaces/panel-stylebook-layers.ts +0 -21
  360. package/src/surfaces/preferences.json +120 -48
  361. package/src/surfaces/preferences.ts +8 -0
  362. package/src/surfaces/publish-quick.json +428 -0
  363. package/src/surfaces/publish-quick.ts +165 -0
  364. package/src/surfaces/publish-workspace.json +2892 -0
  365. package/src/surfaces/publish-workspace.ts +391 -0
  366. package/src/surfaces/rail.json +1 -159
  367. package/src/surfaces/rail.ts +18 -75
  368. package/src/surfaces/schema-form.json +10 -2
  369. package/src/surfaces/schema-form.ts +2 -0
  370. package/src/surfaces/seo.json +1 -1
  371. package/src/surfaces/settings-contributed.json +5 -5
  372. package/src/surfaces/settings-contributed.ts +18 -2
  373. package/src/surfaces/settings-css-vars.json +44 -36
  374. package/src/surfaces/settings-css-vars.ts +77 -4
  375. package/src/surfaces/settings-deploy.json +2 -15
  376. package/src/surfaces/settings-deploy.ts +1 -1
  377. package/src/surfaces/settings-dialog.json +51 -0
  378. package/src/surfaces/settings-dialog.ts +169 -0
  379. package/src/surfaces/settings-overview.json +3 -3
  380. package/src/surfaces/settings-overview.ts +9 -2
  381. package/src/surfaces/settings-pane.json +269 -46
  382. package/src/surfaces/settings-pane.ts +85 -24
  383. package/src/surfaces/shell.json +2 -1
  384. package/src/surfaces/site-panel.json +1029 -0
  385. package/src/surfaces/site-panel.ts +233 -0
  386. package/src/surfaces/slash-menu.json +63 -21
  387. package/src/surfaces/slash-menu.ts +23 -0
  388. package/src/surfaces/statusbar.ts +39 -15
  389. package/src/surfaces/styles-panel.json +735 -0
  390. package/src/surfaces/styles-panel.ts +195 -0
  391. package/src/surfaces/styles-variables.json +149 -0
  392. package/src/surfaces/styles-variables.ts +145 -0
  393. package/src/surfaces/tab-strip.json +4 -4
  394. package/src/surfaces/welcome.ts +1 -1
  395. package/src/surfaces/writing-banner.json +94 -0
  396. package/src/surfaces/writing-banner.ts +80 -0
  397. package/src/tabs/bare-repeaters.ts +101 -0
  398. package/src/tabs/tab.ts +47 -7
  399. package/src/types.ts +21 -26
  400. package/src/ui/form-controls.ts +47 -8
  401. package/src/ui/layers.ts +13 -0
  402. package/src/ui/media-picker.ts +284 -40
  403. package/src/ui/regions.ts +7 -6
  404. package/src/ui/schema-form.ts +259 -27
  405. package/src/ui/virtual-window.ts +26 -1
  406. package/src/utils/base64.ts +3 -3
  407. package/src/utils/edit-display.ts +322 -67
  408. package/src/utils/local-day.ts +18 -0
  409. package/src/workspace/pane-derive.ts +4 -4
  410. package/src/workspace/session.ts +28 -7
  411. package/src/workspace/tab-workspace.ts +137 -0
  412. package/src/workspace/workspace-back.ts +189 -0
  413. package/src/workspace/workspace-home.ts +133 -0
  414. package/src/workspace/workspace-ids.ts +129 -0
  415. package/src/workspace/workspace-switch.ts +372 -0
  416. package/src/workspace/workspace.ts +125 -11
  417. package/styles/shell-frame.css +30 -7
  418. package/styles/shell-frame.json +15 -3
  419. package/dist/chunks/2020-f2gkpy8d.js +0 -4
  420. package/dist/chunks/adapter-4912g339.js +0 -4
  421. package/dist/chunks/cf-account-picker-wccaenh6.js +0 -4
  422. package/dist/chunks/cf-settings-br6105tr.js +0 -4
  423. package/dist/chunks/color-28ksk2qm.js +0 -4
  424. package/dist/chunks/components-db68vsn7.js +0 -4
  425. package/dist/chunks/content-source-1449qesq.js +0 -4
  426. package/dist/chunks/convert-to-repeater-5h457egj.js +0 -4
  427. package/dist/chunks/editor-x9jn86a7.js +0 -4
  428. package/dist/chunks/entry-commands-may24cpt.js +0 -4
  429. package/dist/chunks/extension-sections-pvr14vxr.js +0 -4
  430. package/dist/chunks/file-ops-x9v54pcj.js +0 -4
  431. package/dist/chunks/files-j3knqf0s.js +0 -4
  432. package/dist/chunks/format-host-tyre2trb.js +0 -4
  433. package/dist/chunks/fs-events-64ma9htc.js +0 -4
  434. package/dist/chunks/git-panel-84ps5dex.js +0 -4
  435. package/dist/chunks/iframe-entry-fvbk89gm.js +0 -5
  436. package/dist/chunks/iframe-host-ck6vejt5.js +0 -4
  437. package/dist/chunks/index-b8xw3vy9.js +0 -4
  438. package/dist/chunks/jsonMode-w15cmrcm.js +0 -13
  439. package/dist/chunks/jx-validate-at3zn8wa.js +0 -4
  440. package/dist/chunks/layers-h4xp1pss.js +0 -4
  441. package/dist/chunks/layers-panel-m5hw1d7g.js +0 -4
  442. package/dist/chunks/lit-html-8t8t3dnr.js +0 -4
  443. package/dist/chunks/media-picker-2cweexc5.js +0 -4
  444. package/dist/chunks/menu-ynw13y0s.js +0 -4
  445. package/dist/chunks/monaco-buffer-qzxn567r.js +0 -4
  446. package/dist/chunks/monaco-setup-gxf6656g.js +0 -4
  447. package/dist/chunks/notify-cj5zbrs2.js +0 -4
  448. package/dist/chunks/parse-b7ddhr8m.js +0 -4
  449. package/dist/chunks/platform-3dfws9js.js +0 -4
  450. package/dist/chunks/popover-jt2gxmzs.js +0 -4
  451. package/dist/chunks/publish-panel-1gvxbj21.js +0 -4
  452. package/dist/chunks/quick-search-0dajrwqg.js +0 -4
  453. package/dist/chunks/references-49qgt404.js +0 -4
  454. package/dist/chunks/right-panel-jm1bwe53.js +0 -4
  455. package/dist/chunks/section-registry-6kwc4fky.js +0 -4
  456. package/dist/chunks/studio-0rt52vpq.js +0 -5
  457. package/dist/chunks/studio-18vgbhs5.js +0 -5
  458. package/dist/chunks/studio-24mccdrj.js +0 -5
  459. package/dist/chunks/studio-2eh507zk.js +0 -9
  460. package/dist/chunks/studio-2hcbccfv.js +0 -5
  461. package/dist/chunks/studio-2jbs3rxv.js +0 -5
  462. package/dist/chunks/studio-4h8k4dw9.js +0 -5
  463. package/dist/chunks/studio-5f6n18kx.js +0 -5
  464. package/dist/chunks/studio-5gthpky6.js +0 -5
  465. package/dist/chunks/studio-6ep3js6b.js +0 -9
  466. package/dist/chunks/studio-6wkmf3wz.js +0 -9
  467. package/dist/chunks/studio-841rf3cg.js +0 -5
  468. package/dist/chunks/studio-8gmwgheg.js +0 -5
  469. package/dist/chunks/studio-8qm0ep50.js +0 -5
  470. package/dist/chunks/studio-924p1tm1.js +0 -6
  471. package/dist/chunks/studio-a3shpz9y.js +0 -5
  472. package/dist/chunks/studio-amsbaaar.js +0 -6
  473. package/dist/chunks/studio-at5spjbc.js +0 -7
  474. package/dist/chunks/studio-b2xv945e.js +0 -5
  475. package/dist/chunks/studio-bn5nkafw.js +0 -5
  476. package/dist/chunks/studio-bw6q5xjf.js +0 -9
  477. package/dist/chunks/studio-c7q0t9x1.js +0 -5
  478. package/dist/chunks/studio-camnhc96.js +0 -6
  479. package/dist/chunks/studio-cffqvnzd.js +0 -5
  480. package/dist/chunks/studio-efvfrsf8.js +0 -5
  481. package/dist/chunks/studio-er30bpcx.js +0 -5
  482. package/dist/chunks/studio-ezscgr64.js +0 -5
  483. package/dist/chunks/studio-ffrrncvz.js +0 -5
  484. package/dist/chunks/studio-fpphgn8y.js +0 -10
  485. package/dist/chunks/studio-j1kvhdjk.js +0 -4
  486. package/dist/chunks/studio-j430n16g.js +0 -7
  487. package/dist/chunks/studio-jjkg84nj.js +0 -5
  488. package/dist/chunks/studio-m0q32ars.js +0 -4
  489. package/dist/chunks/studio-m6bdst11.js +0 -5
  490. package/dist/chunks/studio-nv2cptd4.js +0 -64
  491. package/dist/chunks/studio-nwfrc98n.js +0 -5
  492. package/dist/chunks/studio-p1wgvp7r.js +0 -5
  493. package/dist/chunks/studio-pag2wncm.js +0 -5
  494. package/dist/chunks/studio-qvanvzfd.js +0 -5
  495. package/dist/chunks/studio-qwa64kt7.js +0 -5
  496. package/dist/chunks/studio-rk1zc4x3.js +0 -928
  497. package/dist/chunks/studio-se2r3ptc.js +0 -5
  498. package/dist/chunks/studio-sg2a0dh0.js +0 -5
  499. package/dist/chunks/studio-tjzjhe68.js +0 -5
  500. package/dist/chunks/studio-v2w7q8r1.js +0 -7
  501. package/dist/chunks/studio-v5vjq1h0.js +0 -4
  502. package/dist/chunks/studio-v8aga3vp.js +0 -14
  503. package/dist/chunks/studio-vfcdrfmv.js +0 -5
  504. package/dist/chunks/studio-vmfkgdjf.js +0 -5
  505. package/dist/chunks/studio-wt1816jj.js +0 -5
  506. package/dist/chunks/studio-wz73q5kq.js +0 -4
  507. package/dist/chunks/studio-y04qtxvp.js +0 -7
  508. package/dist/chunks/studio-y21g8zzg.js +0 -5
  509. package/dist/chunks/studio-y64hrt8p.js +0 -12
  510. package/dist/chunks/studio-ynn90f58.js +0 -16
  511. package/dist/chunks/studio-yxz682sx.js +0 -5
  512. package/dist/chunks/studio-zp7snvkq.js +0 -5
  513. package/dist/chunks/studio-zq4z6tm6.js +0 -5
  514. package/dist/chunks/toast-c9fn075p.js +0 -4
  515. package/dist/chunks/tsMode-sf49ee9x.js +0 -14
  516. package/dist/chunks/workspace-jtfahpee.js +0 -4
  517. package/src/panels/frontmatter-panel.ts +0 -711
  518. package/src/panels/git-panel.ts +0 -1108
  519. package/src/surfaces/doc-header.json +0 -776
  520. package/src/surfaces/doc-header.ts +0 -222
  521. package/src/surfaces/git-panel.json +0 -1880
  522. package/src/surfaces/git-panel.ts +0 -264
@@ -1,15 +1,15 @@
1
1
  /**
2
- * The Library's five layouts, as geometry and projections rather than as markup.
2
+ * The Content home's four views, as geometry and projections rather than as markup.
3
3
  *
4
- * All five draw the SAME rows — one scan, one filter — so switching layout is a repaint, never a
5
- * re-read. What differs is the geometry, and the geometry is data ({@link LAYOUT_METRICS}) rather
6
- * than five hand-rolled scroll containers: the pane hands each layout a window computed from the
7
- * metric, so virtualization is a property of the Library rather than of whichever layout someone
8
- * remembered to virtualize.
4
+ * All four draw the SAME rows — one scan, one collection, one filter — so switching view is a
5
+ * repaint, never a re-read. What differs is the geometry, and the geometry is data
6
+ * ({@link LAYOUT_METRICS}) rather than five hand-rolled scroll containers: the pane hands each
7
+ * layout a window computed from the metric, so virtualization is a property of the Content home
8
+ * rather than of whichever layout someone remembered to virtualize.
9
9
  *
10
- * **Two of them are grouped, and grouped lists are bounded differently.** Table, Cards and Media
11
- * are flat and uniform, so they window. Calendar and Board are grouped, and a window over a grouped
12
- * list either breaks the groups or needs variable-height measurement; both draw text only (no live
10
+ * **Two of them are grouped, and grouped lists are bounded differently.** Table and Cards are flat
11
+ * and uniform, so they window. Calendar and Board are grouped, and a window over a grouped list
12
+ * either breaks the groups or needs variable-height measurement; both draw text only (no live
13
13
  * preview, so an item costs one `<li>`), and each caps what it draws and SAYS what it left out. A
14
14
  * layout that silently truncated would be the same class of lie as "No files found".
15
15
  *
@@ -20,12 +20,111 @@
20
20
 
21
21
  import { isImage } from "../files/media-upload";
22
22
  import { previewFileSrc } from "../files/media-paths";
23
+ import { localDay } from "../utils/local-day";
23
24
  import { localeLabel } from "@jxsuite/schema/locale";
24
- import { groupByCategory, groupByDate } from "./library-model";
25
+ import { fileToRoute } from "@jxsuite/site/routes";
26
+ import { PAGES_COLLECTION } from "../content/content-collections";
27
+ import { groupByDate, groupByFolder, libraryDate } from "./library-model";
28
+ import { entryFieldOfKey } from "./library-source";
29
+ import type { EntryFieldColumn } from "./library-source";
30
+ import type { EntryFieldTexts } from "../content/entry-summaries";
25
31
  import type { LibraryFile, LibraryLayout } from "./library-model";
26
32
  import type { GridColumn, GridCellValue } from "../grid/grid-source";
27
33
  import type { LibraryGroup, LibraryRow } from "../surfaces/library-pane";
28
34
 
35
+ /** What a collection entry says about itself, once read: its title, status and date. */
36
+ export interface EntryFacts {
37
+ title: string;
38
+ /** "Draft", "Published", or "" for a collection with no draft workflow. */
39
+ status: string;
40
+ /** `YYYY-MM-DD`, or "". */
41
+ date: string;
42
+ /** The schema's fields as list text, keyed by field name, when the entry has been read. */
43
+ fields?: EntryFieldTexts | undefined;
44
+ /** A page's template, by name ("Base", "None"), once the page has been read. */
45
+ template?: string | undefined;
46
+ /**
47
+ * The entry's picture, as a preview URL: the field its schema declares an image (a post's
48
+ * `cover`), from `content/entry-summaries.ts`'s `entryThumb`. "" or absent for none.
49
+ */
50
+ thumb?: string | undefined;
51
+ }
52
+
53
+ /** The field a Board groups a collection by when it has no draft workflow, and its heading. */
54
+ export interface BoardField {
55
+ name: string;
56
+ title: string;
57
+ /** The schema's `enum`, when it declares one: the order the columns stand in. */
58
+ order: readonly string[];
59
+ /** The field holds a list: an entry stands in the column of each of its values. */
60
+ list?: boolean | undefined;
61
+ }
62
+
63
+ /**
64
+ * The values one field's list text holds: each item of a list field (its text joins them with
65
+ * commas, as a CSV cell does), or the one value of any other field. Blank values are none.
66
+ */
67
+ export function fieldValues(text: string | undefined, list: boolean | undefined): string[] {
68
+ if (text === undefined || text.trim() === "") {
69
+ return [];
70
+ }
71
+ if (!list) {
72
+ return [text];
73
+ }
74
+ return text
75
+ .split(",")
76
+ .map((value) => value.trim())
77
+ .filter((value) => value !== "");
78
+ }
79
+
80
+ /** A Board with more columns than this is a list of singletons, not a grouping. */
81
+ const BOARD_FIELD_MAX_GROUPS = 12;
82
+
83
+ /**
84
+ * The field a Board groups by when the entries have no status: the first listed field the schema
85
+ * gives an `enum`, else the first whose values actually repeat. A menu grouped by its `course` is a
86
+ * board; a menu grouped by its one folder is one column, which says nothing. `null` when no field
87
+ * groups the entries (every value unique, or every value the same), so the Board falls back to
88
+ * folders.
89
+ *
90
+ * @param fields The listed fields, in order ({@link entryFieldColumns}).
91
+ * @param enums Each field's declared `enum`, where it has one.
92
+ * @param facts What each entry says, as read so far.
93
+ */
94
+ export function boardFieldOf(
95
+ fields: readonly EntryFieldColumn[],
96
+ enums: ReadonlyMap<string, readonly string[]>,
97
+ facts: readonly (EntryFacts | undefined)[],
98
+ ): BoardField | null {
99
+ const declared = fields.find((field) => enums.has(field.name));
100
+ if (declared) {
101
+ return { ...declared, order: enums.get(declared.name)! };
102
+ }
103
+ for (const field of fields) {
104
+ if (field.measure) {
105
+ continue;
106
+ }
107
+ // A list field's every item is a value: `Patios, Stone` is two, not one string.
108
+ const values = facts.flatMap((entry) => fieldValues(entry?.fields?.[field.name], field.list));
109
+ const distinct = new Set(values).size;
110
+ if (distinct >= 2 && distinct < values.length && distinct <= BOARD_FIELD_MAX_GROUPS) {
111
+ return { ...field, order: [] };
112
+ }
113
+ }
114
+ return null;
115
+ }
116
+
117
+ /** A file's entry facts, or `undefined` for a file that is no entry or has not been read yet. */
118
+ export type EntryFactsOf = (file: LibraryFile) => EntryFacts | undefined;
119
+
120
+ /** Facts by path. Empty for Pages, and for a collection whose entries are still being read. */
121
+ export function entryFactsFrom(facts: ReadonlyMap<string, EntryFacts>): EntryFactsOf {
122
+ return (file) => facts.get(file.path);
123
+ }
124
+
125
+ /** No file has facts. */
126
+ const NO_FACTS: EntryFactsOf = entryFactsFrom(new Map());
127
+
29
128
  /** The geometry a layout scrolls at. `itemWidth` of 0 means one item per row. */
30
129
  export interface LayoutMetric {
31
130
  /** Height of one row of items, in CSS pixels — the unit the window counts in. */
@@ -39,7 +138,7 @@ export interface LayoutMetric {
39
138
  /**
40
139
  * Per-layout geometry.
41
140
  *
42
- * These are nominal sizes that must match the Library surface's own style block. They are
141
+ * These are nominal sizes that must match the Content home surface's own style block. They are
43
142
  * approximate on purpose: the window's job is to keep the rendered count proportional to the
44
143
  * viewport, and being one row out costs one row of overscan, not correctness.
45
144
  */
@@ -47,7 +146,6 @@ export const LAYOUT_METRICS: Readonly<Record<LibraryLayout, LayoutMetric>> = {
47
146
  board: { itemWidth: 0, rowHeight: 0, windowed: false },
48
147
  calendar: { itemWidth: 0, rowHeight: 0, windowed: false },
49
148
  cards: { itemWidth: 200, rowHeight: 194, windowed: true },
50
- media: { itemWidth: 132, rowHeight: 132, windowed: true },
51
149
  table: { itemWidth: 0, rowHeight: 32, windowed: true },
52
150
  };
53
151
 
@@ -86,20 +184,29 @@ export function formatSize(size: number | undefined): string {
86
184
  return `${value < 10 ? value.toFixed(1) : Math.round(value)} ${units[unit]}`;
87
185
  }
88
186
 
89
- /** An ISO timestamp as `YYYY-MM-DD`, or "" when there is none. Never invents "just now". */
187
+ /**
188
+ * An ISO timestamp as the local `YYYY-MM-DD` it fell on, or "" when there is none. Never invents
189
+ * "just now".
190
+ */
90
191
  export function formatModified(modified: string | undefined): string {
91
- if (!modified) {
92
- return "";
93
- }
94
- const parsed = new Date(modified);
95
- return Number.isNaN(parsed.getTime()) ? "" : parsed.toISOString().slice(0, 10);
192
+ return modified ? localDay(new Date(modified)) : "";
96
193
  }
97
194
 
98
- /** The text one table cell shows for a file. */
99
- export function cellText(file: LibraryFile, field: string): string {
195
+ /** The text one table cell shows for a file, and for an entry what it says about itself. */
196
+ export function cellText(file: LibraryFile, field: string, entry?: EntryFacts): string {
100
197
  switch (field) {
101
- case "category": {
102
- return file.category;
198
+ case "date": {
199
+ return entry?.date ?? "";
200
+ }
201
+ case "status": {
202
+ return entry?.status ?? "";
203
+ }
204
+ case "template": {
205
+ return entry?.template ?? "";
206
+ }
207
+ case "title": {
208
+ // Until the entry is read, the file name: the title is never blank while it loads.
209
+ return entry?.title ?? file.name;
103
210
  }
104
211
  case "locale": {
105
212
  // The autonym, not the tag: the column exists so a reader can find their own language in it,
@@ -113,7 +220,8 @@ export function cellText(file: LibraryFile, field: string): string {
113
220
  return file.name;
114
221
  }
115
222
  case "path": {
116
- return file.path;
223
+ // A record is stored in its collection's file; its key (`prices.csv#3`) is no path.
224
+ return file.record?.source ?? file.path;
117
225
  }
118
226
  case "size": {
119
227
  return formatSize(file.size);
@@ -121,8 +229,13 @@ export function cellText(file: LibraryFile, field: string): string {
121
229
  case "type": {
122
230
  return file.type;
123
231
  }
232
+ case "url": {
233
+ // Only a page has a URL of its own; an entry's is its collection page's, read elsewhere.
234
+ return file.collection === PAGES_COLLECTION ? fileToRoute(file.path).urlPattern : "";
235
+ }
124
236
  default: {
125
- return "";
237
+ const own = entryFieldOfKey(field);
238
+ return own === null ? "" : (entry?.fields?.[own] ?? "");
126
239
  }
127
240
  }
128
241
  }
@@ -134,40 +247,56 @@ export function cellTextOf(value: GridCellValue | undefined): string {
134
247
 
135
248
  // ─── Rows ────────────────────────────────────────────────────────────────────
136
249
 
137
- /** The four categories whose files are documents the runtime can render a preview of. */
138
- const PREVIEWABLE = new Set(["Components", "Content", "Layouts", "Pages"]);
139
-
140
250
  /**
141
251
  * One file, as the surface reads it.
142
252
  *
143
- * `live` is whether this layout may draw a real document render at all: Cards may, Media never
144
- * does. Getting that from the caller rather than from the file is what keeps a tile cheap — a
145
- * thumbnail of an asset is an `<img>`, and a live runtime render inside one is the whole cost the
146
- * Media layout exists to avoid.
253
+ * `live` is whether this layout may draw a real document render at all: Cards may, Table never
254
+ * does. Getting that from the caller rather than from the file is what keeps a row cheap. An image
255
+ * is drawn as itself; every other file in a collection is a document the runtime can render.
147
256
  *
148
257
  * @param file The scanned file.
149
258
  * @param columns The Table's columns, from the grid source. Empty for a layout with no cells.
150
259
  * @param live Whether a previewable document may claim the `doc` island.
260
+ * @param entry What the file says about itself, when it is a collection entry that has been read.
151
261
  */
152
262
  export function libraryRow(
153
263
  file: LibraryFile,
154
264
  columns: readonly GridColumn[],
155
265
  live: boolean,
266
+ entry?: EntryFacts,
156
267
  ): LibraryRow {
157
268
  const image = isImage(file.ext);
269
+ // An entry is shown by its own picture (its `cover`), not by a render of its body text.
270
+ const thumb = entry?.thumb ?? "";
158
271
  return {
159
- art: image ? "image" : live && PREVIEWABLE.has(file.category) ? "doc" : "glyph",
160
- cells: columns.map((column) => ({ field: column.field, text: cellText(file, column.field) })),
272
+ // A record is a row of a file, not a document the runtime can render.
273
+ art:
274
+ image || thumb !== ""
275
+ ? "image"
276
+ : live && file.collection !== null && !file.record
277
+ ? "doc"
278
+ : "glyph",
279
+ cells: columns.map((column) => ({
280
+ field: column.field,
281
+ text: cellText(file, column.field, entry),
282
+ })),
161
283
  meta: file.type,
162
- name: file.name,
284
+ name: entry?.title ?? file.name,
163
285
  path: file.path,
164
- src: image ? previewFileSrc(file.path) : "",
286
+ src: image ? previewFileSrc(file.path) : thumb,
165
287
  };
166
288
  }
167
289
 
168
- /** The text-only row both grouped layouts draw: a name, and the path it opens. */
169
- function nameRow(file: LibraryFile): LibraryRow {
170
- return { art: "glyph", cells: [], meta: file.type, name: file.name, path: file.path, src: "" };
290
+ /** The text-only row both grouped layouts draw: a name (an entry's title), and the path it opens. */
291
+ function nameRow(file: LibraryFile, entry?: EntryFacts): LibraryRow {
292
+ return {
293
+ art: "glyph",
294
+ cells: [],
295
+ meta: file.type,
296
+ name: entry?.title ?? file.name,
297
+ path: file.path,
298
+ src: "",
299
+ };
171
300
  }
172
301
 
173
302
  // ─── Calendar ────────────────────────────────────────────────────────────────
@@ -181,19 +310,27 @@ export interface CalendarView {
181
310
  /**
182
311
  * Calendar: one section per day, newest first, plus an explicit account of what it did not place.
183
312
  *
184
- * A file's day comes from a `YYYY-MM-DD` filename prefix or the filesystem's mtime — never from
185
- * "now". Files with neither are listed under "No date" rather than parked on today, because a
186
- * calendar that invents dates is worse than one that admits it cannot place a file.
313
+ * A file's day is an entry's own date field when it has one, else a `YYYY-MM-DD` filename prefix or
314
+ * the filesystem's mtime — never "now". Files with none are listed under "No date" rather than
315
+ * parked on today, because a calendar that invents dates is worse than one that admits it cannot
316
+ * place a file.
187
317
  *
318
+ * @param factsOf What each entry says about itself; its `date` wins over the file's.
188
319
  * @returns The day sections, and the sentence naming the older days it did not draw.
189
320
  */
190
- export function calendarView(files: readonly LibraryFile[]): CalendarView {
191
- const { days, undated } = groupByDate(files);
321
+ export function calendarView(
322
+ files: readonly LibraryFile[],
323
+ factsOf: EntryFactsOf = NO_FACTS,
324
+ ): CalendarView {
325
+ const { days, undated } = groupByDate(files, (file) => {
326
+ const own = factsOf(file)?.date;
327
+ return own || libraryDate(file);
328
+ });
192
329
  const shown = days.slice(0, CALENDAR_DAY_LIMIT);
193
330
  const hiddenDays = days.length - shown.length;
194
331
  const groups: LibraryGroup[] = shown.map((day) => ({
195
332
  count: "",
196
- files: day.files.map((file) => nameRow(file)),
333
+ files: day.files.map((file) => nameRow(file, factsOf(file))),
197
334
  id: day.date,
198
335
  note: "",
199
336
  noteState: "hidden",
@@ -203,7 +340,7 @@ export function calendarView(files: readonly LibraryFile[]): CalendarView {
203
340
  if (undated.length > 0) {
204
341
  groups.push({
205
342
  count: "",
206
- files: undated.slice(0, BOARD_COLUMN_LIMIT).map((file) => nameRow(file)),
343
+ files: undated.slice(0, BOARD_COLUMN_LIMIT).map((file) => nameRow(file, factsOf(file))),
207
344
  /* Not a date, and it must never collide with one: the section's identity is what keeps its
208
345
  node across a repaint, and a day is always `YYYY-MM-DD`, so a leading underscore pair cannot be one. */
209
346
  id: "__undated",
@@ -224,13 +361,90 @@ export function calendarView(files: readonly LibraryFile[]): CalendarView {
224
361
 
225
362
  // ─── Board ───────────────────────────────────────────────────────────────────
226
363
 
227
- /** Board: one column per category present, each capped and each stating its own total. */
228
- export function boardView(files: readonly LibraryFile[]): LibraryGroup[] {
229
- return groupByCategory(files).map((group) => {
364
+ /** The order a status Board's columns stand in: the work in progress first. */
365
+ const STATUS_ORDER = ["Draft", "Published"];
366
+
367
+ /** Group entries by their status, in {@link STATUS_ORDER}, then any other status by name. */
368
+ function groupByStatus(
369
+ files: readonly LibraryFile[],
370
+ factsOf: EntryFactsOf,
371
+ ): { group: string; files: LibraryFile[] }[] {
372
+ const byStatus = new Map<string, LibraryFile[]>();
373
+ for (const file of files) {
374
+ const status = factsOf(file)?.status || "No status";
375
+ const bucket = byStatus.get(status);
376
+ if (bucket) {
377
+ bucket.push(file);
378
+ } else {
379
+ byStatus.set(status, [file]);
380
+ }
381
+ }
382
+ const rank = (status: string) => {
383
+ const at = STATUS_ORDER.indexOf(status);
384
+ return at === -1 ? STATUS_ORDER.length : at;
385
+ };
386
+ return [...byStatus.entries()]
387
+ .map(([group, grouped]) => ({ files: grouped, group }))
388
+ .toSorted((a, b) => rank(a.group) - rank(b.group) || a.group.localeCompare(b.group));
389
+ }
390
+
391
+ /** Group entries by one field's value, in the schema's `enum` order, then any other value by name. */
392
+ function groupByField(
393
+ files: readonly LibraryFile[],
394
+ factsOf: EntryFactsOf,
395
+ field: BoardField,
396
+ ): { group: string; files: LibraryFile[] }[] {
397
+ const none = `No ${field.title.toLowerCase()}`;
398
+ const byValue = new Map<string, LibraryFile[]>();
399
+ for (const file of files) {
400
+ const values = fieldValues(factsOf(file)?.fields?.[field.name], field.list);
401
+ // An entry tagged twice stands in both columns: each is the set of entries with that tag.
402
+ for (const value of values.length === 0 ? [none] : new Set(values)) {
403
+ const bucket = byValue.get(value);
404
+ if (bucket) {
405
+ bucket.push(file);
406
+ } else {
407
+ byValue.set(value, [file]);
408
+ }
409
+ }
410
+ }
411
+ const rank = (value: string) => {
412
+ if (value === none) {
413
+ return Number.MAX_SAFE_INTEGER;
414
+ }
415
+ const at = field.order.indexOf(value);
416
+ return at === -1 ? field.order.length : at;
417
+ };
418
+ return [...byValue.entries()]
419
+ .map(([group, grouped]) => ({ files: grouped, group }))
420
+ .toSorted((a, b) => rank(a.group) - rank(b.group) || a.group.localeCompare(b.group));
421
+ }
422
+
423
+ /**
424
+ * Board: one column per status when the collection's entries have one (Draft, Published), else one
425
+ * per value of the field the collection is grouped by ({@link boardFieldOf}: a menu's `course`),
426
+ * else one per folder present; each capped and each stating its own total. A flat collection
427
+ * grouped by folder is one column, which says nothing, so a field is preferred to it.
428
+ *
429
+ * @param factsOf What each entry says about itself.
430
+ * @param field The field to group by when the entries have no status.
431
+ */
432
+ export function boardView(
433
+ files: readonly LibraryFile[],
434
+ factsOf: EntryFactsOf = NO_FACTS,
435
+ field: BoardField | null = null,
436
+ ): LibraryGroup[] {
437
+ const byStatus = files.some((file) => (factsOf(file)?.status ?? "") !== "");
438
+ const groups = byStatus
439
+ ? groupByStatus(files, factsOf)
440
+ : field
441
+ ? groupByField(files, factsOf, field)
442
+ : groupByFolder(files);
443
+ return groups.map((group) => {
230
444
  const hidden = group.files.length - BOARD_COLUMN_LIMIT;
231
445
  return {
232
446
  count: String(group.files.length),
233
- files: group.files.slice(0, BOARD_COLUMN_LIMIT).map((file) => nameRow(file)),
447
+ files: group.files.slice(0, BOARD_COLUMN_LIMIT).map((file) => nameRow(file, factsOf(file))),
234
448
  id: group.group,
235
449
  note: hidden > 0 ? `${hidden} more — switch to Table to see them.` : "",
236
450
  noteState: hidden > 0 ? "shown" : "hidden",