mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
99 lines
2.6 KiB
TypeScript
99 lines
2.6 KiB
TypeScript
import type { Diff } from './frame.js'
|
|
|
|
/**
|
|
* Optimize a diff by applying all optimization rules in a single pass.
|
|
* This reduces the number of patches that need to be written to the terminal.
|
|
*
|
|
* Rules applied:
|
|
* - Remove empty stdout patches
|
|
* - Merge consecutive cursorMove patches
|
|
* - Remove no-op cursorMove (0,0) patches
|
|
* - Concat adjacent style patches (transition diffs — can't drop either)
|
|
* - Dedupe consecutive hyperlinks with same URI
|
|
* - Cancel cursor hide/show pairs
|
|
* - Remove clear patches with count 0
|
|
*/
|
|
export function optimize(diff: Diff): Diff {
|
|
if (diff.length <= 1) {
|
|
return diff
|
|
}
|
|
|
|
const result: Diff = []
|
|
let len = 0
|
|
|
|
for (const patch of diff) {
|
|
const type = patch.type
|
|
|
|
// Skip no-ops
|
|
if (type === 'stdout') {
|
|
if (patch.content === '') {
|
|
continue
|
|
}
|
|
} else if (type === 'cursorMove') {
|
|
if (patch.x === 0 && patch.y === 0) {
|
|
continue
|
|
}
|
|
} else if (type === 'clear') {
|
|
if (patch.count === 0) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Try to merge with previous patch
|
|
if (len > 0) {
|
|
const lastIdx = len - 1
|
|
const last = result[lastIdx]!
|
|
const lastType = last.type
|
|
|
|
// Merge consecutive cursorMove
|
|
if (type === 'cursorMove' && lastType === 'cursorMove') {
|
|
result[lastIdx] = {
|
|
type: 'cursorMove',
|
|
x: last.x + patch.x,
|
|
y: last.y + patch.y
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
// Collapse consecutive cursorTo (only the last one matters)
|
|
if (type === 'cursorTo' && lastType === 'cursorTo') {
|
|
result[lastIdx] = patch
|
|
|
|
continue
|
|
}
|
|
|
|
// Concat adjacent style patches. styleStr is a transition diff
|
|
// (computed by diffAnsiCodes(from, to)), not a setter — dropping
|
|
// the first is only sound if its undo-codes are a subset of the
|
|
// second's, which is NOT guaranteed. e.g. [\e[49m, \e[2m]: dropping
|
|
// the bg reset leaks it into the next \e[2J/\e[2K via BCE.
|
|
if (type === 'styleStr' && lastType === 'styleStr') {
|
|
result[lastIdx] = { type: 'styleStr', str: last.str + patch.str }
|
|
|
|
continue
|
|
}
|
|
|
|
// Dedupe hyperlinks
|
|
if (type === 'hyperlink' && lastType === 'hyperlink' && patch.uri === last.uri) {
|
|
continue
|
|
}
|
|
|
|
// Cancel cursor hide/show pairs
|
|
if (
|
|
(type === 'cursorShow' && lastType === 'cursorHide') ||
|
|
(type === 'cursorHide' && lastType === 'cursorShow')
|
|
) {
|
|
result.pop()
|
|
len--
|
|
|
|
continue
|
|
}
|
|
}
|
|
|
|
result.push(patch)
|
|
len++
|
|
}
|
|
|
|
return result
|
|
}
|