The LLM runtime has been quiet since it shipped in Phase 3 — users had no surface-level signal that cleanup was loaded, warming, or actively generating. Settings has verbose status text internally, but a dictation-flow user never opens Settings during a run. New: a shared $state store drives a small chip in the sidebar that reflects the true LLM state in ≤500 ms of any transition (brief item #31 acceptance). Five states: off → hidden (user has aiTier === "off") warming → model download or first load in flight; amber pulse ready → loaded + idle; green dot generating → cleanup_transcript_text_cmd or extract_tasks_from_transcript_cmd in flight; accent pulse with Sparkles icon error → last operation failed; red dot with AlertTriangle The store exposes three calls: refreshLlmStatus(aiTier) (polls the backend), markGenerating(detail) / markGenerationDone(success). DictationPage wraps its cleanup + extract calls in mark-generating pairs. SettingsPage's LLM load / unload / delete / download paths also refresh the global store so Settings-initiated transitions surface in the sidebar immediately. The chip collapses to a dot-only compact form when the sidebar is collapsed. No backend changes — everything wires onto the existing `get_llm_status` Tauri command. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
61 lines
1.7 KiB
Svelte
61 lines
1.7 KiB
Svelte
<script lang="ts">
|
|
// @ts-nocheck
|
|
import { Sparkles, AlertTriangle } from "lucide-svelte";
|
|
import { llmStatus } from "$lib/stores/llmStatus.svelte.js";
|
|
|
|
let { compact = false } = $props<{ compact?: boolean }>();
|
|
|
|
let label = $derived.by(() => {
|
|
switch (llmStatus.kind) {
|
|
case "warming":
|
|
return llmStatus.detail ?? "AI warming";
|
|
case "ready":
|
|
return "AI ready";
|
|
case "generating":
|
|
return llmStatus.detail ?? "Cleaning…";
|
|
case "error":
|
|
return "AI error";
|
|
default:
|
|
return "";
|
|
}
|
|
});
|
|
|
|
let dotClass = $derived.by(() => {
|
|
switch (llmStatus.kind) {
|
|
case "warming":
|
|
return "bg-warning animate-pulse";
|
|
case "ready":
|
|
return "bg-success";
|
|
case "generating":
|
|
return "bg-accent animate-pulse";
|
|
case "error":
|
|
return "bg-danger";
|
|
default:
|
|
return "bg-text-tertiary";
|
|
}
|
|
});
|
|
</script>
|
|
|
|
{#if llmStatus.kind !== "off"}
|
|
{#if compact}
|
|
<span
|
|
class="inline-flex items-center justify-center w-[10px] h-[10px] rounded-full {dotClass}"
|
|
title={label}
|
|
aria-label={label}
|
|
></span>
|
|
{:else}
|
|
<span
|
|
class="inline-flex items-center gap-1.5 px-2 py-[3px] rounded-full bg-bg-elevated border border-border-subtle text-[10px] text-text-secondary whitespace-nowrap"
|
|
title={llmStatus.detail ?? label}
|
|
>
|
|
<span class="inline-block w-[6px] h-[6px] rounded-full {dotClass}"></span>
|
|
{#if llmStatus.kind === "error"}
|
|
<AlertTriangle size={10} class="text-danger" />
|
|
{:else if llmStatus.kind === "generating"}
|
|
<Sparkles size={10} class="text-accent" />
|
|
{/if}
|
|
<span>{label}</span>
|
|
</span>
|
|
{/if}
|
|
{/if}
|