|
10 | 10 | import EmptyState from '../ui/EmptyState.svelte';
|
11 | 11 | import Skeleton from '../ui/Skeleton.svelte';
|
12 | 12 | import { rightPanelView, uiHelpers } from '$lib/stores/ui';
|
| 13 | + import { guardianStore } from '$lib/stores/guardian'; |
13 | 14 | import type { RightPanelView } from '$lib/stores/ui';
|
14 | 15 | import type { PetPanelData } from '$lib/types/Pet';
|
15 | 16 | import type { JournalEntry } from '$lib/types/JournalEntry';
|
|
27 | 28 | let selectedActivity = '';
|
28 | 29 | let isSubmitting = false;
|
29 | 30 | let loading = false;
|
| 31 | + let aiEnabled = true; |
30 | 32 |
|
31 | 33 | // Computed values
|
32 | 34 | $: lastEntry = selectedPet?.journalEntries?.length
|
|
58 | 60 | }
|
59 | 61 |
|
60 | 62 | onMount(() => {
|
| 63 | + // Reflect guardian preference for Ruixen insights |
| 64 | + guardianStore.subscribe((g) => { |
| 65 | + aiEnabled = !!(g && g.preferences && g.preferences.aiInsights); |
| 66 | + }); |
| 67 | +
|
61 | 68 | // Subscribe first so incoming loads propagate into state
|
62 | 69 | petStore.subscribe((list) => {
|
63 | 70 | pets = list || [];
|
|
118 | 125 | // Add entry to pet
|
119 | 126 | petHelpers.addJournalEntry(selectedPet.id, entry);
|
120 | 127 |
|
121 |
| - // Analyze with Ruixen orchestrator (rate-limited with offline fallback) |
122 |
| - try { |
123 |
| - const res = await ruixenHelpers.analyzeDaily(selectedPet, entry); |
124 |
| - if (res) { |
125 |
| - analysisStore.update((cache) => ({ ...cache, [entry.id]: res })); |
126 |
| - // Persist onto the entry so it survives reloads and exports |
127 |
| - const petNow = petHelpers.getPet(selectedPet.id); |
128 |
| - if (petNow) { |
129 |
| - const updatedEntries = (petNow.journalEntries || []).map((e) => |
130 |
| - e.id === entry.id |
131 |
| - ? { |
132 |
| - ...e, |
133 |
| - aiAnalysis: { |
134 |
| - ...res, |
135 |
| - modelId: (selectedPet as any)?.model || undefined, |
136 |
| - analyzedAt: new Date().toISOString(), |
137 |
| - }, |
138 |
| - } |
139 |
| - : e |
140 |
| - ); |
141 |
| - petHelpers.update(petNow.id, { journalEntries: updatedEntries }); |
| 128 | + // Analyze with Ruixen orchestrator (rate-limited with offline fallback) if enabled |
| 129 | + if (aiEnabled) { |
| 130 | + try { |
| 131 | + const res = await ruixenHelpers.analyzeDaily(selectedPet, entry); |
| 132 | + if (res) { |
| 133 | + analysisStore.update((cache) => ({ ...cache, [entry.id]: res })); |
| 134 | + // Persist onto the entry so it survives reloads and exports |
| 135 | + const petNow = petHelpers.getPet(selectedPet.id); |
| 136 | + if (petNow) { |
| 137 | + const updatedEntries = (petNow.journalEntries || []).map((e) => |
| 138 | + e.id === entry.id |
| 139 | + ? { |
| 140 | + ...e, |
| 141 | + aiAnalysis: { |
| 142 | + ...res, |
| 143 | + modelId: (selectedPet as any)?.model || undefined, |
| 144 | + analyzedAt: new Date().toISOString(), |
| 145 | + }, |
| 146 | + } |
| 147 | + : e |
| 148 | + ); |
| 149 | + petHelpers.update(petNow.id, { journalEntries: updatedEntries }); |
| 150 | + } |
142 | 151 | }
|
| 152 | + } catch (error) { |
| 153 | + console.error('Ruixen analysis failed:', error); |
143 | 154 | }
|
144 |
| - } catch (error) { |
145 |
| - console.error('Ruixen analysis failed:', error); |
146 | 155 | }
|
147 | 156 |
|
148 | 157 | // Reset form
|
|
170 | 179 | <Skeleton height="h-6" />
|
171 | 180 | <Skeleton height="h-4" />
|
172 | 181 | </div>
|
173 |
| - {:else if !selectedPet && currentView !== 'memories'} |
174 |
| - <EmptyState |
175 |
| - icon="file-text" |
176 |
| - title="No pet selected" |
177 |
| - description="Select a pet from the left panel to view details, add journal entries, and see AI insights." |
178 |
| - actionText="Add a Pet" |
179 |
| - onAction={() => { |
180 |
| - uiHelpers.openCreatePetForm(); |
181 |
| - }} |
182 |
| - /> |
| 182 | + {:else if !selectedPet && currentView !== 'memories' && currentView !== 'dataManager'} |
| 183 | + <div class="h-full grid place-items-center"> |
| 184 | + <EmptyState |
| 185 | + icon="file-text" |
| 186 | + title="No pet selected" |
| 187 | + description="Select a pet from the left panel to view details, add journal entries, and see Ruixen insights." |
| 188 | + actionText="Import Data" |
| 189 | + onAction={() => { |
| 190 | + uiHelpers.setView('dataManager'); |
| 191 | + }} |
| 192 | + secondaryActionText="Add a Pet" |
| 193 | + onSecondaryAction={() => { |
| 194 | + uiHelpers.openCreatePetForm(); |
| 195 | + }} |
| 196 | + /> |
| 197 | + </div> |
183 | 198 | {:else}
|
184 | 199 | <div class="pet-viewport h-full flex flex-col">
|
185 | 200 | <!-- Header with pet info and navigation -->
|
|
373 | 388 | {:else if currentView === 'dataManager'}
|
374 | 389 | <!-- Data Manager full-width in right panel -->
|
375 | 390 | <div class="space-y-4 font-mono">
|
376 |
| - <div |
377 |
| - class="rounded p-3" |
378 |
| - style="background: color-mix(in oklab, var(--petalytics-overlay) 60%, transparent); border: 1px solid var(--petalytics-border);" |
379 |
| - > |
380 |
| - <div class="flex items-center justify-between"> |
381 |
| - <div> |
382 |
| - <div class="text-base font-semibold" style="color: var(--petalytics-text);"> |
383 |
| - Data Manager |
384 |
| - </div> |
385 |
| - <div class="text-xs" style="color: var(--petalytics-subtle);"> |
386 |
| - Backup, export, and import |
387 |
| - </div> |
388 |
| - </div> |
389 |
| - </div> |
390 |
| - </div> |
391 | 391 | <div
|
392 | 392 | class="p-2 rounded"
|
393 | 393 | style="background: var(--petalytics-surface); border: 1px solid var(--petalytics-border);"
|
|
502 | 502 | style="color: var(--petalytics-text);"
|
503 | 503 | >
|
504 | 504 | <Brain size={16} class="mr-2" style="color: var(--petalytics-accent);" />
|
505 |
| - AI Insights (Ruixen) |
| 505 | + Ruixen Insights |
506 | 506 | </h3>
|
507 | 507 | {#if selectedPet.journalEntries.length === 0}
|
508 | 508 | <p class="text-sm" style="color: var(--petalytics-subtle);">
|
509 | 509 | Add journal entries to get AI-powered insights about {selectedPet.name}'s
|
510 | 510 | well-being.
|
511 | 511 | </p>
|
| 512 | + {:else if aiEnabled} |
| 513 | + <AIInsightsCard petId={selectedPet.id} entryId={lastEntry?.id} compact={true} /> |
512 | 514 | {:else}
|
513 |
| - <AIInsightsCard petId={selectedPet.id} entryId={lastEntry?.id} /> |
| 515 | + <p class="text-xs" style="color: var(--petalytics-subtle);"> |
| 516 | + Ruixen: offline (enable insights or add API key) |
| 517 | + </p> |
514 | 518 | {/if}
|
515 | 519 | </div>
|
516 | 520 | </div>
|
|
0 commit comments