From e6ddcb18015d717b1646d591cc96a59ba6e01531 Mon Sep 17 00:00:00 2001 From: 0xallam Date: Sun, 4 Jan 2026 15:20:38 -0800 Subject: [PATCH] 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 --- strix/interface/assets/tui_styles.tcss | 14 +++++++++++--- strix/interface/tui.py | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/strix/interface/assets/tui_styles.tcss b/strix/interface/assets/tui_styles.tcss index c9424f4..f54589e 100644 --- a/strix/interface/assets/tui_styles.tcss +++ b/strix/interface/assets/tui_styles.tcss @@ -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; diff --git a/strix/interface/tui.py b/strix/interface/tui.py index dc01f9f..ffe24ca 100644 --- a/strix/interface/tui.py +++ b/strix/interface/tui.py @@ -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"