|
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