import { TLUiDialogProps, TldrawUiButton, TldrawUiButtonLabel, TldrawUiDialogBody, TldrawUiDialogCloseButton, TldrawUiDialogFooter, TldrawUiDialogHeader, TldrawUiDialogTitle, TldrawUiInput, } from "tldraw" import React from "react" import { PROVIDERS, AI_PERSONALITIES, OLLAMA_MODELS } from "../lib/settings" import { useAuth } from "../context/AuthContext" import { getOllamaConfig } from "../lib/clientConfig" export function SettingsDialog({ onClose }: TLUiDialogProps) { const { session } = useAuth() const [apiKeys, setApiKeys] = React.useState(() => { try { // First try to get user-specific API keys if logged in if (session.authed && session.username) { const userApiKeys = localStorage.getItem(`${session.username}_api_keys`) if (userApiKeys) { try { const parsed = JSON.parse(userApiKeys) if (parsed.keys) { return parsed.keys } } catch (e) { // Continue to fallback } } } // Fallback to global API keys const stored = localStorage.getItem("openai_api_key") if (stored) { try { const parsed = JSON.parse(stored) if (parsed.keys) { return parsed.keys } } catch (e) { // Fallback to old format return { openai: stored } } } return { openai: '', anthropic: '', google: '' } } catch (e) { return { openai: '', anthropic: '', google: '' } } }) const [personality, setPersonality] = React.useState(() => { try { // First try to get user-specific settings if logged in if (session.authed && session.username) { const userApiKeys = localStorage.getItem(`${session.username}_api_keys`) if (userApiKeys) { try { const parsed = JSON.parse(userApiKeys) if (parsed.personality) { return parsed.personality } } catch (e) { // Continue to fallback } } } // Fallback to global settings const stored = localStorage.getItem("openai_api_key") if (stored) { try { const parsed = JSON.parse(stored) if (parsed.personality) { return parsed.personality } } catch (e) { // Continue to fallback } } return 'web-developer' } catch (e) { return 'web-developer' } }) const [ollamaModel, setOllamaModel] = React.useState(() => { try { // First try to get user-specific settings if logged in if (session.authed && session.username) { const userApiKeys = localStorage.getItem(`${session.username}_api_keys`) if (userApiKeys) { try { const parsed = JSON.parse(userApiKeys) if (parsed.ollamaModel) { return parsed.ollamaModel } } catch (e) { // Continue to fallback } } } // Fallback to global settings const stored = localStorage.getItem("openai_api_key") if (stored) { try { const parsed = JSON.parse(stored) if (parsed.ollamaModel) { return parsed.ollamaModel } } catch (e) { // Continue to fallback } } return 'llama3.1:8b' } catch (e) { return 'llama3.1:8b' } }) // Check if Ollama is configured const ollamaConfig = getOllamaConfig() const handleKeyChange = (provider: string, value: string) => { const newKeys = { ...apiKeys, [provider]: value } setApiKeys(newKeys) saveSettings(newKeys, personality, ollamaModel) } const handlePersonalityChange = (newPersonality: string) => { setPersonality(newPersonality) saveSettings(apiKeys, newPersonality, ollamaModel) } const handleOllamaModelChange = (newModel: string) => { setOllamaModel(newModel) saveSettings(apiKeys, personality, newModel) } const saveSettings = (keys: any, personalityValue: string, ollamaModelValue: string) => { // Save to localStorage with the new structure const settings = { keys: keys, provider: 'openai', // Default provider models: Object.fromEntries(PROVIDERS.map((provider) => [provider.id, provider.models[0]])), ollamaModel: ollamaModelValue, personality: personalityValue, } // If user is logged in, save to user-specific storage if (session.authed && session.username) { localStorage.setItem(`${session.username}_api_keys`, JSON.stringify(settings)) // Also save to global storage as fallback localStorage.setItem("openai_api_key", JSON.stringify(settings)) } else { localStorage.setItem("openai_api_key", JSON.stringify(settings)) } } const validateKey = (provider: string, key: string) => { const providerConfig = PROVIDERS.find(p => p.id === provider) if (providerConfig && key.trim()) { return providerConfig.validate(key) } return true } return ( <> AI Settings {/* AI Personality Selector */} AI Personality handlePersonalityChange(e.target.value)} style={{ padding: "8px 12px", border: "1px solid #d1d5db", borderRadius: "6px", fontSize: "14px", backgroundColor: "white", cursor: "pointer" }} > {AI_PERSONALITIES.map((personality) => ( {personality.name} - {personality.description} ))} {/* Ollama Model Selector - Only show if Ollama is configured */} {ollamaConfig && ( 🦙 Private AI Model FREE Running on your private server. No API key needed - select quality vs speed. handleOllamaModelChange(e.target.value)} style={{ width: "100%", padding: "10px 12px", border: "1px solid #d1d5db", borderRadius: "6px", fontSize: "14px", backgroundColor: "white", cursor: "pointer" }} > {OLLAMA_MODELS.map((model) => ( {model.name} - {model.description} ))} Server: {ollamaConfig.url} Model size: {OLLAMA_MODELS.find(m => m.id === ollamaModel)?.size || 'Unknown'} )} {/* API Keys Section */} Cloud API Keys {ollamaConfig ? "Optional fallback - used when private AI is unavailable." : "Enter API keys to use cloud AI services."} {PROVIDERS.map((provider) => ( {provider.name} API Key {provider.models[0]} handleKeyChange(provider.id, value)} /> {apiKeys[provider.id] && !validateKey(provider.id, apiKeys[provider.id]) && ( Invalid API key format )} {provider.help && ( Learn more about {provider.name} setup → )} ))} {/* Close API Keys Section */} Note: API keys are stored locally in your browser. Make sure to use keys with appropriate usage limits for your needs. Close > ) }
Running on your private server. No API key needed - select quality vs speed.
{ollamaConfig ? "Optional fallback - used when private AI is unavailable." : "Enter API keys to use cloud AI services."}