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 ``DefaultConfig`` * **Auto-loading**: ``WIJJIT_*`` environment variables are automatically loaded * **Validation**: Invalid values are logged with warnings * **Documentation**: All options are documented with inline comments Quick Start ----------- Basic configuration is simple and intuitive: .. code-block:: python 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: .. code-block:: python 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()``: .. code-block:: python 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: .. code-block:: python # config.py DEBUG = True ENABLE_MOUSE = False LOG_LEVEL = 'INFO' NOTIFICATION_DURATION = 5.0 QUIT_KEY = 'q' THEME_FILE = 'themes/custom.css' .. code-block:: python # app.py app.config.from_pyfile('config.py') 4. From Python Object ~~~~~~~~~~~~~~~~~~~~~ Load configuration from a class or module: .. code-block:: python 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: .. code-block:: python 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: .. code-block:: bash export WIJJIT_SETTINGS=/path/to/config.py .. code-block:: python app.config.from_envvar('WIJJIT_SETTINGS') 6. From Prefixed Environment Variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Environment variables with the ``WIJJIT_`` prefix are automatically loaded: .. code-block:: bash 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 .. code-block:: python # 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) .. code-block:: python 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 .. code-block:: python app.config['MOUSE_TRACKING_MODE'] = 'all_events' # Track all movement ENABLE_FOCUS_NAVIGATION ^^^^^^^^^^^^^^^^^^^^^^^^ :Type: ``bool`` :Default: ``True`` :Description: Enable Tab/Shift+Tab navigation between focusable elements. Disabling this leaves ``autofocus`` and programmatic focus (``app.focus_element_by_id``) working - it removes only the Tab/Shift+Tab traversal handlers. .. code-block:: python app.config['ENABLE_FOCUS_NAVIGATION'] = False # Disable tab navigation QUIT_KEY ^^^^^^^^ :Type: ``str`` :Default: ``'ctrl+q'`` :Description: Key binding to quit the application .. code-block:: python 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) .. code-block:: python app.config['USE_ALTERNATE_SCREEN'] = False # Use normal screen HIDE_CURSOR ^^^^^^^^^^^ :Type: ``bool`` :Default: ``True`` :Description: Hide terminal cursor during application runtime .. code-block:: python app.config['HIDE_CURSOR'] = False # Keep cursor visible APP_TITLE ^^^^^^^^^ :Type: ``str`` or ``None`` :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. ``None`` leaves the terminal title untouched. .. code-block:: python 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 default ``HIDE_CURSOR=True`` startup state; inline apps are unaffected. .. code-block:: python 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``/``atexit`` terminal-restore net or the ``SIGTSTP`` suspend handler. Set this when embedding a Wijjit app in a host process that manages the terminal itself. Custom transports express the same intent via ``TerminalBackend.owns_terminal`` and do not need this flag. .. code-block:: python 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 ``fg`` will resume it. On Windows, this option is ignored. .. code-block:: python app.config['ENABLE_SUSPEND'] = False # Disable Ctrl+Z suspend .. tip:: When suspended with Ctrl+Z, Wijjit automatically: 1. Saves terminal state (alternate screen, cursor, mouse tracking) 2. Restores normal terminal so you can use the shell 3. 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: ``tuple`` of 3 ``int`` or ``None`` :Default: ``None`` :Description: Global focus color override (R, G, B). When set, all focused elements use this foreground color instead of theme defaults. .. code-block:: python 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_COLOR`` env var :Description: Disable all ANSI colors (respects `NO_COLOR standard `_) .. code-block:: python app.config['NO_COLOR'] = True # Disable colors .. code-block:: bash 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 .. code-block:: python app.config['DEFAULT_THEME'] = 'dark' # Use dark theme THEME_FILE ^^^^^^^^^^ :Type: ``str`` or ``None`` :Default: ``None`` :Description: Path to custom theme file (CSS or JSON format) .. code-block:: python app.config['THEME_FILE'] = 'themes/custom.css' # Auto-loaded at startup STYLE_FILE ^^^^^^^^^^ :Type: ``str`` or ``None`` :Default: ``None`` :Description: Path to additional CSS stylesheet (adds to current theme) .. code-block:: python 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 .. code-block:: python 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: ``float`` or ``None`` :Default: ``None`` :Description: Auto-refresh interval in seconds for animations (None = disabled) .. code-block:: python app.config['REFRESH_INTERVAL'] = 0.1 # 100ms refresh for smooth animations MAX_FPS ^^^^^^^ :Type: ``int`` or ``None`` :Default: ``None`` :Description: Maximum frame rate cap (None = unlimited). Limits FPS by sleeping between frames to prevent excessive CPU usage. .. code-block:: python 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 .. code-block:: python app.config['RUN_SYNC_IN_EXECUTOR'] = True # Prevent UI blocking EXECUTOR_MAX_WORKERS ^^^^^^^^^^^^^^^^^^^^ :Type: ``int`` or ``None`` :Default: ``None`` :Description: Thread pool size for executor (None = auto-detect) .. code-block:: python 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. .. code-block:: python 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 (``0`` disables). 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. .. code-block:: python 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 :meth:`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. Set ``False`` to restore the pre-0.1.0 behaviour of letting the overflow clip. .. code-block:: python 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 .. code-block:: python 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 .. code-block:: python app.config['NOTIFICATION_POSITION'] = 'bottom_right' NOTIFICATION_SPACING ^^^^^^^^^^^^^^^^^^^^ :Type: ``int`` :Default: ``1`` :Description: Vertical spacing between stacked notifications (in lines) .. code-block:: python app.config['NOTIFICATION_SPACING'] = 2 # More spacing NOTIFICATION_MARGIN ^^^^^^^^^^^^^^^^^^^ :Type: ``int`` :Default: ``2`` :Description: Margin from screen edges (in characters) .. code-block:: python app.config['NOTIFICATION_MARGIN'] = 4 # Larger margin NOTIFICATION_MAX_STACK ^^^^^^^^^^^^^^^^^^^^^^ :Type: ``int`` or ``None`` :Default: ``5`` :Description: Maximum concurrent notifications (oldest dismissed when limit reached) .. code-block:: python 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 .. code-block:: python app.config['LOG_LEVEL'] = 'DEBUG' # Show all logs LOG_FILE ^^^^^^^^ :Type: ``str`` or ``None`` :Default: ``None`` :Description: Log file path (None = no file logging) .. code-block:: python 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 .. code-block:: python 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 the ``wijjit`` namespace and don't want Wijjit to override them. .. code-block:: python 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) .. code-block:: python 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 .. code-block:: python 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). .. code-block:: python 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 .. code-block:: python app.config['DEBUG_INPUT_KEYBOARD'] = True # Log keyboard events DEBUG_INPUT_MOUSE ^^^^^^^^^^^^^^^^^ :Type: ``bool`` :Default: ``False`` :Description: Log all mouse events to debug log .. code-block:: python app.config['DEBUG_INPUT_MOUSE'] = True # Log mouse events WARN_SLOW_RENDER_MS ^^^^^^^^^^^^^^^^^^^ :Type: ``int`` or ``None`` :Default: ``None`` :Description: Warn if render time exceeds threshold (milliseconds, None = disabled) .. code-block:: python app.config['WARN_SLOW_RENDER_MS'] = 100 # Warn if render > 100ms Templates (2 options) ~~~~~~~~~~~~~~~~~~~~~ TEMPLATE_DIR ^^^^^^^^^^^^ :Type: ``str`` or ``None`` :Default: ``None`` :Description: Directory of file templates loaded with :func:`wijjit.render_template`. When left as ``None``, Wijjit auto-discovers a ``templates/`` directory next to the module that constructs the app (Flask's convention). Set this to point elsewhere, which also disables auto-discovery. .. code-block:: python 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``. .. code-block:: python 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. .. code-block:: python 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_contrast`` theme for maximum visibility. .. code-block:: python 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 ~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python # config/development.py DEBUG = True LOG_LEVEL = 'DEBUG' SHOW_FPS = True WARN_SLOW_RENDER_MS = 50 DEBUG_INPUT_KEYBOARD = True TEMPLATE_AUTO_RELOAD = True .. code-block:: python # app.py app.config.from_pyfile('config/development.py') Production Environment ~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python # config/production.py DEBUG = False LOG_LEVEL = 'WARNING' LOG_FILE = '/var/log/myapp.log' RUN_SYNC_IN_EXECUTOR = True EXECUTOR_MAX_WORKERS = 4 .. code-block:: python # app.py app.config.from_pyfile('config/production.py') Keyboard-Only Mode ~~~~~~~~~~~~~~~~~~ Perfect for accessibility or when mouse is not available: .. code-block:: python app.config.update( ENABLE_MOUSE=False, ENABLE_FOCUS_NAVIGATION=True, QUIT_KEY='q' # Simple quit key ) Custom Theme with Styles ~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python app.config.update( THEME_FILE='themes/my_theme.css', # Base theme STYLE_FILE='styles/overrides.css' # Additional styles ) Performance Monitoring ~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python 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: .. code-block:: bash export NO_COLOR=1 # Disable ANSI colors (no-color.org standard) export WIJJIT_LOG_LEVEL=DEBUG .. code-block:: python # 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: .. code-block:: text config/ ├── development.py ├── production.py └── testing.py 2. Environment Variables for Secrets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Never commit sensitive data. Use environment variables: .. code-block:: bash export WIJJIT_LOG_FILE=/secure/path/app.log 3. Validate Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~ Check critical config values at startup: .. code-block:: python 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()``: .. code-block:: python # 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: .. code-block:: python # 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: .. code-block:: python 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: .. code-block:: bash # Wrong export wijjit_debug=1 # Correct export WIJJIT_DEBUG=1 Boolean parsing also accepts multiple formats: .. code-block:: bash 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: .. code-block:: python 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: .. code-block:: python 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: .. code-block:: python 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 methods * ``examples/basic/theme_config_demo.py`` - Theme loading examples API Reference ------------- Config Class ~~~~~~~~~~~~ .. code-block:: python 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: .. code-block:: python from wijjit import DefaultConfig # View defaults print(DefaultConfig.ENABLE_MOUSE) # True print(DefaultConfig.QUIT_KEY) # 'ctrl+q' See Also -------- * :doc:`../getting_started/quickstart` - Getting started with Wijjit * :doc:`styling` - Theming and styling guide * :doc:`state_management` - State management guide