|
1 | 1 | <script lang="ts"> |
2 | 2 | import { onMount } from 'svelte'; |
3 | | - import DataManager from '../ui/DataManager.svelte'; |
4 | 3 | import { ChevronLeft, ChevronRight, User, Key, Settings, CheckCircle, AlertCircle, Terminal } from 'lucide-svelte'; |
5 | 4 | import { guardianHelpers } from '$lib/stores/guardian.js'; |
6 | 5 | import { aiAnalysisHelpers } from '$lib/stores/ai-analysis.js'; |
7 | 6 | import { loadThemePreset, themePresets } from '$lib/stores/theme.js'; |
| 7 | + import { uiHelpers } from '$lib/stores/ui'; |
8 | 8 |
|
9 | 9 | let apiKeyInput = ''; |
10 | 10 | let guardianName = ''; |
11 | | - let preferences = { |
12 | | - dailyReminders: false, |
| 11 | + // Preferences trimmed to real features; remove unused reminders/notifications. |
| 12 | + let preferences: { aiInsights: boolean } = { |
13 | 13 | aiInsights: true, |
14 | | - notifications: true, |
15 | 14 | }; |
16 | 15 |
|
17 | 16 | let apiKeyStatus = 'unchecked'; // unchecked, checking, valid, invalid |
18 | | - let showDataManager = false; |
19 | 17 | |
20 | 18 | // CLI-style editing states |
21 | 19 | let editingField: string | null = null; |
|
24 | 22 | let themeIndex = 0; |
25 | 23 |
|
26 | 24 | const displayKey = (k: keyof typeof themePresets) => (k === 'tokyoNight' ? 'tokyo-night' : k); |
| 25 | + |
| 26 | + // OpenRouter model selection |
| 27 | + const models = [ |
| 28 | + 'anthropic/claude-3.5-sonnet', |
| 29 | + 'openai/gpt-4o-mini', |
| 30 | + 'google/gemini-1.5-pro', |
| 31 | + 'mistralai/mixtral-8x7b-instruct', |
| 32 | + ] as const; |
| 33 | + let currentModelIndex = 0; |
| 34 | +
|
| 35 | + function loadModelFromStorage() { |
| 36 | + const saved = guardianHelpers.load(); |
| 37 | + const savedModel = saved?.model as string | undefined; |
| 38 | + if (savedModel) { |
| 39 | + const idx = models.indexOf(savedModel as any); |
| 40 | + currentModelIndex = idx >= 0 ? idx : 0; |
| 41 | + } else { |
| 42 | + currentModelIndex = 0; |
| 43 | + } |
| 44 | + } |
| 45 | +
|
| 46 | + function displayModel() { |
| 47 | + return models[currentModelIndex] || models[0]; |
| 48 | + } |
| 49 | +
|
| 50 | + function persistModel() { |
| 51 | + guardianHelpers.update({ model: displayModel() }); |
| 52 | + } |
| 53 | +
|
| 54 | + function cycleModel(dir: 'prev' | 'next') { |
| 55 | + if (dir === 'next') { |
| 56 | + currentModelIndex = (currentModelIndex + 1) % models.length; |
| 57 | + } else { |
| 58 | + currentModelIndex = (currentModelIndex - 1 + models.length) % models.length; |
| 59 | + } |
| 60 | + persistModel(); |
| 61 | + } |
27 | 62 |
|
28 | 63 | onMount(() => { |
29 | 64 | // Load saved guardian data |
|
34 | 69 | preferences = { ...preferences, ...saved.preferences }; |
35 | 70 | } |
36 | 71 | |
37 | | - // Get current theme from localStorage |
| 72 | + // Get current theme from localStorage |
38 | 73 | const savedTheme = (localStorage.getItem('petalytics-theme') as keyof typeof themePresets) || 'everforest'; |
39 | 74 | currentThemeKey = themeKeys.includes(savedTheme) ? savedTheme : 'everforest'; |
40 | 75 | themeIndex = themeKeys.indexOf(currentThemeKey); |
| 76 | +
|
| 77 | + // Get model selection from guardian storage |
| 78 | + loadModelFromStorage(); |
41 | 79 | }); |
42 | 80 |
|
43 | 81 | function toggleTheme(direction: 'prev' | 'next') { |
|
52 | 90 | } |
53 | 91 |
|
54 | 92 | function togglePreference(key: keyof typeof preferences) { |
55 | | - preferences[key] = !preferences[key]; |
56 | | - handlePreferenceChange(key); |
| 93 | + // currently only ai_insights is supported |
| 94 | + (preferences as any)[key] = !(preferences as any)[key]; |
| 95 | + saveGuardianInfo(); |
57 | 96 | } |
58 | 97 |
|
59 | 98 | function startEdit(field: string) { |
|
119 | 158 | }); |
120 | 159 | } |
121 | 160 |
|
122 | | - function handlePreferenceChange(key: keyof typeof preferences) { |
123 | | - preferences[key] = !preferences[key]; |
124 | | - saveGuardianInfo(); |
125 | | - } |
| 161 | + // removed handlePreferenceChange; inline save in togglePreference |
126 | 162 |
|
127 | 163 | // Keyboard activate handler for elements with role="button" |
128 | 164 | function handleActivate(e: KeyboardEvent, action: () => void) { |
|
194 | 230 | </span> |
195 | 231 | </div> |
196 | 232 |
|
| 233 | + <!-- Model selection (OpenRouter) --> |
| 234 | + <div class="cli-row px-2 py-1"> |
| 235 | + <span class="label" style="color: var(--petalytics-foam);">model</span> |
| 236 | + <span class="value" style="color: var(--petalytics-text);">{displayModel()}</span> |
| 237 | + <div class="ml-2 flex items-center space-x-1"> |
| 238 | + <button type="button" class="arrow-btn" onclick={() => cycleModel('prev')} aria-label="Previous model"><</button> |
| 239 | + <button type="button" class="arrow-btn" onclick={() => cycleModel('next')} aria-label="Next model">></button> |
| 240 | + </div> |
| 241 | + </div> |
| 242 | + |
197 | 243 | <!-- Theme row --> |
198 | 244 | <div class="cli-row px-2 py-1"> |
199 | 245 | <span class="label" style="color: var(--petalytics-foam);">theme</span> |
200 | | - <span class="value" style="color: var(--petalytics-text);"> |
201 | | - {displayKey(currentThemeKey)} |
202 | | - </span> |
| 246 | + <span class="value" style="color: var(--petalytics-text);">{displayKey(currentThemeKey)}</span> |
203 | 247 | <div class="ml-2 flex items-center space-x-1"> |
204 | 248 | <button type="button" class="arrow-btn" onclick={() => toggleTheme('prev')} aria-label="Previous theme"><</button> |
205 | 249 | <button type="button" class="arrow-btn" onclick={() => toggleTheme('next')} aria-label="Next theme">></button> |
206 | 250 | </div> |
207 | 251 | </div> |
208 | 252 |
|
209 | | - <!-- Separator line --> |
210 | | - <div class="my-3"> |
211 | | - <div class="border-t" style="border-color: var(--petalytics-border);"></div> |
212 | | - </div> |
213 | | - |
214 | | - <!-- Preferences section header --> |
215 | | - <div class="cli-row px-2 py-1"> |
216 | | - <span style="color: var(--petalytics-subtle);">#</span> |
217 | | - <span class="ml-2" style="color: var(--petalytics-gold);">preferences</span> |
218 | | - </div> |
219 | | - |
220 | | - <div class="cli-row px-2 py-1" role="button" tabindex="0" aria-pressed={preferences.dailyReminders} onclick={() => togglePreference('dailyReminders')} onkeydown={(e) => handleActivate(e, () => togglePreference('dailyReminders'))}> |
221 | | - <span class="label" style="color: var(--petalytics-foam);">daily_reminders</span> |
222 | | - <span class="value" style="color: var(--petalytics-text);"> |
223 | | - {preferences.dailyReminders ? 'enabled' : 'disabled'} |
224 | | - </span> |
225 | | - <span class="ml-2" style="color: {preferences.dailyReminders ? 'var(--petalytics-pine)' : 'var(--petalytics-subtle)'};"> |
226 | | - {preferences.dailyReminders ? '●' : '○'} |
227 | | - </span> |
228 | | - </div> |
229 | | - |
| 253 | + <!-- Preferences: only ai_insights --> |
230 | 254 | <div class="cli-row px-2 py-1" role="button" tabindex="0" aria-pressed={preferences.aiInsights} onclick={() => togglePreference('aiInsights')} onkeydown={(e) => handleActivate(e, () => togglePreference('aiInsights'))}> |
231 | 255 | <span class="label" style="color: var(--petalytics-foam);">ai_insights</span> |
232 | | - <span class="value" style="color: var(--petalytics-text);"> |
233 | | - {preferences.aiInsights ? 'enabled' : 'disabled'} |
234 | | - </span> |
235 | | - <span class="ml-2" style="color: {preferences.aiInsights ? 'var(--petalytics-pine)' : 'var(--petalytics-subtle)'};"> |
236 | | - {preferences.aiInsights ? '●' : '○'} |
237 | | - </span> |
238 | | - </div> |
239 | | - |
240 | | - <div class="cli-row px-2 py-1" role="button" tabindex="0" aria-pressed={preferences.notifications} onclick={() => togglePreference('notifications')} onkeydown={(e) => handleActivate(e, () => togglePreference('notifications'))}> |
241 | | - <span class="label" style="color: var(--petalytics-foam);">notifications</span> |
242 | | - <span class="value" style="color: var(--petalytics-text);"> |
243 | | - {preferences.notifications ? 'enabled' : 'disabled'} |
244 | | - </span> |
245 | | - <span class="ml-2" style="color: {preferences.notifications ? 'var(--petalytics-pine)' : 'var(--petalytics-subtle)'};"> |
246 | | - {preferences.notifications ? '●' : '○'} |
247 | | - </span> |
| 256 | + <span class="value" style="color: var(--petalytics-text);">{preferences.aiInsights ? 'enabled' : 'disabled'}</span> |
| 257 | + <span class="ml-2" style="color: {preferences.aiInsights ? 'var(--petalytics-pine)' : 'var(--petalytics-subtle)'};">{preferences.aiInsights ? '●' : '○'}</span> |
248 | 258 | </div> |
249 | 259 |
|
250 | 260 | <!-- Separator line --> |
251 | 261 | <div class="my-3"> |
252 | 262 | <div class="border-t" style="border-color: var(--petalytics-border);"></div> |
253 | 263 | </div> |
254 | 264 |
|
255 | | - <!-- Status section header --> |
256 | | - <div class="cli-row px-2 py-1"> |
257 | | - <span style="color: var(--petalytics-subtle);">#</span> |
258 | | - <span class="ml-2" style="color: var(--petalytics-gold);">status</span> |
259 | | - </div> |
260 | | - |
261 | | - <div class="cli-row flex items-center px-2 py-1"> |
262 | | - <span style="color: var(--petalytics-subtle);">></span> |
263 | | - <span style="color: var(--petalytics-foam);">api_status</span> |
264 | | - <span class="flex-1 text-right" style="color: {apiKeyStatus === 'valid' ? 'var(--petalytics-pine)' : apiKeyStatus === 'invalid' ? 'var(--petalytics-love)' : 'var(--petalytics-gold)'};"> |
265 | | - {apiKeyStatus === 'valid' ? 'connected' : apiKeyStatus === 'invalid' ? 'invalid' : apiKeyStatus === 'checking' ? 'checking...' : 'not_set'} |
266 | | - </span> |
267 | | - </div> |
268 | | - |
269 | | - <!-- Data management toggle --> |
270 | | - <div class="cli-row px-2 py-1" role="button" tabindex="0" aria-expanded={showDataManager} onclick={() => showDataManager = !showDataManager} onkeydown={(e) => handleActivate(e, () => (showDataManager = !showDataManager))}> |
| 265 | + <!-- Data Manager launcher (opens in right panel) --> |
| 266 | + <div class="cli-row px-2 py-1" role="button" tabindex="0" onclick={() => uiHelpers.setView('dataManager')} onkeydown={(e) => handleActivate(e, () => uiHelpers.setView('dataManager'))}> |
271 | 267 | <span class="label" style="color: var(--petalytics-foam);">data_manager</span> |
272 | | - <span class="value" style="color: var(--petalytics-text);"> |
273 | | - {showDataManager ? 'show' : 'hidden'} |
274 | | - </span> |
275 | | - <ChevronRight |
276 | | - size={14} |
277 | | - style="color: var(--petalytics-subtle); transform: {showDataManager ? 'rotate(90deg)' : 'rotate(0deg)'}; transition: transform 0.2s;" |
278 | | - class="ml-2" |
279 | | - /> |
| 268 | + <span class="value" style="color: var(--petalytics-text);">open</span> |
| 269 | + <ChevronRight size={14} style="color: var(--petalytics-subtle);" class="ml-2" /> |
280 | 270 | </div> |
281 | | - |
282 | | - {#if showDataManager} |
283 | | - <div class="mt-2 p-2 rounded" style="background: var(--petalytics-overlay);"> |
284 | | - <DataManager /> |
285 | | - </div> |
286 | | - {/if} |
287 | 271 | </div> |
288 | 272 | </div> |
289 | 273 |
|
|
0 commit comments