Configuration
Wijjit uses a Flask-inspired configuration system for managing application settings. All configuration is handled through the app.config object, which provides a flexible and intuitive API for loading and managing settings.
Overview
The configuration system provides:
Multiple loading methods: Direct assignment, files, objects, environment variables
Type-safe defaults: All options have sensible defaults defined in
DefaultConfigAuto-loading:
WIJJIT_*environment variables are automatically loadedValidation: Invalid values are logged with warnings
Documentation: All options are documented with inline comments
Quick Start
Basic configuration is simple and intuitive:
from wijjit import Wijjit
app = Wijjit()
# Direct assignment
app.config['ENABLE_MOUSE'] = False
app.config['QUIT_KEY'] = 'q'
app.config['DEBUG'] = True
# Bulk update
app.config.update(
LOG_LEVEL='INFO',
NOTIFICATION_DURATION=5.0,
SHOW_FPS=True
)
Configuration Loading Methods
The Config class provides six methods for loading configuration:
1. Direct Assignment
The simplest method - just set values directly:
app.config['DEBUG'] = True
app.config['ENABLE_MOUSE'] = False
app.config['THEME_FILE'] = 'my_theme.css'
2. Bulk Update
Update multiple values at once using update():
app.config.update(
DEBUG=True,
LOG_LEVEL='DEBUG',
SHOW_FPS=True,
NOTIFICATION_MAX_STACK=10
)
3. From Python File
Load configuration from a Python file with uppercase variables:
# config.py
DEBUG = True
ENABLE_MOUSE = False
LOG_LEVEL = 'INFO'
NOTIFICATION_DURATION = 5.0
QUIT_KEY = 'q'
THEME_FILE = 'themes/custom.css'
# app.py
app.config.from_pyfile('config.py')
4. From Python Object
Load configuration from a class or module:
class DevelopmentConfig:
DEBUG = True
LOG_LEVEL = 'DEBUG'
SHOW_FPS = True
WARN_SLOW_RENDER_MS = 50
class ProductionConfig:
DEBUG = False
LOG_LEVEL = 'WARNING'
RUN_SYNC_IN_EXECUTOR = True
EXECUTOR_MAX_WORKERS = 4
# Load based on environment
import os
if os.getenv('ENV') == 'production':
app.config.from_object(ProductionConfig)
else:
app.config.from_object(DevelopmentConfig)
from_object also accepts a dotted string path, resolved Flask-style, so
the config target can be selected entirely from configuration/environment without
importing it yourself:
app.config.from_object('myproject.settings') # a module
app.config.from_object('myproject.settings.ProdConfig') # module attribute
app.config.from_object('myproject.settings:ProdConfig') # explicit module:attr form
5. From Environment Variable
Load configuration from a file path stored in an environment variable:
export WIJJIT_SETTINGS=/path/to/config.py
app.config.from_envvar('WIJJIT_SETTINGS')
6. From Prefixed Environment Variables
Environment variables with the WIJJIT_ prefix are automatically loaded:
export WIJJIT_DEBUG=1
export WIJJIT_ENABLE_MOUSE=0
export WIJJIT_LOG_LEVEL=DEBUG
export WIJJIT_QUIT_KEY=q
export WIJJIT_THEME_FILE=/path/to/theme.css
# WIJJIT_* variables are read automatically when the app is created
app = Wijjit()
# app.config['DEBUG'] is now True
# app.config['ENABLE_MOUSE'] is now False
Configuration Options Reference
Input & Interaction (4 options)
ENABLE_MOUSE
- Type:
bool- Default:
True- Description:
Enable mouse event tracking (clicks, hovers, scrolling)
app.config['ENABLE_MOUSE'] = False # Keyboard-only mode
MOUSE_TRACKING_MODE
- Type:
str- Default:
'button_event'- Values:
'button_event','all_events','drag'- Description:
Controls which mouse events are tracked
app.config['MOUSE_TRACKING_MODE'] = 'all_events' # Track all movement
QUIT_KEY
- Type:
str- Default:
'ctrl+q'- Description:
Key binding to quit the application
app.config['QUIT_KEY'] = 'q' # Just 'q'
app.config['QUIT_KEY'] = 'ctrl+c' # Ctrl+C
app.config['QUIT_KEY'] = 'escape' # ESC key
Display & Terminal (5 options)
USE_ALTERNATE_SCREEN
- Type:
bool- Default:
True- Description:
Use alternate screen buffer (recommended for full-screen TUIs)
app.config['USE_ALTERNATE_SCREEN'] = False # Use normal screen
HIDE_CURSOR
- Type:
bool- Default:
True- Description:
Hide terminal cursor during application runtime
app.config['HIDE_CURSOR'] = False # Keep cursor visible
APP_TITLE
- Type:
strorNone- Default:
None- Description:
Terminal window/tab title (OSC 0 text). When set, Wijjit emits the title on startup so the terminal tab or window shows it. Most shells reset the title from their prompt hook when the app exits, so no explicit restore is performed.
Noneleaves the terminal title untouched.
app.config['APP_TITLE'] = 'My Wijjit App' # Set the terminal title
HARDWARE_CURSOR
- Type:
bool- Default:
True- Description:
Park the real terminal cursor on the focused text caret. When the focused element reports a caret cell (
TextInput,TextArea,CodeEditor), each frame ends with a cursor-move plus show-cursor escape, so the hardware cursor sits and blinks on the caret; it is hidden again when no caret is visible. Terminals and screen readers track the hardware cursor, so this is an accessibility improvement over the painted reverse-video caret alone. Works alongside the defaultHIDE_CURSOR=Truestartup state; inline apps are unaffected.
app.config['HARDWARE_CURSOR'] = False # Painted caret only
REMOTE
- Type:
bool- Default:
False- Description:
Run without claiming ownership of the process terminal. The default backend still writes to stdout, but does not install the process-global
SIGTERM/SIGHUP/atexitterminal-restore net or theSIGTSTPsuspend handler. Set this when embedding a Wijjit app in a host process that manages the terminal itself. Custom transports express the same intent viaTerminalBackend.owns_terminaland do not need this flag.
app.config['REMOTE'] = True # Embedded in a host that owns the terminal
Process Control (1 option)
ENABLE_SUSPEND
- Type:
bool- Default:
True- Description:
Enable Ctrl+Z suspend/background support on Unix-like systems (Linux, macOS, BSD). When enabled, pressing Ctrl+Z will properly suspend the application to the background, and
fgwill resume it. On Windows, this option is ignored.
app.config['ENABLE_SUSPEND'] = False # Disable Ctrl+Z suspend
Tip
When suspended with Ctrl+Z, Wijjit automatically:
Saves terminal state (alternate screen, cursor, mouse tracking)
Restores normal terminal so you can use the shell
On resume (
fg), restores TUI state and triggers a re-render
Note
This feature only works on Unix-like systems. On Windows, job control signals are not available.
Colors & Theming (6 options)
FOCUS_COLOR
- Type:
tupleof 3intorNone- Default:
None- Description:
Global focus color override (R, G, B). When set, all focused elements use this foreground color instead of theme defaults.
app.config['FOCUS_COLOR'] = (0, 255, 255) # Cyan focus (default theme color)
app.config['FOCUS_COLOR'] = (255, 128, 0) # Orange focus
app.config['FOCUS_COLOR'] = (0, 255, 0) # Green focus
app.config['FOCUS_COLOR'] = None # Use theme defaults
This is useful for:
Ensuring consistent focus indication across all themes
Accessibility (high-visibility focus colors)
Branding (matching focus color to app accent color)
NO_COLOR
- Type:
bool- Default:
Auto-detected from
NO_COLORenv var- Description:
Disable all ANSI colors (respects NO_COLOR standard)
app.config['NO_COLOR'] = True # Disable colors
export NO_COLOR=1 # Automatically disables colors
DEFAULT_THEME
- Type:
str- Default:
'default'- Values:
'default','dark','light','high_contrast', or custom theme name- Description:
Built-in theme to use
app.config['DEFAULT_THEME'] = 'dark' # Use dark theme
THEME_FILE
- Type:
strorNone- Default:
None- Description:
Path to custom theme file (CSS or JSON format)
app.config['THEME_FILE'] = 'themes/custom.css' # Auto-loaded at startup
STYLE_FILE
- Type:
strorNone- Default:
None- Description:
Path to additional CSS stylesheet (adds to current theme)
app.config['STYLE_FILE'] = 'styles/extra.css' # Additional styles
UNICODE_SUPPORT
- Type:
str- Default:
'auto'- Values:
'auto'(detect),'force'(always),'disable'(never)- Description:
Controls Unicode character support
app.config['UNICODE_SUPPORT'] = 'force' # Always use Unicode
app.config['UNICODE_SUPPORT'] = 'disable' # ASCII only
app.config['UNICODE_SUPPORT'] = 'auto' # Auto-detect (default)
Performance & Threading (4 options)
REFRESH_INTERVAL
- Type:
floatorNone- Default:
None- Description:
Auto-refresh interval in seconds for animations (None = disabled)
app.config['REFRESH_INTERVAL'] = 0.1 # 100ms refresh for smooth animations
MAX_FPS
- Type:
intorNone- Default:
None- Description:
Maximum frame rate cap (None = unlimited). Limits FPS by sleeping between frames to prevent excessive CPU usage.
app.config['MAX_FPS'] = 30 # Cap at 30 FPS
app.config['MAX_FPS'] = 60 # Cap at 60 FPS
app.config['MAX_FPS'] = None # Unlimited (default)
Tip
Use MAX_FPS to reduce CPU usage in resource-constrained environments or when high frame rates aren’t needed.
RUN_SYNC_IN_EXECUTOR
- Type:
bool- Default:
False- Description:
Run synchronous event handlers in thread pool executor
app.config['RUN_SYNC_IN_EXECUTOR'] = True # Prevent UI blocking
EXECUTOR_MAX_WORKERS
- Type:
intorNone- Default:
None- Description:
Thread pool size for executor (None = auto-detect)
app.config['EXECUTOR_MAX_WORKERS'] = 4 # 4 worker threads
Rendering (3 options)
RENDER_THROTTLE_MS
- Type:
int- Default:
0- Description:
Minimum time between renders in milliseconds (throttling). Prevents renders faster than the specified interval to reduce overhead.
app.config['RENDER_THROTTLE_MS'] = 16 # Max ~60 FPS
app.config['RENDER_THROTTLE_MS'] = 33 # Max ~30 FPS
app.config['RENDER_THROTTLE_MS'] = 0 # No throttling (default)
Tip
Use RENDER_THROTTLE_MS to limit render frequency and reduce CPU usage from rapid state changes.
FULL_REPAINT_INTERVAL
- Type:
float- Default:
0.0- Description:
Force a full-screen repaint every N seconds to evict foreign bytes (
0disables). Diff rendering only emits the cells that changed, so anything a third-party library or subprocess writes to the terminal out-of-band lingers until a full redraw. When set, the event loop repaints the whole screen on this cadence so such corruption self-heals.
app.config['FULL_REPAINT_INTERVAL'] = 5.0 # heal every 5 seconds
app.config['FULL_REPAINT_INTERVAL'] = 0.0 # disabled (default)
Tip
Left disabled by default because a periodic full repaint negates the diff renderer’s bytes-saved advantage. If your app knowingly writes to the terminal (spawns a subprocess, prints), prefer a one-shot app.request_full_repaint() right after instead of a standing heartbeat.
AUTO_FIT_LAYOUT
- Type:
bool- Default:
True- Description:
Shrink over-committed layouts to fit the terminal instead of clipping them. When a container’s children ask for more space than the container has — typically hard-coded
width=/height=values on a terminal smaller than the author’s — children with slack give space back in proportion, floored at the size their content genuinely needs. SetFalseto restore the pre-0.1.0 behaviour of letting the overflow clip.
app.config['AUTO_FIT_LAYOUT'] = False # Let over-committed layouts clip
Notifications (5 options)
NOTIFICATION_DURATION
- Type:
float- Default:
3.0- Description:
Default notification duration in seconds
app.config['NOTIFICATION_DURATION'] = 5.0 # 5 second notifications
NOTIFICATION_POSITION
- Type:
str- Default:
'top_right'- Values:
'top_right','top_left','bottom_right','bottom_left'- Description:
Notification stack position
app.config['NOTIFICATION_POSITION'] = 'bottom_right'
NOTIFICATION_SPACING
- Type:
int- Default:
1- Description:
Vertical spacing between stacked notifications (in lines)
app.config['NOTIFICATION_SPACING'] = 2 # More spacing
NOTIFICATION_MARGIN
- Type:
int- Default:
2- Description:
Margin from screen edges (in characters)
app.config['NOTIFICATION_MARGIN'] = 4 # Larger margin
NOTIFICATION_MAX_STACK
- Type:
intorNone- Default:
5- Description:
Maximum concurrent notifications (oldest dismissed when limit reached)
app.config['NOTIFICATION_MAX_STACK'] = 10 # Allow 10 notifications
app.config['NOTIFICATION_MAX_STACK'] = None # Unlimited
Logging (4 options)
LOG_LEVEL
- Type:
str- Default:
'WARNING'- Values:
'DEBUG','INFO','WARNING','ERROR','CRITICAL'- Description:
Logging level
app.config['LOG_LEVEL'] = 'DEBUG' # Show all logs
LOG_FILE
- Type:
strorNone- Default:
None- Description:
Log file path (None = no file logging)
app.config['LOG_FILE'] = 'app.log' # Log to file
LOG_FORMAT
- Type:
str- Default:
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'- Description:
Python logging format string
app.config['LOG_FORMAT'] = '%(levelname)s: %(message)s' # Simple format
SUPPRESS_INTERNAL_LOGGING_CONFIG
- Type:
bool- Default:
False- Description:
When
True, Wijjit will not configure its own loggers, giving the host application full control over logging. Set this if you have pre-configured logging handlers for thewijjitnamespace and don’t want Wijjit to override them.
app.config['SUPPRESS_INTERNAL_LOGGING_CONFIG'] = True # Host owns logging config
Debug & Development (6 options)
DEBUG
- Type:
bool- Default:
False- Description:
Master debug flag (enables various debug features)
app.config['DEBUG'] = True # Enable debug mode
Note
Debug mode also enables strict template undefined checking: referencing
an undefined template name ({{ typo }}) raises jinja2.UndefinedError
instead of silently rendering as an empty string. Use state.get('key')
or the |default filter for genuinely optional keys. Production (the
default) stays lenient so one bad key cannot crash a running TUI;
wijjit validate always checks strictly.
SHOW_FPS
- Type:
bool- Default:
False- Description:
Display FPS counter in top-right corner
app.config['SHOW_FPS'] = True # Show FPS counter
SHOW_BOUNDS
- Type:
bool- Default:
False- Description:
Visualize element bounds by drawing colored rectangles around elements. Different colors indicate element types (cyan=input, yellow=display, magenta=container, green=other).
app.config['SHOW_BOUNDS'] = True # Show element bounds for debugging
Tip
Use SHOW_BOUNDS to debug layout issues and understand element positioning.
DEBUG_INPUT_KEYBOARD
- Type:
bool- Default:
False- Description:
Log all keyboard events to debug log
app.config['DEBUG_INPUT_KEYBOARD'] = True # Log keyboard events
DEBUG_INPUT_MOUSE
- Type:
bool- Default:
False- Description:
Log all mouse events to debug log
app.config['DEBUG_INPUT_MOUSE'] = True # Log mouse events
WARN_SLOW_RENDER_MS
- Type:
intorNone- Default:
None- Description:
Warn if render time exceeds threshold (milliseconds, None = disabled)
app.config['WARN_SLOW_RENDER_MS'] = 100 # Warn if render > 100ms
Templates (2 options)
TEMPLATE_DIR
- Type:
strorNone- Default:
None- Description:
Directory of file templates loaded with
wijjit.render_template(). When left asNone, Wijjit auto-discovers atemplates/directory next to the module that constructs the app (Flask’s convention). Set this to point elsewhere, which also disables auto-discovery.
app.config['TEMPLATE_DIR'] = 'templates/'
# or, equivalently, at construction time:
app = Wijjit(template_dir='templates/')
TEMPLATE_AUTO_RELOAD
- Type:
bool- Default:
False- Description:
Automatically reload file templates when they change on disk (for development). Wired into the Jinja2 environment’s
auto_reload.
app.config['TEMPLATE_AUTO_RELOAD'] = True # Hot reload templates
Accessibility (2 options)
REDUCE_MOTION
- Type:
bool- Default:
False- Description:
Reduce or disable animations (for motion sensitivity). When enabled, spinners and other animations freeze at their first frame, providing a static indicator instead.
app.config['REDUCE_MOTION'] = True # Disable animations
Tip
Enable REDUCE_MOTION for users with motion sensitivity or vestibular disorders. Animations can cause discomfort or nausea for some users.
HIGH_CONTRAST
- Type:
bool- Default:
False- Description:
Use high contrast theme with pure black/white and bright, saturated colors. Automatically switches to the
high_contrasttheme for maximum visibility.
app.config['HIGH_CONTRAST'] = True # High contrast theme for accessibility
Features of the high contrast theme:
Pure white (255, 255, 255) text on pure black (0, 0, 0) backgrounds
Bright, fully saturated accent colors (bright yellow, green, red, cyan)
Bold text throughout for improved readability
Strong borders and focus indicators
No subtle grays or muted effects
Tip
Enable HIGH_CONTRAST for users with vision impairments or those requiring enhanced visibility. This theme follows WCAG high contrast guidelines.
Common Use Cases
Development Environment
# config/development.py
DEBUG = True
LOG_LEVEL = 'DEBUG'
SHOW_FPS = True
WARN_SLOW_RENDER_MS = 50
DEBUG_INPUT_KEYBOARD = True
TEMPLATE_AUTO_RELOAD = True
# app.py
app.config.from_pyfile('config/development.py')
Production Environment
# config/production.py
DEBUG = False
LOG_LEVEL = 'WARNING'
LOG_FILE = '/var/log/myapp.log'
RUN_SYNC_IN_EXECUTOR = True
EXECUTOR_MAX_WORKERS = 4
# app.py
app.config.from_pyfile('config/production.py')
Keyboard-Only Mode
Perfect for accessibility or when mouse is not available:
app.config.update(
ENABLE_MOUSE=False,
ENABLE_FOCUS_NAVIGATION=True,
QUIT_KEY='q' # Simple quit key
)
Custom Theme with Styles
app.config.update(
THEME_FILE='themes/my_theme.css', # Base theme
STYLE_FILE='styles/overrides.css' # Additional styles
)
Performance Monitoring
app.config.update(
SHOW_FPS=True, # Display FPS counter
WARN_SLOW_RENDER_MS=100, # Warn on slow renders
LOG_LEVEL='INFO', # Log performance info
LOG_FILE='performance.log' # Log to file
)
CI/CD Testing
In CI you typically want colorless, deterministic output. Wijjit honors the
NO_COLOR standard, and any WIJJIT_* variable maps to a real config key:
export NO_COLOR=1 # Disable ANSI colors (no-color.org standard)
export WIJJIT_LOG_LEVEL=DEBUG
# NO_COLOR and WIJJIT_* vars are read when the app is created
app = Wijjit() # Colors disabled, debug logging enabled
For driving an app without a real TTY in tests, use the headless harness
(wijjit.testing.WijjitHarness) rather than a config flag.
Best Practices
1. Use Configuration Files for Environments
Instead of hardcoding settings, use separate config files:
config/
├── development.py
├── production.py
└── testing.py
2. Environment Variables for Secrets
Never commit sensitive data. Use environment variables:
export WIJJIT_LOG_FILE=/secure/path/app.log
3. Validate Configuration
Check critical config values at startup:
app = Wijjit()
if app.config['THEME_FILE']:
import os
if not os.path.exists(app.config['THEME_FILE']):
raise FileNotFoundError(f"Theme file not found: {app.config['THEME_FILE']}")
4. Use Config Namespaces
Group related settings using get_namespace():
# Get all notification settings
notif_config = app.config.get_namespace('NOTIFICATION_')
print(notif_config)
# {'duration': 3.0, 'position': 'top_right', ...}
5. Document Custom Configurations
Add comments to your config files:
# config.py
# Performance settings
RUN_SYNC_IN_EXECUTOR = True # Prevent UI blocking
EXECUTOR_MAX_WORKERS = 4 # Balanced for this server
# Development helpers
SHOW_FPS = True # Monitor performance
WARN_SLOW_RENDER_MS = 50 # Alert on slow renders
Troubleshooting
Configuration Not Applied
Problem: Changed config value but app doesn’t reflect it
Solution: Ensure config is set before app.run() or before component initialization:
app = Wijjit()
app.config['ENABLE_MOUSE'] = False # Set BEFORE run()
app.run()
Environment Variables Not Loaded
Problem: WIJJIT_* environment variables not working
Solution: Check variable names are uppercase and have WIJJIT_ prefix:
# Wrong
export wijjit_debug=1
# Correct
export WIJJIT_DEBUG=1
Boolean parsing also accepts multiple formats:
export WIJJIT_DEBUG=true # or 1, yes, on
export WIJJIT_DEBUG=false # or 0, no, off
Theme File Not Loading
Problem: Theme file specified but not applied
Solution: Check file path and format:
import os
theme_path = 'themes/custom.css'
if os.path.exists(theme_path):
app.config['THEME_FILE'] = theme_path
else:
print(f"Theme file not found: {theme_path}")
Also check application logs for theme loading errors:
app.config['LOG_LEVEL'] = 'DEBUG' # See theme loading messages
Invalid Config Values
Problem: Invalid config value causes unexpected behavior
Solution: Check logs for warnings. Wijjit validates config values and logs warnings:
app.config['LOG_LEVEL'] = 'DEBUG' # Enable debug logging
# Invalid value - will log warning and use default
app.config['MOUSE_TRACKING_MODE'] = 'invalid'
Examples
See the following example files for complete demonstrations:
examples/basic/config_demo.py- Basic configuration methodsexamples/basic/theme_config_demo.py- Theme loading examples
API Reference
Config Class
from wijjit import Config
config = Config()
# Loading methods
config.from_object(obj) # From class/module
config.from_pyfile(filename, silent=False) # From Python file
config.from_envvar(var_name, silent=False) # From env var (file path)
config.from_mapping(dict, **kwargs) # From dict
config.from_prefixed_env(prefix='WIJJIT_') # From prefixed env vars
# Utility methods
config.get_namespace(prefix, lowercase=True, trim_namespace=True)
DefaultConfig Class
All default values are defined in:
from wijjit import DefaultConfig
# View defaults
print(DefaultConfig.ENABLE_MOUSE) # True
print(DefaultConfig.QUIT_KEY) # 'ctrl+q'
See Also
Quick Start - Getting started with Wijjit
Styling & Themes - Theming and styling guide
State Management - State management guide