canopycms 0.0.68-int.98 → 0.1.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 (688) hide show
  1. package/CHANGELOG.md +2131 -0
  2. package/README.md +47 -63
  3. package/dist/ai/generate.d.ts +5 -0
  4. package/dist/ai/generate.js +244 -115
  5. package/dist/ai/index.d.ts +1 -1
  6. package/dist/ai/json-to-markdown.d.ts +21 -1
  7. package/dist/ai/json-to-markdown.js +120 -57
  8. package/dist/ai/references.d.ts +72 -0
  9. package/dist/ai/references.js +223 -0
  10. package/dist/ai/resolve-branch.js +9 -2
  11. package/dist/ai/to-plain-text.d.ts +17 -8
  12. package/dist/ai/to-plain-text.js +18 -15
  13. package/dist/ai/types.d.ts +84 -3
  14. package/dist/api/admin-branch-health.d.ts +24 -1
  15. package/dist/api/admin-branch-health.js +52 -20
  16. package/dist/api/admin.d.ts +23 -5
  17. package/dist/api/admin.js +36 -1
  18. package/dist/api/assets.js +88 -78
  19. package/dist/api/branch-create-window.d.ts +12 -0
  20. package/dist/api/branch-create-window.js +13 -0
  21. package/dist/api/branch-merge.d.ts +1 -1
  22. package/dist/api/branch-merge.js +1 -1
  23. package/dist/api/branch-review.d.ts +3 -3
  24. package/dist/api/branch-review.js +16 -7
  25. package/dist/api/branch-status.d.ts +16 -6
  26. package/dist/api/branch-status.js +57 -12
  27. package/dist/api/branch-withdraw.d.ts +2 -2
  28. package/dist/api/branch-withdraw.js +8 -3
  29. package/dist/api/branch.d.ts +25 -12
  30. package/dist/api/branch.js +164 -103
  31. package/dist/api/client.d.ts +78 -58
  32. package/dist/api/client.js +150 -117
  33. package/dist/api/comments.d.ts +12 -3
  34. package/dist/api/comments.js +118 -9
  35. package/dist/api/content.d.ts +9 -9
  36. package/dist/api/content.js +131 -22
  37. package/dist/api/entries-constants.d.ts +14 -3
  38. package/dist/api/entries-constants.js +14 -3
  39. package/dist/api/entries.d.ts +40 -2
  40. package/dist/api/entries.js +102 -6
  41. package/dist/api/github-sync.d.ts +19 -0
  42. package/dist/api/github-sync.js +50 -3
  43. package/dist/api/groups.d.ts +4 -4
  44. package/dist/api/groups.js +2 -1
  45. package/dist/api/guards.js +23 -3
  46. package/dist/api/index.d.ts +3 -1
  47. package/dist/api/index.js +1 -0
  48. package/dist/api/permissions.d.ts +21 -2
  49. package/dist/api/permissions.js +50 -3
  50. package/dist/api/request-deadline.d.ts +59 -0
  51. package/dist/api/request-deadline.js +73 -0
  52. package/dist/api/resolve-references.js +8 -7
  53. package/dist/api/route-builder.d.ts +14 -0
  54. package/dist/api/route-builder.js +14 -1
  55. package/dist/api/schema.d.ts +11 -9
  56. package/dist/api/schema.js +2 -0
  57. package/dist/api/settings-helpers.d.ts +5 -1
  58. package/dist/api/settings-helpers.js +10 -2
  59. package/dist/api/types.d.ts +18 -0
  60. package/dist/api/users-constants.d.ts +6 -0
  61. package/dist/api/users-constants.js +6 -0
  62. package/dist/assets/asset-url.d.ts +28 -16
  63. package/dist/assets/asset-url.js +51 -25
  64. package/dist/assets/finalize.js +3 -0
  65. package/dist/assets/index.d.ts +1 -1
  66. package/dist/assets/materialize.d.ts +153 -0
  67. package/dist/assets/materialize.js +402 -0
  68. package/dist/assets/store-local.d.ts +19 -5
  69. package/dist/assets/store-local.js +69 -10
  70. package/dist/assets/store-s3.d.ts +38 -7
  71. package/dist/assets/store-s3.js +128 -22
  72. package/dist/assets/transform-directives.d.ts +52 -13
  73. package/dist/assets/transform-directives.js +87 -27
  74. package/dist/assets/transform.d.ts +3 -3
  75. package/dist/assets/transform.js +13 -8
  76. package/dist/assets/types.d.ts +31 -4
  77. package/dist/auth/cache.d.ts +1 -1
  78. package/dist/auth/cache.js +1 -1
  79. package/dist/auth/caching-auth-plugin.d.ts +2 -0
  80. package/dist/auth/caching-auth-plugin.js +12 -2
  81. package/dist/auth/file-based-auth-cache.d.ts +8 -0
  82. package/dist/auth/file-based-auth-cache.js +18 -8
  83. package/dist/auth/person-name.d.ts +5 -0
  84. package/dist/auth/person-name.js +8 -0
  85. package/dist/auth/plugin.d.ts +10 -1
  86. package/dist/auth/types.d.ts +2 -0
  87. package/dist/auth/user-metadata-lookup.d.ts +21 -0
  88. package/dist/auth/user-metadata-lookup.js +116 -0
  89. package/dist/authorization/branch.d.ts +1 -1
  90. package/dist/authorization/branch.js +12 -13
  91. package/dist/authorization/groups/schema.d.ts +4 -4
  92. package/dist/authorization/helpers.d.ts +8 -0
  93. package/dist/authorization/helpers.js +8 -0
  94. package/dist/authorization/index.d.ts +1 -1
  95. package/dist/authorization/index.js +1 -1
  96. package/dist/authorization/protected-branch.d.ts +5 -4
  97. package/dist/authorization/protected-branch.js +7 -5
  98. package/dist/branch-health.d.ts +35 -8
  99. package/dist/branch-health.js +54 -18
  100. package/dist/branch-metadata-error.d.ts +27 -0
  101. package/dist/branch-metadata-error.js +23 -0
  102. package/dist/branch-metadata-file.d.ts +205 -24
  103. package/dist/branch-metadata-file.js +126 -25
  104. package/dist/branch-metadata.d.ts +45 -6
  105. package/dist/branch-metadata.js +142 -50
  106. package/dist/branch-provisioning.d.ts +167 -0
  107. package/dist/branch-provisioning.js +501 -0
  108. package/dist/branch-registry.d.ts +8 -0
  109. package/dist/branch-registry.js +61 -9
  110. package/dist/branch-schema-cache.d.ts +29 -12
  111. package/dist/branch-schema-cache.js +64 -21
  112. package/dist/branch-sparse.d.ts +23 -0
  113. package/dist/branch-sparse.js +118 -0
  114. package/dist/branch-workspace.d.ts +42 -3
  115. package/dist/branch-workspace.js +239 -100
  116. package/dist/build/asset-refs.d.ts +94 -0
  117. package/dist/build/asset-refs.js +267 -0
  118. package/dist/build/generate-ai-content.js +5 -0
  119. package/dist/build/inline-flight.d.ts +37 -0
  120. package/dist/build/inline-flight.js +142 -0
  121. package/dist/cli/asset-refs.d.ts +52 -0
  122. package/dist/cli/asset-refs.js +194 -0
  123. package/dist/cli/cli.d.ts +15 -1
  124. package/dist/cli/cli.js +8543 -3193
  125. package/dist/cli/configured-asset-store.d.ts +12 -0
  126. package/dist/cli/configured-asset-store.js +46 -0
  127. package/dist/cli/generate-ai-content.js +4244 -1072
  128. package/dist/cli/init.js +217 -126
  129. package/dist/cli/project-detect.d.ts +2 -0
  130. package/dist/cli/project-detect.js +2 -0
  131. package/dist/cli/template-files/Dockerfile.cms.template +13 -5
  132. package/dist/cli/template-files/cms-stack.ts.template +16 -13
  133. package/dist/client.d.ts +1 -2
  134. package/dist/client.js +1 -2
  135. package/dist/comment-store.d.ts +25 -0
  136. package/dist/comment-store.js +43 -8
  137. package/dist/config/flatten.js +2 -0
  138. package/dist/config/helpers.js +2 -2
  139. package/dist/config/index.d.ts +1 -1
  140. package/dist/config/schemas/config.d.ts +12 -29
  141. package/dist/config/schemas/config.js +9 -12
  142. package/dist/config/schemas/field.d.ts +3 -0
  143. package/dist/config/schemas/field.js +58 -4
  144. package/dist/config/schemas/media.d.ts +4 -13
  145. package/dist/config/schemas/media.js +5 -8
  146. package/dist/config/schemas/url.d.ts +2 -20
  147. package/dist/config/schemas/url.js +2 -25
  148. package/dist/config/types.d.ts +116 -22
  149. package/dist/config/types.js +3 -9
  150. package/dist/config/validation.d.ts +25 -4
  151. package/dist/config/validation.js +116 -3
  152. package/dist/content-id-index.js +2 -2
  153. package/dist/content-index-generation.d.ts +1 -1
  154. package/dist/content-index-generation.js +1 -1
  155. package/dist/content-listing.d.ts +10 -1
  156. package/dist/content-listing.js +22 -5
  157. package/dist/content-reader.d.ts +9 -0
  158. package/dist/content-reader.js +16 -11
  159. package/dist/content-store.d.ts +75 -12
  160. package/dist/content-store.js +249 -86
  161. package/dist/content-tree.js +2 -2
  162. package/dist/context.js +5 -2
  163. package/dist/dangling-reference-log.d.ts +17 -0
  164. package/dist/dangling-reference-log.js +29 -0
  165. package/dist/dev-content-watcher.d.ts +1 -1
  166. package/dist/dev-content-watcher.js +1 -1
  167. package/dist/editor/BranchManager.d.ts +23 -7
  168. package/dist/editor/BranchManager.js +119 -54
  169. package/dist/editor/CanopyEditor.d.ts +1 -1
  170. package/dist/editor/CanopyEditor.js +1 -2
  171. package/dist/editor/CommentsPanel.d.ts +14 -0
  172. package/dist/editor/CommentsPanel.js +68 -43
  173. package/dist/editor/Editor.d.ts +11 -5
  174. package/dist/editor/Editor.js +891 -218
  175. package/dist/editor/EditorAuthGate.d.ts +7 -1
  176. package/dist/editor/EditorAuthGate.js +41 -9
  177. package/dist/editor/EntryNavigator.d.ts +22 -0
  178. package/dist/editor/EntryNavigator.js +353 -105
  179. package/dist/editor/FormHeader.d.ts +25 -0
  180. package/dist/editor/FormHeader.js +82 -0
  181. package/dist/editor/FormRenderer.d.ts +27 -3
  182. package/dist/editor/FormRenderer.js +171 -76
  183. package/dist/editor/PreviewFrame.d.ts +40 -0
  184. package/dist/editor/PreviewFrame.js +215 -0
  185. package/dist/editor/PreviewPane.d.ts +47 -0
  186. package/dist/editor/PreviewPane.js +147 -0
  187. package/dist/editor/PreviewToolbarTools.d.ts +22 -0
  188. package/dist/editor/PreviewToolbarTools.js +73 -0
  189. package/dist/editor/admin/SystemHealthPanel.d.ts +2 -1
  190. package/dist/editor/admin/SystemHealthPanel.js +163 -33
  191. package/dist/editor/admin/useSystemHealth.d.ts +18 -0
  192. package/dist/editor/admin/useSystemHealth.js +70 -1
  193. package/dist/editor/branch-status.d.ts +17 -0
  194. package/dist/editor/branch-status.js +29 -0
  195. package/dist/editor/canopy-path.d.ts +9 -6
  196. package/dist/editor/canopy-path.js +8 -11
  197. package/dist/editor/client-reference-resolver.d.ts +43 -16
  198. package/dist/editor/client-reference-resolver.js +155 -64
  199. package/dist/editor/comments/BranchComments.d.ts +1 -1
  200. package/dist/editor/comments/BranchComments.js +2 -2
  201. package/dist/editor/comments/CloseRefusedNotice.d.ts +4 -0
  202. package/dist/editor/comments/CloseRefusedNotice.js +7 -0
  203. package/dist/editor/comments/CommentThreadsPopover.d.ts +47 -0
  204. package/dist/editor/comments/CommentThreadsPopover.js +274 -0
  205. package/dist/editor/comments/CommentWriteAlert.d.ts +11 -0
  206. package/dist/editor/comments/CommentWriteAlert.js +13 -0
  207. package/dist/editor/comments/EntryComments.d.ts +10 -4
  208. package/dist/editor/comments/EntryComments.js +14 -17
  209. package/dist/editor/comments/FieldWrapper.d.ts +18 -4
  210. package/dist/editor/comments/FieldWrapper.js +62 -28
  211. package/dist/editor/comments/InlineCommentThread.d.ts +9 -2
  212. package/dist/editor/comments/InlineCommentThread.js +11 -47
  213. package/dist/editor/comments/ThreadCarousel.d.ts +1 -1
  214. package/dist/editor/comments/ThreadCarousel.js +18 -21
  215. package/dist/editor/comments/ThreadReplyControls.d.ts +28 -0
  216. package/dist/editor/comments/ThreadReplyControls.js +92 -0
  217. package/dist/editor/comments/field-comment-context.d.ts +14 -0
  218. package/dist/editor/comments/field-comment-context.js +14 -0
  219. package/dist/editor/comments/form-comments.d.ts +48 -0
  220. package/dist/editor/comments/form-comments.js +178 -0
  221. package/dist/editor/comments/item-anchor-copy.d.ts +6 -0
  222. package/dist/editor/comments/item-anchor-copy.js +6 -0
  223. package/dist/editor/comments/move-focus-if-unmoved.d.ts +6 -0
  224. package/dist/editor/comments/move-focus-if-unmoved.js +11 -0
  225. package/dist/editor/comments/popover-placement.d.ts +18 -0
  226. package/dist/editor/comments/popover-placement.js +58 -0
  227. package/dist/editor/comments/use-tracked-writes.d.ts +16 -0
  228. package/dist/editor/comments/use-tracked-writes.js +53 -0
  229. package/dist/editor/components/BranchAccessList.d.ts +15 -0
  230. package/dist/editor/components/BranchAccessList.js +52 -0
  231. package/dist/editor/components/BranchLockBanner.d.ts +22 -0
  232. package/dist/editor/components/BranchLockBanner.js +39 -0
  233. package/dist/editor/components/BranchPill.d.ts +32 -0
  234. package/dist/editor/components/BranchPill.js +33 -0
  235. package/dist/editor/components/BranchesDrawer.d.ts +11 -0
  236. package/dist/editor/components/BranchesDrawer.js +5 -0
  237. package/dist/editor/components/ConfirmDeleteModal.d.ts +6 -1
  238. package/dist/editor/components/ConfirmDeleteModal.js +3 -2
  239. package/dist/editor/components/DestructiveMenuItem.d.ts +7 -0
  240. package/dist/editor/components/DestructiveMenuItem.js +8 -0
  241. package/dist/editor/components/EditorCrashScreen.d.ts +17 -0
  242. package/dist/editor/components/EditorCrashScreen.js +22 -0
  243. package/dist/editor/components/EditorErrorBoundary.d.ts +38 -0
  244. package/dist/editor/components/EditorErrorBoundary.js +40 -0
  245. package/dist/editor/components/EditorHeader.d.ts +56 -106
  246. package/dist/editor/components/EditorHeader.js +130 -131
  247. package/dist/editor/components/EditorRail.d.ts +49 -0
  248. package/dist/editor/components/EditorRail.js +65 -0
  249. package/dist/editor/components/EntryBreadcrumb.d.ts +33 -0
  250. package/dist/editor/components/EntryBreadcrumb.js +56 -0
  251. package/dist/editor/components/EntryCreateModal.d.ts +12 -7
  252. package/dist/editor/components/EntryCreateModal.js +51 -57
  253. package/dist/editor/components/NoEditPermissionNotice.d.ts +6 -5
  254. package/dist/editor/components/NoEditPermissionNotice.js +5 -4
  255. package/dist/editor/components/QuickOpenPalette.d.ts +22 -0
  256. package/dist/editor/components/QuickOpenPalette.js +66 -0
  257. package/dist/editor/components/ReadOnlyDraftNotice.d.ts +11 -0
  258. package/dist/editor/components/ReadOnlyDraftNotice.js +12 -0
  259. package/dist/editor/components/ReasonedMenuItem.d.ts +15 -0
  260. package/dist/editor/components/ReasonedMenuItem.js +15 -0
  261. package/dist/editor/components/ReferencedByList.d.ts +11 -0
  262. package/dist/editor/components/ReferencedByList.js +28 -0
  263. package/dist/editor/components/RelativeTime.d.ts +10 -0
  264. package/dist/editor/components/RelativeTime.js +10 -0
  265. package/dist/editor/components/RenameEntryModal.d.ts +9 -1
  266. package/dist/editor/components/RenameEntryModal.js +19 -31
  267. package/dist/editor/components/RetryToastMessage.d.ts +9 -0
  268. package/dist/editor/components/RetryToastMessage.js +6 -0
  269. package/dist/editor/components/SaveAllResult.d.ts +25 -0
  270. package/dist/editor/components/SaveAllResult.js +86 -0
  271. package/dist/editor/components/SaveSplitButton.d.ts +30 -0
  272. package/dist/editor/components/SaveSplitButton.js +108 -0
  273. package/dist/editor/components/StagedChangesDrawer.d.ts +39 -0
  274. package/dist/editor/components/StagedChangesDrawer.js +92 -0
  275. package/dist/editor/components/UnavailableEntryNotice.d.ts +6 -0
  276. package/dist/editor/components/UnavailableEntryNotice.js +9 -0
  277. package/dist/editor/components/UnavailableTypeMessage.d.ts +6 -0
  278. package/dist/editor/components/UnavailableTypeMessage.js +9 -0
  279. package/dist/editor/components/UndoToastMessage.d.ts +18 -0
  280. package/dist/editor/components/UndoToastMessage.js +10 -0
  281. package/dist/editor/components/UserBadge.d.ts +2 -5
  282. package/dist/editor/components/UserBadge.js +62 -33
  283. package/dist/editor/components/ValidationSummary.d.ts +14 -0
  284. package/dist/editor/components/ValidationSummary.js +24 -0
  285. package/dist/editor/components/index.d.ts +2 -2
  286. package/dist/editor/components/index.js +2 -2
  287. package/dist/editor/context/ApiClientContext.d.ts +8 -2
  288. package/dist/editor/context/ApiClientContext.js +19 -8
  289. package/dist/editor/context/AssetContext.d.ts +13 -35
  290. package/dist/editor/context/AssetContext.js +12 -31
  291. package/dist/editor/context/EditorStateContext.d.ts +4 -4
  292. package/dist/editor/context/EditorStateContext.js +4 -4
  293. package/dist/editor/context/SWRProvider.d.ts +3 -3
  294. package/dist/editor/context/SiteMdxAllowContext.d.ts +4 -0
  295. package/dist/editor/context/SiteMdxAllowContext.js +7 -0
  296. package/dist/editor/context/index.d.ts +2 -1
  297. package/dist/editor/context/index.js +2 -1
  298. package/dist/editor/contrast.d.ts +19 -0
  299. package/dist/editor/contrast.js +41 -0
  300. package/dist/editor/copy.d.ts +18 -0
  301. package/dist/editor/copy.js +18 -0
  302. package/dist/editor/editor-config.js +16 -0
  303. package/dist/editor/editor-utils.d.ts +34 -8
  304. package/dist/editor/editor-utils.js +42 -20
  305. package/dist/editor/entry-search.d.ts +20 -0
  306. package/dist/editor/entry-search.js +34 -0
  307. package/dist/editor/field-breadcrumb.d.ts +12 -0
  308. package/dist/editor/field-breadcrumb.js +85 -0
  309. package/dist/editor/field-props.d.ts +86 -0
  310. package/dist/editor/field-props.js +27 -0
  311. package/dist/editor/field-target.d.ts +30 -0
  312. package/dist/editor/field-target.js +77 -0
  313. package/dist/editor/fields/BlockField.d.ts +12 -0
  314. package/dist/editor/fields/BlockField.js +89 -73
  315. package/dist/editor/fields/CodeField.d.ts +2 -0
  316. package/dist/editor/fields/CodeField.js +6 -2
  317. package/dist/editor/fields/DateTimeField.d.ts +4 -29
  318. package/dist/editor/fields/DateTimeField.js +79 -41
  319. package/dist/editor/fields/ExpandAllButton.d.ts +6 -0
  320. package/dist/editor/fields/ExpandAllButton.js +4 -0
  321. package/dist/editor/fields/FieldCrashFallback.d.ts +17 -0
  322. package/dist/editor/fields/FieldCrashFallback.js +30 -0
  323. package/dist/editor/fields/FieldLabel.d.ts +37 -0
  324. package/dist/editor/fields/FieldLabel.js +19 -0
  325. package/dist/editor/fields/ImageField.d.ts +4 -0
  326. package/dist/editor/fields/ImageField.js +29 -14
  327. package/dist/editor/fields/InlineGroupField.js +4 -3
  328. package/dist/editor/fields/ItemList.d.ts +42 -0
  329. package/dist/editor/fields/ItemList.js +130 -0
  330. package/dist/editor/fields/MarkdownField.d.ts +32 -0
  331. package/dist/editor/fields/MarkdownField.js +223 -46
  332. package/dist/editor/fields/MdxImageDialog.js +1 -1
  333. package/dist/editor/fields/NumberField.d.ts +2 -0
  334. package/dist/editor/fields/NumberField.js +8 -5
  335. package/dist/editor/fields/NumberListField.d.ts +2 -0
  336. package/dist/editor/fields/NumberListField.js +19 -16
  337. package/dist/editor/fields/ObjectField.d.ts +2 -7
  338. package/dist/editor/fields/ObjectField.js +15 -5
  339. package/dist/editor/fields/ObjectListField.d.ts +29 -0
  340. package/dist/editor/fields/ObjectListField.js +57 -0
  341. package/dist/editor/fields/ReferenceField.d.ts +2 -0
  342. package/dist/editor/fields/ReferenceField.js +34 -9
  343. package/dist/editor/fields/SelectField.d.ts +2 -0
  344. package/dist/editor/fields/SelectField.js +11 -2
  345. package/dist/editor/fields/StringListField.d.ts +4 -1
  346. package/dist/editor/fields/StringListField.js +22 -8
  347. package/dist/editor/fields/TextField.d.ts +2 -0
  348. package/dist/editor/fields/TextField.js +6 -2
  349. package/dist/editor/fields/ToggleField.d.ts +2 -0
  350. package/dist/editor/fields/ToggleField.js +8 -3
  351. package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +1 -1
  352. package/dist/editor/fields/entry-link/InsertEntryLink.js +1 -1
  353. package/dist/editor/fields/form-expansion.d.ts +9 -0
  354. package/dist/editor/fields/form-expansion.js +2 -0
  355. package/dist/editor/fields/item-expansion.d.ts +15 -0
  356. package/dist/editor/fields/item-expansion.js +60 -0
  357. package/dist/editor/fields/list-item-keys.d.ts +8 -0
  358. package/dist/editor/fields/list-item-keys.js +50 -0
  359. package/dist/editor/fields/list-item-title.d.ts +20 -0
  360. package/dist/editor/fields/list-item-title.js +85 -0
  361. package/dist/editor/fields/markdown-export-options.d.ts +2 -0
  362. package/dist/editor/fields/markdown-export-options.js +8 -0
  363. package/dist/editor/fields/markdown-fidelity-visitors.d.ts +11 -0
  364. package/dist/editor/fields/markdown-fidelity-visitors.js +160 -0
  365. package/dist/editor/fields/mdx-jsx-support.d.ts +5 -1
  366. package/dist/editor/fields/mdx-jsx-support.js +157 -69
  367. package/dist/editor/fields/rich-text-failures.d.ts +5 -0
  368. package/dist/editor/fields/rich-text-failures.js +26 -0
  369. package/dist/editor/fields/toolbar-wrap.d.ts +7 -0
  370. package/dist/editor/fields/toolbar-wrap.js +35 -0
  371. package/dist/editor/form-outline.d.ts +13 -0
  372. package/dist/editor/form-outline.js +70 -0
  373. package/dist/editor/group-manager/GroupCard.d.ts +4 -1
  374. package/dist/editor/group-manager/GroupCard.js +12 -3
  375. package/dist/editor/group-manager/GroupForm.js +1 -1
  376. package/dist/editor/group-manager/InternalGroupsTab.d.ts +2 -1
  377. package/dist/editor/group-manager/InternalGroupsTab.js +4 -4
  378. package/dist/editor/group-manager/MemberList.d.ts +2 -1
  379. package/dist/editor/group-manager/MemberList.js +15 -4
  380. package/dist/editor/group-manager/hooks/useGroupState.d.ts +5 -0
  381. package/dist/editor/group-manager/hooks/useGroupState.js +30 -7
  382. package/dist/editor/group-manager/index.js +34 -5
  383. package/dist/editor/group-manager/types.d.ts +5 -2
  384. package/dist/editor/hooks/create-branch-request.d.ts +20 -0
  385. package/dist/editor/hooks/create-branch-request.js +54 -0
  386. package/dist/editor/hooks/save-all.d.ts +68 -0
  387. package/dist/editor/hooks/save-all.js +61 -0
  388. package/dist/editor/hooks/useBranchActions.d.ts +11 -1
  389. package/dist/editor/hooks/useBranchActions.js +42 -19
  390. package/dist/editor/hooks/useBranchManager.d.ts +21 -0
  391. package/dist/editor/hooks/useBranchManager.js +321 -125
  392. package/dist/editor/hooks/useBranchesData.d.ts +8 -4
  393. package/dist/editor/hooks/useBranchesData.js +9 -5
  394. package/dist/editor/hooks/useCommentSystem.d.ts +14 -2
  395. package/dist/editor/hooks/useCommentSystem.js +79 -55
  396. package/dist/editor/hooks/useCommentsData.d.ts +11 -7
  397. package/dist/editor/hooks/useCommentsData.js +20 -19
  398. package/dist/editor/hooks/useDraftManager.d.ts +77 -3
  399. package/dist/editor/hooks/useDraftManager.js +887 -94
  400. package/dist/editor/hooks/useEditorLayout.d.ts +53 -7
  401. package/dist/editor/hooks/useEditorLayout.js +98 -20
  402. package/dist/editor/hooks/useEntriesData.d.ts +2 -1
  403. package/dist/editor/hooks/useEntriesData.js +18 -4
  404. package/dist/editor/hooks/useEntryManager.d.ts +41 -2
  405. package/dist/editor/hooks/useEntryManager.js +227 -41
  406. package/dist/editor/hooks/useGroupManager.js +13 -16
  407. package/dist/editor/hooks/usePermissionManager.js +14 -6
  408. package/dist/editor/hooks/usePreviewMarks.d.ts +21 -0
  409. package/dist/editor/hooks/usePreviewMarks.js +33 -0
  410. package/dist/editor/hooks/useReferenceResolution.d.ts +10 -3
  411. package/dist/editor/hooks/useReferenceResolution.js +34 -177
  412. package/dist/editor/hooks/useSchemaManager.d.ts +25 -6
  413. package/dist/editor/hooks/useSchemaManager.js +97 -39
  414. package/dist/editor/hooks/useUserMetadata.d.ts +14 -4
  415. package/dist/editor/hooks/useUserMetadata.js +76 -47
  416. package/dist/editor/hooks/user-metadata-batcher.d.ts +8 -0
  417. package/dist/editor/hooks/user-metadata-batcher.js +33 -0
  418. package/dist/editor/media/AssetCard.d.ts +6 -7
  419. package/dist/editor/media/AssetCard.js +24 -31
  420. package/dist/editor/media/AssetListRow.d.ts +16 -0
  421. package/dist/editor/media/AssetListRow.js +41 -0
  422. package/dist/editor/media/CropStep.js +6 -1
  423. package/dist/editor/media/MediaLibrary.d.ts +4 -3
  424. package/dist/editor/media/MediaLibrary.js +11 -4
  425. package/dist/editor/media/MediaLibraryBody.d.ts +3 -3
  426. package/dist/editor/media/MediaLibraryBody.js +195 -80
  427. package/dist/editor/media/MediaLibrarySkeleton.d.ts +6 -0
  428. package/dist/editor/media/MediaLibrarySkeleton.js +14 -0
  429. package/dist/editor/media/crop-math.d.ts +3 -4
  430. package/dist/editor/media/crop-math.js +11 -22
  431. package/dist/editor/media/editor-image-src.d.ts +8 -0
  432. package/dist/editor/media/editor-image-src.js +10 -0
  433. package/dist/editor/media/format-bytes.d.ts +2 -0
  434. package/dist/editor/media/format-bytes.js +23 -0
  435. package/dist/editor/media/media-library-prefs.d.ts +23 -0
  436. package/dist/editor/media/media-library-prefs.js +49 -0
  437. package/dist/editor/media/media-library-view.d.ts +34 -0
  438. package/dist/editor/media/media-library-view.js +40 -0
  439. package/dist/editor/media/useAssetLibrary.d.ts +30 -0
  440. package/dist/editor/media/useAssetLibrary.js +126 -0
  441. package/dist/editor/media/useStableAssetOrder.d.ts +11 -0
  442. package/dist/editor/media/useStableAssetOrder.js +37 -0
  443. package/dist/editor/media/useThumbnailFailure.d.ts +6 -0
  444. package/dist/editor/media/useThumbnailFailure.js +16 -0
  445. package/dist/editor/permission-manager/GroupSelector.js +2 -2
  446. package/dist/editor/permission-manager/PermissionEditor.d.ts +1 -1
  447. package/dist/editor/permission-manager/PermissionEditor.js +15 -4
  448. package/dist/editor/permission-manager/PermissionLevelBadge.js +1 -1
  449. package/dist/editor/permission-manager/PermissionTree.d.ts +1 -1
  450. package/dist/editor/permission-manager/UserSelector.d.ts +1 -1
  451. package/dist/editor/permission-manager/UserSelector.js +3 -3
  452. package/dist/editor/permission-manager/index.js +12 -3
  453. package/dist/editor/permission-manager/types.d.ts +5 -2
  454. package/dist/editor/platform.d.ts +3 -0
  455. package/dist/editor/platform.js +9 -0
  456. package/dist/editor/preview-asset-base.d.ts +9 -0
  457. package/dist/editor/preview-asset-base.js +17 -0
  458. package/dist/editor/preview-bridge.d.ts +51 -27
  459. package/dist/editor/preview-bridge.js +103 -144
  460. package/dist/editor/preview-marks.d.ts +15 -0
  461. package/dist/editor/preview-marks.js +74 -0
  462. package/dist/editor/preview-path.d.ts +12 -0
  463. package/dist/editor/preview-path.js +45 -1
  464. package/dist/editor/preview-scale.d.ts +27 -0
  465. package/dist/editor/preview-scale.js +34 -0
  466. package/dist/editor/raw-asset-base.d.ts +5 -0
  467. package/dist/editor/raw-asset-base.js +14 -0
  468. package/dist/editor/relative-time.d.ts +4 -4
  469. package/dist/editor/relative-time.js +16 -8
  470. package/dist/editor/schema-editor/CollectionEditor.d.ts +20 -3
  471. package/dist/editor/schema-editor/CollectionEditor.js +361 -62
  472. package/dist/editor/schema-editor/EntryTypeEditor.d.ts +5 -1
  473. package/dist/editor/schema-editor/EntryTypeEditor.js +11 -6
  474. package/dist/editor/schema-editor/collection-draft.d.ts +22 -0
  475. package/dist/editor/schema-editor/collection-draft.js +40 -0
  476. package/dist/editor/schema-editor/index.d.ts +1 -1
  477. package/dist/editor/schema-label.d.ts +10 -0
  478. package/dist/editor/schema-label.js +17 -0
  479. package/dist/editor/settings-conflict.d.ts +5 -0
  480. package/dist/editor/settings-conflict.js +8 -0
  481. package/dist/editor/shell/ActivityRail.d.ts +37 -0
  482. package/dist/editor/shell/ActivityRail.js +91 -0
  483. package/dist/editor/shell/EditorShell.d.ts +17 -0
  484. package/dist/editor/shell/EditorShell.js +31 -0
  485. package/dist/editor/shell/PaneLayout.d.ts +28 -0
  486. package/dist/editor/shell/PaneLayout.js +116 -0
  487. package/dist/editor/shell/SidePanelHost.d.ts +35 -0
  488. package/dist/editor/shell/SidePanelHost.js +184 -0
  489. package/dist/editor/shell/TopBar.d.ts +12 -0
  490. package/dist/editor/shell/TopBar.js +14 -0
  491. package/dist/editor/shell/host-chrome.d.ts +16 -0
  492. package/dist/editor/shell/host-chrome.js +69 -0
  493. package/dist/editor/slug.d.ts +16 -0
  494. package/dist/editor/slug.js +43 -0
  495. package/dist/editor/theme.d.ts +2 -1
  496. package/dist/editor/theme.js +314 -13
  497. package/dist/editor/unanswered.d.ts +17 -0
  498. package/dist/editor/unanswered.js +26 -0
  499. package/dist/editor/unavailable-entry-type.d.ts +20 -0
  500. package/dist/editor/unavailable-entry-type.js +28 -0
  501. package/dist/editor/undo-removal.d.ts +57 -0
  502. package/dist/editor/undo-removal.js +163 -0
  503. package/dist/editor/user-display.d.ts +4 -0
  504. package/dist/editor/user-display.js +5 -0
  505. package/dist/editor/utils/confirm-modal.d.ts +8 -0
  506. package/dist/editor/utils/confirm-modal.js +12 -0
  507. package/dist/editor/utils/editor-errors.d.ts +19 -0
  508. package/dist/editor/utils/editor-errors.js +32 -0
  509. package/dist/editor/utils/focus-return.d.ts +56 -0
  510. package/dist/editor/utils/focus-return.js +106 -0
  511. package/dist/entry-link-resolver.d.ts +11 -1
  512. package/dist/entry-link-resolver.js +65 -5
  513. package/dist/entry-schema-registry.d.ts +3 -3
  514. package/dist/entry-schema-registry.js +7 -4
  515. package/dist/entry-schema.d.ts +49 -3
  516. package/dist/entry-schema.js +18 -0
  517. package/dist/git-manager.d.ts +172 -10
  518. package/dist/git-manager.js +449 -107
  519. package/dist/github-service.d.ts +39 -2
  520. package/dist/github-service.js +101 -7
  521. package/dist/http/handler.js +74 -9
  522. package/dist/http/types.d.ts +2 -0
  523. package/dist/http/worker-not-ready.d.ts +9 -4
  524. package/dist/http/worker-not-ready.js +59 -6
  525. package/dist/index.d.ts +2 -0
  526. package/dist/index.js +3 -0
  527. package/dist/item-keys.d.ts +46 -0
  528. package/dist/item-keys.js +235 -0
  529. package/dist/operating-mode/editor-mode-check.d.ts +15 -0
  530. package/dist/operating-mode/editor-mode-check.js +24 -0
  531. package/dist/operating-mode/mode-env.d.ts +13 -14
  532. package/dist/operating-mode/mode-env.js +13 -14
  533. package/dist/operating-mode/types.d.ts +2 -2
  534. package/dist/paths/branch.d.ts +0 -1
  535. package/dist/paths/branch.js +1 -8
  536. package/dist/paths/index.d.ts +1 -1
  537. package/dist/paths/index.js +1 -1
  538. package/dist/paths/validation.js +0 -2
  539. package/dist/preview.d.ts +8 -0
  540. package/dist/preview.js +7 -0
  541. package/dist/reference-resolver.js +1 -0
  542. package/dist/resolve-canopy-user.d.ts +1 -1
  543. package/dist/resolve-canopy-user.js +1 -1
  544. package/dist/schema/meta-loader.d.ts +18 -8
  545. package/dist/schema/meta-loader.js +42 -9
  546. package/dist/schema/registry-fingerprint.d.ts +14 -0
  547. package/dist/schema/registry-fingerprint.js +63 -0
  548. package/dist/schema/resolver.d.ts +7 -4
  549. package/dist/schema/resolver.js +7 -6
  550. package/dist/schema/schema-store.d.ts +7 -10
  551. package/dist/schema/schema-store.js +13 -11
  552. package/dist/schema/schema-unavailable-error.d.ts +11 -0
  553. package/dist/schema/schema-unavailable-error.js +15 -0
  554. package/dist/schema/types.d.ts +22 -0
  555. package/dist/schema-registry-record.d.ts +35 -0
  556. package/dist/schema-registry-record.js +134 -0
  557. package/dist/server.d.ts +79 -9
  558. package/dist/server.js +62 -8
  559. package/dist/services.d.ts +33 -5
  560. package/dist/services.js +155 -37
  561. package/dist/settings-workspace.d.ts +23 -3
  562. package/dist/settings-workspace.js +159 -45
  563. package/dist/stamp-on-publish.d.ts +36 -0
  564. package/dist/stamp-on-publish.js +71 -0
  565. package/dist/static/index.d.ts +58 -5
  566. package/dist/static/index.js +133 -3
  567. package/dist/static/seo.d.ts +5 -5
  568. package/dist/static/seo.js +5 -5
  569. package/dist/submission-attribution.d.ts +8 -0
  570. package/dist/submission-attribution.js +22 -0
  571. package/dist/system-users.d.ts +12 -0
  572. package/dist/system-users.js +12 -0
  573. package/dist/task-queue/cms-task-queue.d.ts +1 -1
  574. package/dist/task-queue/cms-task-queue.js +1 -1
  575. package/dist/task-queue/index.d.ts +1 -1
  576. package/dist/task-queue/index.js +1 -1
  577. package/dist/task-queue/task-queue.d.ts +6 -0
  578. package/dist/task-queue/task-queue.js +29 -0
  579. package/dist/task-queue/worker-status.d.ts +35 -9
  580. package/dist/task-queue/worker-status.js +84 -17
  581. package/dist/types.d.ts +90 -9
  582. package/dist/url-collision.js +3 -3
  583. package/dist/url-exclusivity-fixtures.d.ts +8 -1
  584. package/dist/url-exclusivity-fixtures.js +11 -1
  585. package/dist/user.js +2 -1
  586. package/dist/utils/atomic-write.js +2 -1
  587. package/dist/utils/base-branch.d.ts +13 -0
  588. package/dist/utils/base-branch.js +23 -0
  589. package/dist/utils/content-serialize.d.ts +15 -7
  590. package/dist/utils/content-serialize.js +353 -51
  591. package/dist/utils/content-write-lock.d.ts +6 -1
  592. package/dist/utils/content-write-lock.js +21 -6
  593. package/dist/utils/debug.d.ts +1 -1
  594. package/dist/utils/debug.js +5 -3
  595. package/dist/utils/error.js +13 -2
  596. package/dist/utils/escape-text.d.ts +7 -0
  597. package/dist/utils/escape-text.js +27 -0
  598. package/dist/utils/fs.d.ts +12 -4
  599. package/dist/utils/fs.js +18 -5
  600. package/dist/utils/git.d.ts +21 -1
  601. package/dist/utils/git.js +63 -8
  602. package/dist/utils/json-source-splice.d.ts +14 -0
  603. package/dist/utils/json-source-splice.js +120 -0
  604. package/dist/utils/markdown-body-splice.d.ts +39 -0
  605. package/dist/utils/markdown-body-splice.js +421 -0
  606. package/dist/utils/occ-json-write.js +18 -12
  607. package/dist/utils/provision-log.d.ts +25 -0
  608. package/dist/utils/provision-log.js +49 -0
  609. package/dist/utils/provisioning-lock.d.ts +32 -1
  610. package/dist/utils/provisioning-lock.js +78 -32
  611. package/dist/utils/sanitize-href.d.ts +9 -23
  612. package/dist/utils/sanitize-href.js +14 -28
  613. package/dist/utils/title-field.d.ts +10 -0
  614. package/dist/utils/title-field.js +47 -0
  615. package/dist/utils/yaml-source-splice.d.ts +58 -0
  616. package/dist/utils/yaml-source-splice.js +515 -0
  617. package/dist/validation/block-structural-keys.d.ts +1 -1
  618. package/dist/validation/block-structural-keys.js +1 -1
  619. package/dist/validation/deletion-checker.d.ts +21 -29
  620. package/dist/validation/deletion-checker.js +41 -117
  621. package/dist/validation/entry-validator.d.ts +6 -10
  622. package/dist/validation/entry-validator.js +19 -26
  623. package/dist/validation/field-traversal.d.ts +22 -0
  624. package/dist/validation/field-traversal.js +39 -8
  625. package/dist/validation/markdown-safety.d.ts +68 -0
  626. package/dist/validation/markdown-safety.js +596 -0
  627. package/dist/validation/mdx-allowlist.d.ts +55 -0
  628. package/dist/validation/mdx-allowlist.js +183 -0
  629. package/dist/validation/reference-validator.d.ts +2 -0
  630. package/dist/validation/reference-validator.js +3 -0
  631. package/dist/version.d.ts +1 -1
  632. package/dist/version.js +1 -1
  633. package/dist/worker/branch-policy.d.ts +124 -0
  634. package/dist/worker/branch-policy.js +243 -0
  635. package/dist/worker/bundle-header.d.ts +117 -0
  636. package/dist/worker/bundle-header.js +253 -0
  637. package/dist/worker/cms-worker.d.ts +236 -142
  638. package/dist/worker/cms-worker.js +772 -285
  639. package/dist/worker/gateway-egress.d.ts +43 -0
  640. package/dist/worker/gateway-egress.js +209 -0
  641. package/dist/worker/gateway-instance.d.ts +42 -0
  642. package/dist/worker/gateway-instance.js +129 -0
  643. package/dist/worker/git-process.d.ts +81 -0
  644. package/dist/worker/git-process.js +234 -0
  645. package/dist/worker/git-sync.d.ts +34 -17
  646. package/dist/worker/git-sync.js +394 -131
  647. package/dist/worker/github-auth.d.ts +9 -9
  648. package/dist/worker/github-auth.js +13 -15
  649. package/dist/worker/github-gateway-http.d.ts +62 -0
  650. package/dist/worker/github-gateway-http.js +977 -0
  651. package/dist/worker/github-gateway-wire.d.ts +590 -0
  652. package/dist/worker/github-gateway-wire.js +488 -0
  653. package/dist/worker/github-gateway.d.ts +238 -0
  654. package/dist/worker/github-gateway.js +726 -0
  655. package/dist/worker/github-mirror-upkeep.d.ts +75 -0
  656. package/dist/worker/github-mirror-upkeep.js +266 -0
  657. package/dist/worker/github-mirror.d.ts +364 -0
  658. package/dist/worker/github-mirror.js +1030 -0
  659. package/dist/worker/github-transfer.d.ts +56 -0
  660. package/dist/worker/github-transfer.js +444 -0
  661. package/dist/worker/history-rewrite.d.ts +4 -5
  662. package/dist/worker/history-rewrite.js +12 -17
  663. package/dist/worker/provisioned-workspace.d.ts +5 -5
  664. package/dist/worker/provisioned-workspace.js +5 -5
  665. package/dist/worker/rebase.d.ts +1 -1
  666. package/dist/worker/rebase.js +58 -54
  667. package/dist/worker/remote-git-maintenance.d.ts +3 -0
  668. package/dist/worker/remote-git-maintenance.js +12 -0
  669. package/dist/worker/schema-gate.d.ts +49 -0
  670. package/dist/worker/schema-gate.js +226 -0
  671. package/dist/worker/self-checks.d.ts +107 -0
  672. package/dist/worker/self-checks.js +358 -0
  673. package/dist/worker/shared-repo-git.d.ts +96 -0
  674. package/dist/worker/shared-repo-git.js +459 -0
  675. package/dist/worker/sparse-cone.d.ts +20 -0
  676. package/dist/worker/sparse-cone.js +136 -0
  677. package/dist/worker/stale-mirror.d.ts +82 -0
  678. package/dist/worker/stale-mirror.js +130 -0
  679. package/dist/worker/task-runner.d.ts +5 -3
  680. package/dist/worker/task-runner.js +287 -186
  681. package/dist/worker/worker-context.d.ts +36 -37
  682. package/package.json +35 -7
  683. package/dist/editor/EditorPanes.d.ts +0 -10
  684. package/dist/editor/EditorPanes.js +0 -98
  685. package/dist/editor/components/EditorFooter.d.ts +0 -11
  686. package/dist/editor/components/EditorFooter.js +0 -9
  687. package/dist/editor/components/EditorSidebar.d.ts +0 -67
  688. package/dist/editor/components/EditorSidebar.js +0 -22
@@ -1,51 +1,80 @@
1
- import { useEffect, useState } from 'react';
1
+ import { useCallback } from 'react';
2
+ import useSWR, { useSWRConfig } from 'swr';
3
+ import { isSystemUserId } from '../../system-users.js';
4
+ import { SYSTEM_USER_LABEL } from '../user-display.js';
5
+ const ANONYMOUS_USER = { id: 'anonymous', name: 'Anonymous', email: 'public' };
6
+ /** A bot has no directory entry to look up; it is answered here, by name. */
7
+ const systemUser = (userId) => ({
8
+ id: userId,
9
+ name: SYSTEM_USER_LABEL,
10
+ email: '',
11
+ });
12
+ const userMetadataKey = (userId) => `canopy:user:${userId}`;
13
+ const isUserRecord = (value) => typeof value === 'object' &&
14
+ value !== null &&
15
+ 'id' in value &&
16
+ typeof value.id === 'string' &&
17
+ 'name' in value &&
18
+ typeof value.name === 'string';
19
+ const cachedRecord = (cache, key) => {
20
+ const data = cache.get(key)?.data;
21
+ return isUserRecord(data) ? data : null;
22
+ };
2
23
  /**
3
- * Fetches user metadata using the provided getter function.
4
- *
5
- * @param cachedUser - Optional: if provided, returns immediately without fetching
24
+ * Caches profiles a response carried, keeping a cached record's email. A profile matching the
25
+ * cached record is not written: any write makes SWR discard a lookup in flight, email included. A
26
+ * user with no record, or whose written record still has no email, is refetched once the writes
27
+ * settle, since the seed may have discarded a lookup; that refetch is detached, so the caller
28
+ * never waits on the directory.
6
29
  */
7
- export function useUserMetadata(userId, getUserMetadata, cachedUser) {
8
- const [userMetadata, setUserMetadata] = useState(cachedUser || null);
9
- const [isLoading, setIsLoading] = useState(!cachedUser);
10
- const [error, setError] = useState(null);
11
- useEffect(() => {
12
- if (cachedUser)
13
- return;
14
- // Special case: anonymous user
15
- if (userId === 'anonymous') {
16
- setUserMetadata({
17
- id: 'anonymous',
18
- name: 'Anonymous',
19
- email: 'public',
20
- });
21
- setIsLoading(false);
30
+ export async function seedUserProfiles(swr, users) {
31
+ const { cache, mutate } = swr;
32
+ const emailless = new Set();
33
+ await Promise.all(users.map(async (user) => {
34
+ const key = userMetadataKey(user.id);
35
+ const record = cachedRecord(cache, key);
36
+ if (record && record.name === user.name && record.avatarUrl === user.avatarUrl)
22
37
  return;
23
- }
24
- let cancelled = false;
25
- const fetchUser = async () => {
26
- setIsLoading(true);
27
- try {
28
- const result = await getUserMetadata(userId);
29
- if (!cancelled) {
30
- setUserMetadata(result);
31
- setError(null);
32
- }
33
- }
34
- catch (err) {
35
- if (!cancelled) {
36
- setError(err instanceof Error ? err : new Error('Failed to fetch user'));
37
- }
38
- }
39
- finally {
40
- if (!cancelled) {
41
- setIsLoading(false);
42
- }
43
- }
44
- };
45
- fetchUser();
46
- return () => {
47
- cancelled = true;
48
- };
49
- }, [userId, getUserMetadata, cachedUser]);
50
- return { userMetadata, isLoading, error };
38
+ const merged = { email: '', ...record, ...user };
39
+ if (!record || !merged.email)
40
+ emailless.add(key);
41
+ await mutate(key, merged, { revalidate: false });
42
+ }));
43
+ for (const key of emailless)
44
+ void mutate(key);
45
+ }
46
+ /** Answers from the cache with no request and no email, for a reader who may not look users up. */
47
+ export function useSeededUserResolver() {
48
+ const { cache } = useSWRConfig();
49
+ return useCallback((userId) => {
50
+ const record = cachedRecord(cache, userMetadataKey(userId));
51
+ return Promise.resolve(record ? { ...record, email: '' } : null);
52
+ }, [cache]);
53
+ }
54
+ /** One user's metadata under a per-user SWR key, so every badge for that user shares a fetch. */
55
+ export function useUserMetadata(userId, getUserMetadata, cachedUser) {
56
+ const skip = cachedUser !== undefined || userId === 'anonymous' || isSystemUserId(userId);
57
+ const { cache } = useSWRConfig();
58
+ const key = userMetadataKey(userId);
59
+ const { data, error, isLoading } = useSWR(skip ? null : key,
60
+ // A lookup that answers null keeps the record already cached, such as a seeded name.
61
+ async () => (await getUserMetadata(userId)) ?? cachedRecord(cache, key));
62
+ if (cachedUser)
63
+ return { userMetadata: cachedUser, isLoading: false, error: null };
64
+ if (userId === 'anonymous')
65
+ return { userMetadata: ANONYMOUS_USER, isLoading: false, error: null };
66
+ if (isSystemUserId(userId)) {
67
+ return { userMetadata: systemUser(userId), isLoading: false, error: null };
68
+ }
69
+ return {
70
+ userMetadata: data ?? null,
71
+ // A seed can land while the lookup is in flight; SWR still reports that lookup as loading.
72
+ isLoading: isLoading && data === undefined,
73
+ // A record to show outranks a failed lookup, so a seeded name survives it.
74
+ error: error === undefined || data
75
+ ? null
76
+ : error instanceof Error
77
+ ? error
78
+ : new Error('Failed to fetch user'),
79
+ };
51
80
  }
@@ -0,0 +1,8 @@
1
+ import type { ApiClient } from '../context/index.js';
2
+ import type { UserSearchResult } from '../../auth/types.js';
3
+ /**
4
+ * Sends the ids asked for within one window as one `POST /users/batch`, split at the cap. Pass
5
+ * it to useUserMetadata, whose per-user SWR key is the cache. A failed request rejects every id
6
+ * in it, so SWR keeps the last good record; an id a successful response omits resolves `null`.
7
+ */
8
+ export declare function createUserMetadataBatcher(apiClient: ApiClient): (userId: string) => Promise<UserSearchResult | null>;
@@ -0,0 +1,33 @@
1
+ import DataLoader from 'dataloader';
2
+ import { MAX_USER_METADATA_BATCH } from '../../api/users-constants.js';
3
+ /** Long enough to gather every badge one render mounts. */
4
+ const BATCH_WINDOW_MS = 10;
5
+ /**
6
+ * Sends the ids asked for within one window as one `POST /users/batch`, split at the cap. Pass
7
+ * it to useUserMetadata, whose per-user SWR key is the cache. A failed request rejects every id
8
+ * in it, so SWR keeps the last good record; an id a successful response omits resolves `null`.
9
+ */
10
+ export function createUserMetadataBatcher(apiClient) {
11
+ const loader = new DataLoader(async (userIds) => {
12
+ let result;
13
+ try {
14
+ result = await apiClient.permissions.batchGetUserMetadata({ userIds: [...userIds] });
15
+ }
16
+ catch (err) {
17
+ console.error('Batch get user metadata failed:', err);
18
+ throw err;
19
+ }
20
+ if (!result.ok) {
21
+ const reason = result.error ?? `status ${result.status}`;
22
+ console.error('Batch get user metadata failed:', reason);
23
+ throw new Error(`Batch get user metadata failed: ${reason}`);
24
+ }
25
+ const byId = new Map((result.data?.users ?? []).map((u) => [u.id, u]));
26
+ return userIds.map((id) => byId.get(id) ?? null);
27
+ }, {
28
+ cache: false,
29
+ maxBatchSize: MAX_USER_METADATA_BATCH,
30
+ batchScheduleFn: (dispatch) => setTimeout(dispatch, BATCH_WINDOW_MS),
31
+ });
32
+ return (userId) => loader.load(userId);
33
+ }
@@ -2,18 +2,17 @@ import React from 'react';
2
2
  import type { AssetRecord } from '../../api/index.js';
3
3
  export interface AssetCardProps {
4
4
  asset: AssetRecord;
5
- /** `media.publicBaseUrl` - see AssetContext. */
5
+ /** `useAssetContext().baseUrl`, the authenticated raw route - see AssetContext. */
6
6
  baseUrl?: string;
7
7
  /** Present in picker mode - clicking the card selects the asset. */
8
8
  onSelect?: (asset: AssetRecord) => void;
9
- /** Present only for admins in manage mode - renders the trash button. */
9
+ /** Present only where the viewer may remove the asset (manage mode) - renders the trash button. */
10
10
  onDelete?: (asset: AssetRecord) => void;
11
+ /** `compact` shrinks the thumbnail and drops the date line. */
12
+ density?: 'grid' | 'compact';
11
13
  }
12
14
  /**
13
- * One thumbnail card in the MediaLibrary grid. Raster and svg assets both go
14
- * through `assetUrl()` unmodified - for svg (a static, non-transform src)
15
- * `assetUrl` ignores the `width` option and just applies `baseUrl`, which is
16
- * exactly the behavior wanted here. PDFs have no image to preview, so they
17
- * render a file-type icon instead.
15
+ * One thumbnail card in the MediaLibrary grid. PDFs have no image to preview, so they render a
16
+ * file-type icon instead.
18
17
  */
19
18
  export declare const AssetCard: React.FC<AssetCardProps>;
@@ -1,30 +1,21 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState } from 'react';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
4
3
  import { ActionIcon, Card, Text, Tooltip } from '@mantine/core';
5
4
  import { IconFileTypePdf, IconPhotoOff, IconTrash } from '@tabler/icons-react';
6
- import { assetUrl } from '../../assets/asset-url.js';
5
+ import { formatRelativeTime } from '../relative-time.js';
6
+ import { editorImageSrc } from './editor-image-src.js';
7
+ import { CARD_THUMBNAIL_HEIGHT } from './media-library-view.js';
8
+ import { useThumbnailFailure } from './useThumbnailFailure.js';
7
9
  const THUMBNAIL_WIDTH = 160;
8
10
  /**
9
- * One thumbnail card in the MediaLibrary grid. Raster and svg assets both go
10
- * through `assetUrl()` unmodified - for svg (a static, non-transform src)
11
- * `assetUrl` ignores the `width` option and just applies `baseUrl`, which is
12
- * exactly the behavior wanted here. PDFs have no image to preview, so they
13
- * render a file-type icon instead.
11
+ * One thumbnail card in the MediaLibrary grid. PDFs have no image to preview, so they render a
12
+ * file-type icon instead.
14
13
  */
15
- export const AssetCard = ({ asset, baseUrl, onSelect, onDelete }) => {
16
- const uploadedDate = new Date(asset.uploadedAt).toLocaleDateString();
17
- const thumbnailSrc = assetUrl(asset, { width: THUMBNAIL_WIDTH, baseUrl });
18
- const [thumbnailFailed, setThumbnailFailed] = useState(false);
19
- // Reset the error state if the underlying asset/src changes, so a
20
- // thumbnail that failed earlier gets another chance to load. Adjusting state
21
- // during render (rather than in a useEffect) avoids an extra render pass -
22
- // see https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes
23
- const [prevThumbnailSrc, setPrevThumbnailSrc] = useState(thumbnailSrc);
24
- if (thumbnailSrc !== prevThumbnailSrc) {
25
- setPrevThumbnailSrc(thumbnailSrc);
26
- setThumbnailFailed(false);
27
- }
14
+ export const AssetCard = ({ asset, baseUrl, onSelect, onDelete, density = 'grid', }) => {
15
+ const compact = density === 'compact';
16
+ const thumbnailHeight = CARD_THUMBNAIL_HEIGHT[density];
17
+ const thumbnailSrc = editorImageSrc(asset.src, baseUrl ?? '', { width: THUMBNAIL_WIDTH });
18
+ const [thumbnailFailed, markThumbnailFailed] = useThumbnailFailure(thumbnailSrc);
28
19
  const handleKeyDown = (event) => {
29
20
  if (!onSelect)
30
21
  return;
@@ -38,23 +29,25 @@ export const AssetCard = ({ asset, baseUrl, onSelect, onDelete }) => {
38
29
  // element, unreachable from the keyboard - role="button" + tabIndex
39
30
  // makes it focusable and announced correctly, and onKeyDown mirrors
40
31
  // native <button> Enter/Space activation.
41
- role: onSelect ? 'button' : undefined, tabIndex: onSelect ? 0 : undefined, onKeyDown: onSelect ? handleKeyDown : undefined, children: [onDelete && (_jsx(ActionIcon, { variant: "filled", color: "red", size: "sm", radius: "xl", style: { position: 'absolute', top: 4, right: 4, zIndex: 1 }, "aria-label": `Delete ${asset.filename}`, "data-testid": `asset-card-delete-${asset.hash32}`, onClick: (event) => {
42
- event.stopPropagation();
43
- onDelete(asset);
44
- }, children: _jsx(IconTrash, { size: 14 }) })), _jsx(Card.Section, { children: asset.kind === 'pdf' ? (_jsx("div", { style: {
45
- height: 100,
32
+ role: onSelect ? 'button' : undefined, tabIndex: onSelect ? 0 : undefined, onKeyDown: onSelect ? handleKeyDown : undefined,
33
+ // Mantine draws its focus ring only on its own controls or this class; a bare Card gets none.
34
+ className: onSelect ? 'mantine-focus-auto' : undefined, "aria-label": onSelect ? `Select ${asset.filename}` : undefined, children: [onDelete && (_jsx(Tooltip, { label: "Remove from library", children: _jsx(ActionIcon, { variant: "default", c: "var(--canopy-danger-text)", radius: "xl", style: { position: 'absolute', top: 4, right: 4, zIndex: 1 }, "aria-label": `Remove ${asset.filename} from library`, "data-testid": `asset-card-delete-${asset.hash32}`, onClick: (event) => {
35
+ event.stopPropagation();
36
+ onDelete(asset);
37
+ }, children: _jsx(IconTrash, { size: 14 }) }) })), _jsx(Card.Section, { children: asset.kind === 'pdf' ? (_jsx("div", { style: {
38
+ height: thumbnailHeight,
46
39
  display: 'flex',
47
40
  alignItems: 'center',
48
41
  justifyContent: 'center',
49
- background: 'var(--mantine-color-gray-1)',
50
- }, children: _jsx(IconFileTypePdf, { size: 40, stroke: 1.5 }) })) : thumbnailFailed ? (_jsxs("div", { "data-testid": `asset-card-thumbnail-fallback-${asset.hash32}`, style: {
51
- height: 100,
42
+ background: 'var(--canopy-surface-muted)',
43
+ }, children: _jsx(IconFileTypePdf, { size: compact ? 28 : 40, stroke: 1.5 }) })) : thumbnailFailed ? (_jsxs("div", { "data-testid": `asset-card-thumbnail-fallback-${asset.hash32}`, style: {
44
+ height: thumbnailHeight,
52
45
  width: '100%',
53
46
  display: 'flex',
54
47
  flexDirection: 'column',
55
48
  alignItems: 'center',
56
49
  justifyContent: 'center',
57
50
  gap: 4,
58
- background: 'var(--mantine-color-gray-1)',
59
- }, children: [_jsx(IconPhotoOff, { size: 28, stroke: 1.5, color: "var(--mantine-color-gray-5)" }), _jsx(Text, { size: "xs", c: "dimmed", children: "Preview unavailable" })] })) : (_jsx("img", { src: thumbnailSrc, alt: asset.filename, style: { width: '100%', height: 100, objectFit: 'cover', display: 'block' }, onError: () => setThumbnailFailed(true) })) }), _jsx(Tooltip, { label: asset.filename, openDelay: 400, children: _jsx(Text, { size: "xs", fw: 500, truncate: true, mt: 4, children: asset.slug }) }), _jsx(Text, { size: "xs", c: "dimmed", children: uploadedDate })] }));
51
+ background: 'var(--canopy-surface-muted)',
52
+ }, children: [_jsx(IconPhotoOff, { size: compact ? 20 : 28, stroke: 1.5, color: "var(--mantine-color-dimmed)" }), _jsx(Text, { size: "xs", c: "dimmed", children: "Preview unavailable" })] })) : (_jsx("img", { src: thumbnailSrc, alt: asset.filename, style: { width: '100%', height: thumbnailHeight, objectFit: 'cover', display: 'block' }, onError: markThumbnailFailed })) }), _jsx(Tooltip, { multiline: true, w: 260, openDelay: 400, label: _jsxs(_Fragment, { children: [_jsx("div", { style: { overflowWrap: 'anywhere' }, children: asset.filename }), _jsxs("div", { style: { overflowWrap: 'anywhere' }, children: ["Slug: ", asset.slug] })] }), children: _jsx(Text, { size: "xs", fw: 500, truncate: true, mt: 4, children: asset.filename }) }), !compact && (_jsx(Tooltip, { label: new Date(asset.uploadedAt).toLocaleString(), children: _jsx(Text, { size: "xs", c: "dimmed", w: "fit-content", children: formatRelativeTime(asset.uploadedAt) }) }))] }));
60
53
  };
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import type { AssetRecord } from '../../api/index.js';
3
+ export interface AssetListRowProps {
4
+ asset: AssetRecord;
5
+ /** `useAssetContext().baseUrl`, the authenticated raw route - see AssetContext. */
6
+ baseUrl?: string;
7
+ /**
8
+ * Present in picker mode - the whole row is then a button that selects the asset. A button
9
+ * cannot hold another, so a selectable row renders no remove control.
10
+ */
11
+ onSelect?: (asset: AssetRecord) => void;
12
+ /** Present only where the viewer may remove the asset - renders the trash button. */
13
+ onDelete?: (asset: AssetRecord) => void;
14
+ }
15
+ /** One asset as a row in the list layout: thumbnail, name, dimensions, size and upload date. */
16
+ export declare const AssetListRow: React.FC<AssetListRowProps>;
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { ActionIcon, Group, Text, Tooltip, UnstyledButton } from '@mantine/core';
4
+ import { IconFileTypePdf, IconPhotoOff, IconTrash } from '@tabler/icons-react';
5
+ import { formatRelativeTime } from '../relative-time.js';
6
+ import { editorImageSrc } from './editor-image-src.js';
7
+ import { formatBytes } from './format-bytes.js';
8
+ import { useThumbnailFailure } from './useThumbnailFailure.js';
9
+ const THUMBNAIL_SIZE = 40;
10
+ const THUMBNAIL_WIDTH = 80;
11
+ const rowStyle = {
12
+ width: '100%',
13
+ padding: 6,
14
+ border: '1px solid var(--mantine-color-default-border)',
15
+ borderRadius: 'var(--mantine-radius-md)',
16
+ };
17
+ const thumbnailBoxStyle = {
18
+ width: THUMBNAIL_SIZE,
19
+ height: THUMBNAIL_SIZE,
20
+ flex: `0 0 ${THUMBNAIL_SIZE}px`,
21
+ display: 'flex',
22
+ alignItems: 'center',
23
+ justifyContent: 'center',
24
+ borderRadius: 'var(--mantine-radius-sm)',
25
+ background: 'var(--canopy-surface-muted)',
26
+ overflow: 'hidden',
27
+ };
28
+ /** One asset as a row in the list layout: thumbnail, name, dimensions, size and upload date. */
29
+ export const AssetListRow = ({ asset, baseUrl, onSelect, onDelete, }) => {
30
+ const thumbnailSrc = editorImageSrc(asset.src, baseUrl ?? '', { width: THUMBNAIL_WIDTH });
31
+ const [thumbnailFailed, markThumbnailFailed] = useThumbnailFailure(thumbnailSrc);
32
+ const hasDimensions = asset.width !== undefined && asset.height !== undefined;
33
+ const content = (_jsxs(Group, { gap: "sm", wrap: "nowrap", style: { flex: 1, minWidth: 0 }, children: [_jsx("div", { style: thumbnailBoxStyle, children: asset.kind === 'pdf' ? (_jsx(IconFileTypePdf, { size: 24, stroke: 1.5, "aria-hidden": true })) : thumbnailFailed ? (_jsx(IconPhotoOff, { size: 20, stroke: 1.5, color: "var(--mantine-color-dimmed)", "data-testid": `asset-row-thumbnail-fallback-${asset.hash32}`, "aria-label": "Preview unavailable" })) : (
34
+ // Decorative: the filename beside it already names the row.
35
+ _jsx("img", { src: thumbnailSrc, alt: "", style: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' }, onError: markThumbnailFailed })) }), _jsx(Tooltip, { multiline: true, w: 260, openDelay: 400, label: _jsxs(_Fragment, { children: [_jsx("div", { style: { overflowWrap: 'anywhere' }, children: asset.filename }), _jsxs("div", { style: { overflowWrap: 'anywhere' }, children: ["Slug: ", asset.slug] })] }), children: _jsx(Text, { size: "sm", fw: 500, truncate: true, style: { flex: 1, minWidth: 0 }, children: asset.filename }) }), _jsx(Text, { size: "xs", c: "dimmed", w: 84, ta: "right", visibleFrom: "sm", style: { flex: '0 0 auto' }, children: hasDimensions ? `${asset.width} × ${asset.height}` : '' }), _jsx(Text, { size: "xs", c: "dimmed", w: 52, ta: "right", visibleFrom: "sm", style: { flex: '0 0 auto' }, children: formatBytes(asset.size) }), _jsx(Tooltip, { label: new Date(asset.uploadedAt).toLocaleString(), children: _jsx(Text, { size: "xs", c: "dimmed", w: 72, ta: "right", style: { flex: '0 0 auto' }, children: formatRelativeTime(asset.uploadedAt) }) })] }));
36
+ const removeButton = onDelete && (_jsx(Tooltip, { label: "Remove from library", children: _jsx(ActionIcon, { variant: "subtle", color: "red", "aria-label": `Remove ${asset.filename} from library`, "data-testid": `asset-row-delete-${asset.hash32}`, onClick: () => onDelete(asset), children: _jsx(IconTrash, { size: 16 }) }) }));
37
+ if (onSelect) {
38
+ return (_jsx(UnstyledButton, { onClick: () => onSelect(asset), "data-testid": `asset-row-${asset.hash32}`, style: rowStyle, children: content }));
39
+ }
40
+ return (_jsxs(Group, { gap: "xs", wrap: "nowrap", "data-testid": `asset-row-${asset.hash32}`, style: rowStyle, children: [content, removeButton] }));
41
+ };
@@ -32,5 +32,10 @@ const CropStepBody = ({ imageSrc, aspect, initialCrop, onConfirm, onClose, }) =>
32
32
  onConfirm(rect);
33
33
  };
34
34
  const initialCroppedAreaPercentages = initialCrop ? cropRectToAreaPercent(initialCrop) : undefined;
35
- return (_jsxs(Stack, { gap: "md", children: [_jsx("div", { style: { position: 'relative', width: '100%', height: 360, background: '#000' }, children: _jsx(Cropper, { image: imageSrc, crop: crop, zoom: zoom, aspect: aspect, initialCroppedAreaPercentages: initialCroppedAreaPercentages, onCropChange: setCrop, onZoomChange: setZoom, onCropComplete: (croppedArea) => setPendingArea(croppedArea) }) }), _jsxs(Group, { gap: "sm", align: "center", children: [_jsx(Text, { size: "sm", w: 50, children: "Zoom" }), _jsx(Slider, { style: { flex: 1 }, min: MIN_ZOOM, max: MAX_ZOOM, step: ZOOM_STEP, value: zoom, onChange: setZoom, label: (value) => `${value.toFixed(2)}x` })] }), _jsxs(Group, { justify: "flex-end", children: [_jsx(Button, { variant: "default", onClick: onClose, children: "Cancel" }), _jsx(Button, { onClick: handleConfirm, disabled: !pendingArea, "data-testid": "crop-step-apply", children: "Apply crop" })] })] }));
35
+ return (_jsxs(Stack, { gap: "md", children: [_jsx("div", { style: {
36
+ position: 'relative',
37
+ width: '100%',
38
+ height: 360,
39
+ background: 'var(--mantine-color-black)',
40
+ }, children: _jsx(Cropper, { image: imageSrc, crop: crop, zoom: zoom, aspect: aspect, initialCroppedAreaPercentages: initialCroppedAreaPercentages, onCropChange: setCrop, onZoomChange: setZoom, onCropComplete: (croppedArea) => setPendingArea(croppedArea) }) }), _jsxs(Group, { gap: "sm", align: "center", children: [_jsx(Text, { size: "sm", w: 50, children: "Zoom" }), _jsx(Slider, { style: { flex: 1 }, min: MIN_ZOOM, max: MAX_ZOOM, step: ZOOM_STEP, value: zoom, onChange: setZoom, label: (value) => `${value.toFixed(2)}x` })] }), _jsxs(Group, { justify: "flex-end", children: [_jsx(Button, { size: "sm", variant: "default", onClick: onClose, children: "Cancel" }), _jsx(Button, { size: "sm", onClick: handleConfirm, disabled: !pendingArea, "data-testid": "crop-step-apply", children: "Apply crop" })] })] }));
36
41
  };
@@ -4,17 +4,18 @@ export interface MediaLibraryProps {
4
4
  opened: boolean;
5
5
  onClose: () => void;
6
6
  /**
7
- * 'manage': right-side Drawer opened from the EditorSidebar Settings menu.
7
+ * 'manage': right-side Drawer opened from the rail's Media button.
8
8
  * 'picker': Modal used by ImageField ("Browse library"/"Replace") and the
9
9
  * MDX image dialog's "From library" tab.
10
10
  */
11
11
  mode: 'manage' | 'picker';
12
12
  /** Required in picker mode - called with the chosen asset when a card is clicked. */
13
13
  onSelect?: (asset: AssetRecord) => void;
14
+ returnFocusTo?: () => HTMLElement | null;
14
15
  }
15
16
  /**
16
- * One component, two presentations, sharing the same grid/upload/filter/
17
- * pagination/delete core (MediaLibraryBody) - see
17
+ * One component, two presentations, sharing the same grid/list/upload/search/
18
+ * delete core (MediaLibraryBody) - see
18
19
  * .claude/future-tasks/resolved/assets-media-system.md "MediaManager design".
19
20
  */
20
21
  export declare const MediaLibrary: React.FC<MediaLibraryProps>;
@@ -2,14 +2,21 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Drawer, Modal, Text, Title } from '@mantine/core';
4
4
  import { MediaLibraryBody } from './MediaLibraryBody.js';
5
+ import { focusReturnProps } from '../utils/focus-return.js';
5
6
  /**
6
- * One component, two presentations, sharing the same grid/upload/filter/
7
- * pagination/delete core (MediaLibraryBody) - see
7
+ * One component, two presentations, sharing the same grid/list/upload/search/
8
+ * delete core (MediaLibraryBody) - see
8
9
  * .claude/future-tasks/resolved/assets-media-system.md "MediaManager design".
9
10
  */
10
- export const MediaLibrary = ({ opened, onClose, mode, onSelect }) => {
11
+ export const MediaLibrary = ({ opened, onClose, mode, onSelect, returnFocusTo, }) => {
11
12
  if (mode === 'picker') {
12
13
  return (_jsx(Modal, { opened: opened, onClose: onClose, title: "Media library", size: "lg", overlayProps: { blur: 2 }, "data-testid": "media-library-picker", children: _jsx(MediaLibraryBody, { opened: opened, mode: "picker", onSelect: onSelect }) }));
13
14
  }
14
- return (_jsx(Drawer, { opened: opened, onClose: onClose, position: "right", title: _jsxs("div", { children: [_jsx(Title, { order: 4, children: "Media library" }), _jsx(Text, { size: "xs", c: "dimmed", children: "Upload and manage images and PDFs" })] }), padding: "md", size: 700, overlayProps: { blur: 2 }, "data-testid": "media-library-manage", children: _jsx(MediaLibraryBody, { opened: opened, mode: "manage" }) }));
15
+ return (_jsx(Drawer, { opened: opened, onClose: onClose, position: "right", title: _jsxs("div", { children: [_jsx(Title, { order: 4, children: "Media library" }), _jsx(Text, { size: "xs", c: "dimmed", children: "Upload and manage images and PDFs" })] }), padding: "md", size: 700,
16
+ // The body fills the drawer and the library's own ScrollArea scrolls, so the toolbar stays put
17
+ // and the list's IntersectionObserver sees a real clipping viewport.
18
+ styles: {
19
+ content: { display: 'flex', flexDirection: 'column' },
20
+ body: { flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' },
21
+ }, overlayProps: { blur: 2 }, "data-testid": "media-library-manage", ...focusReturnProps(returnFocusTo), children: _jsx(MediaLibraryBody, { opened: opened, mode: "manage" }) }));
15
22
  };
@@ -1,14 +1,14 @@
1
1
  import React from 'react';
2
2
  import type { AssetRecord } from '../../api/index.js';
3
3
  export interface MediaLibraryBodyProps {
4
- /** Whether the enclosing Drawer/Modal is open - gates the (re)fetch of the first page on each open. */
4
+ /** Whether the enclosing Drawer/Modal is open - the library (re)loads from page 1 on each open and stops loading on close. */
5
5
  opened: boolean;
6
6
  mode: 'manage' | 'picker';
7
- /** Present in picker mode - clicking a card (or completing an upload) selects the asset. */
7
+ /** Present in picker mode - clicking a card or row selects the asset; an upload is pinned first, not selected. */
8
8
  onSelect?: (asset: AssetRecord) => void;
9
9
  }
10
10
  /**
11
- * Shared grid/upload/filter/pagination/delete core for both MediaLibrary
11
+ * Shared grid/list/upload/search/delete core for both MediaLibrary
12
12
  * presentations (Drawer "manage" mode, Modal "picker" mode) - see
13
13
  * MediaLibrary.tsx for the two wrappers.
14
14
  */