nuxt-media-library 0.0.48 → 0.0.50

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 (100) hide show
  1. package/README.md +45 -0
  2. package/dist/module.d.mts +14 -1
  3. package/dist/module.d.ts +14 -1
  4. package/dist/module.json +1 -1
  5. package/dist/module.mjs +11 -2
  6. package/dist/runtime/components/MLMedia/Dropzone.d.vue.ts +60 -0
  7. package/dist/runtime/components/MLMedia/Dropzone.vue +137 -0
  8. package/dist/runtime/components/MLMedia/Dropzone.vue.d.ts +60 -0
  9. package/dist/runtime/components/MLMedia/Editor.d.vue.ts +23 -0
  10. package/dist/runtime/components/MLMedia/Editor.vue +34 -0
  11. package/dist/runtime/components/MLMedia/Editor.vue.d.ts +23 -0
  12. package/dist/runtime/components/MLMedia/EditorPanel.d.vue.ts +38 -0
  13. package/dist/runtime/components/MLMedia/EditorPanel.vue +220 -0
  14. package/dist/runtime/components/MLMedia/EditorPanel.vue.d.ts +38 -0
  15. package/dist/runtime/components/MLMedia/Field.d.vue.ts +65 -0
  16. package/dist/runtime/components/MLMedia/Field.vue +282 -0
  17. package/dist/runtime/components/MLMedia/Field.vue.d.ts +65 -0
  18. package/dist/runtime/components/MLMedia/FileFacts.d.vue.ts +19 -0
  19. package/dist/runtime/components/MLMedia/FileFacts.vue +38 -0
  20. package/dist/runtime/components/MLMedia/FileFacts.vue.d.ts +19 -0
  21. package/dist/runtime/components/MLMedia/FilePicker.d.vue.ts +23 -0
  22. package/dist/runtime/components/MLMedia/FilePicker.vue +33 -0
  23. package/dist/runtime/components/MLMedia/FilePicker.vue.d.ts +23 -0
  24. package/dist/runtime/components/MLMedia/FilePickerPanel.d.vue.ts +33 -0
  25. package/dist/runtime/components/MLMedia/FilePickerPanel.vue +300 -0
  26. package/dist/runtime/components/MLMedia/FilePickerPanel.vue.d.ts +33 -0
  27. package/dist/runtime/components/MLMedia/FilterPill.d.vue.ts +30 -0
  28. package/dist/runtime/components/MLMedia/FilterPill.vue +84 -0
  29. package/dist/runtime/components/MLMedia/FilterPill.vue.d.ts +30 -0
  30. package/dist/runtime/components/MLMedia/Gallery.d.vue.ts +78 -0
  31. package/dist/runtime/components/MLMedia/Gallery.vue +256 -0
  32. package/dist/runtime/components/MLMedia/Gallery.vue.d.ts +78 -0
  33. package/dist/runtime/components/MLMedia/GallerySection.d.vue.ts +102 -0
  34. package/dist/runtime/components/MLMedia/GallerySection.vue +422 -0
  35. package/dist/runtime/components/MLMedia/GallerySection.vue.d.ts +102 -0
  36. package/dist/runtime/components/MLMedia/KindBadge.d.vue.ts +17 -0
  37. package/dist/runtime/components/MLMedia/KindBadge.vue +22 -0
  38. package/dist/runtime/components/MLMedia/KindBadge.vue.d.ts +17 -0
  39. package/dist/runtime/components/MLMedia/MediaTile.d.vue.ts +63 -0
  40. package/dist/runtime/components/MLMedia/MediaTile.vue +117 -0
  41. package/dist/runtime/components/MLMedia/MediaTile.vue.d.ts +63 -0
  42. package/dist/runtime/components/MLMedia/PickerCard.d.vue.ts +42 -0
  43. package/dist/runtime/components/MLMedia/PickerCard.vue +79 -0
  44. package/dist/runtime/components/MLMedia/PickerCard.vue.d.ts +42 -0
  45. package/dist/runtime/components/MLMedia/PickerRow.d.vue.ts +31 -0
  46. package/dist/runtime/components/MLMedia/PickerRow.vue +65 -0
  47. package/dist/runtime/components/MLMedia/PickerRow.vue.d.ts +31 -0
  48. package/dist/runtime/components/MLMedia/PreviewEye.d.vue.ts +16 -0
  49. package/dist/runtime/components/MLMedia/PreviewEye.vue +21 -0
  50. package/dist/runtime/components/MLMedia/PreviewEye.vue.d.ts +16 -0
  51. package/dist/runtime/components/MLMedia/ProgressVeil.d.vue.ts +25 -0
  52. package/dist/runtime/components/MLMedia/ProgressVeil.vue +98 -0
  53. package/dist/runtime/components/MLMedia/ProgressVeil.vue.d.ts +25 -0
  54. package/dist/runtime/components/MLMedia/StatusBadge.d.vue.ts +17 -0
  55. package/dist/runtime/components/MLMedia/StatusBadge.vue +31 -0
  56. package/dist/runtime/components/MLMedia/StatusBadge.vue.d.ts +17 -0
  57. package/dist/runtime/components/MLMedia/Thumbnail.d.vue.ts +36 -0
  58. package/dist/runtime/components/MLMedia/Thumbnail.vue +84 -0
  59. package/dist/runtime/components/MLMedia/Thumbnail.vue.d.ts +36 -0
  60. package/dist/runtime/components/MLMedia/UsageList.d.vue.ts +26 -0
  61. package/dist/runtime/components/MLMedia/UsageList.vue +66 -0
  62. package/dist/runtime/components/MLMedia/UsageList.vue.d.ts +26 -0
  63. package/dist/runtime/components/MLProcessing/StepLayout.d.vue.ts +1 -1
  64. package/dist/runtime/components/MLProcessing/StepLayout.vue.d.ts +1 -1
  65. package/dist/runtime/composables/useMLFilePicker.d.ts +20 -0
  66. package/dist/runtime/composables/useMLFilePicker.js +17 -0
  67. package/dist/runtime/composables/useMLMediaAssets.d.ts +19 -0
  68. package/dist/runtime/composables/useMLMediaAssets.js +56 -0
  69. package/dist/runtime/composables/useMLMediaBrowse.d.ts +147 -0
  70. package/dist/runtime/composables/useMLMediaBrowse.js +72 -0
  71. package/dist/runtime/composables/useMLMediaEditor.d.ts +22 -0
  72. package/dist/runtime/composables/useMLMediaEditor.js +20 -0
  73. package/dist/runtime/composables/useMLMediaField.d.ts +29 -0
  74. package/dist/runtime/composables/useMLMediaField.js +77 -0
  75. package/dist/runtime/composables/useMLMediaGallery.d.ts +55 -0
  76. package/dist/runtime/composables/useMLMediaGallery.js +134 -0
  77. package/dist/runtime/composables/useMLMediaUpload.d.ts +134 -0
  78. package/dist/runtime/composables/useMLMediaUpload.js +153 -0
  79. package/dist/runtime/composables/useMLServiceUpload.d.ts +10 -2
  80. package/dist/runtime/composables/useMLServiceUpload.js +4 -1
  81. package/dist/runtime/index.d.ts +11 -0
  82. package/dist/runtime/index.js +11 -0
  83. package/dist/runtime/server/index.d.ts +2 -0
  84. package/dist/runtime/server/index.js +1 -0
  85. package/dist/runtime/server/service-uploads-proxy.d.ts +31 -0
  86. package/dist/runtime/server/service-uploads-proxy.js +61 -0
  87. package/dist/runtime/utils/assetDisplay.d.ts +8 -0
  88. package/dist/runtime/utils/assetDisplay.js +29 -0
  89. package/dist/runtime/utils/assetFormat.d.ts +30 -0
  90. package/dist/runtime/utils/assetFormat.js +69 -0
  91. package/dist/runtime/utils/assetThumbnail.d.ts +11 -0
  92. package/dist/runtime/utils/assetThumbnail.js +14 -0
  93. package/dist/runtime/utils/fileAccept.d.ts +26 -0
  94. package/dist/runtime/utils/fileAccept.js +104 -0
  95. package/dist/runtime/utils/galleryDrag.d.ts +2 -0
  96. package/dist/runtime/utils/galleryDrag.js +1 -0
  97. package/dist/runtime/utils/uploader/serviceUpload.d.ts +16 -2
  98. package/dist/runtime/utils/uploader/serviceUpload.js +5 -1
  99. package/dist/types.d.mts +1 -1
  100. package/package.json +1 -1
package/README.md CHANGED
@@ -153,6 +153,51 @@ addMediaGrants(lesson.mediaGrant, profile.mediaGrant) // "mlg_…" or { grant, e
153
153
  grants are added.
154
154
  - Grants live in the browser only, never in server-side state.
155
155
 
156
+ ### Uploads started by your backend services
157
+
158
+ When a backend service starts the upload (the Python client's
159
+ `create_upload_router`), the service creates the session and returns a grant
160
+ for it, and `useMLServiceUpload` sends the parts. Where the parts go is the
161
+ app's decision, not the service's:
162
+
163
+ ```ts
164
+ // nuxt.config.ts
165
+ export default defineNuxtConfig({
166
+ mediaLibrary: {
167
+ baseURL: process.env.MEDIA_LIBRARY_URL, // server side, e.g. http://media-library.internal/v1
168
+ serviceUploads: { proxy: true }, // parts go through /api/media-library/service-uploads
169
+ // or, direct to the media library (needs CORS for the site):
170
+ // serviceUploads: { baseURL: 'https://media.example.com/v1' },
171
+ },
172
+ })
173
+ ```
174
+
175
+ ```ts
176
+ const { upload, progress } = useMLServiceUpload()
177
+ const { sessionId, assetId } = await upload(file, {
178
+ start: '/api/videos/uploads',
179
+ body: { profile: 'lesson-video', context: { lessonId } },
180
+ })
181
+ await $fetch(`/api/videos/lessons/${lessonId}/video`, { method: 'PUT', body: { sessionId, assetId } })
182
+ ```
183
+
184
+ - The upload base is, in order: `upload(file, { baseURL })`,
185
+ `useMLServiceUpload({ baseURL })`, `serviceUploads.baseURL`
186
+ (`NUXT_PUBLIC_MEDIA_LIBRARY_SERVICE_UPLOAD_BASE_URL`), the pass-through
187
+ route when `serviceUploads.proxy` is on, and last the `uploadBaseUrl` an
188
+ older service returns. With none, `upload` fails before any part is sent.
189
+ - The pass-through route (`serviceUploads.proxy: true`) forwards only a
190
+ session's `GET`, parts `PUT`, `finalize` and `abort` to the private
191
+ `mediaLibrary.baseURL`. It requires the browser's `Bearer mlg_…` grant and
192
+ forwards it and `X-Tenant` untouched (the static `X-Tenant` when the
193
+ browser sends none). It never adds the service token, static auth headers
194
+ or a Nuxt-issued grant, drops cookies, and streams request bodies. Mount it
195
+ yourself with `createMLServiceUploadsProxy()` from
196
+ `nuxt-media-library/server` for another path.
197
+ - Don't send these uploads through `createMLCatchAllProxy` with `authorize`:
198
+ grant mode replaces the browser's grant with its own, and the service's
199
+ `claim_upload` rejects the session.
200
+
156
201
  ### Vue installation (non-Nuxt)
157
202
 
158
203
  ```bash
package/dist/module.d.mts CHANGED
@@ -24,8 +24,21 @@ interface ModuleOptions {
24
24
  final?: boolean;
25
25
  };
26
26
  headers?: Record<string, string> | (() => Record<string, string>);
27
+ /**
28
+ * Uploads started by the app's backend services (`useMLServiceUpload`).
29
+ * `proxy: true` adds `/api/media-library/service-uploads/**`, a same-origin
30
+ * pass-through that forwards the browser's media grant and nothing else.
31
+ * `baseURL` is where the browser sends those uploads (the media library's
32
+ * public API root with `/v1`, or a proxy route); with `proxy` it defaults
33
+ * to the pass-through route.
34
+ */
35
+ serviceUploads?: {
36
+ proxy?: boolean;
37
+ baseURL?: string;
38
+ };
27
39
  }
40
+ declare const SERVICE_UPLOADS_ROUTE = "/api/media-library/service-uploads";
28
41
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
29
42
 
30
- export { _default as default };
43
+ export { SERVICE_UPLOADS_ROUTE, _default as default };
31
44
  export type { ChunkUploaderRoutesConfig, ModuleOptions };
package/dist/module.d.ts CHANGED
@@ -24,8 +24,21 @@ interface ModuleOptions {
24
24
  final?: boolean;
25
25
  };
26
26
  headers?: Record<string, string> | (() => Record<string, string>);
27
+ /**
28
+ * Uploads started by the app's backend services (`useMLServiceUpload`).
29
+ * `proxy: true` adds `/api/media-library/service-uploads/**`, a same-origin
30
+ * pass-through that forwards the browser's media grant and nothing else.
31
+ * `baseURL` is where the browser sends those uploads (the media library's
32
+ * public API root with `/v1`, or a proxy route); with `proxy` it defaults
33
+ * to the pass-through route.
34
+ */
35
+ serviceUploads?: {
36
+ proxy?: boolean;
37
+ baseURL?: string;
38
+ };
27
39
  }
40
+ declare const SERVICE_UPLOADS_ROUTE = "/api/media-library/service-uploads";
28
41
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
29
42
 
30
- export { _default as default };
43
+ export { SERVICE_UPLOADS_ROUTE, _default as default };
31
44
  export type { ChunkUploaderRoutesConfig, ModuleOptions };
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "media-library",
3
3
  "configKey": "mediaLibrary",
4
- "version": "0.0.48",
4
+ "version": "0.0.50",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -4,6 +4,7 @@ export * from '../dist/runtime/types/uploader.js';
4
4
  export * from '../dist/runtime/types/preview.js';
5
5
  export * from '../dist/runtime/types/asset.js';
6
6
 
7
+ const SERVICE_UPLOADS_ROUTE = "/api/media-library/service-uploads";
7
8
  const module$1 = defineNuxtModule({
8
9
  meta: {
9
10
  name: "media-library",
@@ -89,6 +90,12 @@ const module$1 = defineNuxtModule({
89
90
  route: "/api/media-library/manage/processing/**",
90
91
  handler: resolver.resolve("./runtime/server/processing-proxy")
91
92
  });
93
+ if (options.serviceUploads?.proxy) {
94
+ addServerHandler({
95
+ route: `${SERVICE_UPLOADS_ROUTE}/**`,
96
+ handler: resolver.resolve("./runtime/server/service-uploads-proxy")
97
+ });
98
+ }
92
99
  nuxt.options.runtimeConfig.mediaLibrary = {
93
100
  ...existingPrivate,
94
101
  baseURL: existingPrivate.baseURL ?? envBaseURL ?? options.baseURL ?? "",
@@ -110,7 +117,9 @@ const module$1 = defineNuxtModule({
110
117
  perPart: existingPublic.checksum?.perPart ?? options.checksum?.perPart ?? true,
111
118
  final: existingPublic.checksum?.final ?? options.checksum?.final ?? true
112
119
  },
113
- headers: existingPublic.headers ?? options.headers ?? {}
120
+ headers: existingPublic.headers ?? options.headers ?? {},
121
+ // Where useMLServiceUpload sends parts (NUXT_PUBLIC_MEDIA_LIBRARY_SERVICE_UPLOAD_BASE_URL).
122
+ serviceUploadBaseURL: existingPublic.serviceUploadBaseURL || options.serviceUploads?.baseURL || (options.serviceUploads?.proxy ? SERVICE_UPLOADS_ROUTE : "")
114
123
  };
115
124
  addPlugin({
116
125
  src: resolver.resolve("./runtime/plugins/config")
@@ -118,4 +127,4 @@ const module$1 = defineNuxtModule({
118
127
  }
119
128
  });
120
129
 
121
- export { module$1 as default };
130
+ export { SERVICE_UPLOADS_ROUTE, module$1 as default };
@@ -0,0 +1,60 @@
1
+ import { type RejectedFile } from '../../utils/fileAccept.js';
2
+ type __VLS_Props = {
3
+ /** Browser accept syntax ("image/*,.glb"). Build one from kinds with acceptForKinds(). */
4
+ accept?: string;
5
+ multiple?: boolean;
6
+ /** Bytes. Larger files are rejected with reason "size". */
7
+ maxSize?: number | null;
8
+ /** How many more files fit. Extra files are rejected with reason "count". */
9
+ maxFiles?: number | null;
10
+ disabled?: boolean;
11
+ /**
12
+ * `area`: a dashed box with an upload button (fields, picker).
13
+ * `overlay`: wraps the default slot and only shows a highlight while files are dragged over it (gallery).
14
+ */
15
+ variant?: 'area' | 'overlay';
16
+ title?: string;
17
+ hint?: string;
18
+ /** The button that opens the computer's file dialog. Defaults to "Upload file(s)". */
19
+ buttonLabel?: string;
20
+ /** One short row instead of a box, for places where the drop zone is secondary (the picker). */
21
+ compact?: boolean;
22
+ };
23
+ type __VLS_Slots = {
24
+ default?: (props: {
25
+ isDragging: boolean;
26
+ open: () => void;
27
+ }) => unknown;
28
+ actions?: (props: {
29
+ open: () => void;
30
+ }) => unknown;
31
+ };
32
+ declare function open(): void;
33
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
34
+ open: typeof open;
35
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
36
+ files: (files: File[]) => any;
37
+ rejected: (files: RejectedFile[]) => any;
38
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
39
+ onFiles?: ((files: File[]) => any) | undefined;
40
+ onRejected?: ((files: RejectedFile[]) => any) | undefined;
41
+ }>, {
42
+ variant: "area" | "overlay";
43
+ compact: boolean;
44
+ disabled: boolean;
45
+ multiple: boolean;
46
+ title: string;
47
+ accept: string;
48
+ maxSize: number | null;
49
+ maxFiles: number | null;
50
+ hint: string;
51
+ buttonLabel: string;
52
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
53
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
54
+ declare const _default: typeof __VLS_export;
55
+ export default _default;
56
+ type __VLS_WithSlots<T, S> = T & {
57
+ new (): {
58
+ $slots: S;
59
+ };
60
+ };
@@ -0,0 +1,137 @@
1
+ <script setup>
2
+ import { computed, ref } from "vue";
3
+ import { useFileUpload } from "#imports";
4
+ import { formatBytes } from "../../utils/assetFormat";
5
+ import { partitionFiles } from "../../utils/fileAccept";
6
+ const props = defineProps({
7
+ accept: { type: String, required: false, default: "*" },
8
+ multiple: { type: Boolean, required: false, default: true },
9
+ maxSize: { type: [Number, null], required: false, default: null },
10
+ maxFiles: { type: [Number, null], required: false, default: null },
11
+ disabled: { type: Boolean, required: false, default: false },
12
+ variant: { type: String, required: false, default: "area" },
13
+ title: { type: String, required: false, default: void 0 },
14
+ hint: { type: String, required: false, default: void 0 },
15
+ buttonLabel: { type: String, required: false, default: void 0 },
16
+ compact: { type: Boolean, required: false, default: false }
17
+ });
18
+ const emit = defineEmits(["files", "rejected"]);
19
+ defineSlots();
20
+ const { isDragging, dropzoneRef } = useFileUpload({
21
+ accept: "*",
22
+ multiple: true,
23
+ dropzone: true,
24
+ onUpdate: (files) => handle(files)
25
+ });
26
+ const input = ref();
27
+ function handle(files) {
28
+ if (props.disabled || !files.length) return;
29
+ const max = props.multiple ? props.maxFiles : Math.min(1, props.maxFiles ?? 1);
30
+ const { accepted, rejected } = partitionFiles(files, { accept: props.accept, maxSize: props.maxSize, maxFiles: max });
31
+ if (accepted.length) emit("files", accepted);
32
+ if (rejected.length) emit("rejected", rejected);
33
+ }
34
+ function open() {
35
+ if (!props.disabled) input.value?.click();
36
+ }
37
+ function onInput(e) {
38
+ const el = e.target;
39
+ handle(Array.from(el.files ?? []));
40
+ el.value = "";
41
+ }
42
+ const carriesFiles = ref(false);
43
+ function onDragEnter(e) {
44
+ carriesFiles.value = !!e.dataTransfer && Array.from(e.dataTransfer.types).includes("Files");
45
+ }
46
+ const active = computed(() => isDragging.value && carriesFiles.value && !props.disabled);
47
+ const sizeHint = computed(() => props.maxSize ? `Up to ${formatBytes(props.maxSize)}` : "");
48
+ defineExpose({ open });
49
+ </script>
50
+
51
+ <template>
52
+ <div
53
+ ref="dropzoneRef"
54
+ class="relative"
55
+ @dragenter.capture="onDragEnter"
56
+ :data-dragging="active || void 0"
57
+ :aria-disabled="disabled || void 0"
58
+ >
59
+ <input
60
+ ref="input"
61
+ type="file"
62
+ class="sr-only"
63
+ tabindex="-1"
64
+ aria-hidden="true"
65
+ :accept="accept === '*' ? void 0 : accept"
66
+ :multiple="multiple"
67
+ :disabled="disabled"
68
+ @change="onInput"
69
+ >
70
+
71
+ <template v-if="variant === 'overlay'">
72
+ <slot
73
+ :is-dragging="active"
74
+ :open="open"
75
+ />
76
+ <div
77
+ v-if="active"
78
+ class="pointer-events-none absolute inset-0 z-20 grid place-items-center rounded-[inherit] border-2 border-dashed border-primary bg-primary/10 backdrop-blur-[1px]"
79
+ >
80
+ <span class="flex items-center gap-2 rounded-md bg-default px-3 py-1.5 text-sm font-semibold text-primary shadow-sm">
81
+ <UIcon
82
+ name="i-lucide-upload"
83
+ class="size-4"
84
+ />
85
+ {{ title ?? "Drop to upload" }}
86
+ </span>
87
+ </div>
88
+ </template>
89
+
90
+ <div
91
+ v-else
92
+ class="flex items-center rounded-lg border-[1.5px] border-dashed text-center transition-colors motion-reduce:transition-none"
93
+ :class="[
94
+ compact ? 'flex-row flex-wrap justify-center gap-x-3 gap-y-1.5 px-3 py-2.5' : 'flex-col gap-2.5 px-4 py-5',
95
+ active ? 'border-primary bg-primary/10' : 'border-accented bg-default',
96
+ disabled ? 'opacity-60' : ''
97
+ ]"
98
+ >
99
+ <slot
100
+ :is-dragging="active"
101
+ :open="open"
102
+ >
103
+ <UIcon
104
+ v-if="!compact"
105
+ name="i-lucide-upload"
106
+ class="size-5"
107
+ :class="active ? 'text-primary' : 'text-dimmed'"
108
+ />
109
+ <p class="text-sm">
110
+ <span :class="compact ? 'text-muted' : 'font-semibold'">{{ title ?? (multiple ? "Drop files here" : "Drop a file here") }}</span>
111
+ <span class="text-muted"> or</span>
112
+ </p>
113
+ <div class="flex flex-wrap justify-center gap-1.5">
114
+ <UButton
115
+ size="sm"
116
+ color="neutral"
117
+ variant="outline"
118
+ icon="i-lucide-upload"
119
+ :label="buttonLabel ?? (multiple ? 'Upload files' : 'Upload file')"
120
+ :disabled="disabled"
121
+ @click="open"
122
+ />
123
+ <slot
124
+ name="actions"
125
+ :open="open"
126
+ />
127
+ </div>
128
+ <p
129
+ v-if="hint || sizeHint"
130
+ class="text-xs text-dimmed"
131
+ >
132
+ {{ [hint, sizeHint].filter(Boolean).join(" \xB7 ") }}
133
+ </p>
134
+ </slot>
135
+ </div>
136
+ </div>
137
+ </template>
@@ -0,0 +1,60 @@
1
+ import { type RejectedFile } from '../../utils/fileAccept.js';
2
+ type __VLS_Props = {
3
+ /** Browser accept syntax ("image/*,.glb"). Build one from kinds with acceptForKinds(). */
4
+ accept?: string;
5
+ multiple?: boolean;
6
+ /** Bytes. Larger files are rejected with reason "size". */
7
+ maxSize?: number | null;
8
+ /** How many more files fit. Extra files are rejected with reason "count". */
9
+ maxFiles?: number | null;
10
+ disabled?: boolean;
11
+ /**
12
+ * `area`: a dashed box with an upload button (fields, picker).
13
+ * `overlay`: wraps the default slot and only shows a highlight while files are dragged over it (gallery).
14
+ */
15
+ variant?: 'area' | 'overlay';
16
+ title?: string;
17
+ hint?: string;
18
+ /** The button that opens the computer's file dialog. Defaults to "Upload file(s)". */
19
+ buttonLabel?: string;
20
+ /** One short row instead of a box, for places where the drop zone is secondary (the picker). */
21
+ compact?: boolean;
22
+ };
23
+ type __VLS_Slots = {
24
+ default?: (props: {
25
+ isDragging: boolean;
26
+ open: () => void;
27
+ }) => unknown;
28
+ actions?: (props: {
29
+ open: () => void;
30
+ }) => unknown;
31
+ };
32
+ declare function open(): void;
33
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
34
+ open: typeof open;
35
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
36
+ files: (files: File[]) => any;
37
+ rejected: (files: RejectedFile[]) => any;
38
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
39
+ onFiles?: ((files: File[]) => any) | undefined;
40
+ onRejected?: ((files: RejectedFile[]) => any) | undefined;
41
+ }>, {
42
+ variant: "area" | "overlay";
43
+ compact: boolean;
44
+ disabled: boolean;
45
+ multiple: boolean;
46
+ title: string;
47
+ accept: string;
48
+ maxSize: number | null;
49
+ maxFiles: number | null;
50
+ hint: string;
51
+ buttonLabel: string;
52
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
53
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
54
+ declare const _default: typeof __VLS_export;
55
+ export default _default;
56
+ type __VLS_WithSlots<T, S> = T & {
57
+ new (): {
58
+ $slots: S;
59
+ };
60
+ };
@@ -0,0 +1,23 @@
1
+ import type { MediaUsage } from './UsageList.vue.js';
2
+ export type MediaEditorResult = {
3
+ action: 'save';
4
+ alt: Record<string, string>;
5
+ } | {
6
+ action: 'remove';
7
+ assetId: string;
8
+ };
9
+ type __VLS_Props = {
10
+ assetId: string;
11
+ siblings?: readonly string[];
12
+ alt?: Readonly<Record<string, string>>;
13
+ readonly?: boolean;
14
+ removeLabel?: string;
15
+ usages?: Readonly<Record<string, MediaUsage[]>>;
16
+ };
17
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
18
+ close: (result: MediaEditorResult | null) => any;
19
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
20
+ onClose?: ((result: MediaEditorResult | null) => any) | undefined;
21
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
22
+ declare const _default: typeof __VLS_export;
23
+ export default _default;
@@ -0,0 +1,34 @@
1
+ <script setup>
2
+ defineProps({
3
+ assetId: { type: String, required: true },
4
+ siblings: { type: Array, required: false },
5
+ alt: { type: Object, required: false },
6
+ readonly: { type: Boolean, required: false },
7
+ removeLabel: { type: String, required: false },
8
+ usages: { type: Object, required: false }
9
+ });
10
+ const emit = defineEmits(["close"]);
11
+ </script>
12
+
13
+ <template>
14
+ <UModal
15
+ :title="readonly ? 'Preview' : 'Edit media'"
16
+ :close="{ onClick: () => emit('close', null) }"
17
+ :ui="{ content: 'sm:max-w-5xl', body: 'p-0 sm:p-0' }"
18
+ @update:open="(open) => !open && emit('close', null)"
19
+ >
20
+ <template #body>
21
+ <MLMediaEditorPanel
22
+ :asset-id="assetId"
23
+ :siblings="siblings"
24
+ :alt="alt"
25
+ :readonly="readonly"
26
+ :remove-label="removeLabel"
27
+ :usages="usages"
28
+ @save="({ alt: a }) => emit('close', { action: 'save', alt: a })"
29
+ @remove="(id) => emit('close', { action: 'remove', assetId: id })"
30
+ @cancel="emit('close', null)"
31
+ />
32
+ </template>
33
+ </UModal>
34
+ </template>
@@ -0,0 +1,23 @@
1
+ import type { MediaUsage } from './UsageList.vue.js';
2
+ export type MediaEditorResult = {
3
+ action: 'save';
4
+ alt: Record<string, string>;
5
+ } | {
6
+ action: 'remove';
7
+ assetId: string;
8
+ };
9
+ type __VLS_Props = {
10
+ assetId: string;
11
+ siblings?: readonly string[];
12
+ alt?: Readonly<Record<string, string>>;
13
+ readonly?: boolean;
14
+ removeLabel?: string;
15
+ usages?: Readonly<Record<string, MediaUsage[]>>;
16
+ };
17
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
18
+ close: (result: MediaEditorResult | null) => any;
19
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
20
+ onClose?: ((result: MediaEditorResult | null) => any) | undefined;
21
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
22
+ declare const _default: typeof __VLS_export;
23
+ export default _default;
@@ -0,0 +1,38 @@
1
+ import type { MediaUsage } from './UsageList.vue.js';
2
+ type __VLS_Props = {
3
+ /** The file to show first. */
4
+ assetId: string;
5
+ /** The record's other files, in order. Enables previous / next. */
6
+ siblings?: readonly string[];
7
+ /** Current alt text per asset ID. Alt text lives on the host record, not the file. */
8
+ alt?: Readonly<Record<string, string>>;
9
+ /** Preview only: no alt editing, no Save. Used from the picker. */
10
+ readonly?: boolean;
11
+ /** Label for the remove button, e.g. "Remove from product". Empty hides it. */
12
+ removeLabel?: string;
13
+ /** Where each file is used, per asset ID. Omit to hide the section. */
14
+ usages?: Readonly<Record<string, MediaUsage[]>>;
15
+ };
16
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
17
+ currentId: import("vue").ComputedRef<string>;
18
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
19
+ remove: (assetId: string) => any;
20
+ cancel: () => any;
21
+ save: (result: {
22
+ alt: Record<string, string>;
23
+ }) => any;
24
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
25
+ onRemove?: ((assetId: string) => any) | undefined;
26
+ onCancel?: (() => any) | undefined;
27
+ onSave?: ((result: {
28
+ alt: Record<string, string>;
29
+ }) => any) | undefined;
30
+ }>, {
31
+ usages: Readonly<Record<string, MediaUsage[]>>;
32
+ siblings: readonly string[];
33
+ alt: Readonly<Record<string, string>>;
34
+ readonly: boolean;
35
+ removeLabel: string;
36
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
37
+ declare const _default: typeof __VLS_export;
38
+ export default _default;