dashboard

Direction: guest calls into the host through it · Capability: none (pure data / dispatch) · Worlds: dashboard-plugin (imports)

CR.4: plugin-contributed dashboard sections.

A plugin puts its own block on the launch page — recent projects, a git summary, whatever it is for. The section lands in the SAME registry the built-in sections live in, so dashboard.sections orders it, the compositor renders it, and the theme styles it, with no host kind-branch.

A function, not data — unlike help

The help seam hands over a string once and drops the guest, because a help page does not change between load and read. A dashboard section is different in kind: render-section takes a ctx the guest cannot know at load — the pane width, whether Nerd Font glyphs are available, the editor version — and DB.6 exists precisely because those change while the editor runs. So the guest stays instantiated and the host calls it per compose.

Freezing a section into text at registration would make it blind to the icon palette and unable to show anything live, which is most of what "whole-author a section" is for.

Where it runs, and what that costs

render-section is a sync call on the host's sync linker (the one grammar and error-parser share), carrying the Reflex-class budget rather than the generous lifecycle default. It executes on the actor thread inside the dashboard compositor.

That cost is real and deliberate. Composition is a LatencyClass::Display action — :dashboard, startup, or a DB.6 option change — never per-keystroke and never per-frame, and the fuel budget bounds a pathological guest to a bounded stall rather than a hang.

The alternative, rendering off-actor and recomposing when the fragment lands, is purer on paramount goal #1 and was rejected on UX: it makes the launch page visibly reflow a frame or two after it appears, at startup, which is the content-jump the UX contract vetoes.

What the host does with a bad fragment

Validates and drops, never traps. Guest output is untrusted: a row with no spans, a span whose link does not parse, or a fragment longer than the row cap is dropped at debug!. A trap poisons the section — it renders nothing further this session and the REST OF THE PAGE still composes, exactly as a trapping error-parser costs its own entries and not the build.

Functions (1)

register-section

register-section: func(id: string, order: s32, default-enabled: bool) -> result<_, string>

Declare a section.

id is NOT auto-namespaced, unlike help.register-topic and theme.register-element. That is deliberate: replacing a built-in section by id is a supported thing to want, so a plugin registering getting-started is exercising the feature rather than squatting. Unload restores whatever it displaced — the registry shadows rather than overwrites.

order is the default sort key (lower sorts first); default-enabled is whether it shows when the user has not set dashboard.sections.

err when the spec is malformed (an empty id) — never a trap.

Example — Register dashboard sections by id and order; an un-namespaced builtin id replaces the builtin · crates/lattice-plugin-host/tests/fixtures/dashboard-guest/src/lib.rs

let _ = register_section("recent", 15, true);
// Not namespaced — this is meant to displace the builtin.
let _ = register_section("getting-started", 20, true);

Types (7)

record ctx

record ctx {
    pane-width: u32,
    nerd-fonts: bool,
    version: string,
}

Read-only facts a section renders against. Mirrors the native DashboardCtx.

Fields

  • pane-width: u32 — Pane width in cells.
  • nerd-fonts: bool — Whether Nerd Font glyphs may be used. A section that draws icons MUST honour this and fall back to the BMP-block palette at the same cell width, or the page's column geometry shifts when the user toggles ui.nerd_fonts.
  • version: string — The editor version string.

enum role

enum role {
    logo,
    cursor,
    title,
    tagline,
    section-heading,
    body,
    key,
    hint,
    link,
}

Semantic style role. Never a colour — each resolves to a dashboard.* theme element at compose time, so a section re-colours on :colorscheme like everything else.

enum align

enum align {
    left,
    center,
}

Line-level alignment.

variant link-target {
    command(string),
    topic(string),
    url(string),
}

What <CR> on a link span follows.

Cases

  • command: string — Run an ex-command — command("tutor") STARTS the tutor.
  • topic: string — Open a :help topic.
  • url: string — Open a URL externally.

record span

record span {
    text: string,
    role: role,
    link: option<link-target>,
}

A run of text with a role and an optional follow target.

Fields

  • text: string
  • role: role
  • link: option<link-target>

record row

record row {
    spans: list<span>,
    align: align,
}

One visual line: spans laid out left→right.

Fields

  • spans: list<span>
  • align: align

record fragment

record fragment {
    rows: list<row>,
}

A section's rendered contribution.