{# ── Typography ──────────────────────────────────────────────────────── #}
{% if comp.type == 'title' %}
{{ comp.content }}
{% elif comp.type == 'header' %}
{{ comp.content }}
{% elif comp.type == 'subheader' %}
{{ comp.content }}
{% elif comp.type == 'write' or comp.type == 'text' %}
{{ comp.content }}
{% elif comp.type == 'markdown' %}
{{ comp.content | safe }}
{% elif comp.type == 'code' %}
{{ comp.content }}
{% elif comp.type == 'html' %}
{{ comp.content | safe }}
{% elif comp.type == 'latex' %}
$${{ comp.content }}$$
{% elif comp.type == 'json' %}
{{ comp.content }}
{# ── NER highlighting ────────────────────────────────────────────────── #}
{% elif comp.type == 'ner' %}
{% set text_content = comp.content %}
{% set ns_ner = namespace(last_idx=0) %}
{% for ent in comp.entities %}
{{ text_content[ns_ner.last_idx:ent.start] }}
{{ text_content[ent.start:ent.end] }}
{{ ent.label }}
{% set ns_ner.last_idx = ent.end %}
{% endfor %}
{{ text_content[ns_ner.last_idx:] }}
{# ── Streaming text ──────────────────────────────────────────────────── #}
{% elif comp.type == 'write_stream' %}
{# ── Data / Table ────────────────────────────────────────────────────── #}
{% elif comp.type == 'dataframe' or comp.type == 'table' %}
{{ comp.content | safe }}
{# ── Metric card ─────────────────────────────────────────────────────── #}
{% elif comp.type == 'metric' %}
{{ comp.label }}
{{ comp.value }}
{% if comp.delta %}
{% set delta_class = 'positive' if not comp.delta.startswith('-') else 'negative' %}
{{ comp.delta }}
{% endif %}
{# ── Badge ───────────────────────────────────────────────────────────── #}
{% elif comp.type == 'badge' %}
{{ comp.content }}
{# ── Status / Alert ──────────────────────────────────────────────────── #}
{% elif comp.type == 'status' %}
{{ comp.content }}
{# ── Widgets ─────────────────────────────────────────────────────────── #}
{% elif comp.type == 'button' %}
{# OAT's default button (no data-variant) is the primary filled style —
only emit the attribute when a variant was actually requested, or
every button always rendered as OAT's "secondary" look. #}
{% elif comp.type == 'text_input' %}
{% elif comp.type == 'email_input' %}
{% elif comp.type == 'password_input' %}
{% elif comp.type == 'datetime_input' %}
{% elif comp.type == 'text_area' %}
{% elif comp.type == 'number_input' %}
{% elif comp.type == 'date_input' %}
{% elif comp.type == 'color_picker' %}
{% elif comp.type == 'checkbox' %}
{% elif comp.type == 'toggle' %}
{% elif comp.type == 'slider' %}
{% elif comp.type == 'radio' %}
{% elif comp.type == 'selectbox' %}
{# ── Media ───────────────────────────────────────────────────────────── #}
{% elif comp.type == 'image' %}
{% elif comp.type == 'logo' %}
{% elif comp.type == 'audio' %}
{% elif comp.type == 'video' %}
{# ── Charts ──────────────────────────────────────────────────────────── #}
{% elif comp.type in ('line_chart', 'bar_chart', 'area_chart', 'scatter_chart', 'chart') %}
{# ── Layout: container ───────────────────────────────────────────────── #}
{% elif comp.type == 'container' %}
{{ render_children(comp.children) }}
{# ── Layout: expander ────────────────────────────────────────────────── #}
{% elif comp.type == 'expander' %}
{% if comp.icon %}{{ comp.icon }} {% endif %}{{ comp.label }}
{{ render_children(comp.children) }}
{# ── Layout: card (CompositeComponent) ───────────────────────────────── #}
{% elif comp.type == 'card' %}
{% if comp.slot_header is defined and comp.slot_header %}
{% for child in comp.slot_header %}{{ render_component(child) }}{% endfor %}
{% elif comp.header %}
{% endif %}
{% for child in comp.children %}
{{ render_component(child) }}
{% endfor %}
{% if comp.slot_footer is defined and comp.slot_footer %}
{% elif comp.footer %}
{% endif %}
{# ── OAT UI primitives ───────────────────────────────────────────────── #}
{% elif comp.type == 'spinner' %}
{% elif comp.type == 'progress' %}
{% if comp.value is not none %}
{% else %}
{% endif %}
{% elif comp.type == 'skeleton' %}
{% elif comp.type == 'meter' %}
{# ── Avatar ──────────────────────────────────────────────────────────── #}
{% elif comp.type == 'avatar' %}
{# aria-label on the
already carries the accessible name — img alt
stays empty/decorative, or a screen reader announces the name twice. #}
{% if comp.src %}
{% else %}
{{ comp.initials | default('') }}
{% endif %}
{% elif comp.type == 'avatar_group' %}
{% for av in comp.avatars %}
{% if av.src %}
{% else %}
{{ av.initials | default('') }}
{% endif %}
{% endfor %}
{# ── Breadcrumb ──────────────────────────────────────────────────────── #}
{% elif comp.type == 'breadcrumb' %}
{# ── Button Group ────────────────────────────────────────────────────── #}
{% elif comp.type == 'button_group' %}
{# ── Toast ───────────────────────────────────────────────────────────── #}
{# oat.ink API: ot.toast(message, title?, options?) #}
{% elif comp.type == 'toast' %}
{# ── Pagination ──────────────────────────────────────────────────────── #}
{% elif comp.type == 'pagination' %}
{# ── Dialog ──────────────────────────────────────────────────────────── #}
{% elif comp.type == 'dialog' %}
{% set _dlg_id = "dlg-" ~ comp.id %}
{# ── Dropdown ────────────────────────────────────────────────────────── #}
{# oat.ink uses popovertarget/popover — NOT slot="trigger" #}
{% elif comp.type == 'dropdown' %}
{% set _menu_id = "menu-" ~ comp.id %}
{# ── Grid ────────────────────────────────────────────────────────────── #}
{# oat.ink's .container carries page-root padding/max-width/centering —
mxlit's already owns that job, so neutralize
them here or a nested grid ends up double-inset from the page edge. #}
{% elif comp.type == 'grid' %}
{% for child in comp.children %}
{{ render_component(child) }}
{% endfor %}
{# ── Grid Row ─────────────────────────────────────────────────────────── #}
{# oat.ink's own .row is display:grid + gap — use it natively instead of a
hand-rolled flex-basis %, which ignored .row's gap and overflowed/wrapped. #}
{% elif comp.type == 'grid_row' %}
{% for child in comp.children %}
{{ render_component(child) }}
{% endfor %}
{# ── Grid Col ─────────────────────────────────────────────────────────── #}
{# oat.ink's [class*=col-] reads --span/--offset custom props to size the
grid-column span — set them directly so any span/offset value works,
not just the offset-1..offset-6 classes oat.min.css predefines. #}
{% elif comp.type == 'grid_col' %}
{% for child in comp.children %}
{{ render_component(child) }}
{% endfor %}
{# ── HTML Table ───────────────────────────────────────────────────────── #}
{% elif comp.type == 'html_table' %}
{% if comp.caption %}{{ comp.caption | e }}{% endif %}
{% for child in comp.children %}{{ render_component(child) }}{% endfor %}
{# ── Table Head ───────────────────────────────────────────────────────── #}
{% elif comp.type == 'table_head' %}
{% for child in comp.children %}{{ render_component(child) }}{% endfor %}
{# ── Table Body ───────────────────────────────────────────────────────── #}
{% elif comp.type == 'table_body' %}
{% for child in comp.children %}{{ render_component(child) }}{% endfor %}
{# ── Table Foot ───────────────────────────────────────────────────────── #}
{% elif comp.type == 'table_foot' %}
{% for child in comp.children %}{{ render_component(child) }}{% endfor %}
{# ── Table Row ────────────────────────────────────────────────────────── #}
{% elif comp.type == 'table_row' %}
{% for child in comp.children %}{{ render_component(child) }}{% endfor %}
{# ── Table Cell ───────────────────────────────────────────────────────── #}
{% elif comp.type == 'table_cell' %}
{% if comp.header %}
1 %}colspan="{{ comp.colspan }}"{% endif %}
{% if comp.rowspan > 1 %}rowspan="{{ comp.rowspan }}"{% endif %}
{% if comp.scope %}scope="{{ comp.scope }}"{% endif %}
{{ html_attrs(comp.attrs) | safe }}>
{% for child in comp.children %}{{ render_component(child) }}{% endfor %}
|
{% else %}
1 %}colspan="{{ comp.colspan }}"{% endif %}
{% if comp.rowspan > 1 %}rowspan="{{ comp.rowspan }}"{% endif %}
{{ html_attrs(comp.attrs) | safe }}>
{% for child in comp.children %}{{ render_component(child) }}{% endfor %}
|
{% endif %}
{# ── File Input ──────────────────────────────────────────────────────── #}
{% elif comp.type == 'file_input' %}
{# ── Input Group ─────────────────────────────────────────────────────── #}
{# oat.ink: #}
{# Every child (prefix slot, plain children, suffix slot) renders through #}
{# group_child() so it stays a bare, direct
{% endmacro %}
{# ── Navbar ────────────────────────────────────────────────────────────────── #}
{% set ns_nav = namespace(has_navbar=false) %}
{% for comp in components %}
{% if comp.type == 'navbar' %}{% set ns_nav.has_navbar = true %}{% endif %}
{% endfor %}
{% if ns_nav.has_navbar %}
{% for comp in components %}
{% if comp.type == 'navbar' %}