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:
Brooklyn Nicholson 2026-04-22 13:28:44 -05:00
parent 88564ad8bc
commit 7027ce42ef
8 changed files with 140 additions and 24 deletions

View file

@ -69,6 +69,12 @@ const memoizedStylesForWrap: Record<NonNullable<Styles['textWrap']>, Styles> = {
flexDirection: 'row',
textWrap: 'wrap'
},
'wrap-char': {
flexGrow: 0,
flexShrink: 1,
flexDirection: 'row',
textWrap: 'wrap-char'
},
'wrap-trim': {
flexGrow: 0,
flexShrink: 1,

View file

@ -343,7 +343,7 @@ function wrapWithSoftWrap(
maxWidth: number,
textWrap: Parameters<typeof wrapText>[2]
): { wrapped: string; softWrap: boolean[] | undefined } {
if (textWrap !== 'wrap' && textWrap !== 'wrap-trim') {
if (textWrap !== 'wrap' && textWrap !== 'wrap-char' && textWrap !== 'wrap-trim') {
return {
wrapped: wrapText(plainText, maxWidth, textWrap),
softWrap: undefined

View file

@ -55,6 +55,7 @@ export type TextStyles = {
export type Styles = {
readonly textWrap?:
| 'wrap'
| 'wrap-char'
| 'wrap-trim'
| 'end'
| 'middle'

View file

@ -50,6 +50,19 @@ export default function wrapText(text: string, maxWidth: number, wrapType: Style
})
}
// Char-granularity wrap: break at exact column boundaries regardless of
// whitespace. Used for text inputs where the cursor position must track
// the wrap boundary deterministically — word-wrap's whitespace-preferring
// reshuffle causes visible cursor flicker as each keystroke can push a
// word across a line break.
if (wrapType === 'wrap-char') {
return wrapAnsi(text, maxWidth, {
trim: false,
hard: true,
wordWrap: false
})
}
if (wrapType === 'wrap-trim') {
return wrapAnsi(text, maxWidth, {
trim: true,