use super::*;
use crate::output::OutputEvent;
use crate::rendering::{RenderInputKind, render};
use crate::tui::transcript_cards::{text_position_for_visual_cell, visual_text_for_width};
fn full_reference(state: &MissionControlState, width: u16) -> Vec<TranscriptVisualLine> {
let mut lines = Vec::new();
for span in project_card_spans(state) {
let mut card = (*span.card).clone();
if streaming::is_windowed(&card) {
card.body = transcript::sanitize_preview(
state
.transcript
.get(span.first_entry_index)
.unwrap()
.body()
.trim_end_matches('\n'),
);
card.body_lines = render(&card.body, RenderInputKind::Plain);
}
append_card_visual_lines(
state,
&mut lines,
&card,
span.first_entry_index,
state.is_transcript_focused(),
width,
span.first_entry_index > 0,
);
}
lines.push(transcript_bottom_padding_visual_line(width, state.theme));
annotate_copy_byte_ranges(&mut lines);
lines
}
fn copy_projection(lines: &[TranscriptVisualLine]) -> String {
let mut text = String::new();
let mut seen = false;
for line in lines.iter().filter(|line| line.copyable) {
if seen && !line.copy_continuation {
text.push('\n');
}
text.push_str(&line.copy_text);
seen = true;
}
text
}
fn assert_windows(state: &MissionControlState, width: u16, check_copy: bool) {
let reference = full_reference(state, width);
let total: usize = reference
.iter()
.map(|line| visual_line_rows(line, width))
.sum();
assert_eq!(cached_visual_rows(state, width), total);
let height = 11u16;
for (index, scrollback) in [0, 1, total / 2, total.saturating_sub(5), usize::MAX, 0]
.into_iter()
.enumerate()
{
let start = total
.saturating_sub(usize::from(height))
.saturating_sub(scrollback);
let mut expected = Vec::new();
let mut row = 0;
let mut offset = 0;
for line in &reference {
let next = row + visual_line_rows(line, width);
if next > start && row < start + usize::from(height) {
if expected.is_empty() {
offset = start.saturating_sub(row);
}
expected.push(line.clone());
}
row = next;
}
let top = visible_visual_lines_windowed(state, width, start, height);
assert_eq!(
top.lines, expected,
"top window width={width} start={start}"
);
assert_eq!(top.scroll_offset, offset);
for line in &mut expected {
line.copy_byte_range = None;
}
let warm = visible_scrollback_visual_lines(state, width, height, scrollback);
assert_eq!(
warm.lines, expected,
"scroll width={width} back={scrollback}"
);
assert_eq!(warm.scroll_offset, offset);
if index == 0 {
let cache = std::mem::take(&mut *state.transcript_cache.borrow_mut());
let cold = visible_scrollback_visual_lines(state, width, height, scrollback);
assert_eq!(warm.lines, cold.lines);
assert_eq!(warm.scroll_offset, cold.scroll_offset);
*state.transcript_cache.borrow_mut() = cache;
}
}
if check_copy {
assert_eq!(
visual_text_for_width(state, width),
copy_projection(&reference)
);
let mut row = 0;
for line in &reference {
let rows = visual_line_rows(line, width);
if row >= total / 2 && line.copyable && !line.copy_text.is_empty() {
for column in [0, 1, 4, usize::from(width) - 1] {
let local = transcript::text_position_for_visual_cell_with_leading_columns(
&line.copy_text,
usize::from(line.visual_copy_column_offset),
0,
column,
width,
)
.unwrap();
let position =
text_position_for_visual_cell(state, row, column, width).unwrap();
assert_eq!(
position.byte,
line.copy_byte_range.unwrap().0 + local.byte.min(line.copy_text.len())
);
}
break;
}
row += rows;
}
}
}
#[test]
fn streaming_viewport_unicode_controls_and_redaction_match_full_formatter() {
for input in [
"first\n\n界 cafe\u{301} 👩\u{200d}💻\nlast\n\n",
"first\n\x1b[31mred\x1b[0m\nend",
"first\n\x1b]8;;https://example.com\nsecret\x1b\\link\x1b]8;;\x1b\\\nend",
"first\nAuthorization: Bearer abcdefghijklmnopqrstuvwxyz\nend",
"first\napi_key=sk-test-only-not-a-real-key-1234567890\nend",
"first\n{\"password\":\n\"test-only-secret\"}\nend",
"first\ncontrol\r\t\x08\x07\u{009b}31mend\nlast",
"# Heading\n**bold**\n```rust\nlet x = 1;\n```",
] {
let mut state = MissionControlState::default();
for character in input.chars() {
state.apply_output_event(&OutputEvent::AssistantDelta {
text: character.to_string(),
});
for width in [1, 2, 3, 8, 24] {
assert_eq!(
visual_lines(&state, width),
full_reference(&state, width),
"{input:?} width={width}"
);
}
}
assert_windows(&state, 8, true);
}
}
fn assert_exceptional_row_windows_and_hits(state: &MissionControlState, width: u16) {
let reference = full_reference(state, width);
let heights: Vec<_> = reference
.iter()
.map(|line| visual_line_rows(line, width))
.collect();
let total: usize = heights.iter().sum();
let mut row = 0;
let mut previous_exceptional = false;
for (line, rows) in reference.iter().zip(heights) {
let exceptional = !line.copy_text.is_ascii();
if line.copyable && (exceptional || previous_exceptional) {
for subrow in 0..rows {
let start = row + subrow;
let window = visible_visual_lines_windowed(state, width, start, 1);
assert_eq!(
window.lines,
vec![line.clone()],
"width={width} row={start}"
);
assert_eq!(window.scroll_offset, subrow);
let mut unannotated = line.clone();
unannotated.copy_byte_range = None;
let scrollback = total - start - 1;
let window = visible_scrollback_visual_lines(state, width, 1, scrollback);
assert_eq!(window.lines, vec![unannotated], "width={width} row={start}");
assert_eq!(window.scroll_offset, subrow);
for column in 0..usize::from(width) {
let expected = transcript::text_position_for_visual_cell_with_leading_columns(
&line.copy_text,
usize::from(line.visual_copy_column_offset),
subrow,
column,
width,
)
.map(|position| {
line.copy_byte_range.unwrap().0 + position.byte.min(line.copy_text.len())
});
assert_eq!(
text_position_for_visual_cell(state, start, column, width)
.map(|position| position.byte),
expected,
"copy width={width} row={start} column={column} text={:?}",
line.copy_text,
);
}
}
}
previous_exceptional = exceptional;
row += rows;
}
}
#[test]
fn streaming_viewport_exceptional_width_rows_match_full_formatter() {
for input in [
"ガガガガ\nNEXT",
"パパパパ\nNEXT",
"لالالالا\nNEXT",
"\u{301}\u{200b}\u{200d}\nNEXT",
"界\u{200b} cafe\u{301} 👩\u{200d}💻\nNEXT",
] {
let mut state = MissionControlState::default();
state.apply_output_event(&OutputEvent::AssistantDelta { text: input.into() });
for width in [8, 1, 2, 3, 4] {
assert_windows(&state, width, true);
assert_exceptional_row_windows_and_hits(&state, width);
}
}
}
#[test]
fn streaming_viewport_long_exceptional_width_scroll_resize_and_copy() {
let mut state = MissionControlState::default();
let exceptional = "ガガガガ\nNEXT\nパパパパ\nNEXT\nلالالالا\nNEXT\n\u{301}\u{200b}\u{200d}\nNEXT\n";
let filler = "ordinary reply\n".repeat(512);
let body = format!("{exceptional}{filler}{exceptional}{filler}{exceptional}");
state.apply_output_event(&OutputEvent::AssistantDelta { text: body });
for width in [8, 1, 2, 3, 8] {
assert_windows(&state, width, true);
assert_exceptional_row_windows_and_hits(&state, width);
}
for delta in ["カ", "゙ガガガ\nNEXT"] {
state.apply_output_event(&OutputEvent::AssistantDelta { text: delta.into() });
assert_windows(&state, 8, true);
assert_exceptional_row_windows_and_hits(&state, 8);
}
}
#[test]
fn streaming_viewport_long_response_scroll_resize_copy_and_finalize() {
let mut state = MissionControlState::default();
state.apply_output_event(&OutputEvent::UserPrompt {
text: "Explain the Unicode output".into(),
});
let mut body = "界 cafe\u{301} 👩\u{200d}💻 **literal** 0123456789\n".repeat(6000);
assert!(body.len() > transcript::MAX_STREAMING_CACHE_BYTES);
body.push_str("unfinished cafe");
state.apply_output_event(&OutputEvent::AssistantDelta { text: body });
assert_windows(&state, 80, true);
for delta in ["\u{301} 👩", "\u{200d}💻\nlast **still plain**"] {
state.apply_output_event(&OutputEvent::AssistantDelta { text: delta.into() });
assert_windows(&state, 41, true);
}
state.focus_transcript();
assert_windows(&state, 24, false);
state.focus_prompt();
let theme = MissionControlTheme::from_runtime_appearance(
&crate::appearance::RuntimeAppearance::default(),
1,
);
assert!(state.set_theme(theme, false));
assert_windows(&state, 80, false);
for width in [40, 41, 42, 43, 44] {
visible_scrollback_visual_lines(&state, width, 11, 0);
}
state
.transcript_cache
.borrow_mut()
.clear_visual_projection();
assert_windows(&state, 41, false);
state.apply_output_event(&OutputEvent::AssistantComplete {
text: "# Reconciled\n**replacement**".into(),
});
state.begin_final_assistant_plain_first_paint();
assert_windows(&state, 41, true);
assert!(visual_text_for_width(&state, 41).contains("**replacement**"));
state.finish_final_assistant_plain_first_paint();
assert_windows(&state, 41, true);
let copied = visual_text_for_width(&state, 41);
assert!(copied.contains("replacement"));
assert!(!copied.contains("**replacement**"));
assert!(!copied.contains("0123456789"));
}
#[test]
fn streaming_viewport_long_unbroken_response_and_redaction() {
let mut state = MissionControlState::default();
let body = "界 cafe\u{301} 👩\u{200d}💻 ".repeat(12000);
assert!(body.len() > transcript::MAX_STREAMING_CACHE_BYTES);
state.apply_output_event(&OutputEvent::AssistantDelta { text: body });
assert_windows(&state, 80, true);
for delta in [
"\n{\"password\":\n\"test-only-secret",
"-completed\"}\n\x1b]8;;https://example.com\nhidden payload",
"\x1b\\safe link\x1b]8;;\x1b\\\nvisible tail",
] {
state.apply_output_event(&OutputEvent::AssistantDelta { text: delta.into() });
assert_windows(&state, 41, false);
}
let copied = visual_text_for_width(&state, 41);
assert!(!copied.contains("test-only-secret-completed"));
assert!(!copied.contains("hidden payload"));
assert!(!copied.contains('\x1b'));
assert!(copied.contains("visible tail"));
assert_windows(&state, 80, true);
let mut quoted = MissionControlState::default();
let mut quoted_body = format!(
"password=\"pending{}",
"unfinished synthetic secret text".repeat(10000)
);
quoted.apply_output_event(&OutputEvent::AssistantDelta {
text: quoted_body.clone(),
});
assert_windows("ed, 80, false);
quoted.apply_output_event(&OutputEvent::AssistantDelta {
text: "\"\npublic tail".into(),
});
assert_windows("ed, 80, true);
assert!(!visual_text_for_width("ed, 80).contains("unfinished synthetic secret text"));
quoted_body.push_str("\"\npublic tail");
quoted.apply_output_event(&OutputEvent::AssistantComplete { text: quoted_body });
assert_windows("ed, 80, true);
let copied = visual_text_for_width("ed, 80);
assert!(copied.contains("public tail"));
assert!(!copied.contains("unfinished synthetic secret text"));
}
#[test]
fn streaming_viewport_completed_prefix_appends_preserve_rows_and_copy_offsets() {
for width in [1, 4, 41, 80] {
let mut state = MissionControlState::default();
state.apply_output_event(&OutputEvent::AssistantDelta {
text: format!("{}unfinished cafe", "ガガ\nلالالا\n界 👩\u{200d}💻\n".repeat(80)),
});
assert_windows(&state, width, true);
for delta in [
"\u{301} 👩".to_string(),
"\u{200d}💻\n".to_string(),
"new ガ line\n".repeat(400),
"tail\n\n".to_string(),
] {
state.apply_output_event(&OutputEvent::AssistantDelta { text: delta });
assert_windows(&state, width, true);
}
}
}
#[test]
#[ignore = "manual projection workload; reports wall time, not physical terminal latency"]
fn streaming_viewport_large_response_workload() {
let viewport_sample = |state: &MissionControlState, scrollback| {
let start = std::time::Instant::now();
let visible = visible_scrollback_visual_lines(state, 80, 24, scrollback);
assert!(visible.lines.len() <= 24);
drop(std::hint::black_box(visible));
start.elapsed().as_micros()
};
let reference_sample = |state: &MissionControlState| {
let start = std::time::Instant::now();
let mut lines = Vec::new();
for span in project_card_spans(state) {
let mut card = (*span.card).clone();
if streaming::is_windowed(&card) {
card.body_lines = render(&card.body, RenderInputKind::Plain);
}
append_card_visual_lines(
state,
&mut lines,
&card,
span.first_entry_index,
state.is_transcript_focused(),
80,
span.first_entry_index > 0,
);
}
lines.push(transcript_bottom_padding_visual_line(80, state.theme));
annotate_copy_byte_ranges(&mut lines);
let rows: usize = lines.iter().map(|line| visual_line_rows(line, 80)).sum();
drop(std::hint::black_box((lines, rows)));
start.elapsed().as_micros()
};
for (label, body) in [
("small_ascii", "streaming response\n".repeat(8)),
("small_unicode", "streaming 界 response\n".repeat(8)),
(
"large_ascii",
"streaming response with completed rows\n".repeat(10000),
),
(
"large_unicode",
"streaming 界 response with completed rows\n".repeat(10000),
),
(
"unbroken_unicode",
"界 cafe\u{301} 👩\u{200d}💻 ".repeat(12000),
),
] {
let source_bytes = body.len();
let mut viewport_state = MissionControlState::default();
let mut reference_state = MissionControlState::default();
let mut rebuild_state = MissionControlState::default();
viewport_state.apply_output_event(&OutputEvent::AssistantDelta { text: body.clone() });
rebuild_state.apply_output_event(&OutputEvent::AssistantDelta { text: body.clone() });
reference_state.apply_output_event(&OutputEvent::AssistantDelta { text: body });
let viewport_cold = viewport_sample(&viewport_state, 0);
let reference_cold = reference_sample(&reference_state);
let rebuild_cold = viewport_sample(&rebuild_state, 0);
let mut viewport_times = Vec::new();
let mut reference_times = Vec::new();
let mut rebuild_times = Vec::new();
for index in 0..30 {
let text = if label == "unbroken_unicode" {
format!(" chunk {index} 界")
} else if label.ends_with("ascii") {
format!("\nchunk {index}")
} else {
format!("\nchunk {index} 界")
};
viewport_state.apply_output_event(&OutputEvent::AssistantDelta { text: text.clone() });
rebuild_state.apply_output_event(&OutputEvent::AssistantDelta { text: text.clone() });
rebuild_state.transcript_cache.borrow_mut().streaming_reuse = None;
reference_state.apply_output_event(&OutputEvent::AssistantDelta { text });
let scrollback = if index % 2 == 0 { 4000 } else { 0 };
let (viewport, rebuild, reference) = match index % 3 {
0 => {
let viewport = viewport_sample(&viewport_state, scrollback);
let rebuild = viewport_sample(&rebuild_state, scrollback);
(viewport, rebuild, reference_sample(&reference_state))
}
1 => {
let rebuild = viewport_sample(&rebuild_state, scrollback);
let reference = reference_sample(&reference_state);
(
viewport_sample(&viewport_state, scrollback),
rebuild,
reference,
)
}
_ => {
let reference = reference_sample(&reference_state);
let viewport = viewport_sample(&viewport_state, scrollback);
(
viewport,
viewport_sample(&rebuild_state, scrollback),
reference,
)
}
};
viewport_times.push(viewport);
reference_times.push(reference);
rebuild_times.push(rebuild);
}
viewport_times.sort_unstable();
reference_times.sort_unstable();
rebuild_times.sort_unstable();
eprintln!(
"streaming_viewport_large_response case={label} bytes={source_bytes} frames=30 viewport_cold_us={viewport_cold} rebuild_cold_us={rebuild_cold} full_reference_cold_us={reference_cold} viewport_median_us={} rebuild_median_us={} full_reference_median_us={} width=80 height=24 physical_terminal=false",
viewport_times[15], rebuild_times[15], reference_times[15],
);
}
}