Skip to content

Markup & UI

AIVI markup is an expression language for UI trees. It looks XML-like, but it is part of the language surface and participates in ordinary AIVI binding rules.

Basic widgets

aivi
value view =
    <Window title="Greeting">
        <Box>
            <Label text="Hello" />
            <Label text="World" />
        </Box>
    </Window>

Capitalised tags name widgets or components. Attributes can be plain text or embedded expressions.

Binding expressions into attributes

aivi
value header = "Users"

value view =
    <Window title="Directory">
        <Label text={header} />
    </Window>

Anything inside {...} is an AIVI expression.

Typed widget properties

Widget attributes are schema-checked host properties, not an open CSS map. That means you bind ordinary expressions into the properties a widget exposes today.

aivi
type Bool -> Float
func previewOpacity = .
 T|> 1.0
 F|> 0.25

value previewVisible = True

value view =
    <Window title="Preview">
        <Button
            label="Blue"
            animateOpacity={True}
            opacity={previewOpacity previewVisible}
        />
    </Window>

Button.opacity takes a Float and the GTK host clamps it into the 0.0 to 1.0 range. Button.animateOpacity takes a Bool and enables the built-in opacity transition class, so changes to opacity fade instead of snapping.

Because these are ordinary attributes, they can be driven by any signal-backed or source-backed expression. Arbitrary cssProps maps are not part of the current AIVI surface yet.

Conditional rendering with <show>

aivi
value isVisible = True

value view =
    <Window title="Status">
        <Box>
            <show when={isVisible} keepMounted={True}>
                <Label text="Visible" />
            </show>
        </Box>
    </Window>

<show> renders its body only when the condition holds.

Local bindings with <with>

aivi
type Item = {
    id: Int,
    title: Text
}

type Screen =
  | Ready (List Item)

value screen =
    Ready [
        { id: 1, title: "Alpha" },
        { id: 2, title: "Beta" }
    ]

value view =
    <Window title="Items">
        <Box>
            <with value={screen} as={currentScreen}>
                <match on={currentScreen}>
                    <case pattern={Ready items}>
                        <each of={items} as={item} key={item.id}>
                            <Label text={item.title} />
                        </each>
                    </case>
                </match>
            </with>
        </Box>
    </Window>

<with> binds a value for the nested subtree.

Pattern matching with <match>

Markup has control nodes for the same pattern-oriented style used elsewhere in the language:

aivi
type Item = {
    id: Int,
    title: Text
}

type Screen =
  | Loading
  | Ready (List Item)
  | Failed Text

value screen =
    Ready [
        { id: 1, title: "Alpha" },
        { id: 2, title: "Beta" }
    ]

value view =
    <Window title="Items">
        <Box>
            <match on={screen}>
                <case pattern={Loading}>
                    <Label text="Loading..." />
                </case>
                <case pattern={Ready items}>
                    <each of={items} as={item} key={item.id}>
                        <Label text={item.title} />
                        <empty>
                            <Label text="No items" />
                        </empty>
                    </each>
                </case>
                <case pattern={Failed reason}>
                    <Label text={reason} />
                </case>
            </match>
        </Box>
    </Window>

Iteration with <each>

<each> renders one subtree per list item and can include an <empty> fallback when the list is empty.

Summary

ElementMeaning
<Label ... />A widget node
<fragment>...</fragment>Group children without an outer widget
<show when={...}>Conditional rendering
<with value={...} as={...}>Bind a value in markup
<match on={...}>Pattern-based rendering
<case pattern={...}>One match branch
<each of={...} as={...} key={...}>Iterate a list
<empty>Empty-list fallback inside <each>

AIVI Language Manual