feat(dictation): PR 1.4 — draft recovery + addToHistory persistence outcome
addToHistory previously swallowed SQLite write failures with a console
warning while still pushing the entry into the in-memory history store
— a silent data-loss bug, since the row vanished on next restart with
no signal to the user. It now returns { persisted, error }, only mutates
in-memory history after the disk write lands, and the dictation flow
awaits the result so it can surface a "Couldn't save" toast and keep
the user's content intact for a retry. The new dictation autosave
writes transcript + segments to localStorage on a 1.5s debounce, on
every segment boundary, and on visibilitychange/pagehide — covering
both live capture and the stopped-but-unsaved walk-away case. On
DictationPage mount any draft <24h old surfaces an inline
Restore/Discard banner that styles as a peer to the existing error
notices. The draft is cleared only after a successful SQLite write or
explicit Discard/Clear; on persistence failure it stays on disk so the
user can recover on relaunch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -223,12 +223,31 @@ if (typeof window !== "undefined" && hasTauriRuntime()) {
|
||||
loadHistory().catch(() => {});
|
||||
}
|
||||
|
||||
export async function addToHistory(entry: TranscriptWriteEntry) {
|
||||
const normalised = normaliseTranscriptEntry(entry);
|
||||
history.unshift(normalised);
|
||||
if (history.length > 500) history.length = 500;
|
||||
/**
|
||||
* Result of an `addToHistory` call. `persisted: true` means the entry is
|
||||
* safely on disk (Tauri SQLite write succeeded, or we're in the non-Tauri
|
||||
* shell where there is no disk). `persisted: false` means the in-memory
|
||||
* history was NOT updated either — the caller is responsible for keeping
|
||||
* the user's source content (e.g. the dictation draft) alive so a retry
|
||||
* is possible. Surfacing this is what closes the historical silent-loss
|
||||
* bug where SQLite failures were swallowed by `console.warn`.
|
||||
*/
|
||||
export interface AddToHistoryResult {
|
||||
persisted: boolean;
|
||||
error?: unknown;
|
||||
}
|
||||
|
||||
if (!hasTauriRuntime()) return;
|
||||
export async function addToHistory(entry: TranscriptWriteEntry): Promise<AddToHistoryResult> {
|
||||
const normalised = normaliseTranscriptEntry(entry);
|
||||
|
||||
// Non-Tauri shell (browser preview, SSR test): no disk to fail. Update
|
||||
// the in-memory store and report success so callers don't think the
|
||||
// write was lost — there was no write to lose.
|
||||
if (!hasTauriRuntime()) {
|
||||
history.unshift(normalised);
|
||||
if (history.length > 500) history.length = 500;
|
||||
return { persisted: true };
|
||||
}
|
||||
|
||||
try {
|
||||
await invoke("add_transcript", {
|
||||
@@ -251,14 +270,22 @@ export async function addToHistory(entry: TranscriptWriteEntry) {
|
||||
antiHallucination: !!entry.antiHallucination,
|
||||
},
|
||||
});
|
||||
// Only mutate the in-memory history after the disk write lands.
|
||||
// Adding to memory while the disk write fails is exactly the misleading
|
||||
// state PR 1.4 set out to fix — the row would appear in History, then
|
||||
// silently vanish on next launch.
|
||||
history.unshift(normalised);
|
||||
if (history.length > 500) history.length = 500;
|
||||
// Fire-and-forget auto-title. Same gate as the existing auto-cleanup
|
||||
// path (`aiTier !== "off" && formatMode !== "Raw"`) so users opt in
|
||||
// by the same Settings choice — no new toggle. Skipped silently if
|
||||
// the LLM isn't loaded; HistoryPage's per-row "Title" button is the
|
||||
// retry path. Never overwrites a title the caller already set.
|
||||
void maybeAutoGenerateTitle(normalised.id, normalised.text, normalised.title);
|
||||
return { persisted: true };
|
||||
} catch (err) {
|
||||
console.warn("addToHistory: SQLite write failed, entry will not persist past restart", err);
|
||||
return { persisted: false, error: err };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user