import { useState, useEffect, useRef } from "react" import { useAuth } from "../context/AuthContext" import { useDialogs } from "tldraw" import { SettingsDialog } from "./SettingsDialog" import { getFathomApiKey, saveFathomApiKey, removeFathomApiKey, isFathomApiKeyConfigured } from "../lib/fathomApiKey" // AI tool model configurations const AI_TOOLS = [ { id: 'chat', name: 'Chat Assistant', icon: '💬', description: 'Conversational AI for questions and discussions', models: { primary: { name: 'Ollama (Local)', model: 'llama3.1:8b', type: 'local' }, fallback: { name: 'OpenAI', model: 'gpt-4o', type: 'cloud' }, } }, { id: 'make-real', name: 'Make Real', icon: '🔧', description: 'Convert wireframes to working prototypes', models: { primary: { name: 'Anthropic', model: 'claude-sonnet-4-5', type: 'cloud' }, fallback: { name: 'OpenAI', model: 'gpt-4o', type: 'cloud' }, } }, { id: 'image-gen', name: 'Image Generation', icon: '🎨', description: 'Generate images from text prompts', models: { primary: { name: 'RunPod', model: 'Stable Diffusion XL', type: 'gpu' }, } }, { id: 'video-gen', name: 'Video Generation', icon: '🎬', description: 'Generate videos from images', models: { primary: { name: 'RunPod', model: 'Wan2.1 I2V', type: 'gpu' }, } }, { id: 'transcription', name: 'Transcription', icon: '🎤', description: 'Transcribe audio to text', models: { primary: { name: 'Browser', model: 'Web Speech API', type: 'local' }, fallback: { name: 'Whisper', model: 'whisper-large-v3', type: 'local' }, } }, { id: 'mycelial', name: 'Mycelial Intelligence', icon: '🍄', description: 'Analyze connections between concepts', models: { primary: { name: 'Ollama (Local)', model: 'llama3.1:70b', type: 'local' }, fallback: { name: 'Anthropic', model: 'claude-sonnet-4-5', type: 'cloud' }, } }, ] interface UserSettingsModalProps { onClose: () => void isDarkMode: boolean onToggleDarkMode: () => void } export function UserSettingsModal({ onClose, isDarkMode, onToggleDarkMode }: UserSettingsModalProps) { const { session, setSession } = useAuth() const { addDialog, removeDialog } = useDialogs() const modalRef = useRef(null) const [hasApiKey, setHasApiKey] = useState(false) const [hasFathomApiKey, setHasFathomApiKey] = useState(false) const [showFathomApiKeyInput, setShowFathomApiKeyInput] = useState(false) const [fathomApiKeyInput, setFathomApiKeyInput] = useState('') const [activeTab, setActiveTab] = useState<'general' | 'ai' | 'integrations'>('general') // Check API key status const checkApiKeys = () => { const settings = localStorage.getItem("openai_api_key") try { if (settings) { try { const parsed = JSON.parse(settings) if (parsed.keys) { const hasValidKey = Object.values(parsed.keys).some(key => typeof key === 'string' && key.trim() !== '' ) setHasApiKey(hasValidKey) } else { setHasApiKey(typeof settings === 'string' && settings.trim() !== '') } } catch (e) { setHasApiKey(typeof settings === 'string' && settings.trim() !== '') } } else { setHasApiKey(false) } } catch (e) { setHasApiKey(false) } } useEffect(() => { checkApiKeys() }, []) useEffect(() => { if (session.authed && session.username) { setHasFathomApiKey(isFathomApiKeyConfigured(session.username)) } }, [session.authed, session.username]) // Handle escape key and click outside useEffect(() => { const handleEscape = (e: KeyboardEvent) => { if (e.key === 'Escape') { onClose() } } const handleClickOutside = (e: MouseEvent) => { if (modalRef.current && !modalRef.current.contains(e.target as Node)) { onClose() } } document.addEventListener('keydown', handleEscape) document.addEventListener('mousedown', handleClickOutside) return () => { document.removeEventListener('keydown', handleEscape) document.removeEventListener('mousedown', handleClickOutside) } }, [onClose]) const openApiKeysDialog = () => { addDialog({ id: "api-keys", component: ({ onClose: dialogClose }: { onClose: () => void }) => ( { dialogClose() removeDialog("api-keys") checkApiKeys() }} /> ), }) } const handleSetVault = () => { window.dispatchEvent(new CustomEvent('open-obsidian-browser')) onClose() } return (

Settings

{activeTab === 'general' && (
Appearance Toggle between light and dark mode
)} {activeTab === 'ai' && (
{/* AI Tools Overview */}

AI Tools & Models

Each tool uses optimized AI models. Local models run on your private server for free, cloud models require API keys.

{AI_TOOLS.map((tool) => (
{tool.icon} {tool.name}

{tool.description}

{tool.models.primary.name}: {tool.models.primary.model} {tool.models.fallback && ( Fallback: {tool.models.fallback.model} )}
))}
{/* API Keys Configuration */}
AI API Keys {hasApiKey ? 'Your cloud AI models are configured and ready' : 'Configure API keys to use cloud AI features'}
{hasApiKey ? 'Configured' : 'Not Set'}
{/* Model type legend */}
Local (Free) GPU (RunPod) Cloud (API Key)
)} {activeTab === 'integrations' && (
{/* Knowledge Management Section */}

Knowledge Management

{/* Obsidian Vault - Local Files */}
📁
Obsidian Vault (Local)

Import notes directly from your local Obsidian vault

{session.obsidianVaultName ? 'Connected' : 'Not Set'}
{session.obsidianVaultName && (

Current vault: {session.obsidianVaultName}

)}
{/* Obsidian Quartz - Published Notes */}
🌐
Obsidian Quartz (Web)

Import notes from your published Quartz site via GitHub

Available

Quartz is a static site generator for Obsidian. If you publish your notes with Quartz, you can browse and import them here.

Learn more about Quartz →
{/* Meeting & Communication Section */}

Meeting & Communication

{/* Fathom Meetings */}
🎥
Fathom Meetings

Import meeting transcripts and AI summaries

{hasFathomApiKey ? 'Connected' : 'Not Set'}
{showFathomApiKeyInput ? (
setFathomApiKeyInput(e.target.value)} placeholder="Enter Fathom API key..." className="settings-input" style={{ width: '100%', marginBottom: '8px' }} onKeyDown={(e) => { if (e.key === 'Enter' && fathomApiKeyInput.trim()) { saveFathomApiKey(fathomApiKeyInput.trim(), session.username) setHasFathomApiKey(true) setShowFathomApiKeyInput(false) setFathomApiKeyInput('') } else if (e.key === 'Escape') { setShowFathomApiKeyInput(false) setFathomApiKeyInput('') } }} autoFocus />
Get your API key from Fathom Settings →
) : (
{hasFathomApiKey && ( )}
)}
{/* Future Integrations Placeholder */}

More integrations coming soon: Google Calendar, Notion, and more

)}
) }