Match pinned transcript headers to the original prompt style (#48775)

## What changed

Add the `› ` prefix to pinned prompt headers, using bold red for spoken prompts and bold dim styling for other prompts. Reserve one column when truncating the header.

## Testing

Add a rendering test comparing pinned headers with the original prompt rows for both regular and spoken prompts. Update snapshots for the prefix and display-width truncation.

GitOrigin-RevId: 89c2d9267374ef8f6504058c12c078025f302fef
This commit is contained in:
Eric Traut
2026-09-27 19:47:40 +00:00
committed by copyberry
parent fdbce2080c
commit 596f8c5c3c
4 changed files with 45 additions and 7 deletions
@@ -46,8 +46,16 @@ pub(super) fn line(
message
};
Some(truncate_line_with_ellipsis_if_overflow(
Line::from(message).style(crate::style::history_prompt_style()),
usize::from(width),
Line::from(vec![
if prompt.spoken {
"› ".red().bold()
} else {
"› ".bold().dim()
},
message.into(),
])
.style(crate::style::history_prompt_style()),
usize::from(width.saturating_sub(1)),
))
}
@@ -1,6 +1,7 @@
//! Sticky context follows the visible turn while leaving source text and hit rows intact.
use super::*;
use crate::history_cell::new_spoken_user_prompt;
use crate::history_cell::new_user_prompt;
use crate::transcript_view::tests::cell;
use crate::transcript_view::tests::render;
@@ -84,6 +85,36 @@ fn header_sanitizes_controls_and_truncates_by_display_width() {
insta::assert_snapshot!(line.to_string());
}
#[test]
fn pinned_prompt_matches_the_original_prompt_style() {
for prompt in [
new_user_prompt("keep going".into(), Vec::new(), Vec::new(), Vec::new()),
new_spoken_user_prompt("keep going".into()),
] {
let width = 30;
let mut expected = Buffer::empty(Rect::new(
/*x*/ 0, /*y*/ 0, width, /*height*/ 3,
));
TextLayout::new(prompt.transcript_hyperlink_lines(width), width).render(
expected.area,
&mut expected,
/*start_row*/ 0,
);
let cells: Vec<Arc<dyn HistoryCell>> = vec![
Arc::new(prompt),
cell("one\ntwo\nthree\nfour\nfive\nsix\nseven\neight"),
];
let mut view = TranscriptView::default();
let actual = render(&mut view, &cells, width, /*height*/ 7);
assert_eq!(
&actual.content[..usize::from(width)],
&expected.content[usize::from(width)..usize::from(width * 2)]
);
assert_eq!(view.area.y, 1);
}
}
#[test]
fn mouse_selection_preserves_header_reservation_at_turn_boundaries() {
let cells = vec![
@@ -1,6 +1,5 @@
---
source: tui/src/transcript_view/prompt_header_tests.rs
assertion_line: 76
expression: line.to_string()
---
界 café next ques…
› 界 café next q…
@@ -4,21 +4,21 @@ assertion_line: 68
expression: "snapshots.join(\"\\n\\n\")"
---
Latest
Second café question
› Second café question
seven
eight
nine
ten
Earlier answer
First question
› First question
one
two
three
four
After prepend
First question
› First question
one
two
three