Tern is a terminal where programs can draw native UI instead of text. A plugin block, or any CLI that speaks its Surface Protocol, returns a tree of nodes like card, table, chart or message, and Tern lays it out, themes it and animates it. The Elements reference lists 74 kinds. I wanted to see all of them at once, in my own theme, behaving the way they would in a real tool. So I built a block that draws every one.

The code is a public gist: Tern Elements Showcase. Clone it and link it:

git clone https://gist.github.com/0f305b68c6803d1b34e98db07b62f414.git tern-showcase
tern plugin link tern-showcase

Then run Open Elements Showcase from the palette. [ and ] switch between families.

The showcase block: a head row, one tab per family, and the layout page

How it is built

A Tern plugin is a folder with a plugin.toml and Luau files. The block lives in the host half, which runs in the session daemon, so it survives closing the window. Each block has three handlers that matter: init returns the state, view returns a node tree, and event and key change the state. After every handler Tern calls view again, diffs the tree by node id and sends only the changes. You never push a frame yourself.

The shell is about 200 lines. It draws a head row, a tabs strip with one tab per family and a key legend in the dock, and it routes events and keys to the active page. Each family is one module that returns a Page table with view, event, key and a once-a-second tick. Every kind gets its own section keyed by its name, so the section for table on the Data page always has the id main.data.table.

Nine agents wrote the nine pages in parallel against that contract. Each one read its family’s docs, wrote the page, ran luau-lsp against the tern.d.luau that tern plugin types generates, then reloaded the plugin in my running Tern and looked at screenshots of its own sections. The finished plugin is about 9,100 lines of Luau, most of it sample data.

Things I learned

  • The program owns every text field. editor, input and otp never edit themselves. Keys go to your key handler, you update text and cursor in state, and the next view redraws them. Focus is a raw frame op, cx:frame({ { "focus", id } }), because the diff never sends it.
  • One broken page breaks the whole block. A syntax error stops the plugin from loading, and the open block pane exits with status 1. A duplicate sibling key is quieter: Tern rejects that view and keeps the last good one. Both happened while nine agents edited files at once. Running the type check before each reload fixed it.
  • A region root is never drawn. main = ui.text(...) shows nothing. Every region needs a container, and ids in the layer region start at layer.<key>, not under your page.
  • Use tern.now() for durations only. It returns monotonic milliseconds, so os.date on it gives nonsense. The status bar clock shows time since the page loaded.
  • Scripting the window works well. tern ctl shot, dump, pick, click and wheel drive the live window. Every image below is a crop of a shot, aligned with the section’s dump rect. One catch: wheel scrolls whatever is under the pointer, and a list with a height cap eats the scroll. I had to pick a point outside inner scrollers first.
  • Some things don’t work yet. In Tern 0.9.9, colspan on an el td sets the attribute but draws one column. wrap = "char" on text looks the same as "word". tone on an el only sets data-tone, so without CSS it adds no color. A plugin cannot make a real block node, because Tern builds those around a command’s lens view, so that section explains this and draws a copy.

The elements

Every screenshot below is from the live block in Tern’s dark theme. Interactive kinds respond in the real block: the table sorts and picks rows, the questionnaire steps through its questions, the inputs take typing, and the overlays open and close.

Layout

Docs: Layout

col

A vertical flex stack; gap, cross-axis align and main-axis justify.

The col kind in the showcase

row

A horizontal flex row; children truncate instead of overflowing.

The row kind in the showcase

card

A framed object: icon, head, meta, end controls, status chip, fold.

The card kind in the showcase

section

An unframed fold: chevron and head over an indented body.

The section kind in the showcase

rule

A hairline divider, or a transcript marker (note, pin).

The rule kind in the showcase

spacer

Fixed vertical space: none 0, xs 2px, sm 6px, md 12px, lg 20px.

The spacer kind in the showcase

split

Panes the user resizes; drags persist through change events.

The split kind in the showcase

scroll

A bounded box that scrolls its children; bar or fade, optionally stuck to its end.

The scroll kind in the showcase

empty

What a view shows while it has nothing: icon, title, one line, the action that fills it.

The empty kind in the showcase

Text and code

Docs: Text and code

text

Styled spans: tokens, links, effects, fitting, citations.

The text kind in the showcase

md

Markdown in omp’s dialect.

The md kind in the showcase

code

Highlighted code: lang/path, numbers, marks, folds, excerpts, preview.

The code kind in the showcase

diff

A change, unified or side by side, word-level emphasis.

The diff kind in the showcase

ansi

Program output in a mini terminal: SGR, OSC 8, \r.

The ansi kind in the showcase

rows

Pre-rendered terminal rows at a fixed width (migration fallback).

The rows kind in the showcase

math

One typeset TeX formula, inline or display.

The math kind in the showcase

Data

Docs: Data

kv

An aligned key/value grid, or one inline line.

The kv kind in the showcase

stats

A flat strip of stat tiles: label, large value, sub line.

The stats kind in the showcase

table

Columns that hide by priority; sort, select, multi-pick; text, badge, button, spark, meter cells.

The table kind in the showcase

tree

A disclosure tree with guides and turning chevrons.

The tree kind in the showcase

json

A raw JSON value as a foldable tree with one-line previews.

The json kind in the showcase

badge

A tone-tinted pill.

The badge kind in the showcase

kbd

Keycaps with platform glyphs.

The kbd kind in the showcase

icon

One icon from Tern’s set; hover any icon for its name.

The icon kind in the showcase

avatar

A person’s, bot’s or provider’s mark, with a status dot.

The avatar kind in the showcase

avatars

An overlapping stack with a +N overflow chip and a label.

The avatars kind in the showcase

image

A PNG/JPEG/GIF/WebP/SVG blob, children drawn over it; click to zoom.

The image kind in the showcase

media

A file on disk by path, in Tern’s media viewer.

The media kind in the showcase

Slides on a sideways track: peek, arrows and dots, or a filmstrip.

The carousel kind in the showcase

attachment

A file chip, thumbnail tile or composer pill with progress, error and actions.

The attachment kind in the showcase

Lists, menus and navigation

Docs: Lists, menus and navigation

list

A listbox of item rows; selection and filter are yours.

The list kind in the showcase

item

One row: icon, label, detail, value, keycaps.

The item kind in the showcase

tabs

Underline strip between views, or a segmented control.

The tabs kind in the showcase

picker

A modal glass sheet drawn from data, placed in the block’s layer region.

The picker kind in the showcase

The picker open in the block’s layer: providers on the left, grouped rows with columns, and a detail pane.

The picker open in the block's layer: providers on the left, grouped rows with columns, and a detail pane.

A button that opens a dropdown of commands.

The menu kind in the showcase

An app’s menus in a row: quiet strip or floating pill.

The menubar kind in the showcase

An app’s sections; items with sub-items open a menu or panel.

The nav kind in the showcase

The trail back up a hierarchy; click a crumb to go there.

The breadcrumb kind in the showcase

pager

Pagination: a range, numbered pages, or a show-more row.

The pager kind in the showcase

Progress and motion

Docs: Progress and motion

spinner

An activity indicator with an optional label; style and tone.

The spinner kind in the showcase

shimmer

Text with a band of light sweeping across it.

The shimmer kind in the showcase

skeleton

Placeholder shapes while data loads; shimmer, pulse and wire looks.

The skeleton kind in the showcase

elapsed

A live timer or countdown; short and clock formats.

The elapsed kind in the showcase

rate

A number that eases to each new value (new target every second).

The rate kind in the showcase

progress

A progress bar, determinate or indeterminate; tones.

The progress kind in the showcase

meter

A level or stacked parts as a bar, ring or block grid; thresholds and marks.

The meter kind in the showcase

chart

Heatmap, bars, spark, line and area (one or several series), donut.

The chart kind in the showcase

effort

Omp’s thinking-effort ring, one sixth per level; fire at max.

The effort kind in the showcase

Inputs and buttons

Docs: Inputs and buttons

editor

Multi-line text the program owns: prompt, placeholder, ghost, decor, code, field look.

The editor kind in the showcase

input

One-line field: well look, or field look with addons.

The input kind in the showcase

button

Tern’s button: variants, sizes, icons, keycaps, on, busy, disabled.

The button kind in the showcase

buttons

Buttons drawn as one control: joined, split, toolbar.

The buttons kind in the showcase

select

A value picked from a menu: field, quiet, searchable combobox.

The select kind in the showcase

switch

An on/off setting: settings row, compact, segment.

The switch kind in the showcase

slider

A value or a range dragged along a track: default and accent.

The slider kind in the showcase

otp

A one-time code, one cell per character.

The otp kind in the showcase

calendar

A month grid or presets beside a week strip, for a day or a range.

The calendar kind in the showcase

date

A date field that opens a month calendar.

The date kind in the showcase

field

A labelled form row: stacked, row, grid; hint, required, error, warning.

The field kind in the showcase

Bars, toasts and overlays

Docs: Bars, toasts and overlays

status

A 24 px strip; right-side segs pinned right, low priority drops first.

The status kind in the showcase

seg

One segment: icon, spans, tone, children, optional click.

The seg kind in the showcase

toast

A transient notice in the window’s toast layer; the node draws nothing.

The toast kind in the showcase

overlay

Glass cards in the layer: dialogs, docked sheets, drawers, anchored popups.

The overlay kind in the showcase

A right-side inspector sheet with a head, a kv body and Cancel/Apply in the foot.

A right-side inspector sheet with a head, a kv body and Cancel/Apply in the foot.

A small modal dialog with keycapped buttons.

A small modal dialog with keycapped buttons.

A bottom drawer holding ANSI log output.

A bottom drawer holding ANSI log output.

Agent, chat and task kinds

Docs: Agent, chat and task kinds

tool

One step of work: data head over a framed, collapsible body, or a call waiting for approval.

The tool kind in the showcase

agent

One worker row: status glyph, badges, task, live stats and what it runs now.

The agent kind in the showcase

trace

An agent’s steps, a chain of thought, a reasoning block with sources.

The trace kind in the showcase

checklist

A phased todo list: full list, a dock pill with popover, or a still-open reminder.

The checklist kind in the showcase

message

One chat message: author, time, avatar, body, reactions, hover actions, status.

The message kind in the showcase

suggestions

Prompts to ask next: chips, numbered rows or small cards.

The suggestions kind in the showcase

questions

A questionnaire: steps, inline chips or a folding list, with answers, Next/Back/Skip/Submit and Other fields all wired.

The questions kind in the showcase

stacktrace

An error and its stack: app frames lit, library frames folded, code at a frame.

The stacktrace kind in the showcase

block

Tern’s own command-lens block around a lens view (Tern builds it; a plugin block can only draw a copy).

The block kind in the showcase

prefs

A full settings page (or sheet) from your data; every click is an event the page answers.

The prefs kind in the showcase

HTML elements

Docs: HTML elements

el

Plain HTML elements from a fixed tag list, with checkbox/radio controls and forms.

The el kind in the showcase

Try it

The gist has everything, including the six pictures the image, media and carousel demos load: gist.github.com/lfsmoura/0f305b68c6803d1b34e98db07b62f414. If you are building a Tern plugin, I found it easier to pick a kind by opening this block next to the design guide than by reading the props tables.