import { useSyncExternalStore } from "react"; /** * Per-browser feature flags (localStorage). Gate optional composer * capabilities; kept client-side on purpose — they only change what the * UI offers / what prompt decorations are sent, never auth or data access. */ export type FeatureKey = "thinking" | "attachments" | "deepSearch"; export const FEATURES: { key: FeatureKey; label: string; hint: string; ready: boolean }[] = [ { key: "thinking", label: "Thinking", hint: "Ask the model to reason step-by-step before answering.", ready: true, }, { key: "attachments", label: "Attachments", hint: "Attach images (OCR transcription) and text files to messages.", ready: true, }, { key: "deepSearch", label: "Deep search", hint: "Let the assistant search the web for fresh sources. Coming soon.", ready: false, }, ]; const STORAGE_KEY = "ka:features"; const DEFAULTS: Record = { thinking: false, attachments: true, deepSearch: false, }; function load(): Record { try { const raw = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? "{}") as Partial>; return { ...DEFAULTS, ...raw }; } catch { return { ...DEFAULTS }; } } let flags = load(); const listeners = new Set<() => void>(); function persist() { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(flags)); } catch { // private mode etc. — flags just won't survive a reload } listeners.forEach((l) => l()); } export function featureEnabled(key: FeatureKey): boolean { return flags[key]; } export function setFeature(key: FeatureKey, value: boolean) { flags = { ...flags, [key]: value }; persist(); } export function useFeatures(): Record { return useSyncExternalStore( (cb) => { listeners.add(cb); return () => { listeners.delete(cb); }; }, () => flags, ); } /** System message prepended to /api/chat when the "thinking" flag is on. */ export const THINKING_SYSTEM_PROMPT = "Think step-by-step before answering. Start with a short 'Thoughts' section showing your reasoning, then give the final answer under an 'Answer' heading.";