From 2fc4ee70874d238fccd8e47dbd9b70c7a8c527ea Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 21 Mar 2026 13:59:12 +0000 Subject: [PATCH] feat(timer): wire VisualTimer to tasks with notifications and persistence Co-Authored-By: Claude Opus 4.6 (1M context) --- package-lock.json | 10 ++ package.json | 1 + src-tauri/Cargo.toml | 1 + src-tauri/capabilities/default.json | 3 +- src-tauri/src/commands/mod.rs | 1 + src-tauri/src/commands/timer.rs | 56 ++++++ src-tauri/src/lib.rs | 5 + src/lib/components/TaskTimer.svelte | 242 ++++++++++++++++++++++++++ src/lib/components/WipTaskList.svelte | 17 +- src/lib/pages/TasksPage.svelte | 19 +- src/routes/+layout.svelte | 21 ++- 11 files changed, 371 insertions(+), 5 deletions(-) create mode 100644 src-tauri/src/commands/timer.rs create mode 100644 src/lib/components/TaskTimer.svelte diff --git a/package-lock.json b/package-lock.json index 66cfee6..ef9f407 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@tauri-apps/api": "^2", "@tauri-apps/plugin-dialog": "^2.6.0", "@tauri-apps/plugin-global-shortcut": "^2.3.1", + "@tauri-apps/plugin-notification": "^2.3.3", "@tauri-apps/plugin-opener": "^2", "lucide-svelte": "^0.577.0" }, @@ -1563,6 +1564,15 @@ "@tauri-apps/api": "^2.8.0" } }, + "node_modules/@tauri-apps/plugin-notification": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-notification/-/plugin-notification-2.3.3.tgz", + "integrity": "sha512-Zw+ZH18RJb41G4NrfHgIuofJiymusqN+q8fGUIIV7vyCH+5sSn5coqRv/MWB9qETsUs97vmU045q7OyseCV3Qg==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.8.0" + } + }, "node_modules/@tauri-apps/plugin-opener": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.5.3.tgz", diff --git a/package.json b/package.json index b5751e4..da19fa5 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@tauri-apps/api": "^2", "@tauri-apps/plugin-dialog": "^2.6.0", "@tauri-apps/plugin-global-shortcut": "^2.3.1", + "@tauri-apps/plugin-notification": "^2.3.3", "@tauri-apps/plugin-opener": "^2", "lucide-svelte": "^0.577.0" }, diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 80d3798..55a49e6 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -26,6 +26,7 @@ tauri = { version = "2", features = ["tray-icon"] } tauri-plugin-opener = "2" tauri-plugin-dialog = "2" tauri-plugin-global-shortcut = "2" +tauri-plugin-notification = "2" # Serialisation serde = { version = "1", features = ["derive"] } diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index f94b23d..875c335 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -17,6 +17,7 @@ "opener:default", "dialog:default", "global-shortcut:allow-register", - "global-shortcut:allow-unregister" + "global-shortcut:allow-unregister", + "notification:default" ] } diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index d592fdd..1761316 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -4,5 +4,6 @@ pub mod hardware; pub mod history; pub mod models; pub mod tasks; +pub mod timer; pub mod transcription; pub mod windows; diff --git a/src-tauri/src/commands/timer.rs b/src-tauri/src/commands/timer.rs new file mode 100644 index 0000000..5dc6bff --- /dev/null +++ b/src-tauri/src/commands/timer.rs @@ -0,0 +1,56 @@ +use serde::Serialize; + +use crate::AppState; +use kon_storage::{clear_timer_state, get_timer_state, save_timer_state}; + +/// Serialisable timer state response. +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct TimerStateResponse { + pub task_id: String, + pub total_seconds: i64, + pub remaining_seconds: i64, + pub started_at: String, + pub paused: bool, +} + +/// Save or update the active timer state. +#[tauri::command] +pub async fn save_timer( + state: tauri::State<'_, AppState>, + task_id: String, + total_seconds: i64, + remaining_seconds: i64, + paused: bool, +) -> Result<(), String> { + save_timer_state(&state.db, &task_id, total_seconds, remaining_seconds, paused) + .await + .map_err(|e| e.to_string()) +} + +/// Retrieve the active timer state (for context restoration on app restart). +#[tauri::command] +pub async fn get_timer( + state: tauri::State<'_, AppState>, +) -> Result, String> { + let row = get_timer_state(&state.db) + .await + .map_err(|e| e.to_string())?; + Ok(row.map(|r| TimerStateResponse { + task_id: r.task_id, + total_seconds: r.total_seconds, + remaining_seconds: r.remaining_seconds, + started_at: r.started_at, + paused: r.paused, + })) +} + +/// Clear the active timer (completed or cancelled). +#[tauri::command] +pub async fn clear_timer( + state: tauri::State<'_, AppState>, +) -> Result<(), String> { + clear_timer_state(&state.db) + .await + .map_err(|e| e.to_string()) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 0d1c80d..ff4152d 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -72,6 +72,7 @@ pub fn run() { .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_global_shortcut::Builder::new().build()) + .plugin(tauri_plugin_notification::init()) .setup(|app| { // Initialise database (blocking in setup — runs once at startup) let db_path = database_path(); @@ -154,6 +155,10 @@ pub fn run() { commands::tasks::reorder_tasks_cmd, commands::tasks::complete_task_cmd, commands::tasks::uncomplete_task_cmd, + // Timer + commands::timer::save_timer, + commands::timer::get_timer, + commands::timer::clear_timer, // Transcription commands::transcription::transcribe_pcm, commands::transcription::transcribe_file, diff --git a/src/lib/components/TaskTimer.svelte b/src/lib/components/TaskTimer.svelte new file mode 100644 index 0000000..f7f3ecf --- /dev/null +++ b/src/lib/components/TaskTimer.svelte @@ -0,0 +1,242 @@ + + +{#if running} +
+ + {#if reduceMotion} + +
+
+
+ {:else} + + {/if} + + +
+

{timeDisplay}

+ {#if taskText} +

{taskText}

+ {/if} +
+ + +
+ + +
+
+{/if} diff --git a/src/lib/components/WipTaskList.svelte b/src/lib/components/WipTaskList.svelte index 47bc28b..af89460 100644 --- a/src/lib/components/WipTaskList.svelte +++ b/src/lib/components/WipTaskList.svelte @@ -1,7 +1,13 @@