=== fossil info ===
status: ok
stdout:
project-name: lazyfossil
repository: /home/geraldo/museum/lazyfossil.fossil
local-root: /home/geraldo/Documents/pi_lazyfossil/
config-db: /home/geraldo/.config/fossil.db
project-code: 3c93e1c05dd9744de1e9512b949b2d45d8052f60
checkout: 8d6055058f6ef79dfca656750fe67af903b7509f 2026-06-06 19:09:44 UTC
parent: e92ba54778766d5e92c24035865991ed568d468e 2026-06-06 19:08:06 UTC
tags: trunk
comment: Fix screenshot path; Add commit screenshot (user: geraldo)
check-ins: 32
=== fossil status ===
status: ok
stdout:
repository: /home/geraldo/museum/lazyfossil.fossil
local-root: /home/geraldo/Documents/pi_lazyfossil/
config-db: /home/geraldo/.config/fossil.db
checkout: 8d6055058f6ef79dfca656750fe67af903b7509f 2026-06-06 19:09:44 UTC
parent: e92ba54778766d5e92c24035865991ed568d468e 2026-06-06 19:08:06 UTC
tags: trunk
comment: Fix screenshot path; Add commit screenshot (user: geraldo)
EDITED Cargo.lock
EDITED Cargo.toml
EDITED Makefile
EDITED README.md
EDITED src/app.rs
EDITED src/fossil.rs
EDITED src/ui.rs
stderr:
setting ignore-glob has both versioned and non-versioned values: using versioned value from file "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob" (to silence this warning, either create an empty file named "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob.no-warn" in the check-out root, or delete the non-versioned setting with "fossil unset ignore-glob")
=== fossil extras --dotfiles ===
status: ok
stdout:
.pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md
doc/images/lazyfossil_logo_2_high_res.png
stderr:
setting ignore-glob has both versioned and non-versioned values: using versioned value from file "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob" (to silence this warning, either create an empty file named "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob.no-warn" in the check-out root, or delete the non-versioned setting with "fossil unset ignore-glob")
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
40105961ed|geraldo|2026-06-06 08:02:23|Consolidate plans into master plan and archive older drafts
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
bab3996240|geraldo|2026-06-06 05:02:24|Add publish github release workflow
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
1ac6b345e4|geraldo|2026-06-05 20:05:30|Fix github workflow binary path
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
de8e50f7b0|geraldo|2026-06-05 18:25:33|Add github workflow
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
d82a046b8e|geraldo|2026-06-05 18:08:41|Mirror fossil repository to github
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/app.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/app.rs ===
status: ok
stdout:
Index: src/app.rs
==================================================================
@@ -4,13 +4,15 @@
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+use std::env;
use std::fs;
use std::io;
use std::path::Path;
+use std::process::Command;
const ASCII_LOGO: &str = include_str!("../doc/images/lazyfossil_logo_01.txt");
use std::time::Duration;
pub fn run() -> Result<()> {
@@ -158,10 +160,50 @@
}
fn current_file_path(&self) -> Option<String> {
self.state.repo.as_ref()?.files.get(self.state.repo.as_ref()?.selected_file).map(|f| f.path.clone())
}
+
+ fn open_in_editor(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(editor) = env::var("EDITOR").ok() else {
+ self.state.error = Some("EDITOR is not defined".to_string());
+ return;
+ };
+ if let Err(err) = self.spawn_external(&editor, &[path.as_str()]) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn discard_current_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ match self.client.discard_file(&path) {
+ Ok(_) => self.refresh(),
+ Err(err) => self.state.error = Some(err.to_string()),
+ }
+ }
+
+ fn open_selected_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(cmd) = open_command_for(&path) else {
+ self.state.error = Some("No app configured for this file type".to_string());
+ return;
+ };
+ let args = if cmd == "xdg-open" { vec![path.as_str()] } else { vec![path.as_str()] };
+ if let Err(err) = self.spawn_external(&cmd, &args) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn spawn_external(&self, program: &str, args: &[&str]) -> std::result::Result<(), String> {
+ disable_raw_mode().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
+ let status = Command::new(program).args(args).status().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture);
+ enable_raw_mode().map_err(|e| e.to_string())?;
+ if status.success() { Ok(()) } else { Err(format!("{} exited with {}", program, status)) }
+ }
fn toggle_selected_file(&mut self) {
let Some(path) = self.current_file_path() else { return; };
if let Some(pos) = self.state.selected_files.iter().position(|p| p == &path) {
self.state.selected_files.remove(pos);
@@ -306,10 +348,13 @@
KeyCode::Char(' ') => self.toggle_selected_file(),
KeyCode::Char('c') => self.start_commit(CommitTarget::Selected),
KeyCode::Char('f') => self.start_commit(CommitTarget::Current),
KeyCode::Char('a') => self.start_commit(CommitTarget::All),
KeyCode::Char('i') => self.start_ignore(),
+ KeyCode::Char('e') => self.open_in_editor(),
+ KeyCode::Char('d') => self.discard_current_file(),
+ KeyCode::Char('o') => self.open_selected_file(),
KeyCode::Tab => {
self.state.tab = match self.state.tab { Tab::WorkingTree => Tab::History, Tab::History => Tab::WorkingTree };
self.refresh_history();
}
_ => {}
@@ -331,10 +376,21 @@
fn is_binary_path(path: &str) -> bool {
let lower = path.to_ascii_lowercase();
[".png", ".jpg", ".jpeg", ".gif", ".ico"].iter().any(|ext| lower.ends_with(ext))
}
+
+fn open_command_for(path: &str) -> Option<String> {
+ let ext = Path::new(path).extension()?.to_str()?.to_ascii_lowercase();
+ let cmd = match ext.as_str() {
+ "txt" | "md" | "rs" | "toml" | "log" => env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()),
+ "png" | "jpg" | "jpeg" | "gif" | "ico" => "xdg-open".to_string(),
+ "pdf" => "xdg-open".to_string(),
+ _ => "xdg-open".to_string(),
+ };
+ Some(cmd)
+}
#[cfg(test)]
mod tests {
use super::*;
use crate::fossil::FileStatus;
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/fossil.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/fossil.rs ===
status: ok
stdout:
Index: src/fossil.rs
==================================================================
@@ -100,10 +100,14 @@
pub fn ignore_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
update_ignore_file(pattern).map_err(|e| FossilError::CommandFailed(e.to_string()))?;
Ok(format!("ignored {}", pattern))
}
+
+ pub fn discard_file(&self, path: &str) -> std::result::Result<String, FossilError> {
+ self.run(&["revert", "--", path])
+ }
pub fn set_binary_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
self.run(&["settings", "binary-glob", pattern])
}
@@ -200,10 +204,12 @@
line.strip_prefix("EDITED ")
.map(|path| FileStatus { path: path.trim().to_string(), status: "edited".to_string() })
.or_else(|| line.strip_prefix("ADDED ").map(|path| FileStatus { path: path.trim().to_string(), status: "added".to_string() }))
.or_else(|| line.strip_prefix("DELETED ").map(|path| FileStatus { path: path.trim().to_string(), status: "deleted".to_string() }))
.or_else(|| line.strip_prefix("CHECKED-OUT ").map(|path| FileStatus { path: path.trim().to_string(), status: "checked-out".to_string() }))
+ .or_else(|| line.strip_prefix("CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
+ .or_else(|| line.strip_prefix("MERGE-CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
})
.collect()
}
fn parse_extra(out: &str) -> Vec<FileStatus> {
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/app.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/app.rs ===
status: ok
stdout:
Index: src/app.rs
==================================================================
@@ -4,13 +4,15 @@
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+use std::env;
use std::fs;
use std::io;
use std::path::Path;
+use std::process::Command;
const ASCII_LOGO: &str = include_str!("../doc/images/lazyfossil_logo_01.txt");
use std::time::Duration;
pub fn run() -> Result<()> {
@@ -158,10 +160,50 @@
}
fn current_file_path(&self) -> Option<String> {
self.state.repo.as_ref()?.files.get(self.state.repo.as_ref()?.selected_file).map(|f| f.path.clone())
}
+
+ fn open_in_editor(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(editor) = env::var("EDITOR").ok() else {
+ self.state.error = Some("EDITOR is not defined".to_string());
+ return;
+ };
+ if let Err(err) = self.spawn_external(&editor, &[path.as_str()]) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn discard_current_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ match self.client.discard_file(&path) {
+ Ok(_) => self.refresh(),
+ Err(err) => self.state.error = Some(err.to_string()),
+ }
+ }
+
+ fn open_selected_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(cmd) = open_command_for(&path) else {
+ self.state.error = Some("No app configured for this file type".to_string());
+ return;
+ };
+ let args = if cmd == "xdg-open" { vec![path.as_str()] } else { vec![path.as_str()] };
+ if let Err(err) = self.spawn_external(&cmd, &args) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn spawn_external(&self, program: &str, args: &[&str]) -> std::result::Result<(), String> {
+ disable_raw_mode().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
+ let status = Command::new(program).args(args).status().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture);
+ enable_raw_mode().map_err(|e| e.to_string())?;
+ if status.success() { Ok(()) } else { Err(format!("{} exited with {}", program, status)) }
+ }
fn toggle_selected_file(&mut self) {
let Some(path) = self.current_file_path() else { return; };
if let Some(pos) = self.state.selected_files.iter().position(|p| p == &path) {
self.state.selected_files.remove(pos);
@@ -306,10 +348,13 @@
KeyCode::Char(' ') => self.toggle_selected_file(),
KeyCode::Char('c') => self.start_commit(CommitTarget::Selected),
KeyCode::Char('f') => self.start_commit(CommitTarget::Current),
KeyCode::Char('a') => self.start_commit(CommitTarget::All),
KeyCode::Char('i') => self.start_ignore(),
+ KeyCode::Char('e') => self.open_in_editor(),
+ KeyCode::Char('d') => self.discard_current_file(),
+ KeyCode::Char('o') => self.open_selected_file(),
KeyCode::Tab => {
self.state.tab = match self.state.tab { Tab::WorkingTree => Tab::History, Tab::History => Tab::WorkingTree };
self.refresh_history();
}
_ => {}
@@ -331,10 +376,21 @@
fn is_binary_path(path: &str) -> bool {
let lower = path.to_ascii_lowercase();
[".png", ".jpg", ".jpeg", ".gif", ".ico"].iter().any(|ext| lower.ends_with(ext))
}
+
+fn open_command_for(path: &str) -> Option<String> {
+ let ext = Path::new(path).extension()?.to_str()?.to_ascii_lowercase();
+ let cmd = match ext.as_str() {
+ "txt" | "md" | "rs" | "toml" | "log" => env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()),
+ "png" | "jpg" | "jpeg" | "gif" | "ico" => "xdg-open".to_string(),
+ "pdf" => "xdg-open".to_string(),
+ _ => "xdg-open".to_string(),
+ };
+ Some(cmd)
+}
#[cfg(test)]
mod tests {
use super::*;
use crate::fossil::FileStatus;
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil info ===
status: ok
stdout:
project-name: lazyfossil
repository: /home/geraldo/museum/lazyfossil.fossil
local-root: /home/geraldo/Documents/pi_lazyfossil/
config-db: /home/geraldo/.config/fossil.db
project-code: 3c93e1c05dd9744de1e9512b949b2d45d8052f60
checkout: 8d6055058f6ef79dfca656750fe67af903b7509f 2026-06-06 19:09:44 UTC
parent: e92ba54778766d5e92c24035865991ed568d468e 2026-06-06 19:08:06 UTC
tags: trunk
comment: Fix screenshot path; Add commit screenshot (user: geraldo)
check-ins: 32
=== fossil status ===
status: ok
stdout:
repository: /home/geraldo/museum/lazyfossil.fossil
local-root: /home/geraldo/Documents/pi_lazyfossil/
config-db: /home/geraldo/.config/fossil.db
checkout: 8d6055058f6ef79dfca656750fe67af903b7509f 2026-06-06 19:09:44 UTC
parent: e92ba54778766d5e92c24035865991ed568d468e 2026-06-06 19:08:06 UTC
tags: trunk
comment: Fix screenshot path; Add commit screenshot (user: geraldo)
EDITED Cargo.lock
EDITED Cargo.toml
EDITED Makefile
EDITED README.md
EDITED src/app.rs
EDITED src/fossil.rs
EDITED src/ui.rs
stderr:
setting ignore-glob has both versioned and non-versioned values: using versioned value from file "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob" (to silence this warning, either create an empty file named "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob.no-warn" in the check-out root, or delete the non-versioned setting with "fossil unset ignore-glob")
=== fossil extras --dotfiles ===
status: ok
stdout:
.pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md
doc/images/lazyfossil_logo_2_high_res.png
stderr:
setting ignore-glob has both versioned and non-versioned values: using versioned value from file "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob" (to silence this warning, either create an empty file named "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob.no-warn" in the check-out root, or delete the non-versioned setting with "fossil unset ignore-glob")
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
40105961ed|geraldo|2026-06-06 08:02:23|Consolidate plans into master plan and archive older drafts
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
bab3996240|geraldo|2026-06-06 05:02:24|Add publish github release workflow
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
1ac6b345e4|geraldo|2026-06-05 20:05:30|Fix github workflow binary path
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
de8e50f7b0|geraldo|2026-06-05 18:25:33|Add github workflow
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
d82a046b8e|geraldo|2026-06-05 18:08:41|Mirror fossil repository to github
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/app.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/app.rs ===
status: ok
stdout:
Index: src/app.rs
==================================================================
@@ -4,13 +4,15 @@
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+use std::env;
use std::fs;
use std::io;
use std::path::Path;
+use std::process::Command;
const ASCII_LOGO: &str = include_str!("../doc/images/lazyfossil_logo_01.txt");
use std::time::Duration;
pub fn run() -> Result<()> {
@@ -158,10 +160,50 @@
}
fn current_file_path(&self) -> Option<String> {
self.state.repo.as_ref()?.files.get(self.state.repo.as_ref()?.selected_file).map(|f| f.path.clone())
}
+
+ fn open_in_editor(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(editor) = env::var("EDITOR").ok() else {
+ self.state.error = Some("EDITOR is not defined".to_string());
+ return;
+ };
+ if let Err(err) = self.spawn_external(&editor, &[path.as_str()]) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn discard_current_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ match self.client.discard_file(&path) {
+ Ok(_) => self.refresh(),
+ Err(err) => self.state.error = Some(err.to_string()),
+ }
+ }
+
+ fn open_selected_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(cmd) = open_command_for(&path) else {
+ self.state.error = Some("No app configured for this file type".to_string());
+ return;
+ };
+ let args = if cmd == "xdg-open" { vec![path.as_str()] } else { vec![path.as_str()] };
+ if let Err(err) = self.spawn_external(&cmd, &args) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn spawn_external(&self, program: &str, args: &[&str]) -> std::result::Result<(), String> {
+ disable_raw_mode().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
+ let status = Command::new(program).args(args).status().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture);
+ enable_raw_mode().map_err(|e| e.to_string())?;
+ if status.success() { Ok(()) } else { Err(format!("{} exited with {}", program, status)) }
+ }
fn toggle_selected_file(&mut self) {
let Some(path) = self.current_file_path() else { return; };
if let Some(pos) = self.state.selected_files.iter().position(|p| p == &path) {
self.state.selected_files.remove(pos);
@@ -306,10 +348,13 @@
KeyCode::Char(' ') => self.toggle_selected_file(),
KeyCode::Char('c') => self.start_commit(CommitTarget::Selected),
KeyCode::Char('f') => self.start_commit(CommitTarget::Current),
KeyCode::Char('a') => self.start_commit(CommitTarget::All),
KeyCode::Char('i') => self.start_ignore(),
+ KeyCode::Char('e') => self.open_in_editor(),
+ KeyCode::Char('d') => self.discard_current_file(),
+ KeyCode::Char('o') => self.open_selected_file(),
KeyCode::Tab => {
self.state.tab = match self.state.tab { Tab::WorkingTree => Tab::History, Tab::History => Tab::WorkingTree };
self.refresh_history();
}
_ => {}
@@ -331,10 +376,21 @@
fn is_binary_path(path: &str) -> bool {
let lower = path.to_ascii_lowercase();
[".png", ".jpg", ".jpeg", ".gif", ".ico"].iter().any(|ext| lower.ends_with(ext))
}
+
+fn open_command_for(path: &str) -> Option<String> {
+ let ext = Path::new(path).extension()?.to_str()?.to_ascii_lowercase();
+ let cmd = match ext.as_str() {
+ "txt" | "md" | "rs" | "toml" | "log" => env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()),
+ "png" | "jpg" | "jpeg" | "gif" | "ico" => "xdg-open".to_string(),
+ "pdf" => "xdg-open".to_string(),
+ _ => "xdg-open".to_string(),
+ };
+ Some(cmd)
+}
#[cfg(test)]
mod tests {
use super::*;
use crate::fossil::FileStatus;
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/app.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/app.rs ===
status: ok
stdout:
Index: src/app.rs
==================================================================
@@ -4,13 +4,15 @@
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+use std::env;
use std::fs;
use std::io;
use std::path::Path;
+use std::process::Command;
const ASCII_LOGO: &str = include_str!("../doc/images/lazyfossil_logo_01.txt");
use std::time::Duration;
pub fn run() -> Result<()> {
@@ -158,10 +160,50 @@
}
fn current_file_path(&self) -> Option<String> {
self.state.repo.as_ref()?.files.get(self.state.repo.as_ref()?.selected_file).map(|f| f.path.clone())
}
+
+ fn open_in_editor(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(editor) = env::var("EDITOR").ok() else {
+ self.state.error = Some("EDITOR is not defined".to_string());
+ return;
+ };
+ if let Err(err) = self.spawn_external(&editor, &[path.as_str()]) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn discard_current_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ match self.client.discard_file(&path) {
+ Ok(_) => self.refresh(),
+ Err(err) => self.state.error = Some(err.to_string()),
+ }
+ }
+
+ fn open_selected_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(cmd) = open_command_for(&path) else {
+ self.state.error = Some("No app configured for this file type".to_string());
+ return;
+ };
+ let args = if cmd == "xdg-open" { vec![path.as_str()] } else { vec![path.as_str()] };
+ if let Err(err) = self.spawn_external(&cmd, &args) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn spawn_external(&self, program: &str, args: &[&str]) -> std::result::Result<(), String> {
+ disable_raw_mode().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
+ let status = Command::new(program).args(args).status().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture);
+ enable_raw_mode().map_err(|e| e.to_string())?;
+ if status.success() { Ok(()) } else { Err(format!("{} exited with {}", program, status)) }
+ }
fn toggle_selected_file(&mut self) {
let Some(path) = self.current_file_path() else { return; };
if let Some(pos) = self.state.selected_files.iter().position(|p| p == &path) {
self.state.selected_files.remove(pos);
@@ -306,10 +348,13 @@
KeyCode::Char(' ') => self.toggle_selected_file(),
KeyCode::Char('c') => self.start_commit(CommitTarget::Selected),
KeyCode::Char('f') => self.start_commit(CommitTarget::Current),
KeyCode::Char('a') => self.start_commit(CommitTarget::All),
KeyCode::Char('i') => self.start_ignore(),
+ KeyCode::Char('e') => self.open_in_editor(),
+ KeyCode::Char('d') => self.discard_current_file(),
+ KeyCode::Char('o') => self.open_selected_file(),
KeyCode::Tab => {
self.state.tab = match self.state.tab { Tab::WorkingTree => Tab::History, Tab::History => Tab::WorkingTree };
self.refresh_history();
}
_ => {}
@@ -331,10 +376,21 @@
fn is_binary_path(path: &str) -> bool {
let lower = path.to_ascii_lowercase();
[".png", ".jpg", ".jpeg", ".gif", ".ico"].iter().any(|ext| lower.ends_with(ext))
}
+
+fn open_command_for(path: &str) -> Option<String> {
+ let ext = Path::new(path).extension()?.to_str()?.to_ascii_lowercase();
+ let cmd = match ext.as_str() {
+ "txt" | "md" | "rs" | "toml" | "log" => env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()),
+ "png" | "jpg" | "jpeg" | "gif" | "ico" => "xdg-open".to_string(),
+ "pdf" => "xdg-open".to_string(),
+ _ => "xdg-open".to_string(),
+ };
+ Some(cmd)
+}
#[cfg(test)]
mod tests {
use super::*;
use crate::fossil::FileStatus;
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/app.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/app.rs ===
status: ok
stdout:
Index: src/app.rs
==================================================================
@@ -4,13 +4,15 @@
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+use std::env;
use std::fs;
use std::io;
use std::path::Path;
+use std::process::Command;
const ASCII_LOGO: &str = include_str!("../doc/images/lazyfossil_logo_01.txt");
use std::time::Duration;
pub fn run() -> Result<()> {
@@ -158,10 +160,50 @@
}
fn current_file_path(&self) -> Option<String> {
self.state.repo.as_ref()?.files.get(self.state.repo.as_ref()?.selected_file).map(|f| f.path.clone())
}
+
+ fn open_in_editor(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(editor) = env::var("EDITOR").ok() else {
+ self.state.error = Some("EDITOR is not defined".to_string());
+ return;
+ };
+ if let Err(err) = self.spawn_external(&editor, &[path.as_str()]) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn discard_current_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ match self.client.discard_file(&path) {
+ Ok(_) => self.refresh(),
+ Err(err) => self.state.error = Some(err.to_string()),
+ }
+ }
+
+ fn open_selected_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(cmd) = open_command_for(&path) else {
+ self.state.error = Some("No app configured for this file type".to_string());
+ return;
+ };
+ let args = if cmd == "xdg-open" { vec![path.as_str()] } else { vec![path.as_str()] };
+ if let Err(err) = self.spawn_external(&cmd, &args) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn spawn_external(&self, program: &str, args: &[&str]) -> std::result::Result<(), String> {
+ disable_raw_mode().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
+ let status = Command::new(program).args(args).status().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture);
+ enable_raw_mode().map_err(|e| e.to_string())?;
+ if status.success() { Ok(()) } else { Err(format!("{} exited with {}", program, status)) }
+ }
fn toggle_selected_file(&mut self) {
let Some(path) = self.current_file_path() else { return; };
if let Some(pos) = self.state.selected_files.iter().position(|p| p == &path) {
self.state.selected_files.remove(pos);
@@ -306,10 +348,13 @@
KeyCode::Char(' ') => self.toggle_selected_file(),
KeyCode::Char('c') => self.start_commit(CommitTarget::Selected),
KeyCode::Char('f') => self.start_commit(CommitTarget::Current),
KeyCode::Char('a') => self.start_commit(CommitTarget::All),
KeyCode::Char('i') => self.start_ignore(),
+ KeyCode::Char('e') => self.open_in_editor(),
+ KeyCode::Char('d') => self.discard_current_file(),
+ KeyCode::Char('o') => self.open_selected_file(),
KeyCode::Tab => {
self.state.tab = match self.state.tab { Tab::WorkingTree => Tab::History, Tab::History => Tab::WorkingTree };
self.refresh_history();
}
_ => {}
@@ -331,10 +376,21 @@
fn is_binary_path(path: &str) -> bool {
let lower = path.to_ascii_lowercase();
[".png", ".jpg", ".jpeg", ".gif", ".ico"].iter().any(|ext| lower.ends_with(ext))
}
+
+fn open_command_for(path: &str) -> Option<String> {
+ let ext = Path::new(path).extension()?.to_str()?.to_ascii_lowercase();
+ let cmd = match ext.as_str() {
+ "txt" | "md" | "rs" | "toml" | "log" => env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()),
+ "png" | "jpg" | "jpeg" | "gif" | "ico" => "xdg-open".to_string(),
+ "pdf" => "xdg-open".to_string(),
+ _ => "xdg-open".to_string(),
+ };
+ Some(cmd)
+}
#[cfg(test)]
mod tests {
use super::*;
use crate::fossil::FileStatus;
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/app.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/app.rs ===
status: ok
stdout:
Index: src/app.rs
==================================================================
@@ -4,13 +4,15 @@
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+use std::env;
use std::fs;
use std::io;
use std::path::Path;
+use std::process::Command;
const ASCII_LOGO: &str = include_str!("../doc/images/lazyfossil_logo_01.txt");
use std::time::Duration;
pub fn run() -> Result<()> {
@@ -158,10 +160,50 @@
}
fn current_file_path(&self) -> Option<String> {
self.state.repo.as_ref()?.files.get(self.state.repo.as_ref()?.selected_file).map(|f| f.path.clone())
}
+
+ fn open_in_editor(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(editor) = env::var("EDITOR").ok() else {
+ self.state.error = Some("EDITOR is not defined".to_string());
+ return;
+ };
+ if let Err(err) = self.spawn_external(&editor, &[path.as_str()]) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn discard_current_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ match self.client.discard_file(&path) {
+ Ok(_) => self.refresh(),
+ Err(err) => self.state.error = Some(err.to_string()),
+ }
+ }
+
+ fn open_selected_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(cmd) = open_command_for(&path) else {
+ self.state.error = Some("No app configured for this file type".to_string());
+ return;
+ };
+ let args = if cmd == "xdg-open" { vec![path.as_str()] } else { vec![path.as_str()] };
+ if let Err(err) = self.spawn_external(&cmd, &args) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn spawn_external(&self, program: &str, args: &[&str]) -> std::result::Result<(), String> {
+ disable_raw_mode().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
+ let status = Command::new(program).args(args).status().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture);
+ enable_raw_mode().map_err(|e| e.to_string())?;
+ if status.success() { Ok(()) } else { Err(format!("{} exited with {}", program, status)) }
+ }
fn toggle_selected_file(&mut self) {
let Some(path) = self.current_file_path() else { return; };
if let Some(pos) = self.state.selected_files.iter().position(|p| p == &path) {
self.state.selected_files.remove(pos);
@@ -306,10 +348,13 @@
KeyCode::Char(' ') => self.toggle_selected_file(),
KeyCode::Char('c') => self.start_commit(CommitTarget::Selected),
KeyCode::Char('f') => self.start_commit(CommitTarget::Current),
KeyCode::Char('a') => self.start_commit(CommitTarget::All),
KeyCode::Char('i') => self.start_ignore(),
+ KeyCode::Char('e') => self.open_in_editor(),
+ KeyCode::Char('d') => self.discard_current_file(),
+ KeyCode::Char('o') => self.open_selected_file(),
KeyCode::Tab => {
self.state.tab = match self.state.tab { Tab::WorkingTree => Tab::History, Tab::History => Tab::WorkingTree };
self.refresh_history();
}
_ => {}
@@ -331,10 +376,21 @@
fn is_binary_path(path: &str) -> bool {
let lower = path.to_ascii_lowercase();
[".png", ".jpg", ".jpeg", ".gif", ".ico"].iter().any(|ext| lower.ends_with(ext))
}
+
+fn open_command_for(path: &str) -> Option<String> {
+ let ext = Path::new(path).extension()?.to_str()?.to_ascii_lowercase();
+ let cmd = match ext.as_str() {
+ "txt" | "md" | "rs" | "toml" | "log" => env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()),
+ "png" | "jpg" | "jpeg" | "gif" | "ico" => "xdg-open".to_string(),
+ "pdf" => "xdg-open".to_string(),
+ _ => "xdg-open".to_string(),
+ };
+ Some(cmd)
+}
#[cfg(test)]
mod tests {
use super::*;
use crate::fossil::FileStatus;
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/fossil.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/fossil.rs ===
status: ok
stdout:
Index: src/fossil.rs
==================================================================
@@ -100,10 +100,14 @@
pub fn ignore_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
update_ignore_file(pattern).map_err(|e| FossilError::CommandFailed(e.to_string()))?;
Ok(format!("ignored {}", pattern))
}
+
+ pub fn discard_file(&self, path: &str) -> std::result::Result<String, FossilError> {
+ self.run(&["revert", "--", path])
+ }
pub fn set_binary_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
self.run(&["settings", "binary-glob", pattern])
}
@@ -200,10 +204,12 @@
line.strip_prefix("EDITED ")
.map(|path| FileStatus { path: path.trim().to_string(), status: "edited".to_string() })
.or_else(|| line.strip_prefix("ADDED ").map(|path| FileStatus { path: path.trim().to_string(), status: "added".to_string() }))
.or_else(|| line.strip_prefix("DELETED ").map(|path| FileStatus { path: path.trim().to_string(), status: "deleted".to_string() }))
.or_else(|| line.strip_prefix("CHECKED-OUT ").map(|path| FileStatus { path: path.trim().to_string(), status: "checked-out".to_string() }))
+ .or_else(|| line.strip_prefix("CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
+ .or_else(|| line.strip_prefix("MERGE-CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
})
.collect()
}
fn parse_extra(out: &str) -> Vec<FileStatus> {
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/ui.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/ui.rs ===
status: ok
stdout:
Index: src/ui.rs
==================================================================
@@ -23,17 +23,23 @@
let mut file_state = ListState::default();
let left = if let Some(repo) = &state.repo {
file_state.select(Some(repo.selected_file));
let items: Vec<ListItem> = repo.files.iter().enumerate().map(|(i, f)| {
- let prefix = if i == repo.selected_file { ">" } else { " " };
let selected = if state.selected_files.iter().any(|p| p == &f.path) { "*" } else { " " };
- let kind = match f.status.as_str() { "extra" => "E", "edited" => "M", "added" => "A", "deleted" => "D", _ => "?" };
- ListItem::new(format!("{}{} {} {}", prefix, selected, kind, f.path))
+ let kind = match f.status.as_str() { "extra" => "??", "edited" => "M", "added" => "A", "deleted" => "D", "conflict" => "C", _ => "?" };
+ let mut item = ListItem::new(format!("{} {}", kind, f.path));
+ if i == repo.selected_file {
+ item = item.style(Style::default().bg(Color::DarkGray));
+ }
+ if selected == "*" {
+ item = item.style(Style::default().add_modifier(Modifier::BOLD));
+ }
+ item
}).collect();
List::new(items)
- .highlight_style(Style::default().add_modifier(Modifier::REVERSED))
+ .highlight_style(Style::default())
.block(Block::default().borders(Borders::ALL).title("Files"))
} else {
List::new(vec![ListItem::new("No repository detected")])
.block(Block::default().borders(Borders::ALL).title("Files"))
};
@@ -101,11 +107,11 @@
cursor = Some((areas[2].x + 1 + 7 + path.chars().count() as u16, areas[2].y + 1));
Paragraph::new(text).block(Block::default().borders(Borders::ALL).title("Ignore"))
} else {
let sel_count = state.selected_files.len();
let base = if let Some(repo) = &state.repo {
- repo.files.get(repo.selected_file).map(|f| format!("q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view | selected: {} [{}]", f.path, f.status)).unwrap_or_else(|| "q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string())
+ repo.files.get(repo.selected_file).map(|f| format!("q quit r refresh p/P sync e edit d discard o open space toggle c commit selected f commit file a commit all i ignore tab switch view | selected: {} [{}]", f.path, f.status)).unwrap_or_else(|| "q quit r refresh p/P sync e edit d discard o open space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string())
} else {
"q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string()
};
Paragraph::new(format!("{}\nselected: {}", base, sel_count)).block(Block::default().borders(Borders::TOP))
};
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/fossil.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/fossil.rs ===
status: ok
stdout:
Index: src/fossil.rs
==================================================================
@@ -100,10 +100,14 @@
pub fn ignore_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
update_ignore_file(pattern).map_err(|e| FossilError::CommandFailed(e.to_string()))?;
Ok(format!("ignored {}", pattern))
}
+
+ pub fn discard_file(&self, path: &str) -> std::result::Result<String, FossilError> {
+ self.run(&["revert", "--", path])
+ }
pub fn set_binary_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
self.run(&["settings", "binary-glob", pattern])
}
@@ -200,10 +204,12 @@
line.strip_prefix("EDITED ")
.map(|path| FileStatus { path: path.trim().to_string(), status: "edited".to_string() })
.or_else(|| line.strip_prefix("ADDED ").map(|path| FileStatus { path: path.trim().to_string(), status: "added".to_string() }))
.or_else(|| line.strip_prefix("DELETED ").map(|path| FileStatus { path: path.trim().to_string(), status: "deleted".to_string() }))
.or_else(|| line.strip_prefix("CHECKED-OUT ").map(|path| FileStatus { path: path.trim().to_string(), status: "checked-out".to_string() }))
+ .or_else(|| line.strip_prefix("CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
+ .or_else(|| line.strip_prefix("MERGE-CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
})
.collect()
}
fn parse_extra(out: &str) -> Vec<FileStatus> {
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/app.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/app.rs ===
status: ok
stdout:
Index: src/app.rs
==================================================================
@@ -4,13 +4,15 @@
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+use std::env;
use std::fs;
use std::io;
use std::path::Path;
+use std::process::Command;
const ASCII_LOGO: &str = include_str!("../doc/images/lazyfossil_logo_01.txt");
use std::time::Duration;
pub fn run() -> Result<()> {
@@ -158,10 +160,50 @@
}
fn current_file_path(&self) -> Option<String> {
self.state.repo.as_ref()?.files.get(self.state.repo.as_ref()?.selected_file).map(|f| f.path.clone())
}
+
+ fn open_in_editor(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(editor) = env::var("EDITOR").ok() else {
+ self.state.error = Some("EDITOR is not defined".to_string());
+ return;
+ };
+ if let Err(err) = self.spawn_external(&editor, &[path.as_str()]) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn discard_current_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ match self.client.discard_file(&path) {
+ Ok(_) => self.refresh(),
+ Err(err) => self.state.error = Some(err.to_string()),
+ }
+ }
+
+ fn open_selected_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(cmd) = open_command_for(&path) else {
+ self.state.error = Some("No app configured for this file type".to_string());
+ return;
+ };
+ let args = if cmd == "xdg-open" { vec![path.as_str()] } else { vec![path.as_str()] };
+ if let Err(err) = self.spawn_external(&cmd, &args) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn spawn_external(&self, program: &str, args: &[&str]) -> std::result::Result<(), String> {
+ disable_raw_mode().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
+ let status = Command::new(program).args(args).status().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture);
+ enable_raw_mode().map_err(|e| e.to_string())?;
+ if status.success() { Ok(()) } else { Err(format!("{} exited with {}", program, status)) }
+ }
fn toggle_selected_file(&mut self) {
let Some(path) = self.current_file_path() else { return; };
if let Some(pos) = self.state.selected_files.iter().position(|p| p == &path) {
self.state.selected_files.remove(pos);
@@ -306,10 +348,13 @@
KeyCode::Char(' ') => self.toggle_selected_file(),
KeyCode::Char('c') => self.start_commit(CommitTarget::Selected),
KeyCode::Char('f') => self.start_commit(CommitTarget::Current),
KeyCode::Char('a') => self.start_commit(CommitTarget::All),
KeyCode::Char('i') => self.start_ignore(),
+ KeyCode::Char('e') => self.open_in_editor(),
+ KeyCode::Char('d') => self.discard_current_file(),
+ KeyCode::Char('o') => self.open_selected_file(),
KeyCode::Tab => {
self.state.tab = match self.state.tab { Tab::WorkingTree => Tab::History, Tab::History => Tab::WorkingTree };
self.refresh_history();
}
_ => {}
@@ -331,10 +376,21 @@
fn is_binary_path(path: &str) -> bool {
let lower = path.to_ascii_lowercase();
[".png", ".jpg", ".jpeg", ".gif", ".ico"].iter().any(|ext| lower.ends_with(ext))
}
+
+fn open_command_for(path: &str) -> Option<String> {
+ let ext = Path::new(path).extension()?.to_str()?.to_ascii_lowercase();
+ let cmd = match ext.as_str() {
+ "txt" | "md" | "rs" | "toml" | "log" => env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()),
+ "png" | "jpg" | "jpeg" | "gif" | "ico" => "xdg-open".to_string(),
+ "pdf" => "xdg-open".to_string(),
+ _ => "xdg-open".to_string(),
+ };
+ Some(cmd)
+}
#[cfg(test)]
mod tests {
use super::*;
use crate::fossil::FileStatus;
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/app.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/app.rs ===
status: ok
stdout:
Index: src/app.rs
==================================================================
@@ -4,13 +4,15 @@
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+use std::env;
use std::fs;
use std::io;
use std::path::Path;
+use std::process::Command;
const ASCII_LOGO: &str = include_str!("../doc/images/lazyfossil_logo_01.txt");
use std::time::Duration;
pub fn run() -> Result<()> {
@@ -158,10 +160,50 @@
}
fn current_file_path(&self) -> Option<String> {
self.state.repo.as_ref()?.files.get(self.state.repo.as_ref()?.selected_file).map(|f| f.path.clone())
}
+
+ fn open_in_editor(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(editor) = env::var("EDITOR").ok() else {
+ self.state.error = Some("EDITOR is not defined".to_string());
+ return;
+ };
+ if let Err(err) = self.spawn_external(&editor, &[path.as_str()]) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn discard_current_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ match self.client.discard_file(&path) {
+ Ok(_) => self.refresh(),
+ Err(err) => self.state.error = Some(err.to_string()),
+ }
+ }
+
+ fn open_selected_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(cmd) = open_command_for(&path) else {
+ self.state.error = Some("No app configured for this file type".to_string());
+ return;
+ };
+ let args = if cmd == "xdg-open" { vec![path.as_str()] } else { vec![path.as_str()] };
+ if let Err(err) = self.spawn_external(&cmd, &args) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn spawn_external(&self, program: &str, args: &[&str]) -> std::result::Result<(), String> {
+ disable_raw_mode().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
+ let status = Command::new(program).args(args).status().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture);
+ enable_raw_mode().map_err(|e| e.to_string())?;
+ if status.success() { Ok(()) } else { Err(format!("{} exited with {}", program, status)) }
+ }
fn toggle_selected_file(&mut self) {
let Some(path) = self.current_file_path() else { return; };
if let Some(pos) = self.state.selected_files.iter().position(|p| p == &path) {
self.state.selected_files.remove(pos);
@@ -306,10 +348,13 @@
KeyCode::Char(' ') => self.toggle_selected_file(),
KeyCode::Char('c') => self.start_commit(CommitTarget::Selected),
KeyCode::Char('f') => self.start_commit(CommitTarget::Current),
KeyCode::Char('a') => self.start_commit(CommitTarget::All),
KeyCode::Char('i') => self.start_ignore(),
+ KeyCode::Char('e') => self.open_in_editor(),
+ KeyCode::Char('d') => self.discard_current_file(),
+ KeyCode::Char('o') => self.open_selected_file(),
KeyCode::Tab => {
self.state.tab = match self.state.tab { Tab::WorkingTree => Tab::History, Tab::History => Tab::WorkingTree };
self.refresh_history();
}
_ => {}
@@ -331,10 +376,21 @@
fn is_binary_path(path: &str) -> bool {
let lower = path.to_ascii_lowercase();
[".png", ".jpg", ".jpeg", ".gif", ".ico"].iter().any(|ext| lower.ends_with(ext))
}
+
+fn open_command_for(path: &str) -> Option<String> {
+ let ext = Path::new(path).extension()?.to_str()?.to_ascii_lowercase();
+ let cmd = match ext.as_str() {
+ "txt" | "md" | "rs" | "toml" | "log" => env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()),
+ "png" | "jpg" | "jpeg" | "gif" | "ico" => "xdg-open".to_string(),
+ "pdf" => "xdg-open".to_string(),
+ _ => "xdg-open".to_string(),
+ };
+ Some(cmd)
+}
#[cfg(test)]
mod tests {
use super::*;
use crate::fossil::FileStatus;
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/fossil.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/fossil.rs ===
status: ok
stdout:
Index: src/fossil.rs
==================================================================
@@ -100,10 +100,14 @@
pub fn ignore_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
update_ignore_file(pattern).map_err(|e| FossilError::CommandFailed(e.to_string()))?;
Ok(format!("ignored {}", pattern))
}
+
+ pub fn discard_file(&self, path: &str) -> std::result::Result<String, FossilError> {
+ self.run(&["revert", "--", path])
+ }
pub fn set_binary_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
self.run(&["settings", "binary-glob", pattern])
}
@@ -200,10 +204,12 @@
line.strip_prefix("EDITED ")
.map(|path| FileStatus { path: path.trim().to_string(), status: "edited".to_string() })
.or_else(|| line.strip_prefix("ADDED ").map(|path| FileStatus { path: path.trim().to_string(), status: "added".to_string() }))
.or_else(|| line.strip_prefix("DELETED ").map(|path| FileStatus { path: path.trim().to_string(), status: "deleted".to_string() }))
.or_else(|| line.strip_prefix("CHECKED-OUT ").map(|path| FileStatus { path: path.trim().to_string(), status: "checked-out".to_string() }))
+ .or_else(|| line.strip_prefix("CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
+ .or_else(|| line.strip_prefix("MERGE-CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
})
.collect()
}
fn parse_extra(out: &str) -> Vec<FileStatus> {
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/app.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/app.rs ===
status: ok
stdout:
Index: src/app.rs
==================================================================
@@ -4,13 +4,15 @@
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+use std::env;
use std::fs;
use std::io;
use std::path::Path;
+use std::process::Command;
const ASCII_LOGO: &str = include_str!("../doc/images/lazyfossil_logo_01.txt");
use std::time::Duration;
pub fn run() -> Result<()> {
@@ -158,10 +160,50 @@
}
fn current_file_path(&self) -> Option<String> {
self.state.repo.as_ref()?.files.get(self.state.repo.as_ref()?.selected_file).map(|f| f.path.clone())
}
+
+ fn open_in_editor(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(editor) = env::var("EDITOR").ok() else {
+ self.state.error = Some("EDITOR is not defined".to_string());
+ return;
+ };
+ if let Err(err) = self.spawn_external(&editor, &[path.as_str()]) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn discard_current_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ match self.client.discard_file(&path) {
+ Ok(_) => self.refresh(),
+ Err(err) => self.state.error = Some(err.to_string()),
+ }
+ }
+
+ fn open_selected_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(cmd) = open_command_for(&path) else {
+ self.state.error = Some("No app configured for this file type".to_string());
+ return;
+ };
+ let args = if cmd == "xdg-open" { vec![path.as_str()] } else { vec![path.as_str()] };
+ if let Err(err) = self.spawn_external(&cmd, &args) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn spawn_external(&self, program: &str, args: &[&str]) -> std::result::Result<(), String> {
+ disable_raw_mode().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
+ let status = Command::new(program).args(args).status().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture);
+ enable_raw_mode().map_err(|e| e.to_string())?;
+ if status.success() { Ok(()) } else { Err(format!("{} exited with {}", program, status)) }
+ }
fn toggle_selected_file(&mut self) {
let Some(path) = self.current_file_path() else { return; };
if let Some(pos) = self.state.selected_files.iter().position(|p| p == &path) {
self.state.selected_files.remove(pos);
@@ -306,10 +348,13 @@
KeyCode::Char(' ') => self.toggle_selected_file(),
KeyCode::Char('c') => self.start_commit(CommitTarget::Selected),
KeyCode::Char('f') => self.start_commit(CommitTarget::Current),
KeyCode::Char('a') => self.start_commit(CommitTarget::All),
KeyCode::Char('i') => self.start_ignore(),
+ KeyCode::Char('e') => self.open_in_editor(),
+ KeyCode::Char('d') => self.discard_current_file(),
+ KeyCode::Char('o') => self.open_selected_file(),
KeyCode::Tab => {
self.state.tab = match self.state.tab { Tab::WorkingTree => Tab::History, Tab::History => Tab::WorkingTree };
self.refresh_history();
}
_ => {}
@@ -331,10 +376,21 @@
fn is_binary_path(path: &str) -> bool {
let lower = path.to_ascii_lowercase();
[".png", ".jpg", ".jpeg", ".gif", ".ico"].iter().any(|ext| lower.ends_with(ext))
}
+
+fn open_command_for(path: &str) -> Option<String> {
+ let ext = Path::new(path).extension()?.to_str()?.to_ascii_lowercase();
+ let cmd = match ext.as_str() {
+ "txt" | "md" | "rs" | "toml" | "log" => env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()),
+ "png" | "jpg" | "jpeg" | "gif" | "ico" => "xdg-open".to_string(),
+ "pdf" => "xdg-open".to_string(),
+ _ => "xdg-open".to_string(),
+ };
+ Some(cmd)
+}
#[cfg(test)]
mod tests {
use super::*;
use crate::fossil::FileStatus;
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/fossil.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/fossil.rs ===
status: ok
stdout:
Index: src/fossil.rs
==================================================================
@@ -100,10 +100,14 @@
pub fn ignore_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
update_ignore_file(pattern).map_err(|e| FossilError::CommandFailed(e.to_string()))?;
Ok(format!("ignored {}", pattern))
}
+
+ pub fn discard_file(&self, path: &str) -> std::result::Result<String, FossilError> {
+ self.run(&["revert", "--", path])
+ }
pub fn set_binary_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
self.run(&["settings", "binary-glob", pattern])
}
@@ -200,10 +204,12 @@
line.strip_prefix("EDITED ")
.map(|path| FileStatus { path: path.trim().to_string(), status: "edited".to_string() })
.or_else(|| line.strip_prefix("ADDED ").map(|path| FileStatus { path: path.trim().to_string(), status: "added".to_string() }))
.or_else(|| line.strip_prefix("DELETED ").map(|path| FileStatus { path: path.trim().to_string(), status: "deleted".to_string() }))
.or_else(|| line.strip_prefix("CHECKED-OUT ").map(|path| FileStatus { path: path.trim().to_string(), status: "checked-out".to_string() }))
+ .or_else(|| line.strip_prefix("CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
+ .or_else(|| line.strip_prefix("MERGE-CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
})
.collect()
}
fn parse_extra(out: &str) -> Vec<FileStatus> {
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/ui.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/ui.rs ===
status: ok
stdout:
Index: src/ui.rs
==================================================================
@@ -23,17 +23,23 @@
let mut file_state = ListState::default();
let left = if let Some(repo) = &state.repo {
file_state.select(Some(repo.selected_file));
let items: Vec<ListItem> = repo.files.iter().enumerate().map(|(i, f)| {
- let prefix = if i == repo.selected_file { ">" } else { " " };
let selected = if state.selected_files.iter().any(|p| p == &f.path) { "*" } else { " " };
- let kind = match f.status.as_str() { "extra" => "E", "edited" => "M", "added" => "A", "deleted" => "D", _ => "?" };
- ListItem::new(format!("{}{} {} {}", prefix, selected, kind, f.path))
+ let kind = match f.status.as_str() { "extra" => "??", "edited" => "M", "added" => "A", "deleted" => "D", "conflict" => "C", _ => "?" };
+ let mut item = ListItem::new(format!("{} {}", kind, f.path));
+ if i == repo.selected_file {
+ item = item.style(Style::default().bg(Color::DarkGray));
+ }
+ if selected == "*" {
+ item = item.style(Style::default().add_modifier(Modifier::BOLD));
+ }
+ item
}).collect();
List::new(items)
- .highlight_style(Style::default().add_modifier(Modifier::REVERSED))
+ .highlight_style(Style::default())
.block(Block::default().borders(Borders::ALL).title("Files"))
} else {
List::new(vec![ListItem::new("No repository detected")])
.block(Block::default().borders(Borders::ALL).title("Files"))
};
@@ -101,11 +107,11 @@
cursor = Some((areas[2].x + 1 + 7 + path.chars().count() as u16, areas[2].y + 1));
Paragraph::new(text).block(Block::default().borders(Borders::ALL).title("Ignore"))
} else {
let sel_count = state.selected_files.len();
let base = if let Some(repo) = &state.repo {
- repo.files.get(repo.selected_file).map(|f| format!("q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view | selected: {} [{}]", f.path, f.status)).unwrap_or_else(|| "q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string())
+ repo.files.get(repo.selected_file).map(|f| format!("q quit r refresh p/P sync e edit d discard o open space toggle c commit selected f commit file a commit all i ignore tab switch view | selected: {} [{}]", f.path, f.status)).unwrap_or_else(|| "q quit r refresh p/P sync e edit d discard o open space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string())
} else {
"q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string()
};
Paragraph::new(format!("{}\nselected: {}", base, sel_count)).block(Block::default().borders(Borders::TOP))
};
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/fossil.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/fossil.rs ===
status: ok
stdout:
Index: src/fossil.rs
==================================================================
@@ -100,10 +100,14 @@
pub fn ignore_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
update_ignore_file(pattern).map_err(|e| FossilError::CommandFailed(e.to_string()))?;
Ok(format!("ignored {}", pattern))
}
+
+ pub fn discard_file(&self, path: &str) -> std::result::Result<String, FossilError> {
+ self.run(&["revert", "--", path])
+ }
pub fn set_binary_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
self.run(&["settings", "binary-glob", pattern])
}
@@ -200,10 +204,12 @@
line.strip_prefix("EDITED ")
.map(|path| FileStatus { path: path.trim().to_string(), status: "edited".to_string() })
.or_else(|| line.strip_prefix("ADDED ").map(|path| FileStatus { path: path.trim().to_string(), status: "added".to_string() }))
.or_else(|| line.strip_prefix("DELETED ").map(|path| FileStatus { path: path.trim().to_string(), status: "deleted".to_string() }))
.or_else(|| line.strip_prefix("CHECKED-OUT ").map(|path| FileStatus { path: path.trim().to_string(), status: "checked-out".to_string() }))
+ .or_else(|| line.strip_prefix("CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
+ .or_else(|| line.strip_prefix("MERGE-CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
})
.collect()
}
fn parse_extra(out: &str) -> Vec<FileStatus> {
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/app.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/app.rs ===
status: ok
stdout:
Index: src/app.rs
==================================================================
@@ -4,13 +4,15 @@
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+use std::env;
use std::fs;
use std::io;
use std::path::Path;
+use std::process::Command;
const ASCII_LOGO: &str = include_str!("../doc/images/lazyfossil_logo_01.txt");
use std::time::Duration;
pub fn run() -> Result<()> {
@@ -158,10 +160,50 @@
}
fn current_file_path(&self) -> Option<String> {
self.state.repo.as_ref()?.files.get(self.state.repo.as_ref()?.selected_file).map(|f| f.path.clone())
}
+
+ fn open_in_editor(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(editor) = env::var("EDITOR").ok() else {
+ self.state.error = Some("EDITOR is not defined".to_string());
+ return;
+ };
+ if let Err(err) = self.spawn_external(&editor, &[path.as_str()]) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn discard_current_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ match self.client.discard_file(&path) {
+ Ok(_) => self.refresh(),
+ Err(err) => self.state.error = Some(err.to_string()),
+ }
+ }
+
+ fn open_selected_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(cmd) = open_command_for(&path) else {
+ self.state.error = Some("No app configured for this file type".to_string());
+ return;
+ };
+ let args = if cmd == "xdg-open" { vec![path.as_str()] } else { vec![path.as_str()] };
+ if let Err(err) = self.spawn_external(&cmd, &args) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn spawn_external(&self, program: &str, args: &[&str]) -> std::result::Result<(), String> {
+ disable_raw_mode().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
+ let status = Command::new(program).args(args).status().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture);
+ enable_raw_mode().map_err(|e| e.to_string())?;
+ if status.success() { Ok(()) } else { Err(format!("{} exited with {}", program, status)) }
+ }
fn toggle_selected_file(&mut self) {
let Some(path) = self.current_file_path() else { return; };
if let Some(pos) = self.state.selected_files.iter().position(|p| p == &path) {
self.state.selected_files.remove(pos);
@@ -306,10 +348,13 @@
KeyCode::Char(' ') => self.toggle_selected_file(),
KeyCode::Char('c') => self.start_commit(CommitTarget::Selected),
KeyCode::Char('f') => self.start_commit(CommitTarget::Current),
KeyCode::Char('a') => self.start_commit(CommitTarget::All),
KeyCode::Char('i') => self.start_ignore(),
+ KeyCode::Char('e') => self.open_in_editor(),
+ KeyCode::Char('d') => self.discard_current_file(),
+ KeyCode::Char('o') => self.open_selected_file(),
KeyCode::Tab => {
self.state.tab = match self.state.tab { Tab::WorkingTree => Tab::History, Tab::History => Tab::WorkingTree };
self.refresh_history();
}
_ => {}
@@ -331,10 +376,21 @@
fn is_binary_path(path: &str) -> bool {
let lower = path.to_ascii_lowercase();
[".png", ".jpg", ".jpeg", ".gif", ".ico"].iter().any(|ext| lower.ends_with(ext))
}
+
+fn open_command_for(path: &str) -> Option<String> {
+ let ext = Path::new(path).extension()?.to_str()?.to_ascii_lowercase();
+ let cmd = match ext.as_str() {
+ "txt" | "md" | "rs" | "toml" | "log" => env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()),
+ "png" | "jpg" | "jpeg" | "gif" | "ico" => "xdg-open".to_string(),
+ "pdf" => "xdg-open".to_string(),
+ _ => "xdg-open".to_string(),
+ };
+ Some(cmd)
+}
#[cfg(test)]
mod tests {
use super::*;
use crate::fossil::FileStatus;
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/app.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/app.rs ===
status: ok
stdout:
Index: src/app.rs
==================================================================
@@ -4,13 +4,15 @@
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+use std::env;
use std::fs;
use std::io;
use std::path::Path;
+use std::process::Command;
const ASCII_LOGO: &str = include_str!("../doc/images/lazyfossil_logo_01.txt");
use std::time::Duration;
pub fn run() -> Result<()> {
@@ -158,10 +160,50 @@
}
fn current_file_path(&self) -> Option<String> {
self.state.repo.as_ref()?.files.get(self.state.repo.as_ref()?.selected_file).map(|f| f.path.clone())
}
+
+ fn open_in_editor(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(editor) = env::var("EDITOR").ok() else {
+ self.state.error = Some("EDITOR is not defined".to_string());
+ return;
+ };
+ if let Err(err) = self.spawn_external(&editor, &[path.as_str()]) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn discard_current_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ match self.client.discard_file(&path) {
+ Ok(_) => self.refresh(),
+ Err(err) => self.state.error = Some(err.to_string()),
+ }
+ }
+
+ fn open_selected_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(cmd) = open_command_for(&path) else {
+ self.state.error = Some("No app configured for this file type".to_string());
+ return;
+ };
+ let args = if cmd == "xdg-open" { vec![path.as_str()] } else { vec![path.as_str()] };
+ if let Err(err) = self.spawn_external(&cmd, &args) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn spawn_external(&self, program: &str, args: &[&str]) -> std::result::Result<(), String> {
+ disable_raw_mode().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
+ let status = Command::new(program).args(args).status().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture);
+ enable_raw_mode().map_err(|e| e.to_string())?;
+ if status.success() { Ok(()) } else { Err(format!("{} exited with {}", program, status)) }
+ }
fn toggle_selected_file(&mut self) {
let Some(path) = self.current_file_path() else { return; };
if let Some(pos) = self.state.selected_files.iter().position(|p| p == &path) {
self.state.selected_files.remove(pos);
@@ -306,10 +348,13 @@
KeyCode::Char(' ') => self.toggle_selected_file(),
KeyCode::Char('c') => self.start_commit(CommitTarget::Selected),
KeyCode::Char('f') => self.start_commit(CommitTarget::Current),
KeyCode::Char('a') => self.start_commit(CommitTarget::All),
KeyCode::Char('i') => self.start_ignore(),
+ KeyCode::Char('e') => self.open_in_editor(),
+ KeyCode::Char('d') => self.discard_current_file(),
+ KeyCode::Char('o') => self.open_selected_file(),
KeyCode::Tab => {
self.state.tab = match self.state.tab { Tab::WorkingTree => Tab::History, Tab::History => Tab::WorkingTree };
self.refresh_history();
}
_ => {}
@@ -331,10 +376,21 @@
fn is_binary_path(path: &str) -> bool {
let lower = path.to_ascii_lowercase();
[".png", ".jpg", ".jpeg", ".gif", ".ico"].iter().any(|ext| lower.ends_with(ext))
}
+
+fn open_command_for(path: &str) -> Option<String> {
+ let ext = Path::new(path).extension()?.to_str()?.to_ascii_lowercase();
+ let cmd = match ext.as_str() {
+ "txt" | "md" | "rs" | "toml" | "log" => env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()),
+ "png" | "jpg" | "jpeg" | "gif" | "ico" => "xdg-open".to_string(),
+ "pdf" => "xdg-open".to_string(),
+ _ => "xdg-open".to_string(),
+ };
+ Some(cmd)
+}
#[cfg(test)]
mod tests {
use super::*;
use crate::fossil::FileStatus;
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/fossil.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/fossil.rs ===
status: ok
stdout:
Index: src/fossil.rs
==================================================================
@@ -100,10 +100,14 @@
pub fn ignore_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
update_ignore_file(pattern).map_err(|e| FossilError::CommandFailed(e.to_string()))?;
Ok(format!("ignored {}", pattern))
}
+
+ pub fn discard_file(&self, path: &str) -> std::result::Result<String, FossilError> {
+ self.run(&["revert", "--", path])
+ }
pub fn set_binary_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
self.run(&["settings", "binary-glob", pattern])
}
@@ -200,10 +204,12 @@
line.strip_prefix("EDITED ")
.map(|path| FileStatus { path: path.trim().to_string(), status: "edited".to_string() })
.or_else(|| line.strip_prefix("ADDED ").map(|path| FileStatus { path: path.trim().to_string(), status: "added".to_string() }))
.or_else(|| line.strip_prefix("DELETED ").map(|path| FileStatus { path: path.trim().to_string(), status: "deleted".to_string() }))
.or_else(|| line.strip_prefix("CHECKED-OUT ").map(|path| FileStatus { path: path.trim().to_string(), status: "checked-out".to_string() }))
+ .or_else(|| line.strip_prefix("CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
+ .or_else(|| line.strip_prefix("MERGE-CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
})
.collect()
}
fn parse_extra(out: &str) -> Vec<FileStatus> {
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/ui.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/ui.rs ===
status: ok
stdout:
Index: src/ui.rs
==================================================================
@@ -23,17 +23,23 @@
let mut file_state = ListState::default();
let left = if let Some(repo) = &state.repo {
file_state.select(Some(repo.selected_file));
let items: Vec<ListItem> = repo.files.iter().enumerate().map(|(i, f)| {
- let prefix = if i == repo.selected_file { ">" } else { " " };
let selected = if state.selected_files.iter().any(|p| p == &f.path) { "*" } else { " " };
- let kind = match f.status.as_str() { "extra" => "E", "edited" => "M", "added" => "A", "deleted" => "D", _ => "?" };
- ListItem::new(format!("{}{} {} {}", prefix, selected, kind, f.path))
+ let kind = match f.status.as_str() { "extra" => "??", "edited" => "M", "added" => "A", "deleted" => "D", "conflict" => "C", _ => "?" };
+ let mut item = ListItem::new(format!("{} {}", kind, f.path));
+ if i == repo.selected_file {
+ item = item.style(Style::default().bg(Color::DarkGray));
+ }
+ if selected == "*" {
+ item = item.style(Style::default().add_modifier(Modifier::BOLD));
+ }
+ item
}).collect();
List::new(items)
- .highlight_style(Style::default().add_modifier(Modifier::REVERSED))
+ .highlight_style(Style::default())
.block(Block::default().borders(Borders::ALL).title("Files"))
} else {
List::new(vec![ListItem::new("No repository detected")])
.block(Block::default().borders(Borders::ALL).title("Files"))
};
@@ -101,11 +107,11 @@
cursor = Some((areas[2].x + 1 + 7 + path.chars().count() as u16, areas[2].y + 1));
Paragraph::new(text).block(Block::default().borders(Borders::ALL).title("Ignore"))
} else {
let sel_count = state.selected_files.len();
let base = if let Some(repo) = &state.repo {
- repo.files.get(repo.selected_file).map(|f| format!("q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view | selected: {} [{}]", f.path, f.status)).unwrap_or_else(|| "q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string())
+ repo.files.get(repo.selected_file).map(|f| format!("q quit r refresh p/P sync e edit d discard o open space toggle c commit selected f commit file a commit all i ignore tab switch view | selected: {} [{}]", f.path, f.status)).unwrap_or_else(|| "q quit r refresh p/P sync e edit d discard o open space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string())
} else {
"q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string()
};
Paragraph::new(format!("{}\nselected: {}", base, sel_count)).block(Block::default().borders(Borders::TOP))
};
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p .pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p doc/images/lazyfossil_logo_2_high_res.png ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p .pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/ui.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/ui.rs ===
status: ok
stdout:
Index: src/ui.rs
==================================================================
@@ -23,17 +23,23 @@
let mut file_state = ListState::default();
let left = if let Some(repo) = &state.repo {
file_state.select(Some(repo.selected_file));
let items: Vec<ListItem> = repo.files.iter().enumerate().map(|(i, f)| {
- let prefix = if i == repo.selected_file { ">" } else { " " };
let selected = if state.selected_files.iter().any(|p| p == &f.path) { "*" } else { " " };
- let kind = match f.status.as_str() { "extra" => "E", "edited" => "M", "added" => "A", "deleted" => "D", _ => "?" };
- ListItem::new(format!("{}{} {} {}", prefix, selected, kind, f.path))
+ let kind = match f.status.as_str() { "extra" => "??", "edited" => "M", "added" => "A", "deleted" => "D", "conflict" => "C", _ => "?" };
+ let mut item = ListItem::new(format!("{} {}", kind, f.path));
+ if i == repo.selected_file {
+ item = item.style(Style::default().bg(Color::DarkGray));
+ }
+ if selected == "*" {
+ item = item.style(Style::default().add_modifier(Modifier::BOLD));
+ }
+ item
}).collect();
List::new(items)
- .highlight_style(Style::default().add_modifier(Modifier::REVERSED))
+ .highlight_style(Style::default())
.block(Block::default().borders(Borders::ALL).title("Files"))
} else {
List::new(vec![ListItem::new("No repository detected")])
.block(Block::default().borders(Borders::ALL).title("Files"))
};
@@ -101,11 +107,11 @@
cursor = Some((areas[2].x + 1 + 7 + path.chars().count() as u16, areas[2].y + 1));
Paragraph::new(text).block(Block::default().borders(Borders::ALL).title("Ignore"))
} else {
let sel_count = state.selected_files.len();
let base = if let Some(repo) = &state.repo {
- repo.files.get(repo.selected_file).map(|f| format!("q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view | selected: {} [{}]", f.path, f.status)).unwrap_or_else(|| "q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string())
+ repo.files.get(repo.selected_file).map(|f| format!("q quit r refresh p/P sync e edit d discard o open space toggle c commit selected f commit file a commit all i ignore tab switch view | selected: {} [{}]", f.path, f.status)).unwrap_or_else(|| "q quit r refresh p/P sync e edit d discard o open space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string())
} else {
"q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string()
};
Paragraph::new(format!("{}\nselected: {}", base, sel_count)).block(Block::default().borders(Borders::TOP))
};
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/fossil.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/fossil.rs ===
status: ok
stdout:
Index: src/fossil.rs
==================================================================
@@ -100,10 +100,14 @@
pub fn ignore_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
update_ignore_file(pattern).map_err(|e| FossilError::CommandFailed(e.to_string()))?;
Ok(format!("ignored {}", pattern))
}
+
+ pub fn discard_file(&self, path: &str) -> std::result::Result<String, FossilError> {
+ self.run(&["revert", "--", path])
+ }
pub fn set_binary_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
self.run(&["settings", "binary-glob", pattern])
}
@@ -200,10 +204,12 @@
line.strip_prefix("EDITED ")
.map(|path| FileStatus { path: path.trim().to_string(), status: "edited".to_string() })
.or_else(|| line.strip_prefix("ADDED ").map(|path| FileStatus { path: path.trim().to_string(), status: "added".to_string() }))
.or_else(|| line.strip_prefix("DELETED ").map(|path| FileStatus { path: path.trim().to_string(), status: "deleted".to_string() }))
.or_else(|| line.strip_prefix("CHECKED-OUT ").map(|path| FileStatus { path: path.trim().to_string(), status: "checked-out".to_string() }))
+ .or_else(|| line.strip_prefix("CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
+ .or_else(|| line.strip_prefix("MERGE-CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
})
.collect()
}
fn parse_extra(out: &str) -> Vec<FileStatus> {
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/app.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/app.rs ===
status: ok
stdout:
Index: src/app.rs
==================================================================
@@ -4,13 +4,15 @@
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+use std::env;
use std::fs;
use std::io;
use std::path::Path;
+use std::process::Command;
const ASCII_LOGO: &str = include_str!("../doc/images/lazyfossil_logo_01.txt");
use std::time::Duration;
pub fn run() -> Result<()> {
@@ -158,10 +160,50 @@
}
fn current_file_path(&self) -> Option<String> {
self.state.repo.as_ref()?.files.get(self.state.repo.as_ref()?.selected_file).map(|f| f.path.clone())
}
+
+ fn open_in_editor(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(editor) = env::var("EDITOR").ok() else {
+ self.state.error = Some("EDITOR is not defined".to_string());
+ return;
+ };
+ if let Err(err) = self.spawn_external(&editor, &[path.as_str()]) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn discard_current_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ match self.client.discard_file(&path) {
+ Ok(_) => self.refresh(),
+ Err(err) => self.state.error = Some(err.to_string()),
+ }
+ }
+
+ fn open_selected_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(cmd) = open_command_for(&path) else {
+ self.state.error = Some("No app configured for this file type".to_string());
+ return;
+ };
+ let args = if cmd == "xdg-open" { vec![path.as_str()] } else { vec![path.as_str()] };
+ if let Err(err) = self.spawn_external(&cmd, &args) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn spawn_external(&self, program: &str, args: &[&str]) -> std::result::Result<(), String> {
+ disable_raw_mode().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
+ let status = Command::new(program).args(args).status().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture);
+ enable_raw_mode().map_err(|e| e.to_string())?;
+ if status.success() { Ok(()) } else { Err(format!("{} exited with {}", program, status)) }
+ }
fn toggle_selected_file(&mut self) {
let Some(path) = self.current_file_path() else { return; };
if let Some(pos) = self.state.selected_files.iter().position(|p| p == &path) {
self.state.selected_files.remove(pos);
@@ -306,10 +348,13 @@
KeyCode::Char(' ') => self.toggle_selected_file(),
KeyCode::Char('c') => self.start_commit(CommitTarget::Selected),
KeyCode::Char('f') => self.start_commit(CommitTarget::Current),
KeyCode::Char('a') => self.start_commit(CommitTarget::All),
KeyCode::Char('i') => self.start_ignore(),
+ KeyCode::Char('e') => self.open_in_editor(),
+ KeyCode::Char('d') => self.discard_current_file(),
+ KeyCode::Char('o') => self.open_selected_file(),
KeyCode::Tab => {
self.state.tab = match self.state.tab { Tab::WorkingTree => Tab::History, Tab::History => Tab::WorkingTree };
self.refresh_history();
}
_ => {}
@@ -331,10 +376,21 @@
fn is_binary_path(path: &str) -> bool {
let lower = path.to_ascii_lowercase();
[".png", ".jpg", ".jpeg", ".gif", ".ico"].iter().any(|ext| lower.ends_with(ext))
}
+
+fn open_command_for(path: &str) -> Option<String> {
+ let ext = Path::new(path).extension()?.to_str()?.to_ascii_lowercase();
+ let cmd = match ext.as_str() {
+ "txt" | "md" | "rs" | "toml" | "log" => env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()),
+ "png" | "jpg" | "jpeg" | "gif" | "ico" => "xdg-open".to_string(),
+ "pdf" => "xdg-open".to_string(),
+ _ => "xdg-open".to_string(),
+ };
+ Some(cmd)
+}
#[cfg(test)]
mod tests {
use super::*;
use crate::fossil::FileStatus;
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil info ===
status: ok
stdout:
project-name: lazyfossil
repository: /home/geraldo/museum/lazyfossil.fossil
local-root: /home/geraldo/Documents/pi_lazyfossil/
config-db: /home/geraldo/.config/fossil.db
project-code: 3c93e1c05dd9744de1e9512b949b2d45d8052f60
checkout: 8d6055058f6ef79dfca656750fe67af903b7509f 2026-06-06 19:09:44 UTC
parent: e92ba54778766d5e92c24035865991ed568d468e 2026-06-06 19:08:06 UTC
tags: trunk
comment: Fix screenshot path; Add commit screenshot (user: geraldo)
check-ins: 32
=== fossil status ===
status: ok
stdout:
repository: /home/geraldo/museum/lazyfossil.fossil
local-root: /home/geraldo/Documents/pi_lazyfossil/
config-db: /home/geraldo/.config/fossil.db
checkout: 8d6055058f6ef79dfca656750fe67af903b7509f 2026-06-06 19:09:44 UTC
parent: e92ba54778766d5e92c24035865991ed568d468e 2026-06-06 19:08:06 UTC
tags: trunk
comment: Fix screenshot path; Add commit screenshot (user: geraldo)
EDITED Cargo.lock
EDITED Cargo.toml
EDITED Makefile
EDITED README.md
EDITED src/app.rs
EDITED src/fossil.rs
EDITED src/ui.rs
stderr:
setting ignore-glob has both versioned and non-versioned values: using versioned value from file "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob" (to silence this warning, either create an empty file named "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob.no-warn" in the check-out root, or delete the non-versioned setting with "fossil unset ignore-glob")
=== fossil extras --dotfiles ===
status: ok
stdout:
.pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md
doc/images/lazyfossil_logo_2_high_res.png
stderr:
setting ignore-glob has both versioned and non-versioned values: using versioned value from file "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob" (to silence this warning, either create an empty file named "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob.no-warn" in the check-out root, or delete the non-versioned setting with "fossil unset ignore-glob")
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
40105961ed|geraldo|2026-06-06 08:02:23|Consolidate plans into master plan and archive older drafts
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
bab3996240|geraldo|2026-06-06 05:02:24|Add publish github release workflow
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
1ac6b345e4|geraldo|2026-06-05 20:05:30|Fix github workflow binary path
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
de8e50f7b0|geraldo|2026-06-05 18:25:33|Add github workflow
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
d82a046b8e|geraldo|2026-06-05 18:08:41|Mirror fossil repository to github
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/app.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/app.rs ===
status: ok
stdout:
Index: src/app.rs
==================================================================
@@ -4,13 +4,15 @@
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+use std::env;
use std::fs;
use std::io;
use std::path::Path;
+use std::process::Command;
const ASCII_LOGO: &str = include_str!("../doc/images/lazyfossil_logo_01.txt");
use std::time::Duration;
pub fn run() -> Result<()> {
@@ -158,10 +160,50 @@
}
fn current_file_path(&self) -> Option<String> {
self.state.repo.as_ref()?.files.get(self.state.repo.as_ref()?.selected_file).map(|f| f.path.clone())
}
+
+ fn open_in_editor(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(editor) = env::var("EDITOR").ok() else {
+ self.state.error = Some("EDITOR is not defined".to_string());
+ return;
+ };
+ if let Err(err) = self.spawn_external(&editor, &[path.as_str()]) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn discard_current_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ match self.client.discard_file(&path) {
+ Ok(_) => self.refresh(),
+ Err(err) => self.state.error = Some(err.to_string()),
+ }
+ }
+
+ fn open_selected_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(cmd) = open_command_for(&path) else {
+ self.state.error = Some("No app configured for this file type".to_string());
+ return;
+ };
+ let args = if cmd == "xdg-open" { vec![path.as_str()] } else { vec![path.as_str()] };
+ if let Err(err) = self.spawn_external(&cmd, &args) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn spawn_external(&self, program: &str, args: &[&str]) -> std::result::Result<(), String> {
+ disable_raw_mode().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
+ let status = Command::new(program).args(args).status().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture);
+ enable_raw_mode().map_err(|e| e.to_string())?;
+ if status.success() { Ok(()) } else { Err(format!("{} exited with {}", program, status)) }
+ }
fn toggle_selected_file(&mut self) {
let Some(path) = self.current_file_path() else { return; };
if let Some(pos) = self.state.selected_files.iter().position(|p| p == &path) {
self.state.selected_files.remove(pos);
@@ -306,10 +348,13 @@
KeyCode::Char(' ') => self.toggle_selected_file(),
KeyCode::Char('c') => self.start_commit(CommitTarget::Selected),
KeyCode::Char('f') => self.start_commit(CommitTarget::Current),
KeyCode::Char('a') => self.start_commit(CommitTarget::All),
KeyCode::Char('i') => self.start_ignore(),
+ KeyCode::Char('e') => self.open_in_editor(),
+ KeyCode::Char('d') => self.discard_current_file(),
+ KeyCode::Char('o') => self.open_selected_file(),
KeyCode::Tab => {
self.state.tab = match self.state.tab { Tab::WorkingTree => Tab::History, Tab::History => Tab::WorkingTree };
self.refresh_history();
}
_ => {}
@@ -331,10 +376,21 @@
fn is_binary_path(path: &str) -> bool {
let lower = path.to_ascii_lowercase();
[".png", ".jpg", ".jpeg", ".gif", ".ico"].iter().any(|ext| lower.ends_with(ext))
}
+
+fn open_command_for(path: &str) -> Option<String> {
+ let ext = Path::new(path).extension()?.to_str()?.to_ascii_lowercase();
+ let cmd = match ext.as_str() {
+ "txt" | "md" | "rs" | "toml" | "log" => env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()),
+ "png" | "jpg" | "jpeg" | "gif" | "ico" => "xdg-open".to_string(),
+ "pdf" => "xdg-open".to_string(),
+ _ => "xdg-open".to_string(),
+ };
+ Some(cmd)
+}
#[cfg(test)]
mod tests {
use super::*;
use crate::fossil::FileStatus;
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/app.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/app.rs ===
status: ok
stdout:
Index: src/app.rs
==================================================================
@@ -4,13 +4,15 @@
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+use std::env;
use std::fs;
use std::io;
use std::path::Path;
+use std::process::Command;
const ASCII_LOGO: &str = include_str!("../doc/images/lazyfossil_logo_01.txt");
use std::time::Duration;
pub fn run() -> Result<()> {
@@ -158,10 +160,50 @@
}
fn current_file_path(&self) -> Option<String> {
self.state.repo.as_ref()?.files.get(self.state.repo.as_ref()?.selected_file).map(|f| f.path.clone())
}
+
+ fn open_in_editor(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(editor) = env::var("EDITOR").ok() else {
+ self.state.error = Some("EDITOR is not defined".to_string());
+ return;
+ };
+ if let Err(err) = self.spawn_external(&editor, &[path.as_str()]) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn discard_current_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ match self.client.discard_file(&path) {
+ Ok(_) => self.refresh(),
+ Err(err) => self.state.error = Some(err.to_string()),
+ }
+ }
+
+ fn open_selected_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(cmd) = open_command_for(&path) else {
+ self.state.error = Some("No app configured for this file type".to_string());
+ return;
+ };
+ let args = if cmd == "xdg-open" { vec![path.as_str()] } else { vec![path.as_str()] };
+ if let Err(err) = self.spawn_external(&cmd, &args) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn spawn_external(&self, program: &str, args: &[&str]) -> std::result::Result<(), String> {
+ disable_raw_mode().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
+ let status = Command::new(program).args(args).status().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture);
+ enable_raw_mode().map_err(|e| e.to_string())?;
+ if status.success() { Ok(()) } else { Err(format!("{} exited with {}", program, status)) }
+ }
fn toggle_selected_file(&mut self) {
let Some(path) = self.current_file_path() else { return; };
if let Some(pos) = self.state.selected_files.iter().position(|p| p == &path) {
self.state.selected_files.remove(pos);
@@ -306,10 +348,13 @@
KeyCode::Char(' ') => self.toggle_selected_file(),
KeyCode::Char('c') => self.start_commit(CommitTarget::Selected),
KeyCode::Char('f') => self.start_commit(CommitTarget::Current),
KeyCode::Char('a') => self.start_commit(CommitTarget::All),
KeyCode::Char('i') => self.start_ignore(),
+ KeyCode::Char('e') => self.open_in_editor(),
+ KeyCode::Char('d') => self.discard_current_file(),
+ KeyCode::Char('o') => self.open_selected_file(),
KeyCode::Tab => {
self.state.tab = match self.state.tab { Tab::WorkingTree => Tab::History, Tab::History => Tab::WorkingTree };
self.refresh_history();
}
_ => {}
@@ -331,10 +376,21 @@
fn is_binary_path(path: &str) -> bool {
let lower = path.to_ascii_lowercase();
[".png", ".jpg", ".jpeg", ".gif", ".ico"].iter().any(|ext| lower.ends_with(ext))
}
+
+fn open_command_for(path: &str) -> Option<String> {
+ let ext = Path::new(path).extension()?.to_str()?.to_ascii_lowercase();
+ let cmd = match ext.as_str() {
+ "txt" | "md" | "rs" | "toml" | "log" => env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()),
+ "png" | "jpg" | "jpeg" | "gif" | "ico" => "xdg-open".to_string(),
+ "pdf" => "xdg-open".to_string(),
+ _ => "xdg-open".to_string(),
+ };
+ Some(cmd)
+}
#[cfg(test)]
mod tests {
use super::*;
use crate::fossil::FileStatus;
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/fossil.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/fossil.rs ===
status: ok
stdout:
Index: src/fossil.rs
==================================================================
@@ -100,10 +100,14 @@
pub fn ignore_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
update_ignore_file(pattern).map_err(|e| FossilError::CommandFailed(e.to_string()))?;
Ok(format!("ignored {}", pattern))
}
+
+ pub fn discard_file(&self, path: &str) -> std::result::Result<String, FossilError> {
+ self.run(&["revert", "--", path])
+ }
pub fn set_binary_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
self.run(&["settings", "binary-glob", pattern])
}
@@ -200,10 +204,12 @@
line.strip_prefix("EDITED ")
.map(|path| FileStatus { path: path.trim().to_string(), status: "edited".to_string() })
.or_else(|| line.strip_prefix("ADDED ").map(|path| FileStatus { path: path.trim().to_string(), status: "added".to_string() }))
.or_else(|| line.strip_prefix("DELETED ").map(|path| FileStatus { path: path.trim().to_string(), status: "deleted".to_string() }))
.or_else(|| line.strip_prefix("CHECKED-OUT ").map(|path| FileStatus { path: path.trim().to_string(), status: "checked-out".to_string() }))
+ .or_else(|| line.strip_prefix("CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
+ .or_else(|| line.strip_prefix("MERGE-CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
})
.collect()
}
fn parse_extra(out: &str) -> Vec<FileStatus> {
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/ui.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/ui.rs ===
status: ok
stdout:
Index: src/ui.rs
==================================================================
@@ -23,17 +23,23 @@
let mut file_state = ListState::default();
let left = if let Some(repo) = &state.repo {
file_state.select(Some(repo.selected_file));
let items: Vec<ListItem> = repo.files.iter().enumerate().map(|(i, f)| {
- let prefix = if i == repo.selected_file { ">" } else { " " };
let selected = if state.selected_files.iter().any(|p| p == &f.path) { "*" } else { " " };
- let kind = match f.status.as_str() { "extra" => "E", "edited" => "M", "added" => "A", "deleted" => "D", _ => "?" };
- ListItem::new(format!("{}{} {} {}", prefix, selected, kind, f.path))
+ let kind = match f.status.as_str() { "extra" => "??", "edited" => "M", "added" => "A", "deleted" => "D", "conflict" => "C", _ => "?" };
+ let mut item = ListItem::new(format!("{} {}", kind, f.path));
+ if i == repo.selected_file {
+ item = item.style(Style::default().bg(Color::DarkGray));
+ }
+ if selected == "*" {
+ item = item.style(Style::default().add_modifier(Modifier::BOLD));
+ }
+ item
}).collect();
List::new(items)
- .highlight_style(Style::default().add_modifier(Modifier::REVERSED))
+ .highlight_style(Style::default())
.block(Block::default().borders(Borders::ALL).title("Files"))
} else {
List::new(vec![ListItem::new("No repository detected")])
.block(Block::default().borders(Borders::ALL).title("Files"))
};
@@ -101,11 +107,11 @@
cursor = Some((areas[2].x + 1 + 7 + path.chars().count() as u16, areas[2].y + 1));
Paragraph::new(text).block(Block::default().borders(Borders::ALL).title("Ignore"))
} else {
let sel_count = state.selected_files.len();
let base = if let Some(repo) = &state.repo {
- repo.files.get(repo.selected_file).map(|f| format!("q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view | selected: {} [{}]", f.path, f.status)).unwrap_or_else(|| "q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string())
+ repo.files.get(repo.selected_file).map(|f| format!("q quit r refresh p/P sync e edit d discard o open space toggle c commit selected f commit file a commit all i ignore tab switch view | selected: {} [{}]", f.path, f.status)).unwrap_or_else(|| "q quit r refresh p/P sync e edit d discard o open space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string())
} else {
"q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string()
};
Paragraph::new(format!("{}\nselected: {}", base, sel_count)).block(Block::default().borders(Borders::TOP))
};
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p .pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/ui.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/ui.rs ===
status: ok
stdout:
Index: src/ui.rs
==================================================================
@@ -23,17 +23,23 @@
let mut file_state = ListState::default();
let left = if let Some(repo) = &state.repo {
file_state.select(Some(repo.selected_file));
let items: Vec<ListItem> = repo.files.iter().enumerate().map(|(i, f)| {
- let prefix = if i == repo.selected_file { ">" } else { " " };
let selected = if state.selected_files.iter().any(|p| p == &f.path) { "*" } else { " " };
- let kind = match f.status.as_str() { "extra" => "E", "edited" => "M", "added" => "A", "deleted" => "D", _ => "?" };
- ListItem::new(format!("{}{} {} {}", prefix, selected, kind, f.path))
+ let kind = match f.status.as_str() { "extra" => "??", "edited" => "M", "added" => "A", "deleted" => "D", "conflict" => "C", _ => "?" };
+ let mut item = ListItem::new(format!("{} {}", kind, f.path));
+ if i == repo.selected_file {
+ item = item.style(Style::default().bg(Color::DarkGray));
+ }
+ if selected == "*" {
+ item = item.style(Style::default().add_modifier(Modifier::BOLD));
+ }
+ item
}).collect();
List::new(items)
- .highlight_style(Style::default().add_modifier(Modifier::REVERSED))
+ .highlight_style(Style::default())
.block(Block::default().borders(Borders::ALL).title("Files"))
} else {
List::new(vec![ListItem::new("No repository detected")])
.block(Block::default().borders(Borders::ALL).title("Files"))
};
@@ -101,11 +107,11 @@
cursor = Some((areas[2].x + 1 + 7 + path.chars().count() as u16, areas[2].y + 1));
Paragraph::new(text).block(Block::default().borders(Borders::ALL).title("Ignore"))
} else {
let sel_count = state.selected_files.len();
let base = if let Some(repo) = &state.repo {
- repo.files.get(repo.selected_file).map(|f| format!("q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view | selected: {} [{}]", f.path, f.status)).unwrap_or_else(|| "q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string())
+ repo.files.get(repo.selected_file).map(|f| format!("q quit r refresh p/P sync e edit d discard o open space toggle c commit selected f commit file a commit all i ignore tab switch view | selected: {} [{}]", f.path, f.status)).unwrap_or_else(|| "q quit r refresh p/P sync e edit d discard o open space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string())
} else {
"q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string()
};
Paragraph::new(format!("{}\nselected: {}", base, sel_count)).block(Block::default().borders(Borders::TOP))
};
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p .pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p doc/images/lazyfossil_logo_2_high_res.png ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p doc/images/lazyfossil_logo_2_high_res.png ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p .pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/ui.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/ui.rs ===
status: ok
stdout:
Index: src/ui.rs
==================================================================
@@ -23,17 +23,23 @@
let mut file_state = ListState::default();
let left = if let Some(repo) = &state.repo {
file_state.select(Some(repo.selected_file));
let items: Vec<ListItem> = repo.files.iter().enumerate().map(|(i, f)| {
- let prefix = if i == repo.selected_file { ">" } else { " " };
let selected = if state.selected_files.iter().any(|p| p == &f.path) { "*" } else { " " };
- let kind = match f.status.as_str() { "extra" => "E", "edited" => "M", "added" => "A", "deleted" => "D", _ => "?" };
- ListItem::new(format!("{}{} {} {}", prefix, selected, kind, f.path))
+ let kind = match f.status.as_str() { "extra" => "??", "edited" => "M", "added" => "A", "deleted" => "D", "conflict" => "C", _ => "?" };
+ let mut item = ListItem::new(format!("{} {}", kind, f.path));
+ if i == repo.selected_file {
+ item = item.style(Style::default().bg(Color::DarkGray));
+ }
+ if selected == "*" {
+ item = item.style(Style::default().add_modifier(Modifier::BOLD));
+ }
+ item
}).collect();
List::new(items)
- .highlight_style(Style::default().add_modifier(Modifier::REVERSED))
+ .highlight_style(Style::default())
.block(Block::default().borders(Borders::ALL).title("Files"))
} else {
List::new(vec![ListItem::new("No repository detected")])
.block(Block::default().borders(Borders::ALL).title("Files"))
};
@@ -101,11 +107,11 @@
cursor = Some((areas[2].x + 1 + 7 + path.chars().count() as u16, areas[2].y + 1));
Paragraph::new(text).block(Block::default().borders(Borders::ALL).title("Ignore"))
} else {
let sel_count = state.selected_files.len();
let base = if let Some(repo) = &state.repo {
- repo.files.get(repo.selected_file).map(|f| format!("q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view | selected: {} [{}]", f.path, f.status)).unwrap_or_else(|| "q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string())
+ repo.files.get(repo.selected_file).map(|f| format!("q quit r refresh p/P sync e edit d discard o open space toggle c commit selected f commit file a commit all i ignore tab switch view | selected: {} [{}]", f.path, f.status)).unwrap_or_else(|| "q quit r refresh p/P sync e edit d discard o open space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string())
} else {
"q quit r refresh p/P sync space toggle c commit selected f commit file a commit all i ignore tab switch view".to_string()
};
Paragraph::new(format!("{}\nselected: {}", base, sel_count)).block(Block::default().borders(Borders::TOP))
};
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/fossil.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/fossil.rs ===
status: ok
stdout:
Index: src/fossil.rs
==================================================================
@@ -100,10 +100,14 @@
pub fn ignore_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
update_ignore_file(pattern).map_err(|e| FossilError::CommandFailed(e.to_string()))?;
Ok(format!("ignored {}", pattern))
}
+
+ pub fn discard_file(&self, path: &str) -> std::result::Result<String, FossilError> {
+ self.run(&["revert", "--", path])
+ }
pub fn set_binary_glob(&self, pattern: &str) -> std::result::Result<String, FossilError> {
self.run(&["settings", "binary-glob", pattern])
}
@@ -200,10 +204,12 @@
line.strip_prefix("EDITED ")
.map(|path| FileStatus { path: path.trim().to_string(), status: "edited".to_string() })
.or_else(|| line.strip_prefix("ADDED ").map(|path| FileStatus { path: path.trim().to_string(), status: "added".to_string() }))
.or_else(|| line.strip_prefix("DELETED ").map(|path| FileStatus { path: path.trim().to_string(), status: "deleted".to_string() }))
.or_else(|| line.strip_prefix("CHECKED-OUT ").map(|path| FileStatus { path: path.trim().to_string(), status: "checked-out".to_string() }))
+ .or_else(|| line.strip_prefix("CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
+ .or_else(|| line.strip_prefix("MERGE-CONFLICT ").map(|path| FileStatus { path: path.trim().to_string(), status: "conflict".to_string() }))
})
.collect()
}
fn parse_extra(out: &str) -> Vec<FileStatus> {
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p src/app.rs ===
status: ok
stdout:
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
05ae23bdc5|geraldo|2026-06-02 19:57:40|Add mouse file selection and diff scrolling
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- src/app.rs ===
status: ok
stdout:
Index: src/app.rs
==================================================================
@@ -4,13 +4,15 @@
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+use std::env;
use std::fs;
use std::io;
use std::path::Path;
+use std::process::Command;
const ASCII_LOGO: &str = include_str!("../doc/images/lazyfossil_logo_01.txt");
use std::time::Duration;
pub fn run() -> Result<()> {
@@ -158,10 +160,50 @@
}
fn current_file_path(&self) -> Option<String> {
self.state.repo.as_ref()?.files.get(self.state.repo.as_ref()?.selected_file).map(|f| f.path.clone())
}
+
+ fn open_in_editor(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(editor) = env::var("EDITOR").ok() else {
+ self.state.error = Some("EDITOR is not defined".to_string());
+ return;
+ };
+ if let Err(err) = self.spawn_external(&editor, &[path.as_str()]) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn discard_current_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ match self.client.discard_file(&path) {
+ Ok(_) => self.refresh(),
+ Err(err) => self.state.error = Some(err.to_string()),
+ }
+ }
+
+ fn open_selected_file(&mut self) {
+ let Some(path) = self.current_file_path() else { return; };
+ let Some(cmd) = open_command_for(&path) else {
+ self.state.error = Some("No app configured for this file type".to_string());
+ return;
+ };
+ let args = if cmd == "xdg-open" { vec![path.as_str()] } else { vec![path.as_str()] };
+ if let Err(err) = self.spawn_external(&cmd, &args) {
+ self.state.error = Some(err);
+ }
+ }
+
+ fn spawn_external(&self, program: &str, args: &[&str]) -> std::result::Result<(), String> {
+ disable_raw_mode().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
+ let status = Command::new(program).args(args).status().map_err(|e| e.to_string())?;
+ let _ = execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture);
+ enable_raw_mode().map_err(|e| e.to_string())?;
+ if status.success() { Ok(()) } else { Err(format!("{} exited with {}", program, status)) }
+ }
fn toggle_selected_file(&mut self) {
let Some(path) = self.current_file_path() else { return; };
if let Some(pos) = self.state.selected_files.iter().position(|p| p == &path) {
self.state.selected_files.remove(pos);
@@ -306,10 +348,13 @@
KeyCode::Char(' ') => self.toggle_selected_file(),
KeyCode::Char('c') => self.start_commit(CommitTarget::Selected),
KeyCode::Char('f') => self.start_commit(CommitTarget::Current),
KeyCode::Char('a') => self.start_commit(CommitTarget::All),
KeyCode::Char('i') => self.start_ignore(),
+ KeyCode::Char('e') => self.open_in_editor(),
+ KeyCode::Char('d') => self.discard_current_file(),
+ KeyCode::Char('o') => self.open_selected_file(),
KeyCode::Tab => {
self.state.tab = match self.state.tab { Tab::WorkingTree => Tab::History, Tab::History => Tab::WorkingTree };
self.refresh_history();
}
_ => {}
@@ -331,10 +376,21 @@
fn is_binary_path(path: &str) -> bool {
let lower = path.to_ascii_lowercase();
[".png", ".jpg", ".jpeg", ".gif", ".ico"].iter().any(|ext| lower.ends_with(ext))
}
+
+fn open_command_for(path: &str) -> Option<String> {
+ let ext = Path::new(path).extension()?.to_str()?.to_ascii_lowercase();
+ let cmd = match ext.as_str() {
+ "txt" | "md" | "rs" | "toml" | "log" => env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()),
+ "png" | "jpg" | "jpeg" | "gif" | "ico" => "xdg-open".to_string(),
+ "pdf" => "xdg-open".to_string(),
+ _ => "xdg-open".to_string(),
+ };
+ Some(cmd)
+}
#[cfg(test)]
mod tests {
use super::*;
use crate::fossil::FileStatus;
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Makefile ===
status: ok
stdout:
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
=== fossil diff -- Makefile ===
status: ok
stdout:
Index: Makefile
==================================================================
@@ -1,3 +1,7 @@
all:
rm -f target/debug/lazyfossil
cargo run
+
+publish:
+ rm -f fossil-debug.log
+ cargo publish
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil info ===
status: ok
stdout:
project-name: lazyfossil
repository: /home/geraldo/museum/lazyfossil.fossil
local-root: /home/geraldo/Documents/pi_lazyfossil/
config-db: /home/geraldo/.config/fossil.db
project-code: 3c93e1c05dd9744de1e9512b949b2d45d8052f60
checkout: 8d6055058f6ef79dfca656750fe67af903b7509f 2026-06-06 19:09:44 UTC
parent: e92ba54778766d5e92c24035865991ed568d468e 2026-06-06 19:08:06 UTC
tags: trunk
comment: Fix screenshot path; Add commit screenshot (user: geraldo)
check-ins: 32
=== fossil status ===
status: ok
stdout:
repository: /home/geraldo/museum/lazyfossil.fossil
local-root: /home/geraldo/Documents/pi_lazyfossil/
config-db: /home/geraldo/.config/fossil.db
checkout: 8d6055058f6ef79dfca656750fe67af903b7509f 2026-06-06 19:09:44 UTC
parent: e92ba54778766d5e92c24035865991ed568d468e 2026-06-06 19:08:06 UTC
tags: trunk
comment: Fix screenshot path; Add commit screenshot (user: geraldo)
stderr:
setting ignore-glob has both versioned and non-versioned values: using versioned value from file "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob" (to silence this warning, either create an empty file named "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob.no-warn" in the check-out root, or delete the non-versioned setting with "fossil unset ignore-glob")
=== fossil extras --dotfiles ===
status: ok
stdout:
.pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md
doc/images/lazyfossil_logo_2_high_res.png
stderr:
setting ignore-glob has both versioned and non-versioned values: using versioned value from file "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob" (to silence this warning, either create an empty file named "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob.no-warn" in the check-out root, or delete the non-versioned setting with "fossil unset ignore-glob")
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
40105961ed|geraldo|2026-06-06 08:02:23|Consolidate plans into master plan and archive older drafts
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
bab3996240|geraldo|2026-06-06 05:02:24|Add publish github release workflow
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
1ac6b345e4|geraldo|2026-06-05 20:05:30|Fix github workflow binary path
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
de8e50f7b0|geraldo|2026-06-05 18:25:33|Add github workflow
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
d82a046b8e|geraldo|2026-06-05 18:08:41|Mirror fossil repository to github
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p .pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p doc/images/lazyfossil_logo_2_high_res.png ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p .pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p doc/images/lazyfossil_logo_2_high_res.png ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p .pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p doc/images/lazyfossil_logo_2_high_res.png ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p .pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p doc/images/lazyfossil_logo_2_high_res.png ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p .pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md ===
status: ok
stdout:
=== fossil info ===
status: ok
stdout:
project-name: lazyfossil
repository: /home/geraldo/museum/lazyfossil.fossil
local-root: /home/geraldo/Documents/pi_lazyfossil/
config-db: /home/geraldo/.config/fossil.db
project-code: 3c93e1c05dd9744de1e9512b949b2d45d8052f60
checkout: 8d6055058f6ef79dfca656750fe67af903b7509f 2026-06-06 19:09:44 UTC
parent: e92ba54778766d5e92c24035865991ed568d468e 2026-06-06 19:08:06 UTC
tags: trunk
comment: Fix screenshot path; Add commit screenshot (user: geraldo)
check-ins: 32
=== fossil status ===
status: ok
stdout:
repository: /home/geraldo/museum/lazyfossil.fossil
local-root: /home/geraldo/Documents/pi_lazyfossil/
config-db: /home/geraldo/.config/fossil.db
checkout: 8d6055058f6ef79dfca656750fe67af903b7509f 2026-06-06 19:09:44 UTC
parent: e92ba54778766d5e92c24035865991ed568d468e 2026-06-06 19:08:06 UTC
tags: trunk
comment: Fix screenshot path; Add commit screenshot (user: geraldo)
stderr:
setting ignore-glob has both versioned and non-versioned values: using versioned value from file "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob" (to silence this warning, either create an empty file named "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob.no-warn" in the check-out root, or delete the non-versioned setting with "fossil unset ignore-glob")
=== fossil extras --dotfiles ===
status: ok
stdout:
.pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md
doc/images/lazyfossil_logo_2_high_res.png
stderr:
setting ignore-glob has both versioned and non-versioned values: using versioned value from file "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob" (to silence this warning, either create an empty file named "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob.no-warn" in the check-out root, or delete the non-versioned setting with "fossil unset ignore-glob")
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
40105961ed|geraldo|2026-06-06 08:02:23|Consolidate plans into master plan and archive older drafts
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
bab3996240|geraldo|2026-06-06 05:02:24|Add publish github release workflow
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
1ac6b345e4|geraldo|2026-06-05 20:05:30|Fix github workflow binary path
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
de8e50f7b0|geraldo|2026-06-05 18:25:33|Add github workflow
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
d82a046b8e|geraldo|2026-06-05 18:08:41|Mirror fossil repository to github
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p .pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p doc/images/lazyfossil_logo_2_high_res.png ===
status: ok
stdout:
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p .pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md ===
status: ok
stdout:
=== fossil info ===
status: ok
stdout:
project-name: lazyfossil
repository: /home/geraldo/museum/lazyfossil.fossil
local-root: /home/geraldo/Documents/pi_lazyfossil/
config-db: /home/geraldo/.config/fossil.db
project-code: 3c93e1c05dd9744de1e9512b949b2d45d8052f60
checkout: 8d6055058f6ef79dfca656750fe67af903b7509f 2026-06-06 19:09:44 UTC
parent: e92ba54778766d5e92c24035865991ed568d468e 2026-06-06 19:08:06 UTC
tags: trunk
comment: Fix screenshot path; Add commit screenshot (user: geraldo)
check-ins: 32
=== fossil status ===
status: ok
stdout:
repository: /home/geraldo/museum/lazyfossil.fossil
local-root: /home/geraldo/Documents/pi_lazyfossil/
config-db: /home/geraldo/.config/fossil.db
checkout: 8d6055058f6ef79dfca656750fe67af903b7509f 2026-06-06 19:09:44 UTC
parent: e92ba54778766d5e92c24035865991ed568d468e 2026-06-06 19:08:06 UTC
tags: trunk
comment: Fix screenshot path; Add commit screenshot (user: geraldo)
EDITED Cargo.lock
EDITED Cargo.toml
EDITED README.md
stderr:
setting ignore-glob has both versioned and non-versioned values: using versioned value from file "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob" (to silence this warning, either create an empty file named "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob.no-warn" in the check-out root, or delete the non-versioned setting with "fossil unset ignore-glob")
=== fossil extras --dotfiles ===
status: ok
stdout:
.pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md
doc/images/lazyfossil_logo_2_high_res.png
stderr:
setting ignore-glob has both versioned and non-versioned values: using versioned value from file "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob" (to silence this warning, either create an empty file named "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob.no-warn" in the check-out root, or delete the non-versioned setting with "fossil unset ignore-glob")
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
40105961ed|geraldo|2026-06-06 08:02:23|Consolidate plans into master plan and archive older drafts
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
bab3996240|geraldo|2026-06-06 05:02:24|Add publish github release workflow
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
1ac6b345e4|geraldo|2026-06-05 20:05:30|Fix github workflow binary path
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
de8e50f7b0|geraldo|2026-06-05 18:25:33|Add github workflow
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
d82a046b8e|geraldo|2026-06-05 18:08:41|Mirror fossil repository to github
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.lock ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
b66b54d85b|geraldo|2026-06-03 20:04:50|Update version in Cargo.lock
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.lock ===
status: ok
stdout:
Index: Cargo.lock
==================================================================
@@ -202,11 +202,11 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
dependencies = [
"anyhow",
"crossterm",
"ratatui",
]
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p Cargo.toml ===
status: ok
stdout:
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0bc11330ff|geraldo|2026-06-04 18:02:47|Add test cases
1094a55d21|geraldo|2026-06-03 20:10:05|Implement commit selection flow and bump version to 0.1.3
f6f5577549|geraldo|2026-06-03 19:48:00|Bump version to 0.1.2 and save current TUI progress
504d232dbd|geraldo|2026-06-03 18:40:24|Preview extra files and bump version to 0.1.1
9847421853|geraldo|2026-06-02 20:15:33|Prepare crates.io metadata and docs
e7057da18b|geraldo|2026-06-02 19:52:08|Fix selected-file diff and footer status line
49410bfd5e|geraldo|2026-06-02 19:18:34|Initial lazyfossil TUI MVP
=== fossil diff -- Cargo.toml ===
status: ok
stdout:
Index: Cargo.toml
==================================================================
@@ -1,8 +1,8 @@
[package]
name = "lazyfossil"
-version = "0.3.3"
+version = "0.3.4"
edition = "2021"
description = "A lazygit-inspired TUI for Fossil SCM"
license = "MIT"
readme = "README.md"
homepage = "https://crates.io/crates/lazyfossil"
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p README.md ===
status: ok
stdout:
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
1e675183b9|geraldo|2026-06-04 18:21:10|Bump version to 0.2.0 and update README for semantic versioning
0b5ee26b73|geraldo|2026-06-03 20:21:03|Update README
daa11aa0c1|geraldo|2026-06-03 20:07:51|Add README
=== fossil diff -- README.md ===
status: ok
stdout:
Index: README.md
==================================================================
@@ -133,11 +133,11 @@
```
## Versioning
This project follows semantic versioning: `MAJOR.MINOR.PATCH`.
-Current version: `0.3.3`.
+Current version: `0.3.4`.
## Credits
### [pi.dev](https://pi.dev)
Pi provides the agent harness used to develop and refine this project. Its tooling made it easier to iterate quickly, validate changes, and improve the TUI with confidence.
=== fossil commit -m Bump version to v0.3.4 Cargo.lock Cargo.toml README.md ===
status: ok
stdout:
Pull from https://geraldo@intmain.dev/museum/lazyfossil
Round-trips: 1 Artifacts sent: 0 received: 0
Pull done, wire bytes sent: 494 received: 1899 remote: 213.238.180.157
New_Version: fd244ab47999613df7fc3f34b393d477aca96fe21262578adecf276f9e52897c
Sync with https://geraldo@intmain.dev/museum/lazyfossil
Round-trips: 1 Artifacts sent: 4 received: 0
Sync done, wire bytes sent: 2522 received: 316 remote: 213.238.180.157
=== fossil info ===
status: ok
stdout:
project-name: lazyfossil
repository: /home/geraldo/museum/lazyfossil.fossil
local-root: /home/geraldo/Documents/pi_lazyfossil/
config-db: /home/geraldo/.config/fossil.db
project-code: 3c93e1c05dd9744de1e9512b949b2d45d8052f60
checkout: fd244ab47999613df7fc3f34b393d477aca96fe2 2026-06-06 19:31:48 UTC
parent: 8d6055058f6ef79dfca656750fe67af903b7509f 2026-06-06 19:09:44 UTC
tags: trunk
comment: Bump version to v0.3.4 (user: geraldo)
check-ins: 33
=== fossil status ===
status: ok
stdout:
repository: /home/geraldo/museum/lazyfossil.fossil
local-root: /home/geraldo/Documents/pi_lazyfossil/
config-db: /home/geraldo/.config/fossil.db
checkout: fd244ab47999613df7fc3f34b393d477aca96fe2 2026-06-06 19:31:48 UTC
parent: 8d6055058f6ef79dfca656750fe67af903b7509f 2026-06-06 19:09:44 UTC
tags: trunk
comment: Bump version to v0.3.4 (user: geraldo)
stderr:
setting ignore-glob has both versioned and non-versioned values: using versioned value from file "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob" (to silence this warning, either create an empty file named "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob.no-warn" in the check-out root, or delete the non-versioned setting with "fossil unset ignore-glob")
=== fossil extras --dotfiles ===
status: ok
stdout:
.pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md
doc/images/lazyfossil_logo_2_high_res.png
stderr:
setting ignore-glob has both versioned and non-versioned values: using versioned value from file "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob" (to silence this warning, either create an empty file named "/home/geraldo/Documents/pi_lazyfossil/.fossil-settings/ignore-glob.no-warn" in the check-out root, or delete the non-versioned setting with "fossil unset ignore-glob")
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c ===
status: ok
stdout:
fd244ab479|geraldo|2026-06-06 19:31:48|Bump version to v0.3.4
8d6055058f|geraldo|2026-06-06 19:09:44|Fix screenshot path; Add commit screenshot
e92ba54778|geraldo|2026-06-06 19:08:06|Add how to install documentation and screenshots
88e4b4d190|geraldo|2026-06-06 08:16:25|Bump version to 0.3.3 and update README
4c2f33e147|geraldo|2026-06-06 08:14:17|Update README
40105961ed|geraldo|2026-06-06 08:02:23|Consolidate plans into master plan and archive older drafts
131a1b3e5c|geraldo|2026-06-06 07:47:18|Add badges
0a0984b16d|geraldo|2026-06-06 07:26:16|Bump version to 0.3.2 and rebuild
fff8b35f24|geraldo|2026-06-06 07:13:07|Add project logo
bab3996240|geraldo|2026-06-06 05:02:24|Add publish github release workflow
760df322ef|geraldo|2026-06-05 20:06:18|Add badge to README
1ac6b345e4|geraldo|2026-06-05 20:05:30|Fix github workflow binary path
67774b0a64|geraldo|2026-06-05 19:05:41|fix github workflow - binary_name
e507e22d1b|geraldo|2026-06-05 18:50:53|Bump version to 0.3.1 and rebuild
de8e50f7b0|geraldo|2026-06-05 18:25:33|Add github workflow
dbe9c779db|geraldo|2026-06-05 18:12:57|Update repository to github
d82a046b8e|geraldo|2026-06-05 18:08:41|Mirror fossil repository to github
14b139337f|geraldo|2026-06-05 18:06:58|Include hidden files in extra listing
3e4f100ab4|geraldo|2026-06-05 18:03:02|Add sync feature
97a7c7a052|geraldo|2026-06-04 18:32:04|Bump version to 0.2.1 and rebuild project
=== fossil timeline -n 20 -t ci -F %h|%a|%d|%c -p .pi/plans/2026-06-06-2026-06-06-editor-discard-open-conflicts.md ===
status: ok
stdout: