From ca54877f9d854c2e7b40d4e3439cec1f4b68f84b Mon Sep 17 00:00:00 2001 From: Henry Lee Date: Sat, 9 May 2026 07:31:57 +1000 Subject: [PATCH] fix(assets): strip directory annotation from input filenames (#12086) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Imported assets render as a generic check-check icon instead of a thumbnail because the OSS `/internal/files/{type}` endpoint returns annotated filenames (`photo.png [input]`) that the assets-sidebar mapper passes through verbatim, which breaks extension-based media-type detection. ## Changes - **What**: Strip ComfyUI's trailing directory-type annotation (` [input]`, ` [output]`, `[temp]`) in `mapInputFileToAssetItem` so `name`, `id`, and the generated `/view?filename=…` URL all use the canonical on-disk filename. Adds a focused unit test. - **Breaking**: None. - **Dependencies**: None. ## Review Focus ### Root cause ComfyUI core PR [comfyanonymous/ComfyUI#13078](https://github.com/comfyanonymous/ComfyUI/pull/13078) (April 2026) changed `/internal/files/{type}` to append the directory type to each entry: ```python # api_server/routes/internal/internal_routes.py return web.json_response( [f"{entry.name} [{directory_type}]" for entry in sorted_files], status=200 ) ``` The annotation is the wire format `LoadImage`-style widgets expect, so the backend change is correct. The assets-sidebar mapper treated the response strings as raw filenames. After [#8914](https://github.com/Comfy-Org/ComfyUI_frontend/pull/8914) changed `getMediaTypeFromFilename` to default unknown extensions to `'other'`, every input asset now routes to `MediaOtherTop` and renders as `icon-[lucide--check-check]`: ``` getMediaTypeFromFilename("photo.png [input]").split('.').pop() === "png [input]" → 'other' ``` The strip happens at data ingestion so every consumer of `AssetItem.name` (sidebar grid, list, filter, gallery, drag-drop, delete) gets the canonical filename automatically. OSS-only — Cloud paths get clean names from the cloud API and are unaffected. Reproduces locally on stock OSS ComfyUI on `main` of both repos; no public issue tracker entry. ## Screenshots (if applicable) Before: image After: image ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12086-fix-assets-strip-directory-annotation-from-input-filenames-35a6d73d365081e9b9eed7d8630d6f0b) by [Unito](https://www.unito.io) --- .../composables/media/assetMappers.test.ts | 52 +++++++++++++++++++ .../assets/composables/media/assetMappers.ts | 20 +++++-- 2 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 src/platform/assets/composables/media/assetMappers.test.ts diff --git a/src/platform/assets/composables/media/assetMappers.test.ts b/src/platform/assets/composables/media/assetMappers.test.ts new file mode 100644 index 0000000000..a4667a234e --- /dev/null +++ b/src/platform/assets/composables/media/assetMappers.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it, vi } from 'vitest' + +import { mapInputFileToAssetItem } from './assetMappers' + +vi.mock('@/scripts/api', () => ({ + api: { + apiURL: (path: string) => `/api${path}` + } +})) + +vi.mock('@/platform/distribution/cloudPreviewUtil', () => ({ + appendCloudResParam: vi.fn() +})) + +describe('mapInputFileToAssetItem', () => { + it('preserves a clean filename', () => { + const asset = mapInputFileToAssetItem('photo.png', 0, 'input') + + expect(asset.name).toBe('photo.png') + expect(asset.id).toBe('input-0-photo.png') + expect(asset.preview_url).toBe('/api/view?filename=photo.png&type=input') + }) + + it.each([ + ['photo.png [input]', 'photo.png'], + ['photo.png [output]', 'photo.png'], + ['photo.png [temp]', 'photo.png'], + ['clip.mp4[input]', 'clip.mp4'], + ['MyFile.WEBP [Input]', 'MyFile.WEBP'] + ])('strips ComfyUI directory annotation: %s -> %s', (input, expectedName) => { + const asset = mapInputFileToAssetItem(input, 1, 'input') + + expect(asset.name).toBe(expectedName) + expect(asset.id).toBe(`input-1-${expectedName}`) + expect(asset.preview_url).toBe( + `/api/view?filename=${encodeURIComponent(expectedName)}&type=input` + ) + }) + + it('leaves non-annotation brackets in the filename intact', () => { + const asset = mapInputFileToAssetItem('my [draft] image.png', 0, 'input') + + expect(asset.name).toBe('my [draft] image.png') + }) + + it('uses the directory passed in for the type query param', () => { + const asset = mapInputFileToAssetItem('clip.mp4 [output]', 0, 'output') + + expect(asset.preview_url).toBe('/api/view?filename=clip.mp4&type=output') + expect(asset.tags).toEqual(['output']) + }) +}) diff --git a/src/platform/assets/composables/media/assetMappers.ts b/src/platform/assets/composables/media/assetMappers.ts index da468ca9b3..67544a0e87 100644 --- a/src/platform/assets/composables/media/assetMappers.ts +++ b/src/platform/assets/composables/media/assetMappers.ts @@ -51,6 +51,17 @@ export function mapTaskOutputToAssetItem( } } +/** + * Strips ComfyUI's trailing directory-type annotation (e.g. ` [input]`, + * ` [output]`, `[temp]`) from a filename returned by the OSS internal + * `/internal/files/{type}` endpoint. The annotation is part of the wire + * format LoadImage-style widgets expect, but for the assets sidebar we + * want the canonical on-disk filename so type detection / titles work. + */ +function stripDirectoryAnnotation(filename: string): string { + return filename.replace(/\s*\[(?:input|output|temp)\]\s*$/i, '') +} + /** * Maps input directory file to AssetItem format * @param filename The filename @@ -63,13 +74,14 @@ export function mapInputFileToAssetItem( index: number, directory: 'input' | 'output' = 'input' ): AssetItem { - const params = new URLSearchParams({ filename, type: directory }) + const cleanName = stripDirectoryAnnotation(filename) + const params = new URLSearchParams({ filename: cleanName, type: directory }) const preview_url = api.apiURL(`/view?${params}`) - appendCloudResParam(params, filename) + appendCloudResParam(params, cleanName) return { - id: `${directory}-${index}-${filename}`, - name: filename, + id: `${directory}-${index}-${cleanName}`, + name: cleanName, size: 0, created_at: new Date().toISOString(), tags: [directory],