summaryrefslogtreecommitdiffhomepage
path: root/packages/frontend/src/lib/components
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-05-23 19:07:21 +0900
committerAdam Malczewski <[email protected]>2026-05-23 19:07:21 +0900
commit997b00034435440d412f955e05e53f09bae83f9e (patch)
tree22d1f530e9e1a97bd19286456d2f06793bd030f7 /packages/frontend/src/lib/components
parentff00dec6ae2971bee38c74cb00fe034de9a839ee (diff)
downloaddispatch-997b00034435440d412f955e05e53f09bae83f9e.tar.gz
dispatch-997b00034435440d412f955e05e53f09bae83f9e.zip
feat: google gemini provider, adaptive thinking for opus 4.7, model search filter
- Added Google (Gemini) as a provider: add-key UI, env var resolution via resolveApiKey, usage tracking via native models endpoint + gemini.google.com cookie scraping - @ai-sdk/anthropic upgraded to v3 (adaptive thinking support) with LanguageModelV1 cast for ai v4 compat - Claude Opus 4.7 uses adaptive thinking (type: adaptive); all other models keep explicit budget tokens - Model selector modal: search filter with space matching dash/underscore - Copy button: all tool results truncated at 300 chars - Sidebar layout fix: Claude Reset panel removed from flex-1 fill to prevent overlap
Diffstat (limited to 'packages/frontend/src/lib/components')
-rw-r--r--packages/frontend/src/lib/components/KeyUsage.svelte53
-rw-r--r--packages/frontend/src/lib/components/ModelSelector.svelte46
2 files changed, 88 insertions, 11 deletions
diff --git a/packages/frontend/src/lib/components/KeyUsage.svelte b/packages/frontend/src/lib/components/KeyUsage.svelte
index 49c3cf6..96a4b08 100644
--- a/packages/frontend/src/lib/components/KeyUsage.svelte
+++ b/packages/frontend/src/lib/components/KeyUsage.svelte
@@ -404,6 +404,59 @@ function hasBucketData(bucket: UsageBucket | undefined): boolean {
<span class="text-xs text-base-content/40">Resets: {formatDate(entry.data.resetAt)}</span>
{/if}
</div>
+ {:else if entry.data.provider === "google"}
+ <div class="flex flex-col gap-0.5 pl-1">
+ <!-- Cookie-scraped usage from gemini.google.com -->
+ {#if entry.data.currentUsage}
+ {@const u = entry.data.currentUsage}
+ <div class="flex flex-col gap-0.5">
+ <div class="flex items-center justify-between">
+ <span class="text-xs text-base-content/50">Current</span>
+ <span class="text-xs font-mono">{u.percentUsed}%</span>
+ </div>
+ <progress class="progress w-full h-2 {progressClass(u.percentUsed / 100)}" value={u.percentUsed} max="100"></progress>
+ {#if u.resetsAt}
+ <span class="text-xs text-base-content/40">Resets: {u.resetsAt}</span>
+ {/if}
+ </div>
+ {/if}
+ {#if entry.data.weeklyUsage}
+ {@const w = entry.data.weeklyUsage}
+ <div class="flex flex-col gap-0.5">
+ <div class="flex items-center justify-between">
+ <span class="text-xs text-base-content/50">Weekly</span>
+ <span class="text-xs font-mono">{w.percentUsed}%</span>
+ </div>
+ <progress class="progress w-full h-2 {progressClass(w.percentUsed / 100)}" value={w.percentUsed} max="100"></progress>
+ {#if w.resetsAt}
+ <span class="text-xs text-base-content/40">Resets: {w.resetsAt}</span>
+ {/if}
+ </div>
+ {/if}
+ <!-- API key rate limits -->
+ {#if !entry.data.currentUsage && entry.data.models && entry.data.models.length > 0}
+ {@const m = entry.data.models[0]}
+ <div class="flex items-center justify-between">
+ <span class="text-xs text-base-content/50">Models</span>
+ <span class="text-xs font-mono">{entry.data.models.length} available</span>
+ </div>
+ {#if m.rpm > 0}
+ <div class="flex items-center justify-between">
+ <span class="text-xs text-base-content/50">RPM</span>
+ <span class="text-xs font-mono">{m.rpm}</span>
+ </div>
+ {/if}
+ {#if m.requestsPerDay > 0}
+ <div class="flex items-center justify-between">
+ <span class="text-xs text-base-content/50">RPD</span>
+ <span class="text-xs font-mono">{m.requestsPerDay.toLocaleString()}</span>
+ </div>
+ {/if}
+ {#if !entry.data.currentUsage}
+ <p class="text-xs text-base-content/40 mt-0.5">Set GEMINI_COOKIE (__Secure-1PSID) for usage %</p>
+ {/if}
+ {/if}
+ </div>
{/if}
{/if}
</div>
diff --git a/packages/frontend/src/lib/components/ModelSelector.svelte b/packages/frontend/src/lib/components/ModelSelector.svelte
index bbbf036..235cf3b 100644
--- a/packages/frontend/src/lib/components/ModelSelector.svelte
+++ b/packages/frontend/src/lib/components/ModelSelector.svelte
@@ -62,6 +62,7 @@ const modelCache = new Map<string, string[]>();
let loadingModels = $state(false);
let modelError = $state<string | null>(null);
let sliderDragging = $state<number | null>(null);
+ let modelSearch = $state("");
let cwdExists = $state<boolean | null>(null);
let cwdCheckTimer: ReturnType<typeof setTimeout> | null = null;
@@ -126,6 +127,7 @@ const modelCache = new Map<string, string[]>();
if (!keyId) return;
showModelModal = true;
modelError = null;
+ modelSearch = "";
// Check session cache
if (modelCache.has(keyId)) {
@@ -393,17 +395,39 @@ const modelCache = new Map<string, string[]>();
<span>{modelError}</span>
</div>
{:else}
- <div class="mt-4 flex flex-col gap-1 max-h-96 overflow-y-auto">
- {#each availableModels as model}
- <button
- class="btn {model === activeModelId
- ? 'btn-primary'
- : 'btn-ghost'} justify-start font-mono text-base"
- onclick={() => selectModel(model)}
- >
- {model}
- </button>
- {/each}
+ {@const search = modelSearch.toLowerCase().trim()}
+ {@const searchRegex = search
+ ? new RegExp(
+ search.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/ /g, "[ _-]"),
+ )
+ : null}
+ {@const filteredModels = searchRegex
+ ? availableModels.filter((m) => searchRegex.test(m.toLowerCase()))
+ : availableModels}
+ <div class="mt-4 flex flex-col gap-1">
+ <input
+ type="text"
+ class="input input-bordered input-sm w-full"
+ placeholder="Filter models..."
+ bind:value={modelSearch}
+ />
+ <div class="mt-2 max-h-96 overflow-y-auto flex flex-col gap-1">
+ {#each filteredModels as model}
+ <button
+ class="btn {model === activeModelId
+ ? 'btn-primary'
+ : 'btn-ghost'} justify-start font-mono text-base"
+ onclick={() => selectModel(model)}
+ >
+ {model}
+ </button>
+ {/each}
+ {#if filteredModels.length === 0}
+ <p class="text-xs text-base-content/50 py-2 text-center">
+ {search ? 'No models match your search.' : 'No models available.'}
+ </p>
+ {/if}
+ </div>
</div>
{/if}
<div class="modal-action">