Preserve isolated local drafts across navigation and reload

This commit is contained in:
aiagent
2026-10-19 22:47:17 -07:00
parent 9d69629bc8
commit 6a36a49290
16 changed files with 174 additions and 38 deletions
+28
View File
@@ -0,0 +1,28 @@
export type DraftStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem' | 'key' | 'length'>
const prefix = 'workbench-draft-v1:'
export function draftKey(scope: string[]) { return prefix + JSON.stringify(scope) }
export function readDraft<T>(storage: DraftStorage, scope: string[], empty: T, valid: (value: unknown) => value is T, olderVersions = false): T {
const raw = storage.getItem(draftKey(scope))
if (raw !== null) { const record = JSON.parse(raw); if (record.cleared) return empty; if (!valid(record.value)) throw new Error('草稿格式无法读取'); return record.value }
if (olderVersions) {
const base = scope.slice(0,-1), current = Number(scope.at(-1)); let latest = -1, found: T | undefined
for (let i=0;i<storage.length;i++) { const key=storage.key(i); if (!key?.startsWith(prefix)) continue
const parts=JSON.parse(key.slice(prefix.length)); if (!Array.isArray(parts) || JSON.stringify(parts.slice(0,-1))!==JSON.stringify(base)) continue
const version=Number(parts.at(-1)); if (version>=current || version<=latest) continue
const record=JSON.parse(storage.getItem(key) ?? 'null'); if (record?.cleared) continue
if (valid(record?.value)) { latest=version; found=record.value }
}
if(found) return found
}
return empty
}
export function writeDraft<T>(storage: DraftStorage, scope: string[], value: T) { storage.setItem(draftKey(scope), JSON.stringify({ value }, (key, item) => key === 'sending' || key === 'error' ? undefined : item)) }
export function clearDraft(storage: DraftStorage, scope: string[], allVersions = false) {
if(allVersions) { const base=scope.slice(0,-1); const keys:string[]=[]; for(let i=0;i<storage.length;i++){const key=storage.key(i);if(!key?.startsWith(prefix))continue; const parts=JSON.parse(key.slice(prefix.length));if(JSON.stringify(parts.slice(0,-1))===JSON.stringify(base))keys.push(key)} for(const key of keys)storage.removeItem(key) }
storage.setItem(draftKey(scope), JSON.stringify({cleared:true}))
}
export function clearIfUnchanged(storage: DraftStorage, scope: string[], expected: string | null) {
if(storage.getItem(draftKey(scope))!==expected) return false
clearDraft(storage,scope)
return true
}