mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-24 08:19:51 +00:00
feat(persistence): fix QuotaExceededError and cross-workspace draft leakage (#8520)
## Summary Completes the workflow persistence overhaul by integrating the new draft system into the app and migrating existing data. Fixes two critical bugs: 1. **QuotaExceededError** - localStorage fills up with workflow drafts, breaking auto-save 2. **Cross-workspace data leakage** - Drafts from one ComfyUI instance appear in another ## Changes - **What**: - `useWorkflowPersistenceV2.ts` - Main composable that hooks into graph changes with 512ms debounce - `migrateV1toV2.ts` - One-time migration of existing drafts to the new scoped format - Updated E2E tests for new storage key patterns - **Why**: Users lose work when storage quota is exceeded, and see confusing workflows from other instances ## How It Works - **Workspace scoping**: Each ComfyUI instance (identified by server URL) has isolated draft storage - **LRU eviction**: When storage is full, oldest drafts are automatically removed (keeps 32 most recent) - **Tab isolation**: Each browser tab tracks its own active/open workflows via sessionStorage - **Debounced saves**: Graph changes are batched with 512ms delay to reduce storage writes ## Migration Existing V1 drafts are automatically migrated on first load. The migration: 1. Reads drafts from old `Comfy.Workflow.*` keys 2. Converts to new workspace-scoped format 3. Cleans up old keys after successful migration --- *Part 4 of 4 in the workflow persistence improvements stack* --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: GitHub Action <action@github.com> Co-authored-by: Simula_r <18093452+simula-r@users.noreply.github.com>
This commit is contained in:
@@ -244,21 +244,9 @@ test.describe(
|
||||
await comfyPage.settings.setSetting('Comfy.Node.Opacity', 0.5)
|
||||
await comfyPage.settings.setSetting('Comfy.ColorPalette', 'light')
|
||||
await comfyPage.nextFrame()
|
||||
const parsed = await (
|
||||
await comfyPage.page.waitForFunction(
|
||||
() => {
|
||||
const workflow = localStorage.getItem('workflow')
|
||||
if (!workflow) return null
|
||||
try {
|
||||
const data = JSON.parse(workflow)
|
||||
return Array.isArray(data?.nodes) ? data : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
},
|
||||
{ timeout: 3000 }
|
||||
)
|
||||
).jsonValue()
|
||||
const parsed = await comfyPage.page.evaluate(() => {
|
||||
return window['app'].graph.serialize()
|
||||
})
|
||||
expect(parsed.nodes).toBeDefined()
|
||||
expect(Array.isArray(parsed.nodes)).toBe(true)
|
||||
for (const node of parsed.nodes) {
|
||||
|
||||
@@ -736,6 +736,25 @@ test.describe('Load workflow', { tag: '@screenshot' }, () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
'single_ksampler_modified.png'
|
||||
)
|
||||
// Wait for V2 persistence debounce to save the modified workflow
|
||||
const start = Date.now()
|
||||
await comfyPage.page.waitForFunction((since) => {
|
||||
for (let i = 0; i < window.localStorage.length; i++) {
|
||||
const key = window.localStorage.key(i)
|
||||
if (!key?.startsWith('Comfy.Workflow.DraftIndex.v2:')) continue
|
||||
const json = window.localStorage.getItem(key)
|
||||
if (!json) continue
|
||||
try {
|
||||
const index = JSON.parse(json)
|
||||
if (typeof index.updatedAt === 'number' && index.updatedAt >= since) {
|
||||
return true
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return false
|
||||
}, start)
|
||||
await comfyPage.setup({ clearStorage: false })
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
'single_ksampler_modified.png'
|
||||
@@ -758,10 +777,17 @@ test.describe('Load workflow', { tag: '@screenshot' }, () => {
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
|
||||
await comfyPage.menu.topbar.saveWorkflow(workflowB)
|
||||
|
||||
// Wait for localStorage to persist the workflow paths before reloading
|
||||
await comfyPage.page.waitForFunction(
|
||||
() => !!window.localStorage.getItem('Comfy.OpenWorkflowsPaths')
|
||||
)
|
||||
// Wait for sessionStorage to persist the workflow paths before reloading
|
||||
// V2 persistence uses sessionStorage with client-scoped keys
|
||||
await comfyPage.page.waitForFunction(() => {
|
||||
for (let i = 0; i < window.sessionStorage.length; i++) {
|
||||
const key = window.sessionStorage.key(i)
|
||||
if (key?.startsWith('Comfy.Workflow.OpenPaths:')) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
await comfyPage.setup({ clearStorage: false })
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user