Getting Started

Installation

Casca requires Python 3.10+. Install from PyPI:

pip install casca

# With hot reload support
pip install casca[hotreload]

# All features
pip install casca[all]

Quick Start

Create your first app in 3 steps:

from casca import App, Container, Label, run_app

class MyApp(App):
    css = "#root { border: solid; padding: 1; }"
    
    def build_ui(self):
        return Container(
            Label("Hello, Terminal!"),
            id="root"
        )

if __name__ == "__main__":
    MyApp().run()

# Or use the convenience function
run_app(Container(Label("Quick!"), id="root"))

Logging

Enable structured logging for debugging:

from casca import setup_logging
import logging

# Debug to stderr
setup_logging(level=logging.DEBUG)

# Or log to file
setup_logging(level=logging.INFO, log_file="casca.log")

Core Engine

App Class

The App class manages the event loop, terminal state, and rendering:

from casca import App

class MyApp(App):
    # Class configuration
    css = ""
    css_path = "styles.css"
    theme = "default"
    debug = False
    studio_mode = False
    
    def build_ui(self):
        return Container(Label("Hello"))
    
    def handle_input(self, event):
        if event.key == 'q':
            self.exit()

app = MyApp()
app.run()

Key Methods

  • run() - Start event loop
  • exit() - Exit application
  • invalidate() - Force UI rebuild
  • set_state(**updates) - Update attributes
  • show_toast(message) - Show notification
  • set_interval(callback, seconds) - Schedule periodic task
  • run_task(fn) - Run in background thread

Widgets

All UI elements inherit from Widget:

from casca import Widget, Container, Label

class CustomWidget(Widget):
    tag = "custom"
    
    def render_content(self, app):
        app.draw_text(self.x + 1, self.y + 1, "Custom!")

Events

Handle keyboard and mouse input:

from casca import KeyEvent, MouseEvent

def handle_input(self, event: KeyEvent):
    if event.key == 'enter':
        self.submit()
    if event.is_printable:
        self.buffer += event.key

def handle_mouse(self, event: MouseEvent):
    if event.pressed:
        self.focus()

State Management

Redux-like store for predictable state:

from casca import create_store, combine_reducers

def counter(state, action):
    if action["type"] == "inc":
        return {**state, "count": state.get("count", 0) + 1}
    return state

store = create_store(counter, {"count": 0})
store.dispatch({"type": "inc"})
print(store.get_state())  # {"count": 1}

class CounterApp(App):
    def __init__(self):
        super().__init__()
        self.set_store(store)

Styling

CSS Parser

Casca supports CSS-like styling with cascade and specificity:

CSS = """
#root {
    border: solid;
    border-color: cyan;
    padding: 1;
}

.btn {
    color: white;
    background: blue;
}

.btn.primary {
    background: green;
}
"""

app = MyApp(css=CSS)

Themes

Use built-in themes or create custom ones:

from casca import THEMES

class MyApp(App):
    theme = "dark"  # or "default", "solarized", "high-contrast"
    
    def on_init(self):
        self.set_theme("light")  # Switch at runtime

Flexbox Layouts

Row and column layouts with justify/align:

from casca import Container, Label

Container(
    Label("Left"),
    Label("Center"),
    Label("Right"),
    style={
        "flex-direction": "row",
        "justify-content": "space-between",
        "align-items": "center",
        "gap": 1
    }
)

Layouts

Container

Basic flexbox container for grouping widgets:

Container(
    Label("Header"),
    Container(Label("Content"), style={"flex": 1}),
    Label("Footer"),
    style={"flex-direction": "column"}
)

Grid Layout

CSS Grid-like 2D layout:

from casca.components.widgets.grid import Grid

grid = Grid(
    columns="1fr 1fr 1fr",
    rows="auto auto",
    gap=1
)
grid.add(Label("Header"), grid_column="1 / span 3", grid_row="1")
grid.add(Label("Sidebar"), grid_column="1", grid_row="2")
grid.add(Label("Content"), grid_column="2 / span 2", grid_row="2")

Scrolling

ScrollView for overflow content:

from casca import ScrollView, Label

ScrollView(
    Label("Line 1"),
    Label("Line 2"),
    # ... many more lines
)

Widgets

Basic Widgets

from casca import (
    Label, Button, Container,
    ProgressBar, Spinner, Card
)

Label("Status: OK")
Button("Click", on_click=lambda: print("Clicked!"))
ProgressBar(value=75, max=100)
Spinner(spinner_type="dots")

Form Widgets

from casca import Input, TextArea, Select, Checkbox, RadioGroup

Input(
    value="",
    placeholder="Enter name",
    on_submit=lambda v: print(v)
)

Select(
    options=["Option 1", "Option 2"],
    on_change=lambda i: print(i)
)

Checkbox(label="Accept", checked=False)

Data Widgets

from casca import ListView, DataGrid, TreeView, Table

ListView(
    items=["Item 1", "Item 2", "Item 3"],
    on_select=lambda i: print(i)
)

DataGrid(
    columns=["Name", "Age"],
    rows=[
        ["Alice", "30"],
        ["Bob", "25"]
    ]
)

Dialogs & Modals

from casca import (
    DialogManager, show_alert,
    show_confirm, show_prompt
)

class MyApp(App):
    def on_init(self):
        self.dialog_manager = DialogManager()
    
    def handle_input(self, event):
        if event.key == 'a':
            show_alert("Title", "Message")
        
        if event.key == 'c':
            show_confirm(
                "Delete?",
                "Are you sure?",
                on_confirm=lambda r: print("Deleted" if r else "Cancelled")
            )

Advanced Features

Virtual Scrolling

Render 10,000+ items efficiently:

from casca.components.navigation.virtual_scroll import VirtualScrollView

def render_item(app, index):
    app.draw_text(app.clip_rect[2], app.clip_rect[0] + index, f"Item {index}")

VirtualScrollView(
    item_count=10000,
    item_height=1,
    render_fn=render_item
)

Accessibility

from casca.core.accessibility import get_accessibility

accessibility = get_accessibility()

# Set tab order
accessibility.focus_manager.set_focus_order(
    ["input1", "input2", "submit"]
)

# Screen reader announcement
accessibility.announce("Form submitted successfully")

# Register keyboard shortcut
accessibility.register_keybinding("Ctrl+S", save_handler)

Screenshot Export

Press F12 to save screenshots to .casca_screenshots/:

from casca import App

class MyApp(App):
    screenshot_dir = ".screenshots"
    screenshot_key = "F12"
    
    # Tune for your terminal font
    screenshot_cell_w = 9.0
    screenshot_cell_h = 18.0
    screenshot_font_size = 14.0

Studio Mode

Press F2 to toggle live inspection:

from casca import App

class MyApp(App):
    studio_mode = True  # Enable by default

Shows: focus path, computed styles, box model, layout metadata

Development

Testing

import pytest
from casca import Container, Label

def test_container_layout():
    container = Container(Label("Test"), id="root")
    container.resolve_styles({})
    w, h = container.calculate_layout(80, 24)
    
    assert w > 0
    assert h > 0

Debugging

from casca import App, setup_logging
import logging

setup_logging(level=logging.DEBUG)

class MyApp(App):
    debug = True  # Detailed errors
    debug_layout = True  # Layout visualization
    
    def on_error(self, exc, context):
        # Custom error handling
        logging.error(f"Error in {context}: {exc}")

Plugins

from casca import register_widget

class CustomWidget:
    CASCA_API_VERSION = "1.0"

register_widget("my-custom-widget", CustomWidget)

# Or use entry points in pyproject.toml:
# [project.entry-points."casca.widgets"]
# custom = "my_package:CustomWidget"