Skip to content

Commit cb72cc0

Browse files
committed
Revert streaming-write preview to the compact tail look (head-anchor change altered UX); keep settled render full show_all in both live+frozen
1 parent 3d961ef commit cb72cc0

1 file changed

Lines changed: 26 additions & 52 deletions

File tree

src/runtime/view/thread/turn/agent_timeline/tool_body_preview.cpp

Lines changed: 26 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -106,28 +106,6 @@ constexpr std::size_t kLiveTailLines = 64;
106106
return std::string{s.substr(start)};
107107
}
108108

109-
// First `keep_lines` lines of `s`, head-anchored. Unlike tail_window,
110-
// this is APPEND-ONLY as `s` grows: line 1..keep_lines never change
111-
// content when more lines arrive at the end, they just stop being the
112-
// last thing rendered. Used for the streaming WRITE preview so the
113-
// rows it commits to native scrollback are byte-identical to the
114-
// settled show_all render's first rows — a tail-anchored preview
115-
// instead SHIFTS its top rows as the file grows, so when those rows
116-
// have already overflowed into scrollback and the card later settles to
117-
// a head-anchored full body, the committed rows no longer match and the
118-
// card is re-emitted below them (the duplicated-write ghost).
119-
[[nodiscard]] std::string head_window(std::string_view s,
120-
std::size_t keep_lines) {
121-
if (s.empty()) return {};
122-
std::size_t nl_seen = 0;
123-
for (std::size_t i = 0; i < s.size(); ++i) {
124-
if (s[i] == '\n') {
125-
if (++nl_seen >= keep_lines) return std::string{s.substr(0, i + 1)};
126-
}
127-
}
128-
return std::string{s};
129-
}
130-
131109
// Body for a terminal line-oriented tool sitting in the LIVE tail.
132110
// Frozen builds keep the full body (full content, painted once); the
133111
// live path elides to a bounded tail window so per-frame split_lines
@@ -322,40 +300,36 @@ maya::ToolBodyPreview::Config tool_body_preview_config(
322300
out.kind = Kind::FileWrite;
323301
out.text_color = text_tertiary;
324302
out.show_footer_stats = true;
325-
// While streaming, the body grows every ~120 ms. The PRIOR
326-
// approach pinned the preview to a TAIL window (last N lines):
327-
// as the file grew, the top rows of that window kept changing
328-
// content. Once they overflowed into native scrollback they
329-
// were frozen there — but on settle the card switches to a
330-
// head-anchored show_all render from line 1, so the committed
331-
// rows no longer matched and the whole card was re-emitted
332-
// below them: the write appeared TWICE in scrollback.
333-
//
334-
// Fix: render the streaming preview HEAD-anchored with
335-
// show_all over a bounded head window. The first N lines never
336-
// change as more arrive (append-only growth), and they are
337-
// byte-identical to the settled show_all render's first N
338-
// lines — so anything that commits to scrollback mid-stream
339-
// stays valid through the freeze handoff. No row is ever
340-
// rewritten; the card only grows downward.
303+
// While streaming, show a SMALL tail preview (the last few
304+
// lines of what's been written so far) — show_all=false lets
305+
// maya elide to its `code_tail` budget, the compact "watch it
306+
// write" look. The duplicated-write ghost came from feeding a
307+
// LARGE tail slice (64 lines): once those rows overflowed into
308+
// native scrollback they were frozen, but on settle the card
309+
// switched to a head-anchored show_all render from line 1, so
310+
// the committed rows no longer matched and the card was
311+
// re-emitted below them (two copies). Keeping the streaming
312+
// preview SMALL (a slice barely above maya's tail budget)
313+
// keeps it inside the live viewport so it never commits to
314+
// scrollback — only the settled show_all render reaches
315+
// scrollback, and it's painted once.
316+
out.show_all = !streaming_now;
341317
out.is_streaming = streaming_now;
342318
if (streaming_now) {
343-
// Head window, full render: append-only and seam-stable.
344-
// O(window) per frame (the slice bounds split_lines).
345-
// Footer derives line/byte totals from out.text, which is
346-
// a partial head here, so suppress it mid-stream (the
347-
// status bar carries the live byte/tok rate); it returns
348-
// with the true total the instant the tool settles.
349-
out.text = head_window(content, kStreamTailLines);
350-
out.show_all = true;
319+
// Small tail slice → O(window) per frame and a fixed
320+
// compact card height. show_all=false above makes maya
321+
// render just its `code_tail` lines from this slice.
322+
// Footer totals would be wrong on a partial body, so
323+
// suppress mid-stream (status bar carries the live rate);
324+
// it returns with the true total the instant we settle.
325+
out.text = tail_window(content, kStreamTailLines);
351326
out.show_footer_stats = false;
352327
} else {
353-
// Terminal: full body, head-anchored, in BOTH the live
354-
// tail and the frozen snapshot. Identical bytes / anchor /
355-
// height across the freeze instant, and a pure superset of
356-
// the streaming head window above — so the handoff never
357-
// moves a committed row.
358-
out.show_all = true;
328+
// Terminal: full body, show_all, in BOTH the live tail and
329+
// the frozen snapshot — identical bytes/height across the
330+
// freeze instant. This is the only write render that ever
331+
// commits to native scrollback, so there is nothing above
332+
// it to disagree with.
359333
out.text = std::move(content);
360334
}
361335
} else if (tc.is_running()) {

0 commit comments

Comments
 (0)