mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-29 03:44:06 +08:00
fix(jetbrains): gate reflow settle window on Busy, not isBusy()
isBusy() is true for awaiting-permission/question, retry, and offline — states recoverPending() can seed right after history load, where no deltas arrive and a moving height genuinely means the panes are still settling. Gating the settle-window shortcut on those states cut the reflow chain to REFLOW_PASSES and reintroduced the crop-until-resize bug on exactly that path. Gate only on SessionState.Busy (the streaming state) so the runaway fix stays while blocked/retry/offline sessions keep their full settle window. Add a Busy-state test that fails if the term is dropped or inverted.
This commit is contained in:
+8
-6
@@ -553,12 +553,14 @@ class SessionMessageListPanel(
|
||||
return
|
||||
}
|
||||
val height = preferredSize.height
|
||||
// A moving height means the layout is still settling only while the session is idle. During
|
||||
// streaming it just tracks incoming content, so restarting the settle window on every delta
|
||||
// was the runaway that pinned the panel in a perpetual forgetAll()/re-measure loop. Count the
|
||||
// pass down while busy so streaming settles in REFLOW_PASSES and hands off to the per-turn
|
||||
// forgetTurn path; [budget] still caps the idle case if a pane never converges.
|
||||
val left = if (height == stable || model.state.isBusy()) remaining - 1 else REFLOW_PASSES
|
||||
// A moving height only means the layout is still settling when nothing is streaming in. While
|
||||
// [SessionState.Busy] deltas land every EDT cycle, so restarting the settle window on each one
|
||||
// was the runaway that pinned the panel in a perpetual forgetAll()/re-measure loop — count the
|
||||
// pass down instead so streaming settles in REFLOW_PASSES and hands off to the per-turn
|
||||
// forgetTurn path. Every other state (idle, awaiting-permission/question, retry, offline —
|
||||
// which recoverPending() can seed right after load) has no deltas arriving, so a moving height
|
||||
// is genuine convergence and must keep restarting; [budget] caps that if a pane never settles.
|
||||
val left = if (height == stable || model.state is SessionState.Busy) remaining - 1 else REFLOW_PASSES
|
||||
stable = height
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
reflowPass(id, left, budget - 1)
|
||||
|
||||
+20
@@ -221,6 +221,26 @@ class SessionMessageListPanelTest : BasePlatformTestCase() {
|
||||
assertTrue("reflow passes must be bounded by the budget, was $reflows", reflows in 1..30)
|
||||
}
|
||||
|
||||
fun `test streaming session settles reflow within the pass window`() {
|
||||
var reflows = 0
|
||||
panel.onReflow = { reflows++ }
|
||||
// Same ever-growing child, but a streaming (Busy) session: a moving height is incoming
|
||||
// content, not the layout still settling, so the chain must count its passes down and stop
|
||||
// after REFLOW_PASSES instead of restarting the settle window and draining the full budget
|
||||
// the idle case relies on. recoverPending()'s non-Busy states (awaiting-permission, retry,
|
||||
// offline) intentionally keep the idle settle behavior and are not gated here.
|
||||
model.loadHistory(listOf(MessageWithPartsDto(msg("u1", "user"), emptyList())))
|
||||
model.setState(SessionState.Busy("thinking"))
|
||||
panel.add(EverGrowing(), 0)
|
||||
panel.setSize(600, 400)
|
||||
|
||||
UIUtil.dispatchAllInvocationEvents()
|
||||
|
||||
// A handful of passes (~REFLOW_PASSES), well short of the idle budget (~25). Dropping or
|
||||
// inverting the Busy term restarts the window every pass and blows past this bound.
|
||||
assertTrue("streaming reflow must settle in the pass window, was $reflows", reflows in 1..10)
|
||||
}
|
||||
|
||||
fun `test apply style drops cached panel measurements`() {
|
||||
val child = Growing(20)
|
||||
panel.add(child, 0)
|
||||
|
||||
Reference in New Issue
Block a user