feat: added live tracking and stale status warnings for agent activity snapshots

- Added live tracking and stale status warnings for agent activity snapshots.
- Fixed text wrapping with ANSI escape sequences to defer style open sequences after whitespace.
- Added VirtualRenderScheduler for deterministic virtual-clock rendering tests.
This commit is contained in:
can1357
2026-08-16 10:18:56 +03:00
parent d4587e318e
commit 02cd22dc9b
29 changed files with 314 additions and 57 deletions
+24 -1
View File
@@ -1001,7 +1001,14 @@ fn split_into_tokens_with_ansi(line: &[u16]) -> SmallVec<[Vec<u16>; 4]> {
&& let Some(seq_len) = ansi_seq_len_u16(line, i)
{
let seq = &line[i..i + seq_len];
if current.is_empty() {
// A sequence that follows visible content closes it (color reset,
// underline off) and must ride along with that token so the closer
// cannot migrate into whitespace discarded at a soft wrap (#8582).
// A sequence after whitespace opens the *next* token's style, so it
// waits for it: gluing it to the whitespace token would keep that
// space alive past the wrap point and open the style on the line
// being broken instead of the one carrying the styled word.
if current.is_empty() || in_whitespace {
pending_ansi.extend_from_slice(seq);
} else {
current.extend_from_slice(seq);
@@ -2020,6 +2027,22 @@ mod tests {
assert_eq!(actual, ["plain \x1b[33mcode\x1b[39m", "next"]);
}
#[test]
fn test_wrap_text_with_ansi_defers_style_open_after_trailing_space() {
let data = to_u16("read this thread \x1b[4mhttps://example.com/very/long/path\x1b[24m");
let lines = wrap_text_with_ansi_impl(&data, 40, DEFAULT_TAB_WIDTH);
let actual: Vec<String> = lines
.iter()
.map(|line| String::from_utf16_lossy(line))
.collect();
// The underline opens on the wrapped line that carries the URL: neither
// the discarded space nor a stray `4m`/`24m` pair stays on the head line.
assert_eq!(actual[0], "read this thread");
assert!(actual[1].starts_with("\x1b[4m"));
assert!(actual[1].contains("https://"));
}
#[test]
fn test_wrap_text_with_ansi_resets_strike_without_resetting_colors() {
let data =