mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 02:32:18 +00:00
feat: navigate to previously active tab when closing current tab (#6624)
When closing a tab, the UI now returns to the most recently active tab instead of always going to the first/next tab. This matches standard browser tab behavior and prevents accidental edits in the wrong workflow. Implementation uses a lazy-cleanup history array (max 32 entries) that tracks tab activations and skips closed tabs when finding the previous tab to switch to. Fixes #6599 https://github.com/user-attachments/assets/0bb87969-fd01-4e6b-96e8-c0f741f23ff8 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6624-feat-navigate-to-previously-active-tab-when-closing-current-tab-2a36d73d365081f5be95db51ff7a03f6) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -3,9 +3,11 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
import type { Subgraph } from '@/lib/litegraph/src/litegraph'
|
||||
import type { ComfyWorkflow } from '@/platform/workflow/management/stores/workflowStore'
|
||||
import type {
|
||||
ComfyWorkflow,
|
||||
LoadedComfyWorkflow
|
||||
} from '@/platform/workflow/management/stores/workflowStore'
|
||||
import {
|
||||
type LoadedComfyWorkflow,
|
||||
useWorkflowBookmarkStore,
|
||||
useWorkflowStore
|
||||
} from '@/platform/workflow/management/stores/workflowStore'
|
||||
@@ -723,4 +725,93 @@ describe('useWorkflowStore', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Tab Activation History', () => {
|
||||
let workflowA: ComfyWorkflow
|
||||
let workflowB: ComfyWorkflow
|
||||
let workflowC: ComfyWorkflow
|
||||
|
||||
beforeEach(async () => {
|
||||
await syncRemoteWorkflows(['a.json', 'b.json', 'c.json'])
|
||||
workflowA = store.getWorkflowByPath('workflows/a.json')!
|
||||
workflowB = store.getWorkflowByPath('workflows/b.json')!
|
||||
workflowC = store.getWorkflowByPath('workflows/c.json')!
|
||||
vi.mocked(api.getUserData).mockResolvedValue({
|
||||
status: 200,
|
||||
text: () => Promise.resolve(defaultGraphJSON)
|
||||
} as Response)
|
||||
})
|
||||
|
||||
it('should return most recently active workflow', async () => {
|
||||
// Open workflows in order: A -> B -> C
|
||||
await store.openWorkflow(workflowA)
|
||||
await store.openWorkflow(workflowB)
|
||||
await store.openWorkflow(workflowC)
|
||||
|
||||
// C is current, B should be most recent
|
||||
const mostRecent = store.getMostRecentWorkflow()
|
||||
expect(mostRecent?.path).toBe(workflowB.path)
|
||||
})
|
||||
|
||||
it('should skip closed workflows (lazy cleanup)', async () => {
|
||||
// Open workflows: A -> B -> C
|
||||
await store.openWorkflow(workflowA)
|
||||
await store.openWorkflow(workflowB)
|
||||
await store.openWorkflow(workflowC)
|
||||
|
||||
// Close B (the most recent before C)
|
||||
await store.closeWorkflow(workflowB)
|
||||
|
||||
// C is current, B is closed, so A should be returned
|
||||
const mostRecent = store.getMostRecentWorkflow()
|
||||
expect(mostRecent?.path).toBe(workflowA.path)
|
||||
})
|
||||
|
||||
it('should return null when no valid history exists', async () => {
|
||||
// Open only one workflow
|
||||
await store.openWorkflow(workflowA)
|
||||
|
||||
// No previous workflows, should return null
|
||||
const mostRecent = store.getMostRecentWorkflow()
|
||||
expect(mostRecent).toBeNull()
|
||||
})
|
||||
|
||||
it('should track history when opening workflows', async () => {
|
||||
// Open A, then B, then A again
|
||||
await store.openWorkflow(workflowA)
|
||||
await store.openWorkflow(workflowB)
|
||||
await store.openWorkflow(workflowA)
|
||||
|
||||
// A is current, B should be most recent
|
||||
const mostRecent = store.getMostRecentWorkflow()
|
||||
expect(mostRecent?.path).toBe(workflowB.path)
|
||||
})
|
||||
|
||||
it('should handle workflow activated multiple times', async () => {
|
||||
// Open: A -> B -> A -> C
|
||||
await store.openWorkflow(workflowA)
|
||||
await store.openWorkflow(workflowB)
|
||||
await store.openWorkflow(workflowA)
|
||||
await store.openWorkflow(workflowC)
|
||||
|
||||
// C is current, A should be most recent (not B)
|
||||
const mostRecent = store.getMostRecentWorkflow()
|
||||
expect(mostRecent?.path).toBe(workflowA.path)
|
||||
})
|
||||
|
||||
it('should clean up history when all previous workflows are closed', async () => {
|
||||
// Open: A -> B -> C
|
||||
await store.openWorkflow(workflowA)
|
||||
await store.openWorkflow(workflowB)
|
||||
await store.openWorkflow(workflowC)
|
||||
|
||||
// Close A and B
|
||||
await store.closeWorkflow(workflowA)
|
||||
await store.closeWorkflow(workflowB)
|
||||
|
||||
// C is current, no valid history
|
||||
const mostRecent = store.getMostRecentWorkflow()
|
||||
expect(mostRecent).toBeNull()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user