feat(ui): add summary/action-item types + errorMessage() helper (T7.1, FR-LLM-2/3/4)

Types for SummaryFile/ActionItem/LlmStatus and a confirmActionItems
wrapper + onLlmDone listener, so the frontend can consume what
generate_summary/confirm_action_items already produce on the backend.

Also adds errorMessage(): every backend command rejects with
{ kind, message } (see src-tauri/src/error.rs), not a native Error, so
`e instanceof Error` is always false for it and `String(e)` renders as
the literal text "[object Object]" — found by actually clicking
"Generate summary" against an unreachable Ollama endpoint.
This commit is contained in:
iamdoubz committed 2026-07-02 07:44:56 -05:00
1 parent b2d423e3ec
commit 075eec6a95
1 file changed
+51 -1
+51 -1
View File
@@ -8,6 +8,22 @@ import { listen, type UnlistenFn } from "@tauri-apps/api/event";
export type MeetingId = string;
export type BackendId = "npu" | "nvidia" | "amd" | "intel" | "cpu";
// Every backend command rejects an `Err` as this shape, not a native Error
// (see src-tauri/src/error.rs) — `e instanceof Error` is always false for it.
export interface WaError {
kind: string;
message: string;
}
export function errorMessage(e: unknown): string {
if (typeof e === "string") return e;
if (e instanceof Error) return e.message;
if (e && typeof e === "object" && typeof (e as WaError).message === "string") {
return (e as WaError).message;
}
return String(e);
}
export interface BackendInfo {
id: BackendId;
name: string;
@@ -23,6 +39,13 @@ export interface HardwareStatus {
estRtf: number;
}
export interface LlmStatus {
provider: string; // ollama|custom|off (Phase 10a adds anthropic|openai)
reachable: boolean;
isLocal: boolean;
models: string[];
}
export interface ModelInfo {
id: string;
label: string;
@@ -57,6 +80,28 @@ export interface SpeakerInfo {
participant_id: string | null;
}
// Drafted action item from a summary (Phase 5, FR-LLM-3). `id` is only set
// once `confirmActionItems` has persisted it as a row.
export interface ActionItem {
id: string | null;
text: string;
owner: string | null;
due_at: number | null;
confirmed: boolean;
}
// On-disk shape of summary.json (FR-LLM-2/4) — `null` until generate_summary
// has run at least once for a meeting.
export interface SummaryFile {
schema: number;
generated_at: number;
provider: string;
model: string;
summary_md: string;
decisions: string[];
action_items: ActionItem[];
}
// Full meeting detail (get_meeting) — DB row + transcript + speakers + notes.
export interface Meeting {
id: MeetingId;
@@ -72,6 +117,7 @@ export interface Meeting {
segments: TranscriptSegment[];
speakers: SpeakerInfo[];
notes_markdown: string;
summary: SummaryFile | null;
calendar_event_id: string | null;
}
@@ -227,7 +273,7 @@ export const api = {
exportMeeting: (meetingId: MeetingId, dest: string, format: "md" | "bundle") =>
invoke<string>("export_meeting", { meetingId, dest, format }),
llmStatus: () => invoke("llm_status"),
llmStatus: () => invoke<LlmStatus>("llm_status"),
// provider ∈ ollama|custom|anthropic|openai|off; apiKey (hosted) → OS credential store (ADR-0011).
setLlmProvider: (config: {
provider: string;
@@ -237,6 +283,8 @@ export const api = {
}) => invoke<void>("set_llm_provider", { config }),
generateSummary: (meetingId: MeetingId, templateId?: string) =>
invoke<void>("generate_summary", { meetingId, templateId }),
confirmActionItems: (meetingId: MeetingId, items: ActionItem[]) =>
invoke<void>("confirm_action_items", { meetingId, items }),
importPst: (path: string, password?: string) => invoke<number>("import_pst", { path, password }),
listCalendarEvents: (from?: number, to?: number) =>
@@ -316,6 +364,8 @@ export const events = {
): Promise<UnlistenFn> => listen("transcript://finalized", (e) => cb(e.payload as never)),
onLlmToken: (cb: (p: { meetingId: string; text: string }) => void): Promise<UnlistenFn> =>
listen("llm://token", (e) => cb(e.payload as never)),
onLlmDone: (cb: (p: { meetingId: string; summary: SummaryFile }) => void): Promise<UnlistenFn> =>
listen("llm://done", (e) => cb(e.payload as never)),
onModelProgress: (
cb: (p: { id: string; receivedBytes: number; totalBytes: number | null }) => void,
): Promise<UnlistenFn> => listen("model://progress", (e) => cb(e.payload as never)),