Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
commit
03634b1ca3
11340 files changed
+3442369
No files matched your search
@@ -0,0 +1,106 @@
|
||||
import { type AnsiCode, ansiCodesToString, reduceAnsiCodes, tokenize, undoAnsiCodes } from '@alcalzone/ansi-tokenize'
|
||||
|
||||
import { lruEvict } from '../ink/lru.js'
|
||||
import { stringWidth } from '../ink/stringWidth.js'
|
||||
|
||||
function isEndCode(code: AnsiCode): boolean {
|
||||
return code.code === code.endCode
|
||||
}
|
||||
|
||||
function filterStartCodes(codes: AnsiCode[]): AnsiCode[] {
|
||||
return codes.filter(c => !isEndCode(c))
|
||||
}
|
||||
|
||||
// LRU cache: same (string, start, end) → same output. Output.get() re-emits
|
||||
// identical writes every frame for stable transcript content; this avoids
|
||||
// re-tokenizing them. CPU profile (Apr 2026) showed sliceAnsi at 18% total
|
||||
// time during scroll. Bounded at 4096 entries — entries are short clipped
|
||||
// lines so memory cost is small.
|
||||
const sliceCache = new Map<string, string>()
|
||||
const SLICE_CACHE_LIMIT = 4096
|
||||
|
||||
export default function sliceAnsi(str: string, start: number, end?: number): string {
|
||||
if (!str) {
|
||||
return ''
|
||||
}
|
||||
|
||||
// Hot-path: only cache when end is defined (the Output.get() use-case).
|
||||
if (end !== undefined) {
|
||||
const key = `${start}|${end}|${str}`
|
||||
const cached = sliceCache.get(key)
|
||||
|
||||
if (cached !== undefined) {
|
||||
sliceCache.delete(key)
|
||||
sliceCache.set(key, cached)
|
||||
|
||||
return cached
|
||||
}
|
||||
|
||||
const result = computeSlice(str, start, end)
|
||||
|
||||
if (sliceCache.size >= SLICE_CACHE_LIMIT) {
|
||||
sliceCache.delete(sliceCache.keys().next().value!)
|
||||
}
|
||||
|
||||
sliceCache.set(key, result)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
return computeSlice(str, start, end)
|
||||
}
|
||||
|
||||
export function sliceCacheSize(): number {
|
||||
return sliceCache.size
|
||||
}
|
||||
|
||||
export function evictSliceCache(keepRatio = 0): void {
|
||||
lruEvict(sliceCache, keepRatio)
|
||||
}
|
||||
|
||||
function computeSlice(str: string, start: number, end?: number): string {
|
||||
const tokens = tokenize(str)
|
||||
let activeCodes: AnsiCode[] = []
|
||||
let position = 0
|
||||
let result = ''
|
||||
let include = false
|
||||
|
||||
for (const token of tokens) {
|
||||
const width = token.type === 'ansi' ? 0 : token.fullWidth ? 2 : stringWidth(token.value)
|
||||
|
||||
if (end !== undefined && position >= end) {
|
||||
if (token.type === 'ansi' || width > 0 || !include) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (token.type === 'ansi') {
|
||||
activeCodes.push(token)
|
||||
|
||||
if (include) {
|
||||
result += token.code
|
||||
}
|
||||
} else {
|
||||
if (!include && position >= start) {
|
||||
if (start > 0 && width === 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
include = true
|
||||
activeCodes = filterStartCodes(reduceAnsiCodes(activeCodes))
|
||||
result = ansiCodesToString(activeCodes)
|
||||
}
|
||||
|
||||
if (include) {
|
||||
result += token.value
|
||||
}
|
||||
|
||||
position += width
|
||||
}
|
||||
}
|
||||
|
||||
const activeStartCodes = filterStartCodes(reduceAnsiCodes(activeCodes))
|
||||
result += ansiCodesToString(undoAnsiCodes(activeStartCodes))
|
||||
|
||||
return result
|
||||
}
|
||||
Reference in new issue
Block a user