Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6da3bf4
chore: update AUR checksum
vjousse Nov 28, 2025
a5d57d4
feat: parse session file content
vjousse Jun 26, 2025
0e0f2b2
fix: use `;` for the separator
vjousse Jun 26, 2025
81819f3
refactor: use anyhow and add tests
vjousse Jun 27, 2025
a1af091
feat: add option to display session label
vjousse Jun 27, 2025
e53c13b
chore: bump min rust version
vjousse Jun 27, 2025
163635d
chore: detect session start/end
vjousse Jun 27, 2025
8b68a6a
fix: simplify file handling, don’t allow to change default pomodorod …
vjousse Aug 11, 2025
509dafe
fix: clippy
vjousse Aug 11, 2025
88261c5
chore: create session file on play
vjousse Aug 13, 2025
cb1eacf
fix: create cache dir if doesn’t exist
vjousse Aug 13, 2025
c0e588c
feat: delete session file on reset
vjousse Aug 15, 2025
2691c96
chore: remove session file on reset
vjousse Aug 15, 2025
8963b96
refactor: session file content
vjousse Aug 15, 2025
f16c0b1
feat: auto start when file created
vjousse Aug 17, 2025
f34c605
chore: add debug
vjousse Sep 26, 2025
33cc3eb
chore: clippy
vjousse Oct 7, 2025
bf6bcaa
fix: current time computation
vjousse Oct 10, 2025
d991f8a
chore: get pomodoro from session file
vjousse Nov 7, 2025
91a70b0
refactor: move pomodoro code
vjousse Nov 7, 2025
9fab84b
fix: don’t use harcoded path in tests
vjousse Nov 7, 2025
0d88678
chore: downgrade rust version for flatpak
vjousse Nov 7, 2025
df91825
test: add session file tests
vjousse Nov 7, 2025
0f0a90e
feat(wip): control gui with session file
vjousse Nov 7, 2025
525642a
chore: clippy
vjousse Nov 7, 2025
4f5ac8e
chore: parse status from file
vjousse Nov 10, 2025
36980eb
chore(wip): pause management
vjousse Nov 18, 2025
119ab5b
refactor: use elapsed time in session file
vjousse Nov 21, 2025
924a79e
chore: lint
vjousse Nov 21, 2025
d54a18a
chore: session file tests
vjousse Feb 17, 2026
fbd6c52
chore: remove dead code
vjousse Feb 17, 2026
43c3e2c
chore: write session file
vjousse Feb 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion aur/PKGBUILD
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ license=('MIT')
depends=('alsa-lib' 'gtk3' 'hicolor-icon-theme' 'glibc' 'webkit2gtk-4.1' 'libsoup' 'cairo' 'glib2' 'pango' 'gcc-libs' 'gdk-pixbuf2' 'libayatana-appindicator')
provides=('pomodorolm')
source=("https://github.com/vjousse/pomodorolm/releases/download/app-v$pkgver/pomodorolm_${pkgver}_amd64.deb")
sha256sums=('8042ccb3d1be79c96ff8b5107ec5e1aee44cf09853d991dba8bd462e3fa14eb8')
sha256sums=('2cc41dbd3937998db8c10387a581af8779fc5d5cbbf2972c6dbe786df9f2f5ce')

package() {
bsdtar -xf "$srcdir/data.tar.gz" -C "$pkgdir"
Expand Down
6 changes: 5 additions & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ description = "A Tauri App"
authors = ["Vincent Jousse"]
license = ""
repository = ""
edition = "2021"
rust-version = "1.60"
edition = "2024"
rust-version = "1.89"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand All @@ -33,6 +33,8 @@ rustls-pemfile = "2.2.0"
rodio = "0.19.0"
clap = { version = "4.0.32", features = ["derive"] }
dirs = "6.0.0"
anyhow = "1.0.98"
chrono = { version = "0.4.42", features = ["serde"] }
[features]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
Expand Down
92 changes: 45 additions & 47 deletions src-tauri/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,84 +1,82 @@
extern crate dirs;
use crate::config::Config;
use crate::config::{Config, pomodoro_state_from_config};
use crate::pomodoro::{SessionStatus, get_session_info, tick_with_file_session_info};

use anyhow::{Context, Result};
use std::fs;
use std::path::Path;
use std::time::{Duration, SystemTime};
use std::time::Duration;
use tokio::time::interval;

pub fn run(config_dir_name: &str) {
pub fn run(config_dir_name: &str, display_label: bool) -> Result<()> {
let config_dir = dirs::config_dir()
.expect("Error while getting the config directory")
.join(config_dir_name);

let config =
Config::get_or_create_from_disk(&config_dir, None).expect("Unable to get config file");
Config::get_or_create_from_disk(&config_dir, None).context("Unable to get config file")?;

// Initialize the Tokio runtime
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(run_pomodoro_checker(config));
rt.block_on(run_pomodoro_checker(config, display_label))
}

async fn run_pomodoro_checker(config: Config) {
let cache_dir = dirs::cache_dir().expect("Error while getting the cache directory");
async fn run_pomodoro_checker(config: Config, display_label: bool) -> Result<()> {
let mut pomodoro = pomodoro_state_from_config(&config);

let file_path = cache_dir.join("pomodoro_session");
let mut interval = interval(Duration::from_secs(1));

loop {
interval.tick().await;

if file_exists(&file_path).await {
if let Some(remaining_time) =
get_remaining_time(&file_path, config.focus_duration as u64).await
{
let total_seconds = config.focus_duration as u64; // Total time for Pomodoro in seconds
let remaining_seconds = remaining_time.as_secs();
let elapsed_seconds = total_seconds - remaining_seconds;

// Create the progress bar
let progress_bar = create_progress_bar(total_seconds, elapsed_seconds);
let formatted_time = format_time(remaining_seconds);

// Check if remaining time is zero
if remaining_seconds == 0 {
// Delete the session file
if let Err(e) = fs::remove_file(&file_path) {
eprintln!("Failed to delete session file: {e}");
}
continue;
}
let next_pomodoro = tick_with_file_session_info(
&pomodoro,
get_session_info(&pomodoro.config.session_file),
)?;

if next_pomodoro.current_session.elapsed_seconds == 1 {
println!("-> New pomodoro created");
}

// Create the progress bar
let progress_bar = create_progress_bar(
next_pomodoro.config.focus_duration.into(),
next_pomodoro.current_session.elapsed_seconds.into(),
);
let remaining_seconds = (next_pomodoro.config.focus_duration
- next_pomodoro.current_session.elapsed_seconds) as u64;

let formatted_time = format_time(remaining_seconds);

if next_pomodoro.current_session.status != SessionStatus::NotStarted {
if let Some(ref label) = next_pomodoro.current_session.label
&& display_label
{
println!("{progress_bar} {formatted_time} {}", label);
} else {
println!("{progress_bar} {formatted_time}");
}
} else {
println!("P -");
}

// Check if remaining time is zero
if remaining_seconds == 0 && file_exists(&pomodoro.config.session_file) {
// Delete the session file
fs::remove_file(&pomodoro.config.session_file)
.context("Failed to delete session file")?;
println!("-> Pomodoro ended normally");
continue;
}

pomodoro = next_pomodoro;
}
}

async fn file_exists(path: &Path) -> bool {
fn file_exists(path: &Path) -> bool {
fs::metadata(path).is_ok()
}

async fn get_remaining_time(path: &Path, duration: u64) -> Option<Duration> {
if let Ok(metadata) = fs::metadata(path) {
if let Ok(modified_time) = metadata.modified() {
let now = SystemTime::now();
let elapsed = now.duration_since(modified_time).ok()?;
let total_duration = Duration::from_secs(duration);

if elapsed >= total_duration {
return Some(Duration::from_secs(0)); // Return zero if the time is up
}

let remaining = total_duration - elapsed;
return Some(remaining);
}
}
None
}

fn create_progress_bar(total_seconds: u64, elapsed_seconds: u64) -> String {
let total_hashes = 10; // Total number of '#' in the progress bar
let filled_length =
Expand Down
31 changes: 29 additions & 2 deletions src-tauri/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::pomodoro;
use crate::pomodoro::{self, default_session_file};
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::fs;
use std::fs::OpenOptions;
Expand Down Expand Up @@ -32,6 +33,8 @@ pub struct Config {
pub minimize_to_tray_on_close: bool,
#[serde(default)]
pub muted: bool,
#[serde(default = "default_session_file")]
pub session_file: PathBuf,
pub short_break_audio: Option<String>,
pub short_break_duration: u16,
#[serde(default)]
Expand Down Expand Up @@ -69,7 +72,7 @@ impl Config {
pub fn get_or_create_from_disk(
config_dir: &Path,
config_file_name: Option<String>,
) -> Result<Self, Box<dyn std::error::Error>> {
) -> Result<Self> {
let config_file_path = Self::get_config_file_path(config_dir, config_file_name);

// Create the config dir and the themes one if they don’t exist
Expand Down Expand Up @@ -120,6 +123,7 @@ impl Default for Config {
minimize_to_tray: true,
minimize_to_tray_on_close: true,
muted: false,
session_file: default_session_file(),
short_break_audio: None,
short_break_duration: 5 * 60,
start_minimized: false,
Expand All @@ -130,3 +134,26 @@ impl Default for Config {
}
}
}

pub fn pomodoro_config(config: &Config) -> pomodoro::Config {
pomodoro::Config {
auto_start_long_break_timer: config.auto_start_break_timer,
auto_start_short_break_timer: config.auto_start_break_timer,
auto_start_focus_timer: config.auto_start_work_timer,
default_focus_label: config.default_focus_label.clone(),
default_short_break_label: config.default_short_break_label.clone(),
default_long_break_label: config.default_long_break_label.clone(),
focus_duration: config.focus_duration,
long_break_duration: config.long_break_duration,
max_focus_rounds: config.max_round_number,
session_file: config.session_file.clone(),
short_break_duration: config.short_break_duration,
}
}

pub fn pomodoro_state_from_config(config: &Config) -> pomodoro::Pomodoro {
pomodoro::Pomodoro {
config: pomodoro_config(config),
..pomodoro::Pomodoro::default()
}
}
Loading