fix Claude pane hang: cap tail, parse off-isolate, coalesce notifies
test / unit + widget + golden + a11y (push) Failing after 27s
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 / dart doc (lib API) (push) Failing after 27s
test / unit + widget + golden + a11y (push) Failing after 27s
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 / dart doc (lib API) (push) Failing after 27s
The native Claude pane (T-137) picks the newest transcript JSONL by mtime — which on a live session is this multi-MB active file. On attach it read and parsed the whole file synchronously on the UI isolate and fired notifyListeners per item, freezing the app. Three fixes: cap the initial read to a recent tail (256KB, injectable for tests); run JSON parsing in a background isolate via Isolate.run; coalesce the controller's notifications with a zero-duration Timer so a burst collapses into one rebuild (a microtask-scheduled notify wouldn't — stream events deliver one per microtask and the notify interleaves). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,8 @@ class ConversationController extends ChangeNotifier {
|
||||
final Future<void> Function()? _onDispose;
|
||||
late final StreamSubscription<ConversationItem> _sub;
|
||||
final List<ConversationItem> _items = [];
|
||||
Timer? _notifyTimer;
|
||||
bool _disposed = false;
|
||||
|
||||
/// Items in arrival (transcript) order.
|
||||
List<ConversationItem> get items => List.unmodifiable(_items);
|
||||
@@ -41,11 +43,22 @@ class ConversationController extends ChangeNotifier {
|
||||
|
||||
void _onItem(ConversationItem item) {
|
||||
_items.add(item);
|
||||
notifyListeners();
|
||||
// Coalesce notifications: the reader emits a burst (the initial tail
|
||||
// read), and a notify-per-item would thrash the view's rebuild +
|
||||
// auto-scroll. A zero-duration Timer fires only after the microtask
|
||||
// queue drains — the stream delivers one event per microtask, so a
|
||||
// microtask-scheduled notify would interleave between deliveries and
|
||||
// fire per item. The timer collapses a whole burst into one rebuild.
|
||||
_notifyTimer ??= Timer(Duration.zero, () {
|
||||
_notifyTimer = null;
|
||||
if (!_disposed) notifyListeners();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_disposed = true;
|
||||
_notifyTimer?.cancel();
|
||||
unawaited(_sub.cancel());
|
||||
unawaited(_onDispose?.call());
|
||||
super.dispose();
|
||||
|
||||
Reference in New Issue
Block a user