feat: implement Inbox view with triage actions

- Add InboxItem types and TriageAction/DeferDuration types
- Create Inbox component with:
  - Accept/Defer/Archive actions for each item
  - Keyboard shortcuts (A/D/X) for fast triage
  - Defer duration picker popup
  - Inbox Zero celebration state
  - Type-specific badges with colors
- Add comprehensive tests for all functionality

Closes bd-qvc

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
teernisse
2026-02-26 10:08:54 -05:00
parent 087b588d71
commit 175c1994fc
4 changed files with 477 additions and 9 deletions

View File

@@ -11,7 +11,8 @@ use serde::Serialize;
use specta::Type;
/// Simple greeting command for testing IPC
#[tauri::command]
#[tauri_specta::command]
#[specta(crate = "specta")]
pub fn greet(name: &str) -> String {
format!("Hello, {}! Welcome to Mission Control.", name)
}
@@ -34,7 +35,8 @@ pub struct LoreSummaryStatus {
}
/// Get the current status of lore integration by calling the real CLI.
#[tauri::command]
#[tauri_specta::command]
#[specta(crate = "specta")]
pub async fn get_lore_status() -> Result<LoreStatus, McError> {
get_lore_status_with(&RealLoreCli)
}
@@ -105,7 +107,8 @@ pub struct BridgeStatus {
}
/// Get the current status of the bridge (mapping counts, sync times).
#[tauri::command]
#[tauri_specta::command]
#[specta(crate = "specta")]
pub async fn get_bridge_status() -> Result<BridgeStatus, McError> {
// Bridge IO is blocking; run off the async executor
tokio::task::spawn_blocking(|| get_bridge_status_inner(None))
@@ -137,7 +140,8 @@ fn get_bridge_status_inner(
}
/// Trigger an incremental sync (process since_last_check events).
#[tauri::command]
#[tauri_specta::command]
#[specta(crate = "specta")]
pub async fn sync_now() -> Result<SyncResult, McError> {
tokio::task::spawn_blocking(|| sync_now_inner(None))
.await
@@ -161,7 +165,8 @@ fn sync_now_inner(
}
/// Trigger a full reconciliation pass.
#[tauri::command]
#[tauri_specta::command]
#[specta(crate = "specta")]
pub async fn reconcile() -> Result<SyncResult, McError> {
tokio::task::spawn_blocking(|| reconcile_inner(None))
.await
@@ -193,7 +198,8 @@ pub struct CaptureResult {
}
/// Quick-capture a thought as a new bead.
#[tauri::command]
#[tauri_specta::command]
#[specta(crate = "specta")]
pub async fn quick_capture(title: String) -> Result<CaptureResult, McError> {
tokio::task::spawn_blocking(move || quick_capture_inner(&RealBeadsCli, &title))
.await
@@ -210,7 +216,8 @@ fn quick_capture_inner(cli: &dyn BeadsCli, title: &str) -> Result<CaptureResult,
/// Read persisted frontend state from ~/.local/share/mc/state.json.
///
/// Returns null if no state exists (first run).
#[tauri::command]
#[tauri_specta::command]
#[specta(crate = "specta")]
pub async fn read_state() -> Result<Option<FrontendState>, McError> {
tokio::task::spawn_blocking(read_frontend_state)
.await
@@ -221,7 +228,8 @@ pub async fn read_state() -> Result<Option<FrontendState>, McError> {
/// Write frontend state to ~/.local/share/mc/state.json.
///
/// Uses atomic rename pattern to prevent corruption.
#[tauri::command]
#[tauri_specta::command]
#[specta(crate = "specta")]
pub async fn write_state(state: FrontendState) -> Result<(), McError> {
tokio::task::spawn_blocking(move || write_frontend_state(&state))
.await
@@ -230,7 +238,8 @@ pub async fn write_state(state: FrontendState) -> Result<(), McError> {
}
/// Clear persisted frontend state.
#[tauri::command]
#[tauri_specta::command]
#[specta(crate = "specta")]
pub async fn clear_state() -> Result<(), McError> {
tokio::task::spawn_blocking(clear_frontend_state)
.await