mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
Selected rows in the model/session/skills pickers and approval/clarify prompts only changed from dim gray to cornsilk, which reads as low contrast on lighter themes and LCDs (reported during TUI v2 blitz). Switch the selected row to `inverse bold` with the brand accent color across modelPicker, sessionPicker, skillsHub, and prompts so the highlight is terminal-portable and unambiguous. Unselected rows stay dim. Also extends the sessionPicker middle meta column (which was always dim) to inherit the row's selection state.
73 lines
1.9 KiB
TypeScript
73 lines
1.9 KiB
TypeScript
const ESC = '\x1b'
|
|
const BEL = '\x07'
|
|
const ST = `${ESC}\\`
|
|
|
|
export const OSC52_CLIPBOARD_QUERY = `${ESC}]52;c;?${BEL}`
|
|
|
|
type OscResponse = { code: number; data: string; type: 'osc' }
|
|
|
|
type OscQuerier = {
|
|
flush: () => Promise<void>
|
|
send: <T>(query: { match: (r: unknown) => r is T; request: string }) => Promise<T | undefined>
|
|
}
|
|
|
|
function wrapForMultiplexer(sequence: string): string {
|
|
if (process.env['TMUX']) {
|
|
return `${ESC}Ptmux;${sequence.split(ESC).join(ESC + ESC)}${ST}`
|
|
}
|
|
|
|
if (process.env['STY']) {
|
|
return `${ESC}P${sequence}${ST}`
|
|
}
|
|
|
|
return sequence
|
|
}
|
|
|
|
export function buildOsc52ClipboardQuery(): string {
|
|
return wrapForMultiplexer(OSC52_CLIPBOARD_QUERY)
|
|
}
|
|
|
|
export function parseOsc52ClipboardData(data: string): null | string {
|
|
const firstSep = data.indexOf(';')
|
|
|
|
if (firstSep === -1) {
|
|
return null
|
|
}
|
|
|
|
const selection = data.slice(0, firstSep)
|
|
const payload = data.slice(firstSep + 1)
|
|
|
|
if ((selection !== 'c' && selection !== 'p') || !payload || payload === '?') {
|
|
return null
|
|
}
|
|
|
|
try {
|
|
return Buffer.from(payload, 'base64').toString('utf8')
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export async function readOsc52Clipboard(querier: null | OscQuerier, timeoutMs = 500): Promise<null | string> {
|
|
if (!querier) {
|
|
return null
|
|
}
|
|
|
|
const timeout = new Promise<undefined>(resolve => setTimeout(resolve, timeoutMs))
|
|
|
|
const query = querier.send<OscResponse>({
|
|
request: buildOsc52ClipboardQuery(),
|
|
match: (r: unknown): r is OscResponse => {
|
|
return !!r && typeof r === 'object' && (r as OscResponse).type === 'osc' && (r as OscResponse).code === 52
|
|
}
|
|
})
|
|
|
|
const response = await Promise.race([query, timeout])
|
|
|
|
await querier.flush()
|
|
|
|
return response ? parseOsc52ClipboardData(response.data) : null
|
|
}
|
|
|
|
export const writeOsc52Clipboard = (s: string) =>
|
|
process.stdout.write(`\x1b]52;c;${Buffer.from(s, 'utf8').toString('base64')}\x07`)
|