mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-12 16:40:05 +00:00
fix: app mode widgets disappear after hard refresh (#9621)
## Summary Fix all app mode widgets (including seed) disappearing after hard refresh due to a race condition in `pruneLinearData` and a missing reactivity dependency in `mappedSelections`. ## Changes - **What**: Guard `pruneLinearData` with `!ChangeTracker.isLoadingGraph` so inputs are preserved while `rootGraph.configure()` hasn't populated nodes yet. Add `graphNodes` dependency to `mappedSelections` computed in `LinearControls.vue` so it re-evaluates when the graph finishes configuring. ## Review Focus The core fix is a one-line guard change: `app.rootGraph && !ChangeTracker.isLoadingGraph` instead of just `app.rootGraph`. The previous guard failed because `rootGraph` exists as an empty graph during loading — `resolveNode()` returns `undefined` for all nodes and everything gets filtered out. Fixes COM-16193 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9621-fix-app-mode-widgets-disappear-after-hard-refresh-31d6d73d3650811193f5e1bc8f3c15c8) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
@@ -64,6 +64,7 @@ useEventListener(
|
||||
)
|
||||
|
||||
const mappedSelections = computed(() => {
|
||||
void graphNodes.value
|
||||
let unprocessedInputs = appModeStore.selectedInputs.flatMap(
|
||||
([nodeId, widgetName]) => {
|
||||
const [node, widget] = resolveNodeWidget(nodeId, widgetName)
|
||||
|
||||
@@ -25,7 +25,7 @@ const mockEmptyWorkflowDialog = vi.hoisted(() => {
|
||||
|
||||
vi.mock('@/scripts/app', () => ({
|
||||
app: {
|
||||
rootGraph: { extra: {}, nodes: [{ id: 1 }] }
|
||||
rootGraph: { extra: {}, nodes: [{ id: 1 }], events: new EventTarget() }
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -242,6 +242,29 @@ describe('appModeStore', () => {
|
||||
expect(store.selectedOutputs).toEqual([1])
|
||||
})
|
||||
|
||||
it('reloads selections on configured event', async () => {
|
||||
const node1 = mockNode(1)
|
||||
|
||||
// Initially nodes are not resolvable — pruning removes them
|
||||
mockResolveNode.mockReturnValue(undefined)
|
||||
workflowStore.activeWorkflow = workflowWithLinearData([[1, 'seed']], [1])
|
||||
await nextTick()
|
||||
expect(store.selectedInputs).toEqual([])
|
||||
expect(store.selectedOutputs).toEqual([])
|
||||
|
||||
// After graph configures, nodes become resolvable
|
||||
mockResolveNode.mockImplementation((id) =>
|
||||
id == 1 ? (node1 as unknown as LGraphNode) : undefined
|
||||
)
|
||||
;(app.rootGraph.events as EventTarget).dispatchEvent(
|
||||
new Event('configured')
|
||||
)
|
||||
await nextTick()
|
||||
|
||||
expect(store.selectedInputs).toEqual([[1, 'seed']])
|
||||
expect(store.selectedOutputs).toEqual([1])
|
||||
})
|
||||
|
||||
it('hasOutputs is false when all output nodes are deleted', async () => {
|
||||
mockResolveNode.mockReturnValue(undefined)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { reactive, computed, watch } from 'vue'
|
||||
import { useEventListener } from '@vueuse/core'
|
||||
|
||||
import { useEmptyWorkflowDialog } from '@/components/builder/useEmptyWorkflowDialog'
|
||||
import { useAppMode } from '@/composables/useAppMode'
|
||||
@@ -76,6 +77,12 @@ export const useAppModeStore = defineStore('appMode', () => {
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
useEventListener(
|
||||
() => app.rootGraph?.events,
|
||||
'configured',
|
||||
resetSelectedToWorkflow
|
||||
)
|
||||
|
||||
watch(
|
||||
() =>
|
||||
isBuilderMode.value
|
||||
|
||||
Reference in New Issue
Block a user