From b65440e1c2f077cde76a243c9efc500ceda60cf8 Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Wed, 14 May 2025 15:42:45 +1000 Subject: [PATCH] Track subgraph open history for breadcrumbs --- .../breadcrumb/SubgraphBreadcrumb.vue | 10 +-- src/stores/subgraphNavigationStore.ts | 71 +++++++++++++++++++ src/stores/workflowStore.ts | 22 +++--- src/utils/typeGuardUtil.ts | 6 ++ 4 files changed, 91 insertions(+), 18 deletions(-) create mode 100644 src/stores/subgraphNavigationStore.ts diff --git a/src/components/breadcrumb/SubgraphBreadcrumb.vue b/src/components/breadcrumb/SubgraphBreadcrumb.vue index 409edcb3f..0fadbabec 100644 --- a/src/components/breadcrumb/SubgraphBreadcrumb.vue +++ b/src/components/breadcrumb/SubgraphBreadcrumb.vue @@ -20,22 +20,24 @@ import type { MenuItem, MenuItemCommandEvent } from 'primevue/menuitem' import { computed } from 'vue' import { useCanvasStore } from '@/stores/graphStore' +import { useSubgraphNavigationStore } from '@/stores/subgraphNavigationStore' import { useWorkflowStore } from '@/stores/workflowStore' const workflowStore = useWorkflowStore() +const navigationStore = useSubgraphNavigationStore() const workflowName = computed(() => workflowStore.activeWorkflow?.filename) const items = computed(() => { - if (!workflowStore.subgraphNamePath.length) return [] + if (!navigationStore.navigationStack.length) return [] - return workflowStore.subgraphNamePath.map((name, index) => ({ - label: name, + return navigationStore.navigationStack.map((subgraph) => ({ + label: subgraph.name, command: async () => { const canvas = useCanvasStore().getCanvas() if (!canvas.graph) throw new TypeError('Canvas has no graph') - canvas.setGraph(canvas.graph.pathToRootGraph[index + 1]) + canvas.setGraph(subgraph) } })) }) diff --git a/src/stores/subgraphNavigationStore.ts b/src/stores/subgraphNavigationStore.ts new file mode 100644 index 000000000..d07676337 --- /dev/null +++ b/src/stores/subgraphNavigationStore.ts @@ -0,0 +1,71 @@ +import type { Subgraph } from '@comfyorg/litegraph' +import { defineStore } from 'pinia' +import { computed, shallowReactive, shallowRef, watch } from 'vue' + +import { isNonNullish } from '@/utils/typeGuardUtil' + +import { useCanvasStore } from './graphStore' +import { useWorkflowStore } from './workflowStore' + +/** + * Stores the current subgraph navigation state; a stack representing subgraph + * navigation history from the root graph to the subgraph that is currently + * open. + */ +export const useSubgraphNavigationStore = defineStore( + 'subgraphNavigation', + () => { + const workflowStore = useWorkflowStore() + const canvasStore = useCanvasStore() + + /** The currently opened subgraph. */ + const activeSubgraph = shallowRef() + + /** The stack of subgraph IDs from the root graph to the currently opened subgraph. */ + const subgraphIdStack = shallowReactive([]) + + /** + * A stack representing subgraph navigation history from the root graph to + * the current opened subgraph. + */ + const navigationStack = computed(() => + subgraphIdStack + .map((id) => canvasStore.getCanvas().graph?.subgraphs.get(id)) + .filter(isNonNullish) + ) + + // Reset on workflow change + watch( + () => workflowStore.activeWorkflow, + () => (subgraphIdStack.length = 0) + ) + + // Update navigation stack when opened subgraph changes + watch( + () => workflowStore.activeSubgraph, + (subgraph) => { + // Navigated back to the root graph + if (!subgraph) { + subgraphIdStack.length = 0 + return + } + + const index = subgraphIdStack.lastIndexOf(subgraph.id) + const lastIndex = subgraphIdStack.length - 1 + + if (index === -1) { + // Opened a new subgraph + subgraphIdStack.push(subgraph.id) + } else if (index !== lastIndex) { + // Navigated to a different subgraph + subgraphIdStack.splice(index + 1, lastIndex - index) + } + } + ) + + return { + activeSubgraph, + navigationStack + } + } +) diff --git a/src/stores/workflowStore.ts b/src/stores/workflowStore.ts index 5a705737e..8c1d15a2d 100644 --- a/src/stores/workflowStore.ts +++ b/src/stores/workflowStore.ts @@ -1,6 +1,7 @@ +import type { Subgraph } from '@comfyorg/litegraph' import _ from 'lodash' import { defineStore } from 'pinia' -import { computed, markRaw, ref, watch } from 'vue' +import { computed, markRaw, ref, shallowRef, watch } from 'vue' import { ComfyWorkflowJSON } from '@/schemas/comfyWorkflowSchema' import { api } from '@/scripts/api' @@ -156,10 +157,9 @@ export interface WorkflowStore { syncWorkflows: (dir?: string) => Promise reorderWorkflows: (from: number, to: number) => void - /** An ordered list of all parent subgraphs, ending with the current subgraph. */ - subgraphNamePath: string[] /** `true` if any subgraph is currently being viewed. */ isSubgraphActive: boolean + activeSubgraph: Subgraph | undefined /** Updates the {@link subgraphNamePath} and {@link isSubgraphActive} values. */ updateActiveGraph: () => void } @@ -427,25 +427,19 @@ export const useWorkflowStore = defineStore('workflow', () => { } } - /** @see WorkflowStore.subgraphNamePath */ - const subgraphNamePath = ref([]) /** @see WorkflowStore.isSubgraphActive */ const isSubgraphActive = ref(false) + /** @see WorkflowStore.activeSubgraph */ + const activeSubgraph = shallowRef() + /** @see WorkflowStore.updateActiveGraph */ const updateActiveGraph = () => { + activeSubgraph.value = comfyApp.canvas?.subgraph if (!comfyApp.canvas) return const { subgraph } = comfyApp.canvas isSubgraphActive.value = isSubgraph(subgraph) - - if (subgraph) { - const [, ...pathFromRoot] = subgraph.pathToRootGraph - - subgraphNamePath.value = pathFromRoot.map((subgraph) => subgraph.name) - } else { - subgraphNamePath.value = [] - } } watch(activeWorkflow, updateActiveGraph) @@ -473,8 +467,8 @@ export const useWorkflowStore = defineStore('workflow', () => { getWorkflowByPath, syncWorkflows, - subgraphNamePath, isSubgraphActive, + activeSubgraph, updateActiveGraph } }) satisfies () => WorkflowStore diff --git a/src/utils/typeGuardUtil.ts b/src/utils/typeGuardUtil.ts index 7812230c7..c35a9716b 100644 --- a/src/utils/typeGuardUtil.ts +++ b/src/utils/typeGuardUtil.ts @@ -21,3 +21,9 @@ export const isAbortError = ( export const isSubgraph = ( item: LGraph | Subgraph | undefined | null ): item is Subgraph => item?.isRootGraph === false + +/** + * Check if an item is non-nullish. + */ +export const isNonNullish = (item: T | undefined | null): item is T => + item != null