From 1c0aefa8505c1993fe31a51feb626b01dc92d280 Mon Sep 17 00:00:00 2001 From: Mateusz Cholewka Date: Sat, 15 Aug 2026 21:54:52 +0200 Subject: [PATCH] Add palette and better navigation --- src/Console/Components/Details.php | 5 +- src/Console/Components/LogItem.php | 81 +++++++++++++++++++-------- src/Console/Components/LogList.php | 12 ++++ src/Console/Components/Navigation.php | 7 ++- src/Console/MessageFormatter.php | 4 +- src/Console/Palette.php | 20 +++++++ src/Console/State/LogItem.php | 1 + src/Console/Windows/Main.php | 20 +++++++ src/Console/Windows/Popup.php | 6 +- src/Server.php | 1 + 10 files changed, 127 insertions(+), 30 deletions(-) create mode 100644 src/Console/Palette.php diff --git a/src/Console/Components/Details.php b/src/Console/Components/Details.php index 62cbc97..579dc91 100644 --- a/src/Console/Components/Details.php +++ b/src/Console/Components/Details.php @@ -18,6 +18,7 @@ use PhpTui\Tui\Extension\Core\Widget\ScrollbarWidget; use PhpTui\Tui\Style\Style; use PhpTui\Tui\Text\Line; +use PhpTui\Tui\Text\Span; use PhpTui\Tui\Text\Text; use PhpTui\Tui\Widget\Widget; use Tapper\Console\CommandAttributes\KeyPressed; @@ -136,7 +137,9 @@ protected function view(Area $area): Widget ]; $infoListItems = array_map(fn ($line) => ListItem::new(Text::fromLine($line)), $info); - $formattedMessage = MessageFormatter::colorizeFormattedJson($log->message); + $formattedMessage = $log->kind === 'wait' + ? [Line::fromSpan(Span::styled($log->message, Style::default()->yellow()))] + : MessageFormatter::colorizeFormattedJson($log->message); $formattedListItems = array_map(fn ($line) => ListItem::new(Text::fromLine($line)), $formattedMessage); $allItems = [ diff --git a/src/Console/Components/LogItem.php b/src/Console/Components/LogItem.php index 8bc73c9..dd25158 100644 --- a/src/Console/Components/LogItem.php +++ b/src/Console/Components/LogItem.php @@ -13,19 +13,20 @@ use PhpTui\Tui\Extension\Core\Widget\ParagraphWidget; use PhpTui\Tui\Layout\Constraint; use PhpTui\Tui\Style\Style; -use PhpTui\Tui\Text\Line; use PhpTui\Tui\Text\Span; -use PhpTui\Tui\Text\Text; use PhpTui\Tui\Widget\Direction; use PhpTui\Tui\Widget\Widget; use Tapper\Console\CommandAttributes\Mouse; use Tapper\Console\Component; use Tapper\Console\MessageFormatter; +use Tapper\Console\Palette; use Tapper\Console\State\LogItem as LogItemState; class LogItem extends Component { - public const int HEIGHT = 3; + public const int HEIGHT = 2; + + private const int SCROLLBAR_GUTTER = 1; private ?LogItemState $log = null; @@ -65,6 +66,43 @@ public function click(): void } } + /** + * @param Span[] $spans + * @return Span[] + */ + private function truncateSpans(array $spans, int $maxWidth, Style $ellipsisStyle): array + { + $totalWidth = array_sum(array_map(fn (Span $span): int => $span->width(), $spans)); + + if ($totalWidth <= $maxWidth) { + return $spans; + } + + $budget = max(0, $maxWidth - 1); + $truncated = []; + + foreach ($spans as $span) { + if ($budget <= 0) { + break; + } + + if ($span->width() <= $budget) { + $truncated[] = $span; + $budget -= $span->width(); + + continue; + } + + $chars = array_slice(mb_str_split($span->content), 0, $budget); + $truncated[] = Span::styled(implode('', $chars), $span->style); + $budget = 0; + } + + $truncated[] = Span::styled('…', $ellipsisStyle); + + return $truncated; + } + protected function view(Area $area): Widget { if (! $this->log) { @@ -77,35 +115,34 @@ protected function view(Area $area): Widget $mark = $this->appState->cursor === $this->log->id; $darkGray = Style::default()->darkGray(); - $markerColor = RgbColor::fromHex('2a2e42'); + $markerColor = RgbColor::fromHex(Palette::SELECTION_BG); $mStyle = Style::default()->bg($markerColor); $message = $this->log->message; + $messageWidth = max(0, $area->width - 17 - self::SCROLLBAR_GUTTER); - $firstLine = ParagraphWidget::fromText( - Text::fromLines( - Line::fromSpans(Span::styled("$time", Style::default()->fg(RgbColor::fromHex('7aa2f7')))), - Line::fromSpans(Span::styled('', $darkGray)), - ) + $timeColumn = ParagraphWidget::fromSpans( + Span::styled($time, Style::default()->fg(RgbColor::fromHex(Palette::ACCENT))), ); - $wMess = ParagraphWidget::fromSpans(...MessageFormatter::colorizeInlineJson($message)); - $wFile = ParagraphWidget::fromSpans(Span::styled(sprintf('↪ %s', $this->log->caller), $darkGray)); + $messageSpans = $this->log->kind === 'wait' + ? [Span::styled($message, Style::default()->yellow())] + : MessageFormatter::colorizeInlineJson($message); + + $wMess = ParagraphWidget::fromSpans(...$this->truncateSpans($messageSpans, $messageWidth, $darkGray)); + $wFile = ParagraphWidget::fromSpans( + ...$this->truncateSpans([Span::styled(sprintf('↪ %s', $this->log->caller), $darkGray)], $messageWidth, $darkGray), + ); if ($mark) { - $firstLine->style($mStyle); + $timeColumn->style($mStyle); $wMess->style($mStyle); $wFile->style($mStyle); } - $firstLine = GridWidget::default() - ->direction(Direction::Vertical) - ->constraints(Constraint::length(2), Constraint::length(1)) - ->widgets($firstLine); - - $secondLine = GridWidget::default() + $messageColumn = GridWidget::default() ->direction(Direction::Vertical) - ->constraints(Constraint::length(1), Constraint::length(1), Constraint::length(1)) + ->constraints(Constraint::length(1), Constraint::length(1)) ->widgets( $wMess, $wFile, @@ -117,10 +154,10 @@ protected function view(Area $area): Widget ->direction(Direction::Horizontal) ->constraints( Constraint::length(17), - Constraint::length($area->width - 17), + Constraint::length($messageWidth), )->widgets( - $firstLine, - $secondLine, + $timeColumn, + $messageColumn, ), ); } diff --git a/src/Console/Components/LogList.php b/src/Console/Components/LogList.php index 16388fe..259eff3 100644 --- a/src/Console/Components/LogList.php +++ b/src/Console/Components/LogList.php @@ -82,6 +82,18 @@ public function scrollDown(): void $this->scroll->scrollDown($this->count, $this->visible); } + #[KeyPressed('g')] + public function jumpToTop(): void + { + $this->scroll->jump(0, $this->count, $this->visible); + } + + #[KeyPressed('G')] + public function jumpToBottom(): void + { + $this->scroll->jump($this->count - 1, $this->count, $this->visible); + } + #[KeyPressed('u', KeyModifiers::CONTROL)] public function pageUp(): void { diff --git a/src/Console/Components/Navigation.php b/src/Console/Components/Navigation.php index 4b93ac6..7a284a9 100644 --- a/src/Console/Components/Navigation.php +++ b/src/Console/Components/Navigation.php @@ -14,6 +14,7 @@ use PhpTui\Tui\Widget\BorderType; use PhpTui\Tui\Widget\Widget; use Tapper\Console\Component; +use Tapper\Console\Palette; class Navigation extends Component { @@ -23,14 +24,14 @@ protected function view(Area $area): Widget $waiting = $this->appState->pendingWaits > 0; - $buttonStyle = Style::default()->fg(RgbColor::fromHex('7aa2f7')); - $descStyle = Style::default()->fg(RgbColor::fromHex('7aa2f7')); + $buttonStyle = Style::default()->fg(RgbColor::fromHex(Palette::ACCENT)); + $descStyle = Style::default()->fg(RgbColor::fromHex(Palette::ACCENT)); $delimiter = Span::styled(' | ', $descStyle); $instruction = []; if (! $this->appState->live && ! $this->appState->previewLog) { - $instruction[] = Span::styled('Live: ', $descStyle, $descStyle); + $instruction[] = Span::styled('Live: ', $descStyle); $instruction[] = Span::styled('esc', $buttonStyle); $instruction[] = $delimiter; } diff --git a/src/Console/MessageFormatter.php b/src/Console/MessageFormatter.php index 736a37c..359972c 100644 --- a/src/Console/MessageFormatter.php +++ b/src/Console/MessageFormatter.php @@ -21,11 +21,11 @@ class MessageFormatter private const string NULL_COLOR = '2bc3de'; - private const string BRACKETS_COLOR = 'c0caf5'; + private const string BRACKETS_COLOR = Palette::TEXT_DEFAULT; private const string PUNCTUATION_COLOR = '89ddff'; - private const string ERROR_COLOR = 'f7768e'; + private const string ERROR_COLOR = Palette::ERROR; public static function colorizeInlineJson(string $json): array { diff --git a/src/Console/Palette.php b/src/Console/Palette.php new file mode 100644 index 0000000..b6782cd --- /dev/null +++ b/src/Console/Palette.php @@ -0,0 +1,20 @@ +width < self::MIN_WIDTH || $area->height < self::MIN_HEIGHT) { + return BlockWidget::default() + ->widget( + ParagraphWidget::fromLines( + Line::fromString('Terminal too small')->alignment(HorizontalAlignment::Center), + Line::fromString(sprintf('Resize to at least %dx%d', self::MIN_WIDTH, self::MIN_HEIGHT)) + ->alignment(HorizontalAlignment::Center) + ->darkGray(), + ), + ); + } + $verticalConstraints = [ Constraint::length(2), Constraint::length($area->height - 4), diff --git a/src/Console/Windows/Popup.php b/src/Console/Windows/Popup.php index 4c69800..3b0ef1c 100644 --- a/src/Console/Windows/Popup.php +++ b/src/Console/Windows/Popup.php @@ -21,6 +21,7 @@ use PhpTui\Tui\Widget\Widget; use Tapper\Console\CommandAttributes\KeyPressed; use Tapper\Console\Component; +use Tapper\Console\Palette; class Popup extends Component { @@ -40,11 +41,12 @@ public function close(): void protected function view(Area $area): Widget { - $keyStyle = Style::default()->fg(RgbColor::fromHex('7aa2f7')); - $descStyle = Style::default()->fg(RgbColor::fromHex('c0caf5')); + $keyStyle = Style::default()->fg(RgbColor::fromHex(Palette::ACCENT)); + $descStyle = Style::default()->fg(RgbColor::fromHex(Palette::TEXT_DEFAULT)); $shortcuts = [ ['↑ / k', '↓ / j', 'Move cursor'], + ['g', 'G', 'Jump to top / bottom'], ['Ctrl+u', 'Ctrl+d', 'Page up / down'], ['space', 'enter', 'Open details'], ['enter', '', 'Continue a paused wait'], diff --git a/src/Server.php b/src/Server.php index be9ae2c..d5a6af2 100644 --- a/src/Server.php +++ b/src/Server.php @@ -80,6 +80,7 @@ public function run(): void $params['trace'], $params['rootDir'], $params['code'], + kind: 'wait', )); self::$id++;