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.app import App
from textual.css.query import NoMatches from textual.css.query import NoMatches
from textual.events import Resize
from tui.config import has_remotes, has_targets from tui.config import has_remotes, has_targets
from tui.screens.main_menu import MainMenuScreen from tui.screens.main_menu import MainMenuScreen
@@ -62,13 +63,36 @@ class GnizaApp(App):
else: else:
self.notify(f"{job.label} failed (exit code {message.return_code})", severity="error") 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: def action_toggle_docs(self) -> None:
try: try:
panel = self.screen.query_one("#docs-panel") panel = self.screen.query_one("#docs-panel")
panel.display = not panel.display panel.display = not panel.display
panel._user_toggled = True
except NoMatches: except NoMatches:
pass 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: async def action_quit(self) -> None:
if job_manager.running_count() > 0: if job_manager.running_count() > 0:
from tui.widgets import ConfirmDialog from tui.widgets import ConfirmDialog

View File

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

View File

@@ -57,8 +57,16 @@ class MainMenuScreen(Screen):
yield Footer() yield Footer()
def on_mount(self) -> None: def on_mount(self) -> None:
self._update_logo_visibility()
self.query_one("#menu-list", OptionList).focus() 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: def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
option_id = event.option.id option_id = event.option.id
if option_id == "quit": if option_id == "quit":