import type { BubbleStyle, SpriteForm } from "../ui/Sprite"; import { DEFAULT_KOKORO_VOICE, type VoiceEngine, type VoiceMode } from "./voice"; export type Theme = "terrarium" | "candlelight" | "nocturne"; export type NotifyStyle = "adaptive" | "bubble" | "toast" | "interrupt"; // off: never. ondemand: only the "Look around" button. ambient: auto, on a slow // timer, paused when idle/hidden so the cloud GPU scales to zero (cost-safe). export type VisionMode = "off" | "ondemand" | "ambient"; /** Ambient capture interval. Doesn't change cloud cost much (GPU stays warm * while active regardless) — idle-off is the real saving — but a slower cadence * is gentler and plenty for a screen that changes on human timescales. */ export const AMBIENT_INTERVAL_MS = 45_000; export interface Settings { theme: Theme; accent: string; form: SpriteForm; /** 0..100 — how often Puck wanders and how dense events are. */ presence: number; mischief: number; /** Comment typography: fairy serif, plain UI, or handwriting. */ voice: BubbleStyle; voiceSound: VoiceMode; /** Which TTS engine: local neural (Kokoro) or the OS voices. */ voiceEngine: VoiceEngine; /** Chosen OS (speechSynthesis) voice name; "" = auto-pick a soft english voice. */ voiceName: string; /** Chosen Kokoro voice id (e.g. "af_heart", "am_puck"). */ kokoroVoice: string; /** Chosen macOS `say` voice name (e.g. "Samantha"); "" = system default. */ sayVoice: string; /** Speak the short reaction shouts ("NANI?!", "shipped!") aloud too. */ speakShouts: boolean; notify: NotifyStyle; /** Mirror surfaced comments to OS notifications when the tab is hidden. */ osNotify: boolean; /** How Puck uses his eyes (vision). Default on-demand: nothing runs unattended. */ visionMode: VisionMode; } export const ACCENTS = ["#e7b85c", "#8fcf86", "#e58fa6", "#b89be6", "#6fc6d6"] as const; export const defaultSettings = (): Settings => ({ theme: "terrarium", accent: "#e7b85c", form: "mossling", presence: 62, mischief: 45, voice: "voice", voiceSound: "full", // audible by default (browser autoplay may need one click first) voiceEngine: "say", // Mac-native voice — works in the overlay and the browser demo alike voiceName: "", kokoroVoice: DEFAULT_KOKORO_VOICE, sayVoice: "Samantha", speakShouts: true, notify: "adaptive", osNotify: false, visionMode: "ondemand", }); /** Readable ink color for text on top of the accent. */ export function inkFor(hex: string): string { const h = hex.replace("#", ""); const n = parseInt(h.length === 3 ? h.replace(/./g, (c) => c + c) : h, 16); const r = (n >> 16) & 255; const g = (n >> 8) & 255; const b = n & 255; return r * 299 + g * 587 + b * 114 > 150000 ? "#22180a" : "#fff7e8"; }