mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-05 18:27:04 +08:00
fix(tui): blitz closeout — input wrap parity, shift-tab yolo, bottom statusline
- input wrap: add <Text wrap="wrap-char"> mode that drives wrap-ansi with wordWrap:false, and align cursorLayout/offsetFromPosition to that same boundary (w=cols, trailing-cell overflow). Word-wrap's whitespace reshuffle was causing the cursor to jump a word left/right on each keystroke near the right edge — blitz row 9 - shift-tab: toggle per-session yolo without submitting a turn (mirrors Claude Code's in-place dangerously-approve); slash /yolo still works for discoverability — blitz row 5 sub-item 11 - statusline: lift StatusRule out of ComposerPane to a new StatusRulePane anchored at the bottom of AppLayout, below the input — blitz row 5 sub-item 12
This commit is contained in:
60
ui-tui/src/__tests__/textInputWrap.test.ts
Normal file
60
ui-tui/src/__tests__/textInputWrap.test.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { cursorLayout, offsetFromPosition } from '../components/textInput.js'
|
||||
|
||||
describe('cursorLayout — char-wrap parity with wrap-ansi', () => {
|
||||
it('places cursor mid-line at its column', () => {
|
||||
expect(cursorLayout('hello world', 6, 40)).toEqual({ column: 6, line: 0 })
|
||||
})
|
||||
|
||||
it('places cursor at end of a non-full line', () => {
|
||||
expect(cursorLayout('hi', 2, 10)).toEqual({ column: 2, line: 0 })
|
||||
})
|
||||
|
||||
it('wraps to next line when cursor lands exactly at the right edge', () => {
|
||||
// 8 chars on an 8-col line: text fills the row exactly; the cursor's
|
||||
// inverted-space cell overflows to col 0 of the next row.
|
||||
expect(cursorLayout('abcdefgh', 8, 8)).toEqual({ column: 0, line: 1 })
|
||||
})
|
||||
|
||||
it('tracks a word across a char-wrap boundary without jumping', () => {
|
||||
// With wordWrap:false, "hello world" at cols=8 is "hello wo\nrld" —
|
||||
// typing incremental letters doesn't reshuffle the word across lines.
|
||||
expect(cursorLayout('hello wo', 8, 8)).toEqual({ column: 0, line: 1 })
|
||||
expect(cursorLayout('hello wor', 9, 8)).toEqual({ column: 1, line: 1 })
|
||||
expect(cursorLayout('hello worl', 10, 8)).toEqual({ column: 2, line: 1 })
|
||||
})
|
||||
|
||||
it('honours explicit newlines', () => {
|
||||
expect(cursorLayout('one\ntwo', 5, 40)).toEqual({ column: 1, line: 1 })
|
||||
expect(cursorLayout('one\ntwo', 4, 40)).toEqual({ column: 0, line: 1 })
|
||||
})
|
||||
|
||||
it('does not wrap when cursor is before the right edge', () => {
|
||||
expect(cursorLayout('abcdefg', 7, 8)).toEqual({ column: 7, line: 0 })
|
||||
})
|
||||
})
|
||||
|
||||
describe('offsetFromPosition — char-wrap inverse of cursorLayout', () => {
|
||||
it('returns 0 for empty input', () => {
|
||||
expect(offsetFromPosition('', 0, 0, 10)).toBe(0)
|
||||
})
|
||||
|
||||
it('maps clicks within a single line', () => {
|
||||
expect(offsetFromPosition('hello', 0, 3, 40)).toBe(3)
|
||||
})
|
||||
|
||||
it('maps clicks past end to value length', () => {
|
||||
expect(offsetFromPosition('hi', 0, 10, 40)).toBe(2)
|
||||
})
|
||||
|
||||
it('maps clicks on a wrapped second row at cols boundary', () => {
|
||||
// "abcdefghij" at cols=8 wraps to "abcdefgh\nij" — click at row 1 col 0
|
||||
// should land on 'i' (offset 8).
|
||||
expect(offsetFromPosition('abcdefghij', 1, 0, 8)).toBe(8)
|
||||
})
|
||||
|
||||
it('maps clicks past a \\n into the target line', () => {
|
||||
expect(offsetFromPosition('one\ntwo', 1, 2, 40)).toBe(6)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user