From 9f68bc7bf2aeb81aa0e01c5ed242b3760ec712dd Mon Sep 17 00:00:00 2001 From: Amolith Date: Wed, 18 Mar 2026 18:52:15 -0600 Subject: [PATCH] Colour-code status, priority, and effort by level Add per-value colour helpers to color.rs replacing the old hardcoded single-colour-per-column approach. CLI: open=green, in_progress=bold yellow, closed=default; high=bold red, medium=default, low=cyan for both priority and effort. Web UI: open=success, in_progress=warning, closed=default badge. Project card badges are conditional on count (secondary when zero). In-progress badge moves to front of row when nonzero. --- src/cmd/done.rs | 2 +- src/cmd/list.rs | 12 +++---- src/cmd/ready.rs | 8 ++--- src/cmd/reopen.rs | 2 +- src/cmd/show.rs | 6 ++-- src/color.rs | 79 +++++++++++++++++++++++++++++++++++++++++++ templates/index.html | 11 ++++-- templates/macros.html | 4 +-- templates/task.html | 2 +- 9 files changed, 105 insertions(+), 21 deletions(-) diff --git a/src/cmd/done.rs b/src/cmd/done.rs index bed0cfb32762a5c8510d1aa032105a570a9125d9..a10dbeb96e5d0c84c8b90e8ffeb3e6e98cfb5dc6 100644 --- a/src/cmd/done.rs +++ b/src/cmd/done.rs @@ -23,7 +23,7 @@ pub fn run(root: &Path, ids: &[String], json: bool) -> Result<()> { } else { let c = crate::color::stdout_theme(); for id in &closed { - println!("{}closed{} {id}", c.green, c.reset); + println!("{}closed{} {id}", c.status(db::Status::Closed), c.reset); } } diff --git a/src/cmd/list.rs b/src/cmd/list.rs index 2b2a801b6d47c652d5b0640569b29f210cad72b5..f4f906c2ac68e97152f6303e8d87fc4a1d67c3f1 100644 --- a/src/cmd/list.rs +++ b/src/cmd/list.rs @@ -1,9 +1,9 @@ use anyhow::Result; use comfy_table::presets::NOTHING; -use comfy_table::{Cell, Color, Table}; +use comfy_table::{Cell, Table}; use std::path::Path; -use crate::color::{cell_bold, cell_fg, stdout_use_color}; +use crate::color::{cell_bold, cell_effort, cell_priority, cell_status, stdout_use_color}; use crate::db; pub fn run( @@ -52,13 +52,13 @@ pub fn run( for t in &tasks { table.add_row(vec![ cell_bold(&t.id, use_color), - cell_fg( + cell_status( format!("[{}]", db::status_label(t.status)), - Color::Yellow, + t.status, use_color, ), - cell_fg(db::priority_label(t.priority), Color::Red, use_color), - cell_fg(db::effort_label(t.effort), Color::Blue, use_color), + cell_priority(db::priority_label(t.priority), t.priority, use_color), + cell_effort(db::effort_label(t.effort), t.effort, use_color), Cell::new(&t.title), ]); } diff --git a/src/cmd/ready.rs b/src/cmd/ready.rs index ae128f83d7d9ca482fc3ee2b28dd5b6ca392a555..aa7fe791e4147936e378966b029b244a43ca9f32 100644 --- a/src/cmd/ready.rs +++ b/src/cmd/ready.rs @@ -1,9 +1,9 @@ use anyhow::Result; use comfy_table::presets::NOTHING; -use comfy_table::{Cell, Color, Table}; +use comfy_table::{Cell, Table}; use std::path::Path; -use crate::color::{cell_bold, cell_fg, stdout_use_color}; +use crate::color::{cell_bold, cell_effort, cell_priority, stdout_use_color}; use crate::db; pub fn run(root: &Path, json: bool) -> Result<()> { @@ -32,8 +32,8 @@ pub fn run(root: &Path, json: bool) -> Result<()> { for t in &tasks { table.add_row(vec![ cell_bold(&t.id, use_color), - cell_fg(db::priority_label(t.priority), Color::Red, use_color), - cell_fg(db::effort_label(t.effort), Color::Blue, use_color), + cell_priority(db::priority_label(t.priority), t.priority, use_color), + cell_effort(db::effort_label(t.effort), t.effort, use_color), Cell::new(&t.title), ]); } diff --git a/src/cmd/reopen.rs b/src/cmd/reopen.rs index 5de0791c8c2f3a380646f6aeac0ec853ff2f3d0c..ddd68145a19d1e99eb67f1cf2cd15613f23ae661 100644 --- a/src/cmd/reopen.rs +++ b/src/cmd/reopen.rs @@ -23,7 +23,7 @@ pub fn run(root: &Path, ids: &[String], json: bool) -> Result<()> { } else { let c = crate::color::stdout_theme(); for id in &reopened { - println!("{}reopened{} {id}", c.green, c.reset); + println!("{}reopened{} {id}", c.status(db::Status::Open), c.reset); } } diff --git a/src/cmd/show.rs b/src/cmd/show.rs index 8f6f16a80a65fa4a34cb5f5b4d823fa5f3f3e594..bf1e652a7e41faf1261588d37c9e5c77ad85f57d 100644 --- a/src/cmd/show.rs +++ b/src/cmd/show.rs @@ -22,7 +22,7 @@ pub fn run(root: &Path, id: &str, json: bool) -> Result<()> { c.bold, task.title, c.reset, - c.yellow, + c.status(task.status), db::status_label(task.status), c.reset ); @@ -39,10 +39,10 @@ pub fn run(root: &Path, id: &str, json: bool) -> Result<()> { task.id, c.reset, task.task_type, - c.red, + c.priority(task.priority), db::priority_label(task.priority), c.reset, - c.blue, + c.effort(task.effort), db::effort_label(task.effort), c.reset, ); diff --git a/src/color.rs b/src/color.rs index 91eb5a50cadd0d7a24f65efd2fb1ff711f7347ef..0e30290b404d116831d3792c85530dfecbee0b9a 100644 --- a/src/color.rs +++ b/src/color.rs @@ -1,12 +1,17 @@ use comfy_table::{Attribute, Cell, Color}; use std::io::IsTerminal; +use crate::db; + pub struct Theme { pub red: &'static str, pub green: &'static str, pub yellow: &'static str, pub blue: &'static str, + pub cyan: &'static str, pub bold: &'static str, + pub bold_red: &'static str, + pub bold_yellow: &'static str, pub reset: &'static str, } @@ -15,7 +20,10 @@ const ON: Theme = Theme { green: "\x1b[32m", yellow: "\x1b[33m", blue: "\x1b[34m", + cyan: "\x1b[36m", bold: "\x1b[1m", + bold_red: "\x1b[1;31m", + bold_yellow: "\x1b[1;33m", reset: "\x1b[0m", }; @@ -24,7 +32,10 @@ const OFF: Theme = Theme { green: "", yellow: "", blue: "", + cyan: "", bold: "", + bold_red: "", + bold_yellow: "", reset: "", }; @@ -74,3 +85,71 @@ pub fn cell_fg(text: impl ToString, color: Color, use_color: bool) -> Cell { cell } } + +impl Theme { + /// ANSI escape for a task status. + pub fn status(&self, s: db::Status) -> &str { + match s { + db::Status::Open => self.green, + db::Status::InProgress => self.bold_yellow, + db::Status::Closed => "", + } + } + + /// ANSI escape for a priority level. + pub fn priority(&self, p: db::Priority) -> &str { + match p { + db::Priority::High => self.bold_red, + db::Priority::Medium => "", + db::Priority::Low => self.cyan, + } + } + + /// ANSI escape for an effort level. + pub fn effort(&self, e: db::Effort) -> &str { + match e { + db::Effort::High => self.bold_red, + db::Effort::Medium => "", + db::Effort::Low => self.cyan, + } + } +} + +/// A table cell styled for a task status. +pub fn cell_status(text: impl ToString, s: db::Status, use_color: bool) -> Cell { + let cell = Cell::new(text); + if !use_color { + return cell; + } + match s { + db::Status::Open => cell.fg(Color::Green), + db::Status::InProgress => cell.fg(Color::Yellow).add_attribute(Attribute::Bold), + db::Status::Closed => cell, + } +} + +/// A table cell styled for a priority level. +pub fn cell_priority(text: impl ToString, p: db::Priority, use_color: bool) -> Cell { + let cell = Cell::new(text); + if !use_color { + return cell; + } + match p { + db::Priority::High => cell.fg(Color::Red).add_attribute(Attribute::Bold), + db::Priority::Medium => cell, + db::Priority::Low => cell.fg(Color::Cyan), + } +} + +/// A table cell styled for an effort level. +pub fn cell_effort(text: impl ToString, e: db::Effort, use_color: bool) -> Cell { + let cell = Cell::new(text); + if !use_color { + return cell; + } + match e { + db::Effort::High => cell.fg(Color::Red).add_attribute(Attribute::Bold), + db::Effort::Medium => cell, + db::Effort::Low => cell.fg(Color::Cyan), + } +} diff --git a/templates/index.html b/templates/index.html index 9ebf498ff166ba867b2392d52dae399024622482..887dbc9599b78c10bd8e1e7aa6eaa459e7eff205 100644 --- a/templates/index.html +++ b/templates/index.html @@ -21,9 +21,14 @@

{{ name }}

- {{ open }} open - {{ in_progress }} in progress - {{ closed }} closed + {% if *in_progress > 0 %} + {{ in_progress }} in progress + {% endif %} + {{ open }} open + {% if *in_progress == 0 %} + 0 in progress + {% endif %} + {{ closed }} closed
{% if *total > 0 %} diff --git a/templates/macros.html b/templates/macros.html index 32380c59fde7a5b323c27f6399f18ceecf0984cc..038883715daeb28e028c0ce70304a075a90b4376 100644 --- a/templates/macros.html +++ b/templates/macros.html @@ -16,7 +16,7 @@ {% for t in tasks %} {{ t.short_id }} - {{ t.status }} + {{ t.status }} {{ t.priority }} {{ t.effort }} {{ t.title }} @@ -47,7 +47,7 @@ {% for t in tasks %} {{ t.short_id }} - {{ t.status }} + {{ t.status }} {{ t.priority }} {{ t.effort }} {{ t.title }} diff --git a/templates/task.html b/templates/task.html index d0751b01d222f27691f84ff08e23198571962a80..ad0484f4f1f29fed94862591eb627944f5130f82 100644 --- a/templates/task.html +++ b/templates/task.html @@ -20,7 +20,7 @@

{{ task.title }}

- {{ task.status }} + {{ task.status }}
{% if !task.description.is_empty() %}