fix(desktop): make draft persistence actually fire — new-chat sentinel, reload flush, session-switch clears
Manual testing of the salvaged draft persistence showed none of it worked end-to-end. Three distinct bugs, all invisible to the store-level unit tests: 1. New-chat drafts were never written. The skip-one-persist sentinel was reset to null after consuming, but null IS a real scope (the unsaved new-session draft) — so in a new chat every persist run matched the "consumed" sentinel and bailed. This silently killed the headline #38498 fix. Use undefined as the no-skip sentinel, which can never collide with a scope. 2. Cmd+R inside the debounce window dropped the trailing text. React does not run effect cleanups on a page reload, so the flush-on-unmount never fired; with the 400ms debounce that meant type-then-reload lost the draft every time. Flush pending writes on pagehide. 3. Session switch/new/resume/branch paths in use-session-actions cleared the composer stores synchronously with the session-id updates. React batches those, so by the time ChatBar's scope-change cleanup ran to stash the departing session's attachments, the store was already empty — the stash recorded [] and the chips were lost anyway. The composer's per-scope restore now owns composer contents wholesale on scope change, so drop the upstream clears (clearComposerDraft only touched the vestigial $composerDraft atom nothing reads). Co-authored-by: mollusk <roger@roger.local> Co-authored-by: Teknium <127238744+teknium1@users.noreply.github.com>
This commit is contained in:
co-authored by
mollusk
Teknium
parent
65ddc7c4a1
commit
fdc0d19566
@@ -186,7 +186,11 @@ export function ChatBar({
|
||||
const editorRef = useRef<HTMLDivElement | null>(null)
|
||||
const draftRef = useRef(draft)
|
||||
const previousBusyRef = useRef(busy)
|
||||
const skipNextDraftPersistScopeRef = useRef<string | null>(null)
|
||||
// `undefined` = no skip pending. The sentinel must be distinguishable from a
|
||||
// real scope, and `null` IS a real scope (the unsaved-new-session draft):
|
||||
// resetting to null made every persist run in a new chat match the consumed
|
||||
// sentinel and bail, so new-chat drafts were never written at all.
|
||||
const skipNextDraftPersistScopeRef = useRef<string | null | undefined>(undefined)
|
||||
const pendingDraftPersistRef = useRef<{ scope: string | null; value: string } | null>(null)
|
||||
const drainingQueueRef = useRef(false)
|
||||
const urlInputRef = useRef<HTMLInputElement | null>(null)
|
||||
@@ -1132,7 +1136,7 @@ export function ChatBar({
|
||||
|
||||
useEffect(() => {
|
||||
if (skipNextDraftPersistScopeRef.current === draftPersistenceScope) {
|
||||
skipNextDraftPersistScopeRef.current = null
|
||||
skipNextDraftPersistScopeRef.current = undefined
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1161,19 +1165,28 @@ export function ChatBar({
|
||||
return () => window.clearTimeout(handle)
|
||||
}, [draft, draftPersistenceScope, queueEdit, sessionId])
|
||||
|
||||
// Flush any pending debounced draft write when leaving a session scope or
|
||||
// unmounting, so the departing session's latest text is always persisted.
|
||||
useEffect(
|
||||
() => () => {
|
||||
// Flush any pending debounced draft write when leaving a session scope,
|
||||
// unmounting, or the window unloading, so the latest text is always
|
||||
// persisted. The pagehide listener is load-bearing: React does NOT run
|
||||
// effect cleanups on a page reload, so without it a Cmd+R inside the
|
||||
// debounce window silently dropped everything typed in the last 400ms.
|
||||
useEffect(() => {
|
||||
const flushPendingDraftPersist = () => {
|
||||
const pending = pendingDraftPersistRef.current
|
||||
|
||||
if (pending) {
|
||||
pendingDraftPersistRef.current = null
|
||||
writePersistedComposerDraft(pending.scope, pending.value)
|
||||
}
|
||||
},
|
||||
[draftPersistenceScope]
|
||||
)
|
||||
}
|
||||
|
||||
window.addEventListener('pagehide', flushPendingDraftPersist)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('pagehide', flushPendingDraftPersist)
|
||||
flushPendingDraftPersist()
|
||||
}
|
||||
}, [draftPersistenceScope])
|
||||
|
||||
const beginQueuedEdit = (entry: QueuedPromptEntry) => {
|
||||
if (!activeQueueSessionKey || queueEdit) {
|
||||
|
||||
Reference in New Issue
Block a user