import { useCallback, useEffect, useRef, useState } from 'react' import { api, type Suggestion } from '../api/client' const DEBOUNCE_MS = 4000 // useCheckpoint manages the grammar-checkpoint lifecycle for one document: // it loads any existing pending suggestions when the doc opens, then fires a // fresh check 4s after the user stops typing. `checking` drives the breathing // dot in the StatusBar. The server rate-limits per document, so a check that // fires too soon simply returns the current set unchanged. export function useCheckpoint(docId: string | null) { const [suggestions, setSuggestions] = useState([]) const [checking, setChecking] = useState(false) const debounceRef = useRef>(undefined) const docIdRef = useRef(docId) docIdRef.current = docId // Token to discard responses from a doc we've since navigated away from. const runRef = useRef(0) const runCheck = useCallback(async () => { const id = docIdRef.current if (!id) return const run = ++runRef.current setChecking(true) try { const fresh = await api.checkDoc(id) if (run === runRef.current && id === docIdRef.current) { setSuggestions(fresh) } } catch (err) { console.error('checkpoint failed', err) } finally { if (run === runRef.current) setChecking(false) } }, []) // Call on every edit; schedules a check 4s after typing settles. const schedule = useCallback(() => { clearTimeout(debounceRef.current) debounceRef.current = setTimeout(runCheck, DEBOUNCE_MS) }, [runCheck]) // Load existing pending suggestions whenever the document changes, and cancel // any in-flight debounce from the previous doc. useEffect(() => { clearTimeout(debounceRef.current) runRef.current++ setSuggestions([]) setChecking(false) if (!docId) return let cancelled = false void (async () => { try { const existing = await api.listSuggestions(docId) if (!cancelled && docIdRef.current === docId) setSuggestions(existing) } catch (err) { console.error('failed to load suggestions', err) } })() return () => { cancelled = true } }, [docId]) useEffect(() => () => clearTimeout(debounceRef.current), []) // Drop one suggestion locally (after accept/dismiss) without a refetch. const removeSuggestion = useCallback((id: string) => { setSuggestions((prev) => prev.filter((s) => s.id !== id)) }, []) return { suggestions, checking, schedule, removeSuggestion } }