Files
MervinPraison 3a10f48632 feat: close extensibility gaps + hotfix 0.3.108
Closes 5 documented gaps from how-to-add-a-feature.md:

- aiui.set_custom_js(path) — inject client JS from Python (new /custom.js route, <script> after plugin-loader). Tests: 10/10
- name= kwarg on all 8 form inputs — stable submit-keys for form_action, avoids label collisions. Tests: 18/18
- Component schema registry — aiui.register_component_schema() + GET /api/components/schemas. Built-in schemas auto-derived from ui.py. Tests: 9/9
- /api/features parallelized via asyncio.gather (warm calls now instant)
- mkdocs build fixed for pygments 2.20 via docs_hooks.py

Other:
- form inputs now emit name + data-label attrs so form_action submit handler collects values correctly (bug fix)
- radio submit handler only stores the checked radio's value
- switch now has hidden checkbox mirror so form_action sees its state
- mkdocs --strict passes
- Example 28 uses the new APIs (no more DevTools console workaround)
- Bump aiui 0.3.107 -> 0.3.108; sync __version__.py

Tests: 542/545 pass (3 pre-existing test-isolation failures unrelated).
2026-04-19 02:36:38 +01:00

27 KiB
Raw Permalink Blame History

Component API Reference

Python functions for building dashboard UIs. Each function returns a dict that the frontend renders as a real UI element — 36 components organized into tiers.

import praisonaiui as aiui

Core Components

aiui.layout(children)

Wrap components in a renderable container.

aiui.layout([
    aiui.card("Users", value=42),
    aiui.table(headers=["A"], rows=[["x"]]),
])
# Returns: {"_components": [...]}
Parameter Type Required Description
children Sequence[dict] ✅ List of component dicts

Returns: dict with _components key — triggers structured rendering in the frontend.

A metric/stat card.

aiui.card("Revenue", value="$1,500", footer="+8% this month")
Parameter Type Required Description
title str ✅ Card header label
value Any ❌ Main display value
footer str ❌ Small text below the value

aiui.columns(children)

Arrange components in a responsive grid row.

aiui.columns([
    aiui.card("A", value=1),
    aiui.card("B", value=2),
    aiui.card("C", value=3),
])
Parameter Type Required Description
children Sequence[dict] ✅ Component dicts to lay out side-by-side

aiui.table(*, headers, rows)

A data table.

aiui.table(
    headers=["Name", "Role"],
    rows=[["Alice", "Engineer"], ["Bob", "Designer"]],
)
Parameter Type Required Description
headers Sequence[str] ✅ Column header labels
rows Sequence[Sequence[Any]] ✅ Row data arrays

aiui.text(content)

A text block.

aiui.text("Last updated 30 seconds ago.")
Parameter Type Required Description
content str ✅ Text content

aiui.chart(title, *, data, chart_type)

A chart.

aiui.chart("Revenue", data=[{"month": "Jan", "value": 100}], chart_type="line")
Parameter Type Required Default Description
title str ✅ — Chart title
data Sequence[dict] ❌ [] Data points
chart_type str ❌ "bar" bar, line, or pie

Essential Components

aiui.metric(label, *, value, delta, delta_color)

A metric card with optional delta indicator.

aiui.metric("Requests", value="12.3k", delta="+15%")
aiui.metric("Errors", value=23, delta="+3", delta_color="inverse")
Parameter Type Required Default Description
label str ✅ — Metric label
value Any ✅ — Main metric value
delta str ❌ None Change indicator (e.g. "+5%")
delta_color str ❌ "normal" "normal", "inverse", or "off"

aiui.progress_bar(label, *, value, max_value)

A progress bar.

aiui.progress_bar("Upload", value=73, max_value=100)
Parameter Type Required Default Description
label str ✅ — Label above the bar
value int|float ✅ — Current value
max_value int|float ❌ 100 Maximum value

aiui.alert(message, *, variant, title)

An alert/notification box.

aiui.alert("Deploy successful!", variant="success", title="Done")
Parameter Type Required Default Description
message str ✅ — Alert body text
variant str ❌ "info" "info", "success", "warning", "error"
title str ❌ None Optional bold title

aiui.badge(text, *, variant)

An inline badge/tag.

aiui.badge("New")
aiui.badge("Deprecated", variant="destructive")
Parameter Type Required Default Description
text str ✅ — Badge label
variant str ❌ "default" "default", "secondary", "destructive", "outline"

aiui.separator()

A horizontal separator line. No parameters.

aiui.tabs(items)

A tabbed container.

aiui.tabs([
    {"label": "Overview", "children": [aiui.text("Tab 1 content")]},
    {"label": "Details",  "children": [aiui.table(headers=["K","V"], rows=[["a","b"]])]},
])
Parameter Type Required Description
items Sequence[dict] ✅ List of {"label": str, "children": [comp_dicts]}

aiui.accordion(items)

A collapsible accordion.

aiui.accordion([
    {"title": "FAQ 1", "content": "Answer 1"},
    {"title": "FAQ 2", "content": "Answer 2"},
])
Parameter Type Required Description
items Sequence[dict] ✅ List of {"title": str, "content": str_or_comp}

aiui.image_display(src, *, alt, caption, width)

An image with optional caption.

aiui.image_display("https://example.com/logo.png", alt="Logo", caption="Our logo", width="300px")
Parameter Type Required Default Description
src str ✅ — Image URL or path
alt str ❌ "" Alt text
caption str ❌ None Caption below image
width str ❌ None CSS width (e.g. "300px", "50%")

aiui.code_block(code, *, language)

A code block with syntax highlighting.

aiui.code_block("def hello():\n    print('Hi!')", language="python")
Parameter Type Required Default Description
code str ✅ — Source code text
language str ❌ "text" Language for highlighting

aiui.json_view(data)

A formatted JSON viewer.

aiui.json_view({"status": "ok", "items": [1, 2, 3]})
Parameter Type Required Description
data Any ✅ Any JSON-serializable data

Form Input Components

aiui.text_input(label, *, value, placeholder)

aiui.text_input("Name", value="Alice", placeholder="Enter name")
Parameter Type Required Default Description
label str ✅ — Input label
value str ❌ "" Default value
placeholder str ❌ "" Placeholder text

aiui.number_input(label, *, value, min_val, max_val, step)

aiui.number_input("Age", value=28, min_val=0, max_val=150, step=1)
Parameter Type Required Default Description
label str ✅ — Input label
value int|float ❌ 0 Default value
min_val int|float ❌ None Minimum allowed
max_val int|float ❌ None Maximum allowed
step int|float ❌ 1 Increment step

aiui.select_input(label, *, options, value)

aiui.select_input("Country", options=["USA", "UK", "Canada"], value="USA")
Parameter Type Required Default Description
label str ✅ — Input label
options Sequence[str] ✅ — Option strings
value str ❌ "" Default selected

aiui.slider_input(label, *, value, min_val, max_val, step)

aiui.slider_input("Volume", value=65, min_val=0, max_val=100, step=5)
Parameter Type Required Default Description
label str ✅ — Input label
value int|float ❌ 0 Default value
min_val int|float ❌ 0 Minimum
max_val int|float ❌ 100 Maximum
step int|float ❌ 1 Increment step

aiui.checkbox_input(label, *, checked)

aiui.checkbox_input("I agree to the terms", checked=True)
Parameter Type Required Default Description
label str ✅ — Checkbox label
checked bool ❌ False Default state

aiui.switch_input(label, *, checked)

aiui.switch_input("Enable notifications", checked=False)
Parameter Type Required Default Description
label str ✅ — Switch label
checked bool ❌ False Default state

aiui.radio_input(label, *, options, value)

aiui.radio_input("Language", options=["Python", "JavaScript", "Go"], value="Python")
Parameter Type Required Default Description
label str ✅ — Group label
options Sequence[str] ✅ — Option strings
value str ❌ "" Default selected

aiui.textarea_input(label, *, value, placeholder, rows)

aiui.textarea_input("Bio", value="Developer", placeholder="About you...", rows=3)
Parameter Type Required Default Description
label str ✅ — Input label
value str ❌ "" Default value
placeholder str ❌ "" Placeholder text
rows int ❌ 4 Visible rows

aiui.multiselect_input(label, *, options, value)

aiui.multiselect_input("Tags", options=["AI", "ML", "NLP"], value=["AI"])
Parameter Type Required Default Description
label str ✅ — Input label
options Sequence[str] ✅ — Available choices
value Sequence[str] ❌ () Default selections

aiui.date_input(label, *, value)

aiui.date_input("Start Date", value="2026-01-15")
Parameter Type Required Default Description
label str ✅ — Input label
value str ❌ None ISO date string

aiui.time_input(label, *, value)

aiui.time_input("Alarm", value="08:30")
Parameter Type Required Default Description
label str ✅ — Input label
value str ❌ None "HH:MM" string

aiui.color_picker_input(label, *, value)

aiui.color_picker_input("Brand Color", value="#6366f1")
Parameter Type Required Default Description
label str ✅ — Input label
value str ❌ "#000000" Default hex color

Layout & Advanced Components

aiui.container(children, *, title)

A container wrapper with optional title.

aiui.container([aiui.text("Inside"), aiui.badge("Tag")], title="Section")
Parameter Type Required Default Description
children Sequence[dict] ✅ — Child component dicts
title str ❌ None Optional heading

aiui.expander(title, *, children, expanded)

A collapsible section.

aiui.expander("Advanced Settings", children=[aiui.text("Hidden content")], expanded=False)
Parameter Type Required Default Description
title str ✅ — Section heading
children Sequence[dict] ✅ — Content components
expanded bool ❌ False Initially expanded?

aiui.divider(text)

A horizontal divider with optional center text.

aiui.divider("OR")
Parameter Type Required Default Description
text str ❌ None Label in the middle of the line

aiui.header(text, *, level)

A heading element.

aiui.header("Dashboard", level=1)
Parameter Type Required Default Description
text str ✅ — Heading text
level int ❌ 1 Heading level 1–6

aiui.markdown_text(content)

A markdown-rendered text block.

aiui.markdown_text("**Bold**, *italic*, and `code`.")
Parameter Type Required Description
content str ✅ Markdown content string

aiui.link(text, *, href, external)

A hyperlink.

aiui.link("Docs", href="https://docs.praison.ai", external=True)
Parameter Type Required Default Description
text str ✅ — Link text
href str ✅ — URL
external bool ❌ False Open in new tab?

aiui.button_group(buttons)

A row of buttons.

aiui.button_group([
    {"label": "Save", "variant": "default"},
    {"label": "Cancel", "variant": "outline"},
])
Parameter Type Required Description
buttons Sequence[dict] ✅ List of {"label": str, "variant": str}

aiui.stat_group(stats)

A grid of stat/metric cards.

aiui.stat_group([
    {"label": "CPU", "value": "72%", "delta": "+5%"},
    {"label": "Memory", "value": "4.2 GB"},
])
Parameter Type Required Description
stats Sequence[dict] ✅ List of {"label": str, "value": Any, "delta": str}

aiui.avatar(*, src, name, fallback)

An avatar image or initials.

aiui.avatar(name="Alice Smith", fallback="AS")
Parameter Type Required Default Description
src str ❌ None Image URL
name str ❌ None Display name
fallback str ❌ None Fallback text (e.g. initials)

aiui.callout(content, *, variant, title)

A callout/highlight box.

aiui.callout("Use GPU for faster training.", variant="info", title="Tip")
Parameter Type Required Default Description
content str ✅ — Callout body text
variant str ❌ "info" "info", "success", "warning", "error"
title str ❌ None Optional bold title

aiui.empty(text)

An empty state placeholder.

aiui.empty("No results found")
Parameter Type Required Default Description
text str ❌ "No data" Placeholder message

aiui.spinner(text)

A loading spinner with text.

aiui.spinner("Loading data...")
Parameter Type Required Default Description
text str ❌ "Loading..." Loading message

Media Components

aiui.audio_player(src, *, autoplay)

aiui.audio_player("/audio/clip.mp3", autoplay=False)
Parameter Type Required Default Description
src str ✅ — Audio file URL
autoplay bool ❌ False Auto-play?

aiui.video_player(src, *, autoplay, poster)

aiui.video_player("/video/demo.mp4", poster="/img/thumb.jpg")
Parameter Type Required Default Description
src str ✅ — Video file URL
autoplay bool ❌ False Auto-play?
poster str ❌ None Poster image URL

aiui.file_download(label, *, href, filename)

aiui.file_download("Download Report", href="/files/report.pdf", filename="report.pdf")
Parameter Type Required Default Description
label str ✅ — Button label
href str ✅ — File URL
filename str ❌ None Suggested filename

aiui.gallery(items)

An image/media gallery grid.

aiui.gallery([
    {"src": "/img/1.jpg", "alt": "Photo 1", "caption": "Sunset"},
    {"src": "/img/2.jpg", "alt": "Photo 2"},
])
Parameter Type Required Description
items Sequence[dict] ✅ List of {"src": str, "alt": str, "caption": str}

Dashboard Components

aiui.toast(message, *, variant, duration)

A toast notification.

aiui.toast("Saved!", variant="success", duration=3000)
Parameter Type Required Default Description
message str ✅ — Notification text
variant str ❌ "info" "info", "success", "warning", "error"
duration int ❌ 3000 Auto-dismiss ms

aiui.dialog(title, *, children, description)

A modal dialog.

aiui.dialog("Confirm", children=[aiui.text("Are you sure?")], description="This action cannot be undone.")
Parameter Type Required Default Description
title str ✅ — Dialog heading
children Sequence[dict] ✅ — Content components
description str ❌ None Subtitle

aiui.caption(text)

Small muted caption text.

aiui.caption("Figure 1: System architecture")
Parameter Type Required Description
text str ✅ Caption content

aiui.html_embed(content)

Raw HTML embed (trusted content only).

aiui.html_embed('<iframe src="https://example.com" width="100%"></iframe>')
Parameter Type Required Description
content str ✅ HTML string

aiui.skeleton(*, width, height, variant)

A skeleton loading placeholder.

aiui.skeleton(width="200px", height="20px", variant="text")
Parameter Type Required Default Description
width str ❌ None CSS width
height str ❌ None CSS height
variant str ❌ "text" "text", "card", or "avatar"

aiui.tooltip_wrap(child, *, content)

Wrap a component with a hover tooltip.

aiui.tooltip_wrap(aiui.badge("Beta"), content="This feature is in beta")
Parameter Type Required Description
child dict ✅ Component to wrap
content str ✅ Tooltip text

Navigation Components

aiui.breadcrumb(items)

A breadcrumb navigation trail.

aiui.breadcrumb([
    {"label": "Home", "href": "/"},
    {"label": "Settings", "href": "/settings"},
    {"label": "Profile", "href": None},
])
Parameter Type Required Description
items Sequence[dict] ✅ List of {"label": str, "href": str|None}

aiui.pagination(*, total, page, per_page)

Pagination controls.

aiui.pagination(total=250, page=3, per_page=25)
Parameter Type Required Default Description
total int ✅ — Total items
page int ❌ 1 Current page (1-based)
per_page int ❌ 10 Items per page

aiui.key_value_list(items, *, title)

A key-value display list.

aiui.key_value_list([
    {"label": "Model", "value": "gpt-4o-mini"},
    {"label": "Temperature", "value": 0.7},
], title="Config")
Parameter Type Required Default Description
items Sequence[dict] ✅ — List of {"label": str, "value": Any}
title str ❌ None Optional heading

aiui.popover(trigger, *, children)

A popover overlay triggered by a component.

aiui.popover(aiui.badge("Info"), children=[aiui.text("More details here.")])
Parameter Type Required Description
trigger dict ✅ Component that triggers the popover
children Sequence[dict] ✅ Content inside the popover

Composition

Components are designed to compose. Use columns() for horizontal layout within a layout():

@aiui.page("overview", title="Overview", icon="📊")
async def overview():
    return aiui.layout([
        # Row of KPI cards
        aiui.columns([
            aiui.metric("Users", value=142, delta="+12%"),
            aiui.metric("Revenue", value="$1.5k", delta="+8%"),
            aiui.metric("Uptime", value="99.9%"),
        ]),
        # Tabbed data
        aiui.tabs([
            {"label": "Table", "children": [
                aiui.table(
                    headers=["Agent", "Tasks", "Status"],
                    rows=[["Researcher", 15, "Active"]],
                ),
            ]},
            {"label": "Chart", "children": [
                aiui.chart("Tasks", data=[{"name": "Researcher", "value": 15}]),
            ]},
        ]),
        # Footer
        aiui.text("Data refreshes every 30 seconds."),
    ])

Component Count Summary

Category Components Count
Core layout, card, columns, table, text, chart 6
Essential metric, progress_bar, alert, badge, separator, tabs, accordion, image_display, code_block, json_view 10
Form Inputs text_input, number_input, select_input, slider_input, checkbox_input, switch_input, radio_input, textarea_input, multiselect_input, date_input, time_input, color_picker_input 12
Layout & Advanced container, expander, divider, header, markdown_text, link, button_group, stat_group, avatar, callout, empty, spinner 12
Media audio_player, video_player, file_download, gallery 4
Dashboard toast, dialog, caption, html_embed, skeleton, tooltip_wrap 6
Navigation breadcrumb, pagination, key_value_list, popover 4
Total 54

Custom Component Types

The dashboard.js renderer supports any dict with a type key. Unknown types render as formatted JSON, making it safe to experiment:

# Custom component — renders as JSON until frontend support is added
{"type": "timeline", "events": [{"time": "10:00", "label": "Deploy"}]}

Component Render Protocol

Understanding how components flow from Python to the browser.

Data Flow

Python                    Server                     Frontend
──────                    ──────                     ────────
aiui.card("X", value=42)  →  {"type":"card",...}    →  renderComponent()
                             ↓                          ↓
@aiui.page() handler      →  /api/pages/{id}/data   →  renderCard()
                             ↓                          ↓
aiui.layout([...])        →  {"_components":[...]}  →  DOM elements

Step-by-Step

  1. Python: Call aiui.card("Revenue", value="$1,500") — returns a dict:

    {"type": "card", "title": "Revenue", "value": "$1,500"}
    
  2. Server: @aiui.page() handler returns aiui.layout([...]):

    {"_components": [{"type": "card", ...}, {"type": "table", ...}]}
    
  3. API: GET /api/pages/{id}/data returns the JSON

  4. Frontend: dashboard.js detects _components key and calls renderComponents():

    function renderComponents(data, container) {
        if (data && data._components) {
            data._components.forEach(comp => {
                container.appendChild(renderComponent(comp));
            });
        }
    }
    
  5. Renderer: renderComponent(comp) dispatches on comp.type:

    function renderComponent(comp) {
        switch (comp.type) {
            case 'card': return renderCard(comp);
            case 'table': return renderTable(comp);
            // ... 48 component types
            default: return renderAsJSON(comp);
        }
    }
    
  6. DOM: Each renderer creates HTML elements with db-* CSS classes

The _components Key

The magic key that triggers structured rendering:

# With _components — renders as UI elements
return {"_components": [aiui.card("X"), aiui.table(...)]}

# Without _components — renders as raw JSON
return {"status": "ok", "data": [...]}

aiui.layout(children) automatically wraps children in {"_components": [...]}.

Component Dict Protocol

Every component function returns a dict with:

Key Required Description
type ✅ Component type name (e.g. "card", "table")
Other keys Varies Component-specific properties
# Card component
{"type": "card", "title": "Revenue", "value": "$1,500", "footer": "+8%"}

# Table component
{"type": "table", "headers": ["A", "B"], "rows": [["x", "y"]]}

# Nested components
{"type": "columns", "children": [
    {"type": "card", "title": "A"},
    {"type": "card", "title": "B"},
]}

CSS Classes

All components use db-* prefixed CSS classes:

Component CSS Class
Card .db-card, .db-card-title, .db-card-value, .db-card-footer
Table .db-viewer table
Alert .db-alert, .db-alert-info, .db-alert-success, etc.
Badge .db-badge, .db-badge-secondary, .db-badge-destructive
Tabs .db-tabs, .db-tab-list, .db-tab-btn, .db-tab-panel

Extending with Custom Components

To add a new component type:

  1. Python: Add function to ui.py:

    def timeline(events: list[dict]) -> dict:
        return {"type": "timeline", "events": events}
    
  2. Frontend: Add renderer to dashboard.js:

    function renderTimeline(comp) {
        const el = document.createElement('div');
        el.className = 'db-timeline';
        // ... render events
        return el;
    }
    
    // Add to switch statement in renderComponent()
    case 'timeline': return renderTimeline(comp);
    
  3. CSS: Add styles for .db-timeline

Until the frontend renderer is added, unknown types render as formatted JSON.