From c957e89e3ec46f1db64dcb1416f5ade7fb6e617e Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Thu, 21 May 2026 14:31:40 +0900 Subject: feat: usage cache with spinner refresh — shows cached data immediately, refreshes in background MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/lib/components/KeyUsage.svelte | 56 ++++++++++++---------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/packages/frontend/src/lib/components/KeyUsage.svelte b/packages/frontend/src/lib/components/KeyUsage.svelte index 4b06309..5e86f36 100644 --- a/packages/frontend/src/lib/components/KeyUsage.svelte +++ b/packages/frontend/src/lib/components/KeyUsage.svelte @@ -17,6 +17,9 @@ loading: boolean; } + // Module-level cache survives remounts during the same page session + const usageCache = new Map(); + let entries = $state([]); async function fetchOne(key: KeyInfo) { @@ -27,20 +30,20 @@ if (!res.ok) { const data = await res.json().catch(() => ({})); updateEntry(key.id, { - data: null, error: data.error ?? `HTTP ${res.status}`, loading: false, }); } else { + const fresh = await res.json() as KeyUsageData; + usageCache.set(key.id, fresh); updateEntry(key.id, { - data: await res.json(), + data: fresh, error: null, loading: false, }); } } catch (e) { updateEntry(key.id, { - data: null, error: e instanceof Error ? e.message : "Failed to fetch", loading: false, }); @@ -57,14 +60,17 @@ } $effect(() => { - // Initialize all entries as loading - entries = keys.map((k) => ({ - keyId: k.id, - provider: k.provider, - data: null, - error: null, - loading: true, - })); + // Show cached data immediately if available, then refresh in background + entries = keys.map((k) => { + const cached = usageCache.get(k.id); + return { + keyId: k.id, + provider: k.provider, + data: cached ?? null, + error: null, + loading: true, + }; + }); // Fire all fetches in parallel for (const key of keys) { fetchOne(key); @@ -165,23 +171,21 @@ {:else}
- {#if claudeLoading} -
-
- Claude - anthropic -
-
- - Loading... -
-
- {:else if claudeAccounts.length > 0} + {#if claudeAccounts.length > 0 || claudeLoading}
Claude anthropic + {#if claudeLoading} + + {/if}
+ + {#if claudeAccounts.length === 0 && claudeLoading} +
+ Loading... +
+ {:else} {#each claudeAccounts as acct, idx (acct.source)} {#if idx > 0}
@@ -228,8 +232,9 @@ {/if}
{/each} -
{/if} + + {/if} {#each nonClaudeEntries as entry (entry.keyId)} @@ -237,6 +242,9 @@
{entry.keyId} {entry.provider} + {#if entry.loading} + + {/if}
{#if entry.loading} -- cgit v1.2.3