hermes-agent/ui-tui/src/lib/fpsStore.ts
Brooklyn Nicholson b1c49d5e73 chore(tui): /clean recent perf work — KISS/DRY pass
24 files, -319 LoC. Behaviour preserved, 369/369 tests green.

- hermes-ink caches: shared lruEvict helper for the four parallel LRU
  caches (stringWidth, wrapText, sliceAnsi, lineWidth); touch-on-read
  stays inlined per cache; tightened output.ts skip-slice fast path.
- wheelAccel: trimmed provenance header, collapsed env parsing, ternary
  dispatch in computeWheelStep.
- perfPane: folded ensureLogDir into once-flag, spread-with-overrides
  for fastPath/phases instead of full rebuilds.
- env: extracted truthy() (used 4×).
- virtualHeights: collapsed user/diff/slash height bumps; trail+todos
  estimate.
- useInputHandlers: scrollIdleTimer cleanup on unmount, ?? undefined
  shorthand.
- useMainApp: dropped dead liveTailVisible IIFE and liveProgress
  indirection.
- appLayout, markdown, messageLine, entry: vertical rhythm, dropped
  narration comments, inlined one-shot vars.
- fix: empty catch blocks → /* best-effort */ for no-empty lint.
2026-04-26 20:38:47 -05:00

51 lines
1.3 KiB
TypeScript

// Tiny FPS tracker fed by ink's onFrame callback. Each entry is an Ink
// frame (React commit + drain-only frames) — the right notion for
// user-perceived motion.
//
// Zero-cost when HERMES_TUI_FPS is unset: trackFrame is undefined so the
// onFrame callback short-circuits at the optional chain.
import { atom } from 'nanostores'
import { SHOW_FPS } from '../config/env.js'
const WINDOW_SIZE = 30
export type FpsState = {
fps: number
/** Wraps at JS-safe int — diff pairs in a debug overlay safely. */
totalFrames: number
/** Ink render-phase total for the last frame. */
lastDurationMs: number
}
export const $fpsState = atom<FpsState>({ fps: 0, lastDurationMs: 0, totalFrames: 0 })
const timestamps: number[] = []
let totalFrames = 0
export const trackFrame = SHOW_FPS
? (durationMs: number) => {
timestamps.push(performance.now())
if (timestamps.length > WINDOW_SIZE) {
timestamps.shift()
}
totalFrames++
if (timestamps.length < 2) {
return
}
const elapsed = (timestamps[timestamps.length - 1]! - timestamps[0]!) / 1000
if (elapsed > 0) {
$fpsState.set({
fps: Math.round(((timestamps.length - 1) / elapsed) * 10) / 10,
lastDurationMs: Math.round(durationMs * 100) / 100,
totalFrames
})
}
}
: undefined