diff --git a/README.md b/README.md index da76752..6ce11fd 100644 --- a/README.md +++ b/README.md @@ -84,10 +84,12 @@ Environment variables override the config file: `shusub2` opens a single dashboard with Accounts, Keys, Logs, and Errors tables stacked vertically. Press `a`, `k`, `l`, or `e` to focus a table, `/` to filter all four tables, and `r` to refresh all data. The two-line detail area -follows the selected row. +follows the selected row. Accounts, Keys, Logs, and Errors use green, yellow, +magenta, and red section styling respectively. -The dashboard uses compact columns sized to fit an 80-column terminal without -horizontal scrolling: +The dashboard sizes every column from the fetched content so wide terminals show +complete keys, accounts, and models. On a narrow terminal, a table scrolls +horizontally instead of truncating a field: ```text ACCOUNT | Group | Today | Daily | 5h | 7d | Avail diff --git a/pyproject.toml b/pyproject.toml index 222d917..bcc9f05 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "shusub2" -version = "0.2.8" +version = "0.2.9" description = "Terminal UI for Sub2API account quota and daily usage" readme = "README.md" requires-python = ">=3.11" diff --git a/sub2api_quota_tui.py b/sub2api_quota_tui.py index 0147481..19923e5 100644 --- a/sub2api_quota_tui.py +++ b/sub2api_quota_tui.py @@ -19,7 +19,7 @@ from typing import Any APP_NAME = "shusub2" -FALLBACK_VERSION = "0.2.8" +FALLBACK_VERSION = "0.2.9" DEFAULT_API_URL = "http://127.0.0.1:18318/api/tui/accounts" DEFAULT_CONFIG_FILE = "~/.config/shusub2/api-url" DEFAULT_STATUS_CONFIG_FILE = "~/.config/shusub2/status-url" @@ -1258,57 +1258,30 @@ def run_textual( yield Footer() @staticmethod - def configure_table(table: DataTable, columns: tuple[tuple[str, int], ...]) -> None: + def configure_table(table: DataTable, columns: tuple[str, ...]) -> None: table.cursor_type = "row" table.zebra_stripes = True - for label, width in columns: - table.add_column(label, width=width) + # Content-sized columns preserve complete values on wide terminals; + # narrow terminals use DataTable's horizontal scrolling instead. + for label in columns: + table.add_column(label) def on_mount(self) -> None: self.configure_table( self.query_one("#accounts", DataTable), - ( - ("ACCOUNT", 16), - ("Group", 5), - ("Today", 8), - ("Daily", 10), - ("5h", 9), - ("7d", 9), - ("Avail", 7), - ), + ("ACCOUNT", "Group", "Today", "Daily", "5h", "7d", "Avail"), ) self.configure_table( self.query_one("#keys", DataTable), - ( - ("KEY", 16), - ("Today", 9), - ("Tokens", 8), - ("Req", 6), - ), + ("KEY", "Today", "Tokens", "Req"), ) self.configure_table( self.query_one("#logs", DataTable), - ( - ("LOG KEY", 10), - ("Account", 10), - ("Model", 12), - ("Cost", 7), - ("Latency", 7), - ("Time", 11), - ("Age", 7), - ), + ("LOG KEY", "Account", "Model", "Cost", "Latency", "Time", "Age"), ) self.configure_table( self.query_one("#errors", DataTable), - ( - ("ERR", 4), - ("Status", 6), - ("Key", 12), - ("Account", 11), - ("Model", 12), - ("Time", 11), - ("Age", 8), - ), + ("ERR", "Status", "Key", "Account", "Model", "Time", "Age"), ) self.refresh_all(refresh=True) self.set_interval(refresh_seconds, self.refresh_accounts) diff --git a/tests/test_payload.py b/tests/test_payload.py index e1491f9..198e66b 100644 --- a/tests/test_payload.py +++ b/tests/test_payload.py @@ -678,7 +678,7 @@ class Sub2APILogsTests(unittest.TestCase): class DashboardLayoutTests(unittest.IsolatedAsyncioTestCase): - async def test_dashboard_fits_three_tables_at_80_by_24_and_focuses_each(self) -> None: + async def test_dashboard_supports_wide_columns_at_80_by_24_and_focuses_each(self) -> None: from textual.app import App mod = load_module() @@ -688,7 +688,7 @@ class DashboardLayoutTests(unittest.IsolatedAsyncioTestCase): "accounts": [ { "id": 1, - "name": "account-one", + "name": "account-one-with-a-complete-expanded-dashboard-name", "routing_group": "fast", "provider": "openai", "kind": "pay_as_you_go", @@ -708,9 +708,9 @@ class DashboardLayoutTests(unittest.IsolatedAsyncioTestCase): "items": [ { "id": 1, - "api_key_name": "key-one", - "account_name": "account-one", - "model": "gpt-5.5-codex", + "api_key_name": "wmy-production-key-with-a-complete-expanded-name", + "account_name": "account-one-with-a-complete-expanded-dashboard-name", + "model": "gpt-5.5-codex-with-a-complete-expanded-dashboard-model-name", "duration_ms": 1200, "created_at": "2026-07-24T12:00:00+08:00", } @@ -723,8 +723,9 @@ class DashboardLayoutTests(unittest.IsolatedAsyncioTestCase): "_node": "cn", "id": 1, "status_code": 500, - "account_name": "account-one", - "requested_model": "gpt-5.5-codex", + "api_key_name": "wmy-production-key-with-a-complete-expanded-name", + "account_name": "account-one-with-a-complete-expanded-dashboard-name", + "requested_model": "gpt-5.5-codex-with-a-complete-expanded-dashboard-model-name", "phase": "upstream", "type": "api_error", "created_at": "2026-07-24T12:01:00+08:00", @@ -767,7 +768,12 @@ class DashboardLayoutTests(unittest.IsolatedAsyncioTestCase): tables = [screen.query_one(selector) for selector in ("#accounts", "#keys", "#logs", "#errors")] for table in tables: self.assertGreaterEqual(table.region.height, 4) - self.assertLessEqual(table.virtual_size.width, table.region.width) + logs_table = screen.query_one("#logs") + errors_table = screen.query_one("#errors") + self.assertGreater(logs_table.virtual_size.width, logs_table.region.width) + self.assertGreater(errors_table.virtual_size.width, errors_table.region.width) + self.assertGreater(logs_table.max_scroll_x, 0) + self.assertGreater(errors_table.max_scroll_x, 0) self.assertEqual(len({table.styles.background for table in tables}), 4) self.assertIn("wmy", str(screen.query_one("#summary").render())) for key, expected_id in (("k", "keys"), ("l", "logs"), ("e", "errors"), ("a", "accounts")): diff --git a/uv.lock b/uv.lock index bd9a178..1275182 100644 --- a/uv.lock +++ b/uv.lock @@ -85,7 +85,7 @@ wheels = [ [[package]] name = "shusub2" -version = "0.2.8" +version = "0.2.9" source = { editable = "." } dependencies = [ { name = "textual" },