import { useState, useEffect, useCallback } from "react"; const STORAGE_KEY = "ai-character-profiles"; const DEFAULT_MODELS = [ "llama3.3:70b", "qwen2.5:72b", "mistral-large", "llama3.1:8b", "qwen2.5:14b", "gemma3:27b", "deepseek-r1:14b", "phi4:14b" ]; const TTS_MODELS = ["Kokoro", "Chatterbox", "F5-TTS", "Qwen3-TTS", "Piper"]; const STT_MODELS = ["Whisper Large-v3", "Whisper Medium", "Whisper Small", "Whisper Turbo"]; const IMAGE_MODELS = ["SDXL", "Flux.1-dev", "Flux.1-schnell", "SD 1.5", "Pony Diffusion"]; const PERSONALITY_TRAITS = [ "Warm", "Witty", "Calm", "Energetic", "Sarcastic", "Nurturing", "Curious", "Playful", "Formal", "Casual", "Empathetic", "Direct", "Creative", "Analytical", "Protective", "Mischievous" ]; const SPEAKING_STYLES = [ "Conversational", "Poetic", "Concise", "Verbose", "Academic", "Informal", "Dramatic", "Deadpan", "Enthusiastic", "Measured" ]; const EMPTY_CHARACTER = { id: null, name: "", tagline: "", avatar: "", accentColor: "#7c6fff", personality: { traits: [], speakingStyle: "", coreValues: "", quirks: "", backstory: "", motivation: "", }, prompts: { systemPrompt: "", wakeWordResponse: "", fallbackResponse: "", errorResponse: "", customPrompts: [], }, models: { llm: "", tts: "", stt: "", imageGen: "", voiceCloneRef: "", ttsSpeed: 1.0, temperature: 0.7, }, liveRepresentation: { live2dModel: "", idleExpression: "", speakingExpression: "", thinkingExpression: "", happyExpression: "", vtsTriggers: "", }, userNotes: "", createdAt: null, updatedAt: null, }; const TABS = ["Identity", "Personality", "Prompts", "Models", "Live2D", "Notes"]; const TAB_ICONS = { Identity: "◈", Personality: "◉", Prompts: "◎", Models: "⬡", Live2D: "◇", Notes: "▣", }; function generateId() { return Date.now().toString(36) + Math.random().toString(36).slice(2); } function ColorPicker({ value, onChange }) { const presets = [ "#7c6fff","#ff6b9d","#00d4aa","#ff9f43","#48dbfb", "#ff6348","#a29bfe","#fd79a8","#55efc4","#fdcb6e" ]; return (
{presets.map(c => (
); } function TagSelector({ options, selected, onChange, max = 6 }) { return (
{options.map(opt => { const active = selected.includes(opt); return ( ); })}
); } function Field({ label, hint, children }) { return (
{hint &&

{hint}

} {children}
); } function Input({ value, onChange, placeholder, type = "text" }) { return ( onChange(e.target.value)} placeholder={placeholder} style={{ width: "100%", background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 8, padding: "10px 14px", color: "#fff", fontSize: 14, fontFamily: "inherit", outline: "none", boxSizing: "border-box", transition: "border-color 0.2s", }} onFocus={e => e.target.style.borderColor = "var(--accent)"} onBlur={e => e.target.style.borderColor = "rgba(255,255,255,0.1)"} /> ); } function Textarea({ value, onChange, placeholder, rows = 4 }) { return (