fix(tui): keep the clipped part of a tool header at narrow widths; /jobs prints plain exit codes and a three-line tail

truncate_spans_to_width dropped the whole overflowing span, so at 80 columns the running and failed headers lost the command. It now pushes the partial span before the ellipsis. /jobs and the job pager no longer print Rust Debug text (exit=Some(0), Exit Code: None); they show 'exit N' or 'no exit yet', and the tail is the last three lines instead of one blob with literal \n. From the shell-task UX audit (artifacts/shell-ux-audit-20261011/REPORT.md, slices 1 and 2).

Evidence: nextest -E 'test(/transcript|shell_job|jobs|truncate|tool_header|history/)': 638 run, 638 passed. Not run: clippy, PTY tests, hosted CI.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
CodeWhale BotandClaude Sonnet 5.5 committed 2026-10-11 03:03:50 -07:00
1 parent 8f4d9a736d
commit 951375d934
2 files changed
+20 -7

No files matched your search

+17 -7
View File
@@ -36,11 +36,11 @@ pub(super) fn format_shell_job_list(jobs: &[ShellJobSnapshot]) -> String {
.map(|id| format!(" task={id}"))
.unwrap_or_default();
lines.push(format!(
"{} {:8} {} exit={:?}{}",
"{} {:8} {} {}{}",
job.id,
status_label(&job.status, job.stale),
codewhale_command_contract::elapsed::format_elapsed_ms(job.elapsed_ms),
job.exit_code,
exit_label(job.exit_code),
task
));
lines.push(format!(" cwd: {}", crate::utils::display_path(&job.cwd)));
@@ -50,8 +50,10 @@ pub(super) fn format_shell_job_list(jobs: &[ShellJobSnapshot]) -> String {
} else {
job.stdout_tail.trim()
};
if !tail.is_empty() {
lines.push(format!(" tail: {}", tail.replace('\n', "\\n")));
let tail_lines: Vec<&str> = tail.lines().collect();
let shown = tail_lines.len().saturating_sub(3);
for line in &tail_lines[shown..] {
lines.push(format!(" | {line}"));
}
}
lines.push(
@@ -61,13 +63,21 @@ pub(super) fn format_shell_job_list(jobs: &[ShellJobSnapshot]) -> String {
lines.join("\n")
}
fn exit_label(code: Option<i64>) -> String {
match code {
Some(0) => "exit 0".to_string(),
Some(code) => format!("exit {code}"),
None => "no exit yet".to_string(),
}
}
pub(super) fn format_shell_poll(result: &ShellResult) -> String {
let mut lines = vec![
format!(
"Bash job {}: {} exit={:?} elapsed={}",
"Bash job {}: {} {} elapsed={}",
result.task_id.as_deref().unwrap_or("(unknown)"),
status_label(&result.status, false),
result.exit_code,
exit_label(result.exit_code),
codewhale_command_contract::elapsed::format_elapsed_ms(result.duration_ms)
),
String::new(),
@@ -187,7 +197,7 @@ pub(crate) fn format_shell_job_detail(detail: &ShellJobDetail) -> String {
"Elapsed: {}",
codewhale_command_contract::elapsed::format_elapsed_ms(job.elapsed_ms)
));
lines.push(format!("Exit Code: {:?}", job.exit_code));
lines.push(format!("Exit Code: {}", exit_label(job.exit_code)));
lines.push(format!("Stdin Available: {}", job.stdin_available));
if let Some(task_id) = job.linked_task_id.as_ref() {
lines.push(format!("Linked Task: {task_id}"));
+3
View File
@@ -1335,6 +1335,9 @@ fn truncate_spans_to_width(spans: Vec<Span<'static>>, max_width: usize) -> Vec<S
for ch in span.content.chars() {
let width = unicode_width::UnicodeWidthChar::width(ch).unwrap_or(0);
if used + width > content_budget {
if !content.is_empty() {
truncated.push(Span::styled(content, span.style));
}
break 'outer;
}
content.push(ch);