- Update all 4 phosphor themes (Green, Amber, White, Red) to use neutral
dark background (#1E1E1E) matching Spotnik reference
- Simplify theme names (remove 'Phosphor' suffix)
- Menu: Use theme.foreground for items, theme.accent for selection
- Button: Use theme colors for default focused style and focus indicator
- Panel/Card: Use theme.border as default border color
- BorderModifier: Use theme.border as default
All 189 tests passing.
- Remove DefaultTheme, DarkTheme, LightTheme
- GreenPhosphorTheme is now the default theme
- Theme cycling order: Green → Amber → White → Red → NCurses
- .default now maps to GreenPhosphorTheme
The render() method was creating a new EnvironmentValues with the default
theme, overwriting any theme changes made by ThemeManager. Now the current
theme is explicitly set from themeManager.currentTheme.
- ThemeManager is no longer a singleton
- Access via Environment: @Environment(\.themeManager)
- Type-safe setTheme() API: themeManager.setTheme(.amber)
- ThemeManager injected by AppRunner into environment
- Custom themes supported via init(themes:)
QuitBehavior:
- .always: Quit works from any screen (default)
- .rootOnly: Quit only works from main screen (no context pushed)
- Quit item hidden in status bar when not allowed
ThemeManager:
- Singleton for managing theme cycling
- cycleTheme() / cyclePreviousTheme() methods
- setTheme(at:) and setTheme(withId:) for direct access
- Automatically updates environment and triggers re-render
- 't' key now cycles themes in AppRunner
- System items are now built dynamically based on flags
- showHelpItem (default: true) - controls ? help visibility
- showThemeItem (default: true) - controls t theme visibility
- Quit item is always shown when showSystemItems is true
- Removed unused configureSystemItems method
- StatusBarState now initializes with all 3 system items
- Added deduplication logic to StatusBar.renderToBuffer
- User items with same shortcut override system items
- Updated Example App to not duplicate 'q quit'
- Updated tests for 3 system items instead of 1
- StatusBar now has separate userItems and systemItems properties
- User items are sorted by order, then system items (fixed order) appended
- Rendering combines both: [sorted user items] + [system items]
- User items override system items with same shortcut (deduplication)
- StatusBarState passes items separately to StatusBar
- Alignment applies to the combined items
- Updated tests to use userItems property
- Add StatusBarItemOrder for consistent item positioning
- System items (quit, help, theme) appear on the right side
- User items appear on the left, can override system items
- Separate tracking of user vs system items in StatusBarState
- Add showSystemItems flag to disable system items in tests
- Items sorted by order value (lower = left, higher = right)
- User items override system items with the same shortcut
- Add comprehensive tests for system item behavior (189 total)
BREAKING CHANGE: Package name changed due to name collision with
existing rensbreur/SwiftTUI package.
Changes:
- Rename package from SwiftTUI to TUIKit in Package.swift
- Rename Sources/SwiftTUI to Sources/TUIKit
- Rename Sources/SwiftTUIExample to Sources/TUIKitExample
- Rename Tests/SwiftTUITests to Tests/TUIKitTests
- Rename SwiftTUI.swift to TUIKit.swift
- Update all imports: import SwiftTUI -> import TUIKit
- Update all code references: SwiftTUI.renderToBuffer -> TUIKit.renderToBuffer
- Update documentation comments
- Rename swiftTUIVersion to tuiKitVersion
All 181 tests passing.
- Remove FocusManager.shared singleton pattern
- Add FocusManagerKey for Environment access via \.focusManager
- FocusState now reads from EnvironmentStorage
- AppRunner creates and injects FocusManager instance
- Button accesses FocusManager via context.environment
- Update all tests to use independent FocusManager instances
- Add new test suite for Environment integration
- Tests can now run in parallel (no more --no-parallel needed)
181 tests passing
- Add @AppStorage and @SceneStorage for persistent settings
- Add @onAppear, @onDisappear and .task() lifecycle modifiers
- Add Preferences system for bottom-up data flow (child → parent)
- Add UserDefaultsStorage with Linux-compatible implementation
- Add flexible .frame() with minWidth/maxWidth/maxHeight/.infinity
- Fix BorderModifier to reduce availableWidth for content
- Fix StatusBar .justified alignment for equal edge spacing
- Fix Alert vertical expansion by removing Spacer
- Fix q-quit not working when StatusBar item has no action
- Fix render loop caused by statusBarItems modifier
- Add Linux compatibility (Glibc, XDG paths)
This commit introduces a SwiftUI-like Environment system and refactors
the StatusBar from a singleton pattern to an environment-based architecture.
## Environment System (new)
- EnvironmentKey protocol for defining custom environment values
- EnvironmentValues container with subscript access
- EnvironmentStorage for thread-local storage during rendering
- @Environment property wrapper for reading values in views
- EnvironmentModifier and .environment() modifier for injecting values
## StatusBar Refactoring
- Replaced StatusBarManager singleton with StatusBarState class
- StatusBarState is created by AppRunner and injected via Environment
- Views access StatusBar via @Environment(\.statusBar)
- Removed all singleton-based code from StatusBar.swift
## .statusBarItems() Modifier (new)
- .statusBarItems([...]) - set items from array
- .statusBarItems { ... } - set items with builder syntax
- .statusBarItems(context: "name") { ... } - push items to context stack
- Enables declarative StatusBar configuration per view
## Example App
- ContentView now uses .statusBarItems() modifier declaratively
- AppState simplified - no manual StatusBar reference needed
- Each page declares its own status bar items
## Tests
- 178 tests across 26 suites, all passing
- New tests for StatusBarItemsModifier
- Tests updated for StatusBarState API
- Add Shortcut enum with predefined Unicode symbols for keyboard shortcuts:
- Special keys: escape (⎋), enter (↵), tab (⇥), backspace (⌫), etc.
- Arrow keys: single (↑↓←→) and combinations (↑↓, ←→, ↑↓←→)
- Modifier keys: command (⌘), option (⌥), control (⌃), shift (⇧)
- Navigation: home, end, pageUp (⇞), pageDown (⇟)
- Common shortcuts: quit, yes, no, cancel, ok
- Add helper methods:
- Shortcut.combine() for combining symbols
- Shortcut.ctrl() for Ctrl+key display (^c)
- Shortcut.range() for range display (1-9)
- Update Example App to use Shortcut constants
- Add 9 new tests for Shortcut constants (103 total)
- Add FocusManager for tracking which element has focus
- Add Focusable protocol for interactive views
- Add Button view with styles (default, primary, destructive, success, plain)
- Add ButtonRow for horizontal button groups
- Add ButtonStyle with customizable colors, borders, and padding
- Keyboard navigation: Tab to move focus, Enter/Space to activate
- Visual focus indicator (▸) for focused buttons
- Add 16 new tests for Focus and Button systems (74 total)
- Add Buttons & Focus demo page to example app
- Handler must return true if event was consumed, false to propagate
- This fixes 'q' not working to quit the app (was being consumed)
- ESC on sub-pages returns true (consumed), on menu returns false (exits)
- ESC key now goes back to menu (instead of B key)
- ESC only exits app when already on main menu
- Menu divider line now uses same color as border
- Updated footer to show [ESC] Back instead of [B] Back
State Management:
- @TState property wrapper for reactive state
- Binding<T> for two-way data binding
- AppState singleton for triggering re-renders
Event System:
- KeyEvent model with Key enum (arrows, enter, escape, characters, etc.)
- KeyEvent.parse() for terminal escape sequence parsing
- KeyEventDispatcher for routing events to handlers
- .onKeyPress() modifier for handling keyboard input
- Terminal.readKeyEvent() for reading complete key events
Interactive Menu:
- Menu now accepts selection Binding for interactive navigation
- Arrow keys move selection up/down (with wrap-around)
- Enter key and shortcuts trigger onSelect callback
- Automatic key handler registration during render
Example App:
- Full page navigation between demo screens
- Press [B] to go back to menu from any page
- Arrow keys and number shortcuts for menu navigation
- Footer shows available keyboard shortcuts
- Menu now uses Renderable protocol instead of ForEach in body
(ForEach rendering in body not fully working yet)
- Input handler now only exits on ESC/q/Q, ignores other keys
- Menu items render correctly with selection indicator and shortcuts