83 lines
2.0 KiB
TypeScript
83 lines
2.0 KiB
TypeScript
|
|
/**
|
||
|
|
* 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<JournalCompletions>(_cache);
|
||
|
|
|
||
|
|
// ------------------- Fetch -------------------
|
||
|
|
|
||
|
|
async function load(): Promise<void> {
|
||
|
|
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<void> | null = null;
|
||
|
|
export function ensureCompletions(): Promise<void> {
|
||
|
|
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();
|
||
|
|
}
|