@miadi/attention-ui
v0.3.0
Published
The React host of @miadi/attention-core for reviewing and answering Chronicle ATTENTION items. Core owns the vocabulary, behaviour and visual specification; this package owns React state and events. No component owns a URL, token, filesystem root, or medi
Readme
@miadi/attention-ui
The React host of @miadi/attention-core. Core owns the
item vocabulary, the panel's behaviour and its visual specification; this
package owns React state and events, plus a ReactNode slot where a host
renders its own navigation. It owns no transport.
A page without React renders the same panel from core
(renderAttentionPanelHtml, mountAttentionPanel); a parity test here holds
the two markups identical. @miadi/attention-ui/styles.css is core's
stylesheet, shipped under this package's path so existing imports keep working.
Lifecycle changes the panel’s posture. Open items remain prominent. In a mixed store, answered decisions move under History. When everything is answered, the panel becomes one quiet Attention complete row; View Decisions makes the full record available on demand without displacing the host’s main content.
import { AttentionPanel } from "@miadi/attention-ui";
import "@miadi/attention-ui/styles.css";
<AttentionPanel
items={items}
onAnswer={async (item, answer) => {
await client.answer({ episode: item.episode, id: item.id, answer });
return { tone: "ok", message: "Answer recorded." };
}}
/>;A public or otherwise non-writing host can use the same context and history surface without presenting a form:
<AttentionPanel items={items} interaction="read-only" />interaction is a host-granted capability, not a network inference. The host
decides whether it is "answer" or "read-only"; the package never inspects a
hostname, token, filesystem root, or medicine-wheel address.
The host supplies items and onAnswer; therefore a Next route, MCP-backed
desktop UI, Forgewright view, or another agent-authored interface can reuse the
same component without inheriting Miadi URLs, tokens, filesystem roots, or
medicine-wheel configuration.
The stylesheet adds responsive row/action layout, visible keyboard focus,
touch-sized controls, and long-identifier containment. Presentation remains
host-driven through theme, labels, className, and the stable
attention-* selectors. In an episode-local room, omit the optional episode
field from display items so the room identity is not repeated; global inboxes
can keep it.
A global inbox — every row from a different episode — passes renderEpisodeRef
to link each row to its room. The row's summary is a disclosure button, so the
node is rendered beside it rather than inside it, and it replaces the plain
slug:
<AttentionPanel
items={openAcrossEveryEpisode}
renderEpisodeRef={(item) => (
<Link href={`/chronicle/${item.episode}`}>{item.episode} →</Link>
)}
onAnswer={answer}
/>The package still learns no URL: the host returns whatever navigation it owns.
A link into a room can name the question it is about. initialOpenId opens
that item on the first render — server-side included, and under History
when it is already answered — so the reader is not handed the list a second
time:
<AttentionPanel items={items} initialOpenId={idFromTheQueryString} />Each row carries data-attention-id, so a host that wants to scroll or focus
can find it without the package owning a ref.
The panel preserves drafts while another item is reviewed, exposes each
disclosure through aria-controls, announces inline validation and async
results, identifies the durable attention ID, and leaves transport/error
semantics to the host callback.
Contract and first host: jgwill/Miadi#631. Quiet history and the public witness boundary: jgwill/Miadi#635.
