nuxt-media-library 0.0.44

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 (164) hide show
  1. package/README.md +243 -0
  2. package/dist/module.d.mts +31 -0
  3. package/dist/module.d.ts +31 -0
  4. package/dist/module.json +9 -0
  5. package/dist/module.mjs +118 -0
  6. package/dist/runtime/components/MLAssetManager/AssetActivity.d.vue.ts +6 -0
  7. package/dist/runtime/components/MLAssetManager/AssetActivity.vue +13 -0
  8. package/dist/runtime/components/MLAssetManager/AssetActivity.vue.d.ts +6 -0
  9. package/dist/runtime/components/MLAssetManager/AssetCard.d.vue.ts +14 -0
  10. package/dist/runtime/components/MLAssetManager/AssetCard.vue +100 -0
  11. package/dist/runtime/components/MLAssetManager/AssetCard.vue.d.ts +14 -0
  12. package/dist/runtime/components/MLAssetManager/AssetDetail.d.vue.ts +14 -0
  13. package/dist/runtime/components/MLAssetManager/AssetDetail.vue +156 -0
  14. package/dist/runtime/components/MLAssetManager/AssetDetail.vue.d.ts +14 -0
  15. package/dist/runtime/components/MLAssetManager/AssetList.d.vue.ts +15 -0
  16. package/dist/runtime/components/MLAssetManager/AssetList.vue +41 -0
  17. package/dist/runtime/components/MLAssetManager/AssetList.vue.d.ts +15 -0
  18. package/dist/runtime/components/MLAssetManager/AssetOverview.d.vue.ts +8 -0
  19. package/dist/runtime/components/MLAssetManager/AssetOverview.vue +321 -0
  20. package/dist/runtime/components/MLAssetManager/AssetOverview.vue.d.ts +8 -0
  21. package/dist/runtime/components/MLAssetManager/AssetPreview.d.vue.ts +10 -0
  22. package/dist/runtime/components/MLAssetManager/AssetPreview.vue +18 -0
  23. package/dist/runtime/components/MLAssetManager/AssetPreview.vue.d.ts +10 -0
  24. package/dist/runtime/components/MLAssetManager/AssetPreviewAudio.d.vue.ts +9 -0
  25. package/dist/runtime/components/MLAssetManager/AssetPreviewAudio.vue +21 -0
  26. package/dist/runtime/components/MLAssetManager/AssetPreviewAudio.vue.d.ts +9 -0
  27. package/dist/runtime/components/MLAssetManager/AssetPreviewDocument.d.vue.ts +9 -0
  28. package/dist/runtime/components/MLAssetManager/AssetPreviewDocument.vue +39 -0
  29. package/dist/runtime/components/MLAssetManager/AssetPreviewDocument.vue.d.ts +9 -0
  30. package/dist/runtime/components/MLAssetManager/AssetPreviewFallback.d.vue.ts +7 -0
  31. package/dist/runtime/components/MLAssetManager/AssetPreviewFallback.vue +41 -0
  32. package/dist/runtime/components/MLAssetManager/AssetPreviewFallback.vue.d.ts +7 -0
  33. package/dist/runtime/components/MLAssetManager/AssetPreviewImage.d.vue.ts +9 -0
  34. package/dist/runtime/components/MLAssetManager/AssetPreviewImage.vue +30 -0
  35. package/dist/runtime/components/MLAssetManager/AssetPreviewImage.vue.d.ts +9 -0
  36. package/dist/runtime/components/MLAssetManager/AssetPreviewSidebar.d.vue.ts +15 -0
  37. package/dist/runtime/components/MLAssetManager/AssetPreviewSidebar.vue +69 -0
  38. package/dist/runtime/components/MLAssetManager/AssetPreviewSidebar.vue.d.ts +15 -0
  39. package/dist/runtime/components/MLAssetManager/AssetPreviewVideo.d.vue.ts +9 -0
  40. package/dist/runtime/components/MLAssetManager/AssetPreviewVideo.vue +148 -0
  41. package/dist/runtime/components/MLAssetManager/AssetPreviewVideo.vue.d.ts +9 -0
  42. package/dist/runtime/components/MLAssetManager/AssetRenditions.d.vue.ts +8 -0
  43. package/dist/runtime/components/MLAssetManager/AssetRenditions.vue +148 -0
  44. package/dist/runtime/components/MLAssetManager/AssetRenditions.vue.d.ts +8 -0
  45. package/dist/runtime/components/MLAssetManager/Processing.d.vue.ts +8 -0
  46. package/dist/runtime/components/MLAssetManager/Processing.vue +84 -0
  47. package/dist/runtime/components/MLAssetManager/Processing.vue.d.ts +8 -0
  48. package/dist/runtime/components/MLAssetManager/SlotCard.d.vue.ts +17 -0
  49. package/dist/runtime/components/MLAssetManager/SlotCard.vue +94 -0
  50. package/dist/runtime/components/MLAssetManager/SlotCard.vue.d.ts +17 -0
  51. package/dist/runtime/components/MLAssetManager/Widget.d.vue.ts +14 -0
  52. package/dist/runtime/components/MLAssetManager/Widget.vue +328 -0
  53. package/dist/runtime/components/MLAssetManager/Widget.vue.d.ts +14 -0
  54. package/dist/runtime/components/MLPreviewProvider.d.vue.ts +31 -0
  55. package/dist/runtime/components/MLPreviewProvider.vue +51 -0
  56. package/dist/runtime/components/MLPreviewProvider.vue.d.ts +31 -0
  57. package/dist/runtime/components/MLProcessing/Forms/ConfigureStepModal.d.vue.ts +13 -0
  58. package/dist/runtime/components/MLProcessing/Forms/ConfigureStepModal.vue +47 -0
  59. package/dist/runtime/components/MLProcessing/Forms/ConfigureStepModal.vue.d.ts +13 -0
  60. package/dist/runtime/components/MLProcessing/Forms/FramePicker.d.vue.ts +11 -0
  61. package/dist/runtime/components/MLProcessing/Forms/FramePicker.vue +162 -0
  62. package/dist/runtime/components/MLProcessing/Forms/FramePicker.vue.d.ts +11 -0
  63. package/dist/runtime/components/MLProcessing/Forms/StepOptionsForm.d.vue.ts +14 -0
  64. package/dist/runtime/components/MLProcessing/Forms/StepOptionsForm.vue +113 -0
  65. package/dist/runtime/components/MLProcessing/Forms/StepOptionsForm.vue.d.ts +14 -0
  66. package/dist/runtime/components/MLProcessing/Pipeline.d.vue.ts +15 -0
  67. package/dist/runtime/components/MLProcessing/Pipeline.vue +178 -0
  68. package/dist/runtime/components/MLProcessing/Pipeline.vue.d.ts +15 -0
  69. package/dist/runtime/components/MLProcessing/PipelineStepItem.d.vue.ts +20 -0
  70. package/dist/runtime/components/MLProcessing/PipelineStepItem.vue +309 -0
  71. package/dist/runtime/components/MLProcessing/PipelineStepItem.vue.d.ts +20 -0
  72. package/dist/runtime/components/MLProcessing/ProgressBar.d.vue.ts +9 -0
  73. package/dist/runtime/components/MLProcessing/ProgressBar.vue +27 -0
  74. package/dist/runtime/components/MLProcessing/ProgressBar.vue.d.ts +9 -0
  75. package/dist/runtime/components/MLProcessing/StepLayout.d.vue.ts +31 -0
  76. package/dist/runtime/components/MLProcessing/StepLayout.vue +36 -0
  77. package/dist/runtime/components/MLProcessing/StepLayout.vue.d.ts +31 -0
  78. package/dist/runtime/components/MLProcessing/Stepper.d.vue.ts +16 -0
  79. package/dist/runtime/components/MLProcessing/Stepper.vue +87 -0
  80. package/dist/runtime/components/MLProcessing/Stepper.vue.d.ts +16 -0
  81. package/dist/runtime/components/MLProcessing/StepperItem.d.vue.ts +16 -0
  82. package/dist/runtime/components/MLProcessing/StepperItem.vue +150 -0
  83. package/dist/runtime/components/MLProcessing/StepperItem.vue.d.ts +16 -0
  84. package/dist/runtime/components/MLProcessing/TopBar.d.vue.ts +21 -0
  85. package/dist/runtime/components/MLProcessing/TopBar.vue +46 -0
  86. package/dist/runtime/components/MLProcessing/TopBar.vue.d.ts +21 -0
  87. package/dist/runtime/components/MLUploader/ButtonUploader.d.vue.ts +55 -0
  88. package/dist/runtime/components/MLUploader/ButtonUploader.vue +211 -0
  89. package/dist/runtime/components/MLUploader/ButtonUploader.vue.d.ts +55 -0
  90. package/dist/runtime/components/MLUploader/Item.d.vue.ts +34 -0
  91. package/dist/runtime/components/MLUploader/Item.vue +242 -0
  92. package/dist/runtime/components/MLUploader/Item.vue.d.ts +34 -0
  93. package/dist/runtime/components/MLUploader/PolicyPopover.d.vue.ts +7 -0
  94. package/dist/runtime/components/MLUploader/PolicyPopover.vue +160 -0
  95. package/dist/runtime/components/MLUploader/PolicyPopover.vue.d.ts +7 -0
  96. package/dist/runtime/components/MLUploader/Widget.d.vue.ts +44 -0
  97. package/dist/runtime/components/MLUploader/Widget.vue +251 -0
  98. package/dist/runtime/components/MLUploader/Widget.vue.d.ts +44 -0
  99. package/dist/runtime/composables/resourcePolicy/mimeUtils.d.ts +4 -0
  100. package/dist/runtime/composables/resourcePolicy/mimeUtils.js +71 -0
  101. package/dist/runtime/composables/resourcePolicy/useMLResourcePolicy.d.ts +17 -0
  102. package/dist/runtime/composables/resourcePolicy/useMLResourcePolicy.js +150 -0
  103. package/dist/runtime/composables/useElapsedTimer.d.ts +13 -0
  104. package/dist/runtime/composables/useElapsedTimer.js +46 -0
  105. package/dist/runtime/composables/useMLAssetLibrary.d.ts +31 -0
  106. package/dist/runtime/composables/useMLAssetLibrary.js +53 -0
  107. package/dist/runtime/composables/useMLAssetManager.d.ts +30 -0
  108. package/dist/runtime/composables/useMLAssetManager.js +229 -0
  109. package/dist/runtime/composables/useMLAssetManagerPreview.d.ts +28 -0
  110. package/dist/runtime/composables/useMLAssetManagerPreview.js +232 -0
  111. package/dist/runtime/composables/useMLAssetPreview.d.ts +23 -0
  112. package/dist/runtime/composables/useMLAssetPreview.js +153 -0
  113. package/dist/runtime/composables/useMLChunkUploader.d.ts +9 -0
  114. package/dist/runtime/composables/useMLChunkUploader.js +22 -0
  115. package/dist/runtime/composables/useMLProcessingPipeline.d.ts +38 -0
  116. package/dist/runtime/composables/useMLProcessingPipeline.js +258 -0
  117. package/dist/runtime/composables/useMLProcessingStatus.d.ts +34 -0
  118. package/dist/runtime/composables/useMLProcessingStatus.js +100 -0
  119. package/dist/runtime/composables/useMLProcessingStepper.d.ts +32 -0
  120. package/dist/runtime/composables/useMLProcessingStepper.js +280 -0
  121. package/dist/runtime/composables/useMLProcessingStrategies.d.ts +7 -0
  122. package/dist/runtime/composables/useMLProcessingStrategies.js +47 -0
  123. package/dist/runtime/composables/useMLRepeater.d.ts +5 -0
  124. package/dist/runtime/composables/useMLRepeater.js +11 -0
  125. package/dist/runtime/css/index.css +1 -0
  126. package/dist/runtime/index.d.ts +13 -0
  127. package/dist/runtime/index.js +13 -0
  128. package/dist/runtime/plugins/asset-direcetive-client.d.ts +2 -0
  129. package/dist/runtime/plugins/asset-direcetive-client.js +64 -0
  130. package/dist/runtime/plugins/config.d.ts +13 -0
  131. package/dist/runtime/plugins/config.js +23 -0
  132. package/dist/runtime/schemas/resource-policy.schema.d.ts +607 -0
  133. package/dist/runtime/schemas/resource-policy.schema.js +117 -0
  134. package/dist/runtime/server/assets-proxy.d.ts +2 -0
  135. package/dist/runtime/server/assets-proxy.js +81 -0
  136. package/dist/runtime/server/create-ml-proxy.d.ts +47 -0
  137. package/dist/runtime/server/create-ml-proxy.js +232 -0
  138. package/dist/runtime/server/delivery-proxy.d.ts +2 -0
  139. package/dist/runtime/server/delivery-proxy.js +74 -0
  140. package/dist/runtime/server/index.d.ts +2 -0
  141. package/dist/runtime/server/index.js +1 -0
  142. package/dist/runtime/server/processing-proxy.d.ts +2 -0
  143. package/dist/runtime/server/processing-proxy.js +55 -0
  144. package/dist/runtime/server/resource-policy-proxy.d.ts +2 -0
  145. package/dist/runtime/server/resource-policy-proxy.js +80 -0
  146. package/dist/runtime/server/tsconfig.json +0 -0
  147. package/dist/runtime/server/uploads-proxy.d.ts +2 -0
  148. package/dist/runtime/server/uploads-proxy.js +99 -0
  149. package/dist/runtime/types/asset.d.ts +174 -0
  150. package/dist/runtime/types/asset.js +7 -0
  151. package/dist/runtime/types/preview.d.ts +20 -0
  152. package/dist/runtime/types/preview.js +0 -0
  153. package/dist/runtime/types/resourcePolicy.d.ts +77 -0
  154. package/dist/runtime/types/resourcePolicy.js +25 -0
  155. package/dist/runtime/types/uploader.d.ts +71 -0
  156. package/dist/runtime/types/uploader.js +0 -0
  157. package/dist/runtime/utils/uploader/checksum.d.ts +21 -0
  158. package/dist/runtime/utils/uploader/checksum.js +23 -0
  159. package/dist/runtime/utils/uploader/chunking.d.ts +8 -0
  160. package/dist/runtime/utils/uploader/chunking.js +21 -0
  161. package/dist/runtime/utils/uploader/uploader.d.ts +20 -0
  162. package/dist/runtime/utils/uploader/uploader.js +206 -0
  163. package/dist/types.d.mts +11 -0
  164. package/package.json +81 -0
@@ -0,0 +1,84 @@
1
+ <script setup>
2
+ import { computed, ref, onMounted, watch } from "vue";
3
+ import { useMLResourcePolicy } from "../../composables/resourcePolicy/useMLResourcePolicy";
4
+ const props = defineProps({
5
+ assetId: { type: String, required: true },
6
+ manager: { type: Object, required: true }
7
+ });
8
+ const resourcePolicy = useMLResourcePolicy();
9
+ const policy = ref(null);
10
+ const policyLoading = ref(false);
11
+ const videoUrl = ref(null);
12
+ const asset = computed(() => props.manager.selectedAsset.value);
13
+ const policyId = computed(
14
+ () => asset.value?.resourcePolicyId ?? asset.value?.resource_policy_id ?? null
15
+ );
16
+ async function loadPolicy() {
17
+ if (!policyId.value) {
18
+ policy.value = null;
19
+ return;
20
+ }
21
+ policyLoading.value = true;
22
+ try {
23
+ policy.value = await resourcePolicy.get(policyId.value);
24
+ } catch {
25
+ policy.value = null;
26
+ } finally {
27
+ policyLoading.value = false;
28
+ }
29
+ }
30
+ async function resolveVideoUrl() {
31
+ if (!asset.value || asset.value.kind !== "video") {
32
+ videoUrl.value = null;
33
+ return;
34
+ }
35
+ try {
36
+ const result = await $fetch(
37
+ `/api/media-library/delivery/assets/${encodeURIComponent(props.assetId)}`
38
+ );
39
+ videoUrl.value = result.downloadUrl ?? null;
40
+ } catch {
41
+ videoUrl.value = null;
42
+ }
43
+ }
44
+ onMounted(() => {
45
+ loadPolicy();
46
+ resolveVideoUrl();
47
+ });
48
+ watch(policyId, loadPolicy);
49
+ watch(() => props.assetId, resolveVideoUrl);
50
+ const showStepper = computed(() => {
51
+ if (policyLoading.value) return false;
52
+ if (!policy.value) return !policyId.value;
53
+ return policy.value.processing_config?.auto_process === false;
54
+ });
55
+ function onComplete() {
56
+ props.manager.refresh(props.assetId);
57
+ }
58
+ function onFailed() {
59
+ props.manager.refresh(props.assetId);
60
+ }
61
+ </script>
62
+
63
+ <template>
64
+ <div>
65
+ <USkeleton v-if="policyLoading" class="h-40 w-full" />
66
+
67
+ <MLProcessingStepper
68
+ v-else-if="showStepper"
69
+ :asset-id="assetId"
70
+ :asset-kind="asset?.kind"
71
+ :download-url="videoUrl"
72
+ @complete="onComplete"
73
+ @failed="onFailed"
74
+ />
75
+
76
+ <MLProcessingPipeline
77
+ v-else
78
+ :asset-id="assetId"
79
+ :asset-status="asset?.status"
80
+ @complete="onComplete"
81
+ @failed="onFailed"
82
+ />
83
+ </div>
84
+ </template>
@@ -0,0 +1,8 @@
1
+ import type { UseMLAssetManagerReturn } from "../../index.js";
2
+ type __VLS_Props = {
3
+ assetId: string;
4
+ manager: UseMLAssetManagerReturn;
5
+ };
6
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -0,0 +1,17 @@
1
+ import type { AssetSlot, AssetDetail } from "../../index.js";
2
+ import type { ProcessingStream } from "../../composables/useMLProcessingStatus.js";
3
+ type __VLS_Props = {
4
+ item: AssetSlot;
5
+ asset: AssetDetail | null;
6
+ selected: boolean;
7
+ processingStream?: ProcessingStream;
8
+ /** Total assets in slot (multi-mode). Defaults derived from item. */
9
+ count?: number;
10
+ };
11
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
12
+ click: () => any;
13
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
14
+ onClick?: (() => any) | undefined;
15
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
@@ -0,0 +1,94 @@
1
+ <script setup>
2
+ import { computed } from "vue";
3
+ import { getSlotAssetIds, isMultiSlot } from "../../types/asset";
4
+ const props = defineProps({
5
+ item: { type: Object, required: true },
6
+ asset: { type: [Object, null], required: true },
7
+ selected: { type: Boolean, required: true },
8
+ processingStream: { type: Object, required: false },
9
+ count: { type: Number, required: false }
10
+ });
11
+ const emit = defineEmits(["click"]);
12
+ const isMulti = computed(() => isMultiSlot(props.item));
13
+ const slotCount = computed(() => props.count ?? getSlotAssetIds(props.item).length);
14
+ const hasAsset = computed(() => slotCount.value > 0 && !!props.asset);
15
+ const isProcessing = computed(() => !!props.processingStream?.isProcessing.value);
16
+ const processingProgress = computed(() => props.processingStream?.progress.value ?? 0);
17
+ const statusColor = computed(() => {
18
+ if (!hasAsset.value) return "neutral";
19
+ const map = {
20
+ ready: "success",
21
+ processing: "warning",
22
+ failed: "error",
23
+ uploaded: "info",
24
+ pending: "neutral"
25
+ };
26
+ return map[props.asset.status] ?? "neutral";
27
+ });
28
+ const statusLabel = computed(() => {
29
+ if (!hasAsset.value) return "EMPTY";
30
+ if (isMulti.value) return `${slotCount.value} ASSET${slotCount.value === 1 ? "" : "S"}`;
31
+ const map = {
32
+ ready: "READY",
33
+ processing: "PROC",
34
+ failed: "FAIL",
35
+ uploaded: "UPLOADED",
36
+ pending: "PENDING"
37
+ };
38
+ return map[props.asset.status] ?? props.asset.status?.toUpperCase();
39
+ });
40
+ const icon = computed(() => {
41
+ if (!hasAsset.value) return "i-lucide-upload";
42
+ const map = {
43
+ IMAGE: "i-lucide-image",
44
+ VIDEO: "i-lucide-film",
45
+ AUDIO: "i-lucide-music",
46
+ DOCUMENT: "i-lucide-file-text",
47
+ ARCHIVE: "i-lucide-archive"
48
+ };
49
+ return map[props.asset.kind?.toUpperCase()] ?? "i-lucide-file";
50
+ });
51
+ </script>
52
+
53
+ <template>
54
+ <div
55
+ class="flex items-center px-3 gap-3 cursor-pointer transition-colors"
56
+ :class="[
57
+ selected ? 'bg-elevated/50 border-l-3 border-primary' : 'hover:bg-elevated/50 border-l-3 border-transparent',
58
+ isProcessing ? 'py-2' : 'h-16'
59
+ ]"
60
+ @click="emit('click')"
61
+ >
62
+ <!-- Icon -->
63
+ <div class="w-10 h-10 rounded-lg shrink-0 flex items-center justify-center overflow-hidden relative"
64
+ :class="hasAsset ? 'bg-muted' : 'bg-muted/50 border border-dashed border-default'"
65
+ >
66
+ <UIcon :name="icon" class="text-muted text-lg" />
67
+ <div v-if="isProcessing" class="absolute top-0.5 right-0.5 w-2 h-2 rounded-full bg-warning animate-pulse" />
68
+ </div>
69
+
70
+ <!-- Info -->
71
+ <div class="flex flex-col min-w-0 flex-1">
72
+ <span class="text-sm font-bold truncate text-default">{{ item.label }}</span>
73
+ <div class="flex items-center gap-1 mt-0.5">
74
+ <UBadge :color="statusColor" :variant="isProcessing ? 'solid' : 'subtle'" size="sm">
75
+ {{ statusLabel }}
76
+ </UBadge>
77
+ </div>
78
+ <UProgress
79
+ v-if="isProcessing"
80
+ :model-value="processingProgress"
81
+ size="2xs"
82
+ color="warning"
83
+ class="mt-1"
84
+ />
85
+ <span v-else-if="hasAsset && isMulti && slotCount > 1" class="text-[10px] text-muted mt-0.5 truncate">
86
+ {{ asset.original_filename || asset.id.slice(0, 12) }} + {{ slotCount - 1 }} more
87
+ </span>
88
+ <span v-else-if="hasAsset" class="text-[10px] text-muted mt-0.5 truncate">
89
+ {{ asset.original_filename || asset.id.slice(0, 12) }}
90
+ </span>
91
+ <span v-else class="text-[10px] text-muted mt-0.5">Click to upload</span>
92
+ </div>
93
+ </div>
94
+ </template>
@@ -0,0 +1,17 @@
1
+ import type { AssetSlot, AssetDetail } from "../../index.js";
2
+ import type { ProcessingStream } from "../../composables/useMLProcessingStatus.js";
3
+ type __VLS_Props = {
4
+ item: AssetSlot;
5
+ asset: AssetDetail | null;
6
+ selected: boolean;
7
+ processingStream?: ProcessingStream;
8
+ /** Total assets in slot (multi-mode). Defaults derived from item. */
9
+ count?: number;
10
+ };
11
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
12
+ click: () => any;
13
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
14
+ onClick?: (() => any) | undefined;
15
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
@@ -0,0 +1,14 @@
1
+ import type { AssetSlot } from "../../index.js";
2
+ type __VLS_Props = {
3
+ /** Browse mode: list of asset IDs */
4
+ assetIds?: string[];
5
+ /** Slot mode: structured items with labels and resource policies */
6
+ items?: AssetSlot[];
7
+ };
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
+ "update:items": (items: AssetSlot[]) => any;
10
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
11
+ "onUpdate:items"?: ((items: AssetSlot[]) => any) | undefined;
12
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
+ declare const _default: typeof __VLS_export;
14
+ export default _default;
@@ -0,0 +1,328 @@
1
+ <script setup>
2
+ import { computed, reactive, ref, watch } from "vue";
3
+ import { useMLAssetManager } from "../../index";
4
+ import { getSlotAssetIds, isMultiSlot } from "../../types/asset";
5
+ const props = defineProps({
6
+ assetIds: { type: Array, required: false },
7
+ items: { type: Array, required: false }
8
+ });
9
+ const emit = defineEmits(["update:items"]);
10
+ const isSlotMode = computed(() => !!props.items);
11
+ const selectedSlotIndex = ref(null);
12
+ const selectedSlot = computed(() => {
13
+ if (!isSlotMode.value || selectedSlotIndex.value === null) return null;
14
+ return props.items[selectedSlotIndex.value] ?? null;
15
+ });
16
+ const resolvedAssetIds = computed(() => {
17
+ if (props.items) {
18
+ return props.items.flatMap((s) => getSlotAssetIds(s));
19
+ }
20
+ return props.assetIds ?? [];
21
+ });
22
+ const manager = useMLAssetManager(resolvedAssetIds);
23
+ const slotAddMode = ref(false);
24
+ const selectedSlotIds = computed(
25
+ () => selectedSlot.value ? getSlotAssetIds(selectedSlot.value) : []
26
+ );
27
+ const selectedSlotIsMulti = computed(
28
+ () => selectedSlot.value ? isMultiSlot(selectedSlot.value) : false
29
+ );
30
+ function selectSlot(index) {
31
+ const slot = props.items?.[index];
32
+ if (!slot) return;
33
+ selectedSlotIndex.value = index;
34
+ slotAddMode.value = false;
35
+ const ids = getSlotAssetIds(slot);
36
+ if (ids.length && ids[0]) {
37
+ manager.select(ids[0]);
38
+ } else {
39
+ manager.select(null);
40
+ if (isMultiSlot(slot)) slotAddMode.value = true;
41
+ }
42
+ }
43
+ function selectAsset(assetId) {
44
+ manager.select(assetId);
45
+ }
46
+ function selectAssetInSlot(assetId) {
47
+ slotAddMode.value = false;
48
+ manager.select(assetId);
49
+ }
50
+ function startAddInSlot() {
51
+ slotAddMode.value = true;
52
+ manager.select(null);
53
+ const idx = selectedSlotIndex.value;
54
+ if (idx !== null) {
55
+ uploaderModels[idx] = [];
56
+ uploaderRefs[idx]?.reset?.();
57
+ }
58
+ }
59
+ watch(
60
+ isSlotMode,
61
+ (val) => {
62
+ if (val && props.items?.length && selectedSlotIndex.value === null) {
63
+ selectSlot(0);
64
+ }
65
+ },
66
+ { immediate: true }
67
+ );
68
+ async function onSlotUploaded(slotIndex, uploadedIds) {
69
+ if (!props.items) return;
70
+ const current = props.items[slotIndex];
71
+ if (!current) return;
72
+ const updated = [...props.items];
73
+ if (isMultiSlot(current)) {
74
+ const existing = current.assetIds ?? [];
75
+ const next = [...existing, ...uploadedIds.filter((id) => id && !existing.includes(id))];
76
+ updated[slotIndex] = { ...current, assetIds: next };
77
+ } else {
78
+ const assetId = uploadedIds[0] || "";
79
+ if (!assetId) return;
80
+ updated[slotIndex] = {
81
+ label: current.label,
82
+ resourceType: current.resourceType,
83
+ collectionName: current.collectionName ?? null,
84
+ assetId
85
+ };
86
+ }
87
+ emit("update:items", updated);
88
+ for (const id of uploadedIds) {
89
+ if (id) await manager.refresh(id);
90
+ }
91
+ const first = uploadedIds[0];
92
+ const uploaderRef = uploaderRefs[slotIndex];
93
+ const stillBusy = uploaderRef?.isBusy?.() ?? false;
94
+ if (first && selectedSlotIndex.value === slotIndex && !stillBusy) {
95
+ slotAddMode.value = false;
96
+ manager.select(first);
97
+ }
98
+ }
99
+ function onSlotAssetDeleted(assetId) {
100
+ if (selectedSlotIndex.value === null || !props.items) return;
101
+ const updated = [...props.items];
102
+ const current = updated[selectedSlotIndex.value];
103
+ if (!current) return;
104
+ if (isMultiSlot(current)) {
105
+ const remaining = (current.assetIds ?? []).filter((id) => id !== assetId);
106
+ updated[selectedSlotIndex.value] = { ...current, assetIds: remaining };
107
+ emit("update:items", updated);
108
+ if (remaining.length && remaining[0]) {
109
+ manager.select(remaining[0]);
110
+ } else {
111
+ manager.select(null);
112
+ slotAddMode.value = true;
113
+ }
114
+ } else if (current.assetId === assetId) {
115
+ updated[selectedSlotIndex.value] = {
116
+ label: current.label,
117
+ resourceType: current.resourceType,
118
+ collectionName: current.collectionName ?? null,
119
+ assetId: ""
120
+ };
121
+ emit("update:items", updated);
122
+ }
123
+ }
124
+ const search = ref("");
125
+ const filteredAssets = computed(() => {
126
+ const all = Array.from(manager.assets.value.values());
127
+ if (!search.value) return all;
128
+ const q = search.value.toLowerCase();
129
+ return all.filter((a) => {
130
+ const name = (a.original_filename || a.title || a.id).toLowerCase();
131
+ return name.includes(q);
132
+ });
133
+ });
134
+ const showUploader = computed(() => {
135
+ if (!isSlotMode.value || !selectedSlot.value) return false;
136
+ if (isMultiSlot(selectedSlot.value)) return slotAddMode.value;
137
+ return !selectedSlot.value.assetId;
138
+ });
139
+ const uploaderModels = reactive({});
140
+ const uploaderRefs = reactive({});
141
+ const visitedUploaderSlots = ref([]);
142
+ function ensureUploaderForCurrentSlot() {
143
+ const idx = selectedSlotIndex.value;
144
+ if (idx === null) return;
145
+ if (!visitedUploaderSlots.value.includes(idx)) {
146
+ visitedUploaderSlots.value.push(idx);
147
+ }
148
+ if (!(idx in uploaderModels)) uploaderModels[idx] = [];
149
+ }
150
+ watch(
151
+ showUploader,
152
+ (v) => {
153
+ if (v) ensureUploaderForCurrentSlot();
154
+ },
155
+ { immediate: true }
156
+ );
157
+ function handleUploaderUpdate(slotIndex, ids) {
158
+ uploaderModels[slotIndex] = ids;
159
+ if (ids.length) onSlotUploaded(slotIndex, ids);
160
+ }
161
+ </script>
162
+
163
+ <template>
164
+ <div class="w-full h-full flex gap-4">
165
+ <!-- Left card: List -->
166
+ <aside
167
+ class="w-72 shrink-0 bg-default border border-default rounded-lg flex flex-col"
168
+ >
169
+ <header
170
+ class="h-10 flex items-center justify-between px-4 border-b border-default shrink-0"
171
+ >
172
+ <span class="text-sm font-bold tracking-tight text-default">
173
+ {{ isSlotMode ? "Slots" : "Assets" }}
174
+ </span>
175
+ <UButton
176
+ icon="i-lucide-refresh-cw"
177
+ variant="ghost"
178
+ color="neutral"
179
+ size="xs"
180
+ @click="manager.refresh()"
181
+ />
182
+ </header>
183
+
184
+ <!-- Search only in browse mode -->
185
+ <div v-if="!isSlotMode" class="p-2 border-b border-default shrink-0">
186
+ <UInput
187
+ v-model="search"
188
+ icon="i-lucide-search"
189
+ placeholder="Search assets..."
190
+ size="sm"
191
+ class="w-full"
192
+ />
193
+ </div>
194
+
195
+ <div class="flex-1 overflow-x-hidden">
196
+ <!-- Slot mode sidebar -->
197
+ <template v-if="isSlotMode && items">
198
+ <MLAssetManagerSlotCard
199
+ v-for="(slot, idx) in items"
200
+ :key="`${slot.label}-${idx}`"
201
+ :item="slot"
202
+ :asset="
203
+ getSlotAssetIds(slot).length ? manager.assets.value.get(getSlotAssetIds(slot)[0]) ?? null : null
204
+ "
205
+ :count="getSlotAssetIds(slot).length"
206
+ :selected="selectedSlotIndex === idx"
207
+ :processing-stream="
208
+ getSlotAssetIds(slot).map((id2) => manager.processingStreams.get(id2)).find((s) => s)
209
+ "
210
+ @click="selectSlot(idx)"
211
+ />
212
+ </template>
213
+
214
+ <!-- Browse mode sidebar -->
215
+ <template v-else>
216
+ <MLAssetManagerAssetList
217
+ :assets="filteredAssets"
218
+ :selected-asset-id="manager.selectedAssetId.value"
219
+ :loading="manager.loading.value"
220
+ :processing-streams="manager.processingStreams"
221
+ @select="selectAsset"
222
+ />
223
+ </template>
224
+ </div>
225
+ </aside>
226
+
227
+ <!-- Right card: Detail / Uploader -->
228
+ <main
229
+ class="flex-1 min-w-0 bg-default border border-default rounded-lg flex flex-col"
230
+ >
231
+ <!-- Multi-slot thumbnail strip -->
232
+ <div
233
+ v-if="
234
+ isSlotMode && selectedSlot && selectedSlotIsMulti && selectedSlotIds.length
235
+ "
236
+ class="flex items-center gap-2 px-3 py-2 border-b border-default shrink-0 overflow-x-auto"
237
+ >
238
+ <button
239
+ v-for="id in selectedSlotIds"
240
+ :key="id"
241
+ type="button"
242
+ class="w-14 h-14 shrink-0 rounded-md border bg-elevated/40 overflow-hidden flex items-center justify-center transition relative"
243
+ :class="
244
+ !slotAddMode && manager.selectedAssetId.value === id ? 'border-primary ring-2 ring-primary' : 'border-default hover:border-muted'
245
+ "
246
+ :title="manager.assets.value.get(id)?.original_filename ?? id"
247
+ @click="selectAssetInSlot(id)"
248
+ >
249
+ <img
250
+ v-if="manager.assets.value.get(id)?.previewUrl"
251
+ :src="manager.assets.value.get(id).previewUrl"
252
+ :alt="manager.assets.value.get(id)?.original_filename ?? ''"
253
+ class="w-full h-full object-cover"
254
+ >
255
+ <UIcon v-else name="i-lucide-file" class="text-muted text-lg" />
256
+ <span
257
+ v-if="manager.processingStreams.get(id)?.isProcessing.value"
258
+ class="absolute top-1 right-1 w-2 h-2 rounded-full bg-warning animate-pulse"
259
+ />
260
+ </button>
261
+ <button
262
+ type="button"
263
+ class="w-14 h-14 shrink-0 rounded-md border-2 border-dashed flex flex-col items-center justify-center text-muted hover:text-default hover:border-default transition"
264
+ :class="
265
+ slotAddMode ? 'border-primary text-primary' : 'border-default'
266
+ "
267
+ title="Add more"
268
+ @click="startAddInSlot"
269
+ >
270
+ <UIcon name="i-lucide-plus" class="text-base" />
271
+ <span class="text-[9px] mt-0.5">Add</span>
272
+ </button>
273
+ </div>
274
+
275
+ <!-- Slot mode: persistent per-slot uploaders. Each visited slot keeps
276
+ its MLUploaderWidget mounted so in-flight uploads survive when the
277
+ user switches slots or views an asset detail. -->
278
+ <div
279
+ v-for="idx in visitedUploaderSlots"
280
+ v-show="showUploader && selectedSlotIndex === idx"
281
+ :key="`slot-uploader-${idx}`"
282
+ class="flex-1 overflow-y-auto p-6"
283
+ >
284
+ <div v-if="props.items?.[idx]" class="mb-4">
285
+ <h3 class="text-sm font-semibold text-default">
286
+ {{ props.items[idx].label }}
287
+ </h3>
288
+ <p class="text-xs text-muted mt-1">
289
+ {{
290
+ isMultiSlot(props.items[idx]) && (props.items[idx].assetIds?.length ?? 0) ? "Upload more files for this slot" : "Upload a file for this slot"
291
+ }}
292
+ </p>
293
+ </div>
294
+ <MLUploaderWidget
295
+ v-if="props.items?.[idx]"
296
+ :ref="(el) => {
297
+ uploaderRefs[idx] = el;
298
+ }"
299
+ :model-value="uploaderModels[idx] || []"
300
+ :resource-type="props.items[idx].resourceType"
301
+ :collection-name="props.items[idx].collectionName"
302
+ @update:model-value="(v) => handleUploaderUpdate(idx, v)"
303
+ />
304
+ </div>
305
+
306
+ <!-- Asset detail -->
307
+ <div v-if="!showUploader && manager.selectedAsset.value" class="flex-1 min-h-0">
308
+ <MLAssetManagerAssetDetail
309
+ :asset="manager.selectedAsset.value"
310
+ :manager="manager"
311
+ :allow-reupload="isSlotMode"
312
+ @deleted="onSlotAssetDeleted"
313
+ />
314
+ </div>
315
+
316
+ <!-- Empty state -->
317
+ <div v-else-if="!showUploader" class="flex-1 min-h-0 flex items-center justify-center">
318
+ <UEmpty
319
+ icon="i-lucide-mouse-pointer-click"
320
+ :title="isSlotMode ? 'Select a slot' : 'Select an asset'"
321
+ :description="
322
+ isSlotMode ? 'Choose a slot from the list to upload or view details.' : 'Choose an asset from the list to view its details.'
323
+ "
324
+ />
325
+ </div>
326
+ </main>
327
+ </div>
328
+ </template>
@@ -0,0 +1,14 @@
1
+ import type { AssetSlot } from "../../index.js";
2
+ type __VLS_Props = {
3
+ /** Browse mode: list of asset IDs */
4
+ assetIds?: string[];
5
+ /** Slot mode: structured items with labels and resource policies */
6
+ items?: AssetSlot[];
7
+ };
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
+ "update:items": (items: AssetSlot[]) => any;
10
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
11
+ "onUpdate:items"?: ((items: AssetSlot[]) => any) | undefined;
12
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
+ declare const _default: typeof __VLS_export;
14
+ export default _default;
@@ -0,0 +1,31 @@
1
+ type __VLS_Props = {
2
+ assetId?: string | null;
3
+ fallbackSrc?: string | null;
4
+ autoRegister?: boolean;
5
+ };
6
+ declare var __VLS_1: {
7
+ asset: import("../types/preview.js").AssetPreview | undefined;
8
+ src: string | null;
9
+ broken: boolean;
10
+ forbidden: boolean;
11
+ loading: boolean;
12
+ renditions: import("..").DeliveryRendition[];
13
+ sources: import("../types/preview.js").AssetSource[];
14
+ thumbnailUrl: string | null;
15
+ status: string | null;
16
+ refresh: () => Promise<void>;
17
+ };
18
+ type __VLS_Slots = {} & {
19
+ default?: (props: typeof __VLS_1) => any;
20
+ };
21
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
22
+ autoRegister: boolean;
23
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
24
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
25
+ declare const _default: typeof __VLS_export;
26
+ export default _default;
27
+ type __VLS_WithSlots<T, S> = T & {
28
+ new (): {
29
+ $slots: S;
30
+ };
31
+ };
@@ -0,0 +1,51 @@
1
+ <script setup>
2
+ import { computed, watch } from "vue";
3
+ import { useMLAssetPreview } from "../composables/useMLAssetPreview";
4
+ const props = defineProps({
5
+ assetId: { type: [String, null], required: false },
6
+ fallbackSrc: { type: [String, null], required: false },
7
+ autoRegister: { type: Boolean, required: false, default: true }
8
+ });
9
+ const assetsStore = useMLAssetPreview();
10
+ const asset = computed(
11
+ () => props.assetId ? assetsStore.getAsset(props.assetId) : void 0
12
+ );
13
+ const src = computed(() => {
14
+ if (asset.value?.url) return asset.value.url;
15
+ return props.fallbackSrc ?? null;
16
+ });
17
+ const broken = computed(() => assetsStore.isBroken(props.assetId));
18
+ const forbidden = computed(() => assetsStore.isForbidden(props.assetId));
19
+ const loading = computed(() => assetsStore.loading.value);
20
+ const renditions = computed(() => asset.value?.renditions ?? []);
21
+ const sources = computed(() => asset.value?.sources ?? []);
22
+ const thumbnailUrl = computed(() => sources.value.find((s) => s.renditionType === "thumbnail")?.url ?? null);
23
+ const status = computed(() => asset.value?.metadata?.status ?? null);
24
+ const refresh = async () => {
25
+ if (!props.assetId || import.meta.server) return;
26
+ await assetsStore.ensureAssets([props.assetId]);
27
+ };
28
+ watch(
29
+ () => props.assetId,
30
+ (id) => {
31
+ if (!props.autoRegister || !id || import.meta.server) return;
32
+ assetsStore.register(id);
33
+ },
34
+ { immediate: true }
35
+ );
36
+ </script>
37
+
38
+ <template>
39
+ <slot
40
+ :asset="asset"
41
+ :src="src"
42
+ :broken="broken"
43
+ :forbidden="forbidden"
44
+ :loading="loading"
45
+ :renditions="renditions"
46
+ :sources="sources"
47
+ :thumbnailUrl="thumbnailUrl"
48
+ :status="status"
49
+ :refresh="refresh"
50
+ />
51
+ </template>