feat(sidebar B.1 #31): visible LLM status chip with live state
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>
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
import { emit } from "@tauri-apps/api/event";
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import { page, settings, templates, profiles, addToHistory, addTask, tasks } from "$lib/stores/page.svelte.js";
|
||||
import { markGenerating, markGenerationDone } from "$lib/stores/llmStatus.svelte.js";
|
||||
import { profilesStore } from "$lib/stores/profiles.svelte.ts";
|
||||
import { toasts } from "$lib/stores/toasts.svelte.js";
|
||||
import Card from "$lib/components/Card.svelte";
|
||||
@@ -444,14 +445,20 @@
|
||||
const llmLoaded = await invoke("get_llm_status").catch(() => false);
|
||||
if (!llmLoaded) return text;
|
||||
|
||||
// Flip the sidebar LLM status chip to "generating" so the user can
|
||||
// see the cleanup pass in flight (brief item #31). Always paired
|
||||
// with markGenerationDone below — success or failure.
|
||||
markGenerating("Cleaning up");
|
||||
try {
|
||||
const cleaned = await invoke("cleanup_transcript_text_cmd", {
|
||||
transcript: text,
|
||||
profileId: profilesStore.activeProfileId,
|
||||
});
|
||||
markGenerationDone(true);
|
||||
return cleaned?.trim() ? cleaned.trim() : text;
|
||||
} catch (err) {
|
||||
console.warn("LLM cleanup failed, keeping existing transcript", err);
|
||||
markGenerationDone(false, typeof err === "string" ? err : err?.message ?? null);
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -459,11 +466,14 @@
|
||||
async function extractTasksForTranscript(text) {
|
||||
const llmLoaded = await invoke("get_llm_status").catch(() => false);
|
||||
if (settings.aiTier === "tasks" && llmLoaded) {
|
||||
markGenerating("Extracting tasks");
|
||||
try {
|
||||
const items = await invoke("extract_tasks_from_transcript_cmd", { transcript: text });
|
||||
markGenerationDone(true);
|
||||
return items.map((taskText) => ({ text: taskText }));
|
||||
} catch (err) {
|
||||
console.warn("LLM extract_tasks failed, falling back to regex", err);
|
||||
markGenerationDone(false, typeof err === "string" ? err : err?.message ?? null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user