wijjit.core.app.Wijjit
- class wijjit.core.app.Wijjit(initial_state=None, template_dir=None, enable_mouse=None, debug=None, backend=None, **config_overrides)[source]
Main Wijjit application class.
The Wijjit class is the central orchestrator for TUI applications. It provides a Flask-like API with view decorators and manages all framework components.
- Parameters:
- Variables:
config (
Config) – Application configuration (Flask-like dict)state (
State) – Application state with reactive updatesrenderer (
Renderer) – Jinja2 template rendererfocus_manager (
FocusManager) – Focus management for interactive elementshover_manager (
HoverManager) – Hover state management for mouse interactionhandler_registry (
HandlerRegistry) – Event handler registry and dispatcherscreen_manager (
ScreenManager) – Terminal screen managementinput_handler (
InputHandler) – Keyboard and mouse input handlingviews (
Dict[str,ViewConfig]) – Registered viewsrunning (
bool) – Whether the app is currently runningneeds_render (
bool) – Whether a re-render is needed
Examples
Basic usage:
>>> app = Wijjit() >>> app.config['ENABLE_MOUSE'] = False >>> @app.view("main", default=True) ... def main_view(): ... return {"template": "Hello, World!"} >>> app.run()
Load configuration from file:
>>> app = Wijjit() >>> app.config.from_pyfile('config.py') >>> app.run()
Configure via environment variables:
>>> # export WIJJIT_DEBUG=1 >>> # export WIJJIT_ENABLE_MOUSE=0 >>> app = Wijjit() # Auto-loads WIJJIT_* env vars >>> app.run()
- __init__(initial_state=None, template_dir=None, enable_mouse=None, debug=None, backend=None, **config_overrides)[source]
Initialize Wijjit application.
- Parameters:
initial_state (
dict[str,Any]orNone) – Initial state dictionarytemplate_dir (
strorNone) – Template directory path (convenience parameter for TEMPLATE_DIR config). When omitted, Wijjit auto-discovers atemplates/directory next to the module that constructs the app (Flask’s convention); pass this to point somewhere else. If neither is set and notemplates/directory exists, the app renders inline templates only andwijjit.render_template()is unavailable.enable_mouse (
boolorNone) – Enable mouse support (convenience parameter for ENABLE_MOUSE config)debug (
boolorNone) – Enable debug mode (convenience parameter for DEBUG config)backend (
TerminalBackendorNone) – Terminal transport for the app. WhenNone(default) aLocalTerminalBackendis used, running against the local console exactly as before. Supply a custom backend to drive the app over a different transport (for example an SSH channel). SeeTerminalBackend.**config_overrides – Additional config overrides (e.g., quit_key=’q’, log_level=’DEBUG’)
- Return type:
None
Notes
Configuration is managed via app.config. Use app.config.from_pyfile(), app.config.from_object(), or app.config.update() to configure the app. Environment variables with
WIJJIT_prefix are automatically loaded.Convenience parameters (template_dir, enable_mouse, debug) and config_overrides take precedence over environment variables and defaults.
Methods
__init__([initial_state, template_dir, ...])Initialize Wijjit application.
bind_focus_key(key, element_id[, priority])Bind a key so that pressing it moves focus to a named element.
close_overlay([overlay])Close an overlay opened by
show_modal/show_dropdown/show_tooltip.configure_focus([enabled])Configure focus navigation behavior.
dismiss_notification(notification_id)Manually dismiss a notification.
focus_element_by_id(element_id)Move keyboard focus to a focusable element by its
id.get_element_by_id(element_id)Find a rendered element by its
id.navigate(view_name, **params)Navigate to a different view (delegates to ViewRouter).
notify(message[, severity, duration, ...])Show a notification message.
on(event_type[, callback, scope, view_name, ...])Register an event handler, directly or as a decorator.
on_action(action_id)Decorator to register an action handler.
on_key(key[, allow_ctrl_q, priority])Decorator to register a key press handler.
quit()Quit the application (delegates to EventLoop).
refresh()Force a re-render on the next loop iteration.
register_completer(name, completer)Register an autocomplete completer by name.
register_element(type_name, element_cls, *)Register a third-party element on this app, including a live one.
Force the next render to repaint the entire screen.
run()Run the application (delegates to EventLoop).
show_dropdown(element, x, y[, on_close, ...])Show a dropdown menu or context menu overlay.
show_modal(element[, on_close, ...])Show a modal dialog overlay.
show_tooltip(element, x, y[, ...])Show a tooltip overlay.
unregister_key(key)Unregister a key handler.
view(name[, default, on_enter, on_exit])Decorator to register a view (delegates to ViewRouter).
Attributes
Get current view name (delegates to ViewRouter).
Whether the application's event loop is currently running.
Get registered views (delegates to ViewRouter).
- property views: dict[str, ViewConfig]
Get registered views (delegates to ViewRouter).
- Returns:
Dictionary of registered views
- Return type:
dict[str,ViewConfig]
- view(name, default=False, on_enter=None, on_exit=None)[source]
Decorator to register a view (delegates to ViewRouter).
The decorated function returns what to render. Prefer the Flask-style helpers
wijjit.render_template_string()/wijjit.render_template(), which package the template and its (live) context:@app.view("main", default=True, on_enter=setup) def main_view(): return render_template_string(TEMPLATE, title="Home")
The view function is re-invoked on every render, so any context it computes stays live. The legacy
{"template": ..., "data": {...}}dict return is still accepted; note that values placed in a staticdatadict are frozen at the first render (use the context kwargs above, or adatacallable, for values that must update).- Parameters:
name (
str) – Unique name for this viewdefault (
bool) – Whether this is the default view (default: False)on_enter (
callable, optional) – Hook called when navigating to this view (sync or async). Takes precedence over anon_enterkey in a legacy dict return.on_exit (
callable, optional) – Hook called when navigating away from this view (sync or async). Takes precedence over anon_exitkey in a legacy dict return.
- Returns:
Decorator function
- Return type:
Callable
Examples
Inline template (preferred):
>>> @app.view("main", default=True) ... def main_view(): ... return render_template_string("Hello, {{ name }}!", name="World")
Load from a file:
>>> @app.view("dashboard") ... def dashboard_view(): ... return render_template("dashboard.wij.j2", stats=get_stats())
Navigate to a different view (delegates to ViewRouter).
Calls on_exit hook of current view, switches to new view, and calls on_enter hook of new view.
- Parameters:
view_name (
str) – Name of view to navigate to**params – Parameters to pass to the view’s data function
- Raises:
ValueError – If view_name doesn’t exist
- Return type:
None
- on(event_type, callback=None, scope=HandlerScope.GLOBAL, view_name=None, element_id=None, priority=0)[source]
Register an event handler, directly or as a decorator.
Called with a
callbackit registers immediately and returns it; called without one it returns a decorator, matching the@app.on_key/@app.on_actiondecorator style.- Parameters:
event_type (
EventType) – Type of event to handlecallback (
Callable[[Event],None]orNone) – Function to call when event occurs. Omit to use as a decorator.scope (
HandlerScope) – Scope at which handler operates (default: GLOBAL)view_name (
strorNone) – View name for view-scoped handlerselement_id (
strorNone) – Element ID for element-scoped handlerspriority (
int) – Handler priority (higher = earlier, default: 0)
- Returns:
The registered callback (direct form), or a decorator that registers and returns the callback it wraps (decorator form).
- Return type:
Callable
Examples
Direct registration:
app.on(EventType.KEY, handler)
As a decorator:
@app.on(EventType.KEY) def handler(event): ...
- on_key(key, allow_ctrl_q=False, priority=0, **kwargs)[source]
Decorator to register a key press handler.
Use this to handle specific key presses globally in your application. This is sugar over the event system - internally it registers a KEY event handler that filters by the specified key.
- Parameters:
key (
str) – The key to handle (e.g., “d”, “q”, “enter”, “space”)allow_ctrl_q (
bool, optional) – Allow binding to Ctrl+Q (default: False). Use with caution as Ctrl+Q is normally reserved for exiting the application.priority (
int, optional) – Handler priority (higher = earlier execution, default: 0)
- Returns:
Decorator function
- Return type:
Callable- Raises:
ValueError – If attempting to bind Ctrl+Q without allow_ctrl_q=True
Notes
This method is implemented as sugar over the event system. Internally, it calls self.on(EventType.KEY, …) with a wrapper that filters by key name. This centralizes event handling, simplifies the mental model, and ensures consistent priority/cancellation behavior.
Examples
>>> @app.on_key("d") ... def delete_handler(event): ... print("Delete key pressed!")
>>> @app.on_key("ctrl+q", allow_ctrl_q=True) ... def custom_exit(event): ... print("Custom Ctrl+Q handler")
>>> @app.on_key("s", priority=10) ... def save_handler(event): ... # This handler runs before lower-priority handlers ... print("Saving...")
- unregister_key(key)[source]
Unregister a key handler.
- Parameters:
key (
str) – Key combination to unregister (e.g., ‘ctrl+s’, ‘f1’)- Returns:
True if handler was found and removed, False otherwise
- Return type:
Examples
>>> @app.on_key("ctrl+s") ... def save_handler(event): ... pass >>> app.unregister_key("ctrl+s") # Returns True True
- bind_focus_key(key, element_id, priority=0)[source]
Bind a key so that pressing it moves focus to a named element.
This is a convenience wrapper over
on_key()andfocus_element_by_id()for the common “jump to this field/panel” keyboard shortcut.- Parameters:
- Returns:
The registered handler function (so it can be referenced if needed).
- Return type:
Callable
Examples
>>> app.bind_focus_key("ctrl+l", "search_box")
- on_action(action_id)[source]
Decorator to register an action handler.
Use this to handle actions from buttons and other interactive elements that have an ‘action’ attribute in templates.
- Parameters:
action_id (
str) – The action ID to handle- Returns:
Decorator function
- Return type:
Callable
Examples
>>> @app.on_action("submit") ... def handle_submit(event): ... print("Form submitted!")
- configure_focus(enabled=True)[source]
Configure focus navigation behavior.
By default, Tab/Shift+Tab navigation is automatically enabled when template contains focusable elements. Use this method to disable or re-enable focus navigation.
- Parameters:
enabled (
bool) – Whether to enable Tab/Shift+Tab navigation (default: True)- Return type:
None
Examples
>>> app.configure_focus(enabled=False) # Disable Tab navigation
- property running: bool
Whether the application’s event loop is currently running.
- Returns:
True from the moment the event loop enters its frame loop until it stops (quit, Ctrl+Q, or teardown). False before
run()and after shutdown.- Return type:
Notes
Delegates to
EventLoop.running, so background worker threads can pollapp.runningto decide when to stop pushing updates. Note the startup race: a thread started beforerun()observes False until the loop actually starts, so gate worker loops on an explicitthreading.Eventrather than on this flag alone.
- quit()[source]
Quit the application (delegates to EventLoop).
Sets the running flag to False, which will exit the event loop.
- Return type:
None
- run()[source]
Run the application (delegates to EventLoop).
Enters the main event loop: 1. Render initial view 2. Loop: read input -> dispatch events -> re-render if needed 3. Exit on quit or Ctrl+Q
The loop continues until quit() is called or the user presses Ctrl+Q.
- Return type:
None
- show_modal(element, on_close=None, dim_background=True, close_on_escape=True, close_on_click_outside=False)[source]
Show a modal dialog overlay.
Modals trap focus and typically dim the background. They are used for important interactions that require user attention.
- Parameters:
element (
Element) – The element to display as a modal (typically a Frame with content)on_close (
Callable, optional) – Callback to invoke when modal is closeddim_background (
bool) – Whether to dim the background behind the modal (default: True)close_on_escape (
bool) – Whether ESC key closes the modal (default: True)close_on_click_outside (
bool) – Whether clicking outside closes the modal (default: False)
- Returns:
The created overlay object. Can be used to manually close via overlay_manager.pop(overlay)
- Return type:
Overlay
Examples
Show a confirmation dialog:
from wijjit import ConfirmDialog def on_confirm(): state.file_deleted = True dialog = ConfirmDialog( title="Confirm Delete", message="Are you sure?", on_confirm=on_confirm, ) app.show_modal(dialog)
- show_dropdown(element, x, y, on_close=None, close_on_click_outside=True, close_on_escape=True)[source]
Show a dropdown menu or context menu overlay.
Dropdowns appear at a specific position (typically below a button or at cursor position for context menus). They close when clicking outside or pressing ESC.
- Parameters:
element (
Element) – The element to display as a dropdown (typically a Menu)x (
int) – X position for the dropdowny (
int) – Y position for the dropdownon_close (
Callable, optional) – Callback to invoke when dropdown is closedclose_on_click_outside (
bool) – Whether clicking outside closes the dropdown (default: True)close_on_escape (
bool) – Whether ESC key closes the dropdown (default: True)
- Returns:
The created overlay object
- Return type:
Overlay
Examples
Show a dropdown menu below a button:
from wijjit.elements.menu import DropdownMenu, MenuItem button = app.get_element_by_id("menu_button") menu = DropdownMenu(items=[ MenuItem(label="Open", action="open"), MenuItem(label="Save", action="save"), ]) app.show_dropdown( menu, x=button.bounds.x, y=button.bounds.y + button.bounds.height )
- show_tooltip(element, x, y, close_on_click_outside=True)[source]
Show a tooltip overlay.
Tooltips appear at a specific position (typically near the cursor or element being hovered). They don’t trap focus and typically close automatically when the mouse moves.
- Parameters:
- Returns:
The created overlay object
- Return type:
Overlay
Notes
Tooltips don’t close on ESC by default since they’re meant to be unobtrusive and auto-close on mouse movement.
Examples
Show a tooltip on hover:
from wijjit import Frame tooltip = Frame(width=30, height=3) app.show_tooltip(tooltip, x=mouse_x + 1, y=mouse_y + 1)
- close_overlay(overlay=None)[source]
Close an overlay opened by
show_modal/show_dropdown/show_tooltip.The public counterpart to those
show_*methods, so callers do not have to reach intooverlay_managerto dismiss what they opened. With no argument, closes the topmost overlay.
- notify(message, severity='info', duration=<object object>, action=None, dismiss_on_action=True, bell=False)[source]
Show a notification message.
Notifications are temporary messages that appear in a corner of the screen. They auto-dismiss after a duration and can optionally include an action button.
- Parameters:
message (
str) – Notification message textseverity (
str, optional) – Severity level: “success”, “error”, “warning”, or “info” (default: “info”)duration (
floatorNone, optional) – Duration in seconds before auto-dismiss. If left unspecified, falls back to theNOTIFICATION_DURATIONconfig value (default 3.0). Set to None explicitly for no auto-dismiss.action (
tupleof(str,callable), optional) – Optional action button as (label, callback) tupledismiss_on_action (
bool, optional) – Whether to auto-dismiss when action is clicked (default: True)bell (
bool, optional) – Whether to play a terminal bell sound (default: False)
- Returns:
Notification ID for manual dismissal via dismiss_notification()
- Return type:
Examples
Show a success notification:
app.notify("File saved successfully!", severity="success")
Show an error with an action button:
def retry():: # Retry logic pass app.notify( "Connection failed", severity="error", action=("Retry", retry), duration=5.0 )
Show a persistent notification with sound:
app.notify( "Update available", severity="info", duration=None, # Won't auto-dismiss bell=True )
- dismiss_notification(notification_id)[source]
Manually dismiss a notification.
- Parameters:
notification_id (
str) – ID returned by notify()- Returns:
True if notification was dismissed, False if not found
- Return type:
Examples
>>> notification_id = app.notify("Processing...", duration=None) >>> # ... do work ... >>> app.dismiss_notification(notification_id)
- register_completer(name, completer)[source]
Register an autocomplete completer by name.
Completers registered here can be referenced in templates using the autocomplete attribute on text inputs.
- Parameters:
name (
str) – Name to register the completer under. Use “#element_id” to auto-wire to specific elements with that ID.completer (
Completer) – The completer instance to register.
- Return type:
None
Examples
Register a word completer for a specific element:
>>> from wijjit.autocomplete import WordCompleter >>> app.register_completer("#search", WordCompleter(["apple", "banana"]))
Register a named completer that can be used by multiple inputs:
>>> app.register_completer("fruits", WordCompleter(["apple", "banana"]))
Then in template:
>>> {% textinput id="search" autocomplete=True %}{% endtextinput %} >>> {% textinput id="fruit" autocomplete="fruits" %}{% endtextinput %}
- register_element(type_name, element_cls, *, tag=None, aliases=(), extension=None, override=False)[source]
Register a third-party element on this app, including a live one.
This is the imperative escape hatch for the normal
wijjit.register_element()seam. It records the plugin in the process-global registry and live-patches this app’s already-built renderer (its element registry and Jinja environment), so an element can be added afterWijjit(...)has constructed the renderer - which the module-level function alone cannot do, since each renderer only drains the global registry at construction.For a plugin shipped as a separate package, prefer the module-level
wijjit.register_element()(or thewijjit.pluginsentry point); use this method for dynamic, in-process registration.- Parameters:
type_name (
str) – The VNode type string for the element.tag (
str, optional) – Template tag name to synthesize (seewijjit.plugins.register_element()).aliases (
sequenceofstr, optional) – Extra type-name aliases.extension (
type, optional) – A hand-written JinjaExtensionclass to use instead of a synthesized one.override (
bool, optional) – Allow shadowing a built-in / existing registration.
- request_full_repaint()[source]
Force the next render to repaint the entire screen.
Diff rendering emits only the cells that changed since the last frame, so anything written to the terminal out-of-band - a foreign library’s
stdout, a subprocess, a strayprint- is invisible to the differ and persists until something forces a full redraw. Call this after knowingly writing to the terminal to overwrite those bytes on the next frame. Also drives the optionalFULL_REPAINT_INTERVALheartbeat.- Return type:
None