@elytracms/studio 0.0.92 → 0.0.94

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 (599) hide show
  1. package/convex/analytics-cost.test.ts +817 -0
  2. package/convex/asset-alt-migration.test.ts +127 -0
  3. package/convex/asset-replace.test.ts +168 -0
  4. package/convex/asset-revalidate.test.ts +6 -6
  5. package/convex/assets.ts +41 -5
  6. package/convex/auth.ts +5 -2
  7. package/convex/authority.ts +35 -11
  8. package/convex/cms.ts +25 -11
  9. package/convex/contract-parity.test.ts +25 -3
  10. package/convex/delivery.ts +12 -9
  11. package/convex/document-revalidate.test.ts +10 -12
  12. package/convex/guard.ts +9 -7
  13. package/convex/http-routes.test.ts +18 -8
  14. package/convex/http.ts +23 -17
  15. package/convex/ingest.test.ts +326 -0
  16. package/convex/ingest.ts +281 -0
  17. package/convex/invites.ts +13 -5
  18. package/convex/members.ts +10 -4
  19. package/convex/orphan-hydration.test.ts +116 -0
  20. package/convex/rail-write-parity.test.ts +11 -8
  21. package/convex/record-functions.ts +325 -0
  22. package/convex/records.test.ts +156 -0
  23. package/convex/redirect-migration.test.ts +205 -0
  24. package/convex/redirect-revalidate.test.ts +5 -1
  25. package/convex/redirects.ts +120 -79
  26. package/convex/role-policy.test.ts +315 -0
  27. package/convex/roles.ts +83 -0
  28. package/convex/schema.ts +36 -0
  29. package/convex/triggers.ts +21 -1
  30. package/dist/addon.d.ts +71 -0
  31. package/dist/addon.js +67 -0
  32. package/dist/addon.js.map +1 -0
  33. package/dist/components/account/account-settings.js +2 -2
  34. package/dist/components/account/account-settings.js.map +1 -1
  35. package/dist/components/account/credential-panel.js +2 -2
  36. package/dist/components/account/credential-panel.js.map +1 -1
  37. package/dist/components/account/self-account-schema.d.ts +6 -15
  38. package/dist/components/account/self-account-schema.js +52 -34
  39. package/dist/components/account/self-account-schema.js.map +1 -1
  40. package/dist/components/account/self-account-view-source.d.ts +1 -1
  41. package/dist/components/account/self-account-view-source.js +6 -4
  42. package/dist/components/account/self-account-view-source.js.map +1 -1
  43. package/dist/components/ai/ai-chat-panel-lazy.js +1 -1
  44. package/dist/components/ai/ai-chat-panel-lazy.js.map +1 -1
  45. package/dist/components/ai/ai-chat-panel.js +6 -6
  46. package/dist/components/ai/ai-chat-panel.js.map +1 -1
  47. package/dist/components/ai/ai-cms-panel.js +2 -2
  48. package/dist/components/ai/ai-cms-panel.js.map +1 -1
  49. package/dist/components/ai/ai-panel-toggle.js +4 -4
  50. package/dist/components/ai/ai-panel-toggle.js.map +1 -1
  51. package/dist/components/ai/chat-ui.js +2 -2
  52. package/dist/components/ai/chat-ui.js.map +1 -1
  53. package/dist/components/ai/proposal-card.d.ts +2 -2
  54. package/dist/components/ai/proposal-card.js +4 -3
  55. package/dist/components/ai/proposal-card.js.map +1 -1
  56. package/dist/components/ai/proposal-issues.js +2 -2
  57. package/dist/components/ai/proposal-issues.js.map +1 -1
  58. package/dist/components/ai/translate-document-action.js +2 -2
  59. package/dist/components/ai/translate-document-action.js.map +1 -1
  60. package/dist/components/ai/translate-document-dialog.js +10 -8
  61. package/dist/components/ai/translate-document-dialog.js.map +1 -1
  62. package/dist/components/ai/translate-skipped.d.ts +19 -0
  63. package/dist/components/ai/translate-skipped.js +46 -0
  64. package/dist/components/ai/translate-skipped.js.map +1 -0
  65. package/dist/components/app-views/default-modules.d.ts +50 -2
  66. package/dist/components/app-views/default-modules.js +65 -20
  67. package/dist/components/app-views/default-modules.js.map +1 -1
  68. package/dist/components/app-views/module.d.ts +53 -3
  69. package/dist/components/app-views/module.js +14 -7
  70. package/dist/components/app-views/module.js.map +1 -1
  71. package/dist/components/app-views/sidebar.js +1 -1
  72. package/dist/components/app-views/sidebar.js.map +1 -1
  73. package/dist/components/app-views/studio-config.d.ts +11 -2
  74. package/dist/components/app-views/studio-config.js.map +1 -1
  75. package/dist/components/assets/asset-detail.js +20 -10
  76. package/dist/components/assets/asset-detail.js.map +1 -1
  77. package/dist/components/assets/asset-grid.js +2 -2
  78. package/dist/components/assets/asset-grid.js.map +1 -1
  79. package/dist/components/assets/asset-metadata.d.ts +15 -4
  80. package/dist/components/assets/asset-metadata.js +15 -2
  81. package/dist/components/assets/asset-metadata.js.map +1 -1
  82. package/dist/components/assets/asset-search.js +4 -1
  83. package/dist/components/assets/asset-search.js.map +1 -1
  84. package/dist/components/assets/asset-selection.d.ts +0 -8
  85. package/dist/components/assets/asset-selection.js +4 -23
  86. package/dist/components/assets/asset-selection.js.map +1 -1
  87. package/dist/components/assets/asset-thumb.js +6 -5
  88. package/dist/components/assets/asset-thumb.js.map +1 -1
  89. package/dist/components/assets/assets-collection-view-source.d.ts +5 -5
  90. package/dist/components/assets/assets-collection-view-source.js +9 -5
  91. package/dist/components/assets/assets-collection-view-source.js.map +1 -1
  92. package/dist/components/assets/assets-module.js +1 -1
  93. package/dist/components/assets/assets-module.js.map +1 -1
  94. package/dist/components/assets/assets-store.d.ts +16 -2
  95. package/dist/components/assets/assets-store.js +28 -6
  96. package/dist/components/assets/assets-store.js.map +1 -1
  97. package/dist/components/assets/assets-view-preference.js +1 -1
  98. package/dist/components/assets/assets-view-preference.js.map +1 -1
  99. package/dist/components/assets/replace-file.d.ts +22 -0
  100. package/dist/components/assets/replace-file.js +59 -0
  101. package/dist/components/assets/replace-file.js.map +1 -0
  102. package/dist/components/auth-context.d.ts +1 -1
  103. package/dist/components/auth-context.js +6 -6
  104. package/dist/components/auth-context.js.map +1 -1
  105. package/dist/components/builder/blocks-model.d.ts +15 -27
  106. package/dist/components/builder/blocks-model.js +38 -44
  107. package/dist/components/builder/blocks-model.js.map +1 -1
  108. package/dist/components/builder/blocks-panel.d.ts +8 -4
  109. package/dist/components/builder/blocks-panel.js +10 -9
  110. package/dist/components/builder/blocks-panel.js.map +1 -1
  111. package/dist/components/builder/builder-store.d.ts +104 -15
  112. package/dist/components/builder/builder-store.js +157 -32
  113. package/dist/components/builder/builder-store.js.map +1 -1
  114. package/dist/components/builder/canvas-body.d.ts +30 -0
  115. package/dist/components/builder/canvas-body.js +38 -0
  116. package/dist/components/builder/canvas-body.js.map +1 -0
  117. package/dist/components/builder/canvas-breakpoints.js +1 -1
  118. package/dist/components/builder/canvas-breakpoints.js.map +1 -1
  119. package/dist/components/builder/canvas-chrome-css.js +7 -0
  120. package/dist/components/builder/canvas-chrome-css.js.map +1 -1
  121. package/dist/components/builder/canvas-dnd.d.ts +9 -9
  122. package/dist/components/builder/canvas-dnd.js +6 -6
  123. package/dist/components/builder/canvas-dnd.js.map +1 -1
  124. package/dist/components/builder/canvas-inline-editing.d.ts +1 -1
  125. package/dist/components/builder/canvas-inline-editing.js.map +1 -1
  126. package/dist/components/builder/canvas-overlay-geometry.d.ts +1 -1
  127. package/dist/components/builder/canvas-overlay-geometry.js.map +1 -1
  128. package/dist/components/builder/canvas-render-context.d.ts +28 -20
  129. package/dist/components/builder/canvas-render-context.js +46 -47
  130. package/dist/components/builder/canvas-render-context.js.map +1 -1
  131. package/dist/components/builder/canvas-runs.d.ts +45 -0
  132. package/dist/components/builder/canvas-runs.js +51 -0
  133. package/dist/components/builder/canvas-runs.js.map +1 -0
  134. package/dist/components/builder/canvas-theme-notice.js +2 -2
  135. package/dist/components/builder/canvas-theme-notice.js.map +1 -1
  136. package/dist/components/builder/canvas.js +35 -8
  137. package/dist/components/builder/canvas.js.map +1 -1
  138. package/dist/components/builder/clipboard-actions.d.ts +16 -1
  139. package/dist/components/builder/clipboard-actions.js +48 -11
  140. package/dist/components/builder/clipboard-actions.js.map +1 -1
  141. package/dist/components/builder/composition-field-editor-lazy.js +1 -1
  142. package/dist/components/builder/composition-field-editor-lazy.js.map +1 -1
  143. package/dist/components/builder/composition-field-editor.d.ts +4 -16
  144. package/dist/components/builder/composition-field-editor.js +32 -43
  145. package/dist/components/builder/composition-field-editor.js.map +1 -1
  146. package/dist/components/builder/delete-key.d.ts +5 -0
  147. package/dist/components/builder/delete-key.js +4 -0
  148. package/dist/components/builder/delete-key.js.map +1 -1
  149. package/dist/components/builder/drag-session.d.ts +1 -1
  150. package/dist/components/builder/drag-session.js.map +1 -1
  151. package/dist/components/builder/fixture-thumbnail.js +1 -1
  152. package/dist/components/builder/fixture-thumbnail.js.map +1 -1
  153. package/dist/components/builder/insert-context.d.ts +18 -26
  154. package/dist/components/builder/insert-context.js +47 -94
  155. package/dist/components/builder/insert-context.js.map +1 -1
  156. package/dist/components/builder/layout-inspector.js +1 -1
  157. package/dist/components/builder/layout-inspector.js.map +1 -1
  158. package/dist/components/builder/node-title.js +1 -1
  159. package/dist/components/builder/node-title.js.map +1 -1
  160. package/dist/components/builder/page-tree.d.ts +4 -4
  161. package/dist/components/builder/page-tree.js +8 -8
  162. package/dist/components/builder/page-tree.js.map +1 -1
  163. package/dist/components/builder/placement-refusal.d.ts +18 -0
  164. package/dist/components/builder/placement-refusal.js +46 -0
  165. package/dist/components/builder/placement-refusal.js.map +1 -0
  166. package/dist/components/builder/property-inspector.js +8 -4
  167. package/dist/components/builder/property-inspector.js.map +1 -1
  168. package/dist/components/builder/reuse-context.js +68 -30
  169. package/dist/components/builder/reuse-context.js.map +1 -1
  170. package/dist/components/builder/reuse.d.ts +79 -1
  171. package/dist/components/builder/reuse.js +70 -3
  172. package/dist/components/builder/reuse.js.map +1 -1
  173. package/dist/components/builder/rich-text-toolbar.js +1 -1
  174. package/dist/components/builder/rich-text-toolbar.js.map +1 -1
  175. package/dist/components/builder/run-inspector.d.ts +11 -0
  176. package/dist/components/builder/run-inspector.js +33 -0
  177. package/dist/components/builder/run-inspector.js.map +1 -0
  178. package/dist/components/builder/selection-overlay.js +3 -3
  179. package/dist/components/builder/selection-overlay.js.map +1 -1
  180. package/dist/components/builder/spacing-handles.d.ts +2 -2
  181. package/dist/components/builder/spacing-handles.js +3 -3
  182. package/dist/components/builder/spacing-handles.js.map +1 -1
  183. package/dist/components/cms/asset-field.d.ts +38 -1
  184. package/dist/components/cms/asset-field.js +184 -60
  185. package/dist/components/cms/asset-field.js.map +1 -1
  186. package/dist/components/cms/asset-picker-core.d.ts +16 -0
  187. package/dist/components/cms/asset-picker-core.js +20 -0
  188. package/dist/components/cms/asset-picker-core.js.map +1 -1
  189. package/dist/components/cms/asset-picker.js +14 -20
  190. package/dist/components/cms/asset-picker.js.map +1 -1
  191. package/dist/components/cms/bulk-actions.d.ts +4 -20
  192. package/dist/components/cms/bulk-actions.js +5 -26
  193. package/dist/components/cms/bulk-actions.js.map +1 -1
  194. package/dist/components/cms/bulk-bar.d.ts +2 -2
  195. package/dist/components/cms/bulk-bar.js +2 -2
  196. package/dist/components/cms/bulk-bar.js.map +1 -1
  197. package/dist/components/cms/cell-edit.d.ts +1 -1
  198. package/dist/components/cms/cell-edit.js +1 -1
  199. package/dist/components/cms/cell-edit.js.map +1 -1
  200. package/dist/components/cms/cell.d.ts +1 -1
  201. package/dist/components/cms/cell.js +3 -3
  202. package/dist/components/cms/cell.js.map +1 -1
  203. package/dist/components/cms/cms-index.js +2 -2
  204. package/dist/components/cms/cms-index.js.map +1 -1
  205. package/dist/components/cms/cms-labels.js +2 -2
  206. package/dist/components/cms/cms-labels.js.map +1 -1
  207. package/dist/components/cms/cms-store.d.ts +7 -6
  208. package/dist/components/cms/cms-store.js +23 -43
  209. package/dist/components/cms/cms-store.js.map +1 -1
  210. package/dist/components/cms/collection-table.js +7 -7
  211. package/dist/components/cms/collection-table.js.map +1 -1
  212. package/dist/components/cms/detail-editor.d.ts +5 -2
  213. package/dist/components/cms/detail-editor.js +20 -15
  214. package/dist/components/cms/detail-editor.js.map +1 -1
  215. package/dist/components/cms/details-pane.js +1 -1
  216. package/dist/components/cms/details-pane.js.map +1 -1
  217. package/dist/components/cms/doc-state.js +1 -1
  218. package/dist/components/cms/doc-state.js.map +1 -1
  219. package/dist/components/cms/document-diff-view.d.ts +3 -1
  220. package/dist/components/cms/document-diff-view.js +17 -12
  221. package/dist/components/cms/document-diff-view.js.map +1 -1
  222. package/dist/components/cms/document-editor-context.js +1 -1
  223. package/dist/components/cms/document-editor-context.js.map +1 -1
  224. package/dist/components/cms/document-staging.js +1 -1
  225. package/dist/components/cms/document-staging.js.map +1 -1
  226. package/dist/components/cms/document-url-path.js +2 -2
  227. package/dist/components/cms/document-url-path.js.map +1 -1
  228. package/dist/components/cms/duplicate-document.d.ts +1 -1
  229. package/dist/components/cms/duplicate-document.js.map +1 -1
  230. package/dist/components/cms/editable-cell.js +3 -3
  231. package/dist/components/cms/editable-cell.js.map +1 -1
  232. package/dist/components/cms/field-anchors.d.ts +7 -4
  233. package/dist/components/cms/field-anchors.js +9 -7
  234. package/dist/components/cms/field-anchors.js.map +1 -1
  235. package/dist/components/cms/field-collaboration.d.ts +1 -1
  236. package/dist/components/cms/field-collaboration.js +1 -1
  237. package/dist/components/cms/field-collaboration.js.map +1 -1
  238. package/dist/components/cms/field-connection.d.ts +1 -1
  239. package/dist/components/cms/field-connection.js +2 -2
  240. package/dist/components/cms/field-connection.js.map +1 -1
  241. package/dist/components/cms/field-control-lib.d.ts +15 -1
  242. package/dist/components/cms/field-control-lib.js +23 -4
  243. package/dist/components/cms/field-control-lib.js.map +1 -1
  244. package/dist/components/cms/field-control.js +4 -4
  245. package/dist/components/cms/field-control.js.map +1 -1
  246. package/dist/components/cms/field-renderer.d.ts +1 -1
  247. package/dist/components/cms/field-renderer.js +2 -2
  248. package/dist/components/cms/field-renderer.js.map +1 -1
  249. package/dist/components/cms/filter-builder.d.ts +1 -1
  250. package/dist/components/cms/filter-builder.js +3 -3
  251. package/dist/components/cms/filter-builder.js.map +1 -1
  252. package/dist/components/cms/gate-validation.d.ts +21 -30
  253. package/dist/components/cms/gate-validation.js +11 -32
  254. package/dist/components/cms/gate-validation.js.map +1 -1
  255. package/dist/components/cms/import-export.js +2 -2
  256. package/dist/components/cms/import-export.js.map +1 -1
  257. package/dist/components/cms/issue-copy.js +1 -1
  258. package/dist/components/cms/issue-copy.js.map +1 -1
  259. package/dist/components/cms/issues-pane.js +3 -3
  260. package/dist/components/cms/issues-pane.js.map +1 -1
  261. package/dist/components/cms/object-array-field.js +6 -6
  262. package/dist/components/cms/object-array-field.js.map +1 -1
  263. package/dist/components/cms/prop-fields.d.ts +7 -2
  264. package/dist/components/cms/prop-fields.js +14 -7
  265. package/dist/components/cms/prop-fields.js.map +1 -1
  266. package/dist/components/cms/reference-confirm.d.ts +1 -1
  267. package/dist/components/cms/reference-confirm.js +2 -2
  268. package/dist/components/cms/reference-confirm.js.map +1 -1
  269. package/dist/components/cms/relation-picker.js +2 -2
  270. package/dist/components/cms/relation-picker.js.map +1 -1
  271. package/dist/components/cms/rich-text/block-style.js +1 -1
  272. package/dist/components/cms/rich-text/block-style.js.map +1 -1
  273. package/dist/components/cms/rich-text/component-embed.js +4 -4
  274. package/dist/components/cms/rich-text/component-embed.js.map +1 -1
  275. package/dist/components/cms/rich-text/editor.js +3 -3
  276. package/dist/components/cms/rich-text/editor.js.map +1 -1
  277. package/dist/components/cms/rich-text/embed-picker.d.ts +1 -1
  278. package/dist/components/cms/rich-text/embed-picker.js +3 -3
  279. package/dist/components/cms/rich-text/embed-picker.js.map +1 -1
  280. package/dist/components/cms/rich-text/embed-props.js +3 -3
  281. package/dist/components/cms/rich-text/embed-props.js.map +1 -1
  282. package/dist/components/cms/rich-text/extensions.js +1 -1
  283. package/dist/components/cms/rich-text/extensions.js.map +1 -1
  284. package/dist/components/cms/rich-text/font-size-control.d.ts +1 -1
  285. package/dist/components/cms/rich-text/font-size-control.js +2 -2
  286. package/dist/components/cms/rich-text/font-size-control.js.map +1 -1
  287. package/dist/components/cms/rich-text/insert.d.ts +20 -9
  288. package/dist/components/cms/rich-text/insert.js +25 -17
  289. package/dist/components/cms/rich-text/insert.js.map +1 -1
  290. package/dist/components/cms/rich-text/lazy.js +1 -1
  291. package/dist/components/cms/rich-text/lazy.js.map +1 -1
  292. package/dist/components/cms/rich-text/link-control.js +2 -2
  293. package/dist/components/cms/rich-text/link-control.js.map +1 -1
  294. package/dist/components/cms/rich-text/slash-command.js +3 -3
  295. package/dist/components/cms/rich-text/slash-command.js.map +1 -1
  296. package/dist/components/cms/rich-text/toolbar.js +1 -1
  297. package/dist/components/cms/rich-text/toolbar.js.map +1 -1
  298. package/dist/components/cms/rich-text/unknown-node.js +2 -2
  299. package/dist/components/cms/rich-text/unknown-node.js.map +1 -1
  300. package/dist/components/cms/rich-text-preview.js +2 -2
  301. package/dist/components/cms/rich-text-preview.js.map +1 -1
  302. package/dist/components/cms/row-actions.d.ts +1 -1
  303. package/dist/components/cms/row-actions.js.map +1 -1
  304. package/dist/components/cms/scalar-value-editor.d.ts +1 -1
  305. package/dist/components/cms/scalar-value-editor.js +2 -2
  306. package/dist/components/cms/scalar-value-editor.js.map +1 -1
  307. package/dist/components/cms/use-discard-controller.d.ts +1 -1
  308. package/dist/components/cms/use-discard-controller.js +1 -1
  309. package/dist/components/cms/use-discard-controller.js.map +1 -1
  310. package/dist/components/cms/use-document-draft.d.ts +7 -1
  311. package/dist/components/cms/use-document-draft.js +16 -2
  312. package/dist/components/cms/use-document-draft.js.map +1 -1
  313. package/dist/components/cms/use-document-versions.js +1 -1
  314. package/dist/components/cms/use-document-versions.js.map +1 -1
  315. package/dist/components/cms/use-field-focus.d.ts +1 -1
  316. package/dist/components/cms/use-field-focus.js +2 -2
  317. package/dist/components/cms/use-field-focus.js.map +1 -1
  318. package/dist/components/cms/use-live-url.d.ts +1 -1
  319. package/dist/components/cms/use-live-url.js +3 -3
  320. package/dist/components/cms/use-live-url.js.map +1 -1
  321. package/dist/components/cms/use-publish-controller.d.ts +2 -3
  322. package/dist/components/cms/use-publish-controller.js +8 -20
  323. package/dist/components/cms/use-publish-controller.js.map +1 -1
  324. package/dist/components/cms/use-published-perspective.d.ts +1 -1
  325. package/dist/components/cms/use-published-perspective.js.map +1 -1
  326. package/dist/components/cms/use-restore-version.d.ts +1 -1
  327. package/dist/components/cms/use-restore-version.js +1 -1
  328. package/dist/components/cms/use-restore-version.js.map +1 -1
  329. package/dist/components/cms/version-compare-lazy.js +1 -1
  330. package/dist/components/cms/version-compare-lazy.js.map +1 -1
  331. package/dist/components/cms/version-compare.js +1 -1
  332. package/dist/components/cms/version-compare.js.map +1 -1
  333. package/dist/components/cms/versions-pane.js +11 -4
  334. package/dist/components/cms/versions-pane.js.map +1 -1
  335. package/dist/components/cms/view-bar.js +3 -3
  336. package/dist/components/cms/view-bar.js.map +1 -1
  337. package/dist/components/collection-view/cms-collection-view-source.d.ts +1 -1
  338. package/dist/components/collection-view/cms-collection-view-source.js +5 -3
  339. package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
  340. package/dist/components/collection-view/collection-view-context.d.ts +1 -1
  341. package/dist/components/collection-view/collection-view-context.js +1 -1
  342. package/dist/components/collection-view/collection-view-context.js.map +1 -1
  343. package/dist/components/collection-view/collection-view-surface.d.ts +13 -0
  344. package/dist/components/collection-view/collection-view-surface.js +18 -0
  345. package/dist/components/collection-view/collection-view-surface.js.map +1 -0
  346. package/dist/components/components/component-detail.js +10 -10
  347. package/dist/components/components/component-detail.js.map +1 -1
  348. package/dist/components/components/component-preview.d.ts +2 -2
  349. package/dist/components/components/component-preview.js +5 -5
  350. package/dist/components/components/component-preview.js.map +1 -1
  351. package/dist/components/components/component-status.d.ts +1 -1
  352. package/dist/components/components/component-status.js +1 -1
  353. package/dist/components/components/component-status.js.map +1 -1
  354. package/dist/components/components/components-index.js +2 -2
  355. package/dist/components/components/components-index.js.map +1 -1
  356. package/dist/components/components/field-inspector.js +1 -1
  357. package/dist/components/components/field-inspector.js.map +1 -1
  358. package/dist/components/components/registry-browser.js +3 -3
  359. package/dist/components/components/registry-browser.js.map +1 -1
  360. package/dist/components/components/taxonomy.d.ts +1 -1
  361. package/dist/components/components/taxonomy.js.map +1 -1
  362. package/dist/components/components/transfer-dialog.js +4 -3
  363. package/dist/components/components/transfer-dialog.js.map +1 -1
  364. package/dist/components/components/transfer.d.ts +10 -10
  365. package/dist/components/components/transfer.js +13 -12
  366. package/dist/components/components/transfer.js.map +1 -1
  367. package/dist/components/current-user-menu.js +1 -1
  368. package/dist/components/current-user-menu.js.map +1 -1
  369. package/dist/components/delivery-health-banner.js +2 -2
  370. package/dist/components/delivery-health-banner.js.map +1 -1
  371. package/dist/components/deployment-setup-gate.js +3 -3
  372. package/dist/components/deployment-setup-gate.js.map +1 -1
  373. package/dist/components/dev-login-badge.js +2 -2
  374. package/dist/components/dev-login-badge.js.map +1 -1
  375. package/dist/components/guards.js +1 -1
  376. package/dist/components/guards.js.map +1 -1
  377. package/dist/components/inspector/document-panes.d.ts +54 -0
  378. package/dist/components/inspector/document-panes.js +41 -0
  379. package/dist/components/inspector/document-panes.js.map +1 -0
  380. package/dist/components/inspector/inspector-sidebar.js +1 -1
  381. package/dist/components/inspector/inspector-sidebar.js.map +1 -1
  382. package/dist/components/nav-issues-registry.d.ts +53 -0
  383. package/dist/components/nav-issues-registry.js +69 -0
  384. package/dist/components/nav-issues-registry.js.map +1 -0
  385. package/dist/components/nav-issues.js +4 -4
  386. package/dist/components/nav-issues.js.map +1 -1
  387. package/dist/components/operation-error.d.ts +1 -1
  388. package/dist/components/operation-error.js +4 -4
  389. package/dist/components/operation-error.js.map +1 -1
  390. package/dist/components/playground-badge.js +2 -2
  391. package/dist/components/playground-badge.js.map +1 -1
  392. package/dist/components/project-list.js +3 -3
  393. package/dist/components/project-list.js.map +1 -1
  394. package/dist/components/project-session.d.ts +2 -2
  395. package/dist/components/project-session.js +5 -5
  396. package/dist/components/project-session.js.map +1 -1
  397. package/dist/components/project-switcher.js +2 -2
  398. package/dist/components/project-switcher.js.map +1 -1
  399. package/dist/components/rail-store-status.js +1 -1
  400. package/dist/components/rail-store-status.js.map +1 -1
  401. package/dist/components/rail-view-source.d.ts +6 -5
  402. package/dist/components/rail-view-source.js +4 -3
  403. package/dist/components/rail-view-source.js.map +1 -1
  404. package/dist/components/records/record-lens.d.ts +51 -0
  405. package/dist/components/records/record-lens.js +156 -0
  406. package/dist/components/records/record-lens.js.map +1 -0
  407. package/dist/components/records/record-rail.d.ts +86 -0
  408. package/dist/components/records/record-rail.js +241 -0
  409. package/dist/components/records/record-rail.js.map +1 -0
  410. package/dist/components/redirects/redirects-schema.js +1 -1
  411. package/dist/components/redirects/redirects-schema.js.map +1 -1
  412. package/dist/components/references/references-panel.js +2 -2
  413. package/dist/components/references/references-panel.js.map +1 -1
  414. package/dist/components/section-nav.js +27 -11
  415. package/dist/components/section-nav.js.map +1 -1
  416. package/dist/components/section-state.js +2 -2
  417. package/dist/components/section-state.js.map +1 -1
  418. package/dist/components/section-surface.js +2 -2
  419. package/dist/components/section-surface.js.map +1 -1
  420. package/dist/components/session-expired-overlay.js +3 -3
  421. package/dist/components/session-expired-overlay.js.map +1 -1
  422. package/dist/components/settings-form.js +1 -1
  423. package/dist/components/settings-form.js.map +1 -1
  424. package/dist/components/sign-in-needed-badge.js +1 -1
  425. package/dist/components/sign-in-needed-badge.js.map +1 -1
  426. package/dist/components/sign-in-view.d.ts +2 -2
  427. package/dist/components/sign-in-view.js +5 -5
  428. package/dist/components/sign-in-view.js.map +1 -1
  429. package/dist/components/studio-context.js +3 -3
  430. package/dist/components/studio-context.js.map +1 -1
  431. package/dist/components/studio-project-screen.js +30 -3
  432. package/dist/components/studio-project-screen.js.map +1 -1
  433. package/dist/components/studio-shell.js +6 -5
  434. package/dist/components/studio-shell.js.map +1 -1
  435. package/dist/components/theme-context.d.ts +1 -1
  436. package/dist/components/theme-context.js +2 -2
  437. package/dist/components/theme-context.js.map +1 -1
  438. package/dist/components/use-hydration-phase.d.ts +1 -1
  439. package/dist/components/use-hydration-phase.js +1 -1
  440. package/dist/components/use-hydration-phase.js.map +1 -1
  441. package/dist/components/users/invite-user-bar.d.ts +27 -0
  442. package/dist/components/users/invite-user-bar.js +25 -9
  443. package/dist/components/users/invite-user-bar.js.map +1 -1
  444. package/dist/components/users/role-policy-drift-issues.d.ts +38 -0
  445. package/dist/components/users/role-policy-drift-issues.js +71 -0
  446. package/dist/components/users/role-policy-drift-issues.js.map +1 -0
  447. package/dist/components/users/users-collection-view-source.d.ts +1 -1
  448. package/dist/components/users/users-collection-view-source.js +4 -2
  449. package/dist/components/users/users-collection-view-source.js.map +1 -1
  450. package/dist/components/users/users-module.js +1 -1
  451. package/dist/components/users/users-module.js.map +1 -1
  452. package/dist/components/users/users-schema.d.ts +10 -1
  453. package/dist/components/users/users-schema.js +45 -25
  454. package/dist/components/users/users-schema.js.map +1 -1
  455. package/dist/components/users/users-store.js +3 -3
  456. package/dist/components/users/users-store.js.map +1 -1
  457. package/dist/components/workspace-projects.d.ts +1 -1
  458. package/dist/components/workspace-projects.js +1 -1
  459. package/dist/components/workspace-projects.js.map +1 -1
  460. package/dist/lib/ai/apply-content.js +56 -116
  461. package/dist/lib/ai/apply-content.js.map +1 -1
  462. package/dist/lib/ai/chat-context.d.ts +8 -0
  463. package/dist/lib/ai/chat-context.js +37 -6
  464. package/dist/lib/ai/chat-context.js.map +1 -1
  465. package/dist/lib/ai/chat-state.d.ts +6 -0
  466. package/dist/lib/ai/chat-state.js.map +1 -1
  467. package/dist/lib/ai/constraints.d.ts +20 -5
  468. package/dist/lib/ai/constraints.js +125 -33
  469. package/dist/lib/ai/constraints.js.map +1 -1
  470. package/dist/lib/ai/fake-provider.d.ts +16 -3
  471. package/dist/lib/ai/fake-provider.js +22 -6
  472. package/dist/lib/ai/fake-provider.js.map +1 -1
  473. package/dist/lib/ai/index.d.ts +3 -1
  474. package/dist/lib/ai/index.js +3 -1
  475. package/dist/lib/ai/index.js.map +1 -1
  476. package/dist/lib/ai/leash.d.ts +34 -0
  477. package/dist/lib/ai/leash.js +28 -0
  478. package/dist/lib/ai/leash.js.map +1 -0
  479. package/dist/lib/ai/node-handler.d.ts +1 -1
  480. package/dist/lib/ai/node-handler.js.map +1 -1
  481. package/dist/lib/ai/rich-text-input.d.ts +8 -8
  482. package/dist/lib/ai/rich-text-input.js +2 -1
  483. package/dist/lib/ai/rich-text-input.js.map +1 -1
  484. package/dist/lib/ai/rich-text-translate.d.ts +27 -4
  485. package/dist/lib/ai/rich-text-translate.js +28 -10
  486. package/dist/lib/ai/rich-text-translate.js.map +1 -1
  487. package/dist/lib/ai/shape.d.ts +13 -13
  488. package/dist/lib/ai/shape.js +69 -93
  489. package/dist/lib/ai/shape.js.map +1 -1
  490. package/dist/lib/ai/tools.d.ts +36 -41
  491. package/dist/lib/ai/tools.js +83 -87
  492. package/dist/lib/ai/tools.js.map +1 -1
  493. package/dist/lib/ai/translate-document.d.ts +32 -4
  494. package/dist/lib/ai/translate-document.js +145 -47
  495. package/dist/lib/ai/translate-document.js.map +1 -1
  496. package/dist/lib/ai/types.d.ts +34 -23
  497. package/dist/lib/ai/types.js.map +1 -1
  498. package/dist/lib/ai/update-fields.d.ts +15 -12
  499. package/dist/lib/ai/update-fields.js +112 -188
  500. package/dist/lib/ai/update-fields.js.map +1 -1
  501. package/dist/lib/ai/writer-form.d.ts +116 -0
  502. package/dist/lib/ai/writer-form.js +429 -0
  503. package/dist/lib/ai/writer-form.js.map +1 -0
  504. package/dist/lib/assets/upload.d.ts +31 -2
  505. package/dist/lib/assets/upload.js +61 -6
  506. package/dist/lib/assets/upload.js.map +1 -1
  507. package/dist/lib/clipboard/os-clipboard.d.ts +10 -2
  508. package/dist/lib/clipboard/os-clipboard.js +8 -4
  509. package/dist/lib/clipboard/os-clipboard.js.map +1 -1
  510. package/dist/lib/clipboard/paste-issues.js +1 -1
  511. package/dist/lib/clipboard/paste-issues.js.map +1 -1
  512. package/dist/lib/clipboard/paste-target.d.ts +18 -9
  513. package/dist/lib/clipboard/paste-target.js +10 -9
  514. package/dist/lib/clipboard/paste-target.js.map +1 -1
  515. package/dist/lib/cms/field-patch.d.ts +0 -10
  516. package/dist/lib/cms/field-patch.js +4 -30
  517. package/dist/lib/cms/field-patch.js.map +1 -1
  518. package/dist/lib/cms/published-documents.js +1 -1
  519. package/dist/lib/cms/published-documents.js.map +1 -1
  520. package/dist/lib/cms-views/view-model.d.ts +9 -9
  521. package/dist/lib/cms-views/view-model.js +2 -2
  522. package/dist/lib/cms-views/view-model.js.map +1 -1
  523. package/dist/lib/collection-view/collection-view-source.d.ts +14 -5
  524. package/dist/lib/collection-view/collection-view-source.js.map +1 -1
  525. package/dist/lib/data/collection-content.js +3 -2
  526. package/dist/lib/data/collection-content.js.map +1 -1
  527. package/dist/lib/data/content-session-refresh.d.ts +1 -1
  528. package/dist/lib/data/content-session-refresh.js.map +1 -1
  529. package/dist/lib/data/playground-unload-guard.d.ts +1 -1
  530. package/dist/lib/data/playground-unload-guard.js +1 -1
  531. package/dist/lib/data/playground-unload-guard.js.map +1 -1
  532. package/dist/lib/dnd/drop-rules.d.ts +25 -31
  533. package/dist/lib/dnd/drop-rules.js +53 -59
  534. package/dist/lib/dnd/drop-rules.js.map +1 -1
  535. package/dist/lib/engine-version.d.ts +1 -1
  536. package/dist/lib/engine-version.js +1 -1
  537. package/dist/lib/engine-version.js.map +1 -1
  538. package/dist/lib/i18n/i18n.js +1 -1
  539. package/dist/lib/i18n/i18n.js.map +1 -1
  540. package/dist/lib/i18n/messages.de.js +44 -3
  541. package/dist/lib/i18n/messages.de.js.map +1 -1
  542. package/dist/lib/i18n/messages.en.d.ts +38 -2
  543. package/dist/lib/i18n/messages.en.js +51 -3
  544. package/dist/lib/i18n/messages.en.js.map +1 -1
  545. package/dist/lib/operations/project-runtime-config.d.ts +34 -6
  546. package/dist/lib/operations/project-runtime-config.js +50 -10
  547. package/dist/lib/operations/project-runtime-config.js.map +1 -1
  548. package/dist/lib/operations/publish-with-revision.d.ts +33 -1
  549. package/dist/lib/operations/publish-with-revision.js +32 -0
  550. package/dist/lib/operations/publish-with-revision.js.map +1 -1
  551. package/dist/lib/operations/runtime-validator.d.ts +19 -68
  552. package/dist/lib/operations/runtime-validator.js +12 -138
  553. package/dist/lib/operations/runtime-validator.js.map +1 -1
  554. package/dist/lib/operations/studio-operations.d.ts +17 -2
  555. package/dist/lib/operations/studio-operations.js +18 -11
  556. package/dist/lib/operations/studio-operations.js.map +1 -1
  557. package/dist/lib/persistence/convex-adapter.d.ts +16 -3
  558. package/dist/lib/persistence/convex-adapter.js +83 -47
  559. package/dist/lib/persistence/convex-adapter.js.map +1 -1
  560. package/dist/lib/persistence/index.js +14 -1
  561. package/dist/lib/persistence/index.js.map +1 -1
  562. package/dist/lib/persona/labels.js +4 -2
  563. package/dist/lib/persona/labels.js.map +1 -1
  564. package/dist/lib/query/operations-query.d.ts +1 -1
  565. package/dist/lib/query/operations-query.js +1 -1
  566. package/dist/lib/query/operations-query.js.map +1 -1
  567. package/dist/lib/references/reference-model.js +1 -1
  568. package/dist/lib/references/reference-model.js.map +1 -1
  569. package/dist/lib/references/references-store.js +1 -1
  570. package/dist/lib/references/references-store.js.map +1 -1
  571. package/dist/lib/workspace/index.d.ts +14 -11
  572. package/dist/lib/workspace/index.js +20 -24
  573. package/dist/lib/workspace/index.js.map +1 -1
  574. package/dist/mount.d.ts +28 -0
  575. package/dist/mount.js +27 -0
  576. package/dist/mount.js.map +1 -0
  577. package/dist/router/root.js +10 -10
  578. package/dist/router/root.js.map +1 -1
  579. package/dist/ui/badge.d.ts +1 -1
  580. package/dist/ui/button.d.ts +2 -2
  581. package/package.json +15 -11
  582. package/dist/components/cms/cms-composition-validators.d.ts +0 -16
  583. package/dist/components/cms/cms-composition-validators.js +0 -133
  584. package/dist/components/cms/cms-composition-validators.js.map +0 -1
  585. package/dist/components/cms/publish-clean.d.ts +0 -28
  586. package/dist/components/cms/publish-clean.js +0 -38
  587. package/dist/components/cms/publish-clean.js.map +0 -1
  588. package/dist/components/redirects/redirects-collection-view-source.d.ts +0 -53
  589. package/dist/components/redirects/redirects-collection-view-source.js +0 -146
  590. package/dist/components/redirects/redirects-collection-view-source.js.map +0 -1
  591. package/dist/components/redirects/redirects-module.d.ts +0 -21
  592. package/dist/components/redirects/redirects-module.js +0 -40
  593. package/dist/components/redirects/redirects-module.js.map +0 -1
  594. package/dist/components/redirects/redirects-store.d.ts +0 -68
  595. package/dist/components/redirects/redirects-store.js +0 -132
  596. package/dist/components/redirects/redirects-store.js.map +0 -1
  597. package/dist/lib/assets/asset-placements.d.ts +0 -28
  598. package/dist/lib/assets/asset-placements.js +0 -83
  599. package/dist/lib/assets/asset-placements.js.map +0 -1
@@ -1,10 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useCallback, useContext, useMemo, useRef, useState } from 'react';
3
- import { deleteAsset, runVariantBackfill, runUpload, xhrUploadTransport } from '@elytracms/studio/lib/assets';
4
- import { loadInboundAssetReferenceViews } from '@elytracms/studio/lib/references';
5
- import { getStudioOperations, operationsAssetRepository } from '@elytracms/studio/lib/operations';
6
- import { listStoreState, useOperationCache, useOperationQuery } from '@elytracms/studio/lib/query';
7
- import { createRequiredContext } from '@elytracms/studio/lib/required-context';
3
+ import { deleteAsset, runVariantBackfill, runUpload, xhrUploadTransport } from '../../lib/assets/index.js';
4
+ import { loadInboundAssetReferenceViews } from '../../lib/references/index.js';
5
+ import { getStudioOperations, operationsAssetRepository } from '../../lib/operations/index.js';
6
+ import { projectCollections } from '../../lib/operations/project-runtime-config.js';
7
+ import { listStoreState, useOperationCache, useOperationQuery } from '../../lib/query/index.js';
8
+ import { createRequiredContext } from '../../lib/required-context.js';
8
9
  import { updateAssetMetadata } from './asset-metadata.js';
9
10
  const { Context: AssetsContext, useRequired: useAssets } = createRequiredContext('Assets', { article: 'an' });
10
11
  const EMPTY_ASSETS = [];
@@ -27,6 +28,9 @@ export function AssetsProvider({ projectId, children, }) {
27
28
  const landedRef = useRef(new Map());
28
29
  const uploadSeq = useRef(0);
29
30
  const [uploads, setUploads] = useState([]);
31
+ // EC-568: the asset collection's fields — their `source` declarations map a file's
32
+ // embedded IPTC facts onto `meta` on every upload and replace. Config, read once.
33
+ const assetFields = useMemo(() => projectCollections(projectId).find((collection) => collection.kind === 'asset')?.fields, [projectId]);
30
34
  /** The shared studio operations bundle + the metadata-write facade over it. */
31
35
  const studio = useCallback(async () => {
32
36
  const bundle = await getStudioOperations();
@@ -58,6 +62,7 @@ export function AssetsProvider({ projectId, children, }) {
58
62
  // confirmed through the `assets.upsert` operation command.
59
63
  blobs: adapter.blobs,
60
64
  assets: assetRepo,
65
+ ...(assetFields ? { assetFields } : {}),
61
66
  ...(typeof XMLHttpRequest !== 'undefined' ? { transport: xhrUploadTransport } : {}),
62
67
  }, {
63
68
  projectId,
@@ -71,7 +76,7 @@ export function AssetsProvider({ projectId, children, }) {
71
76
  setUploads((prev) => prev.filter((u) => u.id !== uploadId));
72
77
  await refresh();
73
78
  }
74
- }, [studio, projectId, refresh, setUploadState]);
79
+ }, [studio, projectId, refresh, setUploadState, assetFields]);
75
80
  const upload = useCallback((files) => {
76
81
  const ids = [];
77
82
  for (const file of files) {
@@ -132,6 +137,21 @@ export function AssetsProvider({ projectId, children, }) {
132
137
  }
133
138
  return result;
134
139
  }, [studio, projectId, cache]);
140
+ const replaceFile = useCallback(async (assetId, file) => {
141
+ const { adapter, assetRepo } = await studio();
142
+ // EC-568: THE upload path, in replace mode — not a second uploader.
143
+ const result = await runUpload({
144
+ blobs: adapter.blobs,
145
+ assets: assetRepo,
146
+ ...(assetFields ? { assetFields } : {}),
147
+ ...(typeof XMLHttpRequest !== 'undefined' ? { transport: xhrUploadTransport } : {}),
148
+ }, { projectId, file, replace: { assetId } });
149
+ if (!result.ok)
150
+ return { ok: false, message: result.failure.message };
151
+ const updated = result.asset;
152
+ cache.patch(assetsQueryKey(projectId), (prev) => (prev ?? []).map((a) => (a.id === assetId ? updated : a)));
153
+ return { ok: true, asset: updated };
154
+ }, [studio, projectId, cache, assetFields]);
135
155
  const generatePreviews = useCallback(async (handlers) => {
136
156
  const { adapter, assetRepo } = await studio();
137
157
  // Reads (list) + the variant patch (upsert) go through the operations facade;
@@ -160,6 +180,7 @@ export function AssetsProvider({ projectId, children, }) {
160
180
  usagesOf,
161
181
  remove,
162
182
  updateMetadata,
183
+ replaceFile,
163
184
  generatePreviews,
164
185
  refresh,
165
186
  }), [
@@ -175,6 +196,7 @@ export function AssetsProvider({ projectId, children, }) {
175
196
  usagesOf,
176
197
  remove,
177
198
  updateMetadata,
199
+ replaceFile,
178
200
  generatePreviews,
179
201
  refresh,
180
202
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"assets-store.js","sourceRoot":"","sources":["../../../src/components/assets/assets-store.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useContext, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { AssetRecord, AssetRepository } from '@elytracms/core/persistence'\nimport { deleteAsset, runVariantBackfill, runUpload, xhrUploadTransport } from '@elytracms/studio/lib/assets'\nimport type {\n BackfillProgress,\n BackfillSignal,\n DeleteAssetResult,\n UploadState,\n UploadableFile,\n} from '@elytracms/studio/lib/assets'\nimport { loadInboundAssetReferenceViews } from '@elytracms/studio/lib/references'\nimport type { ReferenceEntryView } from '@elytracms/studio/lib/references'\nimport { getStudioOperations, operationsAssetRepository } from '@elytracms/studio/lib/operations'\nimport type { OperationsClient, StudioOperations } from '@elytracms/studio/lib/operations'\nimport { listStoreState, useOperationCache, useOperationQuery } from '@elytracms/studio/lib/query'\nimport { createRequiredContext } from '@elytracms/studio/lib/required-context'\nimport { updateAssetMetadata } from './asset-metadata'\nimport type { AssetMetadataPatch, UpdateAssetMetadataResult } from './asset-metadata'\n\n/**\n * Asset library store (EC-151, EC-141). Asset *metadata* mutations go through\n * the shared operations client (`assets.*` commands — validate → authorize →\n * mutate → event); blob *bytes* ride on `adapter.blobs`, which is transport,\n * not domain. The UI works identically against the in-memory seed and a live\n * Convex deployment.\n */\n\nexport interface UploadEntry {\n id: string\n filename: string\n state: UploadState\n}\n\nexport interface AssetsStore {\n /** The active project (e.g. for reference deep links, EC-202). */\n projectId: string\n status: 'loading' | 'ready' | 'error'\n errorMessage?: string\n assets: readonly AssetRecord[]\n uploads: readonly UploadEntry[]\n /**\n * Queue files for upload. Returns the upload-queue id of each file, in order, so a\n * caller can follow exactly ITS uploads (EC-588 A7) — see {@link landedAssetId}.\n */\n upload(files: Iterable<UploadableFile>): readonly string[]\n /**\n * The asset id an upload landed as, once its record is confirmed (EC-588 A7);\n * `undefined` while it runs, after it failed, or for an id this store never issued.\n * A completed upload leaves the queue, so this is where its outcome stays readable.\n */\n landedAssetId(uploadId: string): string | undefined\n retryUpload(uploadId: string): void\n dismissUpload(uploadId: string): void\n /**\n * Index-accurate inbound references of an asset (EC-202): the \"used by\"\n * pre-flight of the block-or-confirm delete dialog, read from the EC-155\n * reference index (not the retired string scan).\n */\n usagesOf(assetId: string): Promise<ReferenceEntryView[]>\n remove(assetId: string, options?: { confirmInUse?: boolean }): Promise<DeleteAssetResult>\n /** Edit alt/title (EC-152); persists through the operations asset facade. */\n updateMetadata(assetId: string, patch: AssetMetadataPatch): Promise<UpdateAssetMetadataResult>\n /**\n * Backfill downscaled previews onto existing images (EC-400 AD-1). Sequential,\n * idempotent, cancellable; each patch flows through the operations facade and\n * the shared query cache (AD-5/AD-17). Reports progress via `handlers.onProgress`\n * and stops when `handlers.signal.cancelled` flips.\n */\n generatePreviews(handlers?: {\n onProgress?: (progress: BackfillProgress) => void\n signal?: BackfillSignal\n }): Promise<BackfillProgress>\n refresh(): Promise<void>\n}\n\nconst { Context: AssetsContext, useRequired: useAssets } = createRequiredContext<AssetsStore>(\n 'Assets',\n { article: 'an' },\n)\n\nconst EMPTY_ASSETS: readonly AssetRecord[] = []\n\n/** Cache key for a project's asset library (shared by the query and its patches). */\nfunction assetsQueryKey(projectId: string): readonly [string, string] {\n return ['assets', projectId]\n}\n\n/** Load a project's asset library (pure over the operations client). A list\n * failure throws so the query surfaces it as an error status. */\nasync function loadAssets(operations: OperationsClient, projectId: string): Promise<AssetRecord[]> {\n const result = await operations.assets.list({ projectId })\n if (!result.ok) throw new Error(result.error.message)\n return [...result.value]\n}\n\nexport function AssetsProvider({\n projectId,\n children,\n}: {\n projectId: string\n children: ReactNode\n}) {\n const uploadFilesRef = useRef(new Map<string, UploadableFile>())\n // EC-588 A7: upload id → the asset id it landed as. Written BEFORE the entry leaves\n // the queue, so a reader never sees an upload that is neither running nor landed.\n const landedRef = useRef(new Map<string, string>())\n const uploadSeq = useRef(0)\n const [uploads, setUploads] = useState<readonly UploadEntry[]>([])\n\n /** The shared studio operations bundle + the metadata-write facade over it. */\n const studio = useCallback(async (): Promise<\n StudioOperations & { assetRepo: AssetRepository }\n > => {\n const bundle = await getStudioOperations()\n return { ...bundle, assetRepo: operationsAssetRepository(bundle.operations) }\n }, [])\n\n // The library list, read through the one operations-backed query cache\n // (EC-308/AD-17). No `invalidateOn`: this provider is the sole writer of its\n // project's assets, so its own mutations patch the cache in place (the EC-301\n // rule — a metadata or focal-point edit must never trigger a full re-list).\n const query = useOperationQuery<AssetRecord[]>({\n queryKey: assetsQueryKey(projectId),\n loader: (operations) => loadAssets(operations, projectId),\n })\n const cache = useOperationCache()\n const refetch = query.refetch\n const assets = query.data ?? EMPTY_ASSETS\n const { status, errorMessage } = listStoreState(query)\n\n /** Re-list from the backend (the public escape hatch + post-upload reconcile). */\n const refresh = useCallback(async () => {\n await refetch()\n }, [refetch])\n\n const setUploadState = useCallback((uploadId: string, state: UploadState) => {\n setUploads((prev) => prev.map((u) => (u.id === uploadId ? { ...u, state } : u)))\n }, [])\n\n const runEntry = useCallback(\n async (uploadId: string, file: UploadableFile, previous?: UploadState) => {\n const { adapter, assetRepo } = await studio()\n const result = await runUpload(\n {\n // Bytes ride on blob storage (transport); the metadata record is\n // confirmed through the `assets.upsert` operation command.\n blobs: adapter.blobs,\n assets: assetRepo,\n ...(typeof XMLHttpRequest !== 'undefined' ? { transport: xhrUploadTransport } : {}),\n },\n {\n projectId,\n file,\n onState: (state) => setUploadState(uploadId, state),\n },\n previous,\n )\n if (result.ok) {\n // Completed uploads leave the queue; the asset appears in the library.\n landedRef.current.set(uploadId, result.asset.id)\n uploadFilesRef.current.delete(uploadId)\n setUploads((prev) => prev.filter((u) => u.id !== uploadId))\n await refresh()\n }\n },\n [studio, projectId, refresh, setUploadState],\n )\n\n const upload = useCallback(\n (files: Iterable<UploadableFile>): readonly string[] => {\n const ids: string[] = []\n for (const file of files) {\n uploadSeq.current += 1\n const uploadId = `upload-${uploadSeq.current}`\n ids.push(uploadId)\n uploadFilesRef.current.set(uploadId, file)\n setUploads((prev) => [\n ...prev,\n { id: uploadId, filename: file.name, state: { phase: 'idle', progress: 0, attempt: 0 } },\n ])\n void runEntry(uploadId, file)\n }\n return ids\n },\n [runEntry],\n )\n\n const landedAssetId = useCallback((uploadId: string) => landedRef.current.get(uploadId), [])\n\n const retryUpload = useCallback(\n (uploadId: string) => {\n const file = uploadFilesRef.current.get(uploadId)\n const entry = uploads.find((u) => u.id === uploadId)\n if (!file || !entry || entry.state.phase !== 'failed') return\n void runEntry(uploadId, file, entry.state)\n },\n [runEntry, uploads],\n )\n\n const dismissUpload = useCallback((uploadId: string) => {\n uploadFilesRef.current.delete(uploadId)\n setUploads((prev) => prev.filter((u) => u.id !== uploadId))\n }, [])\n\n const usagesOf = useCallback(\n // Index-accurate (EC-202): inbound references from the EC-155 index via the\n // operations queries — the same machinery the References panel uses.\n async (assetId: string) => loadInboundAssetReferenceViews(projectId, assetId),\n [projectId],\n )\n\n const remove = useCallback(\n async (assetId: string, options?: { confirmInUse?: boolean }) => {\n const { adapter, assetRepo } = await studio()\n // Metadata removal goes through the `assets.remove` command (via the\n // facade); blob byte deletion stays on the adapter; the usage gate reads\n // the EC-155 reference index (`adapter.references`, EC-202).\n const result = await deleteAsset(\n { assets: assetRepo, blobs: adapter.blobs, references: adapter.references },\n projectId,\n assetId,\n options,\n )\n // Drop the one removed record from the cache rather than re-listing the whole\n // library (EC-301 perf, mirrors updateMetadata) — a delete must not trigger a full\n // `assets.list` round-trip on top of the deletion itself.\n if (result.outcome === 'deleted' || result.outcome === 'not-found') {\n cache.patch<AssetRecord[]>(assetsQueryKey(projectId), (prev) =>\n (prev ?? []).filter((a) => a.id !== assetId),\n )\n }\n return result\n },\n [studio, projectId, cache],\n )\n\n const updateMetadata = useCallback(\n async (assetId: string, patch: AssetMetadataPatch) => {\n // Metadata writes go through the `assets.upsert` command via the\n // operations facade (EC-141) — never straight at a repository.\n const { assetRepo } = await studio()\n const result = await updateAssetMetadata({ assets: assetRepo }, projectId, assetId, patch)\n if (result.ok) {\n // Patch the one updated record into the cache rather than re-listing the\n // whole library (EC-301). A focal-point or alt edit must not trigger a full\n // `assets.list` round-trip — that re-list (plus the marker only moving once\n // it returned) was the focal-point picker's lag and query storm.\n const updated = result.asset\n cache.patch<AssetRecord[]>(assetsQueryKey(projectId), (prev) =>\n (prev ?? []).map((a) => (a.id === assetId ? updated : a)),\n )\n }\n return result\n },\n [studio, projectId, cache],\n )\n\n const generatePreviews = useCallback(\n async (handlers?: {\n onProgress?: (progress: BackfillProgress) => void\n signal?: BackfillSignal\n }) => {\n const { adapter, assetRepo } = await studio()\n // Reads (list) + the variant patch (upsert) go through the operations facade;\n // bytes ride on `adapter.blobs`. The backfill is per-variant (EC-420 AD-4):\n // one fetch of each original fills whichever of thumb/lqip is missing. Each\n // generated record is patched into the shared cache in place (EC-301 perf,\n // AD-17) so the grid updates live without a full re-list.\n return runVariantBackfill(\n {\n assets: assetRepo,\n blobs: adapter.blobs,\n onGenerated: (updated) =>\n cache.patch<AssetRecord[]>(assetsQueryKey(projectId), (prev) =>\n (prev ?? []).map((a) => (a.id === updated.id ? updated : a)),\n ),\n ...(handlers?.onProgress ? { onProgress: handlers.onProgress } : {}),\n ...(handlers?.signal ? { signal: handlers.signal } : {}),\n },\n projectId,\n )\n },\n [studio, projectId, cache],\n )\n\n const store = useMemo<AssetsStore>(\n () => ({\n projectId,\n status,\n ...(errorMessage !== undefined ? { errorMessage } : {}),\n assets,\n uploads,\n upload,\n landedAssetId,\n retryUpload,\n dismissUpload,\n usagesOf,\n remove,\n updateMetadata,\n generatePreviews,\n refresh,\n }),\n [\n projectId,\n status,\n errorMessage,\n assets,\n uploads,\n upload,\n landedAssetId,\n retryUpload,\n dismissUpload,\n usagesOf,\n remove,\n updateMetadata,\n generatePreviews,\n refresh,\n ],\n )\n\n return <AssetsContext.Provider value={store}>{children}</AssetsContext.Provider>\n}\n\nexport { useAssets }\n\n/**\n * The assets store when an `<AssetsProvider>` is mounted above, else `null`.\n * Lets surfaces outside the Assets section (the CMS asset picker, EC-152)\n * reuse an ambient provider or mount their own.\n */\nexport function useOptionalAssets(): AssetsStore | null {\n return useContext(AssetsContext)\n}\n"]}
1
+ {"version":3,"file":"assets-store.js","sourceRoot":"","sources":["../../../src/components/assets/assets-store.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useContext, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { AssetRecord, AssetRepository } from '@elytracms/core/persistence'\nimport { deleteAsset, runVariantBackfill, runUpload, xhrUploadTransport } from '../../lib/assets'\nimport type {\n BackfillProgress,\n BackfillSignal,\n DeleteAssetResult,\n UploadState,\n UploadableFile,\n} from '../../lib/assets'\nimport { loadInboundAssetReferenceViews } from '../../lib/references'\nimport type { ReferenceEntryView } from '../../lib/references'\nimport { getStudioOperations, operationsAssetRepository } from '../../lib/operations'\nimport { projectCollections } from '../../lib/operations/project-runtime-config'\nimport type { OperationsClient, StudioOperations } from '../../lib/operations'\nimport { listStoreState, useOperationCache, useOperationQuery } from '../../lib/query'\nimport { createRequiredContext } from '../../lib/required-context'\nimport { updateAssetMetadata } from './asset-metadata'\nimport type { AssetMetadataPatch, UpdateAssetMetadataResult } from './asset-metadata'\n\n/**\n * Asset library store (EC-151, EC-141). Asset *metadata* mutations go through\n * the shared operations client (`assets.*` commands — validate → authorize →\n * mutate → event); blob *bytes* ride on `adapter.blobs`, which is transport,\n * not domain. The UI works identically against the in-memory seed and a live\n * Convex deployment.\n */\n\nexport interface UploadEntry {\n id: string\n filename: string\n state: UploadState\n}\n\nexport interface AssetsStore {\n /** The active project (e.g. for reference deep links, EC-202). */\n projectId: string\n status: 'loading' | 'ready' | 'error'\n errorMessage?: string\n assets: readonly AssetRecord[]\n uploads: readonly UploadEntry[]\n /**\n * Queue files for upload. Returns the upload-queue id of each file, in order, so a\n * caller can follow exactly ITS uploads (EC-588 A7) — see {@link landedAssetId}.\n */\n upload(files: Iterable<UploadableFile>): readonly string[]\n /**\n * The asset id an upload landed as, once its record is confirmed (EC-588 A7);\n * `undefined` while it runs, after it failed, or for an id this store never issued.\n * A completed upload leaves the queue, so this is where its outcome stays readable.\n */\n landedAssetId(uploadId: string): string | undefined\n retryUpload(uploadId: string): void\n dismissUpload(uploadId: string): void\n /**\n * Index-accurate inbound references of an asset (EC-202): the \"used by\"\n * pre-flight of the block-or-confirm delete dialog, read from the EC-155\n * reference index (not the retired string scan).\n */\n usagesOf(assetId: string): Promise<ReferenceEntryView[]>\n remove(assetId: string, options?: { confirmInUse?: boolean }): Promise<DeleteAssetResult>\n /** Edit alt/title (EC-152); persists through the operations asset facade. */\n updateMetadata(assetId: string, patch: AssetMetadataPatch): Promise<UpdateAssetMetadataResult>\n /**\n * EC-568 — replace the file inside an asset: the same id, the new bytes, everything\n * derived regenerated through THE upload path, the file's embedded metadata over\n * `meta` per key (else kept), the focal point kept.\n */\n replaceFile(assetId: string, file: UploadableFile): Promise<ReplaceFileResult>\n /**\n * Backfill downscaled previews onto existing images (EC-400 AD-1). Sequential,\n * idempotent, cancellable; each patch flows through the operations facade and\n * the shared query cache (AD-5/AD-17). Reports progress via `handlers.onProgress`\n * and stops when `handlers.signal.cancelled` flips.\n */\n generatePreviews(handlers?: {\n onProgress?: (progress: BackfillProgress) => void\n signal?: BackfillSignal\n }): Promise<BackfillProgress>\n refresh(): Promise<void>\n}\n\n/** The outcome of {@link AssetsStore.replaceFile}. */\nexport type ReplaceFileResult = { ok: true; asset: AssetRecord } | { ok: false; message: string }\n\nconst { Context: AssetsContext, useRequired: useAssets } = createRequiredContext<AssetsStore>(\n 'Assets',\n { article: 'an' },\n)\n\nconst EMPTY_ASSETS: readonly AssetRecord[] = []\n\n/** Cache key for a project's asset library (shared by the query and its patches). */\nfunction assetsQueryKey(projectId: string): readonly [string, string] {\n return ['assets', projectId]\n}\n\n/** Load a project's asset library (pure over the operations client). A list\n * failure throws so the query surfaces it as an error status. */\nasync function loadAssets(operations: OperationsClient, projectId: string): Promise<AssetRecord[]> {\n const result = await operations.assets.list({ projectId })\n if (!result.ok) throw new Error(result.error.message)\n return [...result.value]\n}\n\nexport function AssetsProvider({\n projectId,\n children,\n}: {\n projectId: string\n children: ReactNode\n}) {\n const uploadFilesRef = useRef(new Map<string, UploadableFile>())\n // EC-588 A7: upload id → the asset id it landed as. Written BEFORE the entry leaves\n // the queue, so a reader never sees an upload that is neither running nor landed.\n const landedRef = useRef(new Map<string, string>())\n const uploadSeq = useRef(0)\n const [uploads, setUploads] = useState<readonly UploadEntry[]>([])\n\n // EC-568: the asset collection's fields — their `source` declarations map a file's\n // embedded IPTC facts onto `meta` on every upload and replace. Config, read once.\n const assetFields = useMemo(\n () => projectCollections(projectId).find((collection) => collection.kind === 'asset')?.fields,\n [projectId],\n )\n\n /** The shared studio operations bundle + the metadata-write facade over it. */\n const studio = useCallback(async (): Promise<\n StudioOperations & { assetRepo: AssetRepository }\n > => {\n const bundle = await getStudioOperations()\n return { ...bundle, assetRepo: operationsAssetRepository(bundle.operations) }\n }, [])\n\n // The library list, read through the one operations-backed query cache\n // (EC-308/AD-17). No `invalidateOn`: this provider is the sole writer of its\n // project's assets, so its own mutations patch the cache in place (the EC-301\n // rule — a metadata or focal-point edit must never trigger a full re-list).\n const query = useOperationQuery<AssetRecord[]>({\n queryKey: assetsQueryKey(projectId),\n loader: (operations) => loadAssets(operations, projectId),\n })\n const cache = useOperationCache()\n const refetch = query.refetch\n const assets = query.data ?? EMPTY_ASSETS\n const { status, errorMessage } = listStoreState(query)\n\n /** Re-list from the backend (the public escape hatch + post-upload reconcile). */\n const refresh = useCallback(async () => {\n await refetch()\n }, [refetch])\n\n const setUploadState = useCallback((uploadId: string, state: UploadState) => {\n setUploads((prev) => prev.map((u) => (u.id === uploadId ? { ...u, state } : u)))\n }, [])\n\n const runEntry = useCallback(\n async (uploadId: string, file: UploadableFile, previous?: UploadState) => {\n const { adapter, assetRepo } = await studio()\n const result = await runUpload(\n {\n // Bytes ride on blob storage (transport); the metadata record is\n // confirmed through the `assets.upsert` operation command.\n blobs: adapter.blobs,\n assets: assetRepo,\n ...(assetFields ? { assetFields } : {}),\n ...(typeof XMLHttpRequest !== 'undefined' ? { transport: xhrUploadTransport } : {}),\n },\n {\n projectId,\n file,\n onState: (state) => setUploadState(uploadId, state),\n },\n previous,\n )\n if (result.ok) {\n // Completed uploads leave the queue; the asset appears in the library.\n landedRef.current.set(uploadId, result.asset.id)\n uploadFilesRef.current.delete(uploadId)\n setUploads((prev) => prev.filter((u) => u.id !== uploadId))\n await refresh()\n }\n },\n [studio, projectId, refresh, setUploadState, assetFields],\n )\n\n const upload = useCallback(\n (files: Iterable<UploadableFile>): readonly string[] => {\n const ids: string[] = []\n for (const file of files) {\n uploadSeq.current += 1\n const uploadId = `upload-${uploadSeq.current}`\n ids.push(uploadId)\n uploadFilesRef.current.set(uploadId, file)\n setUploads((prev) => [\n ...prev,\n { id: uploadId, filename: file.name, state: { phase: 'idle', progress: 0, attempt: 0 } },\n ])\n void runEntry(uploadId, file)\n }\n return ids\n },\n [runEntry],\n )\n\n const landedAssetId = useCallback((uploadId: string) => landedRef.current.get(uploadId), [])\n\n const retryUpload = useCallback(\n (uploadId: string) => {\n const file = uploadFilesRef.current.get(uploadId)\n const entry = uploads.find((u) => u.id === uploadId)\n if (!file || !entry || entry.state.phase !== 'failed') return\n void runEntry(uploadId, file, entry.state)\n },\n [runEntry, uploads],\n )\n\n const dismissUpload = useCallback((uploadId: string) => {\n uploadFilesRef.current.delete(uploadId)\n setUploads((prev) => prev.filter((u) => u.id !== uploadId))\n }, [])\n\n const usagesOf = useCallback(\n // Index-accurate (EC-202): inbound references from the EC-155 index via the\n // operations queries — the same machinery the References panel uses.\n async (assetId: string) => loadInboundAssetReferenceViews(projectId, assetId),\n [projectId],\n )\n\n const remove = useCallback(\n async (assetId: string, options?: { confirmInUse?: boolean }) => {\n const { adapter, assetRepo } = await studio()\n // Metadata removal goes through the `assets.remove` command (via the\n // facade); blob byte deletion stays on the adapter; the usage gate reads\n // the EC-155 reference index (`adapter.references`, EC-202).\n const result = await deleteAsset(\n { assets: assetRepo, blobs: adapter.blobs, references: adapter.references },\n projectId,\n assetId,\n options,\n )\n // Drop the one removed record from the cache rather than re-listing the whole\n // library (EC-301 perf, mirrors updateMetadata) — a delete must not trigger a full\n // `assets.list` round-trip on top of the deletion itself.\n if (result.outcome === 'deleted' || result.outcome === 'not-found') {\n cache.patch<AssetRecord[]>(assetsQueryKey(projectId), (prev) =>\n (prev ?? []).filter((a) => a.id !== assetId),\n )\n }\n return result\n },\n [studio, projectId, cache],\n )\n\n const updateMetadata = useCallback(\n async (assetId: string, patch: AssetMetadataPatch) => {\n // Metadata writes go through the `assets.upsert` command via the\n // operations facade (EC-141) — never straight at a repository.\n const { assetRepo } = await studio()\n const result = await updateAssetMetadata({ assets: assetRepo }, projectId, assetId, patch)\n if (result.ok) {\n // Patch the one updated record into the cache rather than re-listing the\n // whole library (EC-301). A focal-point or alt edit must not trigger a full\n // `assets.list` round-trip — that re-list (plus the marker only moving once\n // it returned) was the focal-point picker's lag and query storm.\n const updated = result.asset\n cache.patch<AssetRecord[]>(assetsQueryKey(projectId), (prev) =>\n (prev ?? []).map((a) => (a.id === assetId ? updated : a)),\n )\n }\n return result\n },\n [studio, projectId, cache],\n )\n\n const replaceFile = useCallback(\n async (assetId: string, file: UploadableFile): Promise<ReplaceFileResult> => {\n const { adapter, assetRepo } = await studio()\n // EC-568: THE upload path, in replace mode — not a second uploader.\n const result = await runUpload(\n {\n blobs: adapter.blobs,\n assets: assetRepo,\n ...(assetFields ? { assetFields } : {}),\n ...(typeof XMLHttpRequest !== 'undefined' ? { transport: xhrUploadTransport } : {}),\n },\n { projectId, file, replace: { assetId } },\n )\n if (!result.ok) return { ok: false, message: result.failure.message }\n const updated = result.asset\n cache.patch<AssetRecord[]>(assetsQueryKey(projectId), (prev) =>\n (prev ?? []).map((a) => (a.id === assetId ? updated : a)),\n )\n return { ok: true, asset: updated }\n },\n [studio, projectId, cache, assetFields],\n )\n\n const generatePreviews = useCallback(\n async (handlers?: {\n onProgress?: (progress: BackfillProgress) => void\n signal?: BackfillSignal\n }) => {\n const { adapter, assetRepo } = await studio()\n // Reads (list) + the variant patch (upsert) go through the operations facade;\n // bytes ride on `adapter.blobs`. The backfill is per-variant (EC-420 AD-4):\n // one fetch of each original fills whichever of thumb/lqip is missing. Each\n // generated record is patched into the shared cache in place (EC-301 perf,\n // AD-17) so the grid updates live without a full re-list.\n return runVariantBackfill(\n {\n assets: assetRepo,\n blobs: adapter.blobs,\n onGenerated: (updated) =>\n cache.patch<AssetRecord[]>(assetsQueryKey(projectId), (prev) =>\n (prev ?? []).map((a) => (a.id === updated.id ? updated : a)),\n ),\n ...(handlers?.onProgress ? { onProgress: handlers.onProgress } : {}),\n ...(handlers?.signal ? { signal: handlers.signal } : {}),\n },\n projectId,\n )\n },\n [studio, projectId, cache],\n )\n\n const store = useMemo<AssetsStore>(\n () => ({\n projectId,\n status,\n ...(errorMessage !== undefined ? { errorMessage } : {}),\n assets,\n uploads,\n upload,\n landedAssetId,\n retryUpload,\n dismissUpload,\n usagesOf,\n remove,\n updateMetadata,\n replaceFile,\n generatePreviews,\n refresh,\n }),\n [\n projectId,\n status,\n errorMessage,\n assets,\n uploads,\n upload,\n landedAssetId,\n retryUpload,\n dismissUpload,\n usagesOf,\n remove,\n updateMetadata,\n replaceFile,\n generatePreviews,\n refresh,\n ],\n )\n\n return <AssetsContext.Provider value={store}>{children}</AssetsContext.Provider>\n}\n\nexport { useAssets }\n\n/**\n * The assets store when an `<AssetsProvider>` is mounted above, else `null`.\n * Lets surfaces outside the Assets section (the CMS asset picker, EC-152)\n * reuse an ambient provider or mount their own.\n */\nexport function useOptionalAssets(): AssetsStore | null {\n return useContext(AssetsContext)\n}\n"]}
@@ -6,7 +6,7 @@
6
6
  * instead of a new persistence mechanism. Unavailable storage (SSR, privacy
7
7
  * mode) degrades to the default every session — never a crash.
8
8
  */
9
- import { createEnumPreference } from '@elytracms/studio/lib/local-preference';
9
+ import { createEnumPreference } from '../../lib/local-preference.js';
10
10
  export const ASSETS_VIEW_STORAGE_KEY = 'elytra-assets-view-mode';
11
11
  export const DEFAULT_ASSETS_VIEW_MODE = 'list';
12
12
  const preference = createEnumPreference(ASSETS_VIEW_STORAGE_KEY, ['list', 'grid'], DEFAULT_ASSETS_VIEW_MODE);
@@ -1 +1 @@
1
- {"version":3,"file":"assets-view-preference.js","sourceRoot":"","sources":["../../../src/components/assets/assets-view-preference.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAA;AAI7E,MAAM,CAAC,MAAM,uBAAuB,GAAG,yBAAyB,CAAA;AAChE,MAAM,CAAC,MAAM,wBAAwB,GAAmB,MAAM,CAAA;AAE9D,MAAM,UAAU,GAAG,oBAAoB,CACrC,uBAAuB,EACvB,CAAC,MAAM,EAAE,MAAM,CAAC,EAChB,wBAAwB,CACzB,CAAA;AAED,oFAAoF;AACpF,MAAM,UAAU,kBAAkB;IAChC,OAAO,UAAU,CAAC,IAAI,EAAE,CAAA;AAC1B,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,mBAAmB,CAAC,IAAoB;IACtD,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;AACxB,CAAC","sourcesContent":["/**\n * Assets list/grid view-mode preference (EC-319). The same class of transient,\n * personal-local UI preference as the version-compare view mode (EC-317,\n * `lib/diff-view-preference.ts`), the theme, and rail-collapse: it never touches\n * the project graph or any document, so it rides `localStorage` best-effort\n * instead of a new persistence mechanism. Unavailable storage (SSR, privacy\n * mode) degrades to the default every session — never a crash.\n */\nimport { createEnumPreference } from '@elytracms/studio/lib/local-preference'\n\nexport type AssetsViewMode = 'list' | 'grid'\n\nexport const ASSETS_VIEW_STORAGE_KEY = 'elytra-assets-view-mode'\nexport const DEFAULT_ASSETS_VIEW_MODE: AssetsViewMode = 'list'\n\nconst preference = createEnumPreference<AssetsViewMode>(\n ASSETS_VIEW_STORAGE_KEY,\n ['list', 'grid'],\n DEFAULT_ASSETS_VIEW_MODE,\n)\n\n/** Read the persisted preference, or the default when unset/unavailable/invalid. */\nexport function readAssetsViewMode(): AssetsViewMode {\n return preference.read()\n}\n\n/** Persist the preference (best-effort; storage may be unavailable). */\nexport function storeAssetsViewMode(mode: AssetsViewMode): void {\n preference.store(mode)\n}\n"]}
1
+ {"version":3,"file":"assets-view-preference.js","sourceRoot":"","sources":["../../../src/components/assets/assets-view-preference.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AAIjE,MAAM,CAAC,MAAM,uBAAuB,GAAG,yBAAyB,CAAA;AAChE,MAAM,CAAC,MAAM,wBAAwB,GAAmB,MAAM,CAAA;AAE9D,MAAM,UAAU,GAAG,oBAAoB,CACrC,uBAAuB,EACvB,CAAC,MAAM,EAAE,MAAM,CAAC,EAChB,wBAAwB,CACzB,CAAA;AAED,oFAAoF;AACpF,MAAM,UAAU,kBAAkB;IAChC,OAAO,UAAU,CAAC,IAAI,EAAE,CAAA;AAC1B,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,mBAAmB,CAAC,IAAoB;IACtD,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;AACxB,CAAC","sourcesContent":["/**\n * Assets list/grid view-mode preference (EC-319). The same class of transient,\n * personal-local UI preference as the version-compare view mode (EC-317,\n * `lib/diff-view-preference.ts`), the theme, and rail-collapse: it never touches\n * the project graph or any document, so it rides `localStorage` best-effort\n * instead of a new persistence mechanism. Unavailable storage (SSR, privacy\n * mode) degrades to the default every session — never a crash.\n */\nimport { createEnumPreference } from '../../lib/local-preference'\n\nexport type AssetsViewMode = 'list' | 'grid'\n\nexport const ASSETS_VIEW_STORAGE_KEY = 'elytra-assets-view-mode'\nexport const DEFAULT_ASSETS_VIEW_MODE: AssetsViewMode = 'list'\n\nconst preference = createEnumPreference<AssetsViewMode>(\n ASSETS_VIEW_STORAGE_KEY,\n ['list', 'grid'],\n DEFAULT_ASSETS_VIEW_MODE,\n)\n\n/** Read the persisted preference, or the default when unset/unavailable/invalid. */\nexport function readAssetsViewMode(): AssetsViewMode {\n return preference.read()\n}\n\n/** Persist the preference (best-effort; storage may be unavailable). */\nexport function storeAssetsViewMode(mode: AssetsViewMode): void {\n preference.store(mode)\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import type { AssetRecord } from '@elytracms/core/persistence';
2
+ /**
3
+ * What a file REPLACE accepts (EC-568): a file of the same kind as the current one — an
4
+ * image stays an image (every field that holds it accepted an image), a video a video.
5
+ * Any other kind is unrestricted. Pure, for the test.
6
+ */
7
+ export declare function replaceAccept(record: Pick<AssetRecord, 'contentType'>): readonly string[] | undefined;
8
+ /**
9
+ * "Replace file" (EC-568 A): new bytes inside the SAME asset — every placement that
10
+ * points at it shows the new file. Runs THE upload path with `replace` (the store's
11
+ * `replaceFile`), so the derived data is regenerated and the file's embedded metadata
12
+ * overrides `meta` per key. Used by the asset library's detail surface and the image
13
+ * options dialog. `beforeReplace` lets a caller flush an edit buffer first, so a stale
14
+ * buffer can never overwrite what the new file brought.
15
+ */
16
+ export declare function ReplaceFileButton({ record, disabled, disabledHint, beforeReplace, onReplaced, }: {
17
+ record: AssetRecord;
18
+ disabled?: boolean;
19
+ disabledHint?: string;
20
+ beforeReplace?: () => Promise<void>;
21
+ onReplaced?: (asset: AssetRecord) => void;
22
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,59 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { RefreshCw } from 'lucide-react';
3
+ import { useRef, useState } from 'react';
4
+ import { assetAcceptAttr } from '@elytracms/core/cms-core';
5
+ import { Button, Text } from '@elytracms/studio/ui';
6
+ import { translate } from '../../lib/i18n/index.js';
7
+ import { splitByAccept } from '../cms/asset-picker-core.js';
8
+ import { useOptionalAssets } from './assets-store.js';
9
+ /**
10
+ * What a file REPLACE accepts (EC-568): a file of the same kind as the current one — an
11
+ * image stays an image (every field that holds it accepted an image), a video a video.
12
+ * Any other kind is unrestricted. Pure, for the test.
13
+ */
14
+ export function replaceAccept(record) {
15
+ const kind = record.contentType.split('/')[0];
16
+ return kind === 'image' || kind === 'video' || kind === 'audio' ? [kind] : undefined;
17
+ }
18
+ /**
19
+ * "Replace file" (EC-568 A): new bytes inside the SAME asset — every placement that
20
+ * points at it shows the new file. Runs THE upload path with `replace` (the store's
21
+ * `replaceFile`), so the derived data is regenerated and the file's embedded metadata
22
+ * overrides `meta` per key. Used by the asset library's detail surface and the image
23
+ * options dialog. `beforeReplace` lets a caller flush an edit buffer first, so a stale
24
+ * buffer can never overwrite what the new file brought.
25
+ */
26
+ export function ReplaceFileButton({ record, disabled, disabledHint, beforeReplace, onReplaced, }) {
27
+ const assets = useOptionalAssets();
28
+ const input = useRef(null);
29
+ const [busy, setBusy] = useState(false);
30
+ const [error, setError] = useState(null);
31
+ const accept = replaceAccept(record);
32
+ if (!assets)
33
+ return null;
34
+ async function replace(files) {
35
+ if (!assets || files.length === 0)
36
+ return;
37
+ const { accepted, rejected } = splitByAccept(files.slice(0, 1), accept);
38
+ const next = accepted[0];
39
+ if (!next) {
40
+ setError(translate('cms.asset.replaceWrongKind', { name: rejected[0] ?? '', types: (accept ?? []).join(', ') }));
41
+ return;
42
+ }
43
+ setBusy(true);
44
+ setError(null);
45
+ await beforeReplace?.();
46
+ const result = await assets.replaceFile(record.id, next);
47
+ setBusy(false);
48
+ if (result.ok)
49
+ onReplaced?.(result.asset);
50
+ else
51
+ setError(result.message);
52
+ }
53
+ return (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "outline", size: "sm", disabled: disabled || busy, title: disabled ? disabledHint : undefined, onClick: () => input.current?.click(), "data-testid": "asset-replace-file", children: [_jsx(RefreshCw, {}), " ", busy ? translate('cms.asset.replacing') : translate('cms.asset.replaceFile')] }), _jsx("input", { ref: input, type: "file", className: "hidden", ...(accept ? { accept: assetAcceptAttr(accept) } : {}), onChange: (e) => {
54
+ const files = Array.from(e.target.files ?? []);
55
+ e.target.value = '';
56
+ void replace(files);
57
+ } }), error ? (_jsx(Text, { size: "xs", className: "text-destructive", role: "alert", children: error })) : null] }));
58
+ }
59
+ //# sourceMappingURL=replace-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replace-file.js","sourceRoot":"","sources":["../../../src/components/assets/replace-file.tsx"],"names":[],"mappings":"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","sourcesContent":["import { RefreshCw } from 'lucide-react'\nimport { useRef, useState } from 'react'\nimport { assetAcceptAttr } from '@elytracms/core/cms-core'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport { Button, Text } from '@elytracms/studio/ui'\nimport { translate } from '../../lib/i18n'\nimport { splitByAccept } from '../cms/asset-picker-core'\nimport { useOptionalAssets } from './assets-store'\n\n/**\n * What a file REPLACE accepts (EC-568): a file of the same kind as the current one — an\n * image stays an image (every field that holds it accepted an image), a video a video.\n * Any other kind is unrestricted. Pure, for the test.\n */\nexport function replaceAccept(record: Pick<AssetRecord, 'contentType'>): readonly string[] | undefined {\n const kind = record.contentType.split('/')[0]\n return kind === 'image' || kind === 'video' || kind === 'audio' ? [kind] : undefined\n}\n\n/**\n * \"Replace file\" (EC-568 A): new bytes inside the SAME asset — every placement that\n * points at it shows the new file. Runs THE upload path with `replace` (the store's\n * `replaceFile`), so the derived data is regenerated and the file's embedded metadata\n * overrides `meta` per key. Used by the asset library's detail surface and the image\n * options dialog. `beforeReplace` lets a caller flush an edit buffer first, so a stale\n * buffer can never overwrite what the new file brought.\n */\nexport function ReplaceFileButton({\n record,\n disabled,\n disabledHint,\n beforeReplace,\n onReplaced,\n}: {\n record: AssetRecord\n disabled?: boolean\n disabledHint?: string\n beforeReplace?: () => Promise<void>\n onReplaced?: (asset: AssetRecord) => void\n}) {\n const assets = useOptionalAssets()\n const input = useRef<HTMLInputElement>(null)\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const accept = replaceAccept(record)\n if (!assets) return null\n\n async function replace(files: File[]) {\n if (!assets || files.length === 0) return\n const { accepted, rejected } = splitByAccept(files.slice(0, 1), accept)\n const next = accepted[0]\n if (!next) {\n setError(translate('cms.asset.replaceWrongKind', { name: rejected[0] ?? '', types: (accept ?? []).join(', ') }))\n return\n }\n setBusy(true)\n setError(null)\n await beforeReplace?.()\n const result = await assets.replaceFile(record.id, next)\n setBusy(false)\n if (result.ok) onReplaced?.(result.asset)\n else setError(result.message)\n }\n\n return (\n <>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={disabled || busy}\n title={disabled ? disabledHint : undefined}\n onClick={() => input.current?.click()}\n data-testid=\"asset-replace-file\"\n >\n <RefreshCw /> {busy ? translate('cms.asset.replacing') : translate('cms.asset.replaceFile')}\n </Button>\n <input\n ref={input}\n type=\"file\"\n className=\"hidden\"\n {...(accept ? { accept: assetAcceptAttr(accept) } : {})}\n onChange={(e) => {\n const files = Array.from(e.target.files ?? [])\n e.target.value = ''\n void replace(files)\n }}\n />\n {error ? (\n <Text size=\"xs\" className=\"text-destructive\" role=\"alert\">\n {error}\n </Text>\n ) : null}\n </>\n )\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { ProjectRole } from '@elytracms/core/persistence';
3
- import type { AuthAdapter, AuthSession, AuthStatus, ChangePasswordInput, ChangePasswordResult, SignInResult, UpdateProfileInput, UpdateProfileResult } from '@elytracms/studio/lib/auth';
3
+ import type { AuthAdapter, AuthSession, AuthStatus, ChangePasswordInput, ChangePasswordResult, SignInResult, UpdateProfileInput, UpdateProfileResult } from '../lib/auth/index.js';
4
4
  /**
5
5
  * Session store (EC-150). Owns the one `AuthAdapter` the app talks to, keeps
6
6
  * the session in React state, and binds/unbinds the operations-layer principal
@@ -1,11 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
3
- import { selectAuthAdapter, sessionPrincipal } from '@elytracms/studio/lib/auth';
4
- import { onSessionExpiry, raiseSessionExpiry, shouldBlockOnSessionExpiry, } from '@elytracms/studio/lib/session-expiry';
5
- import { SESSION_PROBE_INTERVAL_MS, createSessionRevalidator, } from '@elytracms/studio/lib/session-revalidation';
6
- import { setStudioSession } from '@elytracms/studio/lib/operations';
7
- import { useOperationQuery } from '@elytracms/studio/lib/query';
8
- import { createRequiredContext } from '@elytracms/studio/lib/required-context';
3
+ import { selectAuthAdapter, sessionPrincipal } from '../lib/auth/index.js';
4
+ import { onSessionExpiry, raiseSessionExpiry, shouldBlockOnSessionExpiry, } from '../lib/session-expiry.js';
5
+ import { SESSION_PROBE_INTERVAL_MS, createSessionRevalidator, } from '../lib/session-revalidation.js';
6
+ import { setStudioSession } from '../lib/operations/index.js';
7
+ import { useOperationQuery } from '../lib/query/index.js';
8
+ import { createRequiredContext } from '../lib/required-context.js';
9
9
  const { Context: AuthContext, useRequired: useAuth } = createRequiredContext('Auth', {
10
10
  article: 'an',
11
11
  });
@@ -1 +1 @@
1
- {"version":3,"file":"auth-context.js","sourceRoot":"","sources":["../../src/components/auth-context.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { ProjectRole } from '@elytracms/core/persistence'\nimport { selectAuthAdapter, sessionPrincipal } from '@elytracms/studio/lib/auth'\nimport {\n onSessionExpiry,\n raiseSessionExpiry,\n shouldBlockOnSessionExpiry,\n} from '@elytracms/studio/lib/session-expiry'\nimport {\n SESSION_PROBE_INTERVAL_MS,\n createSessionRevalidator,\n} from '@elytracms/studio/lib/session-revalidation'\nimport type {\n AuthAdapter,\n AuthSession,\n AuthStatus,\n ChangePasswordInput,\n ChangePasswordResult,\n SignInResult,\n UpdateProfileInput,\n UpdateProfileResult,\n} from '@elytracms/studio/lib/auth'\nimport { setStudioSession } from '@elytracms/studio/lib/operations'\nimport { useOperationQuery } from '@elytracms/studio/lib/query'\nimport { createRequiredContext } from '@elytracms/studio/lib/required-context'\n\n/**\n * Session store (EC-150). Owns the one `AuthAdapter` the app talks to, keeps\n * the session in React state, and binds/unbinds the operations-layer principal\n * (`setStudioSession`) so every studio mutation runs through the role hook as\n * the signed-in user. Auth state machine: `loading` (restoring) →\n * `signed-in` | `signed-out` — all three are deterministic, renderable states.\n *\n * EC-403 adds the orthogonal `sessionExpired` flag: when the backend session\n * dies mid-flight (any operation fails the session-expired predicate,\n * reported through `lib/session-expiry.ts`), `status` deliberately STAYS\n * `'signed-in'` — flipping it would let the route guard navigate to the\n * sign-in screen and destroy the unsaved editor state. Instead the flag\n * mounts the blocking in-place re-auth overlay; a successful sign-in applies\n * the fresh session and clears it, and the tree never unmounted.\n */\n\nexport interface AuthStore {\n status: AuthStatus\n session: AuthSession | null\n /**\n * The backend session expired mid-flight (EC-403). While true, the shell\n * blocks all editing behind the in-place re-auth overlay; `status` remains\n * `'signed-in'` so no route guard navigation tears down unsaved state.\n */\n sessionExpired: boolean\n signIn(email: string, password: string): Promise<SignInResult>\n /** Register the first/new user on the active backend, then sign in (EC-179). */\n signUp(email: string, password: string): Promise<SignInResult>\n signOut(): Promise<void>\n /** Edit the signed-in user's OWN name/avatar (EC-240 H-tail); refreshes the session. */\n updateProfile(input: UpdateProfileInput): Promise<UpdateProfileResult>\n /** Change the signed-in user's OWN password (EC-240 H-tail); provider-delegated. */\n changePassword(input: ChangePasswordInput): Promise<ChangePasswordResult>\n}\n\nconst { Context: AuthContext, useRequired: useAuth } = createRequiredContext<AuthStore>('Auth', {\n article: 'an',\n})\n\nexport interface AuthProviderProps {\n children: ReactNode\n /** Test seam: inject an adapter; defaults to `selectAuthAdapter()`. */\n adapter?: AuthAdapter\n}\n\nexport function AuthProvider({ children, adapter }: AuthProviderProps) {\n const [auth] = useState<AuthAdapter>(() => adapter ?? selectAuthAdapter())\n const [status, setStatus] = useState<AuthStatus>('loading')\n const [session, setSession] = useState<AuthSession | null>(null)\n const [sessionExpired, setSessionExpired] = useState(false)\n\n // Latest values for the once-per-adapter expiry subscription below.\n const statusRef = useRef(status)\n statusRef.current = status\n const expiredRef = useRef(sessionExpired)\n expiredRef.current = sessionExpired\n\n const apply = useCallback((next: AuthSession | null) => {\n setStudioSession(next ? sessionPrincipal(next) : null)\n setSession(next)\n setStatus(next ? 'signed-in' : 'signed-out')\n // Any applied session state resolves the expiry block (EC-403): a fresh\n // sign-in replaces the dead session in place; an explicit sign-out hands\n // over to the route guard.\n setSessionExpired(false)\n }, [])\n\n // Restore the session once on the client (SSR renders the 'loading' state).\n useEffect(() => {\n let cancelled = false\n void auth.currentSession().then((restored) => {\n if (!cancelled) apply(restored)\n })\n return () => {\n cancelled = true\n }\n }, [auth, apply])\n\n // EC-403: raise the blocking re-auth state when the operations layer reports\n // session expiry. `status` stays 'signed-in' (see AuthStore docs); the\n // adapter-level signOut resets the DEAD backend session — the shared Convex\n // client would otherwise send the expired JWT with the overlay's sign-in\n // action itself and be rejected at the transport. Local React state is\n // untouched by that reset, so the tree and unsaved edits live on.\n useEffect(\n () =>\n onSessionExpiry(() => {\n if (\n !shouldBlockOnSessionExpiry({\n status: statusRef.current,\n alreadyBlocked: expiredRef.current,\n })\n ) {\n return\n }\n setSessionExpired(true)\n auth.signOut().catch(() => {\n // Resetting a dead session is best-effort; the overlay still blocks.\n })\n }),\n [auth],\n )\n\n // EC-415: proactive re-validation. Probe the adapter's tri-state\n // `probeSession()` on window focus + visibility return + a modest interval\n // while the tab is visible (scheduler defaults: ≥30 s between probes, 5 min\n // interval — see lib/session-revalidation.ts). `invalid` funnels into the\n // SAME EC-403 chokepoint (`raiseSessionExpiry` → the guarded subscription\n // above), so a dead session raises the blocking overlay BEFORE the editor\n // types into a doomed form; `unreachable` is silent by adapter contract —\n // the stored token survives and nothing signs out on a network blip.\n useEffect(() => {\n if (typeof window === 'undefined') return\n const revalidator = createSessionRevalidator({\n probe: () => auth.probeSession(),\n onInvalid: raiseSessionExpiry,\n readGate: () => ({\n signedIn: statusRef.current === 'signed-in',\n blocked: expiredRef.current,\n visible: document.visibilityState !== 'hidden',\n }),\n })\n const trigger = () => void revalidator.trigger()\n window.addEventListener('focus', trigger)\n document.addEventListener('visibilitychange', trigger)\n const interval = window.setInterval(trigger, SESSION_PROBE_INTERVAL_MS)\n return () => {\n window.removeEventListener('focus', trigger)\n document.removeEventListener('visibilitychange', trigger)\n window.clearInterval(interval)\n }\n }, [auth])\n\n const store = useMemo<AuthStore>(\n () => ({\n status,\n session,\n sessionExpired,\n signIn: async (email, password) => {\n const result = await auth.signIn({ email, password })\n if (result.ok) apply(result.session)\n return result\n },\n signUp: async (email, password) => {\n const result = await auth.signUp({ email, password })\n if (result.ok) apply(result.session)\n return result\n },\n signOut: async () => {\n await auth.signOut()\n apply(null)\n },\n updateProfile: async (input) => {\n const result = await auth.updateProfile(input)\n // A successful identity edit refreshes the session so the shell (and the\n // account surface) reflect the new name immediately.\n if (result.ok) apply(result.session)\n return result\n },\n changePassword: (input) => auth.changePassword(input),\n }),\n [auth, status, session, sessionExpired, apply],\n )\n\n return <AuthContext.Provider value={store}>{children}</AuthContext.Provider>\n}\n\nexport { useAuth }\n\n/**\n * Like {@link useAuth}, but tolerant of provider-less renders. For shared\n * leaf surfaces (e.g. the operation-error banner's \"Sign in again\" action)\n * that are also rendered standalone in tests/styleguides: they degrade\n * gracefully instead of demanding the full provider tree.\n */\nexport function useOptionalAuth(): AuthStore | null {\n return useContext(AuthContext)\n}\n\n/**\n * The signed-in user's role on a project (for the shell's role badge).\n * Read through the one operations-backed query cache (EC-308/AD-17) via the\n * `members.find` query — no longer reaching past the spine into\n * `adapter.members` — and re-read when membership changes. `null` while loading\n * or when the user is not a member.\n */\nexport function useProjectRole(projectId: string | null): ProjectRole | null {\n const { session } = useAuth()\n const userId = session?.user.id ?? null\n const query = useOperationQuery<ProjectRole | null>({\n queryKey: ['projectRole', projectId ?? '∅', userId ?? '∅'],\n loader: async (operations) => {\n const result = await operations.members.find({\n projectId: projectId as string,\n userId: userId as string,\n })\n return result.ok ? (result.value?.role ?? null) : null\n },\n invalidateOn: ['members'],\n enabled: projectId !== null && userId !== null,\n })\n return query.data ?? null\n}\n"]}
1
+ {"version":3,"file":"auth-context.js","sourceRoot":"","sources":["../../src/components/auth-context.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { ProjectRole } from '@elytracms/core/persistence'\nimport { selectAuthAdapter, sessionPrincipal } from '../lib/auth'\nimport {\n onSessionExpiry,\n raiseSessionExpiry,\n shouldBlockOnSessionExpiry,\n} from '../lib/session-expiry'\nimport {\n SESSION_PROBE_INTERVAL_MS,\n createSessionRevalidator,\n} from '../lib/session-revalidation'\nimport type {\n AuthAdapter,\n AuthSession,\n AuthStatus,\n ChangePasswordInput,\n ChangePasswordResult,\n SignInResult,\n UpdateProfileInput,\n UpdateProfileResult,\n} from '../lib/auth'\nimport { setStudioSession } from '../lib/operations'\nimport { useOperationQuery } from '../lib/query'\nimport { createRequiredContext } from '../lib/required-context'\n\n/**\n * Session store (EC-150). Owns the one `AuthAdapter` the app talks to, keeps\n * the session in React state, and binds/unbinds the operations-layer principal\n * (`setStudioSession`) so every studio mutation runs through the role hook as\n * the signed-in user. Auth state machine: `loading` (restoring) →\n * `signed-in` | `signed-out` — all three are deterministic, renderable states.\n *\n * EC-403 adds the orthogonal `sessionExpired` flag: when the backend session\n * dies mid-flight (any operation fails the session-expired predicate,\n * reported through `lib/session-expiry.ts`), `status` deliberately STAYS\n * `'signed-in'` — flipping it would let the route guard navigate to the\n * sign-in screen and destroy the unsaved editor state. Instead the flag\n * mounts the blocking in-place re-auth overlay; a successful sign-in applies\n * the fresh session and clears it, and the tree never unmounted.\n */\n\nexport interface AuthStore {\n status: AuthStatus\n session: AuthSession | null\n /**\n * The backend session expired mid-flight (EC-403). While true, the shell\n * blocks all editing behind the in-place re-auth overlay; `status` remains\n * `'signed-in'` so no route guard navigation tears down unsaved state.\n */\n sessionExpired: boolean\n signIn(email: string, password: string): Promise<SignInResult>\n /** Register the first/new user on the active backend, then sign in (EC-179). */\n signUp(email: string, password: string): Promise<SignInResult>\n signOut(): Promise<void>\n /** Edit the signed-in user's OWN name/avatar (EC-240 H-tail); refreshes the session. */\n updateProfile(input: UpdateProfileInput): Promise<UpdateProfileResult>\n /** Change the signed-in user's OWN password (EC-240 H-tail); provider-delegated. */\n changePassword(input: ChangePasswordInput): Promise<ChangePasswordResult>\n}\n\nconst { Context: AuthContext, useRequired: useAuth } = createRequiredContext<AuthStore>('Auth', {\n article: 'an',\n})\n\nexport interface AuthProviderProps {\n children: ReactNode\n /** Test seam: inject an adapter; defaults to `selectAuthAdapter()`. */\n adapter?: AuthAdapter\n}\n\nexport function AuthProvider({ children, adapter }: AuthProviderProps) {\n const [auth] = useState<AuthAdapter>(() => adapter ?? selectAuthAdapter())\n const [status, setStatus] = useState<AuthStatus>('loading')\n const [session, setSession] = useState<AuthSession | null>(null)\n const [sessionExpired, setSessionExpired] = useState(false)\n\n // Latest values for the once-per-adapter expiry subscription below.\n const statusRef = useRef(status)\n statusRef.current = status\n const expiredRef = useRef(sessionExpired)\n expiredRef.current = sessionExpired\n\n const apply = useCallback((next: AuthSession | null) => {\n setStudioSession(next ? sessionPrincipal(next) : null)\n setSession(next)\n setStatus(next ? 'signed-in' : 'signed-out')\n // Any applied session state resolves the expiry block (EC-403): a fresh\n // sign-in replaces the dead session in place; an explicit sign-out hands\n // over to the route guard.\n setSessionExpired(false)\n }, [])\n\n // Restore the session once on the client (SSR renders the 'loading' state).\n useEffect(() => {\n let cancelled = false\n void auth.currentSession().then((restored) => {\n if (!cancelled) apply(restored)\n })\n return () => {\n cancelled = true\n }\n }, [auth, apply])\n\n // EC-403: raise the blocking re-auth state when the operations layer reports\n // session expiry. `status` stays 'signed-in' (see AuthStore docs); the\n // adapter-level signOut resets the DEAD backend session — the shared Convex\n // client would otherwise send the expired JWT with the overlay's sign-in\n // action itself and be rejected at the transport. Local React state is\n // untouched by that reset, so the tree and unsaved edits live on.\n useEffect(\n () =>\n onSessionExpiry(() => {\n if (\n !shouldBlockOnSessionExpiry({\n status: statusRef.current,\n alreadyBlocked: expiredRef.current,\n })\n ) {\n return\n }\n setSessionExpired(true)\n auth.signOut().catch(() => {\n // Resetting a dead session is best-effort; the overlay still blocks.\n })\n }),\n [auth],\n )\n\n // EC-415: proactive re-validation. Probe the adapter's tri-state\n // `probeSession()` on window focus + visibility return + a modest interval\n // while the tab is visible (scheduler defaults: ≥30 s between probes, 5 min\n // interval — see lib/session-revalidation.ts). `invalid` funnels into the\n // SAME EC-403 chokepoint (`raiseSessionExpiry` → the guarded subscription\n // above), so a dead session raises the blocking overlay BEFORE the editor\n // types into a doomed form; `unreachable` is silent by adapter contract —\n // the stored token survives and nothing signs out on a network blip.\n useEffect(() => {\n if (typeof window === 'undefined') return\n const revalidator = createSessionRevalidator({\n probe: () => auth.probeSession(),\n onInvalid: raiseSessionExpiry,\n readGate: () => ({\n signedIn: statusRef.current === 'signed-in',\n blocked: expiredRef.current,\n visible: document.visibilityState !== 'hidden',\n }),\n })\n const trigger = () => void revalidator.trigger()\n window.addEventListener('focus', trigger)\n document.addEventListener('visibilitychange', trigger)\n const interval = window.setInterval(trigger, SESSION_PROBE_INTERVAL_MS)\n return () => {\n window.removeEventListener('focus', trigger)\n document.removeEventListener('visibilitychange', trigger)\n window.clearInterval(interval)\n }\n }, [auth])\n\n const store = useMemo<AuthStore>(\n () => ({\n status,\n session,\n sessionExpired,\n signIn: async (email, password) => {\n const result = await auth.signIn({ email, password })\n if (result.ok) apply(result.session)\n return result\n },\n signUp: async (email, password) => {\n const result = await auth.signUp({ email, password })\n if (result.ok) apply(result.session)\n return result\n },\n signOut: async () => {\n await auth.signOut()\n apply(null)\n },\n updateProfile: async (input) => {\n const result = await auth.updateProfile(input)\n // A successful identity edit refreshes the session so the shell (and the\n // account surface) reflect the new name immediately.\n if (result.ok) apply(result.session)\n return result\n },\n changePassword: (input) => auth.changePassword(input),\n }),\n [auth, status, session, sessionExpired, apply],\n )\n\n return <AuthContext.Provider value={store}>{children}</AuthContext.Provider>\n}\n\nexport { useAuth }\n\n/**\n * Like {@link useAuth}, but tolerant of provider-less renders. For shared\n * leaf surfaces (e.g. the operation-error banner's \"Sign in again\" action)\n * that are also rendered standalone in tests/styleguides: they degrade\n * gracefully instead of demanding the full provider tree.\n */\nexport function useOptionalAuth(): AuthStore | null {\n return useContext(AuthContext)\n}\n\n/**\n * The signed-in user's role on a project (for the shell's role badge).\n * Read through the one operations-backed query cache (EC-308/AD-17) via the\n * `members.find` query — no longer reaching past the spine into\n * `adapter.members` — and re-read when membership changes. `null` while loading\n * or when the user is not a member.\n */\nexport function useProjectRole(projectId: string | null): ProjectRole | null {\n const { session } = useAuth()\n const userId = session?.user.id ?? null\n const query = useOperationQuery<ProjectRole | null>({\n queryKey: ['projectRole', projectId ?? '∅', userId ?? '∅'],\n loader: async (operations) => {\n const result = await operations.members.find({\n projectId: projectId as string,\n userId: userId as string,\n })\n return result.ok ? (result.value?.role ?? null) : null\n },\n invalidateOn: ['members'],\n enabled: projectId !== null && userId !== null,\n })\n return query.data ?? null\n}\n"]}
@@ -1,6 +1,6 @@
1
- import type { ComponentNode } from '@elytracms/core/project-graph';
1
+ import type { AllowList, ComponentNode } from '@elytracms/core/project-graph';
2
2
  import type { ComponentFixture, ComponentManifest } from '@elytracms/core/component-registry';
3
- import type { StudioRegistry } from '@elytracms/studio/lib/registry';
3
+ import type { StudioRegistry } from '../../lib/registry/index.js';
4
4
  /**
5
5
  * Blocks panel model (EC-164, reshaped by EC-439) — the pure, React-free
6
6
  * derivation behind the visual insert surface. Every canvas-eligible registry
@@ -90,22 +90,12 @@ export interface BlockGroup {
90
90
  export interface BlockFilter {
91
91
  /** Case-insensitive match over id, title, description, category, and fixture names. */
92
92
  query?: string;
93
- /** When set, only these component ids may be inserted (slot.allow). */
94
- slotAllow?: string[];
93
+ /**
94
+ * The insertion position's effective vocabulary (EC-584 — `allowAt`: the field's at
95
+ * the composition root, the slot's below). Omitted → any component.
96
+ */
97
+ allow?: AllowList;
95
98
  }
96
- /**
97
- * Intersect two component-id allow-lists. `undefined` means "no constraint from
98
- * this source", so the result is the other list (copied); two present lists yield
99
- * their set intersection, preserving the first list's order. This layers a
100
- * composition field's top-level `allow` vocabulary over a slot's positional
101
- * `allow` at the canvas root (EC-188): a component must satisfy BOTH leashes to be
102
- * offered. A present list is a closed set (EC-544): the intersection of two
103
- * disjoint lists is `[]`, which every reader treats as "allow nothing" — there is
104
- * no coalescing hatch anywhere, and a *declared* `[]` is rejected at the
105
- * declaration site (config resolve / the `empty-allow` registry issue), so an
106
- * empty result here is always computed, never authored.
107
- */
108
- export declare function intersectAllow(slotAllow: readonly string[] | undefined, fieldAllow: readonly string[] | undefined): string[] | undefined;
109
99
  /**
110
100
  * Build the configured subtree a fixture describes: the fixture's props (EC-591/2f:
111
101
  * never the manifest defaults — an unset prop stays absent), plus one bare child
@@ -143,16 +133,14 @@ export declare function tileMonogram(title: string): string;
143
133
  */
144
134
  export declare function blockGroups(studio: StudioRegistry, filter?: BlockFilter, generatedPreview?: GeneratedPreviewLookup): BlockGroup[];
145
135
  /**
146
- * Whether ANY component could be inserted under a slot allow-list (EC-405) — the
147
- * cheap existence check behind the canvas insertion affordances: a "+" only shows
148
- * at a position where the blocks panel would offer at least one tile. Mirrors the
149
- * exact filter {@link blockGroups} applies (canvas-eligible AND, when an allow-list
150
- * is present, on that list), short-circuiting on the first match instead of
151
- * building tiles; falls back to the layout-helper presets (which are also
152
- * insertable there). `undefined`/absent `slotAllow` means "allow any" → true when
153
- * the registry has any canvas-eligible component.
136
+ * Whether ANY component could be inserted at a position whose vocabulary is `allow`
137
+ * (EC-405) — the cheap existence check behind the canvas insertion affordances: a
138
+ * "+" only shows at a position where the blocks panel would offer at least one tile.
139
+ * The exact filter {@link blockGroups} applies (the placement judge), short-circuiting
140
+ * on the first match instead of building tiles; falls back to the layout-helper
141
+ * presets (which are also insertable there). Omitted `allow` → any component.
154
142
  */
155
- export declare function hasInsertableComponent(studio: StudioRegistry, slotAllow?: string[]): boolean;
143
+ export declare function hasInsertableComponent(studio: StudioRegistry, allow?: AllowList): boolean;
156
144
  /**
157
145
  * Layout helper presets (EC-067) as panel tiles — already one tile per preset.
158
146
  * A preset has a single implicit variant and never a preview image, so its
@@ -160,4 +148,4 @@ export declare function hasInsertableComponent(studio: StudioRegistry, slotAllow
160
148
  * supports ({@link layoutPresets} — built from the layout and text roles), never
161
149
  * hardcoded engine ids.
162
150
  */
163
- export declare function presetTiles(studio: StudioRegistry, slotAllow?: string[]): BlockTile[];
151
+ export declare function presetTiles(studio: StudioRegistry, allow?: AllowList): BlockTile[];