Ephemeral-AI-Lab/mayfly--packages-ui ↗★ 22

@ephemeral-ai/mayfly-ui

Renderer-neutral contracts, builders, and UI contribution services for Mayfly

包名
@ephemeral-ai/mayfly-ui
兼容性
待验证
Cordis 依赖范围
^4.0.5
版本
0.1.4-rc.2
许可证
MIT
最近更新
2026年10月22日

安装

此插件尚未提供可验证的 bundle,或兼容性检查未通过。请先阅读仓库说明。 阅读完整 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.