Skip to content

Commit a4d979e

Browse files
viva-jinyiclaudeactions-user
authored
[feat] Implement media asset workflow actions with shared utilities (#6696)
## Summary Implements 4 missing media asset workflow features and creates shared utilities to eliminate code duplication. ## Implemented Features ### 1. Copy Job ID ✅ - Properly extracts promptId using `getOutputAssetMetadata` - Uses `useCopyToClipboard` composable ### 2. Add to Current Workflow ✅ - Adds LoadImage/LoadVideo/LoadAudio nodes to canvas - Supports all media file types (JPEG, PNG, MP4, etc.) - Auto-detects appropriate node type using `detectNodeTypeFromFilename` utility ### 3. Open Workflow in New Tab ✅ - Extracts workflow from asset metadata or embedded PNG - Opens workflow in new tab ### 4. Export Workflow ✅ - Exports workflow as JSON file - Supports optional filename prompt ## Code Refactoring ### Created Shared Utilities: 1. **`assetTypeUtil.ts`** - `getAssetType()` function eliminates 6 instances of `asset.tags?.[0] || 'output'` 2. **`assetUrlUtil.ts`** - `getAssetUrl()` function consolidates 3 URL construction patterns 3. **`workflowActionsService.ts`** - Shared service for workflow export/open operations 4. **`workflowExtractionUtil.ts`** - Extract workflows from jobs/assets 5. **`loaderNodeUtil.ts`** - Detect loader node types from filenames ### Improvements to Existing Code: - Refactored to use `formatUtil.getMediaTypeFromFilename()` - Extracted `deleteAssetApi()` helper to reduce deletion logic duplication (~40 lines) - Moved `isResultItemType` type guard to shared `typeGuardUtil.ts` - Added 9 i18n strings for proper localization - Added `@comfyorg/shared-frontend-utils` dependency ## Input Assets Support Improved input assets to support workflow features where applicable: - ✅ All media files (JPEG/PNG/MP4, etc.) → "Add to current workflow" enabled - ✅ PNG/WEBP/FLAC with embedded metadata → "Open/Export workflow" enabled ## Impact - **~150+ lines** of duplicate code eliminated - **5 new utility files** created to improve code reusability - **11 files** changed, **483 insertions**, **234 deletions** ## Testing ✅ TypeScript typecheck passed ✅ ESLint passed ✅ Knip passed 🤖 Generated with [Claude Code](https://claude.com/claude-code) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6696-feat-Implement-media-asset-workflow-actions-with-shared-utilities-2ab6d73d365081fb8ae9d71ce6e38589) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude <[email protected]> Co-authored-by: GitHub Action <[email protected]>
1 parent 2f81e5b commit a4d979e

File tree

12 files changed

+632
-123
lines changed

12 files changed

+632
-123
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@
131131
"@comfyorg/comfyui-electron-types": "0.4.73-0",
132132
"@comfyorg/design-system": "workspace:*",
133133
"@comfyorg/registry-types": "workspace:*",
134+
"@comfyorg/shared-frontend-utils": "workspace:*",
134135
"@comfyorg/tailwind-utils": "workspace:*",
135136
"@iconify/json": "catalog:",
136137
"@primeuix/forms": "catalog:",

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/locales/en/main.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2036,8 +2036,18 @@
20362036
"downloadStarted": "Downloading {count} files...",
20372037
"downloadsStarted": "Started downloading {count} file(s)",
20382038
"assetsDeletedSuccessfully": "{count} asset(s) deleted successfully",
2039-
"failedToDeleteAssets": "Failed to delete selected assets"
2040-
}
2039+
"failedToDeleteAssets": "Failed to delete selected assets",
2040+
"partialDeleteSuccess": "{succeeded} deleted successfully, {failed} failed"
2041+
},
2042+
"noJobIdFound": "No job ID found for this asset",
2043+
"unsupportedFileType": "Unsupported file type for loader node",
2044+
"nodeTypeNotFound": "Node type {nodeType} not found",
2045+
"failedToCreateNode": "Failed to create node",
2046+
"nodeAddedToWorkflow": "{nodeType} node added to workflow",
2047+
"noWorkflowDataFound": "No workflow data found in this asset",
2048+
"workflowOpenedInNewTab": "Workflow opened in new tab",
2049+
"failedToExportWorkflow": "Failed to export workflow",
2050+
"workflowExportedSuccessfully": "Workflow exported successfully"
20412051
},
20422052
"actionbar": {
20432053
"dockToTop": "Dock to top",

src/platform/assets/components/MediaAssetCard.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
:context="{ type: assetType }"
3838
@view="handleZoomClick"
3939
@download="actions.downloadAsset()"
40-
@play="actions.playAsset(asset.id)"
4140
@video-playing-state-changed="isVideoPlaying = $event"
4241
@video-controls-changed="showVideoControls = $event"
4342
@image-loaded="handleImageLoaded"

src/platform/assets/components/MediaAssetMoreMenu.vue

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
<template>
22
<div class="flex flex-col">
3+
<!-- TODO: 3D assets currently excluded from inspection.
4+
When 3D loader nodes are implemented, update detectNodeTypeFromFilename
5+
to return appropriate node type for .gltf, .glb files and remove this exclusion -->
36
<IconTextButton
47
v-if="asset?.kind !== '3D'"
58
type="transparent"
@@ -12,7 +15,7 @@
1215
</IconTextButton>
1316

1417
<IconTextButton
15-
v-if="showWorkflowOptions"
18+
v-if="showAddToWorkflow"
1619
type="transparent"
1720
label="Add to current workflow"
1821
@click="handleAddToWorkflow"
@@ -28,10 +31,10 @@
2831
</template>
2932
</IconTextButton>
3033

31-
<MediaAssetButtonDivider v-if="showWorkflowOptions" />
34+
<MediaAssetButtonDivider v-if="showAddToWorkflow || showWorkflowActions" />
3235

3336
<IconTextButton
34-
v-if="showWorkflowOptions"
37+
v-if="showWorkflowActions"
3538
type="transparent"
3639
label="Open as workflow in new tab"
3740
@click="handleOpenWorkflow"
@@ -42,7 +45,7 @@
4245
</IconTextButton>
4346

4447
<IconTextButton
45-
v-if="showWorkflowOptions"
48+
v-if="showWorkflowActions"
4649
type="transparent"
4750
label="Export workflow"
4851
@click="handleExportWorkflow"
@@ -52,7 +55,7 @@
5255
</template>
5356
</IconTextButton>
5457

55-
<MediaAssetButtonDivider v-if="showWorkflowOptions && showCopyJobId" />
58+
<MediaAssetButtonDivider v-if="showWorkflowActions && showCopyJobId" />
5659

5760
<IconTextButton
5861
v-if="showCopyJobId"
@@ -85,6 +88,8 @@ import { computed, inject } from 'vue'
8588
8689
import IconTextButton from '@/components/button/IconTextButton.vue'
8790
import { isCloud } from '@/platform/distribution/types'
91+
import { supportsWorkflowMetadata } from '@/platform/workflow/utils/workflowExtractionUtil'
92+
import { detectNodeTypeFromFilename } from '@/utils/loaderNodeUtil'
8893
8994
import { useMediaAssetActions } from '../composables/useMediaAssetActions'
9095
import { MediaAssetKey } from '../schemas/mediaAssetSchema'
@@ -107,7 +112,35 @@ const assetType = computed(() => {
107112
return asset.value?.tags?.[0] || context.value?.type || 'output'
108113
})
109114
110-
const showWorkflowOptions = computed(() => assetType.value === 'output')
115+
// Show "Add to current workflow" for all media files (images, videos, audio)
116+
// This works for any file type that has a corresponding loader node
117+
const showAddToWorkflow = computed(() => {
118+
// Output assets can always be added
119+
if (assetType.value === 'output') return true
120+
121+
// Input assets: check if file type is supported by loader nodes
122+
// Use the same utility as the actual addWorkflow function for consistency
123+
if (assetType.value === 'input' && asset.value?.name) {
124+
const { nodeType } = detectNodeTypeFromFilename(asset.value.name)
125+
return nodeType !== null
126+
}
127+
128+
return false
129+
})
130+
131+
// Show "Open/Export workflow" only for files with workflow metadata
132+
// This is more restrictive - only PNG, WEBP, FLAC support embedded workflows
133+
const showWorkflowActions = computed(() => {
134+
// Output assets always have workflow metadata
135+
if (assetType.value === 'output') return true
136+
137+
// Input assets: only formats that support workflow metadata
138+
if (assetType.value === 'input' && asset.value?.name) {
139+
return supportsWorkflowMetadata(asset.value.name)
140+
}
141+
142+
return false
143+
})
111144
112145
// Only show Copy Job ID for output assets (not for imported/input assets)
113146
const showCopyJobId = computed(() => {
@@ -129,7 +162,7 @@ const handleInspect = () => {
129162
130163
const handleAddToWorkflow = () => {
131164
if (asset.value) {
132-
actions.addWorkflow(asset.value.id)
165+
actions.addWorkflow()
133166
}
134167
close()
135168
}
@@ -143,14 +176,14 @@ const handleDownload = () => {
143176
144177
const handleOpenWorkflow = () => {
145178
if (asset.value) {
146-
actions.openWorkflow(asset.value.id)
179+
actions.openWorkflow()
147180
}
148181
close()
149182
}
150183
151184
const handleExportWorkflow = () => {
152185
if (asset.value) {
153-
actions.exportWorkflow(asset.value.id)
186+
actions.exportWorkflow()
154187
}
155188
close()
156189
}

0 commit comments

Comments
 (0)