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 loopexit()- Exit applicationinvalidate()- Force UI rebuildset_state(**updates)- Update attributesshow_toast(message)- Show notificationset_interval(callback, seconds)- Schedule periodic taskrun_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"