| ... | @@ -14,6 +14,7 @@ import { | ... | @@ -14,6 +14,7 @@ import { |
| 14 | } from "@t3tools/shared/model"; | 14 | } from "@t3tools/shared/model"; |
| 15 | import { create } from "zustand"; | 15 | import { create } from "zustand"; |
| 16 | import { type ChatMessage, type Project, type Thread } from "./types"; | 16 | import { type ChatMessage, type Project, type Thread } from "./types"; |
| | 17 | import { Debouncer } from "@tanstack/react-pacer"; |
| 17 | | 18 | |
| 18 | // ── State ──────────────────────────────────────────────────────────── | 19 | // ── State ──────────────────────────────────────────────────────────── |
| 19 | | 20 | |
| ... | @@ -86,18 +87,7 @@ function persistState(state: AppState): void { | ... | @@ -86,18 +87,7 @@ function persistState(state: AppState): void { |
| 86 | // Ignore quota/storage errors to avoid breaking chat UX. | 87 | // Ignore quota/storage errors to avoid breaking chat UX. |
| 87 | } | 88 | } |
| 88 | } | 89 | } |
| 89 | | 90 | const debouncedPersistState = new Debouncer(persistState, { wait: 500 }); |
| 90 | let persistTimer: ReturnType<typeof setTimeout> | null = null; | | |
| 91 | | | |
| 92 | function debouncedPersistState(state: AppState): void { | | |
| 93 | if (persistTimer !== null) { | | |
| 94 | clearTimeout(persistTimer); | | |
| 95 | } | | |
| 96 | persistTimer = setTimeout(() => { | | |
| 97 | persistTimer = null; | | |
| 98 | persistState(state); | | |
| 99 | }, 500); | | |
| 100 | } | | |
| 101 | | 91 | |
| 102 | // ── Pure helpers ────────────────────────────────────────────────────── | 92 | // ── Pure helpers ────────────────────────────────────────────────────── |
| 103 | | 93 | |
| ... | @@ -413,16 +403,12 @@ export const useStore = create<AppStore>((set) => ({ | ... | @@ -413,16 +403,12 @@ export const useStore = create<AppStore>((set) => ({ |
| 413 | })); | 403 | })); |
| 414 | | 404 | |
| 415 | // Persist state changes with debouncing to avoid localStorage thrashing | 405 | // Persist state changes with debouncing to avoid localStorage thrashing |
| 416 | useStore.subscribe((state) => debouncedPersistState(state)); | 406 | useStore.subscribe((state) => debouncedPersistState.maybeExecute(state)); |
| 417 | | 407 | |
| 418 | // Flush pending writes synchronously before page unload to prevent data loss. | 408 | // Flush pending writes synchronously before page unload to prevent data loss. |
| 419 | if (typeof window !== "undefined") { | 409 | if (typeof window !== "undefined") { |
| 420 | window.addEventListener("beforeunload", () => { | 410 | window.addEventListener("beforeunload", () => { |
| 421 | if (persistTimer !== null) { | 411 | debouncedPersistState.flush(); |
| 422 | clearTimeout(persistTimer); | | |
| 423 | persistTimer = null; | | |
| 424 | persistState(useStore.getState()); | | |
| 425 | } | | |
| 426 | }); | 412 | }); |
| 427 | } | 413 | } |
| 428 | | 414 | |