mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
fix: 3D asset disappears when switching to image output in app mode
Add onUnmounted cleanup to Preview3d to release WebGL context when the component is destroyed by Vue's v-if chain.
This commit is contained in:
@@ -49,7 +49,6 @@ const attrs = useAttrs()
|
||||
/>
|
||||
<Preview3d
|
||||
v-else-if="getMediaType(output) === '3d'"
|
||||
:key="output.url"
|
||||
:class="attrs.class as string"
|
||||
:model-url="output.url"
|
||||
/>
|
||||
|
||||
@@ -75,25 +75,6 @@ describe('Preview3d', () => {
|
||||
expect(cleanup).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('cleans up before reinitializing when modelUrl changes', async () => {
|
||||
const wrapper = await mountPreview3d('http://localhost/view?filename=a.glb')
|
||||
vi.clearAllMocks()
|
||||
|
||||
await wrapper.setProps({
|
||||
modelUrl: 'http://localhost/view?filename=b.glb'
|
||||
})
|
||||
await nextTick()
|
||||
await nextTick()
|
||||
|
||||
const cleanupOrder = cleanup.mock.invocationCallOrder[0]
|
||||
const initOrder = initializeStandaloneViewer.mock.invocationCallOrder[0]
|
||||
expect(cleanupOrder).toBeLessThan(initOrder)
|
||||
expect(initializeStandaloneViewer).toHaveBeenCalledWith(
|
||||
expect.any(HTMLElement),
|
||||
'http://localhost/view?filename=b.glb'
|
||||
)
|
||||
})
|
||||
|
||||
it('reinitializes correctly after unmount and remount', async () => {
|
||||
const url = 'http://localhost/view?filename=model.glb'
|
||||
|
||||
|
||||
@@ -13,19 +13,10 @@ const containerRef = useTemplateRef('containerRef')
|
||||
|
||||
const viewer = ref(useLoad3dViewer())
|
||||
|
||||
watch(
|
||||
[containerRef, () => modelUrl],
|
||||
async () => {
|
||||
if (!containerRef.value || !modelUrl) return
|
||||
watch([containerRef, () => modelUrl], async () => {
|
||||
if (!containerRef.value || !modelUrl) return
|
||||
|
||||
viewer.value.cleanup()
|
||||
await viewer.value.initializeStandaloneViewer(containerRef.value, modelUrl)
|
||||
},
|
||||
{ flush: 'post' }
|
||||
)
|
||||
|
||||
onUnmounted(() => {
|
||||
viewer.value.cleanup()
|
||||
await viewer.value.initializeStandaloneViewer(containerRef.value, modelUrl)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
|
||||
@@ -119,4 +119,19 @@ describe(flattenNodeOutput, () => {
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0].mediaType).toBe('images')
|
||||
})
|
||||
|
||||
it('excludes partial ResultItem objects missing required fields', () => {
|
||||
const output = {
|
||||
images: [
|
||||
{ filename: 'valid.png', subfolder: '', type: 'output' },
|
||||
{ filename: 'no-subfolder.png' },
|
||||
{ subfolder: '', type: 'output' }
|
||||
]
|
||||
} as unknown as NodeExecutionOutput
|
||||
|
||||
const result = flattenNodeOutput(['1', output])
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0].filename).toBe('valid.png')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -11,19 +11,8 @@ function isResultItemLike(item: unknown): item is ResultItem {
|
||||
|
||||
const candidate = item as Record<string, unknown>
|
||||
|
||||
if (
|
||||
candidate.filename !== undefined &&
|
||||
typeof candidate.filename !== 'string'
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
candidate.subfolder !== undefined &&
|
||||
typeof candidate.subfolder !== 'string'
|
||||
) {
|
||||
return false
|
||||
}
|
||||
if (typeof candidate.filename !== 'string') return false
|
||||
if (typeof candidate.subfolder !== 'string') return false
|
||||
|
||||
if (
|
||||
candidate.type !== undefined &&
|
||||
@@ -32,14 +21,6 @@ function isResultItemLike(item: unknown): item is ResultItem {
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
candidate.filename === undefined &&
|
||||
candidate.subfolder === undefined &&
|
||||
candidate.type === undefined
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user