fix: support text and misc generated asset states (#8914)

## Summary
Align generated-asset state classification to a single shared source and
implement the missing text/misc states in both card and list previews.

## Changes
- **What**:
- Extended `getMediaTypeFromFilename` in
`packages/shared-frontend-utils` to return `text` and `other`, and
changed unknown/no-extension fallback from `image` to `other`.
- Added text extension handling (`txt`, `md`, `json`, `csv`, `yaml/yml`,
`xml`, `log`) and kept existing media kinds.
- Updated generated-assets UI to use shared media-type detection
directly (removed the local generated-assets classifier).
  - Added text and misc card preview components:
    - `text` -> `icon-[lucide--text]`
    - `other` -> `icon-[lucide--check-check]`
- Updated list-item preview behavior so only `image`/`video` use preview
media URLs; `text`/`other` use icon fallback.
- Widened media kind schema for asset display metadata to include `text`
and `other`.
- **Breaking**: No API breaking changes; internal media kind union
widened for frontend asset display paths.
- **Dependencies**: None.

## Review Focus
- Verify generated text assets render paragraph/text icon state in card
+ list.
- Verify unknown/misc assets consistently render double-check icon state
in card + list.
- Verify existing image/video/audio/3D behavior remains unchanged.

## Screenshots (if applicable)
<img width="282" height="158" alt="image"
src="https://github.com/user-attachments/assets/76cf2d1b-9d34-4c7c-92a1-50bbc55871e5"
/>
<img width="432" height="489" alt="image"
src="https://github.com/user-attachments/assets/024fece3-f241-484d-a37e-11948559ebbc"
/>
<img width="421" height="494" alt="image"
src="https://github.com/user-attachments/assets/ed64ba0c-bf46-4c3b-996e-4bc613ee029e"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8914-fix-support-text-and-misc-generated-asset-states-3096d73d365081f28ca7c32f306e4b50)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Johnpaul Chiwetelu <49923152+Myestery@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Benjamin Lu
2026-02-21 01:27:28 -08:00
committed by GitHub
parent ea7bbb744f
commit 9184f9bce4
13 changed files with 209 additions and 26 deletions

View File

@@ -112,6 +112,22 @@ const sampleAssets: AssetItem[] = [
created_at: baseTimestamp,
size: 134217728,
tags: []
},
{
id: 'asset-text-1',
name: 'generation-notes.txt',
created_at: baseTimestamp,
preview_url: '/assets/images/default-template.png',
size: 2048,
tags: []
},
{
id: 'asset-other-1',
name: 'workflow-payload.bin',
created_at: baseTimestamp,
preview_url: '/assets/images/default-template.png',
size: 4096,
tags: []
}
]
@@ -134,6 +150,16 @@ export const RunningAndGenerated: Story = {
render: renderAssetsSidebarListView
}
export const TextAndMiscGeneratedAssets: Story = {
args: {
assets: sampleAssets.filter((asset) =>
['.txt', '.bin'].some((suffix) => asset.name.endsWith(suffix))
),
jobs: []
},
render: renderAssetsSidebarListView
}
function renderAssetsSidebarListView(args: StoryArgs) {
return {
components: { AssetsSidebarListView },

View File

@@ -89,4 +89,21 @@ describe('AssetsSidebarListView', () => {
expect(assetListItem?.props('previewUrl')).toBe('/api/view/clip.mp4')
expect(assetListItem?.props('isVideoPreview')).toBe(true)
})
it('uses icon fallback for text assets even when preview_url exists', () => {
const textAsset = {
...buildAsset('text-asset', 'notes.txt'),
preview_url: '/api/view/notes.txt',
user_metadata: {}
} satisfies AssetItem
const wrapper = mountListView([buildOutputItem(textAsset)])
const listItems = wrapper.findAllComponents({ name: 'AssetsListItem' })
const assetListItem = listItems.at(-1)
expect(assetListItem).toBeDefined()
expect(assetListItem?.props('previewUrl')).toBe('')
expect(assetListItem?.props('isVideoPreview')).toBe(false)
})
})

View File

@@ -43,7 +43,7 @@
item.isChild && 'pl-6'
)
"
:preview-url="item.asset.preview_url"
:preview-url="getAssetPreviewUrl(item.asset)"
:preview-alt="item.asset.name"
:icon-name="iconForMediaType(getAssetMediaType(item.asset))"
:is-video-preview="isVideoAsset(item.asset)"
@@ -142,6 +142,14 @@ function isVideoAsset(asset: AssetItem): boolean {
return getAssetMediaType(asset) === 'video'
}
function getAssetPreviewUrl(asset: AssetItem): string {
const mediaType = getAssetMediaType(asset)
if (mediaType === 'image' || mediaType === 'video') {
return asset.preview_url || ''
}
return ''
}
function getAssetSecondaryText(asset: AssetItem): string {
const metadata = getOutputAssetMetadata(asset.user_metadata)
if (typeof metadata?.executionTimeInSeconds === 'number') {

View File

@@ -204,13 +204,22 @@ import {
} from '@vueuse/core'
import Divider from 'primevue/divider'
import { useToast } from 'primevue/usetoast'
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import {
computed,
defineAsyncComponent,
nextTick,
onMounted,
onUnmounted,
ref,
watch
} from 'vue'
import { useI18n } from 'vue-i18n'
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
// Lazy-loaded to avoid pulling THREE.js into the main bundle
const Load3dViewerContent = () =>
import('@/components/load3d/Load3dViewerContent.vue')
const Load3dViewerContent = defineAsyncComponent(
() => import('@/components/load3d/Load3dViewerContent.vue')
)
import AssetsSidebarGridView from '@/components/sidebar/tabs/AssetsSidebarGridView.vue'
import AssetsSidebarListView from '@/components/sidebar/tabs/AssetsSidebarListView.vue'
import SidebarTabTemplate from '@/components/sidebar/tabs/SidebarTabTemplate.vue'