/** * Journal completions — client-side loader for /__COMPLETIONS.json * * Fetches once on first call, caches the result. Call `invalidateCompletions()` * to force a re-fetch (e.g., when the content source changes). */ import { createSignal } from "solid-js"; // ------------------- Types (mirrors CLI) ------------------- export interface DiceCompletion { label: string; notation: string; source: string; } export interface LinkCompletion { path: string; label: string; section: string | null; } export interface JournalCompletions { dice: DiceCompletion[]; links: LinkCompletion[]; } // ------------------- Cache ------------------- let _cache: JournalCompletions = { dice: [], links: [] }; let _loaded = false; /** Thawed value signal; invalidate resets it. */ const [completions, setCompletions] = createSignal(_cache); // ------------------- Fetch ------------------- async function load(): Promise { try { const resp = await fetch("/__COMPLETIONS.json"); if (resp.ok) { const data = await resp.json(); _cache = { dice: Array.isArray(data.dice) ? data.dice : [], links: Array.isArray(data.links) ? data.links : [], }; } else { _cache = { dice: [], links: [] }; } } catch { _cache = { dice: [], links: [] }; } _loaded = true; setCompletions(_cache); } /** Ensure data is loaded (lazy, idempotent). */ let _promise: Promise | null = null; export function ensureCompletions(): Promise { if (!_promise) _promise = load(); return _promise; } /** Force a re-fetch on next use. */ export function invalidateCompletions(): void { _loaded = false; _promise = null; } // ------------------- Hook ------------------- /** * Reactive completions data for the journal input. * Triggers a lazy fetch on first access; returns empty arrays while loading. */ export function useJournalCompletions(): JournalCompletions { if (!_loaded) { void ensureCompletions(); } return completions(); }