mirror of
https://github.com/onyx-and-iris/q3rcon-tui.git
synced 2026-04-09 05:33:31 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fcc91b7e34 | |||
| 9b3ae629f3 | |||
| 74ed189ca5 | |||
| 6e50e0861f | |||
| ab4898dac3 | |||
| 086eeba916 | |||
| 2075e98c17 | |||
| 923faa67ec | |||
| 76483a24b9 |
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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.1'
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
@@ -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 = (
|
||||||
|
f'Unable to execute command {cmd}. ',
|
||||||
|
'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 = ''
|
||||||
|
|||||||
@@ -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.remove_color_codes(response)
|
|
||||||
case m:
|
|
||||||
return self.cvar_table(m)
|
return self.cvar_table(m)
|
||||||
|
else:
|
||||||
|
return Text(self.remove_color_codes(response), style=style)
|
||||||
|
|
||||||
|
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')
|
||||||
|
|||||||
Reference in New Issue
Block a user