mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-28 18:22:40 +00:00
fix: update for reviews
This commit is contained in:
@@ -1749,6 +1749,7 @@
|
|||||||
"nothingToQueue": "Nothing to queue",
|
"nothingToQueue": "Nothing to queue",
|
||||||
"pleaseSelectOutputNodes": "Please select output nodes",
|
"pleaseSelectOutputNodes": "Please select output nodes",
|
||||||
"failedToQueue": "Failed to queue",
|
"failedToQueue": "Failed to queue",
|
||||||
|
"failedToSaveDraft": "Failed to save workflow draft",
|
||||||
"failedExecutionPathResolution": "Could not resolve path to selected nodes",
|
"failedExecutionPathResolution": "Could not resolve path to selected nodes",
|
||||||
"no3dScene": "No 3D scene to apply texture",
|
"no3dScene": "No 3D scene to apply texture",
|
||||||
"failedToApplyTexture": "Failed to apply texture",
|
"failedToApplyTexture": "Failed to apply texture",
|
||||||
|
|||||||
@@ -29,10 +29,11 @@ export const upsertDraft = (
|
|||||||
snapshot: WorkflowDraftSnapshot,
|
snapshot: WorkflowDraftSnapshot,
|
||||||
limit: number = MAX_DRAFTS
|
limit: number = MAX_DRAFTS
|
||||||
): DraftCacheState => {
|
): DraftCacheState => {
|
||||||
|
const effectiveLimit = Math.max(1, limit)
|
||||||
const drafts = { ...state.drafts, [path]: snapshot }
|
const drafts = { ...state.drafts, [path]: snapshot }
|
||||||
const order = touchEntry(state.order, path)
|
const order = touchEntry(state.order, path)
|
||||||
|
|
||||||
while (order.length > limit) {
|
while (order.length > effectiveLimit) {
|
||||||
const oldest = order.shift()
|
const oldest = order.shift()
|
||||||
if (!oldest) continue
|
if (!oldest) continue
|
||||||
if (oldest !== path) {
|
if (oldest !== path) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { createPinia, setActivePinia } from 'pinia'
|
import { createPinia, setActivePinia } from 'pinia'
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
|
import type * as I18n from 'vue-i18n'
|
||||||
|
|
||||||
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
|
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
|
||||||
import { useWorkflowPersistence } from '@/platform/workflow/persistence/composables/useWorkflowPersistence'
|
import { useWorkflowPersistence } from '@/platform/workflow/persistence/composables/useWorkflowPersistence'
|
||||||
@@ -16,6 +17,23 @@ vi.mock('@/platform/settings/settingStore', () => ({
|
|||||||
}))
|
}))
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const mockToastAdd = vi.fn()
|
||||||
|
vi.mock('primevue', () => ({
|
||||||
|
useToast: () => ({
|
||||||
|
add: mockToastAdd
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('vue-i18n', async (importOriginal) => {
|
||||||
|
const actual = await importOriginal<typeof I18n>()
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
useI18n: () => ({
|
||||||
|
t: (key: string) => key
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const loadBlankWorkflow = vi.fn()
|
const loadBlankWorkflow = vi.fn()
|
||||||
vi.mock('@/platform/workflow/core/services/workflowService', () => ({
|
vi.mock('@/platform/workflow/core/services/workflowService', () => ({
|
||||||
useWorkflowService: () => ({
|
useWorkflowService: () => ({
|
||||||
@@ -23,6 +41,15 @@ vi.mock('@/platform/workflow/core/services/workflowService', () => ({
|
|||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock(
|
||||||
|
'@/platform/workflow/templates/composables/useTemplateUrlLoader',
|
||||||
|
() => ({
|
||||||
|
useTemplateUrlLoader: () => ({
|
||||||
|
loadTemplateFromUrlParams: vi.fn()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
const executeCommand = vi.fn()
|
const executeCommand = vi.fn()
|
||||||
vi.mock('@/stores/commandStore', () => ({
|
vi.mock('@/stores/commandStore', () => ({
|
||||||
useCommandStore: () => ({
|
useCommandStore: () => ({
|
||||||
@@ -65,6 +92,9 @@ vi.mock('@/scripts/app', () => ({
|
|||||||
graph: {
|
graph: {
|
||||||
serialize: () => mocks.serializeMock()
|
serialize: () => mocks.serializeMock()
|
||||||
},
|
},
|
||||||
|
rootGraph: {
|
||||||
|
serialize: () => mocks.serializeMock()
|
||||||
|
},
|
||||||
loadGraphData: (...args: unknown[]) => mocks.loadGraphDataMock(...args),
|
loadGraphData: (...args: unknown[]) => mocks.loadGraphDataMock(...args),
|
||||||
canvas: {}
|
canvas: {}
|
||||||
}
|
}
|
||||||
@@ -82,6 +112,7 @@ describe('useWorkflowPersistence', () => {
|
|||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
sessionStorage.clear()
|
sessionStorage.clear()
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
|
mockToastAdd.mockClear()
|
||||||
useWorkflowDraftStore().reset()
|
useWorkflowDraftStore().reset()
|
||||||
mocks.state.graphChangedHandler = null
|
mocks.state.graphChangedHandler = null
|
||||||
mocks.state.currentGraph = { initial: true }
|
mocks.state.currentGraph = { initial: true }
|
||||||
@@ -194,4 +225,30 @@ describe('useWorkflowPersistence', () => {
|
|||||||
workflowStore.openWorkflows.map((workflow) => workflow?.path)
|
workflowStore.openWorkflows.map((workflow) => workflow?.path)
|
||||||
).toContain('workflows/Unsaved Workflow.json')
|
).toContain('workflows/Unsaved Workflow.json')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('shows error toast when draft save fails', async () => {
|
||||||
|
const workflowStore = useWorkflowStore()
|
||||||
|
const draftStore = useWorkflowDraftStore()
|
||||||
|
|
||||||
|
const workflow = workflowStore.createTemporary('FailingDraft.json')
|
||||||
|
await workflowStore.openWorkflow(workflow)
|
||||||
|
|
||||||
|
useWorkflowPersistence()
|
||||||
|
expect(mocks.state.graphChangedHandler).toBeTypeOf('function')
|
||||||
|
|
||||||
|
vi.spyOn(draftStore, 'saveDraft').mockImplementation(() => {
|
||||||
|
throw new Error('Storage quota exceeded')
|
||||||
|
})
|
||||||
|
|
||||||
|
mocks.state.currentGraph = { title: 'Test' }
|
||||||
|
mocks.state.graphChangedHandler!()
|
||||||
|
await vi.advanceTimersByTimeAsync(800)
|
||||||
|
|
||||||
|
expect(mockToastAdd).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
severity: 'error',
|
||||||
|
detail: expect.any(String)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import { useToast } from 'primevue'
|
||||||
import { tryOnScopeDispose } from '@vueuse/core'
|
import { tryOnScopeDispose } from '@vueuse/core'
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -21,6 +23,7 @@ import { getStorageValue, setStorageValue } from '@/scripts/utils'
|
|||||||
import { useCommandStore } from '@/stores/commandStore'
|
import { useCommandStore } from '@/stores/commandStore'
|
||||||
|
|
||||||
export function useWorkflowPersistence() {
|
export function useWorkflowPersistence() {
|
||||||
|
const { t } = useI18n()
|
||||||
const workflowStore = useWorkflowStore()
|
const workflowStore = useWorkflowStore()
|
||||||
const settingStore = useSettingStore()
|
const settingStore = useSettingStore()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -28,6 +31,7 @@ export function useWorkflowPersistence() {
|
|||||||
const templateUrlLoader = useTemplateUrlLoader()
|
const templateUrlLoader = useTemplateUrlLoader()
|
||||||
const TEMPLATE_NAMESPACE = PRESERVED_QUERY_NAMESPACES.TEMPLATE
|
const TEMPLATE_NAMESPACE = PRESERVED_QUERY_NAMESPACES.TEMPLATE
|
||||||
const workflowDraftStore = useWorkflowDraftStore()
|
const workflowDraftStore = useWorkflowDraftStore()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const ensureTemplateQueryFromIntent = async () => {
|
const ensureTemplateQueryFromIntent = async () => {
|
||||||
hydratePreservedQuery(TEMPLATE_NAMESPACE)
|
hydratePreservedQuery(TEMPLATE_NAMESPACE)
|
||||||
@@ -69,7 +73,12 @@ export function useWorkflowPersistence() {
|
|||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save draft', error)
|
console.error('Failed to save draft', error)
|
||||||
// If draft store fails, don't continue saving to storage
|
toast.add({
|
||||||
|
severity: 'error',
|
||||||
|
summary: t('g.error'),
|
||||||
|
detail: t('toastMessages.failedToSaveDraft'),
|
||||||
|
life: 3000
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +164,6 @@ export function useWorkflowPersistence() {
|
|||||||
setStorageValue('Comfy.PreviousWorkflow', activeWorkflowKey)
|
setStorageValue('Comfy.PreviousWorkflow', activeWorkflowKey)
|
||||||
// When the activeWorkflow changes, the graph has already been loaded.
|
// When the activeWorkflow changes, the graph has already been loaded.
|
||||||
// Saving the current state of the graph to the localStorage.
|
// Saving the current state of the graph to the localStorage.
|
||||||
// Use debounced version to avoid immediate save on tab switch
|
|
||||||
persistCurrentWorkflow()
|
persistCurrentWorkflow()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -189,13 +197,14 @@ export function useWorkflowPersistence() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Get storage values before setting watchers
|
// Get storage values before setting watchers
|
||||||
const storedWorkflows = JSON.parse(
|
const parsedWorkflows = JSON.parse(
|
||||||
getStorageValue('Comfy.OpenWorkflowsPaths') || '[]'
|
getStorageValue('Comfy.OpenWorkflowsPaths') || '[]'
|
||||||
) as string[]
|
)
|
||||||
const storedActiveIndex = JSON.parse(
|
const storedWorkflows = Array.isArray(parsedWorkflows) ? parsedWorkflows : []
|
||||||
|
const parsedIndex = JSON.parse(
|
||||||
getStorageValue('Comfy.ActiveWorkflowIndex') || '-1'
|
getStorageValue('Comfy.ActiveWorkflowIndex') || '-1'
|
||||||
) as number
|
)
|
||||||
|
const storedActiveIndex = typeof parsedIndex === 'number' ? parsedIndex : -1
|
||||||
watch(restoreState, ({ paths, activeIndex }) => {
|
watch(restoreState, ({ paths, activeIndex }) => {
|
||||||
if (workflowPersistenceEnabled.value) {
|
if (workflowPersistenceEnabled.value) {
|
||||||
setStorageValue('Comfy.OpenWorkflowsPaths', JSON.stringify(paths))
|
setStorageValue('Comfy.OpenWorkflowsPaths', JSON.stringify(paths))
|
||||||
@@ -220,6 +229,7 @@ export function useWorkflowPersistence() {
|
|||||||
'Failed to parse workflow draft, creating with default',
|
'Failed to parse workflow draft, creating with default',
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
|
workflowDraftStore.removeDraft(path)
|
||||||
workflowStore.createTemporary(draft.name)
|
workflowStore.createTemporary(draft.name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
import {
|
import { MAX_DRAFTS, createDraftCacheState, mostRecentDraftPath, moveDraft, removeDraft, touchEntry, upsertDraft } from '@/platform/workflow/persistence/base/draftCache';
|
||||||
MAX_DRAFTS,
|
import type { WorkflowDraftSnapshot } from '@/platform/workflow/persistence/base/draftCache';
|
||||||
type WorkflowDraftSnapshot,
|
|
||||||
createDraftCacheState,
|
|
||||||
mostRecentDraftPath,
|
|
||||||
moveDraft,
|
|
||||||
removeDraft,
|
|
||||||
touchEntry,
|
|
||||||
upsertDraft
|
|
||||||
} from '@/platform/workflow/persistence/base/draftCache'
|
|
||||||
|
|
||||||
const createSnapshot = (name: string): WorkflowDraftSnapshot => ({
|
const createSnapshot = (name: string): WorkflowDraftSnapshot => ({
|
||||||
data: JSON.stringify({ name }),
|
data: JSON.stringify({ name }),
|
||||||
@@ -87,7 +87,12 @@ export const useWorkflowDraftStore = defineStore('workflowDraft', () => {
|
|||||||
if (!payload) return false
|
if (!payload) return false
|
||||||
try {
|
try {
|
||||||
const workflow = JSON.parse(payload)
|
const workflow = JSON.parse(payload)
|
||||||
await comfyApp.loadGraphData(workflow, true, true, workflowName)
|
await comfyApp.loadGraphData(
|
||||||
|
workflow,
|
||||||
|
/* clean= */ true,
|
||||||
|
/* restore_view= */ true,
|
||||||
|
workflowName
|
||||||
|
)
|
||||||
return true
|
return true
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to load persisted workflow', err)
|
console.error('Failed to load persisted workflow', err)
|
||||||
|
|||||||
Reference in New Issue
Block a user