From 3bda102001da59cb29eace2c6679d21344b72325 Mon Sep 17 00:00:00 2001 From: Keys <70819367+keys-i@users.noreply.github.com> Date: Thu, 13 Aug 2026 09:15:36 +1000 Subject: [PATCH] add: highlight dashboard file previews --- dev/gui.py | 56 ++++++++++++++++++++++++++++++++++++++++++-------- dev/gui.py.man | 2 ++ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/dev/gui.py b/dev/gui.py index 039c4ce..a26de0c 100755 --- a/dev/gui.py +++ b/dev/gui.py @@ -36,6 +36,7 @@ from typing import ClassVar from _manual import PRIVILEGE_LABELS +from rich.syntax import Syntax from rich.text import Text from textual import on, work from textual.app import App, ComposeResult @@ -60,7 +61,6 @@ Static, TabbedContent, TabPane, - TextArea, ) ARCHIVE_URL = "https://github.com/keys-i/scripts/archive/refs/heads/main.zip" @@ -713,6 +713,20 @@ def terminal_text(line: str) -> Text: return Text.from_ansi(line) +def syntax_lexer(path: Path, text: str) -> str: + if path.suffix.casefold() == ".man": + return "markdown" + lexer = Syntax.guess_lexer(str(path), text) + if lexer != "default": + return lexer + shebang = text.partition("\n")[0].casefold() + if "uv run --script" in shebang or "python" in shebang: + return "python" + if any(shell in shebang for shell in ("bash", "zsh", "/sh", " sh")): + return "bash" + return "text" + + def read_file_preview(path: Path, root: Path) -> str: """Read a bounded regular file without following a final symlink.""" if path.is_symlink(): @@ -1701,10 +1715,8 @@ def compose(self) -> ComposeResult: nerd_fonts=self.nerd_fonts, id="repo-tree", ) - yield TextArea( - read_only=True, - show_line_numbers=True, - placeholder="Select a file to preview it", + yield RichLog( + auto_scroll=False, id="file-preview", ) yield Footer() @@ -1716,6 +1728,7 @@ def on_mount(self) -> None: table.add_column("Script", key="script") history = self.query_one("#history", DataTable) history.add_columns("Time", "Script", "Phase", "Result", "Seconds") + self.query_one("#file-preview", RichLog).write("Select a file to preview it") self.refresh_catalog() self.set_interval(0.25, self.update_elapsed) @@ -1879,7 +1892,7 @@ def apply_sort(self, column: str) -> None: @on(DirectoryTree.FileSelected, "#repo-tree") async def preview_file(self, event: DirectoryTree.FileSelected) -> None: - preview = self.query_one("#file-preview", TextArea) + preview = self.query_one("#file-preview", RichLog) try: text = await asyncio.to_thread( read_file_preview, @@ -1887,8 +1900,17 @@ async def preview_file(self, event: DirectoryTree.FileSelected) -> None: self.root, ) except GuiError as error: - text = str(error) - preview.load_text(text) + rendered: str | Syntax = str(error) + else: + rendered = Syntax( + text, + syntax_lexer(event.path, text), + theme="ansi_dark" if self.current_theme.dark else "ansi_light", + line_numbers=True, + ) + preview.clear() + preview.write(rendered) + preview.scroll_home(animate=False) @on(Button.Pressed) def press_button(self, event: Button.Pressed) -> None: @@ -2471,6 +2493,24 @@ def layout_snapshot(app: ScriptsApp) -> dict[str, tuple[int, int, int, int]]: nerd_tree.ICON_NODE, nerd_tree.ICON_NODE_EXPANDED, ) == ("󰈔 ", "󰉋 ", "󰝰 ") + wide.action_files() + await pilot.pause() + await wide.preview_file( + DirectoryTree.FileSelected(nerd_tree.root, root / "dev/gui.py") + ) + await pilot.pause() + preview = wide.query_one("#file-preview", RichLog) + assert preview.lines and preview.max_scroll_y > 0 + preview.focus() + await pilot.press("end") + await pilot.pause() + assert preview.scroll_y > 0 + assert syntax_lexer(root / "sys/windows/clean.ps1", "") == "powershell" + assert ( + syntax_lexer(root / "bin/agent", "#!/usr/bin/env -S uv run --script") + == "python" + ) + assert syntax_lexer(root / "unknown", "plain text") == "text" with tempfile.TemporaryDirectory(prefix="scripts-gui-test-") as directory: test_root = Path(directory) probe = test_root / "long-line.py" diff --git a/dev/gui.py.man b/dev/gui.py.man index 54947e4..e69090b 100644 --- a/dev/gui.py.man +++ b/dev/gui.py.man @@ -58,6 +58,8 @@ downloaded. The dashboard colors headings, safety callouts, tables, links, and code with the selected terminal theme. The `.man` source remains plain Markdown, so it also works through `curl`, Glow, GitHub, and terminals without color. +The Files tab uses Rich/Pygments highlighting for every bundled lexer; unknown +formats remain plain text. ## Keyboard and accessibility