stories
v0.1.3Component stories for any React project — builds CSF story files with the project's own Vite (monorepo-aware), indexes components and their states per git line, compares the working tree with a branch, a commit or a chat turn, renders deterministic screenshots and DOM/React trees through the browser worker, and injects an explorer and compare page into the console.
Install
iii trigger compose::add worker=stories@nextRun in your project with the iii engine running. Exact versions are immutable and digest-pinned.
- data_path: data/stories
keep_lines: 12
viewport:
dpr: 1
height: 768
width: 1024
watch: true
workspaces:
- name: workspace
path: .stories
Component stories for any React project, rendered in the iii console. The
worker finds CSF story files (*.stories.tsx and friends) in one or more
workspaces, builds them with the project's own Vite (its plugins,
aliases, CSS pipeline and React), and keeps an index per git line: the
working tree, its previous build, any ref or commit, or a chat turn's
before/after tree. Two lines compare into a list of components that changed
directly (the story file or a direct import), indirectly (a deeper input),
were added or removed. Through the browser worker it renders deterministic
screenshots and DOM/React trees of any component state, with args and
globals overrides, and diffs two versions of a state into a verdict a
reviewer can act on. A console page shows the explorer (files as on disk,
git status, impact chips, live preview, props) and the compare view (line A
versus line B, side by side).
No Storybook runtime is needed. Existing .stories.* files and
.storybook/preview.* keep working; storybook/test's fn() is shimmed and
records an actions log. play functions, MDX and addons are ignored.
Install
iii trigger compose::add worker=storiesRequirements on the host: git, Node 22+ with npm on PATH (the compiler
is materialized under data/stories/compiler and installed there once), and
the browser worker for screenshots and trees. Each project must have its
dependencies installed (react and react-dom 18+); a project with its own
vite is built with that version.
Configuration (stories)
| Field | Default | Meaning |
|---|---|---|
data_path |
data/stories |
Lines, content-addressed files, renders, history, the compiler. |
console_url |
unset | Console origin the browser worker navigates to for renders. Unset: the ADE's address from console::status. |
watch |
true |
Rebuild the working tree when a story input changes. |
keep_lines |
12 |
Built ref/turn lines kept per workspace before pruning. |
viewport |
1024×768 @1 |
Default render viewport. |
workspaces[] |
one at . |
{ name, path, projects?, stories?, ignore?, base? } |
A workspace is a repository. Projects inside it are discovered from the story
files (the nearest package.json), so client-app/ and admin-app/ in one
repo each build with their own config and React. Shared packages resolve to
their real paths, so a change in packages/ui impacts stories in every
project that imports it. base is the ref changes are classified against
(default HEAD).
Functions
| Function | What it does |
|---|---|
stories::workspaces / stories::lines |
Git state, projects, component count and built lines per workspace. |
stories::components::list |
Components of a line with their states; filters by project, group, tag, path, query and change kind against a base line. |
stories::components::get |
One component: states with args, argTypes and inferred controls, inputs with hops, change, related components, history. |
stories::tree::fs |
The explorer tree: folders and story files as on disk with git status and change kinds. |
stories::builds::create / stories::builds::get |
Build a line (worktree, a ref, a sha, turn:) and poll it. |
stories::compare |
Every component that differs between two lines, classified, with the changed files. |
stories::diff::file |
Unified diff of one input between two lines. |
stories::screenshot |
Deterministic PNG of one state (args, globals, viewport, clip), cached by content. |
stories::tree |
DOM tree with layout boxes, computed styles and owning component; React tree; accessibility outline; html; actions. |
stories::diff |
One state across two lines: tree changes, pixel regions mapped to elements, verdict identical / invisible / visual / unexplained. |
stories::history |
Versions of one component seen by working-tree builds. |
Triggers: stories:changed fires after a working-tree rebuild with the
classified changes ({ workspace?, project?, kinds? }); stories:build
fires on every build state transition.
The review workflow for agents lives in skills/.
Lines and data
data/stories/<workspace>/
lines/worktree/ the live working tree (rebuilt by the watcher)
lines/worktree.prev/ the build before it
lines/<sha>/ a commit or a turn tree, immutable
files/<sha256> build outputs and story inputs, shared by all lines
renders/<hash>/ shot.png, tree.json, meta.json of one render
history/<project>__<id>.jsonlA ref builds in a detached git worktree; a turn tree comes from the ide
worker's private snapshot repository through git archive. node_modules
folders are linked from the live checkout. A component's version is the
hash of every input's content, so a component unchanged between two lines
shares the same render cache.
Story documents are served to the console through stories::ui-file behind
the console's /ui-files/stories/ route; the page
embeds them in a same-origin iframe.
Development
# terminal 1: rebuild the console page on save
pnpm --dir stories/ui watch
# terminal 2: hot reload the page assets
cd stories && III_STORIES_UI_WATCH=1 cargo runIII_STORIES_NODE / III_STORIES_NPM override the binaries;
III_STORIES_IDE_TURNS points at the ide worker's turn store (default
data/shell/turns). Tests: cargo test; the pipeline test runs against a
real project when III_STORIES_FIXTURE= and
III_STORIES_COMPILER_DIR= are set.
Limits
React 18+ only. Projects built by webpack or Next.js need a vite.config
with the mocks they rely on (next/link, next/image). Stories that reach
application singletons (routers, engine clients) render with errors and show
up with an error field. Pixel diffs compare renders from one machine; a
different font stack between hosts reads as unexplained.