Decisions
Plan card
An agent's proposed steps with typed per-step status, approve or reject while proposed, and resolved states.
Starts when it scrolls into view
NORD FROST · DARK · RADIUS 6/10 · BASE 16PX · SHADOW
Code
Cut from the gallery story this page runs, so it stays true.
let tokens = cx.theme().semantic_tokens();
let status: SharedString = self
.last_plan_event
.clone()
.unwrap_or_else(|| "Approve, reject, or open a step".into());
let steps = [
PlanStep::new("compare", "Compare unit prices across suppliers")
.detail("pricing.md · 3 suppliers")
.status(PlanStepStatus::Done),
PlanStep::new("risk", "Check delivery-window risk")
.detail("Cold-chain capacity for the next two weeks")
.status(PlanStepStatus::Done),
PlanStep::new("draft", "Draft the revised bulk order")
.status(match self.plan_state {
PlanState::Approved | PlanState::Running => PlanStepStatus::Running,
_ => PlanStepStatus::Pending,
}),
PlanStep::new("send", "Send confirmations")
.detail("Emails 3 suppliers; needs its own approval"),
];
v_flex()
.gap(tokens.spacing.md)
.child(
PlanCard::new("rollout", "Switch bulk orders to Alpenrose Dairy")
.description(
"Four steps; the last one sends email and will ask again.",
)
.steps(steps)
.state(self.plan_state)
.note("Decided in this session")
.editable(true)
.on_event(cx.listener(|this, event: &PlanEvent, _, cx| {
match event {
PlanEvent::Approved { .. } => {
this.plan_state = PlanState::Approved;
}
PlanEvent::Rejected { .. } => {
this.plan_state = PlanState::Rejected;
}
PlanEvent::EditRequested { .. }
| PlanEvent::StepActivated { .. } => {}
}
this.last_plan_event = Some(format!("{event:?}").into());
cx.notify();
})),
)
.child(
div()
.id("plan-story-status")
.role(Role::Status)
.aria_label(status.clone())
.text_xs()
.text_color(cx.theme().muted_foreground)
.child(status),
)
Events and ownership
- Who owns the state
- PlanCard renders state supplied by the application; it does not own durable work.
- What it emits
- Interactive intent is reported through the typed PlanEvent contract and stable application IDs.
- What it means
- An agent's proposed steps with typed per-step status, approve or reject while proposed, and resolved states.
- Overflow and motion
- Growing content remains reachable in a bounded vertical surface; reduced motion preserves a useful state.
Event types
- PlanEvent
What the demo does and does not show
The demo above is the same Rust as the native component, compiled to WebAssembly and drawn on a WebGPU canvas — not a mock-up, a recording, or a re-implementation. What it paints is what the component paints: layout, colour from the live theme, and motion.
The live browser specimen requires WebGPU; the native component remains the authoritative runtime.
A canvas cannot stand in for the platform, so treat everything that goes through the operating system as unproven here: keyboard handling and shortcuts, screen reader semantics, text input and IME, the clipboard, file drops, and the frame rate you would get on your own GPU. Judge those in a native build. If your browser has no WebGPU, the frame says so rather than showing an empty box.