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:
@@ -117,7 +117,7 @@ Screen {
|
|||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
layout: horizontal;
|
layout: horizontal;
|
||||||
align-vertical: middle;
|
align-vertical: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat_input_container:focus-within {
|
#chat_input_container:focus-within {
|
||||||
@@ -134,7 +134,7 @@ Screen {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 0 0 0 1;
|
padding: 0 0 0 1;
|
||||||
color: #737373;
|
color: #737373;
|
||||||
content-align-vertical: middle;
|
content-align-vertical: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat_history:focus {
|
#chat_history:focus {
|
||||||
@@ -144,7 +144,7 @@ Screen {
|
|||||||
#chat_input {
|
#chat_input {
|
||||||
width: 1fr;
|
width: 1fr;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: #121212;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
color: #d4d4d4;
|
color: #d4d4d4;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -155,6 +155,14 @@ Screen {
|
|||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#chat_input .text-area--cursor-line {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chat_input:focus .text-area--cursor-line {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
#chat_input > .text-area--placeholder {
|
#chat_input > .text-area--placeholder {
|
||||||
color: #525252;
|
color: #525252;
|
||||||
text-style: italic;
|
text-style: italic;
|
||||||
|
|||||||
@@ -55,7 +55,15 @@ class ChatTextArea(TextArea): # type: ignore[misc]
|
|||||||
def set_app_reference(self, app: "StrixTUIApp") -> None:
|
def set_app_reference(self, app: "StrixTUIApp") -> None:
|
||||||
self._app_reference = app
|
self._app_reference = app
|
||||||
|
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
self._update_height()
|
||||||
|
|
||||||
def _on_key(self, event: events.Key) -> None:
|
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:
|
if event.key == "enter" and self._app_reference:
|
||||||
text_content = str(self.text) # type: ignore[has-type]
|
text_content = str(self.text) # type: ignore[has-type]
|
||||||
message = text_content.strip()
|
message = text_content.strip()
|
||||||
@@ -69,6 +77,20 @@ class ChatTextArea(TextArea): # type: ignore[misc]
|
|||||||
|
|
||||||
super()._on_key(event)
|
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]
|
class SplashScreen(Static): # type: ignore[misc]
|
||||||
PRIMARY_GREEN = "#22c55e"
|
PRIMARY_GREEN = "#22c55e"
|
||||||
|
|||||||
Reference in New Issue
Block a user