Skip to content

Commit 4d677b5

Browse files
committed
feat: update GuardianPanel for improved API key handling and add gender/size normalization in PetPanel
1 parent ea04009 commit 4d677b5

File tree

2 files changed

+59
-15
lines changed

2 files changed

+59
-15
lines changed

src/lib/components/panels/GuardianPanel.svelte

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import { onMount } from 'svelte';
2+
import { onMount, tick } from 'svelte';
33
import { ChevronLeft, ChevronRight, User, Key, Settings, CheckCircle, AlertCircle, Terminal } from 'lucide-svelte';
44
import { guardianHelpers } from '$lib/stores/guardian.js';
55
import { aiAnalysisHelpers } from '$lib/stores/ai-analysis.js';
@@ -8,6 +8,8 @@
88
import { fetchOpenRouterModels } from '$lib/utils/ai-analysis.js';
99
1010
let apiKeyInput = '';
11+
let prevApiKey = '';
12+
let apiKeyEl: HTMLInputElement | null = null;
1113
let guardianName = '';
1214
// Preferences trimmed to real features; remove unused reminders/notifications.
1315
let preferences: { aiInsights: boolean } = {
@@ -123,12 +125,27 @@
123125
}
124126
125127
function startEdit(field: string) {
126-
editingField = field;
128+
if (field === 'apiKey') {
129+
// Prepare for paste-first UX: clear input and focus the field
130+
prevApiKey = apiKeyInput;
131+
editingField = field;
132+
apiKeyInput = '';
133+
// Focus after DOM updates
134+
tick().then(() => {
135+
apiKeyEl?.focus();
136+
});
137+
} else {
138+
editingField = field;
139+
}
127140
}
128141
129142
function stopEdit() {
130143
const field = editingField;
131144
editingField = null;
145+
// For apiKey, don't overwrite with empty; restore previous if nothing pasted
146+
if (field === 'apiKey' && !apiKeyInput.trim()) {
147+
apiKeyInput = prevApiKey;
148+
}
132149
saveGuardianInfo();
133150
if (field === 'apiKey') {
134151
validateApiKey();
@@ -194,6 +211,14 @@
194211
action();
195212
}
196213
}
214+
function abbreviateKey(key: string): string {
215+
const trimmed = (key || '').trim();
216+
if (!trimmed) return 'Not set';
217+
if (trimmed.length <= 16) return trimmed;
218+
const prefix = trimmed.slice(0, 12);
219+
const suffix = trimmed.slice(-3);
220+
return `${prefix}...${suffix}`;
221+
}
197222
</script>
198223

199224
<div class="guardian-panel h-full" style="background: var(--petalytics-bg);">
@@ -232,16 +257,21 @@
232257
<span class="value" style="color: var(--petalytics-text);">
233258
{#if editingField === 'apiKey'}
234259
<input
235-
type="password"
260+
bind:this={apiKeyEl}
261+
type="text"
236262
bind:value={apiKeyInput}
237263
onblur={stopEdit}
238264
onkeydown={(e) => handleKeydown(e, 'apiKey')}
239265
class="bg-transparent border-none outline-none w-full text-right input-inline"
240266
style="color: var(--petalytics-text);"
241267
placeholder="sk-or-..."
268+
inputmode="text"
269+
autocapitalize="off"
270+
autocorrect="off"
271+
spellcheck={false}
242272
/>
243273
{:else}
244-
{apiKeyInput ? `${apiKeyInput.slice(0, 8)}****` : 'Not set'}
274+
{abbreviateKey(apiKeyInput)}
245275
{/if}
246276
</span>
247277
<span class="ml-2">
@@ -322,8 +352,7 @@
322352
border-color: var(--petalytics-accent);
323353
box-shadow: 0 0 0 2px color-mix(in oklab, var(--petalytics-accent) 40%, transparent);
324354
}
325-
.cli-row[aria-pressed="true"],
326-
.cli-row[aria-expanded="true"] {
355+
.cli-row[aria-pressed="true"] {
327356
background: var(--petalytics-highlight-high);
328357
border-color: var(--petalytics-accent);
329358
}

src/lib/components/panels/PetPanel.svelte

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@
8484
breed: '',
8585
age: '',
8686
ageUnit: 'years',
87-
gender: 'unknown',
88-
size: 'medium',
87+
gender: '',
88+
size: '',
8989
profileImageUrl: '',
9090
};
9191
@@ -133,8 +133,8 @@
133133
breed: '',
134134
age: '',
135135
ageUnit: 'years',
136-
gender: 'unknown',
137-
size: 'medium',
136+
gender: '',
137+
size: '',
138138
profileImageUrl: '',
139139
};
140140
formErrors = {};
@@ -191,13 +191,28 @@
191191
formErrors.age = 'Please enter a valid age';
192192
}
193193
194-
if (!newPet.gender) {
195-
formErrors.gender = 'Please select gender';
196-
}
194+
// gender optional; defaults to 'unknown' if left blank
197195
198196
return Object.keys(formErrors).length === 0;
199197
}
200198
199+
function normalizeGender(g: string): 'male' | 'female' | 'unknown' {
200+
const v = norm(g);
201+
if (v.startsWith('m')) return 'male';
202+
if (v.startsWith('f')) return 'female';
203+
return 'unknown';
204+
}
205+
206+
function normalizeSize(s: string): 'tiny' | 'small' | 'medium' | 'large' | 'extra_large' {
207+
const v = norm(s);
208+
if (v.startsWith('ti')) return 'tiny';
209+
if (v.startsWith('sm')) return 'small';
210+
if (v.startsWith('l') && !v.startsWith('la')) return 'large';
211+
if (v.startsWith('la')) return 'large';
212+
if (v.startsWith('ex') || v.startsWith('xl') || v.includes('extra')) return 'extra_large';
213+
return 'medium';
214+
}
215+
201216
async function createPet() {
202217
if (!validateForm()) return;
203218
@@ -208,8 +223,8 @@
208223
breed: newPet.breed.trim(),
209224
age: parseInt(newPet.age),
210225
ageUnit: normalizeAgeUnit(newPet.ageUnit),
211-
gender: (newPet.gender as 'male'|'female'|'unknown'),
212-
size: newPet.size as 'tiny'|'small'|'medium'|'large'|'extra_large',
226+
gender: normalizeGender(newPet.gender),
227+
size: normalizeSize(newPet.size),
213228
profileImageUrl: newPet.profileImageUrl || '/images/default-pet.png',
214229
createdAt: new Date().toISOString(),
215230
journalEntries: [],

0 commit comments

Comments
 (0)