Skip to content

Commit ba100c4

Browse files
Hide browser tab star when autosave is enabled (#6568)
- Hide "*" indicator in the browser tab title when autosave is enabled (Comfy.Workflow.AutoSave === 'after delay'). - Refactor: extract readable computed values (`shouldShowUnsavedIndicator`, `isActiveWorkflowModified`, `isActiveWorkflowPersisted`). - Aligns with workflow tab behavior; also hides while Shift is held (matches in-app tab logic). Files touched: - src/composables/useBrowserTabTitle.ts Validation: - Ran `pnpm lint:fix` and `pnpm typecheck` — both passed. Manual test suggestions: - With autosave set to 'after delay': modify a workflow → browser tab should not show `*`. - With autosave 'off': modify or open non-persisted workflow → browser tab shows `*`. - Hold Shift: indicator hidden while held (consistent with workflow tab). ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6568-Hide-browser-tab-star-when-autosave-is-enabled-refactor-title-logic-2a16d73d365081549906e9d1fed07a42) by [Unito](https://www.unito.io)
1 parent cafd2de commit ba100c4

File tree

2 files changed

+87
-12
lines changed

2 files changed

+87
-12
lines changed

src/composables/useBrowserTabTitle.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { t } from '@/i18n'
55
import { useSettingStore } from '@/platform/settings/settingStore'
66
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
77
import { useExecutionStore } from '@/stores/executionStore'
8+
import { useWorkspaceStore } from '@/stores/workspaceStore'
89

910
const DEFAULT_TITLE = 'ComfyUI'
1011
const TITLE_SUFFIX = ' - ComfyUI'
@@ -13,6 +14,7 @@ export const useBrowserTabTitle = () => {
1314
const executionStore = useExecutionStore()
1415
const settingStore = useSettingStore()
1516
const workflowStore = useWorkflowStore()
17+
const workspaceStore = useWorkspaceStore()
1618

1719
const executionText = computed(() =>
1820
executionStore.isIdle
@@ -24,11 +26,27 @@ export const useBrowserTabTitle = () => {
2426
() => settingStore.get('Comfy.UseNewMenu') !== 'Disabled'
2527
)
2628

29+
const isAutoSaveEnabled = computed(
30+
() => settingStore.get('Comfy.Workflow.AutoSave') === 'after delay'
31+
)
32+
33+
const isActiveWorkflowModified = computed(
34+
() => !!workflowStore.activeWorkflow?.isModified
35+
)
36+
const isActiveWorkflowPersisted = computed(
37+
() => !!workflowStore.activeWorkflow?.isPersisted
38+
)
39+
40+
const shouldShowUnsavedIndicator = computed(() => {
41+
if (workspaceStore.shiftDown) return false
42+
if (isAutoSaveEnabled.value) return false
43+
if (!isActiveWorkflowPersisted.value) return true
44+
if (isActiveWorkflowModified.value) return true
45+
return false
46+
})
47+
2748
const isUnsavedText = computed(() =>
28-
workflowStore.activeWorkflow?.isModified ||
29-
!workflowStore.activeWorkflow?.isPersisted
30-
? ' *'
31-
: ''
49+
shouldShowUnsavedIndicator.value ? ' *' : ''
3250
)
3351
const workflowNameText = computed(() => {
3452
const workflowName = workflowStore.activeWorkflow?.filename

tests-ui/tests/composables/BrowserTabTitle.test.ts

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { beforeEach, describe, expect, it, vi } from 'vitest'
2-
import { nextTick, reactive } from 'vue'
2+
import { effectScope, nextTick, reactive } from 'vue'
3+
import type { EffectScope } from 'vue'
34

45
import { useBrowserTabTitle } from '@/composables/useBrowserTabTitle'
56

@@ -38,6 +39,14 @@ vi.mock('@/platform/workflow/management/stores/workflowStore', () => ({
3839
useWorkflowStore: () => workflowStore
3940
}))
4041

42+
// Mock the workspace store
43+
const workspaceStore = reactive({
44+
shiftDown: false
45+
})
46+
vi.mock('@/stores/workspaceStore', () => ({
47+
useWorkspaceStore: () => workspaceStore
48+
}))
49+
4150
describe('useBrowserTabTitle', () => {
4251
beforeEach(() => {
4352
// reset execution store
@@ -51,14 +60,17 @@ describe('useBrowserTabTitle', () => {
5160
// reset setting and workflow stores
5261
;(settingStore.get as any).mockReturnValue('Enabled')
5362
workflowStore.activeWorkflow = null
63+
workspaceStore.shiftDown = false
5464

5565
// reset document title
5666
document.title = ''
5767
})
5868

5969
it('sets default title when idle and no workflow', () => {
60-
useBrowserTabTitle()
70+
const scope: EffectScope = effectScope()
71+
scope.run(() => useBrowserTabTitle())
6172
expect(document.title).toBe('ComfyUI')
73+
scope.stop()
6274
})
6375

6476
it('sets workflow name as title when workflow exists and menu enabled', async () => {
@@ -68,9 +80,11 @@ describe('useBrowserTabTitle', () => {
6880
isModified: false,
6981
isPersisted: true
7082
}
71-
useBrowserTabTitle()
83+
const scope: EffectScope = effectScope()
84+
scope.run(() => useBrowserTabTitle())
7285
await nextTick()
7386
expect(document.title).toBe('myFlow - ComfyUI')
87+
scope.stop()
7488
})
7589

7690
it('adds asterisk for unsaved workflow', async () => {
@@ -80,9 +94,44 @@ describe('useBrowserTabTitle', () => {
8094
isModified: true,
8195
isPersisted: true
8296
}
83-
useBrowserTabTitle()
97+
const scope: EffectScope = effectScope()
98+
scope.run(() => useBrowserTabTitle())
8499
await nextTick()
85100
expect(document.title).toBe('*myFlow - ComfyUI')
101+
scope.stop()
102+
})
103+
104+
it('hides asterisk when autosave is enabled', async () => {
105+
;(settingStore.get as any).mockImplementation((key: string) => {
106+
if (key === 'Comfy.Workflow.AutoSave') return 'after delay'
107+
if (key === 'Comfy.UseNewMenu') return 'Enabled'
108+
return 'Enabled'
109+
})
110+
workflowStore.activeWorkflow = {
111+
filename: 'myFlow',
112+
isModified: true,
113+
isPersisted: true
114+
}
115+
useBrowserTabTitle()
116+
await nextTick()
117+
expect(document.title).toBe('myFlow - ComfyUI')
118+
})
119+
120+
it('hides asterisk while Shift key is held', async () => {
121+
;(settingStore.get as any).mockImplementation((key: string) => {
122+
if (key === 'Comfy.Workflow.AutoSave') return 'off'
123+
if (key === 'Comfy.UseNewMenu') return 'Enabled'
124+
return 'Enabled'
125+
})
126+
workspaceStore.shiftDown = true
127+
workflowStore.activeWorkflow = {
128+
filename: 'myFlow',
129+
isModified: true,
130+
isPersisted: true
131+
}
132+
useBrowserTabTitle()
133+
await nextTick()
134+
expect(document.title).toBe('myFlow - ComfyUI')
86135
})
87136

88137
// Fails when run together with other tests. Suspect to be caused by leaked
@@ -94,17 +143,21 @@ describe('useBrowserTabTitle', () => {
94143
isModified: false,
95144
isPersisted: true
96145
}
97-
useBrowserTabTitle()
146+
const scope: EffectScope = effectScope()
147+
scope.run(() => useBrowserTabTitle())
98148
await nextTick()
99149
expect(document.title).toBe('ComfyUI')
150+
scope.stop()
100151
})
101152

102153
it('shows execution progress when not idle without workflow', async () => {
103154
executionStore.isIdle = false
104155
executionStore.executionProgress = 0.3
105-
useBrowserTabTitle()
156+
const scope: EffectScope = effectScope()
157+
scope.run(() => useBrowserTabTitle())
106158
await nextTick()
107159
expect(document.title).toBe('[30%]ComfyUI')
160+
scope.stop()
108161
})
109162

110163
it('shows node execution title when executing a node using nodeProgressStates', async () => {
@@ -122,9 +175,11 @@ describe('useBrowserTabTitle', () => {
122175
}
123176
}
124177
}
125-
useBrowserTabTitle()
178+
const scope: EffectScope = effectScope()
179+
scope.run(() => useBrowserTabTitle())
126180
await nextTick()
127181
expect(document.title).toBe('[40%][50%] Foo')
182+
scope.stop()
128183
})
129184

130185
it('shows multiple nodes running when multiple nodes are executing', async () => {
@@ -140,8 +195,10 @@ describe('useBrowserTabTitle', () => {
140195
},
141196
'2': { state: 'running', value: 8, max: 10, node: '2', prompt_id: 'test' }
142197
}
143-
useBrowserTabTitle()
198+
const scope: EffectScope = effectScope()
199+
scope.run(() => useBrowserTabTitle())
144200
await nextTick()
145201
expect(document.title).toBe('[40%][2 nodes running]')
202+
scope.stop()
146203
})
147204
})

0 commit comments

Comments
 (0)