-
Notifications
You must be signed in to change notification settings - Fork 388
Media Assets Management Sidebar Tab Implementation #6112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Implement new sidebar tab for managing imported/generated files - Add separate composables for internal and cloud environments - Display execution time from history API on generated outputs - Support gallery view with keyboard navigation - Auto-truncate long filenames in cloud environment - Add utility functions for media type detection - Enable feature only in development mode 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
🎭 Playwright Test Results❌ Some tests failed ⏰ Completed at: 10/17/2025, 03:26:15 PM UTC 📈 Summary
📊 Test Reports by Browser
🎉 Click on the links above to view detailed test results for each browser configuration. |
🎨 Storybook Build Status✅ Build completed successfully! ⏰ Completed at: 10/17/2025, 03:11:14 PM UTC 🔗 Links🎉 Your Storybook is ready for review! |
const imageExts = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp'] | ||
const videoExts = ['mp4', 'webm', 'mov', 'avi'] | ||
const audioExts = ['mp3', 'wav', 'ogg', 'flac'] | ||
const threeDExts = ['obj', 'fbx', 'gltf', 'glb'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: We can move these to the module scope (avoid re-initialization on every call)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we combine the concept of media kind and media type? It seems to be exactly the same but just differing between plural vs. singular. I think singular is better to align with MIME https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/MIME_types
const lastDotIndex = output.filename.lastIndexOf('.') | ||
const nameWithoutExt = | ||
lastDotIndex > -1 | ||
? output.filename.substring(0, lastDotIndex) | ||
: output.filename | ||
const extension = | ||
lastDotIndex > -1 | ||
? output.filename.substring(lastDotIndex) | ||
: '' | ||
|
||
// If name without extension is still long, truncate it | ||
if (nameWithoutExt.length > 10) { | ||
displayName = | ||
nameWithoutExt.substring(0, 10) + | ||
'...' + | ||
nameWithoutExt.substring(nameWithoutExt.length - 10) + | ||
extension | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: extract this to a helper function then use es-toolkit truncate helper.
id: `${taskItem.promptId}-${output.nodeId}-${output.filename}`, | ||
name: displayName, | ||
size: 0, // We don't have size info from history | ||
created_at: taskItem.executionStartTimestamp | ||
? new Date(taskItem.executionStartTimestamp).toISOString() | ||
: new Date().toISOString(), | ||
tags: ['output'], | ||
preview_url: output.url, | ||
user_metadata: { | ||
originalFilename: output.filename, // Store original filename | ||
promptId: taskItem.promptId, | ||
nodeId: output.nodeId, | ||
subfolder: output.subfolder, | ||
...(executionTimeInSeconds && { | ||
executionTimeInSeconds | ||
}), | ||
...(output.format && { | ||
format: output.format | ||
}), | ||
...(taskItem.workflow && { | ||
workflow: taskItem.workflow |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: extract this logic to a dedicate mapping function so that:
- it is more explicit and expressive
- we don't need the inline
&&
notation - we can declare the output type explicitly to prevent future mistakes
id: `${taskItem.promptId}-${output.nodeId}-${output.filename}`, | ||
name: output.filename, | ||
size: 0, | ||
created_at: executionStartTimestamp | ||
? new Date(executionStartTimestamp).toISOString() | ||
: new Date().toISOString(), | ||
tags: ['output'], | ||
preview_url: output.url, | ||
user_metadata: { | ||
promptId: taskItem.promptId, | ||
nodeId: output.nodeId, | ||
subfolder: output.subfolder, | ||
...(executionTimeInSeconds && { | ||
executionTimeInSeconds | ||
}), | ||
...(output.format && { | ||
format: output.format | ||
}), | ||
...(taskItem.workflow && { | ||
workflow: taskItem.workflow | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment here as with https://github.com/Comfy-Org/ComfyUI_frontend/pull/6112/files#r2441443691
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we put the composables in the platform/assets
folder and then re-arrange so it's like:
useMediaAssets/
useAssetsApi.ts
useInternalFilesApi.ts
index.ts
then inside the index.ts file you can put:
import { isCloud } from '@/platform/distribution/types'
if (isCloud) { export { useAssetsApi as useMediaAssets } from './useAssetsApi' }
else { export { useInternalFilesApi as useMediaAssets } from './useInternalFilesApi' }
Then we should also define an interface or abstract class
interface IAssetsProvider {
loading: ref<boolean>
error: ref<null | string>
fetchMediaList: () => UninifedType
}
This way, no other parts of the codebase need to understand the difference in implementations for assets - everything is abstracted behind the UnifiedType
and AssetsProvider
concepts. This makes refactoring, reasoning about the code, and especially dealing with types considerably easier and more maintainable.
The additional benefit is that for OSS build, the entire useAssetsApi.ts
file actually gets tree-shaken.
📋 Overview
Implemented a new Media Assets sidebar tab in ComfyUI for managing user-uploaded input files and generated output files. This feature supports both local and cloud environments and is currently enabled only in development mode.
🎯 Key Features
1. Media Assets Sidebar Tab
2. Environment-Specific Implementation
useInternalMediaAssets
: For local environment/files
API/history
APIuseCloudMediaAssets
: For cloud environmentvery_long_filename_here.png
→very_long_...here.png
)3. Execution Time Display
execution_start
andexecution_success
messages4. Gallery Feature
5. Development Mode Only
import.meta.env.DEV
condition🛠️ Technical Changes
New Files Added
src/components/sidebar/tabs/AssetsSidebarTab.vue
- Main sidebar tab componentsrc/composables/sidebarTabs/useAssetsSidebarTab.ts
- Sidebar tab definitionsrc/composables/useInternalMediaAssets.ts
- Local environment implementationsrc/composables/useCloudMediaAssets.ts
- Cloud environment implementationpackages/design-system/src/icons/image-ai-edit.svg
- Icon additionModified Files
src/stores/workspace/sidebarTabStore.ts
- Added dev mode only tab display logicsrc/platform/assets/components/MediaAssetCard.vue
- Added execution time display, zoom eventsrc/platform/assets/components/MediaImageTop.vue
- Added image dimension detectionpackages/shared-frontend-utils/src/formatUtil.ts
- Added media type determination utility functionssrc/locales/en/main.json
- Added translation keysmedia_asset_OSS_cloud.webm