From 90a4a174a83a74c3ead86564f356d55746866138 Mon Sep 17 00:00:00 2001 From: Lstarsky0 <59827030+Lstarsky0@users.noreply.github.com> Date: Tue, 20 Oct 2026 18:25:00 +0800 Subject: [PATCH 1/2] fix(tui): keep a to-do row's status word when its title is long The work surface gives a row's label the whole width and the detail only what is left, dropping it under four columns. A to-do whose title fills the strip lost its status word, so at 80 columns the in-progress step, the one most worth reading, showed as a bare truncated title. To-do rows (the `graph:` ids the strip already numbers) now reserve the detail's width, at most half the row, before the title is truncated. Titles that fit render as before and the full title stays in the hover. Other rows still give the label the room, so the Context budget row keeps its numbers ahead of the /context hint. Signed-off-by: Lstarsky0 <59827030+Lstarsky0@users.noreply.github.com> --- crates/tui/src/tui/work_surface/render/mod.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/tui/src/tui/work_surface/render/mod.rs b/crates/tui/src/tui/work_surface/render/mod.rs index 7f4300c55f..c17715c806 100644 --- a/crates/tui/src/tui/work_surface/render/mod.rs +++ b/crates/tui/src/tui/work_surface/render/mod.rs @@ -357,7 +357,13 @@ pub fn render(frame: &mut Frame, area: Rect, app: &mut App) { }; let prefix_width = UnicodeWidthStr::width(prefix.as_str()); let row_width = usize::from(content_area.width); - let label_budget = row_width.saturating_sub(prefix_width).max(1); + let detail_room = if row.id.0.starts_with("graph:") { + UnicodeWidthStr::width(detail_candidate.as_str()) + .min(row_width.saturating_sub(prefix_width) / 2) + } else { + 0 + }; + let label_budget = row_width.saturating_sub(prefix_width + detail_room).max(1); let label = truncate_line_to_width(&row.label, label_budget); let detail_budget = row_width.saturating_sub(prefix_width + UnicodeWidthStr::width(label.as_str())); From aedcbb5f458d81d1427c572c66e30cca58d4350d Mon Sep 17 00:00:00 2001 From: Lstarsky0 <59827030+Lstarsky0@users.noreply.github.com> Date: Wed, 21 Oct 2026 06:17:42 +0800 Subject: [PATCH 2/2] fix(tui): reserve the status room only for plan-step rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On a side rail the tasks view also lists the graph's operation and blocker nodes, which share the `graph:` id prefix with to-dos, so the reservation took up to half a wide rail from their titles to show their `state ยท kind` detail. Check the plan-step ids the strip's filter already uses instead of the prefix, so those rows keep the label-first layout and to-dos keep their status words in every placement. Signed-off-by: Lstarsky0 <59827030+Lstarsky0@users.noreply.github.com> --- crates/tui/src/tui/work_surface/model.rs | 2 +- crates/tui/src/tui/work_surface/render/mod.rs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/tui/src/tui/work_surface/model.rs b/crates/tui/src/tui/work_surface/model.rs index a162664bd1..39f223fd31 100644 --- a/crates/tui/src/tui/work_surface/model.rs +++ b/crates/tui/src/tui/work_surface/model.rs @@ -1114,7 +1114,7 @@ fn finished_shell_outcome(locale: Locale, entry: &TaskPanelEntry) -> String { } /// Row ids of the plan-step (to-do) nodes in the cached graph. -fn plan_step_row_ids(app: &App) -> HashSet { +pub(super) fn plan_step_row_ids(app: &App) -> HashSet { app.work_surface .cached_graph .as_ref() diff --git a/crates/tui/src/tui/work_surface/render/mod.rs b/crates/tui/src/tui/work_surface/render/mod.rs index c17715c806..2c300f6ae2 100644 --- a/crates/tui/src/tui/work_surface/render/mod.rs +++ b/crates/tui/src/tui/work_surface/render/mod.rs @@ -30,7 +30,7 @@ use codewhale_palette::{ChromeInk, chrome_style}; use super::model::{ DockTabHitbox, DockTabTarget, RailPanel, WorkHitbox, WorkRow, WorkSurfacePlacement, WorkTone, - visible_rows_for, visible_rows_for_panel, + plan_step_row_ids, visible_rows_for, visible_rows_for_panel, }; mod layout; @@ -121,6 +121,7 @@ pub fn render(frame: &mut Frame, area: Rect, app: &mut App) { HashMap::new() }; let ordinal_width = todo_ordinals.len().max(1).to_string().len(); + let todo_ids = plan_step_row_ids(app); let goal_title = placement.is_strip().then(|| top_goal_title(app)).flatten(); let todo_progress = placement .is_strip() @@ -357,7 +358,7 @@ pub fn render(frame: &mut Frame, area: Rect, app: &mut App) { }; let prefix_width = UnicodeWidthStr::width(prefix.as_str()); let row_width = usize::from(content_area.width); - let detail_room = if row.id.0.starts_with("graph:") { + let detail_room = if todo_ids.contains(&row.id.0) { UnicodeWidthStr::width(detail_candidate.as_str()) .min(row_width.saturating_sub(prefix_width) / 2) } else {