Make TUI responsive to terminal size

- Auto-hide docs panel when terminal < 100 cols, respects manual F1 toggle
- Hide logo on main menu when terminal < 80 cols
- DataTable: flexible height (auto with min/max) instead of fixed 12
- Dialogs, pickers, wizard: percentage-based widths with max-width caps
- Menu list: flexible height and width constraints

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
shuki
2026-03-06 23:44:23 +02:00
parent 5ea4bea00a
commit 3ebf692b36
3 changed files with 56 additions and 11 deletions

View File

@@ -1,5 +1,6 @@
from textual.app import App
from textual.css.query import NoMatches
from textual.events import Resize
from tui.config import has_remotes, has_targets
from tui.screens.main_menu import MainMenuScreen
@@ -62,13 +63,36 @@ class GnizaApp(App):
else:
self.notify(f"{job.label} failed (exit code {message.return_code})", severity="error")
# Width threshold for auto-hiding the docs panel
DOCS_AUTO_HIDE_WIDTH = 100
def action_toggle_docs(self) -> None:
try:
panel = self.screen.query_one("#docs-panel")
panel.display = not panel.display
panel._user_toggled = True
except NoMatches:
pass
def on_resize(self, event: Resize) -> None:
try:
panel = self.screen.query_one("#docs-panel")
except NoMatches:
return
if getattr(panel, "_user_toggled", False):
return
panel.display = event.size.width >= self.DOCS_AUTO_HIDE_WIDTH
def on_screen_resume(self) -> None:
"""Re-evaluate docs panel visibility when switching screens."""
try:
panel = self.screen.query_one("#docs-panel")
except NoMatches:
return
if getattr(panel, "_user_toggled", False):
return
panel.display = self.size.width >= self.DOCS_AUTO_HIDE_WIDTH
async def action_quit(self) -> None:
if job_manager.running_count() > 0:
from tui.widgets import ConfirmDialog

View File

@@ -28,8 +28,10 @@ Screen {
#menu-list {
width: 1fr;
max-width: 35;
height: 20;
max-width: 40;
min-width: 25;
height: auto;
max-height: 22;
margin: 1 2 0 2;
}
@@ -39,7 +41,9 @@ Screen {
/* Data tables */
DataTable {
height: 12;
height: auto;
min-height: 5;
max-height: 16;
margin: 1 0;
}
@@ -125,6 +129,7 @@ SelectionList {
#set-buttons {
height: auto;
margin: 1 0;
overflow: hidden;
}
#backup-buttons Button,
@@ -145,7 +150,8 @@ SelectionList {
/* Dialogs */
#confirm-dialog {
width: 60;
width: 80%;
max-width: 60;
height: auto;
padding: 2;
background: $panel;
@@ -173,8 +179,10 @@ SelectionList {
/* Folder picker */
#folder-picker {
width: 70;
height: 30;
width: 90%;
max-width: 70;
height: 80%;
max-height: 30;
padding: 1;
background: $panel;
border: thick $accent;
@@ -216,7 +224,8 @@ SelectionList {
/* Operation log */
#op-log {
width: 80%;
width: 90%;
max-width: 120;
height: 80%;
padding: 1;
background: $panel;
@@ -254,7 +263,8 @@ SelectionList {
/* Snapshot browser */
#snapshot-browser {
width: 80%;
width: 90%;
max-width: 120;
height: 80%;
padding: 1;
background: $panel;
@@ -302,7 +312,8 @@ SelectionList {
/* Wizard */
#wizard {
width: 60;
width: 90%;
max-width: 60;
padding: 2;
}
@@ -350,8 +361,10 @@ Switch {
/* File picker */
#file-picker {
width: 70;
height: 30;
width: 90%;
max-width: 70;
height: 80%;
max-height: 30;
padding: 1;
background: $panel;
border: thick $accent;

View File

@@ -57,8 +57,16 @@ class MainMenuScreen(Screen):
yield Footer()
def on_mount(self) -> None:
self._update_logo_visibility()
self.query_one("#menu-list", OptionList).focus()
def on_resize(self) -> None:
self._update_logo_visibility()
def _update_logo_visibility(self) -> None:
logo = self.query_one("#logo")
logo.display = self.app.size.width >= 80
def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
option_id = event.option.id
if option_id == "quit":