@elytracms/studio 0.0.52 → 0.0.54

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 (321) hide show
  1. package/dist/components/account/account-settings.js +4 -4
  2. package/dist/components/account/credential-panel.js +1 -1
  3. package/dist/components/account/self-account-view-source.js +3 -3
  4. package/dist/components/ai/ai-chat-panel-lazy.js +1 -1
  5. package/dist/components/ai/ai-chat-panel.js +4 -4
  6. package/dist/components/ai/ai-cms-panel.js +1 -1
  7. package/dist/components/ai/proposal-card.js +4 -4
  8. package/dist/components/ai/translate-document-action.js +1 -1
  9. package/dist/components/ai/translate-document-dialog.d.ts +1 -1
  10. package/dist/components/app-views/default-modules.d.ts +3 -3
  11. package/dist/components/app-views/default-modules.js +20 -20
  12. package/dist/components/app-views/sidebar.d.ts +1 -1
  13. package/dist/components/app-views/sidebar.js +2 -2
  14. package/dist/components/app-views/studio-config.d.ts +1 -1
  15. package/dist/components/assets/asset-detail.js +9 -9
  16. package/dist/components/assets/asset-grid.js +7 -7
  17. package/dist/components/assets/asset-thumb.d.ts +1 -1
  18. package/dist/components/assets/asset-thumb.js +2 -2
  19. package/dist/components/assets/assets-collection-view-source.js +3 -3
  20. package/dist/components/assets/assets-module.js +8 -8
  21. package/dist/components/assets/assets-store.d.ts +1 -1
  22. package/dist/components/assets/assets-store.js +1 -1
  23. package/dist/components/builder/blocks-model.js +1 -1
  24. package/dist/components/builder/blocks-panel.d.ts +1 -1
  25. package/dist/components/builder/blocks-panel.js +6 -6
  26. package/dist/components/builder/canvas.js +11 -11
  27. package/dist/components/builder/clipboard-actions.js +2 -2
  28. package/dist/components/builder/composition-field-editor-lazy.d.ts +1 -1
  29. package/dist/components/builder/composition-field-editor-lazy.js +1 -1
  30. package/dist/components/builder/composition-field-editor.d.ts +1 -1
  31. package/dist/components/builder/composition-field-editor.js +9 -9
  32. package/dist/components/builder/insert-context.d.ts +2 -2
  33. package/dist/components/builder/insert-context.js +2 -2
  34. package/dist/components/builder/insert-menu.d.ts +2 -2
  35. package/dist/components/builder/insert-menu.js +1 -1
  36. package/dist/components/builder/layout-inspector.js +1 -1
  37. package/dist/components/builder/page-tree.js +5 -5
  38. package/dist/components/builder/property-inspector.js +3 -3
  39. package/dist/components/builder/reuse-context.d.ts +1 -1
  40. package/dist/components/builder/reuse-context.js +3 -3
  41. package/dist/components/builder/spacing-handles.js +2 -2
  42. package/dist/components/cms/asset-picker.js +4 -4
  43. package/dist/components/cms/bulk-actions.js +1 -1
  44. package/dist/components/cms/bulk-bar.d.ts +1 -1
  45. package/dist/components/cms/bulk-bar.js +3 -3
  46. package/dist/components/cms/cell-edit.js +1 -1
  47. package/dist/components/cms/cell.js +2 -2
  48. package/dist/components/cms/cms-index.js +1 -1
  49. package/dist/components/cms/cms-store.d.ts +2 -2
  50. package/dist/components/cms/cms-store.js +6 -6
  51. package/dist/components/cms/collection-table.js +17 -17
  52. package/dist/components/cms/detail-editor.js +31 -31
  53. package/dist/components/cms/document-diff-view.js +4 -4
  54. package/dist/components/cms/editable-cell.d.ts +1 -1
  55. package/dist/components/cms/editable-cell.js +6 -6
  56. package/dist/components/cms/field-control.js +15 -15
  57. package/dist/components/cms/field-renderer.d.ts +2 -2
  58. package/dist/components/cms/filter-builder.js +3 -3
  59. package/dist/components/cms/import-export.js +1 -1
  60. package/dist/components/cms/issues-pane.js +1 -1
  61. package/dist/components/cms/prop-fields.js +3 -3
  62. package/dist/components/cms/reference-confirm.js +1 -1
  63. package/dist/components/cms/relation-picker.js +1 -1
  64. package/dist/components/cms/rich-text/block-commands.d.ts +1 -1
  65. package/dist/components/cms/rich-text/component-embed.js +2 -2
  66. package/dist/components/cms/rich-text/editor.js +8 -8
  67. package/dist/components/cms/rich-text/embed-inspector.d.ts +1 -1
  68. package/dist/components/cms/rich-text/embed-inspector.js +2 -2
  69. package/dist/components/cms/rich-text/embed-picker.js +1 -1
  70. package/dist/components/cms/rich-text/embed-props.js +1 -1
  71. package/dist/components/cms/rich-text/extensions.d.ts +1 -1
  72. package/dist/components/cms/rich-text/extensions.js +4 -4
  73. package/dist/components/cms/rich-text/index.d.ts +1 -1
  74. package/dist/components/cms/rich-text/index.js +1 -1
  75. package/dist/components/cms/rich-text/insert.d.ts +1 -1
  76. package/dist/components/cms/rich-text/lazy.d.ts +1 -1
  77. package/dist/components/cms/rich-text/lazy.js +1 -1
  78. package/dist/components/cms/rich-text/slash-command.js +1 -1
  79. package/dist/components/cms/rich-text/toolbar.d.ts +1 -1
  80. package/dist/components/cms/rich-text/toolbar.js +4 -4
  81. package/dist/components/cms/use-cell-edit.d.ts +1 -1
  82. package/dist/components/cms/use-cell-edit.js +2 -2
  83. package/dist/components/cms/use-compare-selection.d.ts +1 -1
  84. package/dist/components/cms/use-discard-controller.d.ts +1 -1
  85. package/dist/components/cms/use-discard-controller.js +2 -2
  86. package/dist/components/cms/use-document-versions.d.ts +1 -1
  87. package/dist/components/cms/use-document-versions.js +1 -1
  88. package/dist/components/cms/use-embed-inspector-host.d.ts +1 -1
  89. package/dist/components/cms/use-published-perspective.d.ts +2 -2
  90. package/dist/components/cms/use-published-perspective.js +2 -2
  91. package/dist/components/cms/use-restore-version.js +1 -1
  92. package/dist/components/cms/version-compare-lazy.js +1 -1
  93. package/dist/components/cms/version-compare.js +6 -6
  94. package/dist/components/cms/versions-model.d.ts +1 -1
  95. package/dist/components/cms/versions-pane.js +5 -5
  96. package/dist/components/cms/view-bar.js +2 -2
  97. package/dist/components/collab/collab-context.js +1 -1
  98. package/dist/components/collection-view/cms-collection-view-source.js +2 -2
  99. package/dist/components/components/component-preview.js +3 -3
  100. package/dist/components/components/components-index.js +2 -2
  101. package/dist/components/components/registry-browser.js +2 -2
  102. package/dist/components/components/transfer-dialog.js +5 -5
  103. package/dist/components/components/transfer.js +4 -4
  104. package/dist/components/current-user-menu.js +2 -2
  105. package/dist/components/delivery-health-banner.js +2 -2
  106. package/dist/components/operation-error.js +1 -1
  107. package/dist/components/project-list.js +5 -5
  108. package/dist/components/project-session.js +1 -1
  109. package/dist/components/project-switcher.js +3 -3
  110. package/dist/components/rail-store-status.js +1 -1
  111. package/dist/components/redirects/redirects-collection-view-source.js +3 -3
  112. package/dist/components/redirects/redirects-module.js +6 -6
  113. package/dist/components/section-nav.js +3 -3
  114. package/dist/components/section-surface.js +1 -1
  115. package/dist/components/studio-project-screen.js +7 -7
  116. package/dist/components/studio-shell.js +9 -9
  117. package/dist/components/use-hydration-phase.js +1 -1
  118. package/dist/components/users/users-collection-view-source.js +4 -4
  119. package/dist/components/users/users-module.js +9 -9
  120. package/dist/components/workspace-projects.js +1 -1
  121. package/dist/customization.d.ts +9 -9
  122. package/dist/customization.js +5 -5
  123. package/dist/lib/ai/anthropic-provider.d.ts +1 -1
  124. package/dist/lib/ai/apply-content.d.ts +1 -1
  125. package/dist/lib/ai/apply-content.js +3 -3
  126. package/dist/lib/ai/chat-state.d.ts +2 -2
  127. package/dist/lib/ai/config.d.ts +1 -1
  128. package/dist/lib/ai/convex-session-headers.d.ts +68 -0
  129. package/dist/lib/ai/convex-session-headers.js +79 -0
  130. package/dist/lib/ai/convex-session-headers.js.map +1 -0
  131. package/dist/lib/ai/convex-session.d.ts +82 -0
  132. package/dist/lib/ai/convex-session.js +92 -0
  133. package/dist/lib/ai/convex-session.js.map +1 -0
  134. package/dist/lib/ai/create-flow.d.ts +2 -2
  135. package/dist/lib/ai/create-flow.js +3 -3
  136. package/dist/lib/ai/fake-provider.d.ts +1 -1
  137. package/dist/lib/ai/index.d.ts +27 -16
  138. package/dist/lib/ai/index.js +27 -16
  139. package/dist/lib/ai/index.js.map +1 -1
  140. package/dist/lib/ai/knowledge.d.ts +1 -1
  141. package/dist/lib/ai/node-handler.d.ts +29 -0
  142. package/dist/lib/ai/node-handler.js +48 -0
  143. package/dist/lib/ai/node-handler.js.map +1 -0
  144. package/dist/lib/ai/provenance.d.ts +1 -1
  145. package/dist/lib/ai/route-provider.d.ts +1 -1
  146. package/dist/lib/ai/route.d.ts +1 -1
  147. package/dist/lib/ai/shape.d.ts +1 -1
  148. package/dist/lib/ai/translate-document.d.ts +1 -1
  149. package/dist/lib/ai/translate-document.js +3 -3
  150. package/dist/lib/ai/vite.d.ts +32 -0
  151. package/dist/lib/ai/vite.js +97 -0
  152. package/dist/lib/ai/vite.js.map +1 -0
  153. package/dist/lib/ai-panel.js +1 -1
  154. package/dist/lib/assets/index.d.ts +4 -4
  155. package/dist/lib/assets/index.js +4 -4
  156. package/dist/lib/assets/upload.d.ts +2 -2
  157. package/dist/lib/assets/upload.js +2 -2
  158. package/dist/lib/auth/convex-auth-adapter.d.ts +2 -2
  159. package/dist/lib/auth/convex-auth-adapter.js +1 -1
  160. package/dist/lib/auth/dev-auto-login-adapter.d.ts +1 -1
  161. package/dist/lib/auth/index.d.ts +9 -9
  162. package/dist/lib/auth/index.js +10 -10
  163. package/dist/lib/auth/local-auth-adapter.d.ts +1 -1
  164. package/dist/lib/auth/route-guard.d.ts +1 -1
  165. package/dist/lib/auth-errors.d.ts +1 -1
  166. package/dist/lib/clipboard/index.d.ts +3 -3
  167. package/dist/lib/clipboard/index.js +3 -3
  168. package/dist/lib/cms/published-documents.d.ts +1 -1
  169. package/dist/lib/cms-views/index.d.ts +2 -2
  170. package/dist/lib/cms-views/index.js +2 -2
  171. package/dist/lib/collection-view/collection-view-source.d.ts +4 -4
  172. package/dist/lib/data/collection-content.js +1 -1
  173. package/dist/lib/data/data-store.js +6 -6
  174. package/dist/lib/data/index.d.ts +4 -4
  175. package/dist/lib/data/index.js +4 -4
  176. package/dist/lib/data/sample-capture.js +4 -4
  177. package/dist/lib/data/sources-store.d.ts +1 -1
  178. package/dist/lib/diff-view-preference.js +1 -1
  179. package/dist/lib/dnd/gap-drag.d.ts +1 -1
  180. package/dist/lib/i18n/i18n.d.ts +1 -1
  181. package/dist/lib/i18n/i18n.js +1 -1
  182. package/dist/lib/i18n/index.d.ts +4 -4
  183. package/dist/lib/i18n/index.js +3 -3
  184. package/dist/lib/i18n/messages.en.d.ts +1 -1
  185. package/dist/lib/i18n/messages.en.js +1 -1
  186. package/dist/lib/locale/binding-fallback.d.ts +1 -1
  187. package/dist/lib/locale/binding-fallback.js +2 -2
  188. package/dist/lib/locale/index.d.ts +6 -6
  189. package/dist/lib/locale/index.js +6 -6
  190. package/dist/lib/locale/locale-switcher.js +3 -3
  191. package/dist/lib/locale/use-studio-locale.js +1 -1
  192. package/dist/lib/operations/asset-repository.d.ts +1 -1
  193. package/dist/lib/operations/cms-persistence.d.ts +1 -1
  194. package/dist/lib/operations/cms-persistence.js +1 -1
  195. package/dist/lib/operations/index.d.ts +6 -6
  196. package/dist/lib/operations/index.js +6 -6
  197. package/dist/lib/operations/session-hydration.d.ts +1 -1
  198. package/dist/lib/operations/session-hydration.js +1 -1
  199. package/dist/lib/operations/studio-operations.d.ts +1 -1
  200. package/dist/lib/operations/studio-operations.js +3 -3
  201. package/dist/lib/persistence/convex-adapter.js +2 -2
  202. package/dist/lib/persistence/convex-blob-storage.d.ts +1 -1
  203. package/dist/lib/persistence/convex-blob-storage.js +1 -1
  204. package/dist/lib/persistence/deployment-readiness.d.ts +2 -2
  205. package/dist/lib/persistence/index.d.ts +6 -6
  206. package/dist/lib/persistence/index.js +7 -7
  207. package/dist/lib/persona/labels.js +1 -1
  208. package/dist/lib/query/index.d.ts +2 -2
  209. package/dist/lib/query/index.js +1 -1
  210. package/dist/lib/rail.js +1 -1
  211. package/dist/lib/references/impact.d.ts +1 -1
  212. package/dist/lib/references/impact.js +1 -1
  213. package/dist/lib/references/index.d.ts +3 -3
  214. package/dist/lib/references/index.js +3 -3
  215. package/dist/lib/references/references-store.d.ts +2 -2
  216. package/dist/lib/references/references-store.js +3 -3
  217. package/dist/lib/registry/index.js +2 -2
  218. package/dist/lib/theme.js +1 -1
  219. package/dist/lib/webhooks/config.d.ts +1 -1
  220. package/dist/lib/webhooks/config.js +2 -2
  221. package/dist/lib/webhooks/dispatcher.js +2 -2
  222. package/dist/lib/webhooks/index.d.ts +6 -6
  223. package/dist/lib/webhooks/index.js +4 -4
  224. package/dist/lib/workspace/index.d.ts +7 -7
  225. package/dist/lib/workspace/index.js +3 -3
  226. package/dist/lib/workspace/switcher-source.d.ts +1 -1
  227. package/dist/router/index.d.ts +1 -1
  228. package/dist/router/index.js +1 -1
  229. package/dist/ui/avatar.js +1 -1
  230. package/dist/ui/badge.js +1 -1
  231. package/dist/ui/button.js +1 -1
  232. package/dist/ui/card.js +1 -1
  233. package/dist/ui/collapsible-section.js +2 -2
  234. package/dist/ui/dialog.js +1 -1
  235. package/dist/ui/dropdown-menu.js +1 -1
  236. package/dist/ui/field.js +1 -1
  237. package/dist/ui/index.d.ts +19 -19
  238. package/dist/ui/index.js +19 -19
  239. package/dist/ui/input.js +1 -1
  240. package/dist/ui/inspector.js +1 -1
  241. package/dist/ui/layout.js +1 -1
  242. package/dist/ui/panel.js +2 -2
  243. package/dist/ui/popover.js +1 -1
  244. package/dist/ui/row-item.js +2 -2
  245. package/dist/ui/segmented-control.js +1 -1
  246. package/dist/ui/separator.js +1 -1
  247. package/dist/ui/toolbar.js +2 -2
  248. package/dist/ui/typography.js +1 -1
  249. package/package.json +13 -5
  250. package/dist/components/assets/asset-library.d.ts +0 -14
  251. package/dist/components/assets/asset-library.js +0 -202
  252. package/dist/components/assets/asset-library.js.map +0 -1
  253. package/dist/components/backend-gate.d.ts +0 -19
  254. package/dist/components/backend-gate.js +0 -64
  255. package/dist/components/backend-gate.js.map +0 -1
  256. package/dist/components/builder/composition-scope.d.ts +0 -16
  257. package/dist/components/builder/composition-scope.js +0 -7
  258. package/dist/components/builder/composition-scope.js.map +0 -1
  259. package/dist/components/cms/alias-selector.d.ts +0 -20
  260. package/dist/components/cms/alias-selector.js +0 -34
  261. package/dist/components/cms/alias-selector.js.map +0 -1
  262. package/dist/components/cms/collections-sidebar.d.ts +0 -18
  263. package/dist/components/cms/collections-sidebar.js +0 -36
  264. package/dist/components/cms/collections-sidebar.js.map +0 -1
  265. package/dist/components/cms/new-page-dialog.d.ts +0 -22
  266. package/dist/components/cms/new-page-dialog.js +0 -42
  267. package/dist/components/cms/new-page-dialog.js.map +0 -1
  268. package/dist/components/cms/rich-text/embed-control.d.ts +0 -17
  269. package/dist/components/cms/rich-text/embed-control.js +0 -62
  270. package/dist/components/cms/rich-text/embed-control.js.map +0 -1
  271. package/dist/components/cms/rich-text/image-control.d.ts +0 -16
  272. package/dist/components/cms/rich-text/image-control.js +0 -36
  273. package/dist/components/cms/rich-text/image-control.js.map +0 -1
  274. package/dist/components/components/fixture-runner.d.ts +0 -10
  275. package/dist/components/components/fixture-runner.js +0 -27
  276. package/dist/components/components/fixture-runner.js.map +0 -1
  277. package/dist/components/references/builder-deep-link.d.ts +0 -12
  278. package/dist/components/references/builder-deep-link.js +0 -53
  279. package/dist/components/references/builder-deep-link.js.map +0 -1
  280. package/dist/components/routes/redirect-chain.d.ts +0 -9
  281. package/dist/components/routes/redirect-chain.js +0 -16
  282. package/dist/components/routes/redirect-chain.js.map +0 -1
  283. package/dist/components/routes/redirect-manager.d.ts +0 -13
  284. package/dist/components/routes/redirect-manager.js +0 -24
  285. package/dist/components/routes/redirect-manager.js.map +0 -1
  286. package/dist/components/routes/route-list.d.ts +0 -35
  287. package/dist/components/routes/route-list.js +0 -69
  288. package/dist/components/routes/route-list.js.map +0 -1
  289. package/dist/components/routes/routes-shell.d.ts +0 -14
  290. package/dist/components/routes/routes-shell.js +0 -68
  291. package/dist/components/routes/routes-shell.js.map +0 -1
  292. package/dist/components/sections.d.ts +0 -37
  293. package/dist/components/sections.js +0 -62
  294. package/dist/components/sections.js.map +0 -1
  295. package/dist/components/users/user-search.d.ts +0 -8
  296. package/dist/components/users/user-search.js +0 -13
  297. package/dist/components/users/user-search.js.map +0 -1
  298. package/dist/components/workspace-gate.d.ts +0 -12
  299. package/dist/components/workspace-gate.js +0 -28
  300. package/dist/components/workspace-gate.js.map +0 -1
  301. package/dist/lib/cms-views/views-store.d.ts +0 -70
  302. package/dist/lib/cms-views/views-store.js +0 -129
  303. package/dist/lib/cms-views/views-store.js.map +0 -1
  304. package/dist/lib/operations/graph-commands.d.ts +0 -136
  305. package/dist/lib/operations/graph-commands.js +0 -85
  306. package/dist/lib/operations/graph-commands.js.map +0 -1
  307. package/dist/lib/operations/graph-persistence.d.ts +0 -27
  308. package/dist/lib/operations/graph-persistence.js +0 -35
  309. package/dist/lib/operations/graph-persistence.js.map +0 -1
  310. package/dist/lib/routing/index.d.ts +0 -10
  311. package/dist/lib/routing/index.js +0 -11
  312. package/dist/lib/routing/index.js.map +0 -1
  313. package/dist/lib/routing/routing-analysis.d.ts +0 -26
  314. package/dist/lib/routing/routing-analysis.js +0 -37
  315. package/dist/lib/routing/routing-analysis.js.map +0 -1
  316. package/dist/lib/routing/routing-store.d.ts +0 -50
  317. package/dist/lib/routing/routing-store.js +0 -72
  318. package/dist/lib/routing/routing-store.js.map +0 -1
  319. package/dist/lib/working-graph.d.ts +0 -82
  320. package/dist/lib/working-graph.js +0 -113
  321. package/dist/lib/working-graph.js.map +0 -1
package/dist/ui/field.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ChevronDown } from 'lucide-react';
3
- import { cn } from '../lib/utils';
3
+ import { cn } from '../lib/utils.js';
4
4
  export function Field({ label, htmlFor, hint, error, required, children, className }) {
5
5
  return (_jsxs("div", { className: cn('flex flex-col gap-1.5', className), children: [_jsxs(FieldLabel, { htmlFor: htmlFor, children: [label, required ? _jsx("span", { className: "text-destructive", children: " *" }) : null] }), children, error ? (_jsx("p", { className: "text-xs font-medium text-destructive", role: "alert", children: error })) : hint ? (_jsx("p", { className: "text-xs text-muted-foreground", children: hint })) : null] }));
6
6
  }
@@ -1,19 +1,19 @@
1
- export * from './typography';
2
- export * from './layout';
3
- export * from './avatar';
4
- export * from './button';
5
- export * from './input';
6
- export * from './badge';
7
- export * from './card';
8
- export * from './panel';
9
- export * from './row-item';
10
- export * from './collapsible-section';
11
- export * from './separator';
12
- export * from './toolbar';
13
- export * from './inspector';
14
- export * from './dialog';
15
- export * from './dropdown-menu';
16
- export * from './popover';
17
- export * from './field';
18
- export * from './toast';
19
- export * from './segmented-control';
1
+ export * from './typography.js';
2
+ export * from './layout.js';
3
+ export * from './avatar.js';
4
+ export * from './button.js';
5
+ export * from './input.js';
6
+ export * from './badge.js';
7
+ export * from './card.js';
8
+ export * from './panel.js';
9
+ export * from './row-item.js';
10
+ export * from './collapsible-section.js';
11
+ export * from './separator.js';
12
+ export * from './toolbar.js';
13
+ export * from './inspector.js';
14
+ export * from './dialog.js';
15
+ export * from './dropdown-menu.js';
16
+ export * from './popover.js';
17
+ export * from './field.js';
18
+ export * from './toast.js';
19
+ export * from './segmented-control.js';
package/dist/ui/index.js CHANGED
@@ -1,20 +1,20 @@
1
- export * from './typography';
2
- export * from './layout';
3
- export * from './avatar';
4
- export * from './button';
5
- export * from './input';
6
- export * from './badge';
7
- export * from './card';
8
- export * from './panel';
9
- export * from './row-item';
10
- export * from './collapsible-section';
11
- export * from './separator';
12
- export * from './toolbar';
13
- export * from './inspector';
14
- export * from './dialog';
15
- export * from './dropdown-menu';
16
- export * from './popover';
17
- export * from './field';
18
- export * from './toast';
19
- export * from './segmented-control';
1
+ export * from './typography.js';
2
+ export * from './layout.js';
3
+ export * from './avatar.js';
4
+ export * from './button.js';
5
+ export * from './input.js';
6
+ export * from './badge.js';
7
+ export * from './card.js';
8
+ export * from './panel.js';
9
+ export * from './row-item.js';
10
+ export * from './collapsible-section.js';
11
+ export * from './separator.js';
12
+ export * from './toolbar.js';
13
+ export * from './inspector.js';
14
+ export * from './dialog.js';
15
+ export * from './dropdown-menu.js';
16
+ export * from './popover.js';
17
+ export * from './field.js';
18
+ export * from './toast.js';
19
+ export * from './segmented-control.js';
20
20
  //# sourceMappingURL=index.js.map
package/dist/ui/input.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cn } from '../lib/utils';
2
+ import { cn } from '../lib/utils.js';
3
3
  /** Named text input primitive — shadcn-style, token-driven. */
4
4
  export function Input({ className, type = 'text', ...props }) {
5
5
  return (_jsx("input", { type: type, className: cn('flex h-8 w-full rounded-md border border-input bg-background px-2.5 text-sm text-foreground shadow-sm transition-colors', 'placeholder:text-muted-foreground/60', 'focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none', 'disabled:cursor-not-allowed disabled:opacity-50', className), ...props }));
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { cn } from '../lib/utils';
2
+ import { cn } from '../lib/utils.js';
3
3
  /**
4
4
  * Named inspector section — the building block of the property inspector (brief §8.2).
5
5
  * Use instead of ad-hoc labelled div groups.
package/dist/ui/layout.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cn } from '../lib/utils';
2
+ import { cn } from '../lib/utils.js';
3
3
  const GAP = {
4
4
  none: 'gap-0',
5
5
  px: 'gap-px',
package/dist/ui/panel.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cn } from '../lib/utils';
3
- import { Row } from './layout';
2
+ import { cn } from '../lib/utils.js';
3
+ import { Row } from './layout.js';
4
4
  /**
5
5
  * Panel — the primary surface primitive for Studio regions (sidebars, inspector,
6
6
  * canvas chrome). Use instead of stray bordered divs.
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as PopoverPrimitive from '@radix-ui/react-popover';
3
- import { cn } from '../lib/utils';
3
+ import { cn } from '../lib/utils.js';
4
4
  /**
5
5
  * Anchored popover primitive — the same visual language as the dropdown menu,
6
6
  * but with popover (not menu) semantics, so it can hold arbitrary interactive
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { cn } from '../lib/utils';
3
- import { Row } from './layout';
2
+ import { cn } from '../lib/utils.js';
3
+ import { Row } from './layout.js';
4
4
  /**
5
5
  * A full-width list row (EC-246c): calm bordered chrome with a leading icon slot,
6
6
  * a truncating main slot, and a trailing slot for actions or badges. Replaces the
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { cn } from '../lib/utils';
2
+ import { cn } from '../lib/utils.js';
3
3
  /** The "segmented pill" tab family — see the module doc comment. */
4
4
  export function SegmentedControl({ items, ariaLabel, equalWidth, className, testId, }) {
5
5
  return (_jsx("div", { role: "group", ...(ariaLabel ? { 'aria-label': ariaLabel } : {}), ...(testId ? { 'data-testid': testId } : {}), className: cn('flex items-center gap-1 rounded-md bg-muted/50 p-0.5', className), children: items.map((item) => (_jsxs("button", { type: "button", "aria-pressed": item.active, onClick: item.onClick, ...(item.ariaLabel ? { 'aria-label': item.ariaLabel, title: item.ariaLabel } : {}), ...(item.testId ? { 'data-testid': item.testId } : {}), className: cn('items-center justify-center gap-1.5 rounded-[5px] px-2.5 py-1 text-xs font-medium transition-colors', equalWidth ? 'flex flex-1' : 'inline-flex', item.active
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cn } from '../lib/utils';
2
+ import { cn } from '../lib/utils.js';
3
3
  /** Named separator primitive. */
4
4
  export function Separator({ orientation = 'horizontal', className, ...props }) {
5
5
  return (_jsx("div", { role: "separator", "aria-orientation": orientation, className: cn('shrink-0 bg-border', orientation === 'horizontal' ? 'h-px w-full' : 'h-full w-px', className), ...props }));
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cn } from '../lib/utils';
3
- import { Separator } from './separator';
2
+ import { cn } from '../lib/utils.js';
3
+ import { Separator } from './separator.js';
4
4
  /** Named toolbar primitive — a horizontal control strip for Studio surfaces. */
5
5
  export function Toolbar({ className, ...props }) {
6
6
  return (_jsx("div", { role: "toolbar", className: cn('flex h-11 shrink-0 items-center gap-1 border border-border bg-panel px-2 rounded-md', className), ...props }));
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cn } from '../lib/utils';
2
+ import { cn } from '../lib/utils.js';
3
3
  const HEADING_SIZE = {
4
4
  display: 'text-3xl md:text-4xl font-semibold tracking-tight',
5
5
  h1: 'text-2xl font-semibold tracking-tight',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elytracms/studio",
3
3
  "author": "finaldream <hi@finaldream.de>",
4
- "version": "0.0.52",
4
+ "version": "0.0.54",
5
5
  "private": false,
6
6
  "description": "The Elytra studio engine (UI + lib + routes + data-only Convex schema) as an importable package. Extraction staged in EXTRACTION.md (EC-223); Stages 1-2 = UI primitives + router-free lib leaves.",
7
7
  "license": "MIT",
@@ -133,16 +133,23 @@
133
133
  "clsx": "^2.1.1",
134
134
  "convex": "^1.42.1",
135
135
  "convex-helpers": "0.1.120",
136
+ "jose": "^5.10.0",
136
137
  "lucide-react": "^1.23.0",
137
138
  "tailwind-merge": "^3.6.0",
138
139
  "zod": "^4.4.3",
139
- "@elytracms/core": "^0.0.52"
140
+ "@elytracms/core": "^0.0.54"
140
141
  },
141
142
  "peerDependencies": {
142
143
  "@tanstack/react-router": "^1.170.0",
143
144
  "react": "^19.0.0",
144
145
  "react-dom": "^19.0.0",
145
- "tailwindcss": "^4.3.0"
146
+ "tailwindcss": "^4.3.0",
147
+ "vite": ">=6"
148
+ },
149
+ "peerDependenciesMeta": {
150
+ "vite": {
151
+ "optional": true
152
+ }
146
153
  },
147
154
  "devDependencies": {
148
155
  "@tanstack/react-router": "^1.170.17",
@@ -151,10 +158,11 @@
151
158
  "@types/react-dom": "^19.2.3",
152
159
  "react": "^19.2.7",
153
160
  "react-dom": "^19.2.7",
154
- "typescript": "^6.0.3"
161
+ "typescript": "^6.0.3",
162
+ "vite": "^8.1.3"
155
163
  },
156
164
  "scripts": {
157
- "build": "tsc -p tsconfig.build.json",
165
+ "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json && node ../../scripts/fix-esm-extensions.mjs dist && node ../../scripts/check-esm-extensions.mjs dist",
158
166
  "typecheck": "tsc --noEmit && tsc -p convex/tsconfig.json --noEmit",
159
167
  "lint": "eslint src"
160
168
  }
@@ -1,14 +0,0 @@
1
- /**
2
- * Asset library surface (EC-151, EC-152): file picker + drag-and-drop uploads
3
- * with visible progress/failure/retry, a searchable grid of stored assets
4
- * (filename/alt/title), metadata editing (alt, title — persisted through the
5
- * operations asset facade), and reference-respecting deletion (usage count +
6
- * block-or-confirm). All storage access goes through the persistence adapter
7
- * via the assets store. Each card answers "where is this asset used?" via the
8
- * shared references panel (EC-172); `focusAssetId` (the `?asset=` deep-link
9
- * param) scrolls that card into view and opens its references dialog.
10
- */
11
- export declare function AssetLibrary({ projectId, focusAssetId, }: {
12
- projectId: string;
13
- focusAssetId?: string;
14
- }): import("react").JSX.Element;
@@ -1,202 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { AlertTriangle, ImageIcon, Link2, Pencil, RotateCcw, Search, Trash2, Upload, X, } from 'lucide-react';
3
- import { useEffect, useRef, useState } from 'react';
4
- import { translate } from '@elytracms/studio/lib/i18n';
5
- import { buildAssetReferenceImpact } from '@elytracms/studio/lib/references';
6
- import { cn } from '@elytracms/studio/lib/utils';
7
- import { ReferenceImpactRows } from '../cms/reference-confirm';
8
- import { ReferencesPanel } from '../references/references-panel';
9
- import { Badge, Button, Code, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Field, Grid, Heading, Input, Panel, PanelBody, PanelHeader, Row, Spacer, Stack, Text, } from '@elytracms/studio/ui';
10
- import { filterAssets } from './asset-search';
11
- import { useAssets } from './assets-store';
12
- /**
13
- * Asset library surface (EC-151, EC-152): file picker + drag-and-drop uploads
14
- * with visible progress/failure/retry, a searchable grid of stored assets
15
- * (filename/alt/title), metadata editing (alt, title — persisted through the
16
- * operations asset facade), and reference-respecting deletion (usage count +
17
- * block-or-confirm). All storage access goes through the persistence adapter
18
- * via the assets store. Each card answers "where is this asset used?" via the
19
- * shared references panel (EC-172); `focusAssetId` (the `?asset=` deep-link
20
- * param) scrolls that card into view and opens its references dialog.
21
- */
22
- export function AssetLibrary({ projectId, focusAssetId, }) {
23
- const store = useAssets();
24
- const fileInputRef = useRef(null);
25
- const [dragging, setDragging] = useState(false);
26
- const [query, setQuery] = useState('');
27
- const onPick = (event) => {
28
- if (event.target.files)
29
- store.upload([...event.target.files]);
30
- event.target.value = '';
31
- };
32
- const onDrop = (event) => {
33
- event.preventDefault();
34
- setDragging(false);
35
- if (event.dataTransfer.files.length > 0)
36
- store.upload([...event.dataTransfer.files]);
37
- };
38
- if (store.status === 'error') {
39
- return (_jsxs(Stack, { gap: "2", className: "p-6", "data-testid": "assets-error", children: [_jsxs(Row, { gap: "2", children: [_jsx(AlertTriangle, { className: "size-4 text-destructive" }), _jsx(Text, { weight: "semibold", children: translate('assets.error.loadFailed') })] }), _jsx(Text, { size: "sm", tone: "muted", children: store.errorMessage ?? translate('common.backendUnreachable') }), _jsx(Row, { children: _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void store.refresh(), children: [_jsx(RotateCcw, {}), " ", translate('common.retry')] }) })] }));
40
- }
41
- return (_jsxs(Stack, { gap: "4", className: cn('flex-1 p-4 sm:p-6', dragging && 'outline-2 outline-dashed outline-primary/60 -outline-offset-4'), onDragOver: (event) => {
42
- event.preventDefault();
43
- setDragging(true);
44
- }, onDragLeave: () => setDragging(false), onDrop: onDrop, "data-testid": "asset-library", children: [_jsxs(Row, { gap: "2", children: [_jsx(Heading, { level: 2, size: "h2", children: translate('assets.title') }), _jsx(Badge, { variant: "outline", children: store.assets.length }), _jsx(Spacer, {}), _jsx(AssetSearchInput, { value: query, onChange: setQuery }), _jsx("input", { ref: fileInputRef, type: "file", multiple: true, className: "hidden", onChange: onPick, "data-testid": "asset-file-input" }), _jsxs(Button, { size: "sm", onClick: () => fileInputRef.current?.click(), children: [_jsx(Upload, {}), " ", translate('common.upload')] })] }), _jsx(DropHint, { visible: dragging || store.assets.length === 0 }), store.uploads.length > 0 ? (_jsxs(Panel, { "data-testid": "upload-queue", children: [_jsx(PanelHeader, { children: _jsx(Text, { size: "xs", weight: "semibold", tone: "muted", children: translate('assets.uploads.heading') }) }), _jsx(PanelBody, { className: "divide-y divide-border p-0", children: store.uploads.map((entry) => (_jsx(UploadRow, { entry: entry, onRetry: () => store.retryUpload(entry.id), onDismiss: () => store.dismissUpload(entry.id) }, entry.id))) })] })) : null, store.status === 'loading' ? (_jsx(Text, { size: "sm", tone: "muted", children: translate('assets.loading') })) : (_jsx(AssetGrid, { assets: filterAssets(store.assets, query), filtered: query.trim().length > 0, projectId: projectId, focusAssetId: focusAssetId }))] }));
45
- }
46
- /** Named search primitive over filename / alt / title (EC-152). */
47
- function AssetSearchInput({ value, onChange, }) {
48
- return (_jsxs("span", { className: "relative", children: [_jsx(Search, { className: "pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: value, onChange: (e) => onChange(e.target.value), placeholder: translate('assets.searchPlaceholder'), "aria-label": translate('assets.searchLabel'), className: "w-56 pl-8", "data-testid": "asset-search" })] }));
49
- }
50
- /** Persistent drop affordance; doubles as the empty state. */
51
- function DropHint({ visible }) {
52
- return (_jsxs(Stack, { gap: "1", align: "center", className: cn('rounded-lg border border-dashed border-border p-6 text-center', !visible && 'hidden sm:flex'), children: [_jsx(ImageIcon, { className: "size-5 text-muted-foreground" }), _jsx(Text, { size: "sm", tone: "muted", children: translate('assets.dropHint') })] }));
53
- }
54
- /** Upload phase → the `assets.upload.phase.*` catalog key suffix (idle → queued). */
55
- const PHASE_KEY = {
56
- idle: 'queued',
57
- preparing: 'preparing',
58
- uploading: 'uploading',
59
- confirming: 'confirming',
60
- done: 'done',
61
- failed: 'failed',
62
- };
63
- /** One in-flight upload: progress while transferring, explicit failure + retry. */
64
- function UploadRow({ entry, onRetry, onDismiss, }) {
65
- const { state } = entry;
66
- const failed = state.phase === 'failed';
67
- return (_jsxs(Stack, { gap: "1", className: "px-3 py-2", "data-testid": "upload-row", children: [_jsxs(Row, { gap: "2", children: [_jsx(Text, { size: "sm", weight: "medium", className: "min-w-0 flex-1 truncate", children: entry.filename }), _jsxs(Badge, { variant: failed ? 'error' : state.phase === 'done' ? 'success' : 'info', children: [translate('assets.upload.phase.' + PHASE_KEY[state.phase]), state.attempt > 1
68
- ? ' · ' + translate('assets.upload.attempt', { count: state.attempt })
69
- : ''] }), failed ? (_jsxs(_Fragment, { children: [state.failure?.retryable !== false ? (_jsxs(Button, { variant: "outline", size: "sm", onClick: onRetry, "data-testid": "upload-retry", children: [_jsx(RotateCcw, {}), " Retry"] })) : null, _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: onDismiss, "aria-label": translate('assets.upload.dismissLabel'), children: _jsx(X, {}) })] })) : null] }), failed ? (_jsxs(Text, { size: "xs", tone: "muted", "data-testid": "upload-error", children: [state.failure?.stage, ": ", state.failure?.message] })) : (_jsx(ProgressBar, { value: state.progress }))] }));
70
- }
71
- /** Named progress primitive for byte transfer. */
72
- function ProgressBar({ value }) {
73
- return (_jsx("span", { className: "block h-1.5 w-full overflow-hidden rounded-full bg-muted", children: _jsx("span", { className: "block h-full rounded-full bg-primary transition-[width]", style: { width: `${Math.round(value * 100)}%` } }) }));
74
- }
75
- function AssetGrid({ assets, filtered, projectId, focusAssetId, }) {
76
- if (assets.length === 0) {
77
- // The unfiltered empty state is the DropHint above; the *filtered* empty
78
- // state must say so explicitly (deterministic, reproducible).
79
- return filtered ? (_jsx(Text, { size: "sm", tone: "muted", "data-testid": "asset-search-empty", children: translate('assets.searchEmpty') })) : null;
80
- }
81
- return (_jsx(Grid, { minItemWidth: "220px", gap: "3", "data-testid": "asset-grid", children: assets.map((asset) => (_jsx(AssetCard, { asset: asset, projectId: projectId, focused: asset.id === focusAssetId }, asset.id))) }));
82
- }
83
- function formatBytes(byteSize) {
84
- if (byteSize < 1024)
85
- return `${byteSize} B`;
86
- if (byteSize < 1024 * 1024)
87
- return `${(byteSize / 1024).toFixed(1)} KB`;
88
- return `${(byteSize / (1024 * 1024)).toFixed(1)} MB`;
89
- }
90
- function AssetCard({ asset, projectId, focused, }) {
91
- const cardRef = useRef(null);
92
- useEffect(() => {
93
- if (focused)
94
- cardRef.current?.scrollIntoView({ block: 'center', behavior: 'smooth' });
95
- }, [focused]);
96
- return (_jsx("div", { ref: cardRef, children: _jsxs(Panel, { className: cn('overflow-hidden', focused && 'ring-2 ring-primary/70'), "data-testid": "asset-card", children: [_jsx(AssetThumbnail, { asset: asset }), _jsxs(Stack, { gap: "1", className: "p-3", children: [_jsx(Text, { size: "sm", weight: "medium", className: "truncate", title: asset.filename, children: asset.title ?? asset.filename }), asset.title ? (_jsx(Text, { size: "xs", tone: "muted", className: "truncate", children: asset.filename })) : null, _jsx(Text, { size: "xs", tone: asset.alt ? 'muted' : 'subtle', className: "truncate", "data-testid": "asset-alt", children: asset.alt
97
- ? translate('assets.card.altPrefix', { alt: asset.alt })
98
- : translate('assets.card.noAlt') }), _jsxs(Row, { gap: "1", wrap: true, children: [_jsx(Code, { tone: "muted", children: asset.contentType }), asset.width && asset.height ? (_jsxs(Code, { tone: "muted", children: [asset.width, "\u00D7", asset.height] })) : null, _jsx(Code, { tone: "muted", children: formatBytes(asset.byteSize) })] }), _jsxs(Row, { gap: "1", children: [_jsx(Code, { tone: "muted", className: "min-w-0 flex-1 truncate", children: asset.id }), _jsx(AssetReferencesButton, { asset: asset, projectId: projectId, autoOpen: focused }), _jsx(EditAssetButton, { asset: asset }), _jsx(DeleteAssetButton, { asset: asset })] })] })] }) }));
99
- }
100
- /**
101
- * "Where is this asset used?" (EC-172): the shared references panel, inbound
102
- * over the EC-155 index, hosted in a dialog per asset card.
103
- */
104
- function AssetReferencesButton({ asset, projectId, autoOpen, }) {
105
- const [open, setOpen] = useState(false);
106
- useEffect(() => {
107
- if (autoOpen)
108
- setOpen(true);
109
- }, [autoOpen]);
110
- return (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": translate('assets.card.referencesLabel', { filename: asset.filename }), onClick: () => setOpen(true), "data-testid": "asset-references", children: _jsx(Link2, {}) }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-w-lg", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: translate('assets.references.title', { name: asset.title ?? asset.filename }) }), _jsx(DialogDescription, { children: translate('assets.references.description') })] }), open ? (_jsx(ReferencesPanel, { projectId: projectId, target: { type: 'asset', id: asset.id } })) : null] }) })] }));
111
- }
112
- /**
113
- * Metadata editing (EC-152): alt + title, persisted through the operations
114
- * asset facade (`assets.upsert`). Failures render in the dialog — never a
115
- * silent loss.
116
- */
117
- function EditAssetButton({ asset }) {
118
- const store = useAssets();
119
- const [open, setOpen] = useState(false);
120
- const [alt, setAlt] = useState('');
121
- const [title, setTitle] = useState('');
122
- const [error, setError] = useState(null);
123
- const [busy, setBusy] = useState(false);
124
- const openDialog = () => {
125
- setAlt(asset.alt ?? '');
126
- setTitle(asset.title ?? '');
127
- setError(null);
128
- setOpen(true);
129
- };
130
- const save = async () => {
131
- setBusy(true);
132
- setError(null);
133
- const result = await store.updateMetadata(asset.id, { alt, title });
134
- setBusy(false);
135
- if (result.ok)
136
- setOpen(false);
137
- else
138
- setError(result.message);
139
- };
140
- return (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": translate('assets.card.editLabel', { filename: asset.filename }), onClick: openDialog, "data-testid": "asset-edit", children: _jsx(Pencil, {}) }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-w-md", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: translate('assets.edit.title', { filename: asset.filename }) }), _jsx(DialogDescription, { children: translate('assets.edit.description') })] }), _jsx(Field, { label: translate('assets.edit.altLabel'), htmlFor: `asset-alt-${asset.id}`, children: _jsx(Input, { id: `asset-alt-${asset.id}`, value: alt, onChange: (e) => setAlt(e.target.value), placeholder: translate('assets.edit.altPlaceholder'), "data-testid": "asset-edit-alt" }) }), _jsx(Field, { label: translate('assets.edit.titleLabel'), htmlFor: `asset-title-${asset.id}`, children: _jsx(Input, { id: `asset-title-${asset.id}`, value: title, onChange: (e) => setTitle(e.target.value), placeholder: asset.filename, "data-testid": "asset-edit-title" }) }), error ? (_jsx(Text, { size: "sm", className: "text-destructive", "data-testid": "asset-edit-error", children: error })) : null, _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setOpen(false), disabled: busy, children: translate('common.cancel') }), _jsx(Button, { size: "sm", onClick: () => void save(), disabled: busy, "data-testid": "asset-edit-save", children: translate('common.save') })] })] }) })] }));
141
- }
142
- /** Image preview from the stored serve URL; icon fallback for other types. */
143
- function AssetThumbnail({ asset }) {
144
- const isImage = asset.contentType.startsWith('image/');
145
- // In-memory/opaque locators (memory://, convex://) are not fetchable by <img>.
146
- const resolvable = asset.url?.startsWith('http') ?? false;
147
- if (isImage && asset.url && resolvable) {
148
- return (_jsx("img", { src: asset.url, alt: asset.alt ?? asset.filename, className: "aspect-video w-full bg-muted object-cover" }));
149
- }
150
- return (_jsx("span", { className: "grid aspect-video w-full place-items-center bg-muted text-muted-foreground", children: _jsx(ImageIcon, { className: "size-6" }) }));
151
- }
152
- /**
153
- * Reference-respecting delete (EC-151, EC-202): the dialog enumerates the exact
154
- * inbound references — index-accurate (EC-155), with deep links to the field or
155
- * node — using the shared EC-173 impact rows. Deleting a referenced asset is
156
- * weak-by-default (allowed, with an explicit "delete anyway"): every reference
157
- * dangles into a visible validation issue rather than silently breaking.
158
- */
159
- function DeleteAssetButton({ asset }) {
160
- const store = useAssets();
161
- const [open, setOpen] = useState(false);
162
- const [usages, setUsages] = useState(null);
163
- const [error, setError] = useState(null);
164
- const [busy, setBusy] = useState(false);
165
- const openDialog = async () => {
166
- setOpen(true);
167
- setError(null);
168
- setUsages(null);
169
- try {
170
- setUsages(await store.usagesOf(asset.id));
171
- }
172
- catch (cause) {
173
- setError(cause instanceof Error ? cause.message : String(cause));
174
- }
175
- };
176
- const confirm = async () => {
177
- setBusy(true);
178
- setError(null);
179
- // The dialog has already enumerated usages (or shown there are none) and the user
180
- // is confirming deletion either way — so confirm unconditionally, letting
181
- // deleteAsset skip its redundant inbound re-check (EC-301 perf). Weak-by-default:
182
- // a still-referenced asset deletes anyway, dangling into a visible issue.
183
- const result = await store.remove(asset.id, { confirmInUse: true });
184
- setBusy(false);
185
- if (result.outcome === 'deleted' || result.outcome === 'not-found') {
186
- setOpen(false);
187
- }
188
- else if (result.outcome === 'blocked') {
189
- // A reference appeared between the pre-flight read and confirm (rare race).
190
- setError(translate(result.usageCount === 1 ? 'assets.delete.raceError.one' : 'assets.delete.raceError.other', { count: result.usageCount }));
191
- }
192
- else {
193
- setError(result.message);
194
- }
195
- };
196
- const impact = usages ? buildAssetReferenceImpact(usages) : null;
197
- const usageCount = impact?.broken.length ?? 0;
198
- return (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": `Delete ${asset.filename}`, onClick: () => void openDialog(), "data-testid": "asset-delete", children: _jsx(Trash2, {}) }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-h-[80vh] max-w-md overflow-y-auto", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: translate('assets.delete.title', { filename: asset.filename }) }), _jsx(DialogDescription, { children: translate('assets.delete.description') })] }), usages === null && error === null ? (_jsx(Text, { size: "sm", tone: "muted", children: translate('assets.delete.checking') })) : usageCount > 0 ? (_jsxs(Stack, { gap: "2", "data-testid": "asset-usage-warning", children: [_jsxs(Row, { gap: "2", children: [_jsx(AlertTriangle, { className: "size-4 text-warning" }), _jsx(Text, { size: "sm", weight: "semibold", children: translate(usageCount === 1 ? 'assets.delete.inUse.one' : 'assets.delete.inUse.other', {
199
- count: usageCount,
200
- }) })] }), _jsx(ReferenceImpactRows, { projectId: store.projectId, broken: impact.broken, onNavigate: () => setOpen(false) }), _jsx(Text, { size: "xs", tone: "muted", children: translate('assets.delete.brokenWarning') })] })) : (_jsx(Text, { size: "sm", tone: "muted", children: translate('assets.delete.noReferences') })), error ? (_jsx(Text, { size: "sm", className: "text-destructive", "data-testid": "asset-delete-error", children: error })) : null, _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setOpen(false), disabled: busy, children: translate('common.cancel') }), _jsx(Button, { variant: "destructive", size: "sm", onClick: () => void confirm(), disabled: busy || usages === null, "data-testid": "asset-delete-confirm", children: usageCount > 0 ? translate('assets.delete.confirmAnyway') : translate('common.delete') })] })] }) })] }));
201
- }
202
- //# sourceMappingURL=asset-library.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"asset-library.js","sourceRoot":"","sources":["../../../src/components/assets/asset-library.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n AlertTriangle,\n ImageIcon,\n Link2,\n Pencil,\n RotateCcw,\n Search,\n Trash2,\n Upload,\n X,\n} from 'lucide-react'\nimport { useEffect, useRef, useState } from 'react'\nimport type { ChangeEvent, DragEvent } from 'react'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport type { UploadState } from '@elytracms/studio/lib/assets'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { buildAssetReferenceImpact } from '@elytracms/studio/lib/references'\nimport type { ReferenceEntryView } from '@elytracms/studio/lib/references'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { ReferenceImpactRows } from '../cms/reference-confirm'\nimport { ReferencesPanel } from '../references/references-panel'\nimport {\n Badge,\n Button,\n Code,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Field,\n Grid,\n Heading,\n Input,\n Panel,\n PanelBody,\n PanelHeader,\n Row,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { filterAssets } from './asset-search'\nimport { useAssets } from './assets-store'\nimport type { UploadEntry } from './assets-store'\n\n/**\n * Asset library surface (EC-151, EC-152): file picker + drag-and-drop uploads\n * with visible progress/failure/retry, a searchable grid of stored assets\n * (filename/alt/title), metadata editing (alt, title — persisted through the\n * operations asset facade), and reference-respecting deletion (usage count +\n * block-or-confirm). All storage access goes through the persistence adapter\n * via the assets store. Each card answers \"where is this asset used?\" via the\n * shared references panel (EC-172); `focusAssetId` (the `?asset=` deep-link\n * param) scrolls that card into view and opens its references dialog.\n */\nexport function AssetLibrary({\n projectId,\n focusAssetId,\n}: {\n projectId: string\n focusAssetId?: string\n}) {\n const store = useAssets()\n const fileInputRef = useRef<HTMLInputElement | null>(null)\n const [dragging, setDragging] = useState(false)\n const [query, setQuery] = useState('')\n\n const onPick = (event: ChangeEvent<HTMLInputElement>) => {\n if (event.target.files) store.upload([...event.target.files])\n event.target.value = ''\n }\n\n const onDrop = (event: DragEvent) => {\n event.preventDefault()\n setDragging(false)\n if (event.dataTransfer.files.length > 0) store.upload([...event.dataTransfer.files])\n }\n\n if (store.status === 'error') {\n return (\n <Stack gap=\"2\" className=\"p-6\" data-testid=\"assets-error\">\n <Row gap=\"2\">\n <AlertTriangle className=\"size-4 text-destructive\" />\n <Text weight=\"semibold\">{translate('assets.error.loadFailed')}</Text>\n </Row>\n <Text size=\"sm\" tone=\"muted\">\n {store.errorMessage ?? translate('common.backendUnreachable')}\n </Text>\n <Row>\n <Button variant=\"outline\" size=\"sm\" onClick={() => void store.refresh()}>\n <RotateCcw /> {translate('common.retry')}\n </Button>\n </Row>\n </Stack>\n )\n }\n\n return (\n <Stack\n gap=\"4\"\n className={cn(\n 'flex-1 p-4 sm:p-6',\n dragging && 'outline-2 outline-dashed outline-primary/60 -outline-offset-4',\n )}\n onDragOver={(event) => {\n event.preventDefault()\n setDragging(true)\n }}\n onDragLeave={() => setDragging(false)}\n onDrop={onDrop}\n data-testid=\"asset-library\"\n >\n <Row gap=\"2\">\n <Heading level={2} size=\"h2\">\n {translate('assets.title')}\n </Heading>\n <Badge variant=\"outline\">{store.assets.length}</Badge>\n <Spacer />\n <AssetSearchInput value={query} onChange={setQuery} />\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n className=\"hidden\"\n onChange={onPick}\n data-testid=\"asset-file-input\"\n />\n <Button size=\"sm\" onClick={() => fileInputRef.current?.click()}>\n <Upload /> {translate('common.upload')}\n </Button>\n </Row>\n\n <DropHint visible={dragging || store.assets.length === 0} />\n\n {store.uploads.length > 0 ? (\n <Panel data-testid=\"upload-queue\">\n <PanelHeader>\n <Text size=\"xs\" weight=\"semibold\" tone=\"muted\">\n {translate('assets.uploads.heading')}\n </Text>\n </PanelHeader>\n <PanelBody className=\"divide-y divide-border p-0\">\n {store.uploads.map((entry) => (\n <UploadRow\n key={entry.id}\n entry={entry}\n onRetry={() => store.retryUpload(entry.id)}\n onDismiss={() => store.dismissUpload(entry.id)}\n />\n ))}\n </PanelBody>\n </Panel>\n ) : null}\n\n {store.status === 'loading' ? (\n <Text size=\"sm\" tone=\"muted\">\n {translate('assets.loading')}\n </Text>\n ) : (\n <AssetGrid\n assets={filterAssets(store.assets, query)}\n filtered={query.trim().length > 0}\n projectId={projectId}\n focusAssetId={focusAssetId}\n />\n )}\n </Stack>\n )\n}\n\n/** Named search primitive over filename / alt / title (EC-152). */\nfunction AssetSearchInput({\n value,\n onChange,\n}: {\n value: string\n onChange: (value: string) => void\n}) {\n return (\n <span className=\"relative\">\n <Search className=\"pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={translate('assets.searchPlaceholder')}\n aria-label={translate('assets.searchLabel')}\n className=\"w-56 pl-8\"\n data-testid=\"asset-search\"\n />\n </span>\n )\n}\n\n/** Persistent drop affordance; doubles as the empty state. */\nfunction DropHint({ visible }: { visible: boolean }) {\n return (\n <Stack\n gap=\"1\"\n align=\"center\"\n className={cn(\n 'rounded-lg border border-dashed border-border p-6 text-center',\n !visible && 'hidden sm:flex',\n )}\n >\n <ImageIcon className=\"size-5 text-muted-foreground\" />\n <Text size=\"sm\" tone=\"muted\">\n {translate('assets.dropHint')}\n </Text>\n </Stack>\n )\n}\n\n/** Upload phase → the `assets.upload.phase.*` catalog key suffix (idle → queued). */\nconst PHASE_KEY: Record<UploadState['phase'], string> = {\n idle: 'queued',\n preparing: 'preparing',\n uploading: 'uploading',\n confirming: 'confirming',\n done: 'done',\n failed: 'failed',\n}\n\n/** One in-flight upload: progress while transferring, explicit failure + retry. */\nfunction UploadRow({\n entry,\n onRetry,\n onDismiss,\n}: {\n entry: UploadEntry\n onRetry: () => void\n onDismiss: () => void\n}) {\n const { state } = entry\n const failed = state.phase === 'failed'\n return (\n <Stack gap=\"1\" className=\"px-3 py-2\" data-testid=\"upload-row\">\n <Row gap=\"2\">\n <Text size=\"sm\" weight=\"medium\" className=\"min-w-0 flex-1 truncate\">\n {entry.filename}\n </Text>\n <Badge variant={failed ? 'error' : state.phase === 'done' ? 'success' : 'info'}>\n {translate('assets.upload.phase.' + PHASE_KEY[state.phase])}\n {state.attempt > 1\n ? ' · ' + translate('assets.upload.attempt', { count: state.attempt })\n : ''}\n </Badge>\n {failed ? (\n <>\n {state.failure?.retryable !== false ? (\n <Button variant=\"outline\" size=\"sm\" onClick={onRetry} data-testid=\"upload-retry\">\n <RotateCcw /> Retry\n </Button>\n ) : null}\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onDismiss}\n aria-label={translate('assets.upload.dismissLabel')}\n >\n <X />\n </Button>\n </>\n ) : null}\n </Row>\n {failed ? (\n <Text size=\"xs\" tone=\"muted\" data-testid=\"upload-error\">\n {state.failure?.stage}: {state.failure?.message}\n </Text>\n ) : (\n <ProgressBar value={state.progress} />\n )}\n </Stack>\n )\n}\n\n/** Named progress primitive for byte transfer. */\nfunction ProgressBar({ value }: { value: number }) {\n return (\n <span className=\"block h-1.5 w-full overflow-hidden rounded-full bg-muted\">\n <span\n className=\"block h-full rounded-full bg-primary transition-[width]\"\n style={{ width: `${Math.round(value * 100)}%` }}\n />\n </span>\n )\n}\n\nfunction AssetGrid({\n assets,\n filtered,\n projectId,\n focusAssetId,\n}: {\n assets: readonly AssetRecord[]\n filtered: boolean\n projectId: string\n focusAssetId?: string\n}) {\n if (assets.length === 0) {\n // The unfiltered empty state is the DropHint above; the *filtered* empty\n // state must say so explicitly (deterministic, reproducible).\n return filtered ? (\n <Text size=\"sm\" tone=\"muted\" data-testid=\"asset-search-empty\">\n {translate('assets.searchEmpty')}\n </Text>\n ) : null\n }\n return (\n <Grid minItemWidth=\"220px\" gap=\"3\" data-testid=\"asset-grid\">\n {assets.map((asset) => (\n <AssetCard\n key={asset.id}\n asset={asset}\n projectId={projectId}\n focused={asset.id === focusAssetId}\n />\n ))}\n </Grid>\n )\n}\n\nfunction formatBytes(byteSize: number): string {\n if (byteSize < 1024) return `${byteSize} B`\n if (byteSize < 1024 * 1024) return `${(byteSize / 1024).toFixed(1)} KB`\n return `${(byteSize / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction AssetCard({\n asset,\n projectId,\n focused,\n}: {\n asset: AssetRecord\n projectId: string\n /** Deep-link target (`?asset=`): scroll into view + open the references dialog. */\n focused?: boolean\n}) {\n const cardRef = useRef<HTMLDivElement | null>(null)\n useEffect(() => {\n if (focused) cardRef.current?.scrollIntoView({ block: 'center', behavior: 'smooth' })\n }, [focused])\n return (\n <div ref={cardRef}>\n <Panel\n className={cn('overflow-hidden', focused && 'ring-2 ring-primary/70')}\n data-testid=\"asset-card\"\n >\n <AssetThumbnail asset={asset} />\n <Stack gap=\"1\" className=\"p-3\">\n <Text size=\"sm\" weight=\"medium\" className=\"truncate\" title={asset.filename}>\n {asset.title ?? asset.filename}\n </Text>\n {asset.title ? (\n <Text size=\"xs\" tone=\"muted\" className=\"truncate\">\n {asset.filename}\n </Text>\n ) : null}\n <Text\n size=\"xs\"\n tone={asset.alt ? 'muted' : 'subtle'}\n className=\"truncate\"\n data-testid=\"asset-alt\"\n >\n {asset.alt\n ? translate('assets.card.altPrefix', { alt: asset.alt })\n : translate('assets.card.noAlt')}\n </Text>\n <Row gap=\"1\" wrap>\n <Code tone=\"muted\">{asset.contentType}</Code>\n {asset.width && asset.height ? (\n <Code tone=\"muted\">\n {asset.width}×{asset.height}\n </Code>\n ) : null}\n <Code tone=\"muted\">{formatBytes(asset.byteSize)}</Code>\n </Row>\n <Row gap=\"1\">\n <Code tone=\"muted\" className=\"min-w-0 flex-1 truncate\">\n {asset.id}\n </Code>\n <AssetReferencesButton asset={asset} projectId={projectId} autoOpen={focused} />\n <EditAssetButton asset={asset} />\n <DeleteAssetButton asset={asset} />\n </Row>\n </Stack>\n </Panel>\n </div>\n )\n}\n\n/**\n * \"Where is this asset used?\" (EC-172): the shared references panel, inbound\n * over the EC-155 index, hosted in a dialog per asset card.\n */\nfunction AssetReferencesButton({\n asset,\n projectId,\n autoOpen,\n}: {\n asset: AssetRecord\n projectId: string\n autoOpen?: boolean\n}) {\n const [open, setOpen] = useState(false)\n useEffect(() => {\n if (autoOpen) setOpen(true)\n }, [autoOpen])\n return (\n <>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={translate('assets.card.referencesLabel', { filename: asset.filename })}\n onClick={() => setOpen(true)}\n data-testid=\"asset-references\"\n >\n <Link2 />\n </Button>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent className=\"max-w-lg\">\n <DialogHeader>\n <DialogTitle>\n {translate('assets.references.title', { name: asset.title ?? asset.filename })}\n </DialogTitle>\n <DialogDescription>{translate('assets.references.description')}</DialogDescription>\n </DialogHeader>\n {open ? (\n <ReferencesPanel projectId={projectId} target={{ type: 'asset', id: asset.id }} />\n ) : null}\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\n/**\n * Metadata editing (EC-152): alt + title, persisted through the operations\n * asset facade (`assets.upsert`). Failures render in the dialog — never a\n * silent loss.\n */\nfunction EditAssetButton({ asset }: { asset: AssetRecord }) {\n const store = useAssets()\n const [open, setOpen] = useState(false)\n const [alt, setAlt] = useState('')\n const [title, setTitle] = useState('')\n const [error, setError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n\n const openDialog = () => {\n setAlt(asset.alt ?? '')\n setTitle(asset.title ?? '')\n setError(null)\n setOpen(true)\n }\n\n const save = async () => {\n setBusy(true)\n setError(null)\n const result = await store.updateMetadata(asset.id, { alt, title })\n setBusy(false)\n if (result.ok) setOpen(false)\n else setError(result.message)\n }\n\n return (\n <>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={translate('assets.card.editLabel', { filename: asset.filename })}\n onClick={openDialog}\n data-testid=\"asset-edit\"\n >\n <Pencil />\n </Button>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <DialogTitle>{translate('assets.edit.title', { filename: asset.filename })}</DialogTitle>\n <DialogDescription>{translate('assets.edit.description')}</DialogDescription>\n </DialogHeader>\n <Field label={translate('assets.edit.altLabel')} htmlFor={`asset-alt-${asset.id}`}>\n <Input\n id={`asset-alt-${asset.id}`}\n value={alt}\n onChange={(e) => setAlt(e.target.value)}\n placeholder={translate('assets.edit.altPlaceholder')}\n data-testid=\"asset-edit-alt\"\n />\n </Field>\n <Field label={translate('assets.edit.titleLabel')} htmlFor={`asset-title-${asset.id}`}>\n <Input\n id={`asset-title-${asset.id}`}\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder={asset.filename}\n data-testid=\"asset-edit-title\"\n />\n </Field>\n {error ? (\n <Text size=\"sm\" className=\"text-destructive\" data-testid=\"asset-edit-error\">\n {error}\n </Text>\n ) : null}\n <DialogFooter>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setOpen(false)} disabled={busy}>\n {translate('common.cancel')}\n </Button>\n <Button\n size=\"sm\"\n onClick={() => void save()}\n disabled={busy}\n data-testid=\"asset-edit-save\"\n >\n {translate('common.save')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\n/** Image preview from the stored serve URL; icon fallback for other types. */\nfunction AssetThumbnail({ asset }: { asset: AssetRecord }) {\n const isImage = asset.contentType.startsWith('image/')\n // In-memory/opaque locators (memory://, convex://) are not fetchable by <img>.\n const resolvable = asset.url?.startsWith('http') ?? false\n if (isImage && asset.url && resolvable) {\n return (\n <img\n src={asset.url}\n alt={asset.alt ?? asset.filename}\n className=\"aspect-video w-full bg-muted object-cover\"\n />\n )\n }\n return (\n <span className=\"grid aspect-video w-full place-items-center bg-muted text-muted-foreground\">\n <ImageIcon className=\"size-6\" />\n </span>\n )\n}\n\n/**\n * Reference-respecting delete (EC-151, EC-202): the dialog enumerates the exact\n * inbound references — index-accurate (EC-155), with deep links to the field or\n * node — using the shared EC-173 impact rows. Deleting a referenced asset is\n * weak-by-default (allowed, with an explicit \"delete anyway\"): every reference\n * dangles into a visible validation issue rather than silently breaking.\n */\nfunction DeleteAssetButton({ asset }: { asset: AssetRecord }) {\n const store = useAssets()\n const [open, setOpen] = useState(false)\n const [usages, setUsages] = useState<ReferenceEntryView[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n\n const openDialog = async () => {\n setOpen(true)\n setError(null)\n setUsages(null)\n try {\n setUsages(await store.usagesOf(asset.id))\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n }\n }\n\n const confirm = async () => {\n setBusy(true)\n setError(null)\n // The dialog has already enumerated usages (or shown there are none) and the user\n // is confirming deletion either way — so confirm unconditionally, letting\n // deleteAsset skip its redundant inbound re-check (EC-301 perf). Weak-by-default:\n // a still-referenced asset deletes anyway, dangling into a visible issue.\n const result = await store.remove(asset.id, { confirmInUse: true })\n setBusy(false)\n if (result.outcome === 'deleted' || result.outcome === 'not-found') {\n setOpen(false)\n } else if (result.outcome === 'blocked') {\n // A reference appeared between the pre-flight read and confirm (rare race).\n setError(\n translate(\n result.usageCount === 1 ? 'assets.delete.raceError.one' : 'assets.delete.raceError.other',\n { count: result.usageCount },\n ),\n )\n } else {\n setError(result.message)\n }\n }\n\n const impact = usages ? buildAssetReferenceImpact(usages) : null\n const usageCount = impact?.broken.length ?? 0\n return (\n <>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={`Delete ${asset.filename}`}\n onClick={() => void openDialog()}\n data-testid=\"asset-delete\"\n >\n <Trash2 />\n </Button>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent className=\"max-h-[80vh] max-w-md overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>{translate('assets.delete.title', { filename: asset.filename })}</DialogTitle>\n <DialogDescription>{translate('assets.delete.description')}</DialogDescription>\n </DialogHeader>\n {usages === null && error === null ? (\n <Text size=\"sm\" tone=\"muted\">\n {translate('assets.delete.checking')}\n </Text>\n ) : usageCount > 0 ? (\n <Stack gap=\"2\" data-testid=\"asset-usage-warning\">\n <Row gap=\"2\">\n <AlertTriangle className=\"size-4 text-warning\" />\n <Text size=\"sm\" weight=\"semibold\">\n {translate(usageCount === 1 ? 'assets.delete.inUse.one' : 'assets.delete.inUse.other', {\n count: usageCount,\n })}\n </Text>\n </Row>\n <ReferenceImpactRows\n projectId={store.projectId}\n broken={impact!.broken}\n onNavigate={() => setOpen(false)}\n />\n <Text size=\"xs\" tone=\"muted\">\n {translate('assets.delete.brokenWarning')}\n </Text>\n </Stack>\n ) : (\n <Text size=\"sm\" tone=\"muted\">\n {translate('assets.delete.noReferences')}\n </Text>\n )}\n {error ? (\n <Text size=\"sm\" className=\"text-destructive\" data-testid=\"asset-delete-error\">\n {error}\n </Text>\n ) : null}\n <DialogFooter>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setOpen(false)} disabled={busy}>\n {translate('common.cancel')}\n </Button>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n onClick={() => void confirm()}\n disabled={busy || usages === null}\n data-testid=\"asset-delete-confirm\"\n >\n {usageCount > 0 ? translate('assets.delete.confirmAnyway') : translate('common.delete')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </>\n )\n}\n"]}
@@ -1,19 +0,0 @@
1
- import type { ReactNode } from 'react';
2
- /**
3
- * Backend boot gate (EC-153). Convex is the default backend and the in-memory
4
- * playground is opt-in only — so the two failure shapes are **visible,
5
- * deterministic error states**, never a silent fallback:
6
- *
7
- * 1. *Misconfigured* (no `VITE_CONVEX_URL`, no playground flag): a boot-error
8
- * screen explaining exactly what to set, rendered before auth or any
9
- * adapter is constructed.
10
- * 2. *Backend unreachable at runtime*: the gate probes `adapter.status()` once
11
- * on the client; a dead deployment renders an error screen with the
12
- * transport detail and a retry — the studio never swaps storage backends.
13
- *
14
- * Playground and healthy-Convex render children untouched. SSR renders the
15
- * deterministic "checking" state (mirrors `SessionLoading`).
16
- */
17
- export declare function BackendGate({ children }: {
18
- children: ReactNode;
19
- }): import("react").JSX.Element;