From 822d9194aeed0bc046d4af68c14dec8349e169ca Mon Sep 17 00:00:00 2001 From: Konstantin Fickel Date: Tue, 7 Apr 2026 13:49:12 +0200 Subject: [PATCH 1/6] fix: cross-platform compatibility for Windows support - Use directories::BaseDirs for config path fallback instead of hardcoded Unix path - Default to notepad on Windows instead of vi for editor commands - Skip +N line argument for notepad in todo edit (notepad doesn't support it) --- src/cli/commands/edit.rs | 8 +++++++- src/cli/commands/new.rs | 8 +++++++- src/cli/commands/todo.rs | 18 ++++++++++++------ src/config.rs | 4 +++- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/cli/commands/edit.rs b/src/cli/commands/edit.rs index a8761f3..4c15017 100644 --- a/src/cli/commands/edit.rs +++ b/src/cli/commands/edit.rs @@ -67,7 +67,13 @@ pub fn run(number: i32) -> Result<(), StreamdError> { }; if let Some(file_path) = sorted_shards[selected_index].location.get("file") { - let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()); + let editor = std::env::var("EDITOR").unwrap_or_else(|_| { + if cfg!(windows) { + "notepad".to_string() + } else { + "vi".to_string() + } + }); Command::new(&editor).arg(file_path).status()?; } diff --git a/src/cli/commands/new.rs b/src/cli/commands/new.rs index de7d351..5c3eecb 100644 --- a/src/cli/commands/new.rs +++ b/src/cli/commands/new.rs @@ -24,7 +24,13 @@ pub fn run() -> Result<(), StreamdError> { drop(file); // Open in editor - let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()); + let editor = std::env::var("EDITOR").unwrap_or_else(|_| { + if cfg!(windows) { + "notepad".to_string() + } else { + "vi".to_string() + } + }); let status = Command::new(&editor).arg(&preliminary_path).status()?; if !status.success() { diff --git a/src/cli/commands/todo.rs b/src/cli/commands/todo.rs index 17bfba2..e54d1e2 100644 --- a/src/cli/commands/todo.rs +++ b/src/cli/commands/todo.rs @@ -92,13 +92,19 @@ pub fn run_edit(number: usize) -> Result<(), StreamdError> { .get("file") .ok_or(StreamdError::MissingFilePath)?; - let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()); - let line_arg = format!("+{}", task.start_line); + let editor = std::env::var("EDITOR").unwrap_or_else(|_| { + if cfg!(windows) { + "notepad".to_string() + } else { + "vi".to_string() + } + }); - let status = Command::new(&editor) - .arg(&line_arg) - .arg(file_path) - .status()?; + let mut cmd = Command::new(&editor); + if !editor.to_lowercase().contains("notepad") { + cmd.arg(format!("+{}", task.start_line)); + } + let status = cmd.arg(file_path).status()?; if !status.success() { return Err(StreamdError::IoError(std::io::Error::other( diff --git a/src/config.rs b/src/config.rs index c828ba9..417e8f0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -37,8 +37,10 @@ impl Settings { fn config_path() -> PathBuf { if let Some(proj_dirs) = ProjectDirs::from("", "", "streamd") { proj_dirs.config_dir().join("config.toml") + } else if let Some(base_dirs) = directories::BaseDirs::new() { + base_dirs.config_dir().join("streamd").join("config.toml") } else { - PathBuf::from("~/.config/streamd/config.toml") + PathBuf::from("streamd_config.toml") } } } From aa1fbbc4b8d030f321023e5f76aa792030bd5d35 Mon Sep 17 00:00:00 2001 From: Konstantin Fickel Date: Tue, 7 Apr 2026 13:49:45 +0200 Subject: [PATCH 2/6] feat: add Windows cross-compilation and release artifacts - Add mkWindowsCraneLib using x86_64-pc-windows-gnu target - Add mkStreamdWindows using mingw-w64 toolchain for cross-compilation - Export streamd-windows package from flake - Add Windows build step and .exe artifact to release workflow --- .forgejo/workflows/release.yml | 5 +++++ flake.nix | 35 +++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index 7c93504..bef91f5 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -49,12 +49,17 @@ jobs: if: steps.version.outputs.SKIP != 'true' run: nix build .#streamd-musl -o result-musl + - name: Build Windows binary + if: steps.version.outputs.SKIP != 'true' + run: nix build .#streamd-windows -o result-windows + - name: Prepare release artifacts if: steps.version.outputs.SKIP != 'true' run: | mkdir -p release cp result-deb release/streamd_${{ steps.version.outputs.VERSION }}_amd64.deb cp result-musl/bin/streamd release/streamd-${{ steps.version.outputs.VERSION }}-linux-x86_64 + cp result-windows/bin/streamd.exe release/streamd-${{ steps.version.outputs.VERSION }}-windows-x86_64.exe - name: Create release if: steps.version.outputs.SKIP != 'true' diff --git a/flake.nix b/flake.nix index 38723ae..12cdf69 100644 --- a/flake.nix +++ b/flake.nix @@ -132,6 +132,38 @@ in craneLib.buildPackage (commonArgs // { inherit cargoArtifacts; }); + mkWindowsCraneLib = + system: + let + pkgs = mkPkgs system; + toolchain = pkgs.rust-bin.stable.latest.default.override { + targets = [ "x86_64-pc-windows-gnu" ]; + }; + in + (crane.mkLib pkgs).overrideToolchain toolchain; + + mkStreamdWindows = + system: + let + pkgs = mkPkgs system; + pkgsCross = pkgs.pkgsCross.mingwW64; + craneLib = mkWindowsCraneLib system; + commonArgs = { + src = craneLib.path ./.; + pname = "streamd"; + inherit version; + strictDeps = true; + CARGO_BUILD_TARGET = "x86_64-pc-windows-gnu"; + CC_x86_64_pc_windows_gnu = "${pkgsCross.stdenv.cc}/bin/x86_64-w64-mingw32-gcc"; + CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER = "${pkgsCross.stdenv.cc}/bin/x86_64-w64-mingw32-gcc"; + nativeBuildInputs = [ pkgsCross.stdenv.cc ]; + buildInputs = [ pkgsCross.windows.pthreads ]; + doCheck = false; + }; + cargoArtifacts = craneLib.buildDepsOnly commonArgs; + in + craneLib.buildPackage (commonArgs // { inherit cargoArtifacts; }); + mkStreamdDeb = system: let @@ -181,9 +213,10 @@ streamd = mkStreamd system; streamd-musl = mkStreamdMusl system; streamd-deb = mkStreamdDeb system; + streamd-windows = mkStreamdWindows system; in { - inherit streamd streamd-musl streamd-deb; + inherit streamd streamd-musl streamd-deb streamd-windows; default = streamd; } ); From ee96033639b1d2689056ff183e7e183db7b8890c Mon Sep 17 00:00:00 2001 From: Konstantin Fickel Date: Mon, 13 Apr 2026 19:49:50 +0200 Subject: [PATCH 3/6] refactor: remove duplicate code --- src/cli/commands/daily.rs | 34 +++++------------------------ src/cli/commands/edit.rs | 41 ++++++++--------------------------- src/cli/commands/mod.rs | 35 ++++++++++++++++++++++++++++++ src/cli/commands/timesheet.rs | 32 +++------------------------ src/cli/commands/todo.rs | 38 ++++++++------------------------ 5 files changed, 61 insertions(+), 119 deletions(-) diff --git a/src/cli/commands/daily.rs b/src/cli/commands/daily.rs index 9b108f6..b202d0f 100644 --- a/src/cli/commands/daily.rs +++ b/src/cli/commands/daily.rs @@ -4,38 +4,13 @@ use std::process::Command; use chrono::{Days, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; use chrono_tz::Tz; -use walkdir::WalkDir; use crate::config::Settings; use crate::error::StreamdError; -use crate::extract::parse_markdown_file; -use crate::localize::localize_stream_file; -use crate::models::{LocalizedShard, RepositoryConfiguration}; +use crate::models::RepositoryConfiguration; use crate::timesheet::load_repository_config; -fn load_all_shards(base_folder: &Path, tz: Tz) -> Result, StreamdError> { - let config = RepositoryConfiguration::new(); - let mut shards = Vec::new(); - - for entry in WalkDir::new(base_folder) - .max_depth(1) - .into_iter() - .filter_map(|e| e.ok()) - { - let path = entry.path(); - if path.extension().map(|e| e == "md").unwrap_or(false) { - let file_name = path.to_string_lossy().to_string(); - let content = fs::read_to_string(path)?; - let stream_file = parse_markdown_file(&file_name, &content); - - if let Ok(shard) = localize_stream_file(&stream_file, &config, tz) { - shards.push(shard); - } - } - } - - Ok(shards) -} +use super::load_markdown_shards; pub fn run(date: Option) -> Result<(), StreamdError> { let settings = Settings::load()?; @@ -69,7 +44,7 @@ pub fn run(date: Option) -> Result<(), StreamdError> { .unwrap() .with_timezone(&Utc); - let all_shards = load_all_shards(base_folder, tz)?; + let all_shards = load_markdown_shards(base_folder, &RepositoryConfiguration::new(), tz)?; let mut daily_shards: Vec<_> = all_shards .into_iter() .filter(|s| { @@ -77,8 +52,9 @@ pub fn run(date: Option) -> Result<(), StreamdError> { .get("file_type") .map(|v| v == "daily") .unwrap_or(false) + && s.moment >= day_start + && s.moment < day_end }) - .filter(|s| s.moment >= day_start && s.moment < day_end) .collect(); daily_shards.sort_by_key(|s| s.moment); diff --git a/src/cli/commands/edit.rs b/src/cli/commands/edit.rs index a8761f3..cfc28c1 100644 --- a/src/cli/commands/edit.rs +++ b/src/cli/commands/edit.rs @@ -1,42 +1,19 @@ -use std::fs; +use std::path::Path; use std::process::Command; -use walkdir::WalkDir; - use crate::config::Settings; use crate::error::StreamdError; -use crate::extract::parse_markdown_file; -use crate::localize::{localize_stream_file, TaskConfiguration}; -use crate::models::LocalizedShard; +use crate::localize::TaskConfiguration; -fn all_files() -> Result, StreamdError> { - let settings = Settings::load()?; - let mut shards = Vec::new(); - - for entry in WalkDir::new(&settings.base_folder) - .max_depth(1) - .into_iter() - .filter_map(|e| e.ok()) - { - let path = entry.path(); - if path.extension().map(|e| e == "md").unwrap_or(false) { - let file_name = path.to_string_lossy().to_string(); - let content = fs::read_to_string(path)?; - let stream_file = parse_markdown_file(&file_name, &content); - - if let Ok(shard) = - localize_stream_file(&stream_file, &TaskConfiguration, chrono_tz::UTC) - { - shards.push(shard); - } - } - } - - Ok(shards) -} +use super::load_markdown_shards; pub fn run(number: i32) -> Result<(), StreamdError> { - let all_shards = all_files()?; + let settings = Settings::load()?; + let all_shards = load_markdown_shards( + Path::new(&settings.base_folder), + &TaskConfiguration, + chrono_tz::UTC, + )?; // Sort by moment (timestamp) let mut sorted_shards = all_shards; diff --git a/src/cli/commands/mod.rs b/src/cli/commands/mod.rs index fa6dbba..e49668b 100644 --- a/src/cli/commands/mod.rs +++ b/src/cli/commands/mod.rs @@ -1,6 +1,41 @@ +use std::fs; +use std::path::Path; + +use chrono_tz::Tz; +use walkdir::WalkDir; + +use crate::error::StreamdError; +use crate::extract::parse_markdown_file; +use crate::localize::localize_stream_file; +use crate::models::{LocalizedShard, RepositoryConfiguration}; + pub mod completions; pub mod daily; pub mod edit; pub mod new; pub mod timesheet; pub mod todo; + +pub fn load_markdown_shards( + base_folder: &Path, + config: &RepositoryConfiguration, + tz: Tz, +) -> Result, StreamdError> { + let mut shards = Vec::new(); + for entry in WalkDir::new(base_folder) + .max_depth(1) + .into_iter() + .filter_map(|e| e.ok()) + { + let path = entry.path(); + if path.extension().map(|e| e == "md").unwrap_or(false) { + let file_name = path.to_string_lossy().to_string(); + let content = fs::read_to_string(path)?; + let stream_file = parse_markdown_file(&file_name, &content); + if let Ok(shard) = localize_stream_file(&stream_file, config, tz) { + shards.push(shard); + } + } + } + Ok(shards) +} diff --git a/src/cli/commands/timesheet.rs b/src/cli/commands/timesheet.rs index a1c4a37..1b422c9 100644 --- a/src/cli/commands/timesheet.rs +++ b/src/cli/commands/timesheet.rs @@ -1,49 +1,23 @@ use std::collections::HashMap; -use std::fs; use std::path::Path; use chrono::Datelike; use chrono::NaiveDate; use chrono::Utc; use chrono_tz::Tz; -use walkdir::WalkDir; use crate::config::Settings; const SEPARATOR_WIDTH: usize = 71; const COLUMN_SEPARATOR_WIDTH: usize = 65; use crate::error::StreamdError; -use crate::extract::parse_markdown_file; -use crate::localize::localize_stream_file; -use crate::models::{LocalizedShard, Timesheet}; +use crate::models::Timesheet; use crate::timesheet::{ extract_timesheets, generate_report, load_repository_config, BasicTimesheetConfiguration, DayType, DayWarning, MonthReport, TimesheetReport, }; -fn load_all_shards(base_folder: &Path, tz: Tz) -> Result, StreamdError> { - let mut shards = Vec::new(); - - for entry in WalkDir::new(base_folder) - .max_depth(1) - .into_iter() - .filter_map(|e| e.ok()) - { - let path = entry.path(); - if path.extension().map(|e| e == "md").unwrap_or(false) { - let file_name = path.to_string_lossy().to_string(); - let content = fs::read_to_string(path)?; - let stream_file = parse_markdown_file(&file_name, &content); - - if let Ok(shard) = localize_stream_file(&stream_file, &BasicTimesheetConfiguration, tz) - { - shards.push(shard); - } - } - } - - Ok(shards) -} +use super::load_markdown_shards; enum DisplayMode { Minutes, @@ -350,7 +324,7 @@ pub fn run(decimal: bool, debug: bool) -> Result<(), StreamdError> { let now = Utc::now(); // Load all markdown files and extract timesheets - let all_shards = load_all_shards(base_folder, tz)?; + let all_shards = load_markdown_shards(base_folder, &BasicTimesheetConfiguration, tz)?; let timesheets = extract_timesheets(&all_shards, now, tz)?; // Generate the report diff --git a/src/cli/commands/todo.rs b/src/cli/commands/todo.rs index 17bfba2..9f25289 100644 --- a/src/cli/commands/todo.rs +++ b/src/cli/commands/todo.rs @@ -1,44 +1,24 @@ use std::fs; +use std::path::Path; use std::process::Command; use chrono::Utc; -use walkdir::WalkDir; use crate::config::Settings; use crate::error::StreamdError; -use crate::extract::parse_markdown_file; -use crate::localize::{localize_stream_file, TaskConfiguration}; +use crate::localize::TaskConfiguration; use crate::models::LocalizedShard; use crate::query::find_shard_by_position; -fn all_files() -> Result, StreamdError> { - let settings = Settings::load()?; - let mut shards = Vec::new(); - - for entry in WalkDir::new(&settings.base_folder) - .max_depth(1) - .into_iter() - .filter_map(|e| e.ok()) - { - let path = entry.path(); - if path.extension().map(|e| e == "md").unwrap_or(false) { - let file_name = path.to_string_lossy().to_string(); - let content = fs::read_to_string(path)?; - let stream_file = parse_markdown_file(&file_name, &content); - - if let Ok(shard) = - localize_stream_file(&stream_file, &TaskConfiguration, chrono_tz::UTC) - { - shards.push(shard); - } - } - } - - Ok(shards) -} +use super::load_markdown_shards; pub fn collect_open_tasks(show_future: bool) -> Result, StreamdError> { - let all_shards = all_files()?; + let settings = Settings::load()?; + let all_shards = load_markdown_shards( + Path::new(&settings.base_folder), + &TaskConfiguration, + chrono_tz::UTC, + )?; let now = Utc::now(); let mut tasks: Vec = find_shard_by_position(&all_shards, "task", "open") From 693ffbc764b6c08717c1e539bb138983e51f6549 Mon Sep 17 00:00:00 2001 From: Konstantin Fickel Date: Mon, 13 Apr 2026 19:52:59 +0200 Subject: [PATCH 4/6] chore: release 0.2.3 --- Cargo.lock | 38 +++++++++++++++++++------------------- Cargo.toml | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8f34603..16fcbfe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -135,9 +135,9 @@ checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "cc" -version = "1.2.59" +version = "1.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" +checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" dependencies = [ "find-msvc-tools", "shlex", @@ -197,9 +197,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.6.1" +version = "4.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406e68b4de5c59cfb8f750a7cbd4d31ae153788b8352167c1e5f4fc26e8c91e9" +checksum = "3ff7a1dccbdd8b078c2bdebff47e404615151534d5043da397ec50286816f9cb" dependencies = [ "clap", ] @@ -432,9 +432,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.94" +version = "0.3.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" +checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" dependencies = [ "once_cell", "wasm-bindgen", @@ -448,15 +448,15 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.184" +version = "0.2.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" +checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" [[package]] name = "libredox" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ "libc", ] @@ -787,7 +787,7 @@ checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] name = "streamd" -version = "0.2.2" +version = "0.2.3" dependencies = [ "chrono", "chrono-tz", @@ -1016,9 +1016,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.117" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" +checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" dependencies = [ "cfg-if", "once_cell", @@ -1029,9 +1029,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.117" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" +checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1039,9 +1039,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.117" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" +checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" dependencies = [ "bumpalo", "proc-macro2", @@ -1052,9 +1052,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.117" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" +checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" dependencies = [ "unicode-ident", ] diff --git a/Cargo.toml b/Cargo.toml index b4d48cb..c8cbad1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "streamd" -version = "0.2.2" +version = "0.2.3" edition = "2021" description = "Personal knowledge management and time-tracking CLI using @Tag annotations" license = "AGPL-3.0-only" From a5d143758a0d57224e917b287951cf55706e740e Mon Sep 17 00:00:00 2001 From: Konstantin Fickel Date: Tue, 7 Apr 2026 13:49:12 +0200 Subject: [PATCH 5/6] fix: cross-platform compatibility for Windows support - Use directories::BaseDirs for config path fallback instead of hardcoded Unix path - Default to notepad on Windows instead of vi for editor commands - Skip +N line argument for notepad in todo edit (notepad doesn't support it) --- src/cli/commands/edit.rs | 8 +++++++- src/cli/commands/new.rs | 8 +++++++- src/cli/commands/todo.rs | 18 ++++++++++++------ src/config.rs | 4 +++- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/cli/commands/edit.rs b/src/cli/commands/edit.rs index cfc28c1..a5feeee 100644 --- a/src/cli/commands/edit.rs +++ b/src/cli/commands/edit.rs @@ -44,7 +44,13 @@ pub fn run(number: i32) -> Result<(), StreamdError> { }; if let Some(file_path) = sorted_shards[selected_index].location.get("file") { - let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()); + let editor = std::env::var("EDITOR").unwrap_or_else(|_| { + if cfg!(windows) { + "notepad".to_string() + } else { + "vi".to_string() + } + }); Command::new(&editor).arg(file_path).status()?; } diff --git a/src/cli/commands/new.rs b/src/cli/commands/new.rs index de7d351..5c3eecb 100644 --- a/src/cli/commands/new.rs +++ b/src/cli/commands/new.rs @@ -24,7 +24,13 @@ pub fn run() -> Result<(), StreamdError> { drop(file); // Open in editor - let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()); + let editor = std::env::var("EDITOR").unwrap_or_else(|_| { + if cfg!(windows) { + "notepad".to_string() + } else { + "vi".to_string() + } + }); let status = Command::new(&editor).arg(&preliminary_path).status()?; if !status.success() { diff --git a/src/cli/commands/todo.rs b/src/cli/commands/todo.rs index 9f25289..66176cf 100644 --- a/src/cli/commands/todo.rs +++ b/src/cli/commands/todo.rs @@ -72,13 +72,19 @@ pub fn run_edit(number: usize) -> Result<(), StreamdError> { .get("file") .ok_or(StreamdError::MissingFilePath)?; - let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()); - let line_arg = format!("+{}", task.start_line); + let editor = std::env::var("EDITOR").unwrap_or_else(|_| { + if cfg!(windows) { + "notepad".to_string() + } else { + "vi".to_string() + } + }); - let status = Command::new(&editor) - .arg(&line_arg) - .arg(file_path) - .status()?; + let mut cmd = Command::new(&editor); + if !editor.to_lowercase().contains("notepad") { + cmd.arg(format!("+{}", task.start_line)); + } + let status = cmd.arg(file_path).status()?; if !status.success() { return Err(StreamdError::IoError(std::io::Error::other( diff --git a/src/config.rs b/src/config.rs index c828ba9..417e8f0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -37,8 +37,10 @@ impl Settings { fn config_path() -> PathBuf { if let Some(proj_dirs) = ProjectDirs::from("", "", "streamd") { proj_dirs.config_dir().join("config.toml") + } else if let Some(base_dirs) = directories::BaseDirs::new() { + base_dirs.config_dir().join("streamd").join("config.toml") } else { - PathBuf::from("~/.config/streamd/config.toml") + PathBuf::from("streamd_config.toml") } } } From 3745b887307f36e89e81bebaf60704c6f011d5b4 Mon Sep 17 00:00:00 2001 From: Konstantin Fickel Date: Tue, 7 Apr 2026 13:49:45 +0200 Subject: [PATCH 6/6] feat: add Windows cross-compilation and release artifacts - Add mkWindowsCraneLib using x86_64-pc-windows-gnu target - Add mkStreamdWindows using mingw-w64 toolchain for cross-compilation - Export streamd-windows package from flake - Add Windows build step and .exe artifact to release workflow --- .forgejo/workflows/release.yml | 5 +++++ flake.nix | 35 +++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index 7c93504..bef91f5 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -49,12 +49,17 @@ jobs: if: steps.version.outputs.SKIP != 'true' run: nix build .#streamd-musl -o result-musl + - name: Build Windows binary + if: steps.version.outputs.SKIP != 'true' + run: nix build .#streamd-windows -o result-windows + - name: Prepare release artifacts if: steps.version.outputs.SKIP != 'true' run: | mkdir -p release cp result-deb release/streamd_${{ steps.version.outputs.VERSION }}_amd64.deb cp result-musl/bin/streamd release/streamd-${{ steps.version.outputs.VERSION }}-linux-x86_64 + cp result-windows/bin/streamd.exe release/streamd-${{ steps.version.outputs.VERSION }}-windows-x86_64.exe - name: Create release if: steps.version.outputs.SKIP != 'true' diff --git a/flake.nix b/flake.nix index 38723ae..12cdf69 100644 --- a/flake.nix +++ b/flake.nix @@ -132,6 +132,38 @@ in craneLib.buildPackage (commonArgs // { inherit cargoArtifacts; }); + mkWindowsCraneLib = + system: + let + pkgs = mkPkgs system; + toolchain = pkgs.rust-bin.stable.latest.default.override { + targets = [ "x86_64-pc-windows-gnu" ]; + }; + in + (crane.mkLib pkgs).overrideToolchain toolchain; + + mkStreamdWindows = + system: + let + pkgs = mkPkgs system; + pkgsCross = pkgs.pkgsCross.mingwW64; + craneLib = mkWindowsCraneLib system; + commonArgs = { + src = craneLib.path ./.; + pname = "streamd"; + inherit version; + strictDeps = true; + CARGO_BUILD_TARGET = "x86_64-pc-windows-gnu"; + CC_x86_64_pc_windows_gnu = "${pkgsCross.stdenv.cc}/bin/x86_64-w64-mingw32-gcc"; + CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER = "${pkgsCross.stdenv.cc}/bin/x86_64-w64-mingw32-gcc"; + nativeBuildInputs = [ pkgsCross.stdenv.cc ]; + buildInputs = [ pkgsCross.windows.pthreads ]; + doCheck = false; + }; + cargoArtifacts = craneLib.buildDepsOnly commonArgs; + in + craneLib.buildPackage (commonArgs // { inherit cargoArtifacts; }); + mkStreamdDeb = system: let @@ -181,9 +213,10 @@ streamd = mkStreamd system; streamd-musl = mkStreamdMusl system; streamd-deb = mkStreamdDeb system; + streamd-windows = mkStreamdWindows system; in { - inherit streamd streamd-musl streamd-deb; + inherit streamd streamd-musl streamd-deb streamd-windows; default = streamd; } );