feat(tui): add multiline chat input with dynamic height

- Support Shift+Enter to insert newlines in chat input
- Chat input container expands dynamically up to 8 lines
- Enter key sends message as before
- Fix cursor line background to match unselected lines
This commit is contained in:
0xallam
2026-01-04 15:20:38 -08:00
committed by Ahmed Allam
parent daba3d8b61
commit e6ddcb1801
2 changed files with 33 additions and 3 deletions

View File

@@ -117,7 +117,7 @@ Screen {
margin-right: 0;
padding: 0;
layout: horizontal;
align-vertical: middle;
align-vertical: top;
}
#chat_input_container:focus-within {
@@ -134,7 +134,7 @@ Screen {
height: 100%;
padding: 0 0 0 1;
color: #737373;
content-align-vertical: middle;
content-align-vertical: top;
}
#chat_history:focus {
@@ -144,7 +144,7 @@ Screen {
#chat_input {
width: 1fr;
height: 100%;
background: #121212;
background: transparent;
border: none;
color: #d4d4d4;
padding: 0;
@@ -155,6 +155,14 @@ Screen {
border: none;
}
#chat_input .text-area--cursor-line {
background: transparent;
}
#chat_input:focus .text-area--cursor-line {
background: transparent;
}
#chat_input > .text-area--placeholder {
color: #525252;
text-style: italic;

View File

@@ -55,7 +55,15 @@ class ChatTextArea(TextArea): # type: ignore[misc]
def set_app_reference(self, app: "StrixTUIApp") -> None:
self._app_reference = app
def on_mount(self) -> None:
self._update_height()
def _on_key(self, event: events.Key) -> None:
if event.key == "shift+enter":
self.insert("\n")
event.prevent_default()
return
if event.key == "enter" and self._app_reference:
text_content = str(self.text) # type: ignore[has-type]
message = text_content.strip()
@@ -69,6 +77,20 @@ class ChatTextArea(TextArea): # type: ignore[misc]
super()._on_key(event)
@on(TextArea.Changed) # type: ignore[misc]
def _update_height(self, _event: TextArea.Changed | None = None) -> None:
if not self.parent:
return
line_count = self.document.line_count
target_lines = min(max(1, line_count), 8)
new_height = target_lines + 2
if self.parent.styles.height != new_height:
self.parent.styles.height = new_height
self.scroll_cursor_visible()
class SplashScreen(Static): # type: ignore[misc]
PRIMARY_GREEN = "#22c55e"