@orthacms/media-admin 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/lib/components/AssetDetailPanel/MetaRow/index.d.ts +11 -0
  2. package/dist/lib/components/AssetDetailPanel/MetaRow/index.d.ts.map +1 -0
  3. package/dist/lib/components/AssetDetailPanel/MetaRow/index.js +9 -0
  4. package/dist/lib/components/AssetDetailPanel/index.d.ts +35 -0
  5. package/dist/lib/components/AssetDetailPanel/index.d.ts.map +1 -0
  6. package/dist/lib/components/AssetDetailPanel/index.js +158 -0
  7. package/dist/lib/components/FolderActionsMenu/index.d.ts +3 -1
  8. package/dist/lib/components/FolderActionsMenu/index.d.ts.map +1 -1
  9. package/dist/lib/components/FolderActionsMenu/index.js +3 -3
  10. package/dist/lib/components/MediaAltDrill/index.d.ts +9 -0
  11. package/dist/lib/components/MediaAltDrill/index.d.ts.map +1 -0
  12. package/dist/lib/components/MediaAltDrill/index.js +60 -0
  13. package/dist/lib/components/MediaAltSignal/index.d.ts +8 -0
  14. package/dist/lib/components/MediaAltSignal/index.d.ts.map +1 -0
  15. package/dist/lib/components/MediaAltSignal/index.js +35 -0
  16. package/dist/lib/components/MediaAssetTable/index.d.ts +25 -0
  17. package/dist/lib/components/MediaAssetTable/index.d.ts.map +1 -0
  18. package/dist/lib/components/MediaAssetTable/index.js +62 -0
  19. package/dist/lib/components/MediaFieldControl/index.d.ts.map +1 -1
  20. package/dist/lib/components/MediaFieldControl/index.js +19 -14
  21. package/dist/lib/components/MediaFieldInput/index.d.ts +19 -0
  22. package/dist/lib/components/MediaFieldInput/index.d.ts.map +1 -0
  23. package/dist/lib/components/MediaFieldInput/index.js +23 -0
  24. package/dist/lib/components/MediaFoldersNav/MediaFolderTreeItem/index.d.ts +17 -3
  25. package/dist/lib/components/MediaFoldersNav/MediaFolderTreeItem/index.d.ts.map +1 -1
  26. package/dist/lib/components/MediaFoldersNav/MediaFolderTreeItem/index.js +16 -6
  27. package/dist/lib/components/MediaFoldersNav/index.d.ts +10 -5
  28. package/dist/lib/components/MediaFoldersNav/index.d.ts.map +1 -1
  29. package/dist/lib/components/MediaFoldersNav/index.js +17 -9
  30. package/dist/lib/components/MediaGrid/MediaAssetCard/index.d.ts +9 -4
  31. package/dist/lib/components/MediaGrid/MediaAssetCard/index.d.ts.map +1 -1
  32. package/dist/lib/components/MediaGrid/MediaAssetCard/index.js +27 -11
  33. package/dist/lib/components/MediaGrid/index.d.ts +10 -10
  34. package/dist/lib/components/MediaGrid/index.d.ts.map +1 -1
  35. package/dist/lib/components/MediaGrid/index.js +10 -15
  36. package/dist/lib/components/MediaSelectionBar/index.d.ts +9 -5
  37. package/dist/lib/components/MediaSelectionBar/index.d.ts.map +1 -1
  38. package/dist/lib/components/MediaSelectionBar/index.js +16 -10
  39. package/dist/lib/components/MediaStorageWidget/index.js +2 -2
  40. package/dist/lib/components/MediaThumbnail/index.d.ts +9 -6
  41. package/dist/lib/components/MediaThumbnail/index.d.ts.map +1 -1
  42. package/dist/lib/components/MediaThumbnail/index.js +7 -6
  43. package/dist/lib/components/MediaToolbar/index.d.ts +10 -8
  44. package/dist/lib/components/MediaToolbar/index.d.ts.map +1 -1
  45. package/dist/lib/components/MediaToolbar/index.js +37 -13
  46. package/dist/lib/components/MediaTopBar/index.d.ts +7 -1
  47. package/dist/lib/components/MediaTopBar/index.d.ts.map +1 -1
  48. package/dist/lib/components/MediaTopBar/index.js +6 -3
  49. package/dist/lib/components/MediaUploadBanner/index.d.ts +7 -5
  50. package/dist/lib/components/MediaUploadBanner/index.d.ts.map +1 -1
  51. package/dist/lib/components/MediaUploadBanner/index.js +15 -8
  52. package/dist/lib/constants/index.d.ts +13 -0
  53. package/dist/lib/constants/index.d.ts.map +1 -1
  54. package/dist/lib/constants/index.js +11 -0
  55. package/dist/lib/hooks/useMediaInsights/index.d.ts +8 -0
  56. package/dist/lib/hooks/useMediaInsights/index.d.ts.map +1 -1
  57. package/dist/lib/hooks/useMediaInsights/index.js +20 -1
  58. package/dist/lib/infrastructure/httpMediaInsightsGateway/index.d.ts.map +1 -1
  59. package/dist/lib/infrastructure/httpMediaInsightsGateway/index.js +9 -0
  60. package/dist/lib/infrastructure/mediaInsightsGateway/index.d.ts +17 -0
  61. package/dist/lib/infrastructure/mediaInsightsGateway/index.d.ts.map +1 -1
  62. package/dist/lib/pages/MediaLibraryPage/index.d.ts +6 -4
  63. package/dist/lib/pages/MediaLibraryPage/index.d.ts.map +1 -1
  64. package/dist/lib/pages/MediaLibraryPage/index.js +82 -36
  65. package/dist/lib/utils/fileExtension/index.d.ts +9 -0
  66. package/dist/lib/utils/fileExtension/index.d.ts.map +1 -0
  67. package/dist/lib/utils/fileExtension/index.js +14 -0
  68. package/dist/lib/utils/mediaPlugin/index.d.ts.map +1 -1
  69. package/dist/lib/utils/mediaPlugin/index.js +35 -32
  70. package/package.json +10 -10
@@ -1,8 +1,8 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { defineMessages, useIntl } from 'react-intl';
3
- import { Button, DensityProvider, InputGroup, InputGroupAddon, InputGroupInput, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@orthacms/design-system';
4
- import { FolderPlus, Search, UploadCloud } from 'lucide-react';
5
- import { KIND_FILTER_ALL, MEDIA_KIND, MEDIA_SORT } from '../../constants';
3
+ import { DensityProvider, InputGroup, InputGroupAddon, InputGroupInput, SegmentedControl, SegmentedControlItem, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, cn } from '@orthacms/design-system';
4
+ import { LayoutGrid, List, Search } from 'lucide-react';
5
+ import { KIND_FILTER_ALL, MEDIA_KIND, MEDIA_SORT, MEDIA_VIEW } from '../../constants';
6
6
  /** Intl descriptors for {@link MediaToolbar}, co-located. */
7
7
  const messages = defineMessages({
8
8
  search: { id: 'media.toolbar.search', defaultMessage: 'Search assets' },
@@ -14,9 +14,9 @@ const messages = defineMessages({
14
14
  id: 'media.toolbar.kindLabel',
15
15
  defaultMessage: 'Filter by type'
16
16
  },
17
- kindAll: { id: 'media.toolbar.kindAll', defaultMessage: 'All types' },
17
+ kindAll: { id: 'media.toolbar.kindAll', defaultMessage: 'All' },
18
18
  kindImage: { id: 'media.toolbar.kindImage', defaultMessage: 'Images' },
19
- kindVideo: { id: 'media.toolbar.kindVideo', defaultMessage: 'Videos' },
19
+ kindVideo: { id: 'media.toolbar.kindVideo', defaultMessage: 'Video' },
20
20
  kindAudio: { id: 'media.toolbar.kindAudio', defaultMessage: 'Audio' },
21
21
  kindDocument: {
22
22
  id: 'media.toolbar.kindDocument',
@@ -48,15 +48,39 @@ const messages = defineMessages({
48
48
  id: 'media.toolbar.sortSmallest',
49
49
  defaultMessage: 'Smallest'
50
50
  },
51
- newFolder: { id: 'media.toolbar.newFolder', defaultMessage: 'New folder' },
52
- upload: { id: 'media.toolbar.upload', defaultMessage: 'Upload' }
51
+ viewLabel: { id: 'media.toolbar.viewLabel', defaultMessage: 'View' },
52
+ viewGrid: { id: 'media.toolbar.viewGrid', defaultMessage: 'Grid view' },
53
+ viewList: { id: 'media.toolbar.viewList', defaultMessage: 'List view' }
53
54
  });
55
+ /** The type chips, in reading order — "All" first, the kinds after it. */
56
+ const KIND_CHIPS = [
57
+ { value: KIND_FILTER_ALL, label: 'kindAll' },
58
+ { value: MEDIA_KIND.Image, label: 'kindImage' },
59
+ { value: MEDIA_KIND.Video, label: 'kindVideo' },
60
+ { value: MEDIA_KIND.Audio, label: 'kindAudio' },
61
+ { value: MEDIA_KIND.Document, label: 'kindDocument' },
62
+ { value: MEDIA_KIND.Archive, label: 'kindArchive' }
63
+ ];
54
64
  /**
55
- * The Media Library's control bar — a search box, a type filter, a sort select,
56
- * and (permission-gated) New folder + Upload actions. Fully controlled: every
57
- * value lives in the page's store and changes dispatch straight back up.
65
+ * The Media Library's control bar — the **type chips** (All · Images · Video ·
66
+ * Audio · Documents · Archives, one pressed at a time; the same pill the
67
+ * records table's status chips are) on the left, then a search box, a sort
68
+ * select and the Grid / List switch on the right. Fully controlled: every value
69
+ * lives in the page and changes dispatch straight back up. New folder and
70
+ * Upload are the page's top-bar actions, not this bar's.
58
71
  */
59
- export function MediaToolbar({ search, onSearchChange, kindFilter, onKindFilterChange, sort, onSortChange, onNewFolder, onUpload, canCreate }) {
72
+ export function MediaToolbar({ search, onSearchChange, kindFilter, onKindFilterChange, sort, onSortChange, view, onViewChange }) {
60
73
  const intl = useIntl();
61
- return (_jsx(DensityProvider, { density: "compact", children: _jsxs("div", { className: "mb-4 flex flex-wrap items-center gap-2", children: [_jsxs(InputGroup, { className: "w-full shadow-none sm:w-auto sm:max-w-[280px] sm:flex-1", children: [_jsx(InputGroupAddon, { children: _jsx(Search, {}) }), _jsx(InputGroupInput, { type: "search", value: search, onChange: (event) => onSearchChange(event.target.value), "aria-label": intl.formatMessage(messages.search), placeholder: intl.formatMessage(messages.searchPlaceholder) })] }), _jsxs(Select, { value: kindFilter, onValueChange: (value) => onKindFilterChange(value), children: [_jsx(SelectTrigger, { className: "w-[150px] shadow-none", "aria-label": intl.formatMessage(messages.kindLabel), children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: KIND_FILTER_ALL, children: intl.formatMessage(messages.kindAll) }), _jsx(SelectItem, { value: MEDIA_KIND.Image, children: intl.formatMessage(messages.kindImage) }), _jsx(SelectItem, { value: MEDIA_KIND.Video, children: intl.formatMessage(messages.kindVideo) }), _jsx(SelectItem, { value: MEDIA_KIND.Audio, children: intl.formatMessage(messages.kindAudio) }), _jsx(SelectItem, { value: MEDIA_KIND.Document, children: intl.formatMessage(messages.kindDocument) }), _jsx(SelectItem, { value: MEDIA_KIND.Archive, children: intl.formatMessage(messages.kindArchive) })] })] }), _jsxs(Select, { value: sort, onValueChange: (value) => onSortChange(value), children: [_jsx(SelectTrigger, { className: "w-[150px] shadow-none", "aria-label": intl.formatMessage(messages.sortLabel), children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: MEDIA_SORT.Newest, children: intl.formatMessage(messages.sortNewest) }), _jsx(SelectItem, { value: MEDIA_SORT.Oldest, children: intl.formatMessage(messages.sortOldest) }), _jsx(SelectItem, { value: MEDIA_SORT.NameAsc, children: intl.formatMessage(messages.sortNameAsc) }), _jsx(SelectItem, { value: MEDIA_SORT.NameDesc, children: intl.formatMessage(messages.sortNameDesc) }), _jsx(SelectItem, { value: MEDIA_SORT.Largest, children: intl.formatMessage(messages.sortLargest) }), _jsx(SelectItem, { value: MEDIA_SORT.Smallest, children: intl.formatMessage(messages.sortSmallest) })] })] }), _jsx("div", { className: "ml-auto flex items-center gap-2 sm:gap-3", children: canCreate ? (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "outline", className: "shadow-none", onClick: onNewFolder, children: [_jsx(FolderPlus, { "aria-hidden": true }), _jsx("span", { className: "hidden sm:inline", children: intl.formatMessage(messages.newFolder) })] }), _jsxs(Button, { onClick: onUpload, children: [_jsx(UploadCloud, { "aria-hidden": true }), intl.formatMessage(messages.upload)] })] })) : null })] }) }));
74
+ return (_jsx(DensityProvider, { density: "compact", children: _jsxs("div", { className: "mb-4 flex flex-wrap items-center gap-2", children: [_jsx("div", { role: "group", "aria-label": intl.formatMessage(messages.kindLabel), className: "flex flex-wrap items-center gap-1.5", children: KIND_CHIPS.map(({ value, label }) => {
75
+ const on = kindFilter === value;
76
+ return (_jsx("button", { type: "button", "aria-pressed": on, onClick: () => onKindFilterChange(value), className: cn('inline-flex h-7 items-center rounded-full border px-2.5 text-xs font-medium outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring', on
77
+ ? 'border-foreground bg-foreground text-background'
78
+ : 'bg-background text-foreground hover:bg-muted'), children: intl.formatMessage(messages[label]) }, value));
79
+ }) }), _jsxs("div", { className: "flex w-full flex-wrap items-center gap-2 sm:ml-auto sm:w-auto", children: [_jsxs(InputGroup, { className: "w-full shadow-none sm:w-[200px]", children: [_jsx(InputGroupAddon, { children: _jsx(Search, {}) }), _jsx(InputGroupInput, { type: "search", value: search, onChange: (event) => onSearchChange(event.target.value), "aria-label": intl.formatMessage(messages.search), placeholder: intl.formatMessage(messages.searchPlaceholder) })] }), _jsxs(Select, { value: sort, onValueChange: (value) => onSortChange(value), children: [_jsx(SelectTrigger, { className: "w-[132px] shadow-none", "aria-label": intl.formatMessage(messages.sortLabel), children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: MEDIA_SORT.Newest, children: intl.formatMessage(messages.sortNewest) }), _jsx(SelectItem, { value: MEDIA_SORT.Oldest, children: intl.formatMessage(messages.sortOldest) }), _jsx(SelectItem, { value: MEDIA_SORT.NameAsc, children: intl.formatMessage(messages.sortNameAsc) }), _jsx(SelectItem, { value: MEDIA_SORT.NameDesc, children: intl.formatMessage(messages.sortNameDesc) }), _jsx(SelectItem, { value: MEDIA_SORT.Largest, children: intl.formatMessage(messages.sortLargest) }), _jsx(SelectItem, { value: MEDIA_SORT.Smallest, children: intl.formatMessage(messages.sortSmallest) })] })] }), _jsxs(SegmentedControl, { variant: "muted", value: view,
80
+ // A single toggle group reports '' when its pressed
81
+ // item is pressed again; a view switch can't be off.
82
+ onValueChange: (value) => {
83
+ if (value)
84
+ onViewChange(value);
85
+ }, "aria-label": intl.formatMessage(messages.viewLabel), children: [_jsx(SegmentedControlItem, { value: MEDIA_VIEW.Grid, "aria-label": intl.formatMessage(messages.viewGrid), className: "px-2", children: _jsx(LayoutGrid, { className: "size-4", "aria-hidden": true }) }), _jsx(SegmentedControlItem, { value: MEDIA_VIEW.List, "aria-label": intl.formatMessage(messages.viewList), className: "px-2", children: _jsx(List, { className: "size-4", "aria-hidden": true }) })] })] })] }) }));
62
86
  }
@@ -1,3 +1,4 @@
1
+ import type { ReactNode } from 'react';
1
2
  import type { MediaFolder } from '../../types/mediaFolder';
2
3
  /**
3
4
  * The page top bar over the Media Library — the teal `Image` tile (matching the
@@ -11,10 +12,15 @@ import type { MediaFolder } from '../../types/mediaFolder';
11
12
  * the store's `breadcrumbs` and each one dispatches `onNavigate` rather than
12
13
  * linking. They are still `BreadcrumbLink`s over real `<button>`s — a control
13
14
  * that changes view without changing location is a button, not a link.
15
+ *
16
+ * `actions` (New folder, Upload) sit at the bar's right end, where every other
17
+ * admin page keeps its page-level actions.
14
18
  */
15
- export declare function MediaTopBar({ breadcrumbs, onNavigate }: {
19
+ export declare function MediaTopBar({ breadcrumbs, onNavigate, actions }: {
16
20
  /** Ancestor chain of the open folder, root-first (excludes the root itself). */
17
21
  breadcrumbs: MediaFolder[];
18
22
  onNavigate: (folderId: string) => void;
23
+ /** Page-level actions, drawn at the bar's right end. */
24
+ actions?: ReactNode;
19
25
  }): import("react").JSX.Element;
20
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/MediaTopBar/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAQ3D;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,EACxB,WAAW,EACX,UAAU,EACb,EAAE;IACC,gFAAgF;IAChF,WAAW,EAAE,WAAW,EAAE,CAAC;IAC3B,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C,+BA4CA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/MediaTopBar/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAevC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAQ3D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,EACxB,WAAW,EACX,UAAU,EACV,OAAO,EACV,EAAE;IACC,gFAAgF;IAChF,WAAW,EAAE,WAAW,EAAE,CAAC;IAC3B,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,wDAAwD;IACxD,OAAO,CAAC,EAAE,SAAS,CAAC;CACvB,+BA6CA"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { defineMessages, useIntl } from 'react-intl';
3
3
  import { Image } from 'lucide-react';
4
- import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, TopBar, TopBarIcon } from '@orthacms/design-system';
4
+ import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, TopBar, TopBarActions, TopBarIcon } from '@orthacms/design-system';
5
5
  import { ROOT_FOLDER_ID } from '../../constants';
6
6
  /** Intl descriptors for {@link MediaTopBar}, co-located. */
7
7
  const messages = defineMessages({
@@ -20,8 +20,11 @@ const messages = defineMessages({
20
20
  * the store's `breadcrumbs` and each one dispatches `onNavigate` rather than
21
21
  * linking. They are still `BreadcrumbLink`s over real `<button>`s — a control
22
22
  * that changes view without changing location is a button, not a link.
23
+ *
24
+ * `actions` (New folder, Upload) sit at the bar's right end, where every other
25
+ * admin page keeps its page-level actions.
23
26
  */
24
- export function MediaTopBar({ breadcrumbs, onNavigate }) {
27
+ export function MediaTopBar({ breadcrumbs, onNavigate, actions }) {
25
28
  const intl = useIntl();
26
29
  const crumbs = [
27
30
  { id: ROOT_FOLDER_ID, name: intl.formatMessage(messages.root) },
@@ -31,5 +34,5 @@ export function MediaTopBar({ breadcrumbs, onNavigate }) {
31
34
  return (_jsxs(TopBar, { children: [_jsx(TopBarIcon, { className: "bg-teal-soft text-teal-soft-foreground", children: _jsx(Image, {}) }), _jsx(Breadcrumb, { "aria-label": intl.formatMessage(messages.nav), children: _jsx(BreadcrumbList, { className: "flex-nowrap font-medium", children: crumbs.map((crumb, index) => [
32
35
  index > 0 ? (_jsx(BreadcrumbSeparator, {}, `${crumb.id}-sep`)) : null,
33
36
  _jsx(BreadcrumbItem, { className: "min-w-0 whitespace-nowrap", children: index === last ? (_jsx(BreadcrumbPage, { className: "flex min-w-0 items-center font-medium", children: crumb.name })) : (_jsx(BreadcrumbLink, { asChild: true, children: _jsx("button", { type: "button", onClick: () => onNavigate(crumb.id), children: crumb.name }) })) }, crumb.id)
34
- ]) }) })] }));
37
+ ]) }) }), actions ? _jsx(TopBarActions, { children: actions }) : null] }));
35
38
  }
@@ -1,10 +1,12 @@
1
1
  import type { UploadItem, UploadSummary } from '../../types/upload';
2
2
  /**
3
- * The upload progress banner — the batch headline ("Uploading 3 of 5 · 47%"), an
4
- * overall size-weighted bar while anything is running, and one
5
- * {@link MediaUploadRow} per file. It sits above the browser rather than inside
6
- * the upload dialog, so the dialog can close on submit and the user keeps
7
- * browsing (and can queue more) while bytes move.
3
+ * The upload tray — a small card pinned to the **bottom-right** of the window
4
+ * with the batch headline ("Uploading 3 of 5 · 47%"), an overall size-weighted
5
+ * bar while anything is running, and one {@link MediaUploadRow} per file (the
6
+ * chevron folds the list away, leaving the headline). It floats outside the
7
+ * upload dialog and over the browser rather than pushing the grid down, so the
8
+ * dialog can close on submit and the user keeps browsing, selecting and
9
+ * queueing more while bytes move.
8
10
  *
9
11
  * `role="status"` sits on the **headline alone**, not the whole banner: a polite
10
12
  * live region re-announces its entire contents on every change, so wrapping the
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/MediaUploadBanner/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAqCpE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,EAC9B,KAAK,EACL,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACZ,EAAE;IACC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,SAAS,EAAE,MAAM,IAAI,CAAC;CACzB,sCAwGA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/MediaUploadBanner/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAyCpE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,EAC9B,KAAK,EACL,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACZ,EAAE;IACC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,SAAS,EAAE,MAAM,IAAI,CAAC;CACzB,sCA8HA"}
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from 'react';
3
3
  import { defineMessages, useIntl } from 'react-intl';
4
- import { Button, Progress } from '@orthacms/design-system';
5
- import { UploadCloud, X } from 'lucide-react';
4
+ import { Button, Progress, cn } from '@orthacms/design-system';
5
+ import { ChevronDown, UploadCloud, X } from 'lucide-react';
6
6
  import { UPLOAD_STATUS } from '../../constants';
7
7
  import { MediaUploadRow } from './MediaUploadRow';
8
8
  /** Intl descriptors for {@link MediaUploadBanner}, co-located. */
@@ -29,6 +29,10 @@ const messages = defineMessages({
29
29
  defaultMessage: 'Overall upload progress'
30
30
  },
31
31
  dismiss: { id: 'media.uploadBanner.dismiss', defaultMessage: 'Dismiss' },
32
+ collapse: {
33
+ id: 'media.uploadBanner.collapse',
34
+ defaultMessage: 'Show the files'
35
+ },
32
36
  /** Announced when one file in a batch fails — the row itself is silent. */
33
37
  failureAnnouncement: {
34
38
  id: 'media.uploadBanner.failureAnnouncement',
@@ -36,11 +40,13 @@ const messages = defineMessages({
36
40
  }
37
41
  });
38
42
  /**
39
- * The upload progress banner — the batch headline ("Uploading 3 of 5 · 47%"), an
40
- * overall size-weighted bar while anything is running, and one
41
- * {@link MediaUploadRow} per file. It sits above the browser rather than inside
42
- * the upload dialog, so the dialog can close on submit and the user keeps
43
- * browsing (and can queue more) while bytes move.
43
+ * The upload tray — a small card pinned to the **bottom-right** of the window
44
+ * with the batch headline ("Uploading 3 of 5 · 47%"), an overall size-weighted
45
+ * bar while anything is running, and one {@link MediaUploadRow} per file (the
46
+ * chevron folds the list away, leaving the headline). It floats outside the
47
+ * upload dialog and over the browser rather than pushing the grid down, so the
48
+ * dialog can close on submit and the user keeps browsing, selecting and
49
+ * queueing more while bytes move.
44
50
  *
45
51
  * `role="status"` sits on the **headline alone**, not the whole banner: a polite
46
52
  * live region re-announces its entire contents on every change, so wrapping the
@@ -52,6 +58,7 @@ const messages = defineMessages({
52
58
  */
53
59
  export function MediaUploadBanner({ items, summary, onRetry, onCancel, onDismiss }) {
54
60
  const intl = useIntl();
61
+ const [open, setOpen] = useState(true);
55
62
  // Failures are announced from their own region. The headline's counts move
56
63
  // when a file fails, but "3 of 5" never says *which* file or why — and the
57
64
  // rows carrying that sentence sit outside the live region on purpose (a
@@ -95,5 +102,5 @@ export function MediaUploadBanner({ items, summary, onRetry, onCancel, onDismiss
95
102
  : summary.failed > 0
96
103
  ? intl.formatMessage(messages.allFailed, { failed: summary.failed })
97
104
  : intl.formatMessage(messages.complete, { count: summary.done });
98
- return (_jsxs("section", { "aria-label": intl.formatMessage(messages.region), className: "mb-4 rounded-xl border bg-muted/40 px-4 py-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(UploadCloud, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": true }), _jsx("p", { className: "min-w-0 flex-1 text-sm font-medium", role: "status", children: headline }), summary.active ? null : (_jsx(Button, { variant: "ghost", size: "icon", className: "size-7 shrink-0 shadow-none", "aria-label": intl.formatMessage(messages.dismiss), onClick: onDismiss, children: _jsx(X, { "aria-hidden": true }) }))] }), summary.active ? (_jsx(Progress, { value: summary.percent, className: "mt-2 h-1.5", "aria-label": intl.formatMessage(messages.overall) })) : null, _jsx("p", { className: "sr-only", role: "status", children: failureNotice }), _jsx("ul", { className: "mt-1 max-h-48 divide-y overflow-auto", children: items.map((item) => (_jsx(MediaUploadRow, { item: item, onRetry: onRetry, onCancel: onCancel }, item.id))) })] }));
105
+ return (_jsxs("section", { "aria-label": intl.formatMessage(messages.region), className: "fixed bottom-4 right-4 z-30 w-80 max-w-[calc(100vw-2rem)] rounded-xl border bg-popover px-4 py-3 text-popover-foreground shadow-lg", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(UploadCloud, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": true }), _jsx("p", { className: "min-w-0 flex-1 text-sm font-medium", role: "status", children: headline }), _jsx(Button, { variant: "ghost", size: "icon", className: "size-7 shrink-0 shadow-none", "aria-label": intl.formatMessage(messages.collapse), "aria-expanded": open, onClick: () => setOpen((value) => !value), children: _jsx(ChevronDown, { "aria-hidden": true, className: cn('transition-transform duration-(--ds-duration-base) motion-reduce:transition-none', !open && 'rotate-180') }) }), summary.active ? null : (_jsx(Button, { variant: "ghost", size: "icon", className: "size-7 shrink-0 shadow-none", "aria-label": intl.formatMessage(messages.dismiss), onClick: onDismiss, children: _jsx(X, { "aria-hidden": true }) }))] }), summary.active ? (_jsx(Progress, { value: summary.percent, className: "mt-2 h-1.5", "aria-label": intl.formatMessage(messages.overall) })) : null, _jsx("p", { className: "sr-only", role: "status", children: failureNotice }), _jsx("ul", { className: cn('mt-1 max-h-48 divide-y overflow-auto', !open && 'hidden'), children: items.map((item) => (_jsx(MediaUploadRow, { item: item, onRetry: onRetry, onCancel: onCancel }, item.id))) })] }));
99
106
  }
@@ -92,4 +92,17 @@ export declare const MEDIA_FOLDERS_PANEL: {
92
92
  readonly maxViewportRatio: 0.2;
93
93
  readonly cssVar: "--media-folders-width";
94
94
  };
95
+ /**
96
+ * How the library draws the open folder's files: tiles, or the design-system
97
+ * table. Remembered per browser under {@link MEDIA_VIEW_STORAGE_KEY} — a
98
+ * reading preference, not shared state.
99
+ */
100
+ export declare const MEDIA_VIEW: {
101
+ readonly Grid: "grid";
102
+ readonly List: "list";
103
+ };
104
+ /** One way of drawing the files. */
105
+ export type MediaView = (typeof MEDIA_VIEW)[keyof typeof MEDIA_VIEW];
106
+ /** Where the chosen {@link MediaView} is kept (`localStorage`). */
107
+ export declare const MEDIA_VIEW_STORAGE_KEY = "orthacms.media.view";
95
108
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/constants/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,+EAA+E;AAC/E,eAAO,MAAM,UAAU,eAAe,CAAC;AAEvC,qDAAqD;AACrD,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAE3C,6DAA6D;AAC7D,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAE3C,wDAAwD;AACxD,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAE3C;;;;GAIG;AACH,eAAO,MAAM,aAAa,UAAU,CAAC;AAErC,4EAA4E;AAC5E,eAAO,MAAM,cAAc,SAAS,CAAC;AAErC,0CAA0C;AAC1C,eAAO,MAAM,aAAa;IACtB,8CAA8C;;IAE9C,6BAA6B;;;;IAI7B,8CAA8C;;CAExC,CAAC;AAEX,iCAAiC;AACjC,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,4EAA4E;AAC5E,eAAO,MAAM,UAAU;;;;;;CAMb,CAAC;AAEX,iCAAiC;AACjC,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC;AAErE,oEAAoE;AACpE,eAAO,MAAM,eAAe,QAAQ,CAAC;AAErC;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,iEAAiE;AACjE,eAAO,MAAM,wBAAwB,UAAe,CAAC;AAErD;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C,yEAAyE;AACzE,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,0CAA0C;AAC1C,eAAO,MAAM,UAAU;;;;;;;CAOb,CAAC;AAEX,iCAAiC;AACjC,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC;AAErE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB;;;;;;;CAOtB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/constants/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,+EAA+E;AAC/E,eAAO,MAAM,UAAU,eAAe,CAAC;AAEvC,qDAAqD;AACrD,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAE3C,6DAA6D;AAC7D,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAE3C,wDAAwD;AACxD,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAE3C;;;;GAIG;AACH,eAAO,MAAM,aAAa,UAAU,CAAC;AAErC,4EAA4E;AAC5E,eAAO,MAAM,cAAc,SAAS,CAAC;AAErC,0CAA0C;AAC1C,eAAO,MAAM,aAAa;IACtB,8CAA8C;;IAE9C,6BAA6B;;;;IAI7B,8CAA8C;;CAExC,CAAC;AAEX,iCAAiC;AACjC,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,4EAA4E;AAC5E,eAAO,MAAM,UAAU;;;;;;CAMb,CAAC;AAEX,iCAAiC;AACjC,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC;AAErE,oEAAoE;AACpE,eAAO,MAAM,eAAe,QAAQ,CAAC;AAErC;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,iEAAiE;AACjE,eAAO,MAAM,wBAAwB,UAAe,CAAC;AAErD;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C,yEAAyE;AACzE,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,0CAA0C;AAC1C,eAAO,MAAM,UAAU;;;;;;;CAOb,CAAC;AAEX,iCAAiC;AACjC,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC;AAErE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB;;;;;;;CAOtB,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,UAAU;;;CAGb,CAAC;AAEX,oCAAoC;AACpC,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC;AAErE,mEAAmE;AACnE,eAAO,MAAM,sBAAsB,wBAAwB,CAAC"}
@@ -86,3 +86,14 @@ export const MEDIA_FOLDERS_PANEL = {
86
86
  maxViewportRatio: 0.2,
87
87
  cssVar: '--media-folders-width'
88
88
  };
89
+ /**
90
+ * How the library draws the open folder's files: tiles, or the design-system
91
+ * table. Remembered per browser under {@link MEDIA_VIEW_STORAGE_KEY} — a
92
+ * reading preference, not shared state.
93
+ */
94
+ export const MEDIA_VIEW = {
95
+ Grid: 'grid',
96
+ List: 'list'
97
+ };
98
+ /** Where the chosen {@link MediaView} is kept (`localStorage`). */
99
+ export const MEDIA_VIEW_STORAGE_KEY = 'orthacms.media.view';
@@ -11,6 +11,7 @@ export declare const mediaInsightsKeys: {
11
11
  storage: (workspaceId: string) => readonly ["media-insights", string, "storage"];
12
12
  uploads: (workspaceId: string, days: number) => readonly ["media-insights", string, "uploads", number];
13
13
  alt: (workspaceId: string) => readonly ["media-insights", string, "alt"];
14
+ altMissing: (workspaceId: string) => readonly ["media-insights", string, "alt-missing"];
14
15
  };
15
16
  /**
16
17
  * Loads assets and bytes per media kind.
@@ -29,4 +30,11 @@ export declare function useMediaUploads(): import("@tanstack/react-query").UseQu
29
30
  * whenever someone narrowed the range.
30
31
  */
31
32
  export declare function useMediaAltCoverage(): import("@tanstack/react-query").UseQueryResult<NoInfer<import("../../infrastructure/mediaInsightsGateway").MediaAltCoverage>, Error>;
33
+ /** How many images the alt-text drill-down lists. */
34
+ export declare const ALT_DRILL_LIMIT = 20;
35
+ /**
36
+ * Loads the images missing alt text, oldest upload first — the drill-down
37
+ * behind the Needs-attention line. Mounted only while its panel is open.
38
+ */
39
+ export declare function useMediaAltMissing(): import("@tanstack/react-query").UseQueryResult<NoInfer<import("../../infrastructure/mediaInsightsGateway").MediaAltMissing>, Error>;
32
40
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/hooks/useMediaInsights/index.ts"],"names":[],"mappings":"AAUA;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB;uBACP,MAAM;2BACF,MAAM;2BAEN,MAAM,QAAQ,MAAM;uBAExB,MAAM;CAE5B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,eAAe,qIAgB9B;AAED,uEAAuE;AACvE,wBAAgB,eAAe,qIAiB9B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,yIAgBlC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/hooks/useMediaInsights/index.ts"],"names":[],"mappings":"AAUA;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB;uBACP,MAAM;2BACF,MAAM;2BAEN,MAAM,QAAQ,MAAM;uBAExB,MAAM;8BAEC,MAAM;CAEnC,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,eAAe,qIAgB9B;AAED,uEAAuE;AACvE,wBAAgB,eAAe,qIAiB9B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,yIAgBlC;AAED,qDAAqD;AACrD,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC;;;GAGG;AACH,wBAAgB,kBAAkB,wIAYjC"}
@@ -18,7 +18,8 @@ export const mediaInsightsKeys = {
18
18
  all: (workspaceId) => ['media-insights', workspaceId],
19
19
  storage: (workspaceId) => ['media-insights', workspaceId, 'storage'],
20
20
  uploads: (workspaceId, days) => ['media-insights', workspaceId, 'uploads', days],
21
- alt: (workspaceId) => ['media-insights', workspaceId, 'alt']
21
+ alt: (workspaceId) => ['media-insights', workspaceId, 'alt'],
22
+ altMissing: (workspaceId) => ['media-insights', workspaceId, 'alt-missing']
22
23
  };
23
24
  /**
24
25
  * Loads assets and bytes per media kind.
@@ -83,3 +84,21 @@ export function useMediaAltCoverage() {
83
84
  enabled: canRead
84
85
  });
85
86
  }
87
+ /** How many images the alt-text drill-down lists. */
88
+ export const ALT_DRILL_LIMIT = 20;
89
+ /**
90
+ * Loads the images missing alt text, oldest upload first — the drill-down
91
+ * behind the Needs-attention line. Mounted only while its panel is open.
92
+ */
93
+ export function useMediaAltMissing() {
94
+ const workspace = useCurrentWorkspace();
95
+ const canRead = useHasPermission(MEDIA_READ);
96
+ return useQuery({
97
+ queryKey: mediaInsightsKeys.altMissing(workspace.id),
98
+ queryFn: () => httpMediaInsightsGateway.altMissing(ALT_DRILL_LIMIT),
99
+ staleTime: STALE_TIME.Standard,
100
+ // One retry, as every Insights read (`[insights:I-16]`).
101
+ retry: 1,
102
+ enabled: canRead
103
+ });
104
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/infrastructure/httpMediaInsightsGateway/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAER,oBAAoB,EAGvB,MAAM,yBAAyB,CAAC;AAEjC;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,oBAkCtC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/infrastructure/httpMediaInsightsGateway/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGR,oBAAoB,EAGvB,MAAM,yBAAyB,CAAC;AAEjC;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,oBA8CtC,CAAC"}
@@ -31,5 +31,14 @@ export const httpMediaInsightsGateway = {
31
31
  catch (error) {
32
32
  throw toApiError(error);
33
33
  }
34
+ },
35
+ async altMissing(limit) {
36
+ try {
37
+ const { data } = await apiClient.get('/insights/media/alt/missing', { params: { limit } });
38
+ return data;
39
+ }
40
+ catch (error) {
41
+ throw toApiError(error);
42
+ }
34
43
  }
35
44
  };
@@ -35,6 +35,21 @@ export type MediaAltCoverage = {
35
35
  * The port over the media Insights endpoints — one method per widget, mirroring
36
36
  * one route per widget. {@link httpMediaInsightsGateway} is the implementation.
37
37
  */
38
+ /** One image with no alt text. */
39
+ export type MediaAltMissingAsset = {
40
+ id: string;
41
+ name: string;
42
+ folderId: string | null;
43
+ /** Who uploaded it — resolved against the workspace roster. */
44
+ uploadedBy: string;
45
+ /** ISO upload timestamp. */
46
+ createdAt: string;
47
+ };
48
+ /** The images behind the "missing alt text" figure, oldest upload first. */
49
+ export type MediaAltMissing = {
50
+ total: number;
51
+ assets: MediaAltMissingAsset[];
52
+ };
38
53
  export type MediaInsightsGateway = {
39
54
  /** `GET /insights/media/storage` — assets and bytes per kind. */
40
55
  storage(): Promise<MediaStorage>;
@@ -42,5 +57,7 @@ export type MediaInsightsGateway = {
42
57
  uploads(days: number): Promise<MediaUploads>;
43
58
  /** `GET /insights/media/alt` — alt-text coverage across images. */
44
59
  altCoverage(): Promise<MediaAltCoverage>;
60
+ /** `GET /insights/media/alt/missing` — the images, oldest upload first. */
61
+ altMissing(limit: number): Promise<MediaAltMissing>;
45
62
  };
46
63
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/infrastructure/mediaInsightsGateway/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,sCAAsC;AACtC,MAAM,MAAM,cAAc,GAAG;IACzB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,qDAAqD;AACrD,MAAM,MAAM,YAAY,GAAG;IACvB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,uCAAuC;AACvC,MAAM,MAAM,YAAY,GAAG;IACvB,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,WAAW,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,uDAAuD;AACvD,MAAM,MAAM,gBAAgB,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG;IAC/B,iEAAiE;IACjE,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC,CAAC;IACjC,+DAA+D;IAC/D,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7C,mEAAmE;IACnE,WAAW,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAC5C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/infrastructure/mediaInsightsGateway/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,sCAAsC;AACtC,MAAM,MAAM,cAAc,GAAG;IACzB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,qDAAqD;AACrD,MAAM,MAAM,YAAY,GAAG;IACvB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,uCAAuC;AACvC,MAAM,MAAM,YAAY,GAAG;IACvB,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,WAAW,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,uDAAuD;AACvD,MAAM,MAAM,gBAAgB,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;GAGG;AACH,kCAAkC;AAClC,MAAM,MAAM,oBAAoB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,SAAS,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,eAAe,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAC/B,iEAAiE;IACjE,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC,CAAC;IACjC,+DAA+D;IAC/D,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7C,mEAAmE;IACnE,WAAW,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzC,2EAA2E;IAC3E,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CACvD,CAAC"}
@@ -3,14 +3,16 @@
3
3
  * `/workspaces/:id/media`. `useMediaLibrary` is the store — folders and the open
4
4
  * folder's assets come from `@orthacms/media-server`, and every action (browse
5
5
  * folders, upload, create folder, rename, duplicate, move, delete, filter, sort,
6
- * open the detail drawer) runs against the API, firing a toast per action —
6
+ * open the details panel) runs against the API, firing a toast per action —
7
7
  * except uploads, whose status lives in the `MediaUploadBanner`. Controls gate
8
8
  * on the real `media:*` matrix via `useHasPermission`.
9
9
  *
10
10
  * A sticky `MediaTopBar` heads the page (the shared icon-tile + breadcrumb
11
- * header every admin page carries), spanning both panes. Below it sits a
12
- * two-pane layout — a folders sidebar beside the asset browser — plus the detail
13
- * drawer and the create/rename/move/upload dialogs. Both panes sit
11
+ * header every admin page carries, with New folder / Upload at its right end),
12
+ * spanning every pane. Below it: the folder tree, the file browser (title, type
13
+ * chips + search + sort + Grid/List, the selection bar, then tiles or the
14
+ * table) and — while a file is open — the non-modal details panel as a third
15
+ * column; plus the create/rename/move/upload dialogs. The panes sit
14
16
  * **flush on the canvas** (no muted board, no bordered island), mirroring the
15
17
  * Content Library's work area: the sidebar is divided by a hairline `border-r`
16
18
  * and each pane scrolls independently. Sized with `flex-1` against the workspace
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/pages/MediaLibraryPage/index.tsx"],"names":[],"mappings":"AA+KA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,gCAwlB/B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/pages/MediaLibraryPage/index.tsx"],"names":[],"mappings":"AA8LA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,gCAorB/B"}