Skip to main content
CODEMUXManual

UI components

The components, props and input behavior available to CodeMux add-on views.

Add-on views use Preact JSX with the components on this page. CodeMux draws them with its own components, so they follow the user's theme automatically. There's no HTML, CSS, SVG or image loading. Props accept fixed values called tokens, never style strings.

import { Card, Stack, Text, Badge, Button } from "@codemux/plugin-sdk";

<Card title="Build status" spacing="sm">
  <Stack direction="horizontal" spacing="sm" align="center">
    <Badge color="success">Passing</Badge>
    <Text color="muted" size="sm">main · 2 minutes ago</Text>
  </Stack>
  <Button onPress={(event) => refresh(event.context)}>Refresh</Button>
</Card>

Each component has a props type, such as StackProps or TextFieldProps. A value outside the allowed range fails type checking. If one reaches CodeMux anyway, CodeMux rejects the update and stops the add-on.

Components

ComponentProps
Stackdirection (vertical | horizontal), spacing, align
Gridcolumns (1–12), spacing, align
Cardtitle, spacing, align
TextText children
BadgeText children
Headinglevel (1–6), text children
MarkdownMarkdown text children. HTML and images are removed. HTTPS links open through CodeMux and need external.open
Buttonlabel or children, disabled, onPress
TextField, TextArealabel, value, placeholder, disabled, onChange (event.value is a string)
Selectlabel, options ({ label, value }[]), value, disabled, onChange
Checkbox, Switchlabel, checked, disabled, onChange (event.value is a boolean)
Tabslabel, options, value, disabled, onChange. Children are the content of the selected tab
Listitems (up to 500 strings), label
Tableheaders, rows (up to 500 rows of up to 20 strings), label
Progressvalue, max, label
Iconname (see Icons), label, color
Divider—
EmptyStatetitle, children

On List, Table and Icon, label is the accessible name. CodeMux virtualizes long lists and tables, so use List or Table rows for large data instead of many Text nodes.

There's no alert or toast component. To show a problem, use Text or Badge with a color, or EmptyState. For a short message outside the view, use ctx.ui.notify.

Shared props

Every component except Icon and Divider also accepts:

PropValues
sizexs, sm, md, lg
colordefault, muted, success, warning, danger, accent
width, heightauto, full

Layout props:

PropValues
spacingnone, xs, sm, md, lg
alignstart, center, end, stretch

Strings such as label, title and placeholder can be up to 4 KiB. Text children and field values can be up to 32 KiB.

Events

Only Button has onPress. Only TextField, TextArea, Select, Checkbox, Switch and Tabs have onChange. Every callback gets { context, value }:

  • context is a fresh context handle with a user interaction. Use it for appendText, links.open and opening views.
  • value is the new value of an input. onPress doesn't set it.

A callback can be async. If it rejects with a PluginError, CodeMux records it and the add-on keeps running. If it throws synchronously or rejects with anything else, the add-on stops. See Faults.

Inputs

TextField and TextArea keep the text locally while the user types and report every change through onChange. The value you render replaces that text only when it's different from the last value the field reported. Rendering the value back never moves the cursor or drops keystrokes. A field without value is uncontrolled.

const [title, setTitle] = useState("");

<TextField
  label="Title"
  value={title}
  onChange={(event) => setTitle(event.value)}
/>

Select, Checkbox, Switch and Tabs always show the value or checked you render, so update your state in onChange:

const [tab, setTab] = useState("open");

<Tabs
  label="Issues"
  value={tab}
  options={[{ label: "Open", value: "open" }, { label: "Closed", value: "closed" }]}
  onChange={(event) => setTab(event.value)}
>
  {tab === "open" ? <OpenIssues /> : <ClosedIssues />}
</Tabs>

onChange runs on every keystroke. Don't make a host request each time. Save when the user clicks a button, or after a pause.

Rendering limits

Each view can have up to 2,000 nodes, 32 levels of nesting and 256 KiB of state, and four views can be open at once. If an update goes over a limit, even for a moment, CodeMux rejects the whole update and stops the add-on.

The SDK combines updates into about 16 batches per second and keeps only the latest value of each prop. Small changes to existing content work better than replacing large parts of the tree.