Velloo documentation
Markdown for LLMs ↗MCP: codegen
emit_code and emit_snippet return agent-consumed IR in the screen framework's native idiom; emit_theme writes the framework's theme artifacts and a DTCG tokens file.
Velloo is the design source; the agent is the bridge to code. emit_code and emit_snippet return a structured intermediate representation, JSX-shaped for React providers or native HTML for the HTML provider, meant for the agent to read and transform into the user’s app code, using the app’s conventions, routing, and providers. It is not paste-ready output: no import statements, no function wrapper, no prettier pass. emit_theme is the exception: direct, user-facing artifacts that need no agent translation. Workflow: Emit to code.
Emit is per screen or per snippet. Board layout (frames, positions, groups, flow arrows) is canvas metadata and is never part of the emitted code. To implement a flow, read the board with get_board and emit each step’s and state’s screen.
emit_code
Return agent-consumed IR for a screen, plus a full diagnostic pass over it.
| Arg | Type | Required | Description |
|---|---|---|---|
screenId |
string | yes | |
componentsAlias |
string | no | Overrides the components import prefix the IR is built against. Defaults to config.codegen.componentsAlias, else @/components/ui |
fold |
boolean | no | false writes every sibling out instead of folding look-alike runs into a .map. Default: fold |
classesUsed |
boolean | no | true also returns the flat list of classes. Default: omitted |
The result is two blocks: a JSON block describing the code, then the JSX as a text block of its own, verbatim, so there is no escaped string to undo before typing it. The JSON’s jsx field points at that block.
Returns:
| Field | Description |
|---|---|
screen |
{ id, name } |
jsx |
The JSX body: no imports, no wrapper. Sent as its own text block |
componentsUsed |
Library component identifiers referenced in the tree |
iconsUsed |
Icon names rendered as JSX tags (<ArrowRight />), imported from lucide-react |
snippetsUsed |
Each referenced snippet’s own IR (the emit_snippet shape), to materialize as a component or inline |
classesUsed |
Unique Tailwind classes in the emitted JSX. Only with classesUsed: true; every class is already in the code, on the element it styles |
componentsToInstall |
Components from a library that ships them as files (shadcn) which the app doesn’t have yet, as kebab names ready for npx shadcn@latest add |
packagesToImport |
For a library the app installs whole (MUI, antd, Chakra UI): the packages the JSX imports from |
helpersToMaterialize |
What the app supplies itself: Velloo composition helpers with runtime logic (such as Gradient, SVG, Image, Layer) and components whose manifest names no other source |
warnings |
Non-fatal caveats: things JSX couldn’t express faithfully, such as an unresolved icon name |
tailwindV3Compat |
Only when the host app is on Tailwind v3: [{ class, v3?, note }], the v4 classes to rename (or rework) while writing the file, covering the screen and its snippets |
diagnostics |
Only when there is something to fix: the full-screen diagnostics pass |
The JSX uses the screen framework’s native idiom:
- shadcn: library component identifiers with Tailwind classes
- MUI (and other native-framework libraries): components imported from the framework’s own module, styled with
sxobjects - no-framework, Tailwind channel: plain-HTML primitives with Tailwind classes
- no-framework, inline-style channel (a
none/nonefolder): Tailwind-free plain HTML with inlinestyleobjects
Lists are emitted as lists. A run of three or more look-alike siblings is emitted once, as a .map over the values that differ. The tree stores four stat cards as four subtrees; the code for them is one template:
{[
{ className: "bg-card", text: "Active users", text2: "8,420" },
{ className: "bg-card", text: "Revenue", text2: "$48.2k" },
{ className: "bg-destructive/10", text: "Churn", text2: "1.9%" },
].map((item, index) => (
<div key={index} className={`rounded-lg border p-5 ${item.className}`}>
<p className="text-sm text-muted-foreground">{item.text}</p>
<span className="text-3xl font-bold">{item.text2}</span>
</div>
))}
Each row holds only the values that differ, named after the attribute they came from, or text for a child. Class words the siblings share stay in the template, and nothing is folded where the result would not be shorter. Pass fold: false to write every sibling out.
Icons carry their size. An icon the design never sized is written size={16}: the canvas draws icons at 16px and lucide-react defaults to 24, so without it every such icon would come out larger in code than on the canvas.
A prop stored as null, as rightSection={open ? <Chevron /> : null} composes to, is left out of the emitted JSX.
Snippet instances emit as component references with the snippet’s PascalCase name, matching emit_snippet; an instance’s extra classes pass as a className prop. An instance carrying $overrides can’t be expressed by the shared component, so its body is inlined instead: args substituted, overrides and extra classes applied. Extension nodes emit under their own id with the extension’s declared importPath, and an extension wins over a library component of the same name, as it does on the canvas. An extension with an invalid id or import path is an error rather than a silent library fallback. A node carrying $emitAs emits as a bare <Name /> of the named host component instead of its design subtree.
Props from another library that reach a lowered plain-HTML element (Mantine’s fw on a Velloo Text) are dropped with a warning. Where the screen uses an app component whose bare name is also a Velloo component, a warning says the JSX is app code, not compose input: paste it back and the bare names mean Velloo’s components.
Errors: ScreenNotFound, UnknownComponent, SnippetNotFound.
HTML output
For the HTML provider, emit_code returns { screen, format: "html", html, hostRoutes, warnings }, plus classesUsed when asked for and diagnostics when present. html is semantic markup with native attributes and hx-* requests preserved; hostRoutes lists relative request and form-action routes the app must serve. Integrate the markup into the app’s templates and implement its behavior. It does not return the React jsx, component-install, or helper-materialization fields.
emit_snippet
Same idea, scoped to a single snippet.
| Arg | Type | Required | Description |
|---|---|---|---|
snippetId |
string | yes | |
componentsAlias |
string | no | Same default chain as emit_code |
Returns { id, componentName, params, jsx, componentsToInstall, helpersToMaterialize, warnings }: the PascalCase component name, the typed params (name, type, and default / optional where declared), and the JSX body with $param references emitted as {name}, plus tailwindV3Compat and diagnostics under the same conditions as emit_code.
For HTML snippets, returns { id, format: "html", html, params, classesUsed, hostRoutes, warnings } plus any diagnostics. Each param appears as a $name marker in the markup, with defaults reported in params rather than baked in. Boolean params show their true branch; the agent supplies the template’s conditional.
emit_theme
Generate theme artifacts for the folder’s default library from a theme. Dry-run by default; the output is direct: write it as returned.
| Arg | Type | Required | Description |
|---|---|---|---|
outputDir |
string | yes | Target app directory (relative paths resolve from the design folder root) |
cssPath |
string | no | Stylesheet location relative to outputDir; default app/globals.css for Tailwind or velloo-theme.css for inline-style targets |
themePath |
string | no | Native-framework targets: theme module location relative to outputDir; default depends on the framework (theme.ts for MUI) |
apply |
boolean | no | Write the files. Default false; dry-run returns per-file diffs |
cssOnly |
boolean | no | Tailwind targets: skip the Tailwind config (v4) or preset (v3) file |
theme |
string | no | Named theme to emit; default "default" |
format |
"framework" or "design-md" |
no | Default framework; design-md emits DESIGN.md and a dark companion when applicable |
tailwind |
3 or 4 |
no | Force the Tailwind major of the emitted artifacts. Default: detected from outputDir’s package.json, falling back to v4 |
cssPath and themePath must stay inside outputDir; an absolute path or one that escapes with .. is rejected.
With format: "design-md", returns document files, warnings, and notes instead of framework artifacts or tokens.json. It carries prose from the followed design-system document when available and skips writing that source document. See Export a DESIGN.md.
With the default framework format, per target:
- Tailwind v4 (shadcn and Tailwind-channel folders):
globals.cssatcssPath, with the token tree as CSS custom properties and an@themeblock, and the folder’s custom CSS appended; atypeset.cssstylesheet next to it, whichglobals.cssimports; andtailwind.config.tsinoutputDirunlesscssOnly. - Tailwind v3: never overwrites the app’s globals file. It writes
velloo-theme.cssin the same directory ascssPath(custom CSS appended), avelloo-typeset.cssbeside it, and, unlesscssOnly, a Tailwind preset inoutputDir:velloo.preset.tswhen the app has a TypeScript Tailwind config, otherwisevelloo.preset.cjs.notesspell out the@importlines and thepresetsregistration to add by hand. - HTML and inline-style folders:
velloo-theme.css(override withcssPath), containing the CSS variables used by inline styles and the folder’s custom CSS. Link it before the app’s stylesheets. - Native frameworks: a theme module at
themePathexportingtheme, built with the framework’s factory (createTheme(...)for MUI,extendTheme(...)for Chakra UI), plus adarkThemeexport when the theme defines dark colors.
Every target also writes a framework-neutral DTCG tokens.json in outputDir. When the folder’s screens use components from a library with a recipe, such as Mantine, emit_theme also writes that library’s native theme module, whatever the folder’s own adapter is.
Returns { files, warnings?, notes? }. Each file entry carries path, contents, a diff against what’s on disk, and whether it was applied. On Tailwind targets, warnings flag palette tokens skipped because they would shadow a semantic slot, and what tokens.json leaves out; native-framework targets return files only.