Skip to content

Commit 07f5ee1

Browse files
committed
feat: implement data manager integration and model selection in Guardian and Viewport panels
1 parent e42397e commit 07f5ee1

File tree

7 files changed

+94
-90
lines changed

7 files changed

+94
-90
lines changed

src/lib/components/panels/GuardianPanel.svelte

Lines changed: 66 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
<script lang="ts">
22
import { onMount } from 'svelte';
3-
import DataManager from '../ui/DataManager.svelte';
43
import { ChevronLeft, ChevronRight, User, Key, Settings, CheckCircle, AlertCircle, Terminal } from 'lucide-svelte';
54
import { guardianHelpers } from '$lib/stores/guardian.js';
65
import { aiAnalysisHelpers } from '$lib/stores/ai-analysis.js';
76
import { loadThemePreset, themePresets } from '$lib/stores/theme.js';
7+
import { uiHelpers } from '$lib/stores/ui';
88
99
let apiKeyInput = '';
1010
let guardianName = '';
11-
let preferences = {
12-
dailyReminders: false,
11+
// Preferences trimmed to real features; remove unused reminders/notifications.
12+
let preferences: { aiInsights: boolean } = {
1313
aiInsights: true,
14-
notifications: true,
1514
};
1615
1716
let apiKeyStatus = 'unchecked'; // unchecked, checking, valid, invalid
18-
let showDataManager = false;
1917
2018
// CLI-style editing states
2119
let editingField: string | null = null;
@@ -24,6 +22,43 @@
2422
let themeIndex = 0;
2523
2624
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+
}
2762
2863
onMount(() => {
2964
// Load saved guardian data
@@ -34,10 +69,13 @@
3469
preferences = { ...preferences, ...saved.preferences };
3570
}
3671
37-
// Get current theme from localStorage
72+
// Get current theme from localStorage
3873
const savedTheme = (localStorage.getItem('petalytics-theme') as keyof typeof themePresets) || 'everforest';
3974
currentThemeKey = themeKeys.includes(savedTheme) ? savedTheme : 'everforest';
4075
themeIndex = themeKeys.indexOf(currentThemeKey);
76+
77+
// Get model selection from guardian storage
78+
loadModelFromStorage();
4179
});
4280
4381
function toggleTheme(direction: 'prev' | 'next') {
@@ -52,8 +90,9 @@
5290
}
5391
5492
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();
5796
}
5897
5998
function startEdit(field: string) {
@@ -119,10 +158,7 @@
119158
});
120159
}
121160
122-
function handlePreferenceChange(key: keyof typeof preferences) {
123-
preferences[key] = !preferences[key];
124-
saveGuardianInfo();
125-
}
161+
// removed handlePreferenceChange; inline save in togglePreference
126162
127163
// Keyboard activate handler for elements with role="button"
128164
function handleActivate(e: KeyboardEvent, action: () => void) {
@@ -194,96 +230,44 @@
194230
</span>
195231
</div>
196232

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">&lt;</button>
239+
<button type="button" class="arrow-btn" onclick={() => cycleModel('next')} aria-label="Next model">&gt;</button>
240+
</div>
241+
</div>
242+
197243
<!-- Theme row -->
198244
<div class="cli-row px-2 py-1">
199245
<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>
203247
<div class="ml-2 flex items-center space-x-1">
204248
<button type="button" class="arrow-btn" onclick={() => toggleTheme('prev')} aria-label="Previous theme">&lt;</button>
205249
<button type="button" class="arrow-btn" onclick={() => toggleTheme('next')} aria-label="Next theme">&gt;</button>
206250
</div>
207251
</div>
208252

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 -->
230254
<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'))}>
231255
<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>
248258
</div>
249259

250260
<!-- Separator line -->
251261
<div class="my-3">
252262
<div class="border-t" style="border-color: var(--petalytics-border);"></div>
253263
</div>
254264

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'))}>
271267
<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" />
280270
</div>
281-
282-
{#if showDataManager}
283-
<div class="mt-2 p-2 rounded" style="background: var(--petalytics-overlay);">
284-
<DataManager />
285-
</div>
286-
{/if}
287271
</div>
288272
</div>
289273

src/lib/components/panels/Viewport.svelte

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { aiAnalysisHelpers, isAnalyzing } from '$lib/stores/ai-analysis';
55
import { PenTool, Brain, Calendar, Activity } from 'lucide-svelte';
66
import AIInsightsCard from '../ui/AIInsightsCard.svelte';
7+
import DataManager from '../ui/DataManager.svelte';
78
import EmptyState from '../ui/EmptyState.svelte';
89
import Skeleton from '../ui/Skeleton.svelte';
910
import { rightPanelView, uiHelpers } from '$lib/stores/ui';
@@ -162,12 +163,12 @@
162163
/>
163164
{/if}
164165
<div>
165-
<h2 class="text-xl font-bold" style="color: var(--petalytics-text);">{selectedPet ? selectedPet.name : 'Memories'}</h2>
166-
<p class="text-xs" style="color: var(--petalytics-subtle);">{selectedPet ? petSubtitle(selectedPet) : 'Archived memories'}</p>
166+
<h2 class="text-xl font-bold" style="color: var(--petalytics-text);">{selectedPet ? selectedPet.name : (currentView === 'memories' ? 'Memories' : currentView === 'dataManager' ? 'Data Manager' : 'Petalytics')}</h2>
167+
<p class="text-xs" style="color: var(--petalytics-subtle);">{selectedPet ? petSubtitle(selectedPet) : (currentView === 'memories' ? 'Archived memories' : currentView === 'dataManager' ? 'Backup, export, and import' : '')}</p>
167168
</div>
168169
</div>
169170

170-
{#if currentView === 'memories'}
171+
{#if currentView === 'memories' || currentView === 'dataManager'}
171172
<div class="flex space-x-2">
172173
<button
173174
on:click={() => {
@@ -281,6 +282,21 @@
281282
{/each}
282283
{/if}
283284
</div>
285+
{:else if currentView === 'dataManager'}
286+
<!-- Data Manager full-width in right panel -->
287+
<div class="space-y-4 font-mono">
288+
<div class="rounded p-3" style="background: color-mix(in oklab, var(--petalytics-overlay) 60%, transparent); border: 1px solid var(--petalytics-border);">
289+
<div class="flex items-center justify-between">
290+
<div>
291+
<div class="text-base font-semibold" style="color: var(--petalytics-text);">Data Manager</div>
292+
<div class="text-xs" style="color: var(--petalytics-subtle);">Backup, export, and import</div>
293+
</div>
294+
</div>
295+
</div>
296+
<div class="p-2 rounded" style="background: var(--petalytics-surface); border: 1px solid var(--petalytics-border);">
297+
<DataManager />
298+
</div>
299+
</div>
284300
{:else if currentView === 'dashboard' && selectedPet}
285301
<!-- Dashboard View -->
286302
<div class="dashboard-grid space-y-4">

src/lib/stores/ai-analysis.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ let analyzer: AIAnalyzer | null = null;
1212
// Initialize analyzer when API key is available
1313
guardianStore.subscribe((guardian) => {
1414
if (guardian.apiKey && guardian.apiKeyValid) {
15-
analyzer = new AIAnalyzer(guardian.apiKey);
15+
analyzer = new AIAnalyzer(guardian.apiKey, (guardian as any).model || 'anthropic/claude-3.5-sonnet');
1616
} else {
1717
analyzer = null;
1818
}

src/lib/stores/guardian.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const STORAGE_KEY = 'petalytics-guardian';
77
const defaultGuardian = {
88
name: '',
99
apiKey: '',
10+
model: 'anthropic/claude-3.5-sonnet',
1011
preferences: {
1112
dailyReminders: false,
1213
aiInsights: true,

src/lib/stores/ui.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { writable } from 'svelte/store';
22

3-
export type RightPanelView = 'dashboard' | 'journal' | 'history' | 'memories' | 'confirmArchive';
3+
export type RightPanelView = 'dashboard' | 'journal' | 'history' | 'memories' | 'confirmArchive' | 'dataManager';
44

55
export const rightPanelView = writable<RightPanelView>('dashboard');
66

src/lib/types/Guardian.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface Guardian {
1010
relationship: string;
1111
};
1212
apiKey: string;
13+
model?: string;
1314
preferences: {
1415
dailyReminders: boolean;
1516
aiInsights: boolean;

src/lib/utils/ai-analysis.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ export interface AnalysisResult {
1313
export class AIAnalyzer {
1414
private apiKey: string;
1515
private baseUrl = 'https://openrouter.ai/api/v1/chat/completions';
16+
private model: string;
1617

17-
constructor(apiKey: string) {
18+
constructor(apiKey: string, model: string = 'anthropic/claude-3.5-sonnet') {
1819
this.apiKey = apiKey;
20+
this.model = model;
1921
}
2022

2123
async analyzeJournalEntry(pet: PetPanelData, entry: JournalEntry): Promise<AnalysisResult> {
@@ -32,7 +34,7 @@ export class AIAnalyzer {
3234
'X-Title': 'Petalytics',
3335
},
3436
body: JSON.stringify({
35-
model: 'anthropic/claude-3.5-sonnet',
37+
model: this.model,
3638
messages: [
3739
{
3840
role: 'system',
@@ -129,7 +131,7 @@ Consider breed-specific traits, age-related needs, and behavioral patterns. Keep
129131
'X-Title': 'Petalytics',
130132
},
131133
body: JSON.stringify({
132-
model: 'openai/gpt-3.5-turbo',
134+
model: this.model,
133135
messages: [{ role: 'user', content: 'test' }],
134136
max_tokens: 1,
135137
}),

0 commit comments

Comments
 (0)