Summary
The InputPanel component in src/tui/inputPanel.js uses a custom block character cursor (\u2588) appended to the input text, which has significant issues. It should be refactored to use Ink's built-in useCursor hook for proper terminal cursor control.
Environment
- OS: Unknown — user to confirm
- Node.js: Unknown — user to confirm
- madz version: Unknown — user to confirm
- LLM provider: N/A (TUI rendering issue)
Reproduction
- Start the madz TUI
- Observe the input cursor at the bottom of the screen, below the status bar
- The cursor is rendered as a block character (
█) appended to the input text
Expected Behavior
The cursor should be controlled by Ink's useCursor hook, which sets the actual terminal cursor position after each render. This provides:
- Proper terminal cursor behavior (blinking, positioning)
- IME (Input Method Editor) support — the composing character is displayed at the cursor location
- No visual artifacts from a block character in the text flow
Actual Behavior
The cursor is a static block character (\u2588) rendered as part of the InputPanel component's output. This approach:
- Does not control the actual terminal cursor position
- Lacks IME support
- Can cause visual glitches or misalignment
- Is not the idiomatic Ink pattern for cursor management
Additional Context
Ink's useCursor hook provides setCursorPosition({ x, y }) to position the cursor relative to the Ink output after each render. The cursor position should be calculated based on the input text length and passed to setCursorPosition. Passing undefined to setCursorPosition hides the cursor.
Reference: https://github.com/vadimdemedes/ink#usecursor
Audit Findings
- File:
src/tui/inputPanel.js — Current cursor is a block character (\u2588) rendered as part of the Blink component output. The InputPanel delegates to Blink which appends the cursor character after the text. No cursor positioning logic exists.
- File:
src/tui/app.js:1228 — InputPanel is rendered in the bottom row of the layout, after ConversationPanel and StatusBar. The cursor position (x, y) must be calculated relative to the Ink output root.
- File:
src/tui/statusBar.js — The status bar is a single-row component rendered immediately above the input. The cursor Y position will be totalRows - 1 (last row).
- No existing
useCursor usage — A grep for useCursor and setCursorPosition returned zero results. This is a greenfield adoption of the hook.
- Integration point: The
InputPanel component needs to call useCursor() and compute x = stringWidth(prompt + inputText) on each render. The y position is the last row of the Ink output.
- Dependency: The
string-width package may need to be installed if not already a dependency, as Ink docs recommend it for wide character support (CJK, emoji).
Summary
The
InputPanelcomponent insrc/tui/inputPanel.jsuses a custom block character cursor (\u2588) appended to the input text, which has significant issues. It should be refactored to use Ink's built-inuseCursorhook for proper terminal cursor control.Environment
Reproduction
█) appended to the input textExpected Behavior
The cursor should be controlled by Ink's
useCursorhook, which sets the actual terminal cursor position after each render. This provides:Actual Behavior
The cursor is a static block character (
\u2588) rendered as part of theInputPanelcomponent's output. This approach:Additional Context
Ink's
useCursorhook providessetCursorPosition({ x, y })to position the cursor relative to the Ink output after each render. The cursor position should be calculated based on the input text length and passed tosetCursorPosition. PassingundefinedtosetCursorPositionhides the cursor.Reference: https://github.com/vadimdemedes/ink#usecursor
Audit Findings
src/tui/inputPanel.js— Current cursor is a block character (\u2588) rendered as part of theBlinkcomponent output. TheInputPaneldelegates toBlinkwhich appends the cursor character after the text. No cursor positioning logic exists.src/tui/app.js:1228—InputPanelis rendered in the bottom row of the layout, afterConversationPanelandStatusBar. The cursor position (x, y) must be calculated relative to the Ink output root.src/tui/statusBar.js— The status bar is a single-row component rendered immediately above the input. The cursor Y position will betotalRows - 1(last row).useCursorusage — A grep foruseCursorandsetCursorPositionreturned zero results. This is a greenfield adoption of the hook.InputPanelcomponent needs to calluseCursor()and computex = stringWidth(prompt + inputText)on each render. Theyposition is the last row of the Ink output.string-widthpackage may need to be installed if not already a dependency, as Ink docs recommend it for wide character support (CJK, emoji).