diff --git a/src/components/sidebar/tabs/AssetsSidebarListView.test.ts b/src/components/sidebar/tabs/AssetsSidebarListView.test.ts index be6ead7e1..a99e23b62 100644 --- a/src/components/sidebar/tabs/AssetsSidebarListView.test.ts +++ b/src/components/sidebar/tabs/AssetsSidebarListView.test.ts @@ -2,8 +2,8 @@ import { mount } from '@vue/test-utils' import { defineComponent } from 'vue' import { describe, expect, it, vi } from 'vitest' -import type { AssetItem } from '@/platform/assets/schemas/assetSchema' import type { OutputStackListItem } from '@/platform/assets/composables/useOutputStacks' +import type { AssetItem } from '@/platform/assets/schemas/assetSchema' import AssetsSidebarListView from './AssetsSidebarListView.vue' @@ -72,4 +72,21 @@ describe('AssetsSidebarListView', () => { expect(wrapper.text()).not.toContain('sideToolbar.generatedAssetsHeader') }) + + it('marks mp4 assets as video previews', () => { + const videoAsset = { + ...buildAsset('video-asset', 'clip.mp4'), + preview_url: '/api/view/clip.mp4', + user_metadata: {} + } satisfies AssetItem + + const wrapper = mountListView([buildOutputItem(videoAsset)]) + + const listItems = wrapper.findAllComponents({ name: 'AssetsListItem' }) + const assetListItem = listItems.at(-1) + + expect(assetListItem).toBeDefined() + expect(assetListItem?.props('previewUrl')).toBe('/api/view/clip.mp4') + expect(assetListItem?.props('isVideoPreview')).toBe(true) + }) }) diff --git a/src/components/sidebar/tabs/AssetsSidebarListView.vue b/src/components/sidebar/tabs/AssetsSidebarListView.vue index b1fc52ac5..0f06ff7ca 100644 --- a/src/components/sidebar/tabs/AssetsSidebarListView.vue +++ b/src/components/sidebar/tabs/AssetsSidebarListView.vue @@ -34,7 +34,7 @@ :aria-label=" t('assetBrowser.ariaLabel.assetCard', { name: item.asset.name, - type: getMediaTypeFromFilename(item.asset.name) + type: getAssetMediaType(item.asset) }) " :class=" @@ -45,9 +45,8 @@ " :preview-url="item.asset.preview_url" :preview-alt="item.asset.name" - :icon-name=" - iconForMediaType(getMediaTypeFromFilename(item.asset.name)) - " + :icon-name="iconForMediaType(getAssetMediaType(item.asset))" + :is-video-preview="isVideoAsset(item.asset)" :primary-text="getAssetPrimaryText(item.asset)" :secondary-text="getAssetSecondaryText(item.asset)" :stack-count="getStackCount(item.asset)" @@ -135,6 +134,14 @@ function getAssetPrimaryText(asset: AssetItem): string { return truncateFilename(asset.name) } +function getAssetMediaType(asset: AssetItem) { + return getMediaTypeFromFilename(asset.name) +} + +function isVideoAsset(asset: AssetItem): boolean { + return getAssetMediaType(asset) === 'video' +} + function getAssetSecondaryText(asset: AssetItem): string { const metadata = getOutputAssetMetadata(asset.user_metadata) if (typeof metadata?.executionTimeInSeconds === 'number') { diff --git a/src/platform/assets/components/AssetsListItem.stories.ts b/src/platform/assets/components/AssetsListItem.stories.ts index ebf66fcd7..91cd7bc63 100644 --- a/src/platform/assets/components/AssetsListItem.stories.ts +++ b/src/platform/assets/components/AssetsListItem.stories.ts @@ -82,6 +82,7 @@ export const GeneratedVideo: Story = { args: { previewUrl: VIDEO_PREVIEW, previewAlt: 'clip-01.mp4', + isVideoPreview: true, primaryText: 'clip-01.mp4', secondaryText: '2m 12s' } diff --git a/src/platform/assets/components/AssetsListItem.test.ts b/src/platform/assets/components/AssetsListItem.test.ts new file mode 100644 index 000000000..aab7edc98 --- /dev/null +++ b/src/platform/assets/components/AssetsListItem.test.ts @@ -0,0 +1,38 @@ +import { mount } from '@vue/test-utils' +import { describe, expect, it } from 'vitest' + +import AssetsListItem from './AssetsListItem.vue' + +describe('AssetsListItem', () => { + it('renders video element with play overlay for video previews', () => { + const wrapper = mount(AssetsListItem, { + props: { + previewUrl: 'https://example.com/preview.mp4', + previewAlt: 'clip.mp4', + isVideoPreview: true + } + }) + + const video = wrapper.find('video') + expect(video.exists()).toBe(true) + expect(video.attributes('src')).toBe('https://example.com/preview.mp4') + expect(video.attributes('preload')).toBe('metadata') + expect(wrapper.find('img').exists()).toBe(false) + expect(wrapper.find('.bg-black\\/15').exists()).toBe(true) + expect(wrapper.find('.icon-\\[lucide--play\\]').exists()).toBe(true) + }) + + it('does not show play overlay for non-video previews', () => { + const wrapper = mount(AssetsListItem, { + props: { + previewUrl: 'https://example.com/preview.jpg', + previewAlt: 'image.png', + isVideoPreview: false + } + }) + + expect(wrapper.find('img').exists()).toBe(true) + expect(wrapper.find('video').exists()).toBe(false) + expect(wrapper.find('.icon-\\[lucide--play\\]').exists()).toBe(false) + }) +}) diff --git a/src/platform/assets/components/AssetsListItem.vue b/src/platform/assets/components/AssetsListItem.vue index d20317147..581be8e60 100644 --- a/src/platform/assets/components/AssetsListItem.vue +++ b/src/platform/assets/components/AssetsListItem.vue @@ -35,12 +35,24 @@ :icon-class="iconClass" :icon-aria-label="iconAriaLabel" > - +
+ + +