|
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