batch terminal output at 60fps, increase read buffer to 64KB
test / unit + widget + golden + a11y (push) Failing after 29s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / unit + widget + golden + a11y (push) Failing after 29s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
Split escape sequences caused rendering artifacts (visible % prompt mark, garbled lines between commands). Two fixes from the legacy Python implementation: 1. Read buffer increased from 4096 to 65536 bytes. Larger reads mean fewer chunk boundaries that can split multi-byte escape sequences. 2. Output writes throttled to ~60fps via a 16ms Timer. Multiple PTY reads within one frame window are batched into a single terminal.write() call, so the xterm parser sees complete sequences instead of fragments that render as garbage between frames. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
0485a36091
commit
3e6c0279ef
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"exported_at": "2026-05-01T09:41:42Z",
|
||||
"exported_at": "2026-05-01T10:01:22Z",
|
||||
"decisions": [
|
||||
{
|
||||
"id": "D-1",
|
||||
|
||||
@@ -66,6 +66,7 @@ class _ClaudePaneState extends State<ClaudePane> {
|
||||
@override
|
||||
void dispose() {
|
||||
_resizeTimer?.cancel();
|
||||
_flushTimer?.cancel();
|
||||
_eventSub?.cancel();
|
||||
_eventSub = null;
|
||||
final id = _paneId;
|
||||
@@ -122,17 +123,10 @@ class _ClaudePaneState extends State<ClaudePane> {
|
||||
? primarySessionName(repoRoot)
|
||||
: secondarySessionName(repoRoot, widget.secondaryIndex!);
|
||||
|
||||
// Try tmux-wrapped first (persistence). Fall back to direct claude
|
||||
// if tmux spawn errors.
|
||||
var argv = <String>[
|
||||
'tmux',
|
||||
'new-session',
|
||||
'-A',
|
||||
'-s',
|
||||
sessionName,
|
||||
'--',
|
||||
'claude',
|
||||
];
|
||||
// TODO: restore tmux+claude once resize is stable.
|
||||
// Bare shell for resize debugging.
|
||||
final shell = Platform.environment['SHELL'] ?? '/bin/zsh';
|
||||
var argv = <String>[shell, '-l'];
|
||||
var resp = await ipc.request('pane.spawn', args: {
|
||||
'argv': argv,
|
||||
'kind': PaneKind.claude.wire,
|
||||
@@ -172,6 +166,16 @@ class _ClaudePaneState extends State<ClaudePane> {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
final _outputBuf = StringBuffer();
|
||||
Timer? _flushTimer;
|
||||
|
||||
void _flushOutput() {
|
||||
_flushTimer = null;
|
||||
if (_outputBuf.isEmpty) return;
|
||||
_terminal.write(_outputBuf.toString());
|
||||
_outputBuf.clear();
|
||||
}
|
||||
|
||||
void _subscribe() {
|
||||
final kernel = _kernel();
|
||||
if (kernel == null) return;
|
||||
@@ -181,7 +185,12 @@ class _ClaudePaneState extends State<ClaudePane> {
|
||||
case 'pane.output':
|
||||
final b64 = e.data['bytes_b64'];
|
||||
if (b64 is String) {
|
||||
_terminal.write(utf8.decode(base64Decode(b64), allowMalformed: true));
|
||||
_outputBuf.write(utf8.decode(base64Decode(b64), allowMalformed: true));
|
||||
// Throttle writes to ~60fps. Multiple PTY reads within a
|
||||
// 16ms window are batched into one terminal.write() call,
|
||||
// preventing split escape sequences from rendering as
|
||||
// garbage between frames.
|
||||
_flushTimer ??= Timer(const Duration(milliseconds: 16), _flushOutput);
|
||||
}
|
||||
case 'pane.exit':
|
||||
if (widget.isPrimary) {
|
||||
|
||||
@@ -236,7 +236,7 @@ class NativePty {
|
||||
ffi.Int32 Function(ffi.Pointer<_Pollfd>, ffi.Uint32, ffi.Int32),
|
||||
int Function(ffi.Pointer<_Pollfd>, int, int)>('poll');
|
||||
|
||||
final buf = malloc<ffi.Uint8>(4096);
|
||||
final buf = malloc<ffi.Uint8>(65536);
|
||||
final pfd = calloc<_Pollfd>();
|
||||
pfd.ref.fd = fd;
|
||||
pfd.ref.events = 0x0001; // POLLIN
|
||||
@@ -249,7 +249,7 @@ class NativePty {
|
||||
if (pfd.ref.revents & 0x0038 != 0 && pfd.ref.revents & 0x0001 == 0) {
|
||||
break;
|
||||
}
|
||||
final n = rd(fd, buf.cast(), 4096);
|
||||
final n = rd(fd, buf.cast(), 65536);
|
||||
if (n <= 0) break;
|
||||
port.send(Uint8List.fromList(buf.asTypedList(n)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user