mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-07 06:00:03 +00:00
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:
@@ -56,7 +56,8 @@ describe('formatUtil', () => {
|
||||
{ filename: 'image.jpeg', expected: 'image' },
|
||||
{ filename: 'animation.gif', expected: 'image' },
|
||||
{ filename: 'web.webp', expected: 'image' },
|
||||
{ filename: 'bitmap.bmp', expected: 'image' }
|
||||
{ filename: 'bitmap.bmp', expected: 'image' },
|
||||
{ filename: 'modern.avif', expected: 'image' }
|
||||
]
|
||||
|
||||
it.for(imageTestCases)(
|
||||
@@ -96,26 +97,37 @@ describe('formatUtil', () => {
|
||||
expect(getMediaTypeFromFilename('scene.fbx')).toBe('3D')
|
||||
expect(getMediaTypeFromFilename('asset.gltf')).toBe('3D')
|
||||
expect(getMediaTypeFromFilename('binary.glb')).toBe('3D')
|
||||
expect(getMediaTypeFromFilename('apple.usdz')).toBe('3D')
|
||||
})
|
||||
})
|
||||
|
||||
describe('text files', () => {
|
||||
it('should identify text file extensions correctly', () => {
|
||||
expect(getMediaTypeFromFilename('notes.txt')).toBe('text')
|
||||
expect(getMediaTypeFromFilename('readme.md')).toBe('text')
|
||||
expect(getMediaTypeFromFilename('data.json')).toBe('text')
|
||||
expect(getMediaTypeFromFilename('table.csv')).toBe('text')
|
||||
expect(getMediaTypeFromFilename('config.yaml')).toBe('text')
|
||||
})
|
||||
})
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should handle empty strings', () => {
|
||||
expect(getMediaTypeFromFilename('')).toBe('image')
|
||||
expect(getMediaTypeFromFilename('')).toBe('other')
|
||||
})
|
||||
|
||||
it('should handle files without extensions', () => {
|
||||
expect(getMediaTypeFromFilename('README')).toBe('image')
|
||||
expect(getMediaTypeFromFilename('README')).toBe('other')
|
||||
})
|
||||
|
||||
it('should handle unknown extensions', () => {
|
||||
expect(getMediaTypeFromFilename('document.pdf')).toBe('image')
|
||||
expect(getMediaTypeFromFilename('data.json')).toBe('image')
|
||||
expect(getMediaTypeFromFilename('document.pdf')).toBe('other')
|
||||
expect(getMediaTypeFromFilename('archive.bin')).toBe('other')
|
||||
})
|
||||
|
||||
it('should handle files with multiple dots', () => {
|
||||
expect(getMediaTypeFromFilename('my.file.name.png')).toBe('image')
|
||||
expect(getMediaTypeFromFilename('archive.tar.gz')).toBe('image')
|
||||
expect(getMediaTypeFromFilename('archive.tar.gz')).toBe('other')
|
||||
})
|
||||
|
||||
it('should handle paths with directories', () => {
|
||||
@@ -124,8 +136,8 @@ describe('formatUtil', () => {
|
||||
})
|
||||
|
||||
it('should handle null and undefined gracefully', () => {
|
||||
expect(getMediaTypeFromFilename(null)).toBe('image')
|
||||
expect(getMediaTypeFromFilename(undefined)).toBe('image')
|
||||
expect(getMediaTypeFromFilename(null)).toBe('other')
|
||||
expect(getMediaTypeFromFilename(undefined)).toBe('other')
|
||||
})
|
||||
|
||||
it('should handle special characters in filenames', () => {
|
||||
|
||||
@@ -494,19 +494,41 @@ export function formatDuration(milliseconds: number): string {
|
||||
return parts.join(' ')
|
||||
}
|
||||
|
||||
const IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp'] as const
|
||||
const IMAGE_EXTENSIONS = [
|
||||
'png',
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'gif',
|
||||
'webp',
|
||||
'bmp',
|
||||
'avif',
|
||||
'tif',
|
||||
'tiff'
|
||||
] as const
|
||||
const VIDEO_EXTENSIONS = ['mp4', 'webm', 'mov', 'avi'] as const
|
||||
const AUDIO_EXTENSIONS = ['mp3', 'wav', 'ogg', 'flac'] as const
|
||||
const THREE_D_EXTENSIONS = ['obj', 'fbx', 'gltf', 'glb'] as const
|
||||
const THREE_D_EXTENSIONS = ['obj', 'fbx', 'gltf', 'glb', 'usdz'] as const
|
||||
const TEXT_EXTENSIONS = [
|
||||
'txt',
|
||||
'md',
|
||||
'markdown',
|
||||
'json',
|
||||
'csv',
|
||||
'yaml',
|
||||
'yml',
|
||||
'xml',
|
||||
'log'
|
||||
] as const
|
||||
|
||||
const MEDIA_TYPES = ['image', 'video', 'audio', '3D'] as const
|
||||
type MediaType = (typeof MEDIA_TYPES)[number]
|
||||
const MEDIA_TYPES = ['image', 'video', 'audio', '3D', 'text', 'other'] as const
|
||||
export type MediaType = (typeof MEDIA_TYPES)[number]
|
||||
|
||||
// Type guard helper for checking array membership
|
||||
type ImageExtension = (typeof IMAGE_EXTENSIONS)[number]
|
||||
type VideoExtension = (typeof VIDEO_EXTENSIONS)[number]
|
||||
type AudioExtension = (typeof AUDIO_EXTENSIONS)[number]
|
||||
type ThreeDExtension = (typeof THREE_D_EXTENSIONS)[number]
|
||||
type TextExtension = (typeof TEXT_EXTENSIONS)[number]
|
||||
|
||||
/**
|
||||
* Truncates a filename while preserving the extension
|
||||
@@ -543,20 +565,21 @@ export function truncateFilename(
|
||||
/**
|
||||
* Determines the media type from a filename's extension (singular form)
|
||||
* @param filename The filename to analyze
|
||||
* @returns The media type: 'image', 'video', 'audio', or '3D'
|
||||
* @returns The media type: 'image', 'video', 'audio', '3D', 'text', or 'other'
|
||||
*/
|
||||
export function getMediaTypeFromFilename(
|
||||
filename: string | null | undefined
|
||||
): MediaType {
|
||||
if (!filename) return 'image'
|
||||
if (!filename) return 'other'
|
||||
const ext = filename.split('.').pop()?.toLowerCase()
|
||||
if (!ext) return 'image'
|
||||
if (!ext) return 'other'
|
||||
|
||||
// Type-safe array includes check using type assertion
|
||||
if (IMAGE_EXTENSIONS.includes(ext as ImageExtension)) return 'image'
|
||||
if (VIDEO_EXTENSIONS.includes(ext as VideoExtension)) return 'video'
|
||||
if (AUDIO_EXTENSIONS.includes(ext as AudioExtension)) return 'audio'
|
||||
if (THREE_D_EXTENSIONS.includes(ext as ThreeDExtension)) return '3D'
|
||||
if (TEXT_EXTENSIONS.includes(ext as TextExtension)) return 'text'
|
||||
|
||||
return 'image'
|
||||
return 'other'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user