Skip to content

[#8] Added four built-in themes with dark and light previews.#50

Open
AlexSkrypnyk wants to merge 7 commits into
mainfrom
feature/8-builtin-themes
Open

[#8] Added four built-in themes with dark and light previews.#50
AlexSkrypnyk wants to merge 7 commits into
mainfrom
feature/8-builtin-themes

Conversation

@AlexSkrypnyk

@AlexSkrypnyk AlexSkrypnyk commented Jul 13, 2026

Copy link
Copy Markdown
Member

Closes #8

Summary

Adds four curated, name-selectable built-in themes - midnight, frost, ember and mono - alongside the existing default theme.
DefaultTheme gains a small palette seam of five protected SGR-role accessors that its colour-carrying appearance atoms route through, so each new theme recolours a form by overriding only those five methods; behaviour for default is unchanged.
ThemeManager registers the four new names, a new playground demo makes them explorable via --theme/--mode, and generated dark/light preview SVGs illustrate each one in the README and the themes documentation page.

Changes

  • Palette seam + theme classes: DefaultTheme gains five protected SGR-role accessors (accentSgr(), valueSgr(), indicatorSgr(), matchSgr(), borderSgr()) that its colour-carrying appearance atoms (title(), value(), indicator(), highlight(), highlightMatch(), border(), marker(), radio(), caret()) now route through instead of each inlining its own $this->isDark ? … : … ternary; behaviour for default is unchanged. Four new classes - MidnightTheme, FrostTheme, EmberTheme and MonoTheme - extend DefaultTheme and override only those five accessors to recolour every widget.
  • Registration: ThemeManager::$registry gains the four new names (midnight, frost, ember, mono) alongside default.
  • Tests: a new BuiltinThemesTest covers each theme's five palette roles per mode, the bold-on-selected value() behaviour, and colour-off degradation to plain text; ThemeManagerTest and AllWidgetsFormTest gain coverage for the new names; FormTest swaps its example theme value from 'dark' to 'midnight'.
  • Playground demos: playground/10-builtin-themes/run.php renders one form under a chosen built-in theme via --theme and --mode; playground/6-theme-detect/run.php now forces the dark or light palette with --mode instead of --theme=dark/--theme=light (which named unregistered themes and errored). Both documented in playground/README.md.
  • Screenshot tooling + previews: docs/util/svg-term-render.js gains a --light option to render on a light surface; docs/util/update-assets.php gains capture jobs for all four themes in both dark and light; the resulting eight SVGs are committed under docs/assets/.
  • Docs: README.md gains a Themes section listing all five themes with preview images; docs/content/themes.mdx is rewritten to document the palette-seam extension pattern and clarify that dark/light is a mode display option, not a separate theme; the new theme names are added to the docs spellcheck dictionary.

Screenshots

This PR adds dark and light preview SVGs for each of the four new themes (docs/assets/theme-{midnight,frost,ember,mono}.svg and the matching -light variants), shown in the README's new Themes section and in docs/content/themes.mdx.

Before / After

Theme registry (ThemeManager::$registry)
-----------------------------------------
BEFORE                          AFTER
┌───────────────────────┐       ┌───────────────────────┐
│ 'default' → Default    │       │ 'default'  → Default   │
└───────────────────────┘       │ 'midnight' → Midnight  │
                                 │ 'frost'    → Frost     │
                                 │ 'ember'    → Ember     │
                                 │ 'mono'     → Mono      │
                                 └───────────────────────┘

DefaultTheme's palette seam
-----------------------------------------
BEFORE - each atom hardcodes its own mode ternary
  title()     → paint($isDark ? '1;36' : '1;34', $text)
  highlight() → paint($isDark ? '1;36' : '1;34', $text)
  marker()    → paint($isDark ? '1;36' : '1;34', $text)
  radio()     → paint($isDark ? '1;36' : '1;34', $text)
  caret()     → paint($isDark ? '1;36' : '1;34', $text)
  value()     → paint($this->emphasize('32', $selected), $text)
  indicator() → paint($isDark ? '1;33' : '35', $text)
  border()    → paint($isDark ? '36' : '34', $text)

AFTER - atoms route through 5 named accessors; a theme overrides only these
  title(), highlight(), marker(), radio(), caret() → accentSgr()
  value()                                          → valueSgr()
  indicator()                                      → indicatorSgr()
  highlightMatch()                                 → matchSgr()
  border()                                         → borderSgr()

  MidnightTheme / FrostTheme / EmberTheme / MonoTheme each extend
  DefaultTheme and override only those 5 accessors - layout, glyphs
  and dark/light mode stay inherited.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds four selectable built-in themes, centralizes mode-dependent palette styling, updates theme documentation and previews, adds a built-in theme playground, and expands tests for resolution, rendering, light/dark modes, and no-color output.

Changes

Built-in theme system

Layer / File(s) Summary
Theme palette architecture
src/Theme/DefaultTheme.php, src/Theme/*Theme.php, src/Theme/ThemeManager.php
Palette SGR selection is centralized in DefaultTheme; midnight, frost, ember, and mono provide specialized palettes and are registered by name.
Theme selection examples and documentation
README.md, docs/content/themes.mdx, playground/10-builtin-themes/run.php, playground/README.md
Documentation and examples describe built-in themes, mode selection, custom themes, and interactive previews.
Theme asset generation
docs/util/svg-term-render.js, docs/util/update-assets.php
Asset jobs generate dark and light SVG renders for the added themes using the new --light option.
Theme resolution and rendering tests
tests/phpunit/Unit/Theme/*, tests/phpunit/Unit/Builder/FormTest.php, tests/phpunit/Unit/Testing/AllWidgetsFormTest.php, docs/cspell.json
Tests cover theme lookup, palette output, selected values, no-color behavior, builder configuration, widget rendering, and updated documentation vocabulary.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Form
  participant ThemeManager
  participant BuiltInTheme
  participant Widgets
  Form->>ThemeManager: resolve selected theme name
  ThemeManager->>BuiltInTheme: create palette instance
  BuiltInTheme->>Widgets: provide mode-dependent styles
  Widgets->>Form: render themed fields
Loading

Possibly related PRs

  • drevops/tui#3: Introduced the ThemeManager-based theme architecture extended here.
  • drevops/tui#21: Refactored the theming code path that these built-in themes extend.
  • drevops/tui#35: Added matched-character highlighting that is now routed through centralized palette helpers.

Suggested labels: A3

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR adds four registered theme classes, tests resolution/rendering, preserves loud failures, and updates the README as required.
Out of Scope Changes check ✅ Passed The changes all support theme additions, previews, documentation, or tests, with no unrelated functionality introduced.
Docstring Coverage ✅ Passed Docstring coverage is 84.44% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: four new built-in themes and their dark/light previews.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/8-builtin-themes

Comment @coderabbitai help to get the list of available commands.

@AlexSkrypnyk AlexSkrypnyk added the A2 Board worker 2 label Jul 13, 2026
@AlexSkrypnyk AlexSkrypnyk added this to the 0.1.0 milestone Jul 13, 2026
@AlexSkrypnyk AlexSkrypnyk added the AUTOMERGE Worker auto-merges this PR once it is ready for review label Jul 13, 2026
@github-actions

This comment has been minimized.

@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.42%. Comparing base (178719e) to head (9893322).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #50   +/-   ##
=======================================
  Coverage   99.41%   99.42%           
=======================================
  Files          84       88    +4     
  Lines        2744     2769   +25     
=======================================
+ Hits         2728     2753   +25     
  Misses         16       16           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@playground/README.md`:
- Around line 144-150: Update the older dark/light command examples in the
README to use the runner’s --mode=dark and --mode=light options instead of
--theme=dark and --theme=light, keeping the documented commands consistent with
the current theme definitions.

In `@README.md`:
- Line 158: Update the theme description near “Every form is themeable” to
hyphenate “built-in” when describing the shipped themes, leaving the rest of the
wording unchanged.

In `@src/Theme/DefaultTheme.php`:
- Around line 292-304: Update the valueSgr() docblock to remove the “per mode”
wording and describe the base implementation as a constant value, while
preserving the existing SGR behavior and guidance about emphasize().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: dffab024-04e0-44ef-aabb-fc0a49938eab

📥 Commits

Reviewing files that changed from the base of the PR and between 178719e and 62fd7dc.

⛔ Files ignored due to path filters (8)
  • docs/assets/theme-ember-light.svg is excluded by !**/*.svg
  • docs/assets/theme-ember.svg is excluded by !**/*.svg
  • docs/assets/theme-frost-light.svg is excluded by !**/*.svg
  • docs/assets/theme-frost.svg is excluded by !**/*.svg
  • docs/assets/theme-midnight-light.svg is excluded by !**/*.svg
  • docs/assets/theme-midnight.svg is excluded by !**/*.svg
  • docs/assets/theme-mono-light.svg is excluded by !**/*.svg
  • docs/assets/theme-mono.svg is excluded by !**/*.svg
📒 Files selected for processing (16)
  • README.md
  • docs/content/themes.mdx
  • docs/util/svg-term-render.js
  • docs/util/update-assets.php
  • playground/10-builtin-themes/run.php
  • playground/README.md
  • src/Theme/DefaultTheme.php
  • src/Theme/EmberTheme.php
  • src/Theme/FrostTheme.php
  • src/Theme/MidnightTheme.php
  • src/Theme/MonoTheme.php
  • src/Theme/ThemeManager.php
  • tests/phpunit/Unit/Builder/FormTest.php
  • tests/phpunit/Unit/Testing/AllWidgetsFormTest.php
  • tests/phpunit/Unit/Theme/BuiltinThemesTest.php
  • tests/phpunit/Unit/Theme/ThemeManagerTest.php

Comment thread playground/README.md
Comment thread README.md Outdated
Comment thread src/Theme/DefaultTheme.php
@github-actions

This comment has been minimized.

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Code Coverage Report:
  2026-07-13 11:46:12

 Summary:
  Classes: 93.18% (82/88)
  Methods: 98.59% (630/639)
  Lines:   99.42% (2753/2769)

DrevOps\Tui\Answers\Answer
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Answers\Answers
  Methods: 100.00% ( 9/ 9)   Lines: 100.00% ( 20/ 20)
DrevOps\Tui\Answers\SummaryFormatter
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 20/ 20)
DrevOps\Tui\Builder\FieldBuilder
  Methods: 100.00% (37/37)   Lines: 100.00% (136/136)
DrevOps\Tui\Builder\Form
  Methods:  94.44% (17/18)   Lines:  98.82% ( 84/ 85)
DrevOps\Tui\Builder\PanelBuilder
  Methods: 100.00% (21/21)   Lines: 100.00% ( 33/ 33)
DrevOps\Tui\Condition\CompositeCondition
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 19/ 19)
DrevOps\Tui\Condition\Condition
  Methods: 100.00% (10/10)   Lines: 100.00% ( 38/ 38)
DrevOps\Tui\Config\Config
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% ( 17/ 17)
DrevOps\Tui\Config\DateBounds
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 31/ 31)
DrevOps\Tui\Config\Field
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% ( 57/ 57)
DrevOps\Tui\Config\FieldType
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% (  9/  9)
DrevOps\Tui\Config\NumberBounds
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 20/ 20)
DrevOps\Tui\Config\Option
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% (  9/  9)
DrevOps\Tui\Config\Panel
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Config\Weekday
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 15/ 15)
DrevOps\Tui\Derive\Derive
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 14/ 14)
DrevOps\Tui\Derive\Deriver
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% ( 13/ 13)
DrevOps\Tui\Derive\Transform
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% (  7/  7)
DrevOps\Tui\Discovery\AbstractDiscover
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Discovery\Dotenv
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 19/ 19)
DrevOps\Tui\Discovery\JsonValue
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 16/ 16)
DrevOps\Tui\Discovery\PathExists
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% (  3/  3)
DrevOps\Tui\Discovery\Scan
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 20/ 20)
DrevOps\Tui\Engine\Engine
  Methods: 100.00% (12/12)   Lines: 100.00% (101/101)
DrevOps\Tui\Handler\HandlerRegistry
  Methods:  85.71% ( 6/ 7)   Lines:  95.45% ( 21/ 22)
DrevOps\Tui\Input\ArrayKeyStream
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 12/ 12)
DrevOps\Tui\Input\Binding
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Input\DefaultKeyMap
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% ( 38/ 38)
DrevOps\Tui\Input\Hint
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  2/  2)
DrevOps\Tui\Input\Key
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% (  7/  7)
DrevOps\Tui\Input\KeyEncoder
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% ( 21/ 21)
DrevOps\Tui\Input\KeyMap
  Methods: 100.00% (12/12)   Lines: 100.00% ( 65/ 65)
DrevOps\Tui\Input\KeyMapManager
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% (  8/  8)
DrevOps\Tui\Input\KeyParser
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 68/ 68)
DrevOps\Tui\Input\Scope
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% ( 11/ 11)
DrevOps\Tui\Input\ScopedKeyMap
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% (  4/  4)
DrevOps\Tui\Input\VimKeyMap
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% ( 12/ 12)
DrevOps\Tui\Render\Ansi
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% (  5/  5)
DrevOps\Tui\Render\Box
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 15/ 15)
DrevOps\Tui\Render\ExternalEditor
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 27/ 27)
DrevOps\Tui\Render\HelpSection
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Render\Navigator
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 15/ 15)
DrevOps\Tui\Render\PanelController
  Methods: 100.00% (21/21)   Lines: 100.00% (142/142)
DrevOps\Tui\Render\Scroller
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 12/ 12)
DrevOps\Tui\Render\Terminal
  Methods: 100.00% (14/14)   Lines: 100.00% ( 39/ 39)
DrevOps\Tui\Render\TerminalControl
  Methods: 100.00% ( 9/ 9)   Lines: 100.00% (  9/  9)
DrevOps\Tui\Render\Viewport
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Resolver\InputResolver
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 27/ 27)
DrevOps\Tui\Schema\AgentHelp
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 31/ 31)
DrevOps\Tui\Schema\SchemaGenerator
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 33/ 33)
DrevOps\Tui\Schema\SchemaValidator
  Methods: 100.00% ( 9/ 9)   Lines: 100.00% ( 53/ 53)
DrevOps\Tui\Testing\BufferedTerminal
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 10/ 10)
DrevOps\Tui\Testing\TuiTester
  Methods: 100.00% (11/11)   Lines: 100.00% ( 26/ 26)
DrevOps\Tui\Theme\DefaultTheme
  Methods:  94.59% (70/74)   Lines:  97.94% (238/243)
DrevOps\Tui\Theme\EmberTheme
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% (  5/  5)
DrevOps\Tui\Theme\FrostTheme
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% (  5/  5)
DrevOps\Tui\Theme\MidnightTheme
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% (  5/  5)
DrevOps\Tui\Theme\MonoTheme
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% (  5/  5)
DrevOps\Tui\Theme\ThemeManager
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% (  8/  8)
DrevOps\Tui\Translation\Translator
  Methods: 100.00% (10/10)   Lines: 100.00% ( 43/ 43)
DrevOps\Tui\Tui
  Methods: 100.00% (14/14)   Lines: 100.00% ( 41/ 41)
DrevOps\Tui\Widget\AbstractWidget
  Methods: 100.00% (18/18)   Lines: 100.00% ( 50/ 50)
DrevOps\Tui\Widget\CalendarWidget
  Methods: 100.00% (12/12)   Lines: 100.00% ( 55/ 55)
DrevOps\Tui\Widget\ChoiceListTrait
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% ( 21/ 21)
DrevOps\Tui\Widget\ConfirmWidget
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 23/ 23)
DrevOps\Tui\Widget\FilePickerWidget
  Methods: 100.00% (29/29)   Lines: 100.00% (183/183)
DrevOps\Tui\Widget\MatchResult
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Widget\MatchTier
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  6/  6)
DrevOps\Tui\Widget\Matcher
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% ( 79/ 79)
DrevOps\Tui\Widget\MultiSearchWidget
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% (  3/  3)
DrevOps\Tui\Widget\MultiSelectWidget
  Methods: 100.00% (13/13)   Lines: 100.00% ( 94/ 94)
DrevOps\Tui\Widget\NumberWidget
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 30/ 30)
DrevOps\Tui\Widget\PasswordDisplay
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  5/  5)
DrevOps\Tui\Widget\PasswordWidget
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 40/ 40)
DrevOps\Tui\Widget\PauseWidget
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% ( 11/ 11)
DrevOps\Tui\Widget\ReorderWidget
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 57/ 57)
DrevOps\Tui\Widget\SearchWidget
  Methods: 100.00% ( 9/ 9)   Lines: 100.00% ( 58/ 58)
DrevOps\Tui\Widget\SelectWidget
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 37/ 37)
DrevOps\Tui\Widget\SuggestWidget
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 48/ 48)
DrevOps\Tui\Widget\TextWidget
  Methods: 100.00% (11/11)   Lines: 100.00% ( 53/ 53)
DrevOps\Tui\Widget\TextareaWidget
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 46/ 46)
DrevOps\Tui\Widget\ToggleWidget
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 32/ 32)
DrevOps\Tui\Widget\WidgetFactory
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 46/ 46)
DrevOps\Tui\Widget\WidgetRunner
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  5/  5)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A2 Board worker 2 AUTOMERGE Worker auto-merges this PR once it is ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add more built-in themes

1 participant