Chat mode, phase 2: local history, answer actions, motion polish
- Conversations in a versioned IndexedDB store (freezmo-chat v1, zod validated with the same schema backup/sync use, capped at 100 x 300). Settled turns persist (question always; answer when produced, partial stops included); ?c=<id> keeps reloads on the conversation via history.replaceState (no router round-trip mid-chat). - History UI: desktop column, phone sheet (spring, backdrop, Escape), auto titles from the first question, inline rename, delete with an Undo toast, diacritic-folding search over titles and message text. Scope and model are remembered per conversation; history is readable offline. - Assistant actions: Turn into flashcards (New deck with the answer as notes, landing on the options step - deck seed gained step) and Save to notes (appends to the single scoped deck, trimmed at 40k). - Chats travel in the backup export (preview counts them) and in the encrypted sync payload; still never the key. - Motion from lib/motion.ts: message fade-up, sheet springs for the model/context/history sheets, 150ms composer height transition, all skipped under reduced motion. - Tests: chat suite 63 -> 89 checks (history, reload, rename/search/ delete/undo, actions, export, two-device sync round-trip, offline, desktop panel). Full set: 12/12 suites, 513 checks. SW -> freezmo-v33. Also: tests/model-compare.mjs benchmark (diagnostic) for deciding the default text model on numbers; defaults unchanged.
This commit is contained in:
parent
756a6257a2
commit
5ca0de920c
19 changed files with 1107 additions and 113 deletions
|
|
@ -13,6 +13,7 @@
|
|||
import { z } from "zod";
|
||||
|
||||
import { backupDataSchema } from "./backup";
|
||||
import { chatHistoryStore, ensureChatsHydrated, replaceChats } from "./chat-store";
|
||||
import { ApiError } from "./client-api";
|
||||
import { replaceFolders } from "./folders";
|
||||
import { replaceHomework } from "./homework";
|
||||
|
|
@ -162,6 +163,7 @@ async function apiDelete(id: string): Promise<void> {
|
|||
|
||||
async function buildPayload(): Promise<SyncPayload> {
|
||||
await ensureLearningHydrated();
|
||||
await ensureChatsHydrated();
|
||||
return {
|
||||
app: "freezmo",
|
||||
kind: "sync",
|
||||
|
|
@ -173,6 +175,7 @@ async function buildPayload(): Promise<SyncPayload> {
|
|||
homework: homeworkStore.getSnapshot(),
|
||||
history: learningStore.getAnswersSnapshot(),
|
||||
srs: learningStore.getSrsSnapshot(),
|
||||
chats: chatHistoryStore.getSnapshot(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -185,6 +188,7 @@ async function applyRemote(key: CryptoKey, remote: { updatedAt: number; envelope
|
|||
replaceFolders(parsed.data.folders);
|
||||
replaceDecks(parsed.data.decks);
|
||||
replaceHomework(parsed.data.homework);
|
||||
replaceChats(parsed.data.chats ?? []);
|
||||
});
|
||||
await replaceLearning(parsed.data.history ?? [], parsed.data.srs ?? []);
|
||||
setSyncRecord({ lastSyncedAt: remote.updatedAt, dirtyAt: null, conflict: false });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue