mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-07 02:51:50 +00:00
fix(tui): keep streaming progress stable during interaction
This commit is contained in:
parent
1c964ed43f
commit
355e0ae960
15 changed files with 278 additions and 106 deletions
28
ui-tui/src/__tests__/interactionMode.test.ts
Normal file
28
ui-tui/src/__tests__/interactionMode.test.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { getInteractionMode, markScrolling, markTyping, resetInteractionMode } from '../app/interactionMode.js'
|
||||
import { SCROLLING_IDLE_MS, TYPING_IDLE_MS } from '../config/timing.js'
|
||||
|
||||
describe('interactionMode', () => {
|
||||
afterEach(() => {
|
||||
resetInteractionMode()
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('holds scrolling mode briefly then returns idle', () => {
|
||||
vi.useFakeTimers()
|
||||
markScrolling()
|
||||
expect(getInteractionMode()).toBe('scrolling')
|
||||
vi.advanceTimersByTime(SCROLLING_IDLE_MS)
|
||||
expect(getInteractionMode()).toBe('idle')
|
||||
})
|
||||
|
||||
it('typing takes priority over scrolling', () => {
|
||||
vi.useFakeTimers()
|
||||
markTyping()
|
||||
markScrolling()
|
||||
expect(getInteractionMode()).toBe('typing')
|
||||
vi.advanceTimersByTime(TYPING_IDLE_MS)
|
||||
expect(getInteractionMode()).toBe('idle')
|
||||
})
|
||||
})
|
||||
53
ui-tui/src/__tests__/scroll.test.ts
Normal file
53
ui-tui/src/__tests__/scroll.test.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { scrollWithSelectionBy } from '../app/scroll.js'
|
||||
|
||||
function makeScroll(overrides: Partial<Record<string, unknown>> = {}) {
|
||||
return {
|
||||
getPendingDelta: vi.fn(() => 0),
|
||||
getScrollHeight: vi.fn(() => 100),
|
||||
getScrollTop: vi.fn(() => 10),
|
||||
getViewportHeight: vi.fn(() => 20),
|
||||
getViewportTop: vi.fn(() => 0),
|
||||
scrollBy: vi.fn(),
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
describe('scrollWithSelectionBy', () => {
|
||||
it('clamps to the actual remaining scroll distance before calling scrollBy', () => {
|
||||
const s = makeScroll({
|
||||
getScrollHeight: vi.fn(() => 30),
|
||||
getScrollTop: vi.fn(() => 9),
|
||||
getViewportHeight: vi.fn(() => 20)
|
||||
})
|
||||
const selection = {
|
||||
captureScrolledRows: vi.fn(),
|
||||
getState: vi.fn(() => null),
|
||||
shiftAnchor: vi.fn(),
|
||||
shiftSelection: vi.fn()
|
||||
}
|
||||
|
||||
scrollWithSelectionBy(10, { scrollRef: { current: s as never }, selection })
|
||||
|
||||
expect(s.scrollBy).toHaveBeenCalledWith(1)
|
||||
})
|
||||
|
||||
it('does nothing at the edge instead of queueing dead pending deltas', () => {
|
||||
const s = makeScroll({
|
||||
getScrollHeight: vi.fn(() => 30),
|
||||
getScrollTop: vi.fn(() => 10),
|
||||
getViewportHeight: vi.fn(() => 20)
|
||||
})
|
||||
const selection = {
|
||||
captureScrolledRows: vi.fn(),
|
||||
getState: vi.fn(() => null),
|
||||
shiftAnchor: vi.fn(),
|
||||
shiftSelection: vi.fn()
|
||||
}
|
||||
|
||||
scrollWithSelectionBy(10, { scrollRef: { current: s as never }, selection })
|
||||
|
||||
expect(s.scrollBy).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
13
ui-tui/src/__tests__/virtualHistoryClamp.test.ts
Normal file
13
ui-tui/src/__tests__/virtualHistoryClamp.test.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { shouldSetVirtualClamp } from '../hooks/useVirtualHistory.js'
|
||||
|
||||
describe('virtual history clamp bounds', () => {
|
||||
it('does not clamp sticky live tail content', () => {
|
||||
expect(shouldSetVirtualClamp({ itemCount: 20, sticky: true, viewportHeight: 10 })).toBe(false)
|
||||
})
|
||||
|
||||
it('sets clamp bounds after manual scroll breaks sticky mode', () => {
|
||||
expect(shouldSetVirtualClamp({ itemCount: 20, sticky: false, viewportHeight: 10 })).toBe(true)
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue