@codingfactory/mediables-vue 2.4.23 → 2.6.1

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 (58) hide show
  1. package/README.md +7 -1
  2. package/dist/{PixiFrameExporter-DmyELJBR.cjs → PixiFrameExporter-CetwQ3WI.cjs} +2 -2
  3. package/dist/{PixiFrameExporter-DmyELJBR.cjs.map → PixiFrameExporter-CetwQ3WI.cjs.map} +1 -1
  4. package/dist/{PixiFrameExporter-UPjs5JGK.js → PixiFrameExporter-DFchWLl5.js} +2 -2
  5. package/dist/{PixiFrameExporter-UPjs5JGK.js.map → PixiFrameExporter-DFchWLl5.js.map} +1 -1
  6. package/dist/components/ExistingMediaSelector.vue.d.ts +13 -1
  7. package/dist/components/ImageEditor/ImageEditor.vue.d.ts +1 -0
  8. package/dist/components/ImageEditorModal.vue.d.ts +2 -0
  9. package/dist/components/MediaAlbumInlineCreate.vue.d.ts +2 -0
  10. package/dist/components/MediaAlbumUnavailableState.vue.d.ts +2 -0
  11. package/dist/components/MediaLibraryPicker.vue.d.ts +2 -0
  12. package/dist/components/MediaLibraryPickerModal.vue.d.ts +2 -0
  13. package/dist/components/MediaManagementView.vue.d.ts +2 -0
  14. package/dist/components/MediaPreviewSheet.vue.d.ts +2 -0
  15. package/dist/components/MediaSelectionTray.vue.d.ts +2 -0
  16. package/dist/components/MediaSourceSheet.vue.d.ts +2 -0
  17. package/dist/components/MediaThumbnailCell.vue.d.ts +2 -0
  18. package/dist/components/MediaUploadSheet.vue.d.ts +2 -0
  19. package/dist/components/VirtualMediaGrid.vue.d.ts +2 -0
  20. package/dist/composables/useFloatingPills.d.ts +4 -0
  21. package/dist/composables/useMediaDeletion.d.ts +66 -0
  22. package/dist/composables/useMediaLibraryPickerController.d.ts +60 -0
  23. package/dist/composables/useMediaLibraryQuery.d.ts +25 -0
  24. package/dist/composables/useMediaLibrarySession.d.ts +12 -0
  25. package/dist/composables/useMediaLibraryStateMachine.d.ts +6 -0
  26. package/dist/composables/useMediaLibraryTelemetry.d.ts +4 -0
  27. package/dist/composables/useMediaTrash.d.ts +64 -0
  28. package/dist/composables/useMediaUploadQueue.d.ts +2 -0
  29. package/dist/editor-BTwIhrcA.cjs +2 -0
  30. package/dist/editor-BTwIhrcA.cjs.map +1 -0
  31. package/dist/{editor-2Q72CZjK.js → editor-CYj5y5bp.js} +1417 -1167
  32. package/dist/editor-CYj5y5bp.js.map +1 -0
  33. package/dist/filters/registry.d.ts +4 -0
  34. package/dist/index-DNgtg1bF.js +28402 -0
  35. package/dist/index-DNgtg1bF.js.map +1 -0
  36. package/dist/index-nQskCfGV.cjs +342 -0
  37. package/dist/index-nQskCfGV.cjs.map +1 -0
  38. package/dist/index.d.ts +24 -0
  39. package/dist/mediables-vanilla.cjs +1 -1
  40. package/dist/mediables-vanilla.mjs +1 -1
  41. package/dist/mediables-vue.cjs +1 -1
  42. package/dist/mediables-vue.mjs +92 -74
  43. package/dist/render-page/assets/{index-ZZVWF3LA.js → index-y90zwXpc.js} +713 -531
  44. package/dist/render-page/index.html +1 -1
  45. package/dist/style.css +1 -1
  46. package/dist/types/collection.d.ts +1 -1
  47. package/dist/types/media.d.ts +26 -0
  48. package/dist/types/mediaLibraryPicker.d.ts +183 -0
  49. package/dist/types/mediaLibraryState.d.ts +29 -0
  50. package/docs/media-library-api.md +76 -0
  51. package/package.json +6 -3
  52. package/dist/editor-2Q72CZjK.js.map +0 -1
  53. package/dist/editor-DjvxEsss.cjs +0 -42
  54. package/dist/editor-DjvxEsss.cjs.map +0 -1
  55. package/dist/index-DMnweeFo.cjs +0 -342
  56. package/dist/index-DMnweeFo.cjs.map +0 -1
  57. package/dist/index-Dj_Ppp4_.js +0 -25226
  58. package/dist/index-Dj_Ppp4_.js.map +0 -1
@@ -14,7 +14,7 @@ export interface UploadProgress {
14
14
  /** Upload progress percentage (0-100) */
15
15
  progress: number;
16
16
  /** Current upload status */
17
- status: 'pending' | 'queued' | 'uploading' | 'processing' | 'completed' | 'error';
17
+ status: 'pending' | 'queued' | 'uploading' | 'attaching' | 'processing' | 'completed' | 'error';
18
18
  /** Error message if status is 'error' */
19
19
  error?: string | undefined;
20
20
  /** Uploaded media item (available when status is 'completed') */
@@ -39,7 +39,33 @@ export interface Media {
39
39
  height?: number;
40
40
  created_at: string;
41
41
  updated_at: string;
42
+ is_orphaned?: boolean;
43
+ deletion_status?: DeletionStatus;
44
+ is_trashed?: boolean;
45
+ purge_after?: string | null;
42
46
  }
47
+ /**
48
+ * Lifecycle states from the trash-aware deletion state machine.
49
+ * Mirrors the PHP `Mediables\Support\MediaUsage\DeletionStatus` enum.
50
+ *
51
+ * active — visible in library, dedup-eligible, attachable
52
+ * trashed — user-deleted, restorable, files still on disk
53
+ * purging — physical cleanup in progress
54
+ * purged — cleanup completed
55
+ * purge_failed — cleanup failed, requires admin intervention
56
+ */
57
+ export type DeletionStatus = 'active' | 'trashed' | 'purging' | 'purged' | 'purge_failed';
58
+ /**
59
+ * Possible per-item statuses returned by the batch delete endpoint.
60
+ */
61
+ export type BatchDeleteItemStatus = 'trashed' | 'already_trashed' | 'blocked' | 'protected' | 'not_found' | 'failed';
62
+ /**
63
+ * Possible statuses returned by the trash restore endpoint.
64
+ * `duplicate_conflict` is the Round 3 fix — restore detects an active
65
+ * duplicate exists and surfaces the collision instead of crashing on
66
+ * the unique index.
67
+ */
68
+ export type RestoreStatus = 'restored' | 'duplicate_conflict' | 'not_trashed' | 'not_found' | 'failed';
43
69
  /**
44
70
  * Collection response from paginated API endpoints
45
71
  */
@@ -0,0 +1,183 @@
1
+ import type { VNode } from 'vue';
2
+ import type { StorageAdapter } from './adapter';
3
+ import type { Media } from './media';
4
+ export type MediaKind = 'image' | 'video' | 'audio' | 'document' | (string & {});
5
+ export type MediaSort = 'newest' | 'oldest';
6
+ export type MediaSelectionMode = 'single' | 'multiple';
7
+ export type MediaLibraryUploadBehavior = 'adapter' | 'delegate';
8
+ export type MediaLibraryView = {
9
+ scope: 'library';
10
+ } | {
11
+ scope: 'album';
12
+ albumId: string;
13
+ albumName?: string;
14
+ };
15
+ export interface MediaLibraryFilters {
16
+ query?: string;
17
+ kinds?: MediaKind[];
18
+ sort?: MediaSort;
19
+ }
20
+ export interface MediaUploadTarget {
21
+ type: 'library' | 'album';
22
+ albumId?: string;
23
+ }
24
+ export interface MediaLibraryRequestConfig {
25
+ params?: Record<string, unknown>;
26
+ signal?: AbortSignal;
27
+ headers?: Record<string, string>;
28
+ }
29
+ export interface MediaLibraryHttpClient {
30
+ get<T>(url: string, config?: MediaLibraryRequestConfig): Promise<T>;
31
+ post<T>(url: string, body?: unknown, config?: MediaLibraryRequestConfig): Promise<T>;
32
+ put?<T>(url: string, body?: unknown, config?: MediaLibraryRequestConfig): Promise<T>;
33
+ patch?<T>(url: string, body?: unknown, config?: MediaLibraryRequestConfig): Promise<T>;
34
+ delete?<T>(url: string, config?: MediaLibraryRequestConfig): Promise<T>;
35
+ }
36
+ export interface MediaLibraryPickerDeps {
37
+ httpClient: MediaLibraryHttpClient;
38
+ uploadAdapter?: StorageAdapter;
39
+ resolveThumbnailUrl?: (media: Media, variant?: 'sm' | 'md' | 'lg') => string | Promise<string>;
40
+ }
41
+ export interface MediaSessionPolicy {
42
+ key: string;
43
+ ttlMinutes?: number;
44
+ restoreSelection?: boolean;
45
+ restoreView?: boolean;
46
+ restoreUploadsMeta?: boolean;
47
+ }
48
+ export type SelectedItemStatus = 'ready' | 'hydrating' | 'missing' | 'forbidden' | 'uploading' | 'failed' | 'file_lost';
49
+ export interface SelectedMediaItem {
50
+ id: string;
51
+ media: Media | null;
52
+ status: SelectedItemStatus;
53
+ statusMessage?: string;
54
+ countsTowardMax: boolean;
55
+ }
56
+ export interface MediaLibraryHydratedRecord {
57
+ id: string;
58
+ status: 'ready' | 'missing' | 'forbidden';
59
+ media?: Media;
60
+ }
61
+ export interface MediaLibraryResponsePagination {
62
+ next_cursor: string | null;
63
+ previous_cursor: string | null;
64
+ has_more: boolean;
65
+ per_page: number | null;
66
+ count: number | null;
67
+ }
68
+ export interface MediaLibraryResponseMeta {
69
+ cursor: string | null;
70
+ has_more: boolean;
71
+ pagination?: MediaLibraryResponsePagination;
72
+ }
73
+ export interface MediaLibraryResponse {
74
+ data: Media[];
75
+ meta: MediaLibraryResponseMeta;
76
+ hydrated: MediaLibraryHydratedRecord[];
77
+ }
78
+ export interface MediaLibraryApiEnvelope {
79
+ success?: boolean;
80
+ message?: string;
81
+ data?: unknown;
82
+ meta?: unknown;
83
+ hydrated?: unknown;
84
+ }
85
+ export interface MediaLibraryAlbum {
86
+ id: string;
87
+ name: string;
88
+ description?: string;
89
+ mediaCount?: number;
90
+ }
91
+ export interface MediaLibraryPickerMetric {
92
+ name: string;
93
+ payload?: Record<string, unknown>;
94
+ timestamp: string;
95
+ }
96
+ export interface MediaLibraryUploadRequestPayload {
97
+ files: File[];
98
+ view: MediaLibraryView;
99
+ filters: MediaLibraryFilters;
100
+ sessionId: string;
101
+ }
102
+ export type MediaLibraryPickerErrorCode = 'query_failed' | 'load_more_failed' | 'refresh_failed' | 'upload_failed' | 'upload_partial_failure' | 'selection_invalid' | 'permission_denied' | 'hydration_failed' | 'session_restore_failed' | 'configuration_error' | 'album_create_failed';
103
+ export interface MediaLibraryPickerError {
104
+ code: MediaLibraryPickerErrorCode;
105
+ message: string;
106
+ retriable: boolean;
107
+ cause?: unknown;
108
+ }
109
+ export interface MediaLibraryPickerLabels {
110
+ title?: string;
111
+ searchPlaceholder?: string;
112
+ viewLibrary?: string;
113
+ browseAlbums?: string;
114
+ createAlbum?: string;
115
+ createAlbumNameLabel?: string;
116
+ emptyLibrary?: string;
117
+ emptyAlbum?: string;
118
+ networkError?: string;
119
+ retry?: string;
120
+ selectedCount?: (count: number) => string;
121
+ confirmAction?: string;
122
+ cancel?: string;
123
+ dismiss?: string;
124
+ uploadLabel?: string;
125
+ albumUnavailableTitle?: string;
126
+ albumUnavailableBody?: string;
127
+ goToLibrary?: string;
128
+ }
129
+ export interface MediaLibraryPickerProps {
130
+ deps: MediaLibraryPickerDeps;
131
+ modelValue?: string[];
132
+ defaultSelectedIds?: string[];
133
+ selectionMode?: MediaSelectionMode;
134
+ maxSelection?: number;
135
+ preserveSelectionOnViewChange?: boolean;
136
+ initialView?: MediaLibraryView;
137
+ initialFilters?: MediaLibraryFilters;
138
+ enableUpload?: boolean;
139
+ uploadBehavior?: MediaLibraryUploadBehavior;
140
+ enablePreview?: boolean;
141
+ enableAlbumCreation?: boolean;
142
+ allowedMediaKinds?: MediaKind[];
143
+ accept?: string;
144
+ validateSelection?: (items: SelectedMediaItem[]) => string | null;
145
+ uploadTarget?: MediaUploadTarget;
146
+ autoSelectUploaded?: boolean;
147
+ createAlbumDefaultVisibility?: 'private' | 'authenticated' | 'public';
148
+ sessionPolicy?: MediaSessionPolicy;
149
+ labels?: MediaLibraryPickerLabels;
150
+ showFooter?: boolean;
151
+ testIdPrefix?: string;
152
+ telemetry?: (metric: MediaLibraryPickerMetric) => void;
153
+ }
154
+ export interface MediaLibraryConfirmPayload {
155
+ orderedIds: string[];
156
+ items: SelectedMediaItem[];
157
+ view: MediaLibraryView;
158
+ filters: MediaLibraryFilters;
159
+ dirty: boolean;
160
+ sessionId: string;
161
+ }
162
+ export interface MediaLibrarySelectionChangePayload {
163
+ orderedIds: string[];
164
+ items: SelectedMediaItem[];
165
+ dirty: boolean;
166
+ }
167
+ export interface MediaLibraryViewChangePayload {
168
+ from: MediaLibraryView;
169
+ to: MediaLibraryView;
170
+ reason: 'user' | 'restore' | 'permission-unavailable' | 'inline-album-created';
171
+ restoredFromSession: boolean;
172
+ }
173
+ export type PickerCancelReason = 'user' | 'backdrop' | 'escape' | 'host-close' | 'route-change';
174
+ export interface MediaLibraryPickerSlots {
175
+ toolbar?: () => VNode[];
176
+ empty?: (ctx: {
177
+ view: MediaLibraryView;
178
+ filters: MediaLibraryFilters;
179
+ }) => VNode[];
180
+ previewMeta?: (ctx: {
181
+ media: Media;
182
+ }) => VNode[];
183
+ }
@@ -0,0 +1,29 @@
1
+ import type { ComputedRef, Ref } from 'vue';
2
+ import type { UploadProgress } from './collection';
3
+ import type { Media } from './media';
4
+ import type { MediaLibraryAlbum, MediaLibraryFilters, MediaLibraryPickerError, MediaLibraryView, SelectedMediaItem } from './mediaLibraryPicker';
5
+ export interface MediaLibraryQueryState {
6
+ items: Ref<Media[]>;
7
+ cursor: Ref<string | null>;
8
+ hasMore: Ref<boolean>;
9
+ loading: Ref<boolean>;
10
+ loadingMore: Ref<boolean>;
11
+ permissionDenied: Ref<boolean>;
12
+ error: Ref<MediaLibraryPickerError | null>;
13
+ }
14
+ export interface MediaLibrarySelectionState {
15
+ items: Ref<SelectedMediaItem[]>;
16
+ orderedIds: ComputedRef<string[]>;
17
+ }
18
+ export interface MediaLibraryUploadState {
19
+ queue: ComputedRef<UploadProgress[]>;
20
+ isUploading: ComputedRef<boolean>;
21
+ }
22
+ export interface MediaLibraryControllerState {
23
+ view: Ref<MediaLibraryView>;
24
+ filters: Ref<MediaLibraryFilters>;
25
+ albums: Ref<MediaLibraryAlbum[]>;
26
+ query: MediaLibraryQueryState;
27
+ selection: MediaLibrarySelectionState;
28
+ upload: MediaLibraryUploadState;
29
+ }
@@ -0,0 +1,76 @@
1
+ # Media Library API
2
+
3
+ `MediaLibraryPicker` reads media through a host-provided `httpClient`. For blow.glass, the host implements the contract in `socialkit/Modules/Media` at `GET /api/v1/media/library`.
4
+
5
+ ## Request
6
+
7
+ `GET /api/v1/media/library`
8
+
9
+ Query parameters:
10
+
11
+ - `view[scope]`: `library | album`
12
+ - `view[album_id]`: required when `scope=album`
13
+ - `query`: optional search string
14
+ - `kinds[]`: optional `image | video | audio | document`
15
+ - `sort`: `newest | oldest`
16
+ - `created_after`: optional ISO datetime
17
+ - `uploaded_by_me`: optional boolean
18
+ - `cursor`: optional opaque cursor
19
+ - `per_page`: optional `1-100`, default `40`
20
+ - `hydrate_ids[]`: optional UUID list, max `50`
21
+
22
+ ## Success Response
23
+
24
+ ```json
25
+ {
26
+ "data": [
27
+ {
28
+ "uuid": "01981b2d-1111-4f75-8d0a-123456789abc",
29
+ "file_name": "furnace-progress.jpg",
30
+ "mime_type": "image/jpeg",
31
+ "size": 204800,
32
+ "disk": "public",
33
+ "collection_name": "images",
34
+ "original_url": "https://example.test/storage/furnace-progress.jpg",
35
+ "url": "https://example.test/storage/furnace-progress.jpg",
36
+ "thumbnail_url": "https://example.test/storage/conversions/furnace-progress-thumb.jpg",
37
+ "preview_url": "https://example.test/storage/conversions/furnace-progress-preview.jpg",
38
+ "caption": null,
39
+ "alt_text": null,
40
+ "created_at": "2026-04-12T12:34:56+00:00",
41
+ "updated_at": "2026-04-12T12:34:56+00:00"
42
+ }
43
+ ],
44
+ "meta": {
45
+ "cursor": "eyJjIjoiMjAyNi0wNC0xMlQxMjozNDo1Ni4xMjM0NTYrMDA6MDAiLCJpIjoiMDE5ODFiMmQtMTExMS00Zjc1LThkMGEtMTIzNDU2Nzg5YWJjIn0=",
46
+ "has_more": true
47
+ },
48
+ "hydrated": [
49
+ {
50
+ "id": "01981b2d-1111-4f75-8d0a-123456789abc",
51
+ "status": "ready",
52
+ "media": {
53
+ "uuid": "01981b2d-1111-4f75-8d0a-123456789abc"
54
+ }
55
+ },
56
+ {
57
+ "id": "01981b2d-2222-4f75-8d0a-123456789abc",
58
+ "status": "missing"
59
+ }
60
+ ]
61
+ }
62
+ ```
63
+
64
+ ## Cursor Contract
65
+
66
+ - The cursor is base64-encoded JSON with:
67
+ - `c`: ISO datetime with microseconds
68
+ - `i`: media UUID
69
+ - `sort=newest` uses `(created_at DESC, uuid DESC)`
70
+ - `sort=oldest` uses `(created_at ASC, uuid ASC)`
71
+
72
+ ## Host Notes
73
+
74
+ - Host apps should inject a thin client that returns parsed bodies, not Axios responses.
75
+ - Upload flows should reuse `StorageAdapter` and `useMediaUploadQueue`.
76
+ - The picker assumes `media.deleted_at IS NULL` is enforced by the backend query layer, including joined album queries.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@codingfactory/mediables-vue",
3
- "version": "2.4.23",
3
+ "version": "2.6.1",
4
4
  "publishConfig": {
5
- "access": "public"
5
+ "access": "restricted"
6
6
  },
7
7
  "type": "module",
8
8
  "description": "Vue components for Laravel Mediables package with comprehensive video support",
@@ -16,7 +16,8 @@
16
16
  "types": "./dist/index.d.ts",
17
17
  "files": [
18
18
  "dist",
19
- "docs/video-subsystem"
19
+ "docs/video-subsystem",
20
+ "docs/media-library-api.md"
20
21
  ],
21
22
  "exports": {
22
23
  ".": {
@@ -48,6 +49,7 @@
48
49
  "docs:preview": "redocly preview-docs docs/openapi.yaml"
49
50
  },
50
51
  "peerDependencies": {
52
+ "@tanstack/vue-virtual": "^3.10.8",
51
53
  "@ionic/vue": "^8.0.0",
52
54
  "@pixi/extract": "^7.2.4",
53
55
  "@pixi/filter-color-replace": "^5.1.1",
@@ -84,6 +86,7 @@
84
86
  "dependencies": {
85
87
  "@capacitor/haptics": "^7.0.2",
86
88
  "@capacitor/status-bar": "^7.0.2",
89
+ "@tanstack/vue-virtual": "^3.10.8",
87
90
  "@ionic/vue": "^8.0.0",
88
91
  "@material/material-color-utilities": "^0.3.0",
89
92
  "@vueuse/core": "^12.0.0",