@kenkaiiii/ggcoder 4.8.2 → 4.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/prompt-commands.d.ts.map +1 -1
- package/dist/core/prompt-commands.js +20 -7
- package/dist/core/prompt-commands.js.map +1 -1
- package/dist/ui/App.d.ts +16 -0
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +53 -16
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/atomic-flush-regression.test.d.ts +2 -0
- package/dist/ui/atomic-flush-regression.test.d.ts.map +1 -0
- package/dist/ui/atomic-flush-regression.test.js +218 -0
- package/dist/ui/atomic-flush-regression.test.js.map +1 -0
- package/dist/ui/components/AssistantMessage.test.js +25 -0
- package/dist/ui/components/AssistantMessage.test.js.map +1 -1
- package/dist/ui/components/Markdown.d.ts.map +1 -1
- package/dist/ui/components/Markdown.js +9 -1
- package/dist/ui/components/Markdown.js.map +1 -1
- package/dist/ui/footer-jump-regression.test.js +56 -9
- package/dist/ui/footer-jump-regression.test.js.map +1 -1
- package/dist/ui/frame-clip-regression.test.d.ts +2 -0
- package/dist/ui/frame-clip-regression.test.d.ts.map +1 -0
- package/dist/ui/frame-clip-regression.test.js +215 -0
- package/dist/ui/frame-clip-regression.test.js.map +1 -0
- package/dist/ui/hooks/useTranscriptHistory.d.ts +9 -1
- package/dist/ui/hooks/useTranscriptHistory.d.ts.map +1 -1
- package/dist/ui/hooks/useTranscriptHistory.js +34 -19
- package/dist/ui/hooks/useTranscriptHistory.js.map +1 -1
- package/dist/ui/live-item-flush.d.ts +21 -0
- package/dist/ui/live-item-flush.d.ts.map +1 -1
- package/dist/ui/live-item-flush.js +35 -0
- package/dist/ui/live-item-flush.js.map +1 -1
- package/dist/ui/live-item-flush.test.js +72 -1
- package/dist/ui/live-item-flush.test.js.map +1 -1
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +126 -1
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/startup-banner-order.test.d.ts +2 -0
- package/dist/ui/startup-banner-order.test.d.ts.map +1 -0
- package/dist/ui/startup-banner-order.test.js +105 -0
- package/dist/ui/startup-banner-order.test.js.map +1 -0
- package/dist/ui/streaming-flush-bounce.test.js +41 -20
- package/dist/ui/streaming-flush-bounce.test.js.map +1 -1
- package/dist/ui/testing/screen-recorder.d.ts.map +1 -1
- package/dist/ui/testing/screen-recorder.js +11 -1
- package/dist/ui/testing/screen-recorder.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Regression for the mid-screen footer "strand" in scrollback mode.
|
|
3
|
+
//
|
|
4
|
+
// If any single frame reaches terminal height (the app's estimate-based
|
|
5
|
+
// live-area clamp can undercount — emoji widths, wrapped footer lines,
|
|
6
|
+
// transient tool-panel rows), Ink records lastOutputHeight >= rows. On the
|
|
7
|
+
// next SHORTER frame, log-update's eraseLines clamps at the screen top and
|
|
8
|
+
// rewrites the frame top-anchored: the footer lands mid-screen with blank
|
|
9
|
+
// rows below it and stays there. The patched ink's clipFrameToTerminalHeight
|
|
10
|
+
// option (set by ggcoder's scrollback-mode INK_OPTIONS) clips frames to
|
|
11
|
+
// rows - 2 so that poisoned state can never be entered. This test renders a
|
|
12
|
+
// frame that overflows the terminal, then shrinks it, and asserts the footer
|
|
13
|
+
// ends at the bottom of the written content — not stranded above blank rows.
|
|
14
|
+
import React from "react";
|
|
15
|
+
import { render, Text, Box } from "ink";
|
|
16
|
+
import { describe, expect, it } from "vitest";
|
|
17
|
+
import stripAnsi from "strip-ansi";
|
|
18
|
+
import { ScreenRecorder, makeRecordingStdout } from "./testing/screen-recorder.js";
|
|
19
|
+
const COLUMNS = 80;
|
|
20
|
+
const ROWS = 24;
|
|
21
|
+
const FOOTER = "SIM_FOOTER_BOTTOM";
|
|
22
|
+
function Frame({ bodyRows }) {
|
|
23
|
+
return (_jsxs(Box, { flexDirection: "column", width: COLUMNS, children: [Array.from({ length: bodyRows }, (_, i) => (_jsx(Text, { children: `BODY_${String(i + 1).padStart(2, "0")}` }, i))), _jsx(Text, { children: "SIM_INPUT" }), _jsx(Text, { children: FOOTER })] }));
|
|
24
|
+
}
|
|
25
|
+
async function tick() {
|
|
26
|
+
await new Promise((resolve) => setTimeout(resolve, 45));
|
|
27
|
+
}
|
|
28
|
+
describe("scrollback frame clip (clipFrameToTerminalHeight)", () => {
|
|
29
|
+
it("clips overflow frames and never strands the footer above blank rows", async () => {
|
|
30
|
+
const recorder = new ScreenRecorder({ columns: COLUMNS, rows: ROWS });
|
|
31
|
+
const stdout = makeRecordingStdout(recorder);
|
|
32
|
+
// Overflow: 30 body rows + 2 control rows > 24 terminal rows.
|
|
33
|
+
const mounted = render(_jsx(Frame, { bodyRows: 30 }), {
|
|
34
|
+
stdout,
|
|
35
|
+
columns: COLUMNS,
|
|
36
|
+
rows: ROWS,
|
|
37
|
+
patchConsole: false,
|
|
38
|
+
maxFps: 1000,
|
|
39
|
+
// Mirrors ggcoder's scrollback-mode INK_OPTIONS.
|
|
40
|
+
clipFrameToTerminalHeight: true,
|
|
41
|
+
});
|
|
42
|
+
await tick();
|
|
43
|
+
// The clip must keep the frame under terminal height: with rows - 2 = 22
|
|
44
|
+
// frame rows, the oldest body rows are dropped (BODY_01..BODY_10 clipped,
|
|
45
|
+
// mirroring the app's bottom-anchored live-area clamp).
|
|
46
|
+
const tallLines = recorder.viewportLines().map((line) => stripAnsi(line));
|
|
47
|
+
expect(tallLines.some((line) => line.includes("BODY_01"))).toBe(false);
|
|
48
|
+
expect(tallLines.some((line) => line.includes("BODY_30"))).toBe(true);
|
|
49
|
+
expect(tallLines.some((line) => line.includes(FOOTER))).toBe(true);
|
|
50
|
+
// Shrink: the turn ends and the frame collapses to controls + done row.
|
|
51
|
+
// Without the clip this is the poisoned transition (eraseLines clamps at
|
|
52
|
+
// the screen top, frame rewrites top-anchored, footer stranded mid-screen
|
|
53
|
+
// above a block of blank rows).
|
|
54
|
+
mounted.rerender(_jsx(Frame, { bodyRows: 3 }));
|
|
55
|
+
await tick();
|
|
56
|
+
mounted.unmount();
|
|
57
|
+
const lines = recorder.viewportLines().map((line) => stripAnsi(line));
|
|
58
|
+
const footerIdx = lines.findIndex((line) => line.includes(FOOTER));
|
|
59
|
+
const lastNonBlank = lines.reduce((acc, line, i) => (line.trim().length > 0 ? i : acc), -1);
|
|
60
|
+
expect(footerIdx, "footer present after shrink").toBeGreaterThanOrEqual(0);
|
|
61
|
+
expect(lastNonBlank, "no blank rows below the footer (no strand)").toBe(footerIdx);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe("scrollback bottom anchoring (anchorFrameToBottom)", () => {
|
|
65
|
+
it("keeps the footer's absolute row fixed when the frame shrinks", async () => {
|
|
66
|
+
const recorder = new ScreenRecorder({ columns: COLUMNS, rows: ROWS });
|
|
67
|
+
const stdout = makeRecordingStdout(recorder);
|
|
68
|
+
// A frame that fits comfortably (12 body + 2 control rows).
|
|
69
|
+
const mounted = render(_jsx(Frame, { bodyRows: 12 }), {
|
|
70
|
+
stdout,
|
|
71
|
+
columns: COLUMNS,
|
|
72
|
+
rows: ROWS,
|
|
73
|
+
patchConsole: false,
|
|
74
|
+
maxFps: 1000,
|
|
75
|
+
// Mirrors ggcoder's scrollback-mode INK_OPTIONS.
|
|
76
|
+
clipFrameToTerminalHeight: true,
|
|
77
|
+
anchorFrameToBottom: true,
|
|
78
|
+
});
|
|
79
|
+
await tick();
|
|
80
|
+
const footerRowAt = () => stripAnsi(recorder.fullText())
|
|
81
|
+
.split("\n")
|
|
82
|
+
.findIndex((line) => line.includes(FOOTER));
|
|
83
|
+
const tallFooterRow = footerRowAt();
|
|
84
|
+
expect(tallFooterRow, "footer present while tall").toBeGreaterThanOrEqual(0);
|
|
85
|
+
// The frame shrinks by 9 rows with no compensating scrollback bytes — the
|
|
86
|
+
// tool-panel-hide / status-swap / finalize scenario. Without anchoring,
|
|
87
|
+
// the footer would move UP by 9 rows; with it, blank pad lines land above
|
|
88
|
+
// the frame and the footer's absolute row stays exactly where it was.
|
|
89
|
+
mounted.rerender(_jsx(Frame, { bodyRows: 3 }));
|
|
90
|
+
await tick();
|
|
91
|
+
expect(footerRowAt(), "footer row unchanged after shrink").toBe(tallFooterRow);
|
|
92
|
+
// Growing again must CONSUME the pad debt: the growth overwrites the
|
|
93
|
+
// blank pad rows above the frame (cursor-up) instead of scrolling new
|
|
94
|
+
// rows, so the footer stays exactly put — not pushed further down — and
|
|
95
|
+
// the gap doesn't survive as permanent whitespace in the transcript.
|
|
96
|
+
mounted.rerender(_jsx(Frame, { bodyRows: 8 }));
|
|
97
|
+
await tick();
|
|
98
|
+
expect(footerRowAt(), "footer row unchanged on regrow (pad consumed)").toBe(tallFooterRow);
|
|
99
|
+
// No stranded blank gap: between the first body row and the footer, every
|
|
100
|
+
// row is real content (the regrown frame reclaimed the pad rows).
|
|
101
|
+
const lines = stripAnsi(recorder.fullText()).split("\n");
|
|
102
|
+
const firstBody = lines.findIndex((line) => line.includes("BODY_01"));
|
|
103
|
+
const footerIdx = lines.findIndex((line) => line.includes(FOOTER));
|
|
104
|
+
const blanksInside = lines
|
|
105
|
+
.slice(firstBody, footerIdx)
|
|
106
|
+
.filter((line) => line.trim().length === 0).length;
|
|
107
|
+
expect(blanksInside, "no permanent blank gap inside the frame region").toBe(0);
|
|
108
|
+
mounted.unmount();
|
|
109
|
+
});
|
|
110
|
+
it("lets the footer return up at idle (anchor inactive — slash menu close)", async () => {
|
|
111
|
+
const recorder = new ScreenRecorder({ columns: COLUMNS, rows: ROWS });
|
|
112
|
+
const stdout = makeRecordingStdout(recorder);
|
|
113
|
+
const mounted = render(_jsx(Frame, { bodyRows: 4 }), {
|
|
114
|
+
stdout,
|
|
115
|
+
columns: COLUMNS,
|
|
116
|
+
rows: ROWS,
|
|
117
|
+
patchConsole: false,
|
|
118
|
+
maxFps: 1000,
|
|
119
|
+
clipFrameToTerminalHeight: true,
|
|
120
|
+
anchorFrameToBottom: true,
|
|
121
|
+
});
|
|
122
|
+
const patched = mounted;
|
|
123
|
+
expect(typeof patched.setFrameAnchorActive, "runtime toggle exposed").toBe("function");
|
|
124
|
+
// Idle: App turns pad creation off (agent not running).
|
|
125
|
+
patched.setFrameAnchorActive(false);
|
|
126
|
+
await tick();
|
|
127
|
+
const footerRowAt = () => stripAnsi(recorder.fullText())
|
|
128
|
+
.split("\n")
|
|
129
|
+
.findIndex((line) => line.includes(FOOTER));
|
|
130
|
+
const baseFooterRow = footerRowAt();
|
|
131
|
+
// Slash menu opens: frame grows by 8 rows — footer moves down.
|
|
132
|
+
mounted.rerender(_jsx(Frame, { bodyRows: 12 }));
|
|
133
|
+
await tick();
|
|
134
|
+
const openFooterRow = footerRowAt();
|
|
135
|
+
expect(openFooterRow, "footer moved down while menu open").toBeGreaterThan(baseFooterRow);
|
|
136
|
+
// Slash menu closes: with the anchor INACTIVE the shrink is symmetric —
|
|
137
|
+
// the footer returns to its pre-menu row and NO pad rows are created.
|
|
138
|
+
mounted.rerender(_jsx(Frame, { bodyRows: 4 }));
|
|
139
|
+
await tick();
|
|
140
|
+
expect(footerRowAt(), "footer returned up after menu close").toBe(baseFooterRow);
|
|
141
|
+
// No whitespace was injected above the frame: the row above BODY_01 in
|
|
142
|
+
// the buffer is not a blank pad line.
|
|
143
|
+
const lines = stripAnsi(recorder.fullText()).split("\n");
|
|
144
|
+
const firstBody = lines.findIndex((line) => line.includes("BODY_01"));
|
|
145
|
+
const blanksInside = lines
|
|
146
|
+
.slice(firstBody, footerRowAt())
|
|
147
|
+
.filter((line) => line.trim().length === 0).length;
|
|
148
|
+
expect(blanksInside, "no pad rows created at idle").toBe(0);
|
|
149
|
+
mounted.unmount();
|
|
150
|
+
});
|
|
151
|
+
it("backfills a bottom-pinned idle shrink with the transcript tail", async () => {
|
|
152
|
+
const recorder = new ScreenRecorder({ columns: COLUMNS, rows: ROWS });
|
|
153
|
+
const stdout = makeRecordingStdout(recorder);
|
|
154
|
+
const mounted = render(_jsx(Frame, { bodyRows: 4 }), {
|
|
155
|
+
stdout,
|
|
156
|
+
columns: COLUMNS,
|
|
157
|
+
rows: ROWS,
|
|
158
|
+
patchConsole: false,
|
|
159
|
+
maxFps: 1000,
|
|
160
|
+
clipFrameToTerminalHeight: true,
|
|
161
|
+
anchorFrameToBottom: true,
|
|
162
|
+
});
|
|
163
|
+
const patched = mounted;
|
|
164
|
+
patched.setFrameAnchorActive(false);
|
|
165
|
+
patched.setFrameShrinkBackfill((rows) => Array.from({ length: rows }, (_, i) => `BACKFILL_${String(i + 1).padStart(2, "0")}`).join("\n"));
|
|
166
|
+
await tick();
|
|
167
|
+
// Fill the screen above the frame with transcript so the frame bottom is
|
|
168
|
+
// pinned at the screen bottom — the chat-in-progress shape.
|
|
169
|
+
patched.insertBeforeFrame(Array.from({ length: ROWS }, (_, i) => `HIST_${String(i + 1).padStart(2, "0")}`).join("\n") +
|
|
170
|
+
"\n");
|
|
171
|
+
// The insert's forced-render fallback fires after 100ms — wait it out so
|
|
172
|
+
// the pinned state is committed before the menu opens.
|
|
173
|
+
await tick();
|
|
174
|
+
await tick();
|
|
175
|
+
await tick();
|
|
176
|
+
const footerRowAt = () => stripAnsi(recorder.fullText())
|
|
177
|
+
.split("\n")
|
|
178
|
+
.findIndex((line) => line.includes(FOOTER));
|
|
179
|
+
const pinnedFooterRow = footerRowAt();
|
|
180
|
+
// Slash menu opens: bottom-pinned idle growth must NOT scroll the
|
|
181
|
+
// terminal — ink repaints the screen in place (cursor home + erase-down +
|
|
182
|
+
// shorter tail + taller frame). A scroll pushes a screenful into native
|
|
183
|
+
// scrollback, and every open/close cycle would deposit one more duplicate
|
|
184
|
+
// copy of those rows (the repeated half-banners). In-place repaint
|
|
185
|
+
// appends NOTHING to the recorder's cumulative buffer — a scroll would
|
|
186
|
+
// grow it by the frame-height delta.
|
|
187
|
+
void pinnedFooterRow;
|
|
188
|
+
const bufferLenBeforeOpen = stripAnsi(recorder.fullText()).split("\n").length;
|
|
189
|
+
mounted.rerender(_jsx(Frame, { bodyRows: 12 }));
|
|
190
|
+
await tick();
|
|
191
|
+
const openLines = stripAnsi(recorder.fullText()).split("\n");
|
|
192
|
+
expect(openLines.length, "menu open repainted in place (no scroll)").toBe(bufferLenBeforeOpen);
|
|
193
|
+
expect(openLines.some((line) => line.includes("BACKFILL_")), "tail visible above the open menu").toBe(true);
|
|
194
|
+
// Slash menu closes: same in-place repaint — taller tail + shorter frame.
|
|
195
|
+
mounted.rerender(_jsx(Frame, { bodyRows: 4 }));
|
|
196
|
+
await tick();
|
|
197
|
+
expect(stripAnsi(recorder.fullText()).split("\n").length, "menu close repainted in place (no scroll)").toBe(bufferLenBeforeOpen);
|
|
198
|
+
// Inspect the visible screen (bottom ROWS of the buffer): footer at the
|
|
199
|
+
// bottom, tail above the frame, no blanks, no duplicated rows.
|
|
200
|
+
const screen = recorder.viewportLines().map((line) => stripAnsi(line));
|
|
201
|
+
const screenFooterIdx = screen.findIndex((line) => line.includes(FOOTER));
|
|
202
|
+
expect(screenFooterIdx, "footer present after repaint").toBeGreaterThanOrEqual(0);
|
|
203
|
+
const lastNonBlank = screen.reduce((acc, line, i) => (line.trim().length > 0 ? i : acc), -1);
|
|
204
|
+
expect(lastNonBlank, "footer is the last content row").toBe(screenFooterIdx);
|
|
205
|
+
// Everything above the frame is transcript tail — each row appears
|
|
206
|
+
// exactly once (no duplicated banner/prompt rows) and none are blank.
|
|
207
|
+
const frameTop = screenFooterIdx - 6; // 4 body + 2 control rows
|
|
208
|
+
const aboveFrame = screen.slice(0, frameTop);
|
|
209
|
+
expect(aboveFrame.every((line) => line.includes("BACKFILL_")), "all rows above the frame are backfill transcript").toBe(true);
|
|
210
|
+
const uniqueRows = new Set(aboveFrame);
|
|
211
|
+
expect(uniqueRows.size, "no duplicated rows above the frame").toBe(aboveFrame.length);
|
|
212
|
+
mounted.unmount();
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
//# sourceMappingURL=frame-clip-regression.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-clip-regression.test.js","sourceRoot":"","sources":["../../src/ui/frame-clip-regression.test.tsx"],"names":[],"mappings":"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"}
|
|
@@ -20,6 +20,14 @@ interface UseTranscriptHistoryOptions<TItem extends TranscriptHistoryItem> {
|
|
|
20
20
|
terminalHistoryPrinter?: TranscriptHistoryPrinter<TItem>;
|
|
21
21
|
terminalHistoryContext: TerminalHistoryContext;
|
|
22
22
|
writeStdout: (data: string) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Atomic scrollback enqueue (patched Ink `insertBeforeFrame`). Bytes passed
|
|
25
|
+
* here are NOT written immediately — they are folded into the next Ink frame
|
|
26
|
+
* write, so the live-frame shrink and the scrollback insert reach the
|
|
27
|
+
* terminal in ONE write and the footer never jumps. Falls back to
|
|
28
|
+
* `writeStdout` (erase frame → write → restore frame) when absent.
|
|
29
|
+
*/
|
|
30
|
+
enqueueStdout?: (data: string) => void;
|
|
23
31
|
sessionPathRef: React.RefObject<string | undefined>;
|
|
24
32
|
sessionManagerRef: React.RefObject<SessionManager | null>;
|
|
25
33
|
sessionStore?: SessionStoreLike<TItem>;
|
|
@@ -43,6 +51,6 @@ export interface UseTranscriptHistoryResult<TItem extends TranscriptHistoryItem>
|
|
|
43
51
|
finalizeSubmittedUserItem: (item: TItem, deferredLiveItems?: readonly TItem[]) => void;
|
|
44
52
|
clearPendingHistory: () => void;
|
|
45
53
|
}
|
|
46
|
-
export declare function useTranscriptHistory<TItem extends TranscriptHistoryItem = CompletedItem>({ terminalHistoryPrinter, terminalHistoryContext, writeStdout, sessionPathRef, sessionManagerRef, sessionStore, history, setHistory, setLiveItems, compactHistoryItems, persistDisplayItem, trimFlushItems, }: UseTranscriptHistoryOptions<TItem>): UseTranscriptHistoryResult<TItem>;
|
|
54
|
+
export declare function useTranscriptHistory<TItem extends TranscriptHistoryItem = CompletedItem>({ terminalHistoryPrinter, terminalHistoryContext, writeStdout, enqueueStdout, sessionPathRef, sessionManagerRef, sessionStore, history, setHistory, setLiveItems, compactHistoryItems, persistDisplayItem, trimFlushItems, }: UseTranscriptHistoryOptions<TItem>): UseTranscriptHistoryResult<TItem>;
|
|
47
55
|
export {};
|
|
48
56
|
//# sourceMappingURL=useTranscriptHistory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTranscriptHistory.d.ts","sourceRoot":"","sources":["../../../src/ui/hooks/useTranscriptHistory.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"useTranscriptHistory.d.ts","sourceRoot":"","sources":["../../../src/ui/hooks/useTranscriptHistory.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react
|
|
|
2
2
|
import { DISPLAY_ITEM_CUSTOM_KIND } from "../../core/session-manager.js";
|
|
3
3
|
import { compactHistory } from "../item-helpers.js";
|
|
4
4
|
import { trimFlushedItems } from "../live-item-flush.js";
|
|
5
|
-
export function useTranscriptHistory({ terminalHistoryPrinter, terminalHistoryContext, writeStdout, sessionPathRef, sessionManagerRef, sessionStore, history, setHistory, setLiveItems, compactHistoryItems = (items) => compactHistory(items), persistDisplayItem, trimFlushItems = (items) => trimFlushedItems(items), }) {
|
|
5
|
+
export function useTranscriptHistory({ terminalHistoryPrinter, terminalHistoryContext, writeStdout, enqueueStdout, sessionPathRef, sessionManagerRef, sessionStore, history, setHistory, setLiveItems, compactHistoryItems = (items) => compactHistory(items), persistDisplayItem, trimFlushItems = (items) => trimFlushedItems(items), }) {
|
|
6
6
|
const terminalHistoryContextRef = useRef(terminalHistoryContext);
|
|
7
7
|
const pendingHistoryFlushRef = useRef([]);
|
|
8
8
|
const drainedHistoryFlushRef = useRef([]);
|
|
@@ -20,14 +20,20 @@ export function useTranscriptHistory({ terminalHistoryPrinter, terminalHistoryCo
|
|
|
20
20
|
return;
|
|
21
21
|
terminalHistoryPrinter.print(items, terminalHistoryContextRef.current, {
|
|
22
22
|
...options,
|
|
23
|
-
write: writeStdout,
|
|
23
|
+
write: enqueueStdout ?? writeStdout,
|
|
24
24
|
});
|
|
25
|
-
}, [terminalHistoryPrinter, writeStdout]);
|
|
25
|
+
}, [enqueueStdout, terminalHistoryPrinter, writeStdout]);
|
|
26
26
|
const queueFlush = useCallback((items) => {
|
|
27
27
|
const flushed = trimFlushItems(items);
|
|
28
28
|
if (flushed.length === 0)
|
|
29
29
|
return;
|
|
30
30
|
pendingHistoryFlushRef.current = [...pendingHistoryFlushRef.current, ...flushed];
|
|
31
|
+
// Render the rows to ANSI and enqueue the bytes NOW, before any state
|
|
32
|
+
// update. With the patched `insertBeforeFrame` the enqueue is passive (no
|
|
33
|
+
// terminal write), so the bytes ride the very next frame write — the one
|
|
34
|
+
// produced by the batched setLiveItems/generation updates below, which is
|
|
35
|
+
// also the write that shrinks the live frame. One write = no footer jump.
|
|
36
|
+
printHistoryItems(flushed);
|
|
31
37
|
const sessionPath = sessionPathRef.current;
|
|
32
38
|
const sessionManager = sessionManagerRef.current;
|
|
33
39
|
if (sessionPath && sessionManager) {
|
|
@@ -49,26 +55,37 @@ export function useTranscriptHistory({ terminalHistoryPrinter, terminalHistoryCo
|
|
|
49
55
|
const queuedIds = new Set(items.map((item) => item.id));
|
|
50
56
|
sessionStore.liveItems = (sessionStore.liveItems ?? []).filter((item) => !queuedIds.has(item.id));
|
|
51
57
|
}
|
|
58
|
+
// Remove the flushed rows from the live frame in the SAME React batch as
|
|
59
|
+
// the generation bump. Ink emits one frame write per commit (throttled,
|
|
60
|
+
// leading edge), so deferring this shrink to a later effect would split
|
|
61
|
+
// enqueue and shrink across two writes — the second one (shrink without
|
|
62
|
+
// compensating scrollback bytes) is exactly what strands the footer.
|
|
63
|
+
const flushedIds = new Set(flushed.map((item) => item.id));
|
|
64
|
+
setLiveItems((prev) => prev.filter((item) => !flushedIds.has(item.id)));
|
|
52
65
|
setHistoryFlushGeneration((generation) => generation + 1);
|
|
53
|
-
}, [
|
|
66
|
+
}, [
|
|
67
|
+
persistDisplayItem,
|
|
68
|
+
printHistoryItems,
|
|
69
|
+
sessionManagerRef,
|
|
70
|
+
sessionPathRef,
|
|
71
|
+
sessionStore,
|
|
72
|
+
setLiveItems,
|
|
73
|
+
trimFlushItems,
|
|
74
|
+
]);
|
|
54
75
|
useEffect(() => {
|
|
55
76
|
printHistoryItems(history);
|
|
56
77
|
}, [history, printHistoryItems]);
|
|
57
78
|
useLayoutEffect(() => {
|
|
79
|
+
// Printing and live-row removal already happened synchronously inside
|
|
80
|
+
// queueFlush (they must batch into the commit that carries the enqueued
|
|
81
|
+
// scrollback bytes). This effect only moves the queue along so the
|
|
82
|
+
// follow-up effect below can fold the flushed rows into React history.
|
|
58
83
|
const flushed = pendingHistoryFlushRef.current;
|
|
59
84
|
if (flushed.length === 0)
|
|
60
85
|
return;
|
|
61
|
-
// Keep the visible row continuous during finalization: write the completed
|
|
62
|
-
// transcript into terminal history while Ink still knows about the current
|
|
63
|
-
// live frame, then remove those live rows. Doing this in the opposite order
|
|
64
|
-
// collapses the live frame to the controls first, so a large final history
|
|
65
|
-
// write scrolls underneath the footer/input and visibly pushes them upward.
|
|
66
86
|
pendingHistoryFlushRef.current = [];
|
|
67
87
|
drainedHistoryFlushRef.current = flushed;
|
|
68
|
-
|
|
69
|
-
const flushedIds = new Set(flushed.map((item) => item.id));
|
|
70
|
-
setLiveItems((prev) => prev.filter((item) => !flushedIds.has(item.id)));
|
|
71
|
-
}, [historyFlushGeneration, printHistoryItems, setLiveItems]);
|
|
88
|
+
}, [historyFlushGeneration]);
|
|
72
89
|
useEffect(() => {
|
|
73
90
|
const flushed = drainedHistoryFlushRef.current;
|
|
74
91
|
if (flushed.length === 0)
|
|
@@ -89,14 +106,12 @@ export function useTranscriptHistory({ terminalHistoryPrinter, terminalHistoryCo
|
|
|
89
106
|
streamedAssistantFlushRef.current = { flushedChars: 0, text: "" };
|
|
90
107
|
const priorLiveItems = trimFlushItems([...deferredLiveItems]);
|
|
91
108
|
const finalizedItems = [...priorLiveItems, item];
|
|
109
|
+
// queueFlush renders + enqueues the rows synchronously (the printer
|
|
110
|
+
// dedupes by id), so the deferred final assistant output is anchored in
|
|
111
|
+
// scrollback before the next prompt clears the live frame.
|
|
92
112
|
queueFlush(finalizedItems);
|
|
93
|
-
// Print synchronously so deferred final assistant output is anchored in
|
|
94
|
-
// scrollback before the next prompt clears the live frame. The queued flush
|
|
95
|
-
// still persists and updates React history; the printer dedupes by id when
|
|
96
|
-
// the effect drains the queue.
|
|
97
|
-
printHistoryItems(finalizedItems);
|
|
98
113
|
setLiveItems([]);
|
|
99
|
-
}, [
|
|
114
|
+
}, [queueFlush, setLiveItems, trimFlushItems]);
|
|
100
115
|
const clearPendingHistory = useCallback(() => {
|
|
101
116
|
pendingHistoryFlushRef.current = [];
|
|
102
117
|
terminalHistoryPrinter?.clear();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTranscriptHistory.js","sourceRoot":"","sources":["../../../src/ui/hooks/useTranscriptHistory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAClF,OAAO,EAAE,wBAAwB,EAAuB,MAAM,+BAA+B,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"useTranscriptHistory.js","sourceRoot":"","sources":["../../../src/ui/hooks/useTranscriptHistory.ts"],"names":[],"mappings":"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"}
|
|
@@ -48,6 +48,27 @@ export declare function flushOverflow<T extends FlushableItem>(liveItems: T[]):
|
|
|
48
48
|
flushed: T[];
|
|
49
49
|
remaining: T[];
|
|
50
50
|
};
|
|
51
|
+
/**
|
|
52
|
+
* When a turn finalizes, decide how many leading items must be flushed to
|
|
53
|
+
* scrollback immediately because the pinned frame is too tall for the live
|
|
54
|
+
* area. Finalized items lose the streaming-time table/code-block clamp, so a
|
|
55
|
+
* too-tall pinned frame exceeds the terminal and Ink can only paint its
|
|
56
|
+
* bottom rows — the top of the response (e.g. a table's header) is never
|
|
57
|
+
* painted anywhere until the next turn's flush finally writes it to
|
|
58
|
+
* scrollback.
|
|
59
|
+
*
|
|
60
|
+
* The budget is CUMULATIVE: a turn segmented on [DONE:N] markers pins several
|
|
61
|
+
* assistant items whose individual heights fit while their sum overflows.
|
|
62
|
+
* Walking from the end, keep the largest suffix that fits within
|
|
63
|
+
* `liveAreaRows` and flush the leading complement — a contiguous prefix, so
|
|
64
|
+
* transcript order is preserved. An item taller than the whole budget flushes
|
|
65
|
+
* itself and everything before it. Returns 0 when everything fits or the
|
|
66
|
+
* live area hasn't been measured yet.
|
|
67
|
+
*/
|
|
68
|
+
export declare function countOversizedFlushItems(items: readonly {
|
|
69
|
+
kind: string;
|
|
70
|
+
text?: string;
|
|
71
|
+
}[], estimateRows: (text: string) => number, liveAreaRows: number): number;
|
|
51
72
|
/**
|
|
52
73
|
* Called when `onTurnEnd` fires with a tool_use stop reason (LLM responded
|
|
53
74
|
* with only tool calls, no text). Flushes all items IF none are still pending
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live-item-flush.d.ts","sourceRoot":"","sources":["../../src/ui/live-item-flush.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAUrC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAG/C;AAQD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,aAAa,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CA0DzE;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,aAAa,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAE5E;AAaD,wBAAgB,aAAa,CAAC,CAAC,SAAS,aAAa,EACnD,SAAS,EAAE,CAAC,EAAE,GACb;IAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAAC,SAAS,EAAE,CAAC,EAAE,CAAA;CAAE,CAmClC;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,aAAa,EACpD,SAAS,EAAE,CAAC,EAAE,EACd,UAAU,EAAE,MAAM,GACjB;IAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAAC,SAAS,EAAE,CAAC,EAAE,CAAA;CAAE,CAuBlC"}
|
|
1
|
+
{"version":3,"file":"live-item-flush.d.ts","sourceRoot":"","sources":["../../src/ui/live-item-flush.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAUrC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAG/C;AAQD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,aAAa,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CA0DzE;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,aAAa,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAE5E;AAaD,wBAAgB,aAAa,CAAC,CAAC,SAAS,aAAa,EACnD,SAAS,EAAE,CAAC,EAAE,GACb;IAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAAC,SAAS,EAAE,CAAC,EAAE,CAAA;CAAE,CAmClC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,EACjD,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EACtC,YAAY,EAAE,MAAM,GACnB,MAAM,CAeR;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,aAAa,EACpD,SAAS,EAAE,CAAC,EAAE,EACd,UAAU,EAAE,MAAM,GACjB;IAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAAC,SAAS,EAAE,CAAC,EAAE,CAAA;CAAE,CAuBlC"}
|
|
@@ -148,6 +148,41 @@ export function flushOverflow(liveItems) {
|
|
|
148
148
|
const candidates = liveItems.slice(0, splitAt);
|
|
149
149
|
return { flushed: candidates, remaining: liveItems.slice(splitAt) };
|
|
150
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* When a turn finalizes, decide how many leading items must be flushed to
|
|
153
|
+
* scrollback immediately because the pinned frame is too tall for the live
|
|
154
|
+
* area. Finalized items lose the streaming-time table/code-block clamp, so a
|
|
155
|
+
* too-tall pinned frame exceeds the terminal and Ink can only paint its
|
|
156
|
+
* bottom rows — the top of the response (e.g. a table's header) is never
|
|
157
|
+
* painted anywhere until the next turn's flush finally writes it to
|
|
158
|
+
* scrollback.
|
|
159
|
+
*
|
|
160
|
+
* The budget is CUMULATIVE: a turn segmented on [DONE:N] markers pins several
|
|
161
|
+
* assistant items whose individual heights fit while their sum overflows.
|
|
162
|
+
* Walking from the end, keep the largest suffix that fits within
|
|
163
|
+
* `liveAreaRows` and flush the leading complement — a contiguous prefix, so
|
|
164
|
+
* transcript order is preserved. An item taller than the whole budget flushes
|
|
165
|
+
* itself and everything before it. Returns 0 when everything fits or the
|
|
166
|
+
* live area hasn't been measured yet.
|
|
167
|
+
*/
|
|
168
|
+
export function countOversizedFlushItems(items, estimateRows, liveAreaRows) {
|
|
169
|
+
if (liveAreaRows <= 0)
|
|
170
|
+
return 0;
|
|
171
|
+
let rows = 0;
|
|
172
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
173
|
+
const item = items[i];
|
|
174
|
+
if (item === undefined)
|
|
175
|
+
continue;
|
|
176
|
+
// Non-assistant rows (step_done, etc.) are single-line; assistant items
|
|
177
|
+
// are estimated from their text. +1 approximates the transcript margin
|
|
178
|
+
// between items — over-counting just flushes marginally earlier.
|
|
179
|
+
const itemRows = item.kind === "assistant" && typeof item.text === "string" ? estimateRows(item.text) : 1;
|
|
180
|
+
rows += itemRows + (i < items.length - 1 ? 1 : 0);
|
|
181
|
+
if (rows > liveAreaRows)
|
|
182
|
+
return i + 1;
|
|
183
|
+
}
|
|
184
|
+
return 0;
|
|
185
|
+
}
|
|
151
186
|
/**
|
|
152
187
|
* Called when `onTurnEnd` fires with a tool_use stop reason (LLM responded
|
|
153
188
|
* with only tool calls, no text). Flushes all items IF none are still pending
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live-item-flush.js","sourceRoot":"","sources":["../../src/ui/live-item-flush.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"live-item-flush.js","sourceRoot":"","sources":["../../src/ui/live-item-flush.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { pruneHistory, flushOnTurnText, flushOnTurnEnd, MAX_HISTORY_ITEMS, } from "./live-item-flush.js";
|
|
2
|
+
import { pruneHistory, flushOnTurnText, flushOnTurnEnd, countOversizedFlushItems, MAX_HISTORY_ITEMS, } from "./live-item-flush.js";
|
|
3
|
+
import { estimateRenderedRows } from "./utils/assistant-stream-split.js";
|
|
3
4
|
// ── Test helpers ──────────────────────────────────────────
|
|
4
5
|
let idCounter = 0;
|
|
5
6
|
function id() {
|
|
@@ -103,6 +104,76 @@ describe("flushOnTurnText", () => {
|
|
|
103
104
|
expect(flushed).toHaveLength(2);
|
|
104
105
|
});
|
|
105
106
|
});
|
|
107
|
+
// ── countOversizedFlushItems ──────────────────────────────
|
|
108
|
+
describe("countOversizedFlushItems", () => {
|
|
109
|
+
const LIVE_ROWS = 20;
|
|
110
|
+
const COLUMNS = 80;
|
|
111
|
+
const estimate = (text) => estimateRenderedRows(text, COLUMNS);
|
|
112
|
+
const shortText = { kind: "assistant", text: "A short answer.", id: "short" };
|
|
113
|
+
const stepDone = { kind: "step_done", id: "step" };
|
|
114
|
+
// A markdown table tall enough to exceed the live area: header + separator
|
|
115
|
+
// + 30 rows, double-counted by the table heuristic.
|
|
116
|
+
const tallTable = {
|
|
117
|
+
kind: "assistant",
|
|
118
|
+
text: [
|
|
119
|
+
"| Feature | Detail |",
|
|
120
|
+
"| --- | --- |",
|
|
121
|
+
...Array.from({ length: 30 }, (_, i) => `| Row ${i} | some detail text |`),
|
|
122
|
+
].join("\n"),
|
|
123
|
+
id: "table",
|
|
124
|
+
};
|
|
125
|
+
it("returns 0 when every assistant item fits the live area", () => {
|
|
126
|
+
expect(countOversizedFlushItems([shortText, stepDone], estimate, LIVE_ROWS)).toBe(0);
|
|
127
|
+
});
|
|
128
|
+
it("flushes through the oversized assistant item (the table-cut-off bug)", () => {
|
|
129
|
+
expect(countOversizedFlushItems([tallTable], estimate, LIVE_ROWS)).toBe(1);
|
|
130
|
+
});
|
|
131
|
+
it("flushes earlier siblings too, preserving transcript order", () => {
|
|
132
|
+
// [short, step_done, tallTable, short] — flushing only the table would
|
|
133
|
+
// print it to scrollback BEFORE the pinned earlier items get flushed on
|
|
134
|
+
// the next turn, corrupting order. Cut must include everything up to and
|
|
135
|
+
// including the last oversized item.
|
|
136
|
+
const items = [shortText, stepDone, tallTable, { ...shortText, id: "tail" }];
|
|
137
|
+
expect(countOversizedFlushItems(items, estimate, LIVE_ROWS)).toBe(3);
|
|
138
|
+
});
|
|
139
|
+
it("ignores non-assistant items even with long text", () => {
|
|
140
|
+
const longError = { kind: "error", text: "x".repeat(10_000), id: "err" };
|
|
141
|
+
expect(countOversizedFlushItems([longError], estimate, LIVE_ROWS)).toBe(0);
|
|
142
|
+
});
|
|
143
|
+
it("returns 0 when the live area has not been measured yet", () => {
|
|
144
|
+
expect(countOversizedFlushItems([tallTable], estimate, 0)).toBe(0);
|
|
145
|
+
});
|
|
146
|
+
it("returns 0 for an empty item list", () => {
|
|
147
|
+
expect(countOversizedFlushItems([], estimate, LIVE_ROWS)).toBe(0);
|
|
148
|
+
});
|
|
149
|
+
it("flushes a tall plain-text response too (no table required)", () => {
|
|
150
|
+
const tallText = {
|
|
151
|
+
kind: "assistant",
|
|
152
|
+
text: Array.from({ length: 40 }, (_, i) => `line ${i}`).join("\n"),
|
|
153
|
+
id: "tall-text",
|
|
154
|
+
};
|
|
155
|
+
expect(countOversizedFlushItems([tallText], estimate, LIVE_ROWS)).toBe(1);
|
|
156
|
+
});
|
|
157
|
+
it("CUMULATIVE: several medium items that fit individually but overflow together", () => {
|
|
158
|
+
// [DONE:N] segmentation pins multiple assistant items per turn. Three
|
|
159
|
+
// 10-row items each fit a 20-row live area alone, but the pinned set is
|
|
160
|
+
// ~32 rows — same top-rows-never-painted bug as a single tall table.
|
|
161
|
+
const medium = (id) => ({
|
|
162
|
+
kind: "assistant",
|
|
163
|
+
text: Array.from({ length: 10 }, (_, i) => `para line ${i}`).join("\n"),
|
|
164
|
+
id,
|
|
165
|
+
});
|
|
166
|
+
const items = [medium("a"), { kind: "step_done", id: "s" }, medium("b"), medium("c")];
|
|
167
|
+
// Suffix that fits: [medium c] (10+0) + [medium b] (10+1) = 21 > 20, so
|
|
168
|
+
// only "c" survives — flush the first three.
|
|
169
|
+
expect(countOversizedFlushItems(items, estimate, LIVE_ROWS)).toBe(3);
|
|
170
|
+
});
|
|
171
|
+
it("CUMULATIVE: keeps the whole set when the sum fits the budget", () => {
|
|
172
|
+
const small = (id) => ({ kind: "assistant", text: "one line", id });
|
|
173
|
+
const items = [small("a"), { kind: "step_done", id: "s" }, small("b")];
|
|
174
|
+
expect(countOversizedFlushItems(items, estimate, LIVE_ROWS)).toBe(0);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
106
177
|
// ── flushOnTurnEnd ────────────────────────────────────────
|
|
107
178
|
describe("flushOnTurnEnd", () => {
|
|
108
179
|
describe("with tool_use stop reason", () => {
|