7 Commits

Author SHA1 Message Date
74ed189ca5 minor bump 2026-02-20 18:17:05 +00:00
6e50e0861f add Renderable type annotation
add {Writable}.error() for displaying error messages in red.
2026-02-20 18:16:47 +00:00
ab4898dac3 prevent keypresses from ConfigScreen propogating to the mainframe
improve the error message should a command execution fail.
2026-02-20 18:16:15 +00:00
086eeba916 make hover and focus more consistent with one another.
reorganise css.
2026-02-20 17:55:17 +00:00
2075e98c17 typo 2026-02-20 17:11:20 +00:00
923faa67ec patch bump 2026-02-20 16:18:43 +00:00
76483a24b9 improve the hovering and focus effects for the buttons. 2026-02-20 16:18:29 +00:00
5 changed files with 118 additions and 33 deletions

View File

@@ -43,7 +43,7 @@ q3rcon-tui --host=localhost --port=28960 --password=rconpassword
Additional flags: Additional flags:
- `--raw`: Boolean flag, if set the RichLog will print raw responses without rendering tables. - `--raw`: Boolean flag, if set the RichLog will print raw responses without rendering tables.
- `--append`: Boolean flag, if set the RichLog output with append each response continuously. - `--append`: Boolean flag, if set the RichLog output will append each response continuously.
- `--version`: Print the version of the TUI. - `--version`: Print the version of the TUI.
- `--help`: Print the help message. - `--help`: Print the help message.

View File

@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2026-present onyx-and-iris <code@onyxandiris.online> # SPDX-FileCopyrightText: 2026-present onyx-and-iris <code@onyxandiris.online>
# #
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
__version__ = '0.4.1' __version__ = '0.5.0'

View File

@@ -44,43 +44,123 @@
Button { Button {
width: 100%; width: 100%;
height: 3; height: 4;
margin: 0 1; margin: 0 1;
background: #5e81ac; background: #5e81ac;
color: #eceff4; color: #eceff4;
border: none;
text-style: bold; text-style: bold;
border: solid #5e81ac;
} }
Button:hover { Button:hover {
background: #81a1c1; background: #88c0d0;
color: #2e3440;
text-style: bold; text-style: bold;
border: solid #88c0d0;
}
Button:focus {
background: #88c0d0;
color: #2e3440;
text-style: bold;
border: solid #88c0d0;
} }
Button.success { Button.success {
background: #a3be8c; background: #a3be8c;
border: solid #a3be8c;
} }
Button.success:hover { Button.success:hover {
background: #8fbcbb; background: #88c0d0;
color: #2e3440;
text-style: bold;
border: solid #88c0d0;
}
Button.success:focus {
background: #88c0d0;
color: #2e3440;
text-style: bold;
border: solid #88c0d0;
} }
Button.warning { Button.warning {
background: #ebcb8b; background: #ebcb8b;
color: #2e3440; color: #2e3440;
border: solid #ebcb8b;
} }
Button.warning:hover { Button.warning:hover {
background: #d08770; background: #88c0d0;
color: #2e3440; color: #2e3440;
text-style: bold;
border: solid #88c0d0;
}
Button.warning:focus {
background: #88c0d0;
color: #2e3440;
text-style: bold;
border: solid #88c0d0;
} }
Button.error { Button.error {
background: #bf616a; background: #bf616a;
border: solid #bf616a;
} }
Button.error:hover { Button.error:hover {
background: #d08770; background: #88c0d0;
color: #2e3440;
text-style: bold;
border: solid #88c0d0;
}
Button.error:focus {
background: #88c0d0;
color: #2e3440;
text-style: bold;
border: solid #88c0d0;
}
#send {
background: #a3be8c;
border: solid #a3be8c;
}
#send:focus {
background: #88c0d0;
color: #2e3440;
text-style: bold;
border: solid #88c0d0;
}
#send:hover {
background: #88c0d0;
color: #2e3440;
text-style: bold;
border: solid #88c0d0;
}
#config {
background: #ebcb8b;
color: #2e3440;
border: solid #ebcb8b;
}
#config:hover {
background: #88c0d0;
color: #2e3440;
text-style: bold;
border: solid #88c0d0;
}
#config:focus {
background: #88c0d0;
color: #2e3440;
text-style: bold;
border: solid #88c0d0;
} }
#quit { #quit {
@@ -90,25 +170,16 @@ Button.error:hover {
#quit:hover { #quit:hover {
background: #d08770; background: #d08770;
border: solid #ebcb8b; color: #eceff4;
text-style: bold;
border: none;
} }
#send { #quit:focus {
background: #a3be8c;
}
#config {
background: #ebcb8b;
color: #2e3440;
}
#config:hover {
background: #d08770; background: #d08770;
color: #2e3440; color: #eceff4;
} text-style: bold;
border: none;
#send:hover {
background: #8fbcbb;
} }
/* Configuration Dialog Styles */ /* Configuration Dialog Styles */

View File

@@ -26,6 +26,10 @@ class RconApp(App):
) )
async def on_key(self, event) -> None: async def on_key(self, event) -> None:
# prevent keypresses from ConfigScreen from triggering actions in RconApp
if self.screen and isinstance(self.screen, ConfigScreen):
return
match event.key: match event.key:
case 'enter' if self.query_one('#command', Input).has_focus: case 'enter' if self.query_one('#command', Input).has_focus:
self.query_one('#send', Button).press() self.query_one('#send', Button).press()
@@ -64,9 +68,14 @@ class RconApp(App):
self.query_one('#response', RichLog).write( self.query_one('#response', RichLog).write(
self.writable.parse(cmd, response) self.writable.parse(cmd, response)
) )
except RCONError as e: except RCONError:
output = (
'Unable to execute command.',
'It may be due to a map change or a server restart.',
'If the problem persists, please check your connection settings and ensure the server is running.',
)
self.query_one('#response', RichLog).write( self.query_one('#response', RichLog).write(
f'{type(e).__name__}: Unable to connect to server: is the server running and are the host, port, and password correct? ({e})' self.writable.error('\n'.join(output))
) )
self.query_one('#command', Input).value = '' self.query_one('#command', Input).value = ''

View File

@@ -1,9 +1,12 @@
import re import re
from rich.table import Table from rich.table import Table
from rich.text import Text
from .settings import settings from .settings import settings
Renderable = Text | Table | str
class Writable: class Writable:
RE_COLOR_CODES = re.compile(r'\^[0-9]') RE_COLOR_CODES = re.compile(r'\^[0-9]')
@@ -34,20 +37,22 @@ class Writable:
def remove_color_codes(s: str) -> str: def remove_color_codes(s: str) -> str:
return Writable.RE_COLOR_CODES.sub('', s) return Writable.RE_COLOR_CODES.sub('', s)
def parse(self, cmd, response: str) -> str: def parse(self, cmd, response: str, style=None) -> Renderable:
response = response.removeprefix('print\n') response = response.removeprefix('print\n')
if settings.raw: if settings.raw:
return response return Text(response, style=style)
match cmd: match cmd:
case 'status': case 'status':
return self.status_table(response) return self.status_table(response)
case _: case _:
match self.RE_CVAR.match(response): if m := self.RE_CVAR.match(response):
case None: return self.cvar_table(m)
return self.remove_color_codes(response) else:
case m: return Text(self.remove_color_codes(response), style=style)
return self.cvar_table(m)
def error(self, message: str) -> Text:
return Text(message, style='#c73d4b')
def status_table(self, status_response: str) -> Table | str: def status_table(self, status_response: str) -> Table | str:
table = Table(show_header=True, header_style='bold #88c0d0') table = Table(show_header=True, header_style='bold #88c0d0')