|
1 | 1 | <script lang="ts">
|
2 |
| - import { onMount } from 'svelte'; |
| 2 | + import { onMount, tick } from 'svelte'; |
3 | 3 | import { ChevronLeft, ChevronRight, User, Key, Settings, CheckCircle, AlertCircle, Terminal } from 'lucide-svelte';
|
4 | 4 | import { guardianHelpers } from '$lib/stores/guardian.js';
|
5 | 5 | import { aiAnalysisHelpers } from '$lib/stores/ai-analysis.js';
|
|
8 | 8 | import { fetchOpenRouterModels } from '$lib/utils/ai-analysis.js';
|
9 | 9 |
|
10 | 10 | let apiKeyInput = '';
|
| 11 | + let prevApiKey = ''; |
| 12 | + let apiKeyEl: HTMLInputElement | null = null; |
11 | 13 | let guardianName = '';
|
12 | 14 | // Preferences trimmed to real features; remove unused reminders/notifications.
|
13 | 15 | let preferences: { aiInsights: boolean } = {
|
|
123 | 125 | }
|
124 | 126 |
|
125 | 127 | 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 | + } |
127 | 140 | }
|
128 | 141 |
|
129 | 142 | function stopEdit() {
|
130 | 143 | const field = editingField;
|
131 | 144 | 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 | + } |
132 | 149 | saveGuardianInfo();
|
133 | 150 | if (field === 'apiKey') {
|
134 | 151 | validateApiKey();
|
|
194 | 211 | action();
|
195 | 212 | }
|
196 | 213 | }
|
| 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 | + } |
197 | 222 | </script>
|
198 | 223 |
|
199 | 224 | <div class="guardian-panel h-full" style="background: var(--petalytics-bg);">
|
|
232 | 257 | <span class="value" style="color: var(--petalytics-text);">
|
233 | 258 | {#if editingField === 'apiKey'}
|
234 | 259 | <input
|
235 |
| - type="password" |
| 260 | + bind:this={apiKeyEl} |
| 261 | + type="text" |
236 | 262 | bind:value={apiKeyInput}
|
237 | 263 | onblur={stopEdit}
|
238 | 264 | onkeydown={(e) => handleKeydown(e, 'apiKey')}
|
239 | 265 | class="bg-transparent border-none outline-none w-full text-right input-inline"
|
240 | 266 | style="color: var(--petalytics-text);"
|
241 | 267 | placeholder="sk-or-..."
|
| 268 | + inputmode="text" |
| 269 | + autocapitalize="off" |
| 270 | + autocorrect="off" |
| 271 | + spellcheck={false} |
242 | 272 | />
|
243 | 273 | {:else}
|
244 |
| - {apiKeyInput ? `${apiKeyInput.slice(0, 8)}****` : 'Not set'} |
| 274 | + {abbreviateKey(apiKeyInput)} |
245 | 275 | {/if}
|
246 | 276 | </span>
|
247 | 277 | <span class="ml-2">
|
|
322 | 352 | border-color: var(--petalytics-accent);
|
323 | 353 | box-shadow: 0 0 0 2px color-mix(in oklab, var(--petalytics-accent) 40%, transparent);
|
324 | 354 | }
|
325 |
| -.cli-row[aria-pressed="true"], |
326 |
| -.cli-row[aria-expanded="true"] { |
| 355 | +.cli-row[aria-pressed="true"] { |
327 | 356 | background: var(--petalytics-highlight-high);
|
328 | 357 | border-color: var(--petalytics-accent);
|
329 | 358 | }
|
|
0 commit comments