Agents answer in live cards you can use, not walls of text.
Website · Playground · Install · Components · Live data · For agents
Iris is a bb plugin that lets agents answer with live, interactive UI instead of walls of text. An agent writes a short ```iris block, and bb draws it as a real card inside the reply: charts you can hover, sliders that recompute, tables you can sort, test runs that fill in while they run, pickers that wait for your answer. Every card wears your bb theme and follows it when you switch.
A real bb thread at 60 fps: the person hovers a chart, drags a what-if slider and tunes a decision. Watch it in full resolution.
Iris is listed in the BB Community marketplace, so bb installs it by name. On a Mac running bb 0.48 or later:
bb plugin install irisOr straight from this repository, following its release tags:
bb plugin install "git:https://github.com/vburojevic/bb-plugin-iris.git@^1.0.0"That's it. New agent sessions learn Iris on their own, and the gallery appears in bb's sidebar under Iris. Sessions that were already running pick it up when they next start or resume. Iris adds about 3,600 tokens of instructions to each session it reaches: its rules and an index of every component. Prop details and examples load only when an agent asks for them.
Tip
Try every component in your browser first: the playground runs Iris's real renderer, with a live editor and links you can share.
|
Show, don't tell. Status reports, proof that work is done, PR and CI state, comparisons and plans come back as cards, with the answer in a sentence or two above them. |
140 components. Layout, charts, data grids, diagrams, code and diffs, test and coverage reports, eval runs, agent traces, flame graphs, plans, decisions, maps, media, 3D, math, sheets, API inspectors, whiteboards and more. See every one. |
|
Your theme, live. Colors come only from your bb theme: the accent is its primary, tones use its success, warning and danger colors, and nothing has a palette of its own. |
Live data. A live card re-reads its files every 2 seconds. |
|
Interactive both ways. Inputs recompute cards in place, buttons put text in your composer, and |
Apps when you need them. |
|
Safe by design. No |
Measured. Every release passes an eval suite that runs real chat moments through an agent and scores whether it showed the right card. It scores 94.5 of 100 today, over 35 cases run twice each. |
An agent writes JSX-style markup with live values, and bb renders it as you read.
<state seats={10} plan="Pro" />
<let price={(plan == "Pro" ? 29 : 12) * seats} />
<Card title="Estimate" icon="calculator">
<Row>
<Segmented bind="plan" options={["Basic", "Pro"]} />
<Slider bind="seats" min={1} max={50} label="Seats" />
</Row>
<Row>
<Metric label="Per month" value={price} format="usd" />
<Metric label="Per year" value={price * 12 * 0.85}
format="usd" note="15% off yearly" />
</Row>
</Card>
From the agent's reply to a card you can use:
flowchart LR
A["Agent writes<br>an iris block"] --> B["iris_check<br>reads its files, moves every<br>input, outlines the card"]
B --> C["bb streams the reply;<br>each tag draws<br>as it arrives"]
C --> D["You use the card:<br>inputs recompute,<br>live cards re-read every 2 s"]
D -- "a button or a picker" --> E["Your answer goes<br>back to the agent"]
A broken part shows a readable error with an Ask to fix button while the rest of the card renders.
![]() Proof it's done Acceptance Is it really done: each criterion with its status, a note and evidence that opens. | ![]() A decision to make Decision Options scored against criteria with weights people can tune; the ranking updates live. | ![]() Data to read Chart Line, area, bar, horizontal bar, pie, donut, scatter and radar charts in the theme's colors, with hover readouts, annotations and a table view. |
![]() A test run Tests A test run: counts, failures first with messages and file links. | ![]() Where the PR stands PullRequest This branch's pull request: state, checks, review status, mergeability and a link. | ![]() A plan to approve Plan Steps of work with files, risk and estimates. |
![]() When it will land Forecast Done counts over time as an ETA with a band from recent rates, a burn-down or burn-up, scope changes and plain words. | ![]() Spend against a limit Budget Spend against a limit: a meter per line, the total, a breakdown, the burn rate and when the limit is hit at it. | ![]() What changed on screen CompareGrid Many before and after image pairs in a grid, each tile with its changed-pixel count and a status (changed, missing, same); sort and filter by change. |
Open a family to see each card as it renders in bb. Every picture links to its live page in the playground, with its code and every prop.
Layout · 8 components: Card, Stack, Row, Grid, Tabs, Tab, Details, Divider
![]() Card The frame for one idea: a titled surface with a toolbar (expand, export, source). | ![]() Stack Children in a column with even spacing. | ![]() Row Children side by side with equal widths; wraps on narrow screens. |
![]() Grid Tiles in a responsive grid. | ![]() Tabs Switch between Tab children. | ![]() Tab One page inside Tabs. |
![]() Details A collapsible section for secondary detail. | ![]() Divider A hairline between sections, optionally labeled. |
Content · 14 components: Text, Heading, Callout, Metric, Badge, Icon, KeyValue, List, Item, Link, Image, Progress, Gauge, Empty
![]() Text Markdown prose: bold, italics, `code`, links, lists. | ![]() Heading A section heading inside a card. | ![]() Callout A highlighted note: a warning, a tip, the bottom line. |
![]() Metric One key number with a label, an optional change and a sparkline. | ![]() Badge A one to three word status chip. | Icon A Lucide icon. |
![]() KeyValue Labeled fields: settings, metadata, a summary. | ![]() List Rows with a title, detail, icon, trailing value and an optional click action. | ![]() Item One row inside List. |
![]() Link A link to a URL or a workspace file. | ![]() Image An image from the workspace, thread storage or an https URL. | ![]() Progress A bar showing how far along something is. |
![]() Gauge A dial for one value against a range. | ![]() Empty What to show when there is nothing: an icon, a line and an optional action. |
Data · 7 components: Table, Json, SegmentedBar, Sparkline, Heatmap, CalendarHeatmap, DataGrid
![]() Table Rows and columns with sorting, search, paging and number formatting. | ![]() Json A collapsible JSON explorer for an object or a loaded file. | ![]() SegmentedBar Shares of one whole as a single bar with a legend. |
![]() Sparkline A tiny trend line for inline use. | ![]() Heatmap A grid of rows by columns, each cell colored by its value: eval scores by case and run, failures by suite and day, latency by route and region. | ![]() CalendarHeatmap Activity per day on a calendar grid, like a contributions graph. |
![]() DataGrid An explorer for large results (CSV, JSON, SQL. |
Charts · 1 component: Chart
![]() Chart Line, area, bar, horizontal bar, pie, donut, scatter and radar charts in the theme's colors, with hover readouts, annotations and a table view. |
Diagrams · 3 components: Diagram, Tree, Graph
![]() Diagram A Mermaid diagram (flowchart, sequence, state, class, ER, gantt). | ![]() Tree A collapsible tree: a file tree, a hierarchy, an outline. | ![]() Graph An interactive node-and-edge graph from data: dependency, call, module or service graphs. |
Code · 5 components: Code, Snippet, Diff, Logs, StackTrace
![]() Code A code block with syntax highlighting in bb's code theme. | ![]() Snippet Lines of a real file from the workspace, read when the card renders; clicking opens the file. | ![]() Diff A unified diff for one file in bb's diff viewer, unified or split. |
![]() Logs Command output with real ANSI colors, line numbers, a filter and error lines marked. | ![]() StackTrace An error and its stack with your own frames highlighted and clickable; library frames fold away. |
Project · 12 components: Tests, Findings, Coverage, Benchmark, Delta, TestHistory, Changes, Commits, PullRequest, Environments, Threads, Pipeline
![]() Tests A test run: counts, failures first with messages and file links. | ![]() Findings Review, lint or type errors grouped by severity, each with file, line, detail and fix. | ![]() Coverage Code coverage per file with the weakest files first. |
![]() Benchmark Before and after timings with the change and whether it improved. | ![]() Delta Before and after for any values: each row's change, its percent and whether it went the right way, with a tally of better, worse and same. | ![]() TestHistory Pass and fail per test across runs, with flaky tests (pass and fail flipping, or passing on retry) marked first, then failing ones and since when, then fixed ones. |
![]() Changes Files changed in this thread's checkout with +/- counts; each expands to its diff. | ![]() Commits This branch's commits since the default branch, newest first; each opens its changes. | ![]() PullRequest This branch's pull request: state, checks, review status, mergeability and a link. |
![]() Environments This project's worktrees: branch, threads in each, changed file count and pull request. | ![]() Threads Live status of bb threads: running, waiting on the person, done, failed. | ![]() Pipeline A CI run: runs, jobs and steps with status and duration, failures open. |
Agents · 4 components: EvalReport, AgentTrace, ContextMeter, ToolCall
![]() EvalReport Eval cases across runs against a baseline: the score and its change, categories, and a case-by-run grid with regressions first. | ![]() AgentTrace An agent run as a span tree on a timeline: model calls, tool calls and subagents with time, tokens, cost and errors. | ![]() ContextMeter How full a session's context window is, split by category, with model, cost and how both moved over the session. |
![]() ToolCall One tool or MCP call: name and server, arguments, result, timing and retries, collapsed to one line until opened. |
Debug · 3 components: Flamegraph, Bisect, BlastRadius
![]() Flamegraph A CPU profile as a flame graph: click a frame to zoom into it, search to highlight matches and their share, hover for self and total time, and the heaviest functions listed. | ![]() Bisect A git bisect in flight: the commits from good to bad, each marked, the range still in play, the commit under test and about how many steps are left. | ![]() BlastRadius What a change can break: each changed file with the files that import it, level by level, and the tests that reach it, as a tree. |
Evidence · 4 components: Acceptance, Compare, CompareGrid, Provenance
![]() Acceptance Is it really done: each criterion with its status, a note and evidence that opens. | ![]() Compare A visual difference between screenshots, reference first: wipe (a draggable divider), side by side, blink, or diff (changed pixels marked, with a count). | ![]() CompareGrid Many before and after image pairs in a grid, each tile with its changed-pixel count and a status (changed, missing, same); sort and filter by change. |
![]() Provenance Which build or revision is really installed or deployed where, from source to live, one stage per step. |
Planning · 10 components: Plan, Steps, Timeline, Decision, Board, Roadmap, Estimate, Calendar, Status, DecisionLog
![]() Plan Steps of work with files, risk and estimates. | ![]() Steps A checklist of progress: done, current, todo, blocked. | ![]() Timeline Dated events in order, newest or oldest first. |
![]() Decision Options scored against criteria with weights people can tune; the ranking updates live. | ![]() Board A kanban board: cards in status columns. | ![]() Roadmap A Gantt chart of dated work in lanes, with milestones and a today line. |
![]() Estimate Three-point estimates with P50 and P90 totals. | ![]() Calendar Events on a month grid. | ![]() Status Health of several workstreams: on track, at risk, off track, done. |
![]() DecisionLog Decisions made so far: topic, what was decided, who decided, why and when, newest first, with topic filters and search. |
Inputs · 14 components: Slider, Stepper, Toggle, Segmented, Select, Radio, Checklist, Rank, ColorInput, Input, TextArea, DateInput, Rating, Button
![]() Slider Pick a number on a range; the card recomputes live. | ![]() Stepper A count with minus and plus buttons. | ![]() Toggle An on/off switch. |
![]() Segmented Pick one of a few short options. | ![]() Select Pick one of many options from a menu. | ![]() Radio Pick one option, each with a detail line. |
![]() Checklist Pick several options; the state is a list of the picked values. | ![]() Rank The person puts options in order: drag the grip, or the up and down buttons, or Alt and the arrow keys. | ![]() ColorInput Pick a color: a swatch that opens the system picker, a hex field, optional presets, and the contrast on each background it will sit on. |
![]() Input A one-line text field. | ![]() TextArea A multi-line text field. | ![]() DateInput Pick a date (yyyy-mm-dd). |
![]() Rating One to N stars. | ![]() Button Runs an action: change state, put text in the composer, send it, copy, open a file or URL. |
Pickers · 5 components: FollowUps, Choices, Submit, Variants, Variant
![]() FollowUps Suggested next prompts after a finished answer. | ![]() Choices Optional next steps after a finished turn as tappable cards; the pick goes into the composer. | ![]() Submit Only inside an iris_ask picker: sends the answer back to you. |
![]() Variants Design directions to flip through: one large preview at a time with a switcher, arrows and keys. | ![]() Variant One direction inside Variants: its preview is its children, or html for a sandboxed mock page. |
Media · 8 components: Gallery, Screens, Hotspots, Map, PhotoSearch, VideoPlayer, VideoCompare, AudioPlayer
![]() Gallery Images in a grid; a click opens a lightbox with arrows, Esc to close and the caption. | ![]() Screens Screenshots as a matrix: one row per screen, one column per device, theme or locale, each tile with its label and an optional status. | ![]() Hotspots Numbered pins on an image; each opens its title and text. |
![]() Map Places on an OpenStreetMap map with a numbered list beside it, fit to show them all; route joins them in order with distances. | ![]() PhotoSearch A real photo for a description, from Wikimedia Commons only, with its author and license credited under it. | ![]() VideoPlayer A video that streams and seeks: chapters on the scrubber and in a list, notes that jump to their time, frame stepping (, and . |
![]() VideoCompare Two videos on one timeline, reference first: one play and scrub control, side by side or wipe, frame steps (, and .), speed, and an offset that aligns them ([ and ] shift a frame). | ![]() AudioPlayer Audio with a waveform drawn from the decoded file (click or drag to seek) and a transcript whose current line highlights and follows. |
Design · 6 components: DeviceFrame, Swatches, TypeSpecimen, MarkUp, Annotate, ThemePreview
![]() DeviceFrame A screenshot inside a phone, tablet, Mac window or browser frame, drawn plainly with no brand artwork. | ![]() Swatches Colors with names and their WCAG contrast ratio against each background you choose, marked pass or fail for AA and AAA, normal and large text. | ![]() TypeSpecimen A font rendered at each size and weight with sample text, for checking a type scale. |
![]() MarkUp The person marks up an image: boxes, arrows and pins, each numbered with a note. | ![]() Annotate A screenshot with numbered callouts: boxes drawn over regions, or pins, given in percent. | ![]() ThemePreview A mock app screen drawn only from the theme you give it (primary, background, foreground, border, radius, font and more), following inputs live: bind ColorInput and Slider states to it to try a theme. |
Math · 2 components: Math, FunctionPlot
![]() Math TeX formulas typeset with KaTeX. | ![]() FunctionPlot Plots y = f(x) for one or more arrow functions over a domain, with a hover readout. |
Live · 2 components: LiveLog, LiveProgress
![]() LiveLog Tails a log file a running job keeps writing: ANSI colors, error lines marked, scrolls with new output and pauses when you scroll up. | ![]() LiveProgress Progress of a running job read from a JSON file it rewrites: {done, total, label?, eta?} or {tasks: [...]}. |
Learning · 4 components: Quiz, Flashcards, Walkthrough, Timer
![]() Quiz Questions one at a time with options; each answer shows right or wrong and why. | ![]() Flashcards A deck to practice: click or Space flips a card, then sort it into Know it or Still learning (keys K and L, or the arrows). | ![]() Walkthrough A guided explanation one step at a time: a title, markdown and optional code with highlighted lines. |
![]() Timer A countdown (give seconds) or a stopwatch with laps. |
Graphics · 2 components: Scene, Shader
![]() Scene A 3D scene in three.js the person can orbit (drag), zoom (scroll or pinch) and click. | ![]() Shader A Shadertoy-style fragment shader in WebGL2: write mainImage(out vec4 fragColor, in vec2 fragCoord) with iTime, iResolution, iMouse, iFrame, iTimeDelta, iDate, plus theme colors iPalette[0..3], iBackground, iForeground. |
API · 5 components: HttpExchange, Waterfall, ErDiagram, QueryPlan, SchemaDiff
![]() HttpExchange One HTTP request and its response: method, URL, status, time, size, headers (secrets masked) and the body as a JSON tree or text. | ![]() Waterfall Network requests on a timeline: each bar split into queue, DNS, connect, TLS, wait and download, with DOMContentLoaded and Load lines. | ![]() ErDiagram Tables, columns and relations drawn from schema text: SQL CREATE TABLE, a Prisma schema or a Convex schema.ts (format is detected). |
![]() QueryPlan A Postgres EXPLAIN (FORMAT JSON) plan as a collapsible tree: each node's own time (with ANALYZE) or cost as a bar, estimated against actual rows, and warnings for bad estimates, spills and big filtered scans. | ![]() SchemaDiff A database migration before it runs: each table with columns added, changed or dropped, every step marked destructive (loses data), risky (can fail, lock or break running code) or safe. |
Review · 3 components: CodeTour, DocReview, Redline
![]() CodeTour A guided walk across real files: each step highlights lines in a workspace file with a markdown note. | ![]() DocReview A spec, plan or doc (markdown) the person reads and comments on, block by block, then approves or asks for changes. | ![]() Redline A word-level redline of prose (docs, copy, specs): deletions struck, insertions marked inline. |
Terminal · 2 components: TerminalReplay, DevServers
![]() TerminalReplay Replays a recorded terminal session with its timing and real ANSI colors: play, pause, speed, scrub and markers. | ![]() DevServers Live list of running dev servers and their ports: status, port links, pid, CPU, memory, uptime and log. |
Whiteboard · 1 component: Whiteboard
![]() Whiteboard A freeform canvas of sticky notes, groups, text and arrows. |
Localization · 1 component: StringsTable
![]() StringsTable Localized strings side by side: a row per key, a column per locale, each cell flagged when missing, untranslated, marked for review, with placeholders that don't match the source, or likely to truncate. |
Files · 4 components: FileTree, AssetGallery, PdfViewer, FontPreview
![]() FileTree A real folder from the workspace or thread storage as a tree: file and folder sizes, git markers (A M D R U) from this checkout's changes, a filter and a Changed only toggle. | ![]() AssetGallery Every image, SVG and app icon under a folder, grouped by kind, each with its pixel size and file size on a checkerboard. | ![]() PdfViewer A PDF rendered page by page with pdf.js (pages draw as they scroll in): page count and jump, zoom, fit width. |
![]() FontPreview A font file (ttf, otf, woff, woff2) loaded as is: its names, format and metrics, editable sample text at any size, sliders for variable axes, and a grid of every glyph it maps. |
Compute · 1 component: Cells
![]() Cells A notebook-like stack of named cells run by Iris's sandboxed interpreter, each showing its value (number, text, list, table or component). |
Time · 3 components: TimeZones, Cron, DateDiff
![]() TimeZones A meeting planner across time zones: rows of people or places on a shared 24-hour scale, working hours shaded, and a slot the person drags (or moves with arrow keys) showing each local time. | ![]() Cron A cron schedule read as plain words, each field explained, and the next runs listed with how far away they are. | ![]() DateDiff Business days between two dates: working days, calendar days, weeks, weekends and the holidays skipped, over a day strip of the range. |
Release · 1 component: Release
![]() Release One card for a release: version, build, overall state, platforms, checks, store or TestFlight state, blockers and a timeline. |
Accessibility · 1 component: AccessibilityAudit
![]() AccessibilityAudit An axe-core or Lighthouse accessibility report: counts by impact, failing rules grouped by impact or WCAG criterion, each with its nodes (selector, HTML, how it fails) and help link. |
Geo · 1 component: GeoHeatmap
![]() GeoHeatmap A choropleth of countries or US states shaded by value in one accent hue, with a legend, hover readouts and a table view. |
Tracking · 2 components: Budget, Forecast
![]() Budget Spend against a limit: a meter per line, the total, a breakdown, the burn rate and when the limit is hit at it. | ![]() Forecast Done counts over time as an ETA with a band from recent rates, a burn-down or burn-up, scope changes and plain words. |
![]() Changes This thread's changed files, each opening its diff. |
![]() Threads The threads it started: running, waiting on you, done or failed. |
Cards read bb's own live data with load("bb:<source>"), scoped to the card's thread (its project and the threads it started), and draw it with any component. Inside <Card state="live"> it re-reads every 2 seconds; iris_check reads the real values, so the agent sees what the card will. The live project components, <Changes>, <Commits>, <PullRequest>, <Threads> and <Environments>, read bb themselves and update as soon as bb reports a change.
<let t={load("bb:thread")} />
<let kids={load("bb:threads?usage=1")} />
<Card title="This session" state="live">
<Gauge label="Context" value={t.context ? t.context.percent : 0} format="percent" />
<Table rows={kids} columns={["title", "status", {key: "waitingOnYou", label: "Needs you"}]} />
</Card>
<ContextMeter thread={load("bb:thread")} /> draws the context window split by category, with model and cost; in a live card it keeps the readings it sees as a history line, where compactions show as drops. Given load("bb:threads?usage=1") it totals context and cost by model.
Every bb: source
| Source | What it returns |
|---|---|
bb:thread |
This thread: title, status, waiting on you, queued messages, context window use (tokens, percent, model, categories), cost |
bb:threads |
Threads this thread started; ?scope=project for the project's top-level threads; ?usage=1 adds context and cost |
bb:usage |
Plan usage windows per provider: used percent, resets at, spend and limit |
bb:pr |
This branch's pull request: state, checks, review, mergeability |
bb:commits |
This branch's commits since its base, ahead and behind |
bb:changes |
Files changed on this branch, uncommitted included, with line counts |
bb:environments |
The project's worktrees with their threads, changed files and pull requests |
bb:machines |
bb machines and whether they're connected |
bb:plugins |
Installed plugins and their health |
bb:terminals |
This thread's terminals: title, status, exit code; ?output=1 adds the tail of what each prints, for a live <Logs> |
A live card keeps showing its last good values when a refresh fails, with a quiet "Last updated 14:02 · retrying" under it, and shows an error only for a source that never read fine. load(path, {fallback: value}) gives the card a value of its own to show instead of an error. Plugin data (plugin: sources) refreshes every 10 seconds rather than every 2, and cards on the page share one read of the same source.
Hover any card in a thread and press the pin button: the card stays in view while the chat scrolls on, still live. A thread keeps up to five pins, each marked with who pinned it and with its own Unpin, and pins follow the thread to your other devices. They show in four places:
- Above the composer, as a "2 pinned" chip. A click opens the pinned cards right there; on a phone this is the quickest way to them.
- On bb's home page, under Pinned cards: every open thread's pins, live, the most recently pinned first. A thread's title opens it.
- In the thread context card at the pane's upper right, when that is on in bb (Settings, Experiments, Thread context card). The chip hides itself there.
- In the thread's right panel (Info tab, under Pinned cards).
The thread's agent can pin too, for a status it keeps current:
iris_pinpins a card from its source, or from a file such as$BB_THREAD_STORAGE/status.iris. A file pin re-reads the file every 3 seconds, so the agent updates the card by rewriting the file. The card is checked first; one with errors isn't pinned. Pinning again with the sameidreplaces the pin in place.iris_unpinandiris_pinsremove and list the thread's pins.- From a shell:
bb iris pin <file> [--id fleet] [--title ...] [--once] [--alert-when ...] [--alert-message ...],bb iris unpin <id>|--allandbb iris pins [--json]. These act on the thread they run in only. - Any client can use the same pins through
bb plugin rpc call iris pins.list|pins.all|pins.set|pins.ack|pins.remove(listed bybb plugin rpc list iris).
A pin can carry a condition over its card's own names, for example iris_pin with alert_when: 'build.state == "failed"' on a card that loads build from a status file. Iris checks it every 30 seconds, reading the card's files and feeds as the card would. When it turns true:
- the pin shows the alert with Dismiss, and the chip above the composer reads "2 pinned · 1 alert";
- every open bb window shows a toast with Open (bb keeps it in its notification center);
- bb's desktop app shows a system notification when bb may already show them and its window isn't in front;
- the thread is marked unread, so it stands out in the sidebar on every device.
It asks again only after the condition clears and holds again. A source that is still loading or can't be read leaves the alert as it was. Alerts never message the thread or wake the agent.
Phone pushes aren't possible: bb gives plugins no notification API, and bb's push notifications fire only when an agent finishes a turn or asks a question. On the phone, an alert shows as the thread's unread mark and on the pin itself.
Every card's toolbar, and every pin, has Copy as image: the card is drawn to a PNG in your theme, without its toolbar, and put on the clipboard. Where a page can't write images to the clipboard (some browsers, the phone app), the PNG is saved instead: a download on a computer, the share sheet on a phone. Pages and apps (iris-app and iris-react blocks) run in a sandboxed frame the page can't read, so they don't copy as images.
An agent can keep a card in a file and show it inline: it writes the markup to $BB_THREAD_STORAGE/status.iris and puts ::iris{file="$BB_THREAD_STORAGE/status.iris"} on its own line in a reply. The card follows the file, read again every 3 seconds while it is on screen, so rewriting the file updates the card in place. A failed read keeps the last good card with a note. The same file can be pinned with iris_pin to keep it in view.
iris_cards (or bb iris cards [--json], or the cards.list RPC) lists the thread's cards, newest last, with what the person set in each: a slider's value, a pick, typed notes, stored values. An agent can act on a card's state without asking again.
iris_card_set sets those inputs instead: {card: "<id>", values: {seats: 20}} with the card's id from iris_cards (or its number), bb iris set <id> seats=20, or the cards.set RPC. Each value must be a <state> the card declares and keep its type; the card is checked with the new values first, and an open card redraws with them at once. The agent can show the person another scenario in the card they already have.
iris_check and bb iris check read the card's files, bb data and plugin feeds (plugin: sources, up to 5 seconds each) the way the card will, and warn when a required prop comes out empty, naming the fields the data does have (<Metric value> is empty: it has no field "ship"; its fields are ships, status).
They also warn when a Table or Gallery holds data a more specific component draws better, with the rewrite built from the card's own columns: before and after values point at Delta, checks with results at Acceptance, severity and file at Findings, colors at Swatches or Variants, screenshots captioned by device or theme at Screens, and before and after shots at Compare (<Table> reads as before and after (case, before, after): <Delta rows={rows.map((r) => ({ label: r.case, before: r.before, after: r.after }))} />). Warnings never block a card.
- Show as an Iris card. In a message's menu, this asks the agent to redo that answer as a card. Select part of a message first to ask about just that part. The composer has the same command.
- Open in the web playground. A card's toolbar opens it in the playground, ready to edit and share. The card's source travels after the
#in the link, so no server sees it. Files and bb data the card loads stay on your Mac, so they show as pending there. - Iris: open the playground in bb's command palette opens an empty editor.
Any bb plugin can feed live data to Iris cards without Iris knowing about it. Serve read-only JSON (or CSV) under the /iris/ prefix of your plugin's HTTP routes:
bb.http.route("GET", "/iris/status", (c) => Response.json(statusFor(c.req.query("thread"))), { auth: "local" });Cards then read it with load("plugin:<your-plugin-id>/status"). Check a feed the way a card reads it with bb iris feed check <your-plugin-id>/status: whether it answers, its content type, size and time, caching, and the fields a card can use. Iris fetches /api/v1/plugins/<id>/http/iris/status?thread=<threadId> from bb's own page, parses it by content type, and re-reads it every 2 seconds inside a <Card state="live">. Only the /iris/ prefix is ever read, answers are capped at 4 MB, and query keys pass through (plugin:notes/today?tag=work). Teach your plugin's agents to draw it with a line in your own instructions; Iris needs no change. Iris serves a sample at plugin:iris/example.
Important
Treat everything under /iris/ as readable by any card in any thread. The thread parameter is a hint for scoping, not access control, so publish only what any agent may show. Cards can't send it to a place of their choosing: they make no network requests of their own, and a web image or media file loads only when its full URL is written in the block. With webLookups on, a map's places and a photo's search words do go to OpenStreetMap, Photon and Wikimedia Commons through Iris's server, so don't feed private data into those. A card choosing among several written URLs can still signal which one it picked, so don't publish secrets.
Agents get these tools, and the same checks run from a shell. iris_examples(["Recipes"]) returns patterns that combine them: a status card that follows a file, a Variants picker, and setting a card's inputs.
Agent tools
| Tool | What it does |
|---|---|
iris_check |
Validates a block, reads its files, moves every input to its extremes, and returns an outline of what the person will see; an iris-react module is compiled and its imports checked |
iris_docs |
Every prop and an example for the named components |
iris_examples |
Working examples for whole component families, and recipes (["Recipes"]) |
iris_ask |
A waiting picker written in Iris markup, an iris-react app or HTML; the answer comes back as JSON |
iris_pin, iris_unpin, iris_pins |
Pin a card (or a file holding one) beside the conversation, unpin, list |
iris_cards, iris_card_set |
Read what the person set in the thread's cards, and set a card's inputs |
CLI
| Command | What it does |
|---|---|
bb iris check <file> |
Checks a block or iris-react app from a file and prints what the person will see |
bb iris components |
Lists every component with its one-line summary |
bb iris docs <Name> |
Every prop and an example for the named components |
bb iris try "<prompt>" |
Runs a prompt in a subthread that knows Iris |
bb iris pin <file>, bb iris unpin <id>, bb iris pins |
Pin, unpin and list this thread's pinned cards |
bb iris cards, bb iris set <n> name=value |
List this thread's cards with their inputs, and set a card's inputs |
bb iris feed check <plugin>/<name> |
Checks a plugin's Iris data feed the way a card reads it |
Iris reaches every top-level thread, whoever started it, and leaves threads that agents spawn under them alone unless you turn workers on.
Every setting
| Setting | Values | Default |
|---|---|---|
stance |
eager shows anything that can be shown; balanced keeps short answers as prose |
eager |
rollout |
everywhere (every top-level thread), or trial for threads started with bb iris try only |
everywhere |
workers |
Teach threads that agents spawn under a top-level thread too (about 3,600 tokens of instructions per session) | false |
skipPlugins |
Plugin ids, comma-separated, whose own top-level threads skip Iris | empty |
webLookups |
Maps, place search and photos through Iris's server (OpenStreetMap, Photon, Wikimedia Commons) | true |
Change one with bb plugin config iris set <key> <value>, then bb plugin reload iris.
- Expressions run in a tree-walking interpreter: no
eval, no globals, no prototype access, and a step budget per evaluation. Only functions Iris registers can be called. load(),<Snippet>and<Image>read only the thread's workspace and thread storage (and other threads' storage folders), through the plugin's server, up to 8 MB. Players, PDFs and fonts stream through a bb preview lease scoped to that same root, which bb checks on every request and which expires after an hour.- Cards make no network requests of their own. With
webLookupson (the default), Iris's server looks up map places and tiles (OpenStreetMap, Photon) and photos (Wikimedia Commons) for<Map>and<PhotoSearch>, so the place names and search words written in those cards reach those services. TurnwebLookupsoff to keep every card offline. ```iris-apppages and```iris-reactapps run in a sandboxed iframe with an opaque origin and a no-network content policy injected before any of their markup. Sending a message never happens from the page alone: Iris shows the exact text in a bar outside the frame, and only a click on its Send button sends it. A picker answer goes back at once only right after a click inside the page; any other answer waits in that bar. Insert, copy and open a link act only right after a click inside the page.
Note
Known limit: a sandboxed page can still navigate itself, so it could send away data it already holds. It never holds anything the agent didn't put in the block.
npm run eval runs 35 generic chat moments (status reports, mid-task progress updates, proof that something is done, PR and CI state, data to decide on, rich questions for the person, cards kept in view) through Claude Code headless with exactly the instructions and tools Iris gives agents in bb (iris_docs, iris_examples, iris_check, and stand-ins for iris_ask and iris_pin that log their calls, served by evals/mcp-server.ts). Each answer is scored on Iris being used (a block, a ::iris{file} card, or a picker or pin), every card checking clean, a fitting component and tool, and no markdown tables, plus a 1 to 5 judgment of fit. Every case runs twice and scores the mean, so one unlucky answer moves the suite less. npm run release:check runs the tests, the type check, the site build and the eval gate, which fails when the score drops more than 3 points below evals/baseline.json and names the cases that fell most.
Build, test and record from a checkout
npm install
npm test # engine, catalog, reports, prompt size
npx tsc --noEmit
node scripts/build-chunks.mjs # when a chunk changes; commit dist/chunks/
node scripts/brand.mjs # regenerates the icon and logos
bb plugin install . && bb plugin devDevelop against the one installed Iris: install the checkout once (bb plugin install .) and run bb plugin dev, which rebuilds and reloads on every save. Don't install extra copies under other ids.
The playground is the real gallery with bb's SDK swapped for a stand-in (site/). node scripts/build-site.mjs builds it into site-dist/; scripts/capture-site-css.mjs refreshes bb's CSS from a running bb, scripts/capture-atlas.mjs re-shoots the component pictures from the built site, npx tsx scripts/readme-atlas.ts rewrites the atlas above, node scripts/readme-icons.mjs redraws the feature icons, and scripts/record-demo.mjs with scripts/encode-demo.sh re-records the demo frame by frame from a separate dev bb.
Made for bb by Vedran Burojević. Open the playground.


































































































![VideoCompare: Two videos on one timeline, reference first: one play and scrub control, side by side or wipe, frame steps (, and .), speed, and an offset that aligns them ([ and ] shift a frame).](/vburojevic/bb-plugin-iris/raw/main/docs/atlas/dark/VideoCompare.webp)










![LiveProgress: Progress of a running job read from a JSON file it rewrites: {done, total, label?, eta?} or {tasks: [...]}.](/vburojevic/bb-plugin-iris/raw/main/docs/atlas/dark/LiveProgress.webp)





![Shader: A Shadertoy-style fragment shader in WebGL2: write mainImage(out vec4 fragColor, in vec2 fragCoord) with iTime, iResolution, iMouse, iFrame, iTimeDelta, iDate, plus theme colors iPalette[0..3], iBackground, iForeground.](/vburojevic/bb-plugin-iris/raw/main/docs/atlas/dark/Shader.webp)

























