Ephemeral-AI-Lab/mayfly--packages-ui ↗★ 22
@ephemeral-ai/mayfly-ui
Renderer-neutral contracts, builders, and UI contribution services for Mayfly
Install
This plugin has no verified bundle, or compatibility checks failed. Read the repository notes first. Read the full README ↗
README
Read the full README ↗@ephemeral-ai/mayfly-ui
Pure, renderer-neutral builders for Mayfly's public UI wire format. The ui
namespace recursively clones caller data, then creates deeply frozen
MayflyUiNode objects with the same shape a plugin can write by hand. It includes
every content leaf, flex and viewport children, row/column stacks, surfaces,
scrolling, explicit Markdown and Mermaid nodes, structured charts, controlled patterns,
progress, spacers, and dividers. Plain nodes can
enter a stack directly; use ui.child only when child layout metadata is needed.
Caller-owned data is isolated. Snapshots already created by this module may be shared across builders and registry updates without cloning their immutable subtrees again. Arbitrary frozen objects do not receive this optimization. Wire data must not contain cycles or accessor properties; getters are rejected without being executed. Registration identity remains fixed even if the caller later changes the original definition.
import { defineMayflyComponent, ui } from '@ephemeral-ai/mayfly-ui'
export const metric = defineMayflyComponent({
id: '@acme/metric',
render: props => ui.surface({
title: props.label,
child: ui.stack.column([
ui.child(ui.progress({ value: props.value, max: 100 }), {
grow: 1,
when: { minWidth: 40 },
}),
]),
}),
})
Compose
compose is the one layered shell for visible UI:
import { compose, ui } from '@ephemeral-ai/mayfly-ui'
const surface = compose.panel({
title: 'Plugins',
escapeLabel: 'close',
body: compose.browser({
list: ui.list({ id: 'plugins', role: 'browse', selectedIds: [], items: [] }),
detail: ui.text('Select a plugin'),
}),
})
panel is a command Panel (overlay chrome, editor slot). decisionLane is the one lane for agent requests;
decisionStrip is a user-triggered Yes/No lane (y/n, Esc no). tabs, wizard, rail, browser, statusPage, and
completionPalette are body-only compositions. tabs is horizontal tabs over arbitrary pages; rail is the
vertical equivalent with a column rule. Product command panels are keys-only: no visible Save/Cancel/Close
buttons; multi-field forms save with the last field's Enter and Ctrl+S.
defineMayflyComponent records a package-namespaced id, then deeply freezes
each rendered node. It is a pure package-level factory, not a
runtime registry. Core still validates node kinds, values, depth, quotas, and
renderer safety when a plugin contributes the expanded tree.
The root entry is side-effect free. The explicit ./provider entry owns the four
Fiber-scoped snapshot registries; handles publish with set() and remove with
dispose(). A snapshot is frozen before its revision advances. set() is a
data refresh (reason: 'data') or a new instance boundary
(reason: 'replace', the only update that may change scope); callbacks do
not create acknowledgements — action handlers return structured replies that
core admits before a single-use publisher updates the original registration.
The package has no dependency on Harness, core, pi-tui, or a terminal runtime.
ui.markdown(source), ui.diagram(source), and ui.chart({ chart, ...data }) preserve
only renderer-neutral wire data. Mermaid and chart libraries are core-owned;
plugins do not install them or pass library-specific configuration.