diff --git a/src/platform/workflow/core/services/workflowService.ts b/src/platform/workflow/core/services/workflowService.ts index 10bbee0d3c..3660e66842 100644 --- a/src/platform/workflow/core/services/workflowService.ts +++ b/src/platform/workflow/core/services/workflowService.ts @@ -21,6 +21,7 @@ import { useDialogService } from '@/services/dialogService' import { useAppMode } from '@/composables/useAppMode' import type { AppMode } from '@/composables/useAppMode' import { useDomWidgetStore } from '@/stores/domWidgetStore' +import { useAppModeStore } from '@/stores/appModeStore' import { useExecutionErrorStore } from '@/stores/executionErrorStore' import { useWorkspaceStore } from '@/stores/workspaceStore' import { @@ -404,6 +405,7 @@ export const useWorkflowService = () => { // Determine the initial app mode for fresh loads from serialized state. // null means linearMode was never explicitly set (not builder-saved). const freshLoadMode = linearModeToAppMode(workflowData.extra?.linearMode) + useAppModeStore().loadSelections(workflowData.extra?.linearData) function trackIfEnteringApp(workflow: ComfyWorkflow) { if (!wasAppMode && workflow.initialMode === 'app') { diff --git a/src/stores/appModeStore.test.ts b/src/stores/appModeStore.test.ts index c8d4d3e661..ae900db5d3 100644 --- a/src/stores/appModeStore.test.ts +++ b/src/stores/appModeStore.test.ts @@ -197,14 +197,12 @@ describe('appModeStore', () => { id == 1 ? (node1 as unknown as LGraphNode) : undefined ) - workflowStore.activeWorkflow = workflowWithLinearData( - [ + store.loadSelections({ + inputs: [ [1, 'prompt'], [99, 'width'] - ], - [] - ) - await nextTick() + ] + }) expect(store.selectedInputs).toEqual([[1, 'prompt']]) }) @@ -215,14 +213,12 @@ describe('appModeStore', () => { id == 1 ? (node1 as unknown as LGraphNode) : undefined ) - workflowStore.activeWorkflow = workflowWithLinearData( - [ + store.loadSelections({ + inputs: [ [1, 'prompt'], [1, 'deleted_widget'] - ], - [] - ) - await nextTick() + ] + }) expect(store.selectedInputs).toEqual([ [1, 'prompt'], @@ -236,8 +232,7 @@ describe('appModeStore', () => { id == 1 ? (node1 as unknown as LGraphNode) : undefined ) - workflowStore.activeWorkflow = workflowWithLinearData([], [1, 99]) - await nextTick() + store.loadSelections({ outputs: [1, 99] }) expect(store.selectedOutputs).toEqual([1]) }) @@ -247,8 +242,11 @@ describe('appModeStore', () => { // Initially nodes are not resolvable — pruning removes them mockResolveNode.mockReturnValue(undefined) - workflowStore.activeWorkflow = workflowWithLinearData([[1, 'seed']], [1]) + const inputs: [number, string][] = [[1, 'seed']] + workflowStore.activeWorkflow = workflowWithLinearData(inputs, [1]) + store.loadSelections({ inputs }) await nextTick() + expect(store.selectedInputs).toEqual([]) expect(store.selectedOutputs).toEqual([]) @@ -268,8 +266,7 @@ describe('appModeStore', () => { it('hasOutputs is false when all output nodes are deleted', async () => { mockResolveNode.mockReturnValue(undefined) - workflowStore.activeWorkflow = workflowWithLinearData([], [10, 20]) - await nextTick() + store.loadSelections({ outputs: [10, 20] }) expect(store.selectedOutputs).toEqual([]) expect(store.hasOutputs).toBe(false) diff --git a/src/stores/appModeStore.ts b/src/stores/appModeStore.ts index 75603e9c37..edc8f04a0d 100644 --- a/src/stores/appModeStore.ts +++ b/src/stores/appModeStore.ts @@ -1,5 +1,5 @@ import { defineStore } from 'pinia' -import { reactive, computed, watch } from 'vue' +import { ref, computed, watch } from 'vue' import { useEventListener } from '@vueuse/core' import { useEmptyWorkflowDialog } from '@/components/builder/useEmptyWorkflowDialog' @@ -25,9 +25,9 @@ export const useAppModeStore = defineStore('appMode', () => { const { mode, setMode, isBuilderMode, isSelectMode } = useAppMode() const emptyWorkflowDialog = useEmptyWorkflowDialog() - const selectedInputs = reactive<[NodeId, string][]>([]) - const selectedOutputs = reactive([]) - const hasOutputs = computed(() => !!selectedOutputs.length) + const selectedInputs = ref<[NodeId, string][]>([]) + const selectedOutputs = ref([]) + const hasOutputs = computed(() => !!selectedOutputs.value.length) const hasNodes = computed(() => { // Nodes are not reactive, so trigger recomputation when workflow changes void workflowStore.activeWorkflow @@ -54,8 +54,8 @@ export const useAppModeStore = defineStore('appMode', () => { function loadSelections(data: Partial | undefined) { const { inputs, outputs } = pruneLinearData(data) - selectedInputs.splice(0, selectedInputs.length, ...inputs) - selectedOutputs.splice(0, selectedOutputs.length, ...outputs) + selectedInputs.value = inputs + selectedOutputs.value = outputs } function resetSelectedToWorkflow() { @@ -65,20 +65,6 @@ export const useAppModeStore = defineStore('appMode', () => { loadSelections(activeWorkflow.changeTracker?.activeState?.extra?.linearData) } - watch( - () => workflowStore.activeWorkflow, - (newWorkflow) => { - if (newWorkflow) { - loadSelections( - newWorkflow.changeTracker?.activeState?.extra?.linearData - ) - } else { - loadSelections(undefined) - } - }, - { immediate: true } - ) - useEventListener( () => app.rootGraph?.events, 'configured', @@ -88,7 +74,7 @@ export const useAppModeStore = defineStore('appMode', () => { watch( () => isBuilderMode.value - ? { inputs: selectedInputs, outputs: selectedOutputs } + ? { inputs: selectedInputs.value, outputs: selectedOutputs.value } : null, (data) => { if (!data || ChangeTracker.isLoadingGraph) return @@ -144,10 +130,10 @@ export const useAppModeStore = defineStore('appMode', () => { const storeName = isPromotedWidgetView(widget) ? widget.sourceWidgetName : widget.name - const index = selectedInputs.findIndex( + const index = selectedInputs.value.findIndex( ([id, name]) => storeId == id && storeName === name ) - if (index !== -1) selectedInputs.splice(index, 1) + if (index !== -1) selectedInputs.value.splice(index, 1) } return { @@ -155,6 +141,7 @@ export const useAppModeStore = defineStore('appMode', () => { exitBuilder, hasNodes, hasOutputs, + loadSelections, pruneLinearData, removeSelectedInput, resetSelectedToWorkflow,