This commit is contained in:
onysd 2026-07-31 13:28:13 +03:00
parent 4a803f87e4
commit ded4aa04c7

View file

@ -607,11 +607,18 @@ class CopyButton(Static):
class LogTailScreen(Screen):
"""Live-tails one or two log files. Escape/b goes back to the main menu."""
"""Live-tails one or two log files. Escape/b goes back to the main menu.
RichLog renders through the terminal like everything else in this app,
so plain mouse-drag text selection doesn't reach the terminal -- Textual
captures the mouse for widget interaction instead. "c" copies everything
shown so far (all panes, with headers) as a workaround, using the same
clipboard/OSC 52 fallback as the CopyButton widgets."""
BINDINGS = [
Binding("escape", "back", "Back"),
Binding("b", "back", "Back"),
Binding("c", "copy_logs", "Copy logs"),
]
def __init__(self, panes: list[tuple[str, Path]]):
@ -619,6 +626,7 @@ class LogTailScreen(Screen):
self._panes = panes
self._offsets: dict[Path, int] = {path: 0 for _, path in panes}
self._logs: dict[Path, RichLog] = {}
self._buffers: dict[Path, list[str]] = {path: [] for _, path in panes}
def compose(self) -> ComposeResult:
yield Header()
@ -649,6 +657,7 @@ class LogTailScreen(Screen):
text = data.decode("utf-8", errors="replace")
if text:
self._logs[path].write(text)
self._buffers[path].append(text)
def _poll(self) -> None:
for _, path in self._panes:
@ -664,11 +673,25 @@ class LogTailScreen(Screen):
f.seek(offset)
data = f.read()
self._offsets[path] = size
self._logs[path].write(data.decode("utf-8", errors="replace"))
text = data.decode("utf-8", errors="replace")
self._logs[path].write(text)
self._buffers[path].append(text)
def action_back(self) -> None:
self.app.pop_screen()
def action_copy_logs(self) -> None:
parts = []
for title, path in self._panes:
content = "".join(self._buffers[path]).strip()
parts.append(f"--- {title} ({path.name}) ---\n{content or '(no logs yet)'}")
text = "\n\n".join(parts)
if copy_to_clipboard(text):
self.notify("Logs copied to clipboard")
return
self.app.copy_to_clipboard(text)
self.notify("Logs copied to clipboard")
class LogPickerScreen(Screen):
BINDINGS = [Binding("escape", "back", "Back")]
@ -803,7 +826,12 @@ SETUP_FIELDS: list[tuple[str, list[SetupField]]] = [
SetupField(
"TELESRV_ADVERTISE_IP", "Server public IP or hostname",
"What clients use to reach this server -- your VPS's public IP, "
"or a domain name if you have one pointed at it.",
"or a domain name if you have one pointed at it. Calls (TURN/SFU) "
"are the one exception: they need a literal IP address, not a "
"hostname, and default to reusing this value -- if you enter a "
"hostname here and want calls to work, set "
"TELESRV_TURN_ADVERTISE_IP / TELESRV_SFU_ADVERTISE_IP to a real "
"IP separately afterward, from Configure .env.",
required=True,
),
]),