mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-08 21:39:58 +00:00
Compare commits
1 Commits
fix/codera
...
fix/codera
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf399a556f |
@@ -1,7 +1,8 @@
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { downloadFile, openFileInNewTab } from '@/base/common/downloadUtil'
|
||||
import { downloadFile } from '@/base/common/downloadUtil'
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
||||
import { useMediaAssetGalleryStore } from '@/platform/assets/composables/useMediaAssetGalleryStore'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
|
||||
import type { MenuOption } from './useMoreOptionsMenu'
|
||||
@@ -23,7 +24,7 @@ export function useImageMenuOptions() {
|
||||
if (!img) return
|
||||
const url = new URL(img.src)
|
||||
url.searchParams.delete('preview')
|
||||
void openFileInNewTab(url.toString())
|
||||
useMediaAssetGalleryStore().openUrl(url.toString())
|
||||
}
|
||||
|
||||
const copyImage = async (node: LGraphNode) => {
|
||||
@@ -87,7 +88,7 @@ export function useImageMenuOptions() {
|
||||
},
|
||||
{
|
||||
label: t('contextMenu.Open Image'),
|
||||
icon: 'icon-[lucide--external-link]',
|
||||
icon: 'icon-[lucide--maximize]',
|
||||
action: () => openImage(node)
|
||||
},
|
||||
{
|
||||
|
||||
@@ -121,68 +121,6 @@ describe('resolveSubgraphInputLink', () => {
|
||||
expect(result).toBe('seed_input')
|
||||
})
|
||||
|
||||
test('skips broken links where getLink returns undefined', () => {
|
||||
const { subgraph, subgraphNode } = createSubgraphSetup('prompt')
|
||||
addLinkedInteriorInput(subgraph, 'prompt', 'valid_input', 'valid')
|
||||
const broken = addLinkedInteriorInput(
|
||||
subgraph,
|
||||
'prompt',
|
||||
'broken_input',
|
||||
'broken'
|
||||
)
|
||||
|
||||
const originalGetLink = subgraph.getLink.bind(subgraph)
|
||||
vi.spyOn(subgraph, 'getLink').mockImplementation((linkId) => {
|
||||
if (typeof linkId !== 'number') return originalGetLink(linkId)
|
||||
if (linkId === broken.linkId) return undefined
|
||||
return originalGetLink(linkId)
|
||||
})
|
||||
|
||||
const result = resolveSubgraphInputLink(
|
||||
subgraphNode,
|
||||
'prompt',
|
||||
({ targetInput }) => targetInput.name
|
||||
)
|
||||
|
||||
expect(result).toBe('valid_input')
|
||||
})
|
||||
|
||||
test('returns result from latest connection when multiple links resolve', () => {
|
||||
const { subgraph, subgraphNode } = createSubgraphSetup('prompt')
|
||||
addLinkedInteriorInput(subgraph, 'prompt', 'older_input', 'older')
|
||||
addLinkedInteriorInput(subgraph, 'prompt', 'newer_input', 'newer')
|
||||
|
||||
const result = resolveSubgraphInputLink(
|
||||
subgraphNode,
|
||||
'prompt',
|
||||
({ targetInput }) => targetInput.name
|
||||
)
|
||||
|
||||
expect(result).toBe('newer_input')
|
||||
})
|
||||
|
||||
test('falls back to earlier link when latest resolve callback returns undefined', () => {
|
||||
const { subgraph, subgraphNode } = createSubgraphSetup('prompt')
|
||||
addLinkedInteriorInput(subgraph, 'prompt', 'fallback_input', 'fallback')
|
||||
const newer = addLinkedInteriorInput(
|
||||
subgraph,
|
||||
'prompt',
|
||||
'skipped_input',
|
||||
'skipped'
|
||||
)
|
||||
|
||||
const result = resolveSubgraphInputLink(
|
||||
subgraphNode,
|
||||
'prompt',
|
||||
({ targetInput }) => {
|
||||
if (targetInput.link === newer.linkId) return undefined
|
||||
return targetInput.name
|
||||
}
|
||||
)
|
||||
|
||||
expect(result).toBe('fallback_input')
|
||||
})
|
||||
|
||||
test('caches getTargetWidget result within the same callback evaluation', () => {
|
||||
const { subgraph, subgraphNode } = createSubgraphSetup('model')
|
||||
const linked = addLinkedInteriorInput(
|
||||
|
||||
@@ -160,6 +160,37 @@ describe('useMediaAssetGalleryStore', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('openUrl', () => {
|
||||
it('should create a ResultItemImpl with overridden url getter', () => {
|
||||
const store = useMediaAssetGalleryStore()
|
||||
const testUrl = 'https://example.com/node-image.png'
|
||||
|
||||
store.openUrl(testUrl)
|
||||
|
||||
expect(ResultItemImpl).toHaveBeenCalledWith({
|
||||
filename: 'node-image.png',
|
||||
subfolder: '',
|
||||
type: 'output',
|
||||
nodeId: '0',
|
||||
mediaType: 'images'
|
||||
})
|
||||
expect(store.items).toHaveLength(1)
|
||||
expect(store.items[0].url).toBe(testUrl)
|
||||
expect(store.activeIndex).toBe(0)
|
||||
})
|
||||
|
||||
it('should handle urls without a filename path', () => {
|
||||
const store = useMediaAssetGalleryStore()
|
||||
|
||||
store.openUrl('https://example.com/')
|
||||
|
||||
expect(ResultItemImpl).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ filename: '' })
|
||||
)
|
||||
expect(store.items[0].url).toBe('https://example.com/')
|
||||
})
|
||||
})
|
||||
|
||||
describe('close', () => {
|
||||
it('should reset activeIndex to -1', () => {
|
||||
const store = useMediaAssetGalleryStore()
|
||||
|
||||
@@ -37,11 +37,32 @@ export const useMediaAssetGalleryStore = defineStore(
|
||||
activeIndex.value = 0
|
||||
}
|
||||
|
||||
const openUrl = (url: string) => {
|
||||
const resultItem = new ResultItemImpl({
|
||||
filename: url.split('/').pop() ?? '',
|
||||
subfolder: '',
|
||||
type: 'output',
|
||||
nodeId: '0',
|
||||
mediaType: 'images'
|
||||
})
|
||||
|
||||
Object.defineProperty(resultItem, 'url', {
|
||||
get() {
|
||||
return url
|
||||
},
|
||||
configurable: true
|
||||
})
|
||||
|
||||
items.value = [resultItem]
|
||||
activeIndex.value = 0
|
||||
}
|
||||
|
||||
return {
|
||||
activeIndex,
|
||||
items,
|
||||
close,
|
||||
openSingle
|
||||
openSingle,
|
||||
openUrl
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import _ from 'es-toolkit/compat'
|
||||
|
||||
import { downloadFile, openFileInNewTab } from '@/base/common/downloadUtil'
|
||||
import { downloadFile } from '@/base/common/downloadUtil'
|
||||
import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems'
|
||||
import { useSubgraphOperations } from '@/composables/graph/useSubgraphOperations'
|
||||
import { useNodeAnimatedImage } from '@/composables/node/useNodeAnimatedImage'
|
||||
@@ -35,6 +35,7 @@ import type {
|
||||
ISerialisedNode
|
||||
} from '@/lib/litegraph/src/types/serialisation'
|
||||
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
|
||||
import { useMediaAssetGalleryStore } from '@/platform/assets/composables/useMediaAssetGalleryStore'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
|
||||
@@ -687,7 +688,7 @@ export const useLitegraphService = () => {
|
||||
callback: () => {
|
||||
const url = new URL(img.src)
|
||||
url.searchParams.delete('preview')
|
||||
void openFileInNewTab(url.toString())
|
||||
useMediaAssetGalleryStore().openUrl(url.toString())
|
||||
}
|
||||
},
|
||||
...getCopyImageOption(img),
|
||||
|
||||
@@ -20,6 +20,11 @@
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<ResultGallery
|
||||
v-model:active-index="mediaAssetGalleryStore.activeIndex"
|
||||
:all-gallery-items="mediaAssetGalleryStore.items"
|
||||
/>
|
||||
|
||||
<GlobalToast />
|
||||
<InviteAcceptedToast />
|
||||
<RerouteMigrationToast />
|
||||
@@ -50,6 +55,7 @@ import { runWhenGlobalIdle } from '@/base/common/async'
|
||||
import MenuHamburger from '@/components/MenuHamburger.vue'
|
||||
import UnloadWindowConfirmDialog from '@/components/dialog/UnloadWindowConfirmDialog.vue'
|
||||
import GraphCanvas from '@/components/graph/GraphCanvas.vue'
|
||||
import ResultGallery from '@/components/sidebar/tabs/queue/ResultGallery.vue'
|
||||
import GlobalToast from '@/components/toast/GlobalToast.vue'
|
||||
import InviteAcceptedToast from '@/platform/workspace/components/toasts/InviteAcceptedToast.vue'
|
||||
import RerouteMigrationToast from '@/components/toast/RerouteMigrationToast.vue'
|
||||
@@ -57,6 +63,7 @@ import { useBrowserTabTitle } from '@/composables/useBrowserTabTitle'
|
||||
import { useCoreCommands } from '@/composables/useCoreCommands'
|
||||
import { useQueuePolling } from '@/platform/remote/comfyui/useQueuePolling'
|
||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||
import { useMediaAssetGalleryStore } from '@/platform/assets/composables/useMediaAssetGalleryStore'
|
||||
import { useProgressFavicon } from '@/composables/useProgressFavicon'
|
||||
import { SERVER_CONFIG_ITEMS } from '@/constants/serverConfig'
|
||||
import type { ServerConfig, ServerConfigValue } from '@/constants/serverConfig'
|
||||
@@ -109,6 +116,7 @@ const colorPaletteStore = useColorPaletteStore()
|
||||
const queueStore = useQueueStore()
|
||||
const assetsStore = useAssetsStore()
|
||||
const versionCompatibilityStore = useVersionCompatibilityStore()
|
||||
const mediaAssetGalleryStore = useMediaAssetGalleryStore()
|
||||
const graphCanvasContainerRef = ref<HTMLDivElement | null>(null)
|
||||
const { isBuilderMode } = useAppMode()
|
||||
const { linearMode } = storeToRefs(useCanvasStore())
|
||||
|
||||
Reference in New Issue
Block a user