In user-key mode a browser with no stored Ollama key now gets a polished gate instead of the app shell: one masked input with a show/hide toggle, a single Continue button, inline validation errors, and a "?" help sheet with the steps to a free key. - Validates the draft key through /api/health (real 1-token probe) before saving; invalid-key and network failures read differently. - Key still lives only in this browser's localStorage; nothing server-side. - Gate wraps /, /create and deck study; server-key and local-daemon modes skip it; removing the key in Settings brings it back. - Removes the now-redundant dashboard/create API-key banner. - Monochrome corner glows (dark near-black / light near-white), 24px card, 48px field and button, gentle fade/slide-in, reduced-motion aware. - Help modal: centred dialog on desktop, bottom sheet on mobile, closes via X, Escape or backdrop. - README and browser suites updated (34 gate checks, 15 key-flow checks, 12 shipped-default checks; ui/folders/docs/pwa/security/contrast green).
39 lines
1.1 KiB
TypeScript
39 lines
1.1 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback, useEffect, useState, useSyncExternalStore } from "react";
|
|
|
|
import { fetchHealth, type Health } from "./client-api";
|
|
import { settingsStore } from "./settings";
|
|
|
|
/**
|
|
* Health status for the current browser (re-checks whenever the API key
|
|
* changes). Used by the settings dialog and the API-key gate.
|
|
*/
|
|
export function useHealth() {
|
|
const apiKey = useSyncExternalStore(
|
|
settingsStore.subscribe,
|
|
() => settingsStore.getSnapshot().apiKey,
|
|
() => "",
|
|
);
|
|
const [health, setHealth] = useState<Health | null>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const refresh = useCallback(async () => {
|
|
setLoading(true);
|
|
setError(null);
|
|
try {
|
|
setHealth(await fetchHealth());
|
|
} catch (loadError) {
|
|
setError(loadError instanceof Error ? loadError.message : "Could not reach the Freezmo server.");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
void refresh();
|
|
}, [refresh, apiKey]);
|
|
|
|
return { health, loading, error, refresh, apiKey };
|
|
}
|