mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-29 06:31:32 +00:00
fix(tui): raise picker selection contrast with inverse + bold
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.
This commit is contained in:
parent
c3b8c8e42c
commit
fc6a27098e
24 changed files with 248 additions and 93 deletions
|
|
@ -28,7 +28,9 @@ describe('readClipboardText', () => {
|
|||
it('tries powershell.exe first on WSL', async () => {
|
||||
const run = vi.fn().mockResolvedValue({ stdout: 'from wsl\n' })
|
||||
|
||||
await expect(readClipboardText('linux', run, { WSL_INTEROP: '/tmp/socket' } as NodeJS.ProcessEnv)).resolves.toBe('from wsl\n')
|
||||
await expect(readClipboardText('linux', run, { WSL_INTEROP: '/tmp/socket' } as NodeJS.ProcessEnv)).resolves.toBe(
|
||||
'from wsl\n'
|
||||
)
|
||||
expect(run).toHaveBeenCalledWith(
|
||||
'powershell.exe',
|
||||
['-NoProfile', '-NonInteractive', '-Command', 'Get-Clipboard -Raw'],
|
||||
|
|
@ -39,7 +41,9 @@ describe('readClipboardText', () => {
|
|||
it('uses wl-paste on Wayland Linux', async () => {
|
||||
const run = vi.fn().mockResolvedValue({ stdout: 'from wayland\n' })
|
||||
|
||||
await expect(readClipboardText('linux', run, { WAYLAND_DISPLAY: 'wayland-1' } as NodeJS.ProcessEnv)).resolves.toBe('from wayland\n')
|
||||
await expect(readClipboardText('linux', run, { WAYLAND_DISPLAY: 'wayland-1' } as NodeJS.ProcessEnv)).resolves.toBe(
|
||||
'from wayland\n'
|
||||
)
|
||||
expect(run).toHaveBeenCalledWith(
|
||||
'wl-paste',
|
||||
['--type', 'text'],
|
||||
|
|
@ -53,7 +57,9 @@ describe('readClipboardText', () => {
|
|||
.mockRejectedValueOnce(new Error('wl-paste missing'))
|
||||
.mockResolvedValueOnce({ stdout: 'from xclip\n' })
|
||||
|
||||
await expect(readClipboardText('linux', run, { WAYLAND_DISPLAY: 'wayland-1' } as NodeJS.ProcessEnv)).resolves.toBe('from xclip\n')
|
||||
await expect(readClipboardText('linux', run, { WAYLAND_DISPLAY: 'wayland-1' } as NodeJS.ProcessEnv)).resolves.toBe(
|
||||
'from xclip\n'
|
||||
)
|
||||
expect(run).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'wl-paste',
|
||||
|
|
@ -71,7 +77,9 @@ describe('readClipboardText', () => {
|
|||
it('returns null when every clipboard backend fails', async () => {
|
||||
const run = vi.fn().mockRejectedValue(new Error('clipboard failed'))
|
||||
|
||||
await expect(readClipboardText('linux', run, { WAYLAND_DISPLAY: 'wayland-1' } as NodeJS.ProcessEnv)).resolves.toBeNull()
|
||||
await expect(
|
||||
readClipboardText('linux', run, { WAYLAND_DISPLAY: 'wayland-1' } as NodeJS.ProcessEnv)
|
||||
).resolves.toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -101,6 +109,7 @@ describe('writeClipboardText', () => {
|
|||
|
||||
it('writes text to pbcopy on macOS', async () => {
|
||||
const stdin = { end: vi.fn() }
|
||||
|
||||
const child = {
|
||||
once: vi.fn((event: string, cb: (code?: number) => void) => {
|
||||
if (event === 'close') {
|
||||
|
|
@ -111,10 +120,15 @@ describe('writeClipboardText', () => {
|
|||
}),
|
||||
stdin
|
||||
}
|
||||
|
||||
const start = vi.fn().mockReturnValue(child)
|
||||
|
||||
await expect(writeClipboardText('hello world', 'darwin', start as any)).resolves.toBe(true)
|
||||
expect(start).toHaveBeenCalledWith('pbcopy', [], expect.objectContaining({ stdio: ['pipe', 'ignore', 'ignore'], windowsHide: true }))
|
||||
expect(start).toHaveBeenCalledWith(
|
||||
'pbcopy',
|
||||
[],
|
||||
expect.objectContaining({ stdio: ['pipe', 'ignore', 'ignore'], windowsHide: true })
|
||||
)
|
||||
expect(stdin.end).toHaveBeenCalledWith('hello world')
|
||||
})
|
||||
|
||||
|
|
@ -129,6 +143,7 @@ describe('writeClipboardText', () => {
|
|||
}),
|
||||
stdin: { end: vi.fn() }
|
||||
}
|
||||
|
||||
const start = vi.fn().mockReturnValue(child)
|
||||
|
||||
await expect(writeClipboardText('hello world', 'darwin', start as any)).resolves.toBe(false)
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ describe('readOsc52Clipboard', () => {
|
|||
data: `c;${Buffer.from('queried text', 'utf8').toString('base64')}`,
|
||||
type: 'osc'
|
||||
})
|
||||
|
||||
const flush = vi.fn().mockResolvedValue(undefined)
|
||||
|
||||
await expect(readOsc52Clipboard({ flush, send })).resolves.toBe('queried text')
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const originalPlatform = process.platform
|
|||
async function importPlatform(platform: NodeJS.Platform) {
|
||||
vi.resetModules()
|
||||
Object.defineProperty(process, 'platform', { value: platform })
|
||||
|
||||
return import('../lib/platform.js')
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,28 +17,55 @@ describe('terminalParityHints', () => {
|
|||
it('suggests IDE setup only for VS Code-family terminals that still need bindings', async () => {
|
||||
const readFile = vi.fn().mockRejectedValue(Object.assign(new Error('missing'), { code: 'ENOENT' }))
|
||||
|
||||
const hints = await terminalParityHints(
|
||||
{ TERM_PROGRAM: 'vscode' } as NodeJS.ProcessEnv,
|
||||
{ fileOps: { readFile }, homeDir: '/tmp/fake-home' }
|
||||
)
|
||||
const hints = await terminalParityHints({ TERM_PROGRAM: 'vscode' } as NodeJS.ProcessEnv, {
|
||||
fileOps: { readFile },
|
||||
homeDir: '/tmp/fake-home'
|
||||
})
|
||||
|
||||
expect(hints.some(h => h.key === 'ide-setup')).toBe(true)
|
||||
})
|
||||
|
||||
it('suppresses IDE setup hint when keybindings are already configured', async () => {
|
||||
const readFile = vi.fn().mockResolvedValue(
|
||||
JSON.stringify([
|
||||
{ key: 'shift+enter', command: 'workbench.action.terminal.sendSequence', when: 'terminalFocus', args: { text: '\\\r\n' } },
|
||||
{ key: 'ctrl+enter', command: 'workbench.action.terminal.sendSequence', when: 'terminalFocus', args: { text: '\\\r\n' } },
|
||||
{ key: 'cmd+enter', command: 'workbench.action.terminal.sendSequence', when: 'terminalFocus', args: { text: '\\\r\n' } },
|
||||
{ key: 'cmd+z', command: 'workbench.action.terminal.sendSequence', when: 'terminalFocus', args: { text: '\u001b[122;9u' } },
|
||||
{ key: 'shift+cmd+z', command: 'workbench.action.terminal.sendSequence', when: 'terminalFocus', args: { text: '\u001b[122;10u' } }
|
||||
{
|
||||
key: 'shift+enter',
|
||||
command: 'workbench.action.terminal.sendSequence',
|
||||
when: 'terminalFocus',
|
||||
args: { text: '\\\r\n' }
|
||||
},
|
||||
{
|
||||
key: 'ctrl+enter',
|
||||
command: 'workbench.action.terminal.sendSequence',
|
||||
when: 'terminalFocus',
|
||||
args: { text: '\\\r\n' }
|
||||
},
|
||||
{
|
||||
key: 'cmd+enter',
|
||||
command: 'workbench.action.terminal.sendSequence',
|
||||
when: 'terminalFocus',
|
||||
args: { text: '\\\r\n' }
|
||||
},
|
||||
{
|
||||
key: 'cmd+z',
|
||||
command: 'workbench.action.terminal.sendSequence',
|
||||
when: 'terminalFocus',
|
||||
args: { text: '\u001b[122;9u' }
|
||||
},
|
||||
{
|
||||
key: 'shift+cmd+z',
|
||||
command: 'workbench.action.terminal.sendSequence',
|
||||
when: 'terminalFocus',
|
||||
args: { text: '\u001b[122;10u' }
|
||||
}
|
||||
])
|
||||
)
|
||||
|
||||
const hints = await terminalParityHints(
|
||||
{ TERM_PROGRAM: 'vscode' } as NodeJS.ProcessEnv,
|
||||
{ fileOps: { readFile }, homeDir: '/tmp/fake-home' }
|
||||
)
|
||||
const hints = await terminalParityHints({ TERM_PROGRAM: 'vscode' } as NodeJS.ProcessEnv, {
|
||||
fileOps: { readFile },
|
||||
homeDir: '/tmp/fake-home'
|
||||
})
|
||||
|
||||
expect(hints.some(h => h.key === 'ide-setup')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -21,10 +21,17 @@ describe('terminalSetup helpers', () => {
|
|||
expect(getVSCodeStyleConfigDir('Code', 'darwin', {} as NodeJS.ProcessEnv, '/home/me')).toBe(
|
||||
'/home/me/Library/Application Support/Code/User'
|
||||
)
|
||||
expect(getVSCodeStyleConfigDir('Code', 'linux', {} as NodeJS.ProcessEnv, '/home/me')).toBe('/home/me/.config/Code/User')
|
||||
expect(getVSCodeStyleConfigDir('Code', 'win32', { APPDATA: 'C:/Users/me/AppData/Roaming' } as NodeJS.ProcessEnv, '/home/me')).toBe(
|
||||
'C:/Users/me/AppData/Roaming/Code/User'
|
||||
expect(getVSCodeStyleConfigDir('Code', 'linux', {} as NodeJS.ProcessEnv, '/home/me')).toBe(
|
||||
'/home/me/.config/Code/User'
|
||||
)
|
||||
expect(
|
||||
getVSCodeStyleConfigDir(
|
||||
'Code',
|
||||
'win32',
|
||||
{ APPDATA: 'C:/Users/me/AppData/Roaming' } as NodeJS.ProcessEnv,
|
||||
'/home/me'
|
||||
)
|
||||
).toBe('C:/Users/me/AppData/Roaming/Code/User')
|
||||
})
|
||||
|
||||
it('strips line comments from keybindings JSON', () => {
|
||||
|
|
@ -79,6 +86,7 @@ describe('configureTerminalKeybindings', () => {
|
|||
|
||||
it('reports conflicts without overwriting existing bindings', async () => {
|
||||
const mkdir = vi.fn().mockResolvedValue(undefined)
|
||||
|
||||
const readFile = vi.fn().mockResolvedValue(
|
||||
JSON.stringify([
|
||||
{
|
||||
|
|
@ -89,6 +97,7 @@ describe('configureTerminalKeybindings', () => {
|
|||
}
|
||||
])
|
||||
)
|
||||
|
||||
const writeFile = vi.fn().mockResolvedValue(undefined)
|
||||
const copyFile = vi.fn().mockResolvedValue(undefined)
|
||||
|
||||
|
|
@ -209,6 +218,7 @@ describe('configureTerminalKeybindings', () => {
|
|||
}
|
||||
])
|
||||
)
|
||||
|
||||
await expect(
|
||||
shouldPromptForTerminalSetup({
|
||||
env: { TERM_PROGRAM: 'vscode' } as NodeJS.ProcessEnv,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { looksLikeDroppedPath } from '../app/useComposerState.js'
|
||||
|
||||
describe('looksLikeDroppedPath', () => {
|
||||
it('recognizes macOS screenshot temp paths and file URIs', () => {
|
||||
expect(looksLikeDroppedPath('/var/folders/x/T/TemporaryItems/Screenshot\\ 2026-04-21\\ at\\ 1.04.43 PM.png')).toBe(true)
|
||||
expect(looksLikeDroppedPath('file:///var/folders/x/T/TemporaryItems/Screenshot%202026-04-21%20at%201.04.43%20PM.png')).toBe(true)
|
||||
expect(looksLikeDroppedPath('/var/folders/x/T/TemporaryItems/Screenshot\\ 2026-04-21\\ at\\ 1.04.43 PM.png')).toBe(
|
||||
true
|
||||
)
|
||||
expect(
|
||||
looksLikeDroppedPath('file:///var/folders/x/T/TemporaryItems/Screenshot%202026-04-21%20at%201.04.43%20PM.png')
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('rejects normal multiline or plain text paste', () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue