mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-28 18:22:40 +00:00
[feat] Add right-click context menu to MediaAssetCard (#6844)
## Summary - Add right-click context menu functionality to MediaAssetCard - Separate context menu into its own component (MediaAssetContextMenu.vue) - Ensure only one context menu is visible at a time within the same tab ## Changes - Add `MediaAssetContextMenu.vue` - new component for context menu - Update `MediaAssetCard.vue` - show context menu on right-click and more button click - Delete `MediaAssetMoreMenu.vue` - consolidated into context menu - Delete `MediaAssetButtonDivider.vue` - unused - Update `AssetsSidebarTab.vue` - add context menu state management - Refactor `useMediaAssetActions.ts` ## Screenshot [screen-capture.webm](https://github.com/user-attachments/assets/6fe414ef-b134-4fbe-98aa-6437bb354b41) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -58,20 +58,20 @@ export function useMediaAssetActions() {
|
||||
}
|
||||
}
|
||||
|
||||
const downloadAsset = () => {
|
||||
const asset = mediaContext?.asset.value
|
||||
if (!asset) return
|
||||
const downloadAsset = (asset?: AssetItem) => {
|
||||
const targetAsset = asset ?? mediaContext?.asset.value
|
||||
if (!targetAsset) return
|
||||
|
||||
try {
|
||||
const filename = asset.name
|
||||
const filename = targetAsset.name
|
||||
let downloadUrl: string
|
||||
|
||||
// In cloud, use preview_url directly (from cloud storage)
|
||||
// In OSS/localhost, use the /view endpoint
|
||||
if (isCloud && asset.preview_url) {
|
||||
downloadUrl = asset.preview_url
|
||||
if (isCloud && targetAsset.preview_url) {
|
||||
downloadUrl = targetAsset.preview_url
|
||||
} else {
|
||||
downloadUrl = getAssetUrl(asset)
|
||||
downloadUrl = getAssetUrl(targetAsset)
|
||||
}
|
||||
|
||||
downloadFile(downloadUrl, filename)
|
||||
@@ -198,13 +198,13 @@ export function useMediaAssetActions() {
|
||||
}
|
||||
}
|
||||
|
||||
const copyJobId = async () => {
|
||||
const asset = mediaContext?.asset.value
|
||||
if (!asset) return
|
||||
const copyJobId = async (asset?: AssetItem) => {
|
||||
const targetAsset = asset ?? mediaContext?.asset.value
|
||||
if (!targetAsset) return
|
||||
|
||||
// Try asset.id first (OSS), then fall back to metadata (Cloud)
|
||||
const metadata = getOutputAssetMetadata(asset.user_metadata)
|
||||
const promptId = asset.id || metadata?.promptId
|
||||
const metadata = getOutputAssetMetadata(targetAsset.user_metadata)
|
||||
const promptId = targetAsset.id || metadata?.promptId
|
||||
|
||||
if (!promptId) {
|
||||
toast.add({
|
||||
@@ -223,12 +223,14 @@ export function useMediaAssetActions() {
|
||||
* Add a loader node to the current workflow for this asset
|
||||
* Uses shared utility to detect appropriate node type based on file extension
|
||||
*/
|
||||
const addWorkflow = async () => {
|
||||
const asset = mediaContext?.asset.value
|
||||
if (!asset) return
|
||||
const addWorkflow = async (asset?: AssetItem) => {
|
||||
const targetAsset = asset ?? mediaContext?.asset.value
|
||||
if (!targetAsset) return
|
||||
|
||||
// Detect node type using shared utility
|
||||
const { nodeType, widgetName } = detectNodeTypeFromFilename(asset.name)
|
||||
const { nodeType, widgetName } = detectNodeTypeFromFilename(
|
||||
targetAsset.name
|
||||
)
|
||||
|
||||
if (!nodeType || !widgetName) {
|
||||
toast.add({
|
||||
@@ -266,13 +268,13 @@ export function useMediaAssetActions() {
|
||||
}
|
||||
|
||||
// Get metadata to construct the annotated path
|
||||
const metadata = getOutputAssetMetadata(asset.user_metadata)
|
||||
const assetType = getAssetType(asset, 'input')
|
||||
const metadata = getOutputAssetMetadata(targetAsset.user_metadata)
|
||||
const assetType = getAssetType(targetAsset, 'input')
|
||||
|
||||
// Create annotated path for the asset
|
||||
const annotated = createAnnotatedPath(
|
||||
{
|
||||
filename: asset.name,
|
||||
filename: targetAsset.name,
|
||||
subfolder: metadata?.subfolder || '',
|
||||
type: isResultItemType(assetType) ? assetType : undefined
|
||||
},
|
||||
@@ -300,12 +302,12 @@ export function useMediaAssetActions() {
|
||||
* Open the workflow from this asset in a new tab
|
||||
* Uses shared workflow extraction and action service
|
||||
*/
|
||||
const openWorkflow = async () => {
|
||||
const asset = mediaContext?.asset.value
|
||||
if (!asset) return
|
||||
const openWorkflow = async (asset?: AssetItem) => {
|
||||
const targetAsset = asset ?? mediaContext?.asset.value
|
||||
if (!targetAsset) return
|
||||
|
||||
// Extract workflow using shared utility
|
||||
const { workflow, filename } = await extractWorkflowFromAsset(asset)
|
||||
const { workflow, filename } = await extractWorkflowFromAsset(targetAsset)
|
||||
|
||||
// Use shared action service
|
||||
const result = await workflowActions.openWorkflowAction(workflow, filename)
|
||||
@@ -331,12 +333,12 @@ export function useMediaAssetActions() {
|
||||
* Export the workflow from this asset as a JSON file
|
||||
* Uses shared workflow extraction and action service
|
||||
*/
|
||||
const exportWorkflow = async () => {
|
||||
const asset = mediaContext?.asset.value
|
||||
if (!asset) return
|
||||
const exportWorkflow = async (asset?: AssetItem) => {
|
||||
const targetAsset = asset ?? mediaContext?.asset.value
|
||||
if (!targetAsset) return
|
||||
|
||||
// Extract workflow using shared utility
|
||||
const { workflow, filename } = await extractWorkflowFromAsset(asset)
|
||||
const { workflow, filename } = await extractWorkflowFromAsset(targetAsset)
|
||||
|
||||
// Use shared action service
|
||||
const result = await workflowActions.exportWorkflowAction(
|
||||
|
||||
Reference in New Issue
Block a user