Skip to content

Commit 5f7395e

Browse files
committed
Revamp insights UI and rename to Ruixen Insights
Unify terminology by replacing AI insights labels with Ruixen Insights across panels and cards. Enhance RuixenInsights component: ensure reactive data refresh on pet changes and introduce styled pills for section tags, improved list formatting, and due indicators for reminders to boost clarity. Enable compact mode on AIInsightsCard for cleaner layout. Extend species-specific tips with guidance for rabbits and hamsters.
1 parent 17c97ee commit 5f7395e

File tree

9 files changed

+170
-89
lines changed

9 files changed

+170
-89
lines changed

src/lib/components/panels/GuardianPanel.svelte

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -364,14 +364,21 @@
364364
onkeydown={(e) => handleActivate(e, () => togglePreference('aiInsights'))}
365365
>
366366
<span class="label" style="color: var(--petalytics-foam);">ai_insights</span>
367-
<span class="value" style="color: var(--petalytics-text);"
368-
>{preferences.aiInsights ? 'enabled' : 'disabled'}</span
369-
>
367+
<span class="value" style="color: var(--petalytics-text);">
368+
{#if preferences.aiInsights}
369+
{guardianHelpers.load()?.apiKeyValid ? 'cloud' : 'offline'}
370+
{:else}
371+
offline
372+
{/if}
373+
</span>
370374
<span
371375
class="ml-2"
372376
style="color: {preferences.aiInsights
373-
? 'var(--petalytics-pine)'
374-
: 'var(--petalytics-subtle)'};">{preferences.aiInsights ? '' : ''}</span
377+
? guardianHelpers.load()?.apiKeyValid
378+
? 'var(--petalytics-iris)'
379+
: 'var(--petalytics-subtle)'
380+
: 'var(--petalytics-subtle)'};"
381+
>{preferences.aiInsights && guardianHelpers.load()?.apiKeyValid ? '☁︎' : ''}</span
375382
>
376383
</div>
377384

src/lib/components/panels/PetPanel.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@
288288

289289
<div
290290
class="cli-content p-3 font-mono text-sm overflow-y-auto"
291-
style="color: var(--petalytics-text);"
291+
style="color: var(--petalytics-text); max-height: 100%;"
292292
>
293293
<!-- Toggle create form -->
294294
<div

src/lib/components/panels/Viewport.svelte

Lines changed: 54 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import EmptyState from '../ui/EmptyState.svelte';
1111
import Skeleton from '../ui/Skeleton.svelte';
1212
import { rightPanelView, uiHelpers } from '$lib/stores/ui';
13+
import { guardianStore } from '$lib/stores/guardian';
1314
import type { RightPanelView } from '$lib/stores/ui';
1415
import type { PetPanelData } from '$lib/types/Pet';
1516
import type { JournalEntry } from '$lib/types/JournalEntry';
@@ -27,6 +28,7 @@
2728
let selectedActivity = '';
2829
let isSubmitting = false;
2930
let loading = false;
31+
let aiEnabled = true;
3032
3133
// Computed values
3234
$: lastEntry = selectedPet?.journalEntries?.length
@@ -58,6 +60,11 @@
5860
}
5961
6062
onMount(() => {
63+
// Reflect guardian preference for Ruixen insights
64+
guardianStore.subscribe((g) => {
65+
aiEnabled = !!(g && g.preferences && g.preferences.aiInsights);
66+
});
67+
6168
// Subscribe first so incoming loads propagate into state
6269
petStore.subscribe((list) => {
6370
pets = list || [];
@@ -118,31 +125,33 @@
118125
// Add entry to pet
119126
petHelpers.addJournalEntry(selectedPet.id, entry);
120127
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+
}
142151
}
152+
} catch (error) {
153+
console.error('Ruixen analysis failed:', error);
143154
}
144-
} catch (error) {
145-
console.error('Ruixen analysis failed:', error);
146155
}
147156
148157
// Reset form
@@ -170,16 +179,22 @@
170179
<Skeleton height="h-6" />
171180
<Skeleton height="h-4" />
172181
</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>
183198
{:else}
184199
<div class="pet-viewport h-full flex flex-col">
185200
<!-- Header with pet info and navigation -->
@@ -373,21 +388,6 @@
373388
{:else if currentView === 'dataManager'}
374389
<!-- Data Manager full-width in right panel -->
375390
<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>
391391
<div
392392
class="p-2 rounded"
393393
style="background: var(--petalytics-surface); border: 1px solid var(--petalytics-border);"
@@ -502,15 +502,19 @@
502502
style="color: var(--petalytics-text);"
503503
>
504504
<Brain size={16} class="mr-2" style="color: var(--petalytics-accent);" />
505-
AI Insights (Ruixen)
505+
Ruixen Insights
506506
</h3>
507507
{#if selectedPet.journalEntries.length === 0}
508508
<p class="text-sm" style="color: var(--petalytics-subtle);">
509509
Add journal entries to get AI-powered insights about {selectedPet.name}'s
510510
well-being.
511511
</p>
512+
{:else if aiEnabled}
513+
<AIInsightsCard petId={selectedPet.id} entryId={lastEntry?.id} compact={true} />
512514
{: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>
514518
{/if}
515519
</div>
516520
</div>

src/lib/components/ui/AIInsightsCard.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<div class="flex items-center space-x-2 mb-3">
2121
<Brain size={16} style="color: var(--petalytics-accent);" />
2222
<span class="font-medium text-sm" style="color: var(--petalytics-text);"
23-
>AI Analysis • Ruixen</span
23+
>Ruixen Insights</span
2424
>
2525
</div>
2626
{/if}

src/lib/components/ui/DataManager.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@
106106
<div class="cli-row px-2 py-2">
107107
<div class="flex items-center gap-2">
108108
<img
109-
src={pet.profileImageUrl || 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHJ4PSIxNiIgZmlsbD0iI0YzNEY0RiIvPjxzdmcgeD0iOCIgeT0iOCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJ3aGl0ZSI+PHBhdGggZD0iTTUuMjUgNEM0LjU1OTY0IDQgNCA0LjU1OTY0IDQgNS4yNVYxMC43NUM0IDExLjQ0MDQgNC41NTk2NCAxMiA1LjI1IDEySDEwLjc1QzExLjQ0MDQgMTIgMTIgMTEuNDQwNCAxMiAxMC43NVY1LjI1QzEyIDQuNTU5NjQgMTEuNDQwNCA0IDEwLjc1IDRINS4yNVoiLz48L3N2Zz48L3N2Zz4='}
109+
src={pet.profileImageUrl ||
110+
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHJ4PSIxNiIgZmlsbD0iI0YzNEY0RiIvPjxzdmcgeD0iOCIgeT0iOCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJ3aGl0ZSI+PHBhdGggZD0iTTUuMjUgNEM0LjU1OTY0IDQgNCA0LjU1OTY0IDQgNS4yNVYxMC43NUM0IDExLjQ0MDQgNC41NTk2NCAxMiA1LjI1IDEySDEwLjc1QzExLjQ0MDQgMTIgMTIgMTEuNDQwNCAxMiAxMC43NVY1LjI1QzEyIDQuNTU5NjQgMTEuNDQwNCA0IDEwLjc1IDRINS4yNVoiLz48L3N2Zz48L3N2Zz4='}
110111
alt={pet.name}
111112
class="w-8 h-8 rounded-full object-cover"
112113
onerror={(e) => {

src/lib/components/ui/EmptyState.svelte

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import { Heart, FileText, Camera, Calendar } from 'lucide-svelte';
2+
import { Heart, FileText, Camera, Calendar, PawPrint } from 'lucide-svelte';
33
44
interface Props {
55
// accept multiple icon key aliases for resilience
@@ -8,9 +8,19 @@
88
description: string;
99
actionText?: string;
1010
onAction?: () => void;
11+
secondaryActionText?: string;
12+
onSecondaryAction?: () => void;
1113
}
1214
13-
let { icon = 'heart', title, description, actionText, onAction }: Props = $props();
15+
let {
16+
icon = 'heart',
17+
title,
18+
description,
19+
actionText,
20+
onAction,
21+
secondaryActionText,
22+
onSecondaryAction,
23+
}: Props = $props();
1424
1525
const icons = {
1626
heart: Heart,
@@ -42,10 +52,20 @@
4252
{description}
4353
</p>
4454

45-
{#if actionText && onAction}
46-
<button onclick={onAction} class="button flex items-center space-x-2 mx-auto">
47-
<IconComponent size={16} />
48-
<span>{actionText}</span>
49-
</button>
55+
{#if (actionText && onAction) || (secondaryActionText && onSecondaryAction)}
56+
<div class="flex items-center justify-center gap-2">
57+
{#if actionText && onAction}
58+
<button onclick={onAction} class="button flex items-center space-x-2">
59+
<IconComponent size={16} />
60+
<span>{actionText}</span>
61+
</button>
62+
{/if}
63+
{#if secondaryActionText && onSecondaryAction}
64+
<button onclick={onSecondaryAction} class="button flex items-center space-x-2">
65+
<PawPrint size={16} />
66+
<span>{secondaryActionText}</span>
67+
</button>
68+
{/if}
69+
</div>
5070
{/if}
5171
</div>

src/lib/components/ui/RuixenInsights.svelte

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@
55
66
export let pet: PetPanelData;
77
8-
const ws = ruixenHelpers.weeklySummary(pet);
9-
const tips = ruixenHelpers.speciesInsights(pet);
10-
const reminders = ruixenHelpers.scheduleReminders(pet);
8+
let ws = ruixenHelpers.weeklySummary(pet);
9+
let tips = ruixenHelpers.speciesInsights(pet);
10+
let reminders = ruixenHelpers.scheduleReminders(pet);
11+
12+
$: if (pet) {
13+
ws = ruixenHelpers.weeklySummary(pet);
14+
tips = ruixenHelpers.speciesInsights(pet);
15+
reminders = ruixenHelpers.scheduleReminders(pet);
16+
}
1117
</script>
1218

1319
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
@@ -17,17 +23,20 @@
1723
>
1824
<div class="flex items-center mb-2">
1925
<Activity size={16} class="mr-2" style="color: var(--petalytics-accent);" />
20-
<span class="font-semibold" style="color: var(--petalytics-text);"
21-
>ai_insights: weekly_summary</span
26+
<span
27+
class="text-[10px] uppercase tracking-wide px-1.5 py-0.5 rounded border"
28+
style="border-color: var(--petalytics-accent); color: var(--petalytics-accent);"
2229
>
30+
weekly
31+
</span>
32+
<span class="ml-2 font-semibold" style="color: var(--petalytics-text);">{ws.title}</span>
2333
</div>
2434
<div class="text-sm space-y-1" style="color: var(--petalytics-text);">
25-
<div class="font-medium">{ws.title}</div>
26-
<ul class="list-disc pl-4 space-y-1">
27-
<li>Energy levels: {ws.energy} trend</li>
28-
<li>Appetite: {ws.appetite}</li>
29-
<li>Social behavior: {ws.social}</li>
30-
<li>Recommendation: {ws.recommendation}</li>
35+
<ul class="pl-4 space-y-1" style="list-style: disc;">
36+
<li><span class="opacity-70">Energy:</span> {ws.energy} trend</li>
37+
<li><span class="opacity-70">Appetite:</span> {ws.appetite}</li>
38+
<li><span class="opacity-70">Social:</span> {ws.social}</li>
39+
<li><span class="opacity-70">Recommendation:</span> {ws.recommendation}</li>
3140
</ul>
3241
</div>
3342
</div>
@@ -38,33 +47,51 @@
3847
>
3948
<div class="flex items-center mb-2">
4049
<Calendar size={16} class="mr-2" style="color: var(--petalytics-accent);" />
41-
<span class="font-semibold" style="color: var(--petalytics-text);"
42-
>ai_breed_insights & ai_schedule</span
50+
<span
51+
class="text-[10px] uppercase tracking-wide px-1.5 py-0.5 rounded border"
52+
style="border-color: var(--petalytics-accent); color: var(--petalytics-accent);"
53+
>
54+
breed
55+
</span>
56+
<span class="mx-2 text-[10px] opacity-60">•</span>
57+
<span
58+
class="text-[10px] uppercase tracking-wide px-1.5 py-0.5 rounded border"
59+
style="border-color: var(--petalytics-accent); color: var(--petalytics-accent);"
4360
>
61+
schedule
62+
</span>
4463
</div>
4564
<div class="text-sm space-y-3" style="color: var(--petalytics-text);">
4665
<div>
4766
<div class="text-xs mb-1" style="color: var(--petalytics-subtle);">
48-
ai_breed_insights: {pet.breed || pet.species || 'pet'}
67+
{pet.breed || pet.species || 'pet'} insights
4968
</div>
50-
<ul class="list-disc pl-4 space-y-1">
51-
{#each tips as tip}<li>{tip}</li>{/each}
69+
<ul class="pl-4 space-y-1" style="list-style: disc;">
70+
{#each tips as tip}
71+
<li>{tip}</li>
72+
{/each}
5273
</ul>
5374
</div>
5475
<div>
5576
<div class="text-xs mb-1" style="color: var(--petalytics-subtle);">
56-
ai_schedule: {pet.name} ({pet.breed || pet.species || 'pet'}, {pet.age}{pet.ageUnit ===
57-
'months'
77+
{pet.name} ({pet.breed || pet.species || 'pet'}, {pet.age}{pet.ageUnit === 'months'
5878
? 'm'
5979
: pet.ageUnit === 'weeks'
6080
? 'w'
61-
: 'y'})
81+
: 'y'}) schedule
6282
</div>
63-
<ul class="list-disc pl-4 space-y-1">
83+
<ul class="pl-4 space-y-1" style="list-style: disc;">
6484
{#each reminders as r}
6585
<li>
66-
{r.title}: {r.note}{#if r.due}
67-
⚠️{/if}
86+
<span class="font-medium">{r.title}</span>: {r.note}
87+
{#if r.due}
88+
<span
89+
class="ml-2 align-middle text-[10px] uppercase tracking-wide px-1 py-0.5 rounded border"
90+
style="border-color: var(--petalytics-accent); color: var(--petalytics-accent);"
91+
>
92+
due
93+
</span>
94+
{/if}
6895
</li>
6996
{/each}
7097
</ul>

0 commit comments

Comments
 (0)