mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-28 06:51:16 +08:00
fix(tui): address Copilot review comments
- stringWidth: true LRU on cache hit (touch-on-read via delete+set) so hot strings stay resident under long sessions; was insertion-order FIFO before - virtualHeights: include todos, panel sections, and intro version in messageHeightKey so height-cache reuse correctly invalidates when todo content / panel sections change - virtualHeights: estimate trail+todos rows at todos.length+2 (or 2 collapsed) instead of the generic ~1-line fallback, so initial virtualization offsets are closer to reality - useInputHandlers: clearTimeout on unmount for scrollIdleTimer so pending relaxStreaming() never fires after teardown - render-node-to-output: drop unused declined.noHint counter from scrollFastPathStats; it was always 0 (the "hint missing" branch is outside the diagnostics block) - perfPane / hermes-ink.d.ts: follow the noHint removal - wheelAccel: replace ~/claude-code path comment with generic attribution that doesn't reference a developer-local checkout
This commit is contained in:
@@ -79,7 +79,6 @@ export type ScrollFastPathStats = {
|
||||
declined: {
|
||||
noPrevScreen: number
|
||||
heightDeltaMismatch: number
|
||||
noHint: number
|
||||
other: number
|
||||
}
|
||||
lastDeclineReason?: string
|
||||
@@ -95,7 +94,6 @@ export const scrollFastPathStats: ScrollFastPathStats = {
|
||||
declined: {
|
||||
noPrevScreen: 0,
|
||||
heightDeltaMismatch: 0,
|
||||
noHint: 0,
|
||||
other: 0
|
||||
}
|
||||
}
|
||||
@@ -105,7 +103,6 @@ export function resetScrollFastPathStats(): void {
|
||||
scrollFastPathStats.taken = 0
|
||||
scrollFastPathStats.declined.noPrevScreen = 0
|
||||
scrollFastPathStats.declined.heightDeltaMismatch = 0
|
||||
scrollFastPathStats.declined.noHint = 0
|
||||
scrollFastPathStats.declined.other = 0
|
||||
scrollFastPathStats.lastDeclineReason = undefined
|
||||
scrollFastPathStats.lastHeightDelta = undefined
|
||||
|
||||
@@ -311,13 +311,16 @@ export const stringWidth: (str: string) => number = str => {
|
||||
const cached = widthCache.get(str)
|
||||
|
||||
if (cached !== undefined) {
|
||||
// True LRU: refresh recency by re-inserting (Map iteration is insertion order).
|
||||
widthCache.delete(str)
|
||||
widthCache.set(str, cached)
|
||||
|
||||
return cached
|
||||
}
|
||||
|
||||
const w = rawStringWidth(str)
|
||||
|
||||
if (widthCache.size >= WIDTH_CACHE_LIMIT) {
|
||||
// Drop oldest entry — Map iteration order is insertion order.
|
||||
widthCache.delete(widthCache.keys().next().value!)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useInput } from '@hermes/ink'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { useRef } from 'react'
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import { TYPING_IDLE_MS } from '../config/timing.js'
|
||||
import type {
|
||||
@@ -40,6 +40,16 @@ export function useInputHandlers(ctx: InputHandlerContext): InputHandlerResult {
|
||||
// as the BASE — final rows = wheelStep × accelMult.
|
||||
const wheelAccelRef = useRef(initWheelAccelForHost())
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
if (scrollIdleTimer.current) {
|
||||
clearTimeout(scrollIdleTimer.current)
|
||||
scrollIdleTimer.current = null
|
||||
}
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
const scrollTranscript = (delta: number) => {
|
||||
if (getUiState().busy) {
|
||||
turnController.boostStreamingForScroll()
|
||||
|
||||
@@ -132,7 +132,6 @@ export const logFrameEvent = ENABLED
|
||||
taken: scrollFastPathStats.taken,
|
||||
declined: {
|
||||
heightDeltaMismatch: scrollFastPathStats.declined.heightDeltaMismatch,
|
||||
noHint: scrollFastPathStats.declined.noHint,
|
||||
noPrevScreen: scrollFastPathStats.declined.noPrevScreen,
|
||||
other: scrollFastPathStats.declined.other
|
||||
},
|
||||
|
||||
@@ -12,8 +12,22 @@ export const hashText = (text: string) => {
|
||||
return (h >>> 0).toString(36)
|
||||
}
|
||||
|
||||
export const messageHeightKey = (msg: Msg) =>
|
||||
[msg.role, msg.kind ?? '', hashText([msg.text, msg.thinking ?? '', msg.tools?.join('\n') ?? ''].join('\0'))].join(':')
|
||||
export const messageHeightKey = (msg: Msg) => {
|
||||
const todoSig = msg.todos?.map(t => `${t.status}:${t.content}`).join('\u0001') ?? ''
|
||||
const panelSig =
|
||||
msg.panelData?.sections
|
||||
.map(s => `${s.title ?? ''}:${s.text?.length ?? 0}:${s.items?.length ?? 0}:${s.rows?.length ?? 0}`)
|
||||
.join('\u0001') ?? ''
|
||||
const introSig = msg.kind === 'intro' ? (msg.info?.version ?? '') : ''
|
||||
|
||||
return [
|
||||
msg.role,
|
||||
msg.kind ?? '',
|
||||
hashText(
|
||||
[msg.text, msg.thinking ?? '', msg.tools?.join('\n') ?? '', todoSig, panelSig, introSig].join('\0')
|
||||
)
|
||||
].join(':')
|
||||
}
|
||||
|
||||
export const wrappedLines = (text: string, width: number) => {
|
||||
const w = Math.max(1, width)
|
||||
@@ -34,6 +48,14 @@ export const estimatedMsgHeight = (
|
||||
return Math.max(3, (msg.panelData?.sections.length ?? 1) * 2 + 1)
|
||||
}
|
||||
|
||||
if (msg.kind === 'trail' && msg.todos?.length) {
|
||||
if (msg.todoCollapsedByDefault) {
|
||||
return 2
|
||||
}
|
||||
|
||||
return Math.max(2, msg.todos.length + 2)
|
||||
}
|
||||
|
||||
const bodyWidth = Math.max(20, cols - 5)
|
||||
const text = msg.role === 'assistant' && limitHistory ? boundedHistoryRenderText(msg.text) : msg.text
|
||||
let h = wrappedLines(text || ' ', bodyWidth)
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// Wheel-scroll acceleration state machine.
|
||||
//
|
||||
// Ported from claude-code's src/components/ScrollKeybindingHandler.tsx
|
||||
// (commit cb7cfba6 of their research snapshot at ~/claude-code). The
|
||||
// algorithm is theirs; the tuning constants below are theirs; this file
|
||||
// is a straight port adapted to our module structure.
|
||||
// Algorithm and tuning constants adapted from a reference implementation
|
||||
// of trackpad/wheel-event acceleration in TUI scroll handlers; this file
|
||||
// is the port adapted to our module structure.
|
||||
//
|
||||
// Problem: one wheel event = 1 scrolled row feels sluggish on trackpads
|
||||
// (which can fire 200+ events/sec) and during deliberate mouse scrolls.
|
||||
|
||||
1
ui-tui/src/types/hermes-ink.d.ts
vendored
1
ui-tui/src/types/hermes-ink.d.ts
vendored
@@ -112,7 +112,6 @@ declare module '@hermes/ink' {
|
||||
declined: {
|
||||
noPrevScreen: number
|
||||
heightDeltaMismatch: number
|
||||
noHint: number
|
||||
other: number
|
||||
}
|
||||
lastDeclineReason?: string
|
||||
|
||||
Reference in New Issue
Block a user