/** * TypeScript types mirroring the Rust backend data structures. * * These are used by the IPC layer and components to maintain * type safety across the Tauri boundary. */ // -- Backend response types (match Rust structs in commands/mod.rs) -- export interface LoreStatus { last_sync: string | null; is_healthy: boolean; message: string; summary: LoreSummaryStatus | null; } export interface LoreSummaryStatus { open_issues: number; authored_mrs: number; reviewing_mrs: number; } export interface BridgeStatus { mapping_count: number; pending_count: number; suspect_count: number; last_sync: string | null; last_reconciliation: string | null; } export interface SyncResult { created: number; closed: number; skipped: number; /** Number of suspect_orphan flags cleared (item reappeared) */ healed: number; /** Error messages from non-fatal errors during sync */ errors: string[]; } // -- Structured error types (match Rust error.rs) -- /** Error codes for programmatic handling */ export type McErrorCode = | "LORE_UNAVAILABLE" | "LORE_UNHEALTHY" | "LORE_FETCH_FAILED" | "BRIDGE_LOCKED" | "BRIDGE_MAP_CORRUPTED" | "BRIDGE_SYNC_FAILED" | "BEADS_UNAVAILABLE" | "BEADS_CREATE_FAILED" | "BEADS_CLOSE_FAILED" | "IO_ERROR" | "INTERNAL_ERROR"; /** Structured error from Tauri IPC commands */ export interface McError { code: McErrorCode; message: string; recoverable: boolean; } /** Type guard to check if an error is a structured McError */ export function isMcError(err: unknown): err is McError { return ( typeof err === "object" && err !== null && "code" in err && "message" in err && "recoverable" in err ); } /** Result from the quick_capture command */ export interface CaptureResult { bead_id: string; } // -- Frontend-only types -- /** The type of work item surfaced in the Focus View */ export type FocusItemType = "mr_review" | "issue" | "mr_authored" | "manual"; /** A single work item that can be THE ONE THING */ export interface FocusItem { /** Unique key matching bridge mapping (e.g., "mr_review:g/p:847") */ id: string; /** Human-readable title */ title: string; /** Type badge to display */ type: FocusItemType; /** Project path (e.g., "platform/core") */ project: string; /** URL to open in browser (GitLab link) */ url: string; /** Entity IID (e.g., MR !847, Issue #42) */ iid: number; /** ISO timestamp of last update */ updatedAt: string | null; /** Optional context quote (e.g., reviewer comment) */ contextQuote: string | null; /** Who is requesting attention */ requestedBy: string | null; } /** Action the user takes on a focused item */ export type FocusAction = "start" | "defer_1h" | "defer_tomorrow" | "skip"; /** An entry in the decision log */ export interface DecisionEntry { timestamp: string; action: FocusAction; itemId: string; reason: string | null; } /** Staleness level derived from item age */ export type Staleness = "fresh" | "normal" | "amber" | "urgent"; // -- Inbox types -- /** Type of work item in the inbox */ export type InboxItemType = "mention" | "mr_feedback" | "review_request" | "assignment" | "manual"; /** A work item awaiting triage in the inbox */ export interface InboxItem { /** Unique identifier */ id: string; /** Human-readable title */ title: string; /** Type of inbox item */ type: InboxItemType; /** Whether this item has been triaged */ triaged: boolean; /** When the item was created/arrived */ createdAt: string; /** Optional snippet/preview */ snippet?: string; /** Source project */ project?: string; /** Web URL for opening in browser */ url?: string; /** Who triggered this item (e.g., commenter name) */ actor?: string; } /** Triage action the user can take on an inbox item */ export type TriageAction = "accept" | "defer" | "archive"; /** Duration options for deferring an item */ export type DeferDuration = "1h" | "3h" | "tomorrow" | "next_week"; /** Compute staleness from an ISO timestamp */ export function computeStaleness(updatedAt: string | null): Staleness { if (!updatedAt) return "normal"; const ageMs = Date.now() - new Date(updatedAt).getTime(); const ageDays = ageMs / (1000 * 60 * 60 * 24); if (ageDays < 1) return "fresh"; if (ageDays < 3) return "normal"; if (ageDays < 7) return "amber"; return "urgent"; }