Something went wrong. Try again.
small gleam coding and persistent agent daemon with a detachable cli and a canvas ui forked from okami.mom/albedo
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392<script lang="ts"> import { onMount } from 'svelte'; import { models, profiles, type Model, type Profile } from './api'; import { shortCount } from './context'; import { nextModelEffort } from './modelEffort'; import { compareRank, matchFields, searchKey, searchWords, splitSegments, type MatchRank } from './format'; import EffortBar from './EffortBar.svelte'; import SearchCard from './SearchCard.svelte';
// Unified provider + model selector with TUI-style fuzzy search across // provider names and model ids, character match highlights, ranking within // and across profile groups, and an effort segmented bar in the footer. let { profile = $bindable(), model = $bindable(), effort = $bindable(), currentProfile = null, currentModel = null, autofocus = false, onpick, oneffort, }: { profile: string | null; model: string | null; effort: string | null; /** The session's current provider, sorted first and tagged `current`. */ currentProfile?: string | null; /** The session's current model id, tagged `current` on `currentProfile`. */ currentModel?: string | null; autofocus?: boolean; /** Called when a model row is picked (clicked or Enter on the first/highlighted match). */ onpick?: (profile: string, model: string, effort: string | null) => void; /** Called when an effort pill in the footer is clicked. */ oneffort?: (effort: string) => void; } = $props();
let all = $state<Profile[]>([]); let byProfile = $state<Record<string, Model[]>>({}); let loading = $state(true); let problem = $state(''); let query = $state(''); let highlighted = $state(0); /** An arrow moved the highlight, so it is drawn with no filter too: Enter picks it. */ let keyed = $state(false); // svelte-ignore state_referenced_locally const pinFirst = currentProfile ?? profile;
onMount(() => { profiles() .then(async (found) => { all = found.profiles; if (!profile) { const def = found.profiles.find((p) => p.id === found.fallback) ?? found.profiles[0]; if (def) { profile = def.id; model ??= def.model; effort ??= def.effort; } } await Promise.all( found.profiles.map((p) => models(p.id) .then((items) => { byProfile = { ...byProfile, [p.id]: items }; if (profile === p.id && !model && items[0]) model = items[0].id; }) .catch(() => { byProfile = { ...byProfile, [p.id]: [] }; }), ), ); }) .catch((error) => (problem = String((error as Error).message ?? error))) .finally(() => (loading = false)); });
/** Seed a profile's model list with its configured default + current model until /models finishes. */ function rowsOf(p: Profile): Model[] { const loaded = byProfile[p.id] ?? []; const seen = new Set(loaded.map((m) => m.id)); const seeds: Model[] = []; for (const id of [p.id === currentProfile ? currentModel : null, p.model]) { if (id && !seen.has(id)) { seen.add(id); seeds.push({ id, label: id, efforts: [], default_context_tokens: null, effective_context_tokens: null, max_context_tokens: null, max_output_tokens: null, input_modalities: [], image_edge: null, raised: false, cap_key: `${p.id}/${id}`, cache_policy: { ttl_seconds: null, source: null }, source: 'seed', observed_at: null, }); } } return [...seeds, ...loaded]; }
const orderedProfiles = $derived( [...all].sort((a, b) => { if (a.id === pinFirst && b.id !== pinFirst) return -1; if (b.id === pinFirst && a.id !== pinFirst) return 1; return a.id.localeCompare(b.id); }), );
function matchModel(words: string[], pId: string, mId: string): { rank: MatchRank; hits: number[] } | null { const res = matchFields(words, mId, pId); if (!res) return null; const whole = words.join(''); if (whole && whole === searchKey(pId + mId)) { res.rank[0] = 1; } return res; }
const groups = $derived.by(() => { const words = searchWords(query);
const rawGroups: { profile: Profile; best: MatchRank; scored: { profile: string; model: Model; hits: number[]; rank: MatchRank }[]; }[] = [];
for (const p of orderedProfiles) { const scored: { profile: string; model: Model; hits: number[]; rank: MatchRank }[] = []; for (const m of rowsOf(p)) { const matched = matchModel(words, p.id, m.id); if (matched) scored.push({ profile: p.id, model: m, hits: matched.hits, rank: matched.rank }); } if (!scored.length) continue; if (words.length) scored.sort((a, b) => compareRank(a.rank, b.rank)); rawGroups.push({ profile: p, best: scored[0].rank, scored }); }
if (words.length) { rawGroups.sort((a, b) => compareRank(a.best, b.best)); }
let idx = 0; return rawGroups.map((g) => ({ profile: g.profile, items: g.scored.map((s) => ({ profile: s.profile, model: s.model, hits: s.hits, flatIndex: idx++, })), })); });
const flat = $derived(groups.flatMap((g) => g.items));
$effect(() => { query; highlighted = 0; });
const activeModelRecord = $derived( (profile ? byProfile[profile]?.find((m) => m.id === model) : undefined) ?? null, ); const efforts = $derived(activeModelRecord?.efforts ?? []);
function nextEffortFor(pId: string, m: Model): string | null { // Synthetic default/current rows have no metadata, unlike catalog rows // whose empty effort list explicitly means no supported effort. const known = byProfile[pId]?.find((item) => item.id === m.id); return nextModelEffort( known ? known.efforts.map((e) => e.id) : null, effort, all.find((p) => p.id === pId)?.effort ?? null, pId === profile, ); }
function selectRow(pId: string, m: Model) { const nextEff = nextEffortFor(pId, m); profile = pId; model = m.id; effort = nextEff; onpick?.(pId, m.id, nextEff); }
function selectEffort(nextEff: string) { effort = nextEff; oneffort?.(nextEff); }
function keydown(event: KeyboardEvent) { if (event.key === 'ArrowDown') { if (!flat.length) return; event.preventDefault(); keyed = true; highlighted = (highlighted + 1) % flat.length; return; } if (event.key === 'ArrowUp') { if (!flat.length) return; event.preventDefault(); keyed = true; highlighted = (highlighted + flat.length - 1) % flat.length; return; } if (event.key === 'Enter' && !event.isComposing) { const target = flat[highlighted] ?? flat[0]; if (!target) return; event.preventDefault(); event.stopPropagation(); selectRow(target.profile, target.model); } }</script>
<SearchCard bind:query placeholder="filter by model or provider…" ariaLabel="models" {autofocus} onkeydown={keydown} foot={efforts.length ? effortFoot : undefined}> {#each groups as group (group.profile.id)} <div class="group-head"> <span>{group.profile.id}</span> <span class="count">{group.items.length}</span> </div> {#each group.items as item (`${item.profile}:${item.model.id}`)} {@const isCurrent = currentProfile !== null ? item.profile === currentProfile && item.model.id === currentModel : item.profile === profile && item.model.id === model} {@const isSelected = item.profile === profile && item.model.id === model} {@const ctx = item.model.effective_context_tokens ?? item.model.default_context_tokens} <button type="button" role="option" class="row" class:selected={isSelected} class:highlighted={item.flatIndex === highlighted && (keyed || query.trim().length > 0)} aria-selected={isSelected} onpointerenter={() => (highlighted = item.flatIndex)} onclick={() => selectRow(item.profile, item.model)} > <span class="name"> <span class="id"> {#each splitSegments(item.model.id, item.hits) as seg} {#if seg.hit}<mark>{seg.text}</mark>{:else}{seg.text}{/if} {/each} </span> {#if isCurrent} <span class="tag current">current</span> {/if} </span> {#if ctx} <span class="ctx">{shortCount(ctx)}</span> {/if} </button> {/each} {:else} <p class="empty">{loading ? 'loading models…' : 'no models match'}</p> {/each} {#if problem}<p class="problem">{problem}</p>{/if}</SearchCard>
{#snippet effortFoot()} <EffortBar efforts={efforts.map((o) => o.id)} value={effort} onpick={selectEffort} />{/snippet}
<style>
.group-head { display: flex; align-items: baseline; gap: 6px; padding: 7px 10px 3px; color: color-mix(in oklab, var(--text) 65%, var(--muted)); font-size: calc(11px * var(--ui-scale)); font-weight: 600; line-height: 1.2; letter-spacing: -0.01em; } .group-head .count { color: var(--muted); font-family: var(--mono); font-size: calc(10.5px * var(--ui-scale)); font-weight: 500; font-variant-numeric: tabular-nums; } .group-head::after { content: ''; flex: 1; align-self: center; height: 1px; background: color-mix(in oklab, var(--text) 12%, transparent); } .row { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 10px; width: 100%; padding: 6px 10px; color: var(--text); font: inherit; text-align: left; background: none; border: 0; border-radius: 8px; cursor: pointer; } .row:hover, .row.highlighted { background: var(--hover); } .row.selected { background: color-mix(in oklab, var(--accent) 15%, transparent); } .row.selected::before { content: ''; position: absolute; top: 6px; bottom: 6px; left: 2px; width: 2.5px; background: var(--accent); border-radius: 2px; } .name { display: flex; align-items: center; gap: 7px; min-width: 0; font-family: var(--mono); font-size: calc(12px * var(--ui-scale)); } .row.selected .id { font-weight: 600; } .id { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .id mark { padding: 0; color: var(--accent); font-weight: 650; background: none; } .tag { flex: none; padding: 1px 5px; font-family: var(--sans); font-size: calc(10px * var(--ui-scale)); font-weight: 600; border-radius: 4px; } .tag.current { color: var(--added); background: color-mix(in oklab, var(--added) 16%, transparent); } .ctx { flex: none; color: var(--muted); font-family: var(--mono); font-size: calc(11px * var(--ui-scale)); font-variant-numeric: tabular-nums; } .empty { margin: 0; padding: 14px 10px; color: var(--faint); font-size: calc(12px * var(--ui-scale)); text-align: center; }
.problem { margin: 0; padding: 6px 11px; color: var(--danger); font-size: calc(12px * var(--ui-scale)); }</style>