@jini-ai/admin 0.4.12 → 0.6.10

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 (151) hide show
  1. package/CHANGELOG.md +70 -2
  2. package/dist/agent-plugins/controllers/agent-plugin-files.controller.js +14 -9
  3. package/dist/agent-plugins/controllers/agent-plugins.controller.js +12 -9
  4. package/dist/agent-plugins/package-file-tree.js +54 -51
  5. package/dist/agent-plugins/react/components/AgentPluginRow.d.ts +1 -1
  6. package/dist/agent-plugins/react/components/AgentPluginRow.js +9 -2
  7. package/dist/agent-plugins/react/hooks/AgentPluginInspector.hooks.d.ts +5 -5
  8. package/dist/agent-plugins/react/hooks/AgentPluginInspector.hooks.js +14 -5
  9. package/dist/agent-plugins/react/hooks/AgentPluginPanel.hooks.d.ts +15 -13
  10. package/dist/agent-plugins/react/hooks/AgentPluginPanel.hooks.js +54 -40
  11. package/dist/agent-plugins/react/hooks/AgentPluginsPage.hooks.d.ts +13 -13
  12. package/dist/agent-plugins/react/hooks/AgentPluginsPage.hooks.js +23 -11
  13. package/dist/browser/navigation.js +44 -27
  14. package/dist/comments/adapters/memory.js +30 -12
  15. package/dist/comments/conformance/comments-api.conformance.js +55 -33
  16. package/dist/comments/controllers/comment-queue.controller.js +13 -4
  17. package/dist/comments/react/hooks/use-comment-queue.hooks.d.ts +17 -2
  18. package/dist/comments/react/hooks/use-comment-queue.hooks.js +79 -30
  19. package/dist/comments/react/hooks/use-comment-settings.hooks.js +9 -5
  20. package/dist/comments/react/index.d.ts +2 -1
  21. package/dist/comments/react/index.js +1 -1
  22. package/dist/consent/react/ConsentFields.d.ts +35 -0
  23. package/dist/consent/react/ConsentFields.js +76 -0
  24. package/dist/consent/react/ConsentPreviewFrame.d.ts +14 -0
  25. package/dist/consent/react/ConsentPreviewFrame.js +14 -0
  26. package/dist/consent/react/ConsentSettingsPanel.d.ts +27 -4
  27. package/dist/consent/react/ConsentSettingsPanel.js +6 -17
  28. package/dist/consent/react/index.d.ts +6 -1
  29. package/dist/consent/react/index.js +2 -0
  30. package/dist/consent/react/settings-panel.hooks.d.ts +28 -1
  31. package/dist/consent/react/settings-panel.hooks.js +26 -0
  32. package/dist/core/entities/rules.js +126 -81
  33. package/dist/core/manifest/rules.js +10 -10
  34. package/dist/core/module/create-admin.js +195 -117
  35. package/dist/core/ports/redirects.d.ts +8 -6
  36. package/dist/core/ports/redirects.js +8 -6
  37. package/dist/core/routing/rules.js +9 -4
  38. package/dist/core/transport/http.js +37 -20
  39. package/dist/forms/adapters/memory.js +22 -11
  40. package/dist/forms/conformance/forms-api.conformance.d.ts +6 -4
  41. package/dist/forms/conformance/forms-api.conformance.js +42 -46
  42. package/dist/forms/field-settings.js +150 -115
  43. package/dist/forms/html-rules.js +45 -28
  44. package/dist/forms/react/components/FieldSettingsRows.d.ts +22 -0
  45. package/dist/forms/react/components/FieldSettingsRows.js +29 -0
  46. package/dist/forms/react/components/FieldSettingsSection.d.ts +1 -1
  47. package/dist/forms/react/components/FieldSettingsSection.js +5 -6
  48. package/dist/forms/react/components/FormEditorFieldsBody.d.ts +1 -1
  49. package/dist/forms/react/components/FormEditorFieldsBody.js +4 -15
  50. package/dist/forms/react/components/FormEditorFieldsBodyParts.d.ts +18 -0
  51. package/dist/forms/react/components/FormEditorFieldsBodyParts.js +32 -0
  52. package/dist/forms/react/components/FormSubmissions.d.ts +1 -1
  53. package/dist/forms/react/components/FormSubmissions.js +4 -20
  54. package/dist/forms/react/components/FormSubmissionsTable.d.ts +19 -0
  55. package/dist/forms/react/components/FormSubmissionsTable.js +22 -0
  56. package/dist/media/adapters/memory.js +31 -26
  57. package/dist/media/controllers/edit-media.controller.js +2 -1
  58. package/dist/media/controllers/library.controller.js +48 -35
  59. package/dist/media/controllers/providers.controller.js +2 -1
  60. package/dist/media/html-attributes.js +29 -15
  61. package/dist/media/index.d.ts +1 -1
  62. package/dist/media/index.js +1 -1
  63. package/dist/media/react/components/MediaIcons.d.ts +0 -3
  64. package/dist/media/react/components/MediaIcons.js +0 -9
  65. package/dist/media/react/components/MediaLightbox.d.ts +2 -1
  66. package/dist/media/react/components/MediaLightbox.js +11 -5
  67. package/dist/media/react/hooks/LibraryTab.hooks.d.ts +55 -45
  68. package/dist/media/react/hooks/LibraryTab.hooks.js +107 -70
  69. package/dist/media/react/hooks/MediaCard.hooks.d.ts +27 -31
  70. package/dist/media/react/hooks/MediaCard.hooks.js +52 -26
  71. package/dist/media/react/hooks/MediaGrid.hooks.d.ts +1 -1
  72. package/dist/media/react/hooks/MediaLightbox.hooks.d.ts +10 -14
  73. package/dist/media/react/hooks/MediaLightbox.hooks.js +9 -56
  74. package/dist/media/react/hooks/MediaPage.hooks.d.ts +6 -0
  75. package/dist/media/react/hooks/MediaPage.hooks.js +29 -12
  76. package/dist/media/react/hooks/ProvidersTab.hooks.d.ts +33 -33
  77. package/dist/media/react/hooks/ProvidersTab.hooks.js +119 -79
  78. package/dist/media/react/hooks/UploadButton.hooks.d.ts +7 -5
  79. package/dist/media/react/hooks/UploadButton.hooks.js +36 -24
  80. package/dist/media/react/index.js +4 -0
  81. package/dist/media/react/tabs/LibraryTab.d.ts +1 -1
  82. package/dist/media/react/tabs/LibraryTab.js +2 -2
  83. package/dist/media/rules.d.ts +7 -0
  84. package/dist/media/rules.js +25 -2
  85. package/dist/react/components/ConfirmDialog/ConfirmDialog.js +5 -8
  86. package/dist/react/components/ConfirmDialog/confirm-dialog-attrs.d.ts +20 -0
  87. package/dist/react/components/ConfirmDialog/confirm-dialog-attrs.js +24 -0
  88. package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.js +16 -7
  89. package/dist/react/components/RowMenu/RowMenu.js +16 -10
  90. package/dist/react/components/Sidebar.js +32 -26
  91. package/dist/react/entities/EntityEdit.js +17 -14
  92. package/dist/react/entities/EntityField.d.ts +5 -2
  93. package/dist/react/entities/EntityField.js +38 -23
  94. package/dist/react/entities/EntityList.js +6 -5
  95. package/dist/react/entities/RelationTruncationNotice.d.ts +9 -0
  96. package/dist/react/entities/RelationTruncationNotice.js +9 -0
  97. package/dist/react/entities/use-entity-edit.d.ts +4 -2
  98. package/dist/react/entities/use-entity-edit.js +52 -33
  99. package/dist/react/shell/AdminShell.js +40 -23
  100. package/dist/react/shell/model.d.ts +4 -1
  101. package/dist/react/shell/model.js +3 -2
  102. package/dist/react/shell/use-admin-shell-session.js +25 -26
  103. package/dist/redirects/adapters/memory.js +9 -2
  104. package/dist/redirects/conformance/redirects-api.conformance.js +66 -36
  105. package/dist/redirects/react/hooks/use-import-redirects-form.hooks.d.ts +1 -1
  106. package/dist/redirects/react/hooks/use-import-redirects-form.hooks.js +10 -3
  107. package/dist/redirects/react/hooks/wired.hooks.d.ts +1 -1
  108. package/dist/security/adapters/http.js +25 -10
  109. package/dist/security/adapters/memory.js +19 -5
  110. package/dist/security/controllers/access-tokens.controller.js +27 -26
  111. package/dist/security/controllers/other-credentials.controller.js +4 -2
  112. package/dist/security/controllers/root-key.controller.js +17 -9
  113. package/dist/security/react/hooks/AccessTokensTab.hooks.d.ts +40 -38
  114. package/dist/security/react/hooks/AccessTokensTab.hooks.js +36 -9
  115. package/dist/security/react/hooks/RootKeyTab.hooks.d.ts +14 -14
  116. package/dist/security/react/hooks/RootKeyTab.hooks.js +12 -3
  117. package/dist/security/react/tabs/RootKeyTab.d.ts +1 -1
  118. package/dist/security/react/tabs/RootKeyTab.js +12 -3
  119. package/dist/security/rules.js +45 -25
  120. package/dist/seo/controllers/seo.controller.js +4 -2
  121. package/dist/seo/react/components/MediaRefField.d.ts +1 -1
  122. package/dist/seo/react/components/MediaRefField.js +19 -2
  123. package/dist/seo/react/hooks/MediaRefField.hooks.js +3 -1
  124. package/dist/seo/react/hooks/SeoPage.hooks.d.ts +1 -1
  125. package/dist/seo/react/hooks/use-share-image-warning.hooks.js +48 -34
  126. package/dist/seo/react/pages/SeoPage.d.ts +4 -4
  127. package/dist/seo/react/pages/SeoPage.js +26 -6
  128. package/dist/seo/rules.js +39 -10
  129. package/dist/source-control/conformance/source-control-api.conformance.js +68 -38
  130. package/dist/source-control/controllers/source-control.controller.js +20 -8
  131. package/dist/source-control/react/hooks/ProvidersTab.hooks.d.ts +2 -1
  132. package/dist/source-control/react/hooks/ProvidersTab.hooks.js +18 -11
  133. package/dist/widgets/conformance/widgets-api.conformance.js +15 -1
  134. package/dist/widgets/controllers/instance-editor.controller.js +33 -24
  135. package/dist/widgets/controllers/library.controller.js +18 -12
  136. package/dist/widgets/controllers/region-editor.controller.js +6 -5
  137. package/dist/widgets/react/hooks/page-views.hooks.d.ts +1 -0
  138. package/dist/widgets/react/hooks/page-views.hooks.js +3 -2
  139. package/dist/widgets/react/hooks/use-widget-regions.hooks.d.ts +1 -1
  140. package/dist/widgets/react/hooks/use-widget-regions.hooks.js +9 -3
  141. package/dist/widgets/react/hooks/use-widgets-library.hooks.js +9 -2
  142. package/dist/widgets/react/pages/WidgetInstanceEditor.d.ts +1 -1
  143. package/dist/widgets/react/pages/WidgetInstanceEditor.js +7 -8
  144. package/dist/widgets/rules.d.ts +14 -2
  145. package/dist/widgets/rules.js +9 -6
  146. package/package.json +23 -14
  147. package/src/themes/default/admin.css +51 -0
  148. package/src/themes/default/chat.css +21 -0
  149. package/src/themes/default/manifest.json +29 -0
  150. package/dist/media/styles.d.ts +0 -4
  151. package/dist/media/styles.js +0 -5
@@ -4,9 +4,6 @@ export declare function ReloadIcon(_required: Record<string, never>, _optional?:
4
4
  export declare function HiddenEyeIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
5
5
  export declare function EyeIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
6
6
  export declare function ExpandIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
7
- export declare function CloseIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
8
- export declare function PreviousIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
9
- export declare function NextIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
10
7
  export declare function AllIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
11
8
  export declare function ImageIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
12
9
  export declare function VideoIcon(_required: Record<string, never>, _optional?: Record<string, never>): import("react").JSX.Element;
@@ -15,15 +15,6 @@ export function EyeIcon(_required, _optional = {}) {
15
15
  export function ExpandIcon(_required, _optional = {}) {
16
16
  return _jsx("svg", { className: "jini-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: _jsx("path", { className: "jini-icon-path", d: "M6 2H2v4m10-4h4v4M2 12v4h4m10-4v4h-4" }) });
17
17
  }
18
- export function CloseIcon(_required, _optional = {}) {
19
- return _jsx("svg", { className: "jini-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: _jsx("path", { className: "jini-icon-path", d: "m4 4 10 10M14 4 4 14" }) });
20
- }
21
- export function PreviousIcon(_required, _optional = {}) {
22
- return _jsx("svg", { className: "jini-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: _jsx("path", { className: "jini-icon-path", d: "m11 4-5 5 5 5" }) });
23
- }
24
- export function NextIcon(_required, _optional = {}) {
25
- return _jsx("svg", { className: "jini-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: _jsx("path", { className: "jini-icon-path", d: "m7 4 5 5-5 5" }) });
26
- }
27
18
  export function AllIcon(_required, _optional = {}) {
28
19
  return _jsxs("svg", { className: "jini-icon", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("rect", { className: "jini-icon-rect", x: "4", y: "4", width: "6.5", height: "6.5", rx: "1.2" }), _jsx("rect", { className: "jini-icon-rect", x: "13.5", y: "4", width: "6.5", height: "6.5", rx: "1.2" }), _jsx("rect", { className: "jini-icon-rect", x: "4", y: "13.5", width: "6.5", height: "6.5", rx: "1.2" }), _jsx("rect", { className: "jini-icon-rect", x: "13.5", y: "13.5", width: "6.5", height: "6.5", rx: "1.2" })] });
29
20
  }
@@ -1,5 +1,6 @@
1
1
  import type { MediaLightboxProps } from '../hooks/MediaLightbox.hooks.js';
2
2
  /** One shared dialog for the grid: mounting N real dialogs to show at most one open
3
- * at a time buys nothing and costs N DOM subtrees. */
3
+ * at a time buys nothing and costs N DOM subtrees. Chrome, keyboard and focus come from
4
+ * the shared `Lightbox`; this only supplies the asset and its agent-addressable parts. */
4
5
  export declare function MediaLightbox(props: MediaLightboxProps, _optional?: Record<string, never>): import("react").JSX.Element;
5
6
  //# sourceMappingURL=MediaLightbox.d.ts.map
@@ -1,12 +1,18 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { MediaDialog } from './MediaDialog.js';
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Lightbox } from '@jini-ai/ui/admin-widgets';
3
3
  import { useMediaLightbox } from '../hooks/MediaLightbox.hooks.js';
4
- import { CloseIcon, PreviousIcon, NextIcon } from './MediaIcons.js';
5
4
  import { MediaPreview } from './MediaCard.js';
6
5
  /** One shared dialog for the grid: mounting N real dialogs to show at most one open
7
- * at a time buys nothing and costs N DOM subtrees. */
6
+ * at a time buys nothing and costs N DOM subtrees. Chrome, keyboard and focus come from
7
+ * the shared `Lightbox`; this only supplies the asset and its agent-addressable parts. */
8
8
  export function MediaLightbox(props, _optional = {}) {
9
9
  const vm = useMediaLightbox(props);
10
- return (_jsxs(MediaDialog, { className: "jini-media-lightbox", dialogRef: vm.dialogRef, attrs: { 'data-jini-part': 'media.lightbox', 'data-jini-size': 'large' }, title: vm.title, open: vm.open, onClose: vm.onClose, children: [_jsxs("div", { className: "jini-media-lightbox-header", children: [_jsx("span", { className: "jini-media-lightbox-title", "aria-hidden": "true", children: vm.item?.title }), _jsx("span", { className: "jini-media-lightbox-counter", children: vm.position }), _jsxs("button", { type: "button", className: "jini-media-lightbox-close", ref: vm.closeRef, "data-jini-part": "media.lightbox.close", "data-agent-element": "media-lightbox-close", "data-jini-autofocus": true, onClick: vm.onClose, children: [_jsx(CloseIcon, {}), _jsx("span", { className: "jini-visually-hidden", children: "Close" })] })] }), _jsxs("div", { className: "jini-media-lightbox-stage", children: [vm.hasPrev && _jsxs("button", { type: "button", className: "jini-media-lightbox-nav jini-media-lightbox-nav-prev", "data-jini-part": "media.lightbox.previous", "data-agent-element": "media-lightbox-prev", onClick: vm.previous, children: [_jsx(PreviousIcon, {}), _jsx("span", { className: "jini-visually-hidden", children: "Previous" })] }), _jsx("div", { className: "jini-media-lightbox-media", children: vm.item && _jsx(MediaPreview, { item: vm.item, api: vm.api }, vm.item.id) }), vm.hasNext && _jsxs("button", { type: "button", className: "jini-media-lightbox-nav jini-media-lightbox-nav-next", "data-jini-part": "media.lightbox.next", "data-agent-element": "media-lightbox-next", onClick: vm.next, children: [_jsx(NextIcon, {}), _jsx("span", { className: "jini-visually-hidden", children: "Next" })] })] })] }));
10
+ return (_jsx(Lightbox, { ...vm.lightbox, className: "jini-media-lightbox", labels: { close: 'Close', previous: 'Previous', next: 'Next' }, partAttrs: MEDIA_LIGHTBOX_PARTS, children: vm.item && _jsx("div", { className: "jini-media-lightbox-media", children: _jsx(MediaPreview, { item: vm.item, api: props.api }, vm.item.id) }) }));
11
11
  }
12
+ const MEDIA_LIGHTBOX_PARTS = {
13
+ dialog: { 'data-jini-part': 'media.lightbox', 'data-jini-size': 'large' },
14
+ close: { 'data-jini-part': 'media.lightbox.close', 'data-agent-element': 'media-lightbox-close' },
15
+ previous: { 'data-jini-part': 'media.lightbox.previous', 'data-agent-element': 'media-lightbox-prev' },
16
+ next: { 'data-jini-part': 'media.lightbox.next', 'data-agent-element': 'media-lightbox-next' },
17
+ };
12
18
  //# sourceMappingURL=MediaLightbox.js.map
@@ -1,19 +1,65 @@
1
+ import type { RefObject } from 'react';
1
2
  import type { TabViewProps } from '../../../core/react/bind-react.js';
2
3
  import { createLibraryController } from '../../controllers/library.controller.js';
3
4
  import type { MediaAsset } from '../../models.js';
5
+ import type { LibraryState } from '../../controllers/library.controller.js';
6
+ import type { MediaApiPort } from '../../ports.js';
7
+ type LibraryController = ReturnType<typeof createLibraryController>;
4
8
  export declare function useLibraryTab({ params, permissions }: TabViewProps, _optional?: Record<string, never>): {
5
- emptyLabel: string;
6
- canUpload: boolean;
7
- success: string | null;
8
- gridRef: import("react").RefObject<HTMLDivElement | null>;
9
- onRetry: () => void;
9
+ purgeDialog: {
10
+ filename?: string;
11
+ open: boolean;
12
+ pending: boolean;
13
+ };
14
+ upload({ input, alt }: Parameters<LibraryController["upload"]>[0]): Promise<boolean>;
15
+ cancelPurge: () => void | undefined;
16
+ confirmPurge: () => Promise<void> | undefined;
17
+ t: {
18
+ readonly media: "Media";
19
+ readonly all: "All";
20
+ readonly images: "Images";
21
+ readonly videos: "Videos";
22
+ readonly providers: "External Providers";
23
+ readonly upload: "Upload";
24
+ readonly title: "Title";
25
+ readonly alt: "Alt text";
26
+ readonly save: "Save";
27
+ readonly cancel: "Cancel";
28
+ readonly empty: "No media found";
29
+ readonly loading: "Loading media…";
30
+ readonly choose: "Choose media";
31
+ readonly trash: "Trash";
32
+ readonly purge: "Delete permanently";
33
+ readonly purgeWarning: "This permanently deletes the file. This cannot be undone.";
34
+ readonly edit: "Edit metadata";
35
+ readonly preview: "View larger";
36
+ readonly replace: "Replace file";
37
+ readonly unavailable: "Provider service unavailable";
38
+ readonly untyped: "Some assets have no recorded content type and only appear under All.";
39
+ };
40
+ loading: boolean;
41
+ busy: boolean;
42
+ untyped: boolean;
10
43
  statusFilter: string;
44
+ orderBy: import("../../models.js").MediaOrderBy;
45
+ search: string;
11
46
  onStatus: ({ value }: {
12
47
  value: string;
13
48
  }) => void;
49
+ onSearch: ({ value: search }: {
50
+ value: string;
51
+ }) => void;
52
+ onOrder: ({ value }: {
53
+ value: string;
54
+ }) => void;
55
+ emptyLabel: string;
56
+ canUpload: boolean;
57
+ success: string | null;
58
+ gridRef: RefObject<HTMLDivElement | null>;
59
+ onRetry: () => void;
14
60
  canPurge: boolean;
15
61
  controller: {
16
- getSnapshot: () => Readonly<import("../../index.js").LibraryState>;
62
+ getSnapshot: () => Readonly<LibraryState>;
17
63
  subscribe: ({ listener }: {
18
64
  listener: () => void;
19
65
  }) => () => void;
@@ -44,7 +90,7 @@ export declare function useLibraryTab({ params, permissions }: TabViewProps, _op
44
90
  }, _optional?: Record<string, never>): Promise<boolean>;
45
91
  dispose(_required?: Record<string, never>, _optional?: Record<string, never>): void;
46
92
  } | null;
47
- snapshot: import("../../index.js").LibraryState | null;
93
+ snapshot: LibraryState | null;
48
94
  actions: {
49
95
  onPurge?: () => void | undefined;
50
96
  onRestore?: () => void;
@@ -55,7 +101,7 @@ export declare function useLibraryTab({ params, permissions }: TabViewProps, _op
55
101
  busy: boolean;
56
102
  }[];
57
103
  items: readonly MediaAsset[];
58
- mediaApi: import("../../ports.js").MediaApiPort;
104
+ mediaApi: MediaApiPort;
59
105
  editing: MediaAsset | null;
60
106
  closeEdit: () => void;
61
107
  saved: ({ message }?: {
@@ -64,42 +110,6 @@ export declare function useLibraryTab({ params, permissions }: TabViewProps, _op
64
110
  lightboxIndex: number | null;
65
111
  onNavigate: import("react").Dispatch<import("react").SetStateAction<number | null>>;
66
112
  closeLightbox: () => void;
67
- onSearch: ({ value: search }: {
68
- value: string;
69
- }) => void;
70
- orderBy: import("../../models.js").MediaOrderBy;
71
- onOrder: ({ value }: {
72
- value: string;
73
- }) => void;
74
- search: string;
75
- loading: boolean;
76
- busy: boolean;
77
- untyped: boolean;
78
- upload({ input, alt }: Parameters<ReturnType<typeof createLibraryController>["upload"]>[0]): Promise<boolean>;
79
- cancelPurge: () => void | undefined;
80
- confirmPurge: () => Promise<void> | undefined;
81
- t: {
82
- readonly media: "Media";
83
- readonly all: "All";
84
- readonly images: "Images";
85
- readonly videos: "Videos";
86
- readonly providers: "External Providers";
87
- readonly upload: "Upload";
88
- readonly title: "Title";
89
- readonly alt: "Alt text";
90
- readonly save: "Save";
91
- readonly cancel: "Cancel";
92
- readonly empty: "No media found";
93
- readonly loading: "Loading media…";
94
- readonly choose: "Choose media";
95
- readonly trash: "Trash";
96
- readonly purge: "Delete permanently";
97
- readonly purgeWarning: "This permanently deletes the file. This cannot be undone.";
98
- readonly edit: "Edit metadata";
99
- readonly preview: "View larger";
100
- readonly replace: "Replace file";
101
- readonly unavailable: "Provider service unavailable";
102
- readonly untyped: "Some assets have no recorded content type and only appear under All.";
103
- };
104
113
  };
114
+ export {};
105
115
  //# sourceMappingURL=LibraryTab.hooks.d.ts.map
@@ -3,13 +3,104 @@ import { MEDIA_QUERY_KEYS, useMediaLibraryQuery } from './MediaQuery.hooks.js';
3
3
  import { useEffect, useLayoutEffect, useRef, useState } from 'react';
4
4
  import { useController } from '../../../core/react/use-controller.js';
5
5
  import { createLibraryController } from '../../controllers/library.controller.js';
6
- import { useSharedMediaLibrary } from './MediaPage.hooks.js';
6
+ import { settleFromCache, useSharedMediaLibrary } from './MediaPage.hooks.js';
7
7
  import { useMediaPorts } from './MediaPorts.hooks.js';
8
8
  import { mediaMessagesEn } from '../../messages.en.js';
9
9
  import { canRestoreMedia } from '../../rules.js';
10
+ const EMPTY_LABELS = {
11
+ images: 'No images yet. Upload an image to see it here.',
12
+ videos: 'No videos yet. Upload a video to see it here.',
13
+ all: 'No media found',
14
+ };
15
+ function contentFilter({ params }, _optional = {}) {
16
+ return params.filter === 'images' || params.filter === 'videos' ? params.filter : 'all';
17
+ }
18
+ function libraryPermissions({ permissions, api }, _optional = {}) {
19
+ return {
20
+ canUpload: permissions.includes('media.upload'),
21
+ canEdit: permissions.includes('media.update'),
22
+ canTrash: permissions.includes('media.trash'),
23
+ canRestore: permissions.includes('media.restore') && canRestoreMedia({ api }),
24
+ canPurge: permissions.includes('media.delete.force'),
25
+ };
26
+ }
27
+ /** Focuses the card with `id`, else whichever card now sits at `index`, else the grid itself. */
28
+ function focusCard({ grid, index }, { id } = {}) {
29
+ const cards = Array.from(grid?.querySelectorAll('[data-jini-part="media.card"]') ?? []);
30
+ const byId = id === undefined ? undefined : cards.find(card => card.dataset.mediaId === id);
31
+ const target = byId ?? cards[Math.min(index, cards.length - 1)] ?? grid;
32
+ target?.focus({ preventScroll: true });
33
+ }
34
+ function queryView({ snapshot }, _optional = {}) {
35
+ return {
36
+ statusFilter: snapshot?.query.status ?? 'all',
37
+ orderBy: snapshot?.query.orderBy ?? 'created',
38
+ search: snapshot?.query.search ?? '',
39
+ };
40
+ }
41
+ function progressView({ snapshot, filter }, _optional = {}) {
42
+ return {
43
+ loading: !snapshot || (snapshot.items === null && snapshot.loading && !snapshot.error),
44
+ busy: snapshot?.busy ?? false,
45
+ untyped: filter !== 'all' && snapshot?.hasUntyped === true,
46
+ };
47
+ }
48
+ function purgeDialogView({ snapshot }, _optional = {}) {
49
+ const pending = snapshot?.pendingPurge;
50
+ return { open: !!pending, pending: snapshot?.busy ?? false, ...(pending ? { filename: pending.title } : {}) };
51
+ }
52
+ function queryHandlers({ controller }, _optional = {}) {
53
+ return {
54
+ onStatus: ({ value }) => {
55
+ const { status: _status, ...query } = controller?.getSnapshot().query ?? {};
56
+ void controller?.setQuery({ query: { ...query, ...(value === 'active' || value === 'trashed' ? { status: value } : {}) } });
57
+ },
58
+ onSearch: ({ value: search }) => {
59
+ void controller?.setQuery({ query: { ...controller.getSnapshot().query, search } });
60
+ },
61
+ onOrder: ({ value }) => {
62
+ void controller?.setQuery({ query: { ...controller.getSnapshot().query, orderBy: value === 'alphabetical' ? 'alphabetical' : 'created' } });
63
+ },
64
+ };
65
+ }
66
+ function cardActions({ items, pendingIds, can, controller, announce, onEdit, onPreview }, _optional = {}) {
67
+ return items.map((item, index) => {
68
+ const write = (run, verb) => {
69
+ void run?.then(saved => { if (saved)
70
+ announce({ message: `${verb} ${item.title}`, focus: { id: item.id, index } }); });
71
+ };
72
+ return {
73
+ item,
74
+ busy: pendingIds.includes(item.id),
75
+ ...(can.canEdit ? { onEdit: () => onEdit(item) } : {}),
76
+ onPreview: () => onPreview(index),
77
+ ...(can.canTrash ? { onTrash: () => write(controller?.trash({ id: item.id }), 'Trashed') } : {}),
78
+ ...(can.canRestore ? { onRestore: () => write(controller?.restore({ id: item.id }), 'Restored') } : {}),
79
+ ...(can.canPurge ? { onPurge: () => controller?.requestPurge({ item }) } : {}),
80
+ };
81
+ });
82
+ }
83
+ /** Once a write has settled and the grid re-rendered, return focus to the affected card. */
84
+ function useFocusAfterWrites({ gridRef, snapshot }, _optional = {}) {
85
+ const [focusAfterAction, setFocusAfterAction] = useState(null);
86
+ useLayoutEffect(() => {
87
+ if (!focusAfterAction || snapshot?.busy || snapshot?.loading)
88
+ return;
89
+ focusCard({ grid: gridRef.current, index: focusAfterAction.index }, { id: focusAfterAction.id });
90
+ setFocusAfterAction(null);
91
+ }, [focusAfterAction, snapshot?.busy, snapshot?.loading, snapshot?.items]);
92
+ const [focusAfterPurge, setFocusAfterPurge] = useState(null);
93
+ useLayoutEffect(() => {
94
+ if (focusAfterPurge === null || snapshot?.pendingPurge || snapshot?.busy)
95
+ return;
96
+ focusCard({ grid: gridRef.current, index: focusAfterPurge });
97
+ setFocusAfterPurge(null);
98
+ }, [focusAfterPurge, snapshot?.pendingPurge, snapshot?.busy, snapshot?.items]);
99
+ return { setFocusAfterAction, setFocusAfterPurge };
100
+ }
10
101
  export function useLibraryTab({ params, permissions = [] }, _optional = {}) {
11
102
  const { mediaApi, mediaEvents } = useMediaPorts();
12
- const filter = params.filter === 'images' || params.filter === 'videos' ? params.filter : 'all';
103
+ const filter = contentFilter({ params });
13
104
  const shared = useSharedMediaLibrary();
14
105
  const cached = useMediaLibraryQuery({ api: mediaApi }, { enabled: !shared });
15
106
  const invalidate = useInvalidate();
@@ -25,28 +116,10 @@ export function useLibraryTab({ params, permissions = [] }, _optional = {}) {
25
116
  const { controller, snapshot } = shared ?? local;
26
117
  useEffect(() => !shared ? mediaEvents?.subscribe({ onRefresh: () => invalidate({ key: MEDIA_QUERY_KEYS.root }) }) : undefined, [mediaEvents, controller, shared, invalidate]);
27
118
  useEffect(() => { if (!shared)
28
- local.controller?.settleLibrary({ ...(cached.query.data ? { all: cached.query.data } : {}), error: cached.query.error }); }, [shared, local.controller, cached.query.data, cached.query.error]);
119
+ settleFromCache({ controller: local.controller, query: cached.query }); }, [shared, local.controller, cached.query.data, cached.query.error]);
29
120
  const gridRef = useRef(null);
30
121
  const [success, setSuccess] = useState(null);
31
- const [focusAfterAction, setFocusAfterAction] = useState(null);
32
- useLayoutEffect(() => {
33
- if (!focusAfterAction || snapshot?.busy || snapshot?.loading)
34
- return;
35
- const cards = Array.from(gridRef.current?.querySelectorAll('[data-jini-part="media.card"]') ?? []);
36
- const target = cards.find(card => card.dataset.mediaId === focusAfterAction.id) ?? cards[Math.min(focusAfterAction.index, cards.length - 1)] ?? gridRef.current;
37
- target?.focus({ preventScroll: true });
38
- setFocusAfterAction(null);
39
- }, [focusAfterAction, snapshot?.busy, snapshot?.loading, snapshot?.items]);
40
- const [focusAfterPurge, setFocusAfterPurge] = useState(null);
41
- useLayoutEffect(() => {
42
- if (focusAfterPurge === null || snapshot?.pendingPurge || snapshot?.busy)
43
- return;
44
- const grid = gridRef.current;
45
- const cards = grid?.querySelectorAll('[data-jini-part="media.card"]');
46
- const target = cards?.[Math.min(focusAfterPurge, cards.length - 1)] ?? grid;
47
- target?.focus({ preventScroll: true });
48
- setFocusAfterPurge(null);
49
- }, [focusAfterPurge, snapshot?.pendingPurge, snapshot?.busy, snapshot?.items]);
122
+ const { setFocusAfterAction, setFocusAfterPurge } = useFocusAfterWrites({ gridRef, snapshot });
50
123
  useEffect(() => {
51
124
  // A shared page controller survives tab unmounts. Re-entering after an error must
52
125
  // be a recovery opportunity even when its cached filter already matches this tab.
@@ -58,32 +131,9 @@ export function useLibraryTab({ params, permissions = [] }, _optional = {}) {
58
131
  // buttons move the number without re-deriving "what's next" from an item reference.
59
132
  const [lightboxIndex, setLightboxIndex] = useState(null);
60
133
  const items = snapshot?.items ?? [], pendingIds = snapshot?.pendingIds ?? [];
61
- const canUpload = permissions.includes('media.upload');
62
- const canEdit = permissions.includes('media.update');
63
- const canTrash = permissions.includes('media.trash');
64
- const canRestore = permissions.includes('media.restore') && canRestoreMedia({ api: mediaApi });
65
- const canPurge = permissions.includes('media.delete.force');
66
- const actions = items.map((item, index) => ({
67
- item,
68
- busy: pendingIds.includes(item.id),
69
- ...(canEdit ? { onEdit: () => setEditing(item) } : {}),
70
- onPreview: () => setLightboxIndex(index),
71
- ...(canTrash
72
- ? {
73
- onTrash: () => {
74
- void controller?.trash({ id: item.id }).then(saved => { if (saved) {
75
- setSuccess(`Trashed ${item.title}`);
76
- setFocusAfterAction({ id: item.id, index });
77
- } });
78
- },
79
- }
80
- : {}),
81
- ...(canRestore ? { onRestore: () => { void controller?.restore({ id: item.id }).then(saved => { if (saved) {
82
- setSuccess(`Restored ${item.title}`);
83
- setFocusAfterAction({ id: item.id, index });
84
- } }); } } : {}),
85
- ...(canPurge ? { onPurge: () => controller?.requestPurge({ item }) } : {}),
86
- }));
134
+ const can = libraryPermissions({ permissions, api: mediaApi });
135
+ const announce = ({ message, focus }) => { setSuccess(message); setFocusAfterAction(focus); };
136
+ const actions = cardActions({ items, pendingIds, can, controller, announce, onEdit: setEditing, onPreview: setLightboxIndex });
87
137
  function closeEdit() {
88
138
  setEditing(null);
89
139
  }
@@ -99,18 +149,12 @@ export function useLibraryTab({ params, permissions = [] }, _optional = {}) {
99
149
  setFocusAfterAction({ id: item.id, index: Math.max(0, items.findIndex(row => row.id === item.id)) });
100
150
  }
101
151
  return {
102
- emptyLabel: filter === 'images' ? 'No images yet. Upload an image to see it here.'
103
- : filter === 'videos' ? 'No videos yet. Upload a video to see it here.' : 'No media found',
104
- canUpload,
152
+ emptyLabel: EMPTY_LABELS[filter],
153
+ canUpload: can.canUpload,
105
154
  success,
106
155
  gridRef,
107
156
  onRetry: () => { void controller?.load(); },
108
- statusFilter: snapshot?.query.status ?? 'all',
109
- onStatus: ({ value }) => {
110
- const { status: _status, ...query } = controller?.getSnapshot().query ?? {};
111
- void controller?.setQuery({ query: { ...query, ...(value === 'active' || value === 'trashed' ? { status: value } : {}) } });
112
- },
113
- canPurge,
157
+ canPurge: can.canPurge,
114
158
  controller,
115
159
  snapshot,
116
160
  actions,
@@ -122,19 +166,12 @@ export function useLibraryTab({ params, permissions = [] }, _optional = {}) {
122
166
  lightboxIndex,
123
167
  onNavigate: setLightboxIndex,
124
168
  closeLightbox: () => setLightboxIndex(null),
125
- onSearch: ({ value: search }) => {
126
- void controller?.setQuery({ query: { ...controller.getSnapshot().query, search } });
127
- },
128
- orderBy: snapshot?.query.orderBy ?? 'created',
129
- onOrder: ({ value }) => {
130
- void controller?.setQuery({ query: { ...controller.getSnapshot().query, orderBy: value === 'alphabetical' ? 'alphabetical' : 'created' } });
131
- },
132
- search: snapshot?.query.search ?? '',
133
- loading: !snapshot || (snapshot.items === null && snapshot.loading && !snapshot.error),
134
- busy: snapshot?.busy ?? false,
135
- untyped: filter !== 'all' && snapshot?.hasUntyped === true,
169
+ ...queryHandlers({ controller }),
170
+ ...queryView({ snapshot }),
171
+ ...progressView({ snapshot, filter }),
172
+ purgeDialog: purgeDialogView({ snapshot }),
136
173
  async upload({ input, alt }) {
137
- if (!canUpload)
174
+ if (!can.canUpload)
138
175
  return false;
139
176
  const result = await controller?.upload({ input, ...(alt === undefined ? {} : { alt }) }) ?? false;
140
177
  if (result)
@@ -145,7 +182,7 @@ export function useLibraryTab({ params, permissions = [] }, _optional = {}) {
145
182
  confirmPurge: () => {
146
183
  const item = controller?.getSnapshot().pendingPurge;
147
184
  // The controller refuses to purge without a pending item, so there is nothing to announce.
148
- if (!canPurge || !controller || !item)
185
+ if (!can.canPurge || !controller || !item)
149
186
  return;
150
187
  const index = items.findIndex(row => row.id === item.id);
151
188
  return controller.confirmPurge({ confirmed: true }).then(saved => {
@@ -13,14 +13,26 @@ export interface MediaCardProps {
13
13
  readonly onPurge?: () => void;
14
14
  readonly onChoose?: () => void;
15
15
  }
16
+ type PreviewStage = 'image' | 'video' | 'unsupported';
16
17
  export declare function useMediaCard(props: MediaCardProps, _optional?: Record<string, never>): {
17
- busy: boolean;
18
- byteSize: string | null;
19
- uploadDate: string | null;
20
- metadataSeparator: string;
21
- preview: () => void | undefined;
22
- canPreview: boolean;
23
- imagePreview: boolean;
18
+ statusClassName: string;
19
+ statusLabel: "active" | "trashed";
20
+ statusTone: "warning" | "success";
21
+ trashed: boolean;
22
+ menuItems: {
23
+ onPress: () => void;
24
+ disabled: boolean;
25
+ attrs?: {
26
+ 'data-jini-variant': string;
27
+ };
28
+ id: string;
29
+ label: string;
30
+ }[];
31
+ src: string | undefined;
32
+ stage: PreviewStage;
33
+ onImageError: () => void;
34
+ onVideoError: () => void;
35
+ alt: string;
24
36
  handles: {
25
37
  expand: string;
26
38
  edit: string;
@@ -43,32 +55,15 @@ export declare function useMediaCard(props: MediaCardProps, _optional?: Record<s
43
55
  'data-agent-element': string;
44
56
  'data-agent-label': string;
45
57
  };
46
- menuItems: ({
47
- id: string;
48
- label: string;
49
- onPress: () => void;
50
- disabled: boolean;
51
- attrs?: never;
52
- } | {
53
- id: string;
54
- label: string;
55
- attrs: {
56
- 'data-jini-variant': string;
57
- };
58
- onPress: () => void;
59
- disabled: boolean;
60
- })[];
61
- src: string | undefined;
62
- stage: "video" | "image" | "unsupported";
58
+ canPreview: boolean;
59
+ imagePreview: boolean;
63
60
  image: boolean | "" | undefined;
64
61
  video: boolean | "" | undefined;
65
- onImageError: () => void;
66
- onVideoError: () => void;
67
- alt: string;
68
- statusClassName: string;
69
- statusLabel: "active" | "trashed";
70
- statusTone: "warning" | "success";
71
- trashed: boolean;
62
+ preview: () => void | undefined;
63
+ byteSize: string | null;
64
+ uploadDate: string | null;
65
+ metadataSeparator: string;
66
+ busy: boolean;
72
67
  item: MediaAsset;
73
68
  handleBase?: string;
74
69
  locale?: string;
@@ -80,4 +75,5 @@ export declare function useMediaCard(props: MediaCardProps, _optional?: Record<s
80
75
  onPurge?: () => void;
81
76
  onChoose?: () => void;
82
77
  };
78
+ export {};
83
79
  //# sourceMappingURL=MediaCard.hooks.d.ts.map
@@ -1,5 +1,52 @@
1
1
  import { useState } from 'react';
2
2
  import { safeMediaUrl, formatByteSize, formatUploadDate, mediaRowHandles } from '../../rules.js';
3
+ function menuItems({ props }, _optional = {}) {
4
+ const disabled = props.busy === true, trashed = props.item.status === 'trashed';
5
+ const entries = [
6
+ { id: 'edit', label: 'Edit metadata', onPress: props.onEdit, shown: true },
7
+ { id: 'trash', label: 'Trash', onPress: props.onTrash, shown: !trashed },
8
+ // Restore belongs only in a trashed card's menu; active cards have nothing to restore.
9
+ { id: 'restore', label: 'Restore', onPress: props.onRestore, shown: trashed },
10
+ { id: 'purge', label: 'Delete permanently', onPress: props.onPurge, shown: trashed },
11
+ ];
12
+ return entries.flatMap(({ id, label, onPress, shown }) => onPress && shown
13
+ ? [{ id, label, ...(id === 'purge' ? { attrs: { 'data-jini-variant': 'danger' } } : {}), onPress, disabled }]
14
+ : []);
15
+ }
16
+ function metadata({ item }, { locale = 'en-US' } = {}) {
17
+ const byteSize = item.byteSize === undefined ? null : formatByteSize({ bytes: item.byteSize }, { locale });
18
+ const uploadDate = formatUploadDate({ createdAt: item.createdAt }, { locale });
19
+ return { byteSize, uploadDate, metadataSeparator: byteSize && uploadDate ? ' · ' : '' };
20
+ }
21
+ function previewFlags({ src, stage }, { onPreview } = {}) {
22
+ const previewable = !!onPreview && !!src;
23
+ return {
24
+ canPreview: previewable && stage !== 'unsupported',
25
+ imagePreview: previewable && stage === 'image',
26
+ image: src && stage === 'image',
27
+ video: src && stage === 'video',
28
+ };
29
+ }
30
+ function statusView({ item }, _optional = {}) {
31
+ const trashed = item.status === 'trashed';
32
+ return {
33
+ statusClassName: `jini-status jini-status-${item.status}`,
34
+ statusLabel: item.status,
35
+ statusTone: trashed ? 'warning' : 'success',
36
+ trashed,
37
+ };
38
+ }
39
+ function agentAttrs({ handleBase, title }, _optional = {}) {
40
+ return {
41
+ handles: { expand: `${handleBase}-expand`, edit: `${handleBase}-edit`, menu: `${handleBase}-menu` },
42
+ previewAttrs: { 'data-jini-part': 'media.preview', 'data-agent-element': `${handleBase}-expand`,
43
+ 'data-agent-label': 'Open this asset larger in the lightbox', 'aria-label': `View "${title}" larger` },
44
+ editAttrs: { 'data-jini-part': 'media.edit', 'data-agent-element': `${handleBase}-edit`,
45
+ 'data-agent-label': 'Open the edit form for this asset as a modal', 'aria-label': `Edit "${title}"` },
46
+ menuLabel: `Actions for "${title}"`,
47
+ menuAttrs: { 'data-agent-element': `${handleBase}-menu`, 'data-agent-label': `Actions for "${title}"` },
48
+ };
49
+ }
3
50
  export function useMediaCard(props, _optional = {}) {
4
51
  /** Preview optimistically: image -> video -> placeholder. The list supplies no reliable media
5
52
  * kind, and the byte route guarantees GET rather than HEAD, so probing headers would assume an
@@ -12,41 +59,20 @@ export function useMediaCard(props, _optional = {}) {
12
59
  // immediately when the versioned URL changes, including in an already-open lightbox.
13
60
  const stage = probe.src === src ? probe.stage : 'image';
14
61
  const handleBase = props.handleBase ?? mediaRowHandles({ media: [props.item] })[0];
15
- const byteSize = props.item.byteSize === undefined ? null : formatByteSize({ bytes: props.item.byteSize }, { locale: props.locale ?? 'en-US' });
16
- const uploadDate = formatUploadDate({ createdAt: props.item.createdAt }, { locale: props.locale ?? 'en-US' });
17
62
  return {
18
63
  ...props,
19
64
  busy: props.busy === true,
20
- byteSize, uploadDate,
21
- metadataSeparator: byteSize && uploadDate ? ' · ' : '',
65
+ ...metadata({ item: props.item }, { locale: props.locale }),
22
66
  preview: () => props.onPreview?.(),
23
- canPreview: !!props.onPreview && !!src && stage !== 'unsupported',
24
- imagePreview: !!props.onPreview && !!src && stage === 'image',
25
- handles: { expand: `${handleBase}-expand`, edit: `${handleBase}-edit`, menu: `${handleBase}-menu` },
26
- previewAttrs: { 'data-jini-part': 'media.preview', 'data-agent-element': `${handleBase}-expand`,
27
- 'data-agent-label': 'Open this asset larger in the lightbox', 'aria-label': `View "${props.item.title}" larger` },
28
- editAttrs: { 'data-jini-part': 'media.edit', 'data-agent-element': `${handleBase}-edit`,
29
- 'data-agent-label': 'Open the edit form for this asset as a modal', 'aria-label': `Edit "${props.item.title}"` },
30
- menuLabel: `Actions for "${props.item.title}"`,
31
- menuAttrs: { 'data-agent-element': `${handleBase}-menu`, 'data-agent-label': `Actions for "${props.item.title}"` },
32
- menuItems: [
33
- ...(props.onEdit ? [{ id: 'edit', label: 'Edit metadata', onPress: props.onEdit, disabled: props.busy === true }] : []),
34
- ...(props.onTrash && props.item.status !== 'trashed' ? [{ id: 'trash', label: 'Trash', onPress: props.onTrash, disabled: props.busy === true }] : []),
35
- // Restore belongs only in a trashed card's menu; active cards have nothing to restore.
36
- ...(props.onRestore && props.item.status === 'trashed' ? [{ id: 'restore', label: 'Restore', onPress: props.onRestore, disabled: props.busy === true }] : []),
37
- ...(props.onPurge && props.item.status === 'trashed' ? [{ id: 'purge', label: 'Delete permanently', attrs: { 'data-jini-variant': 'danger' }, onPress: props.onPurge, disabled: props.busy === true }] : []),
38
- ],
67
+ ...previewFlags({ src, stage }, { onPreview: props.onPreview }),
68
+ ...agentAttrs({ handleBase, title: props.item.title }),
69
+ menuItems: menuItems({ props }),
39
70
  src,
40
71
  stage,
41
- image: src && stage === 'image',
42
- video: src && stage === 'video',
43
72
  onImageError: () => setProbe({ src, stage: 'video' }),
44
73
  onVideoError: () => setProbe({ src, stage: 'unsupported' }),
45
74
  alt: props.item.alt || props.item.title || 'Untitled asset',
46
- statusClassName: `jini-status jini-status-${props.item.status}`,
47
- statusLabel: props.item.status,
48
- statusTone: props.item.status === 'trashed' ? 'warning' : 'success',
49
- trashed: props.item.status === 'trashed',
75
+ ...statusView({ item: props.item }),
50
76
  };
51
77
  }
52
78
  //# sourceMappingURL=MediaCard.hooks.js.map
@@ -14,8 +14,8 @@ export declare function useMediaGrid(props: MediaGridProps, _optional?: Record<s
14
14
  locale?: string;
15
15
  busy?: boolean;
16
16
  onPurge?: () => void;
17
- onEdit?: () => void;
18
17
  onPreview?: () => void;
18
+ onEdit?: () => void;
19
19
  onTrash?: () => void;
20
20
  onRestore?: () => void;
21
21
  onChoose?: () => void;