Skip to content

Commit f2aa34b

Browse files
committed
Added favicons
1 parent 6e22773 commit f2aa34b

19 files changed

+503
-31
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Thank you for your interest in contributing to Petalytics! This guide will help
77
1. **Fork and clone the repository**
88

99
```bash
10-
git clone https://github.com/YOUR_USERNAME/petalytics.git
10+
git clone https://github.com/gitcoder89431/petalytics.git
1111
cd petalytics
1212
```
1313

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
## ✨ Features
99

1010
- **🤖 AI-Powered Insights** - Get personalized care recommendations using OpenRouter API
11+
- Ruixen agent orchestrator with built-in rate limiting and offline heuristics
1112
- **📝 Smart Journaling** - Record daily activities, mood, and behavior patterns
1213
- **🎨 Beautiful Themes** - 4 gorgeous themes (Everforest, Gruvbox Dark, Tokyo Night, Nord)
1314
- **📊 Health Tracking** - Monitor wellness trends over time

src/app.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@
33
<head>
44
<meta charset="utf-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<link rel="icon" type="image/svg+xml" href="/favicon.svg?v=1" />
7+
<link rel="icon" href="/favicon.ico?v=1" sizes="any" />
8+
<link rel="icon" type="image/png" href="/favicon-96x96.png?v=1" sizes="96x96" />
9+
<link rel="apple-touch-icon" href="/apple-touch-icon.png?v=1" />
10+
<link rel="manifest" href="/site.webmanifest?v=1" />
11+
<link rel="mask-icon" href="/favicon.svg?v=1" color="#2d0a79" />
12+
<meta name="theme-color" content="#2d0a79" />
13+
<meta name="application-name" content="Petalytics" />
14+
<meta name="apple-mobile-web-app-title" content="Petalytics" />
615
%sveltekit.head%
716
</head>
817
<body data-sveltekit-preload-data="hover">

src/lib/components/Layout.svelte

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
11
<script lang="ts">
22
import { onMount } from 'svelte';
3-
import { cssVariables } from '$lib/stores/theme.js';
3+
import { cssVariables } from '$lib/stores/theme';
44
import GuardianPanel from './panels/GuardianPanel.svelte';
55
import PetPanel from './panels/PetPanel.svelte';
66
import Viewport from './panels/Viewport.svelte';
7+
import { guardianStore } from '$lib/stores/guardian';
8+
import { ruixen } from '$lib/stores/ruixen';
79
810
let layoutRef: HTMLDivElement;
911
let currentTime = '';
1012
13+
// Ruixen status stores
14+
const ruixenQueue = ruixen.queueSize;
15+
const ruixenDaily = ruixen.dailyUsage;
16+
17+
// Compute Ruixen state label and color
18+
$: ruixenState = (() => {
19+
const hasKey = Boolean($guardianStore?.apiKey) && Boolean($guardianStore?.apiKeyValid);
20+
if (!hasKey) return { label: 'Needs API key', color: '--petalytics-love' };
21+
const model = ($guardianStore as any)?.model || 'openai/gpt-oss-20b:free';
22+
const isFree = /:free$/i.test(model);
23+
const daily = Number($ruixenDaily || 0);
24+
const q = Number($ruixenQueue || 0);
25+
if (isFree && daily >= 45) return { label: 'Daily cap reached', color: '--petalytics-gold' };
26+
if (q > 0) return { label: `Queued ${q}`, color: '--petalytics-gold' };
27+
return { label: 'Ready', color: '--petalytics-pine' };
28+
})();
29+
1130
function updateTime() {
1231
const now = new Date();
1332
const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
@@ -70,29 +89,20 @@
7089
>
7190
<!-- Left: Workspaces / Logo -->
7291
<div class="flex items-center space-x-4">
73-
<div
74-
class="px-2 h-5 flex items-center justify-center text-xs font-mono"
75-
style="background: var(--petalytics-pine); color: var(--petalytics-bg);"
76-
>
77-
🐾
78-
</div>
79-
<span class="font-mono" style="color: var(--petalytics-foam);">petalytics@desktop</span>
92+
<img src="/favicon.svg" alt="Petalytics" class="h-5 w-5" />
93+
<span class="font-mono" style="color: var(--petalytics-foam);">petalytics@ruixenOS</span>
8094
</div>
8195

8296
<!-- Center: Current Time -->
8397
<div class="font-mono font-medium" style="color: var(--petalytics-subtle);">
8498
{currentTime}
8599
</div>
86100

87-
<!-- Right: System Status -->
101+
<!-- Right: System Status (Ruixen) -->
88102
<div class="flex items-center space-x-3 text-xs font-mono">
89-
<div class="flex items-center space-x-1">
90-
<span style="color: var(--petalytics-foam);">●</span>
91-
<span style="color: var(--petalytics-subtle);">journal</span>
92-
</div>
93-
<div class="flex items-center space-x-1">
94-
<span style="color: var(--petalytics-gold);">●</span>
95-
<span style="color: var(--petalytics-subtle);">ai</span>
103+
<div class="flex items-center space-x-2">
104+
<span style={`color: var(${ruixenState.color});`}>●</span>
105+
<span style="color: var(--petalytics-subtle);">Ruixen: {ruixenState.label}</span>
96106
</div>
97107
</div>
98108
</div>

src/lib/components/panels/Viewport.svelte

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
<script lang="ts">
22
import { onMount } from 'svelte';
33
import { petStore, selectedPetStore, petHelpers, selectedPetHelpers } from '$lib/stores/pets';
4-
import { aiAnalysisHelpers, isAnalyzing } from '$lib/stores/ai-analysis';
4+
import { aiAnalysisHelpers, isAnalyzing, analysisStore } from '$lib/stores/ai-analysis';
5+
import { ruixenHelpers } from '$lib/stores/ruixen';
56
import { PenTool, Brain, Calendar, Activity } from 'lucide-svelte';
67
import AIInsightsCard from '../ui/AIInsightsCard.svelte';
8+
import RuixenInsights from '../ui/RuixenInsights.svelte';
79
import DataManager from '../ui/DataManager.svelte';
810
import EmptyState from '../ui/EmptyState.svelte';
911
import Skeleton from '../ui/Skeleton.svelte';
@@ -55,6 +57,10 @@
5557
// Subscribe first so incoming loads propagate into state
5658
petStore.subscribe((list) => {
5759
pets = list || [];
60+
// Hydrate AI cache from persisted entries on first load of list
61+
if (pets && pets.length) {
62+
aiAnalysisHelpers.hydrateFromPets(pets);
63+
}
5864
if (selectedPetId) {
5965
selectedPet = pets.find((p) => p.id === selectedPetId) || null;
6066
} else if (!selectedPetId && pets.length > 0) {
@@ -108,11 +114,22 @@
108114
// Add entry to pet
109115
petHelpers.addJournalEntry(selectedPet.id, entry);
110116
111-
// Analyze with AI (non-blocking)
117+
// Analyze with Ruixen orchestrator (rate-limited with offline fallback)
112118
try {
113-
await aiAnalysisHelpers.analyzeEntry(selectedPet, entry);
119+
const res = await ruixenHelpers.analyzeDaily(selectedPet, entry);
120+
if (res) {
121+
analysisStore.update((cache) => ({ ...cache, [entry.id]: res }));
122+
// Persist onto the entry so it survives reloads and exports
123+
const petNow = petHelpers.getPet(selectedPet.id);
124+
if (petNow) {
125+
const updatedEntries = (petNow.journalEntries || []).map((e) =>
126+
e.id === entry.id ? { ...e, aiAnalysis: { ...res, modelId: (selectedPet as any)?.model || undefined, analyzedAt: new Date().toISOString() } } : e
127+
);
128+
petHelpers.update(petNow.id, { journalEntries: updatedEntries });
129+
}
130+
}
114131
} catch (error) {
115-
console.error('AI analysis failed:', error);
132+
console.error('Ruixen analysis failed:', error);
116133
}
117134
118135
// Reset form
@@ -251,6 +268,7 @@
251268
{archivedPetsList().length} pets
252269
</div>
253270
</div>
271+
254272
</div>
255273

256274
{#if archivedPetsList().length === 0}
@@ -405,7 +423,7 @@
405423
style="color: var(--petalytics-text);"
406424
>
407425
<Brain size={16} class="mr-2" style="color: var(--petalytics-accent);" />
408-
AI Insights
426+
AI Insights (Ruixen)
409427
</h3>
410428
{#if selectedPet.journalEntries.length === 0}
411429
<p class="text-sm" style="color: var(--petalytics-subtle);">
@@ -417,6 +435,12 @@
417435
{/if}
418436
</div>
419437
</div>
438+
439+
{#if selectedPet}
440+
<div class="mt-4">
441+
<RuixenInsights pet={selectedPet} />
442+
</div>
443+
{/if}
420444
</div>
421445
{:else if currentView === 'journal' && selectedPet}
422446
<!-- Journal Entry Form -->

src/lib/components/ui/AIInsightsCard.svelte

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<script lang="ts">
2+
import { Activity, Calendar } from 'lucide-svelte';
3+
import type { PetPanelData } from '$lib/types/Pet';
4+
import { ruixenHelpers } from '$lib/stores/ruixen';
5+
6+
export let pet: PetPanelData;
7+
8+
const ws = ruixenHelpers.weeklySummary(pet);
9+
const tips = ruixenHelpers.speciesInsights(pet);
10+
const reminders = ruixenHelpers.scheduleReminders(pet);
11+
</script>
12+
13+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
14+
<div class="p-3 rounded border" style="background: var(--petalytics-surface); border-color: var(--petalytics-border);">
15+
<div class="flex items-center mb-2">
16+
<Activity size={16} class="mr-2" style="color: var(--petalytics-accent);" />
17+
<span class="font-semibold" style="color: var(--petalytics-text);">ai_insights: weekly_summary</span>
18+
</div>
19+
<div class="text-sm space-y-1" style="color: var(--petalytics-text);">
20+
<div class="font-medium">{ws.title}</div>
21+
<ul class="list-disc pl-4 space-y-1">
22+
<li>Energy levels: {ws.energy} trend</li>
23+
<li>Appetite: {ws.appetite}</li>
24+
<li>Social behavior: {ws.social}</li>
25+
<li>Recommendation: {ws.recommendation}</li>
26+
</ul>
27+
</div>
28+
</div>
29+
30+
<div class="p-3 rounded border" style="background: var(--petalytics-surface); border-color: var(--petalytics-border);">
31+
<div class="flex items-center mb-2">
32+
<Calendar size={16} class="mr-2" style="color: var(--petalytics-accent);" />
33+
<span class="font-semibold" style="color: var(--petalytics-text);">ai_breed_insights & ai_schedule</span>
34+
</div>
35+
<div class="text-sm space-y-3" style="color: var(--petalytics-text);">
36+
<div>
37+
<div class="text-xs mb-1" style="color: var(--petalytics-subtle);">ai_breed_insights: {pet.breed || pet.species || 'pet'}</div>
38+
<ul class="list-disc pl-4 space-y-1">
39+
{#each tips as tip}<li>{tip}</li>{/each}
40+
</ul>
41+
</div>
42+
<div>
43+
<div class="text-xs mb-1" style="color: var(--petalytics-subtle);">ai_schedule: {pet.name} ({pet.breed || pet.species || 'pet'}, {pet.age}{pet.ageUnit === 'months' ? 'm' : pet.ageUnit === 'weeks' ? 'w' : 'y'})</div>
44+
<ul class="list-disc pl-4 space-y-1">
45+
{#each reminders as r}
46+
<li>{r.title}: {r.note}{#if r.due} ⚠️{/if}</li>
47+
{/each}
48+
</ul>
49+
</div>
50+
</div>
51+
</div>
52+
</div>

src/lib/stores/ai-analysis.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,30 @@ guardianStore.subscribe((guardian) => {
1919
});
2020

2121
export const aiAnalysisHelpers = {
22+
hydrateFromPets(pets: PetPanelData[]) {
23+
try {
24+
const map: Record<string, AnalysisResult> = {};
25+
for (const pet of pets || []) {
26+
for (const entry of pet.journalEntries || []) {
27+
const raw: any = (entry as any).aiAnalysis;
28+
if (!raw) continue;
29+
if (typeof raw === 'string') {
30+
try {
31+
const parsed = JSON.parse(raw);
32+
if (parsed && parsed.summary) map[entry.id] = parsed as AnalysisResult;
33+
} catch {}
34+
} else if (typeof raw === 'object' && raw.summary) {
35+
map[entry.id] = raw as AnalysisResult;
36+
}
37+
}
38+
}
39+
if (Object.keys(map).length > 0) {
40+
analysisStore.update((cache) => ({ ...map, ...cache }));
41+
}
42+
} catch (e) {
43+
console.error('hydrateFromPets failed', e);
44+
}
45+
},
2246
async analyzeEntry(pet: PetPanelData, entry: JournalEntry): Promise<AnalysisResult | null> {
2347
if (!analyzer) {
2448
throw new Error('API key not configured');

0 commit comments

Comments
 (0)