@miadi/reference-ui
v0.2.6
Published
Render Miadi references — miadi-chronicle:<episode>, miadi-foundation:<slug>, miadi-ceremony:<id>, miadi-circle:<id>, miadi-notebook:<uuid>, miadi-prototype:<slug>, miadi-review:<uuid>, miadi-tracing:<domain>/<resource>/<id>, rise://<owner>/<repo>/<path>
Readme
@miadi/reference-ui
Part of the Miadi stack: @miadi/inquiry-weave/references parses and scans Miadi
addresses, and this package renders them. A Miadi address is a name for a thing in
the chronicle or its grounding:
| address | names |
| --- | --- |
| miadi-chronicle:550 | an episode (550, ep550, or a folder name) |
| miadi-chronicle:550/foundations/<topic> | a foundation packet an episode wrote itself |
| miadi-foundation:<slug>[/<file>][#place] | a distributed foundation packet, or one file in it |
| miadi-ceremony:<id>, miadi-circle:<id> | a ceremony or circle; its page asks the reader for a seat |
| miadi-notebook:<uuid> | a Deep Diver notebook an episode holds |
| miadi-review:<uuid> | a Miadi review; the resolve door also names the episodes holding it |
| miadi-prototype:<slug> | a prototype made from an episode |
| miadi-tracing:<domain>/<resource>/<id>[@<v>] | a trace, prompt, session, dataset or score config (miadi-trace:<domain>/<id> and miadi-prompt:<domain>/<name>[@<v>] are accepted) |
| rise://<owner>/<repo>/<path>[@<commit>] | a specification file |
| miadi-capability:<owner>/<repo>/<kind>/<name> | one thing a git repository provides: a package, a CLI, a skill (Episode 568); its page names what its files say and, for a package, what consumes it |
| miadi-tmux:<account>@<host>:<session> | a tmux session; the open door sends a person who runs that host to it in the cockpit, and the terminal click moves to it when it is on the same server (jgwill/Miadi#968) |
// after the scheme is accepted on input and dropped on output. Rendering options
travel in mc.* query parameters (mc.show=card, mc.label=…, mc.highlight=true),
or in a directive: {{ miadi-chronicle:550 | show=card, label="The walk" }},
{{ foundation:lattice }}.
The package holds no URL and no token. The host decides who resolves a reference.
Install
npm i @miadi/reference-uiimport "@miadi/reference-ui/styles.css"Render markdown with React
import ReactMarkdown, { defaultUrlTransform } from "react-markdown"
import { createRemoteResolver, prepareReferenceViews, referenceUrlTransform } from "@miadi/reference-ui"
import { createReferenceLink } from "@miadi/reference-ui/react"
const origin = process.env.MIADI_PUBLIC_URL! // e.g. the Miadi front that serves /api/chronicle/resolve
export async function Body({ markdown }: { markdown: string }) {
const resolver = createRemoteResolver({ origin, init: { next: { revalidate: 300 } } })
const { markdown: text, views } = await prepareReferenceViews(markdown, resolver, { base: origin, linkBare: true })
return (
<ReactMarkdown urlTransform={referenceUrlTransform(defaultUrlTransform)} components={{ a: createReferenceLink(views) }}>
{text}
</ReactMarkdown>
)
}prepareReferenceViews folds directives into links, links bare mentions when
linkBare is set, and resolves each reference once. createReferenceLink renders
a reference link as a chip or card and leaves every other link as it was. Pass
LinkComponent (for example Next's Link) to keep client navigation.
A front that cannot be reached, or a reference that names nothing, renders as an unresolved chip. It never raises an error on the page.
Render without React
import { renderReferenceHtml, viewFromAnswer } from "@miadi/reference-ui"
const view = viewFromAnswer(uri, await resolver(uri), { base: origin })
if (view) element.innerHTML = renderReferenceHtml(view)Every text is escaped. The React components produce the same markup.
Resolve on your own server
A resolver is (uri) => Promise<ResolveAnswer | null>. createRemoteResolver
calls GET <origin>/api/chronicle/resolve?uri=…. A host that holds its own catalog
builds views directly with episodeReferenceView, foundationReferenceView,
episodeFoundationView, artefactReferenceView and wheelReferenceView.
A review address answers holders too: every episode the reader may see that holds
the review, with the relation and the version it used. referenceHolders(answer)
returns them with every field checked, so a review page can show where it is used:
const holders = referenceHolders(await resolver(`miadi-review:${id}`))
const line = holders.map((h) => `miadi-chronicle:${h.number ?? h.episode}`).join(" · ")
// prepare `line` with prepareReferenceViews like any other markdownAn artefact or spec chip points at the front's open door
(/api/chronicle/open?uri=…), which redirects to the notebook, the published
prototype, the room holding a trace, the review's page, or the specification file. The chip never
carries a provider URL, a project id or an editing address.
Styling
Class names start with mr-ref. Colours are custom properties on .mr-ref
(--mr-east, --mr-south, --mr-west, --mr-north, --mr-foundation, --mr-wheel,
--mr-muted, --mr-card-bg, --mr-hover-bg, --mr-title). Dark values apply under a
.dark ancestor or [data-theme="dark"]. A host that darkens by system preference
(@media (prefers-color-scheme: dark) on :root:not(.light)) adds the class
mr-auto-dark to <html> and gets dark inks with the system; a .light root or region
keeps light ones. Every ink measures at least 4.5:1 against the page, the card and the
hover surface in both themes.
Passing views to a client component
prepareReferenceViews returns a Map. To cross into a client component, pass
Object.fromEntries(views); lookupView and createReferenceLink accept either form.
Keys come from referenceKey, so look a view up with lookupView(views, href), never
with views[href].
