mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-28 06:21:33 +00:00
fix(tui): improve macOS paste and shortcut parity
- support Cmd-as-super and readline-style fallback shortcuts on macOS - add layered clipboard/OSC52 paste handling and immediate image-path attach - add IDE terminal setup helpers, terminal parity hints, and aligned docs
This commit is contained in:
parent
432772dbdf
commit
9556fef5a1
31 changed files with 1303 additions and 100 deletions
|
|
@ -2,29 +2,88 @@ import { execFile, spawn } from 'node:child_process'
|
|||
import { promisify } from 'node:util'
|
||||
|
||||
const execFileAsync = promisify(execFile)
|
||||
const CLIPBOARD_MAX_BUFFER = 4 * 1024 * 1024
|
||||
const POWERSHELL_ARGS = ['-NoProfile', '-NonInteractive', '-Command', 'Get-Clipboard -Raw'] as const
|
||||
|
||||
type ClipboardRun = typeof execFileAsync
|
||||
|
||||
export function isUsableClipboardText(text: null | string): text is string {
|
||||
if (!text || !/[^\s]/.test(text)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (text.includes('\u0000')) {
|
||||
return false
|
||||
}
|
||||
|
||||
let suspicious = 0
|
||||
for (const ch of text) {
|
||||
const code = ch.charCodeAt(0)
|
||||
const isControl = code < 0x20 && ch !== '\n' && ch !== '\r' && ch !== '\t'
|
||||
if (isControl || ch === '\ufffd') {
|
||||
suspicious += 1
|
||||
}
|
||||
}
|
||||
|
||||
return suspicious <= Math.max(2, Math.floor(text.length * 0.02))
|
||||
}
|
||||
|
||||
function readClipboardCommands(platform: NodeJS.Platform, env: NodeJS.ProcessEnv): Array<{ args: readonly string[]; cmd: string }> {
|
||||
if (platform === 'darwin') {
|
||||
return [{ cmd: 'pbpaste', args: [] }]
|
||||
}
|
||||
|
||||
if (platform === 'win32') {
|
||||
return [{ cmd: 'powershell', args: POWERSHELL_ARGS }]
|
||||
}
|
||||
|
||||
const attempts: Array<{ args: readonly string[]; cmd: string }> = []
|
||||
|
||||
if (env.WSL_INTEROP) {
|
||||
attempts.push({ cmd: 'powershell.exe', args: POWERSHELL_ARGS })
|
||||
}
|
||||
|
||||
if (env.WAYLAND_DISPLAY) {
|
||||
attempts.push({ cmd: 'wl-paste', args: ['--type', 'text'] })
|
||||
}
|
||||
|
||||
attempts.push({ cmd: 'xclip', args: ['-selection', 'clipboard', '-out'] })
|
||||
|
||||
return attempts
|
||||
}
|
||||
|
||||
/**
|
||||
* Read plain text from the system clipboard.
|
||||
*
|
||||
* On macOS this uses `pbpaste`. On other platforms we intentionally return
|
||||
* null for now; the TUI's text-paste hotkeys are primarily targeted at the
|
||||
* macOS clarify/input flow.
|
||||
* Uses native platform tools in fallback order:
|
||||
* - macOS: pbpaste
|
||||
* - Windows: PowerShell Get-Clipboard -Raw
|
||||
* - WSL: powershell.exe Get-Clipboard -Raw
|
||||
* - Linux Wayland: wl-paste --type text
|
||||
* - Linux X11: xclip -selection clipboard -out
|
||||
*/
|
||||
export async function readClipboardText(
|
||||
platform: NodeJS.Platform = process.platform,
|
||||
run: typeof execFileAsync = execFileAsync
|
||||
run: ClipboardRun = execFileAsync,
|
||||
env: NodeJS.ProcessEnv = process.env
|
||||
): Promise<string | null> {
|
||||
if (platform !== 'darwin') {
|
||||
return null
|
||||
for (const attempt of readClipboardCommands(platform, env)) {
|
||||
try {
|
||||
const result = await run(attempt.cmd, [...attempt.args], {
|
||||
encoding: 'utf8',
|
||||
maxBuffer: CLIPBOARD_MAX_BUFFER,
|
||||
windowsHide: true
|
||||
})
|
||||
|
||||
if (typeof result.stdout === 'string') {
|
||||
return result.stdout
|
||||
}
|
||||
} catch {
|
||||
// Fall through to the next clipboard backend.
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await run('pbpaste', [], { encoding: 'utf8', windowsHide: true })
|
||||
|
||||
return typeof result.stdout === 'string' ? result.stdout : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue