14 input shapes · 10 output shapes · schema-first · single source of truth
v15.3
Principle
01
JSON Schema (via OpenAPI) is the single source of truth. Every Floom app is described by a spec; every input and output renderer is derived from that spec. We never invent parallel type unions.
Source
OpenAPI 3.1 + JSON Schema
Request: parameters[].schema and requestBody.content.*.schema. Response: responses.*.content.*.schema.
Discriminator
Pure functions
pickInputShape(schema) and pickOutputShape(schema). Deterministic. Never throws. Ships in packages/renderer.
type: string, maxLength > 200 or x-floom-multiline: true
text
type: string (catch-all)
number
type: number or type: integer
boolean
type: boolean
Output shapes
Shape
Schema fragment
(explicit)
x-floom-output-shape: <shape> — always wins
pdf
type: string, contentMediaType: application/pdf
image
type: string, contentMediaType: image/*
audio
type: string, contentMediaType: audio/*
table
type: array, items.type: object
markdown
type: string, contentMediaType: text/markdown
code
type: string, x-floom-language: <lang>
stream
x-floom-stream: true or Transfer-Encoding: chunked
text
type: string (catch-all)
object
type: object (catch-all)
error
state === "output-error" (any shape)
Input shapes · 14
03 · empty / filled / error
Every input component accepts { spec, value, onChange, label, error, disabled }. All three states must be designed — empty is the default, filled is the happy path, error is how users recover.
text
Single-line plain text. Default catch-all for type: string.
type: string
title: City
description: Any city name
empty
City
Any city name
filled
City *
error
City *
City is required
textarea
Multi-line text. Triggered by maxLength > 200 or x-floom-multiline.
Every Floom run moves through three states. The same component tree is mounted across all three; only the rendered children differ. No conditional remounts, so refinement / retry feel like the same conversation.
state · 01
input-available
Schema resolved, composer / form rendered, nothing has run yet. This is every app's landing state for signed-in users.
state · 02a
output-available
Run succeeded. data is set, schema is the response schema, renderer picks an OutputShape. Refinement composer stays visible for conversational apps.
state · 02b
output-error
Run failed. Error block with message + code + retry action. data is null. Input card stays filled so the user can iterate without re-typing.
Composition
06
<SchemaInput> per field. Composition rules are deterministic and based on field count + shape adjacency. No layout manager; these three patterns cover 95% of apps.
Vertical stack · default
City
Departure
Return
Two-column · wide viewport
From
To
Depart
Return
Inline · short + boolean
Count
3
Include metadata
Prompt mode vs form mode
07 · controlled by x-floom-shape
The same schema can render in two modes. prompt uses a textarea composer + Claude interprets free-text into fields (flyfast, openpaper). form uses <SchemaInput> directly with a Run button (html-to-pdf, resize-image). Default is auto: if the op has a single textarea-shaped field, treat as prompt; else form.
x-floom-shape: prompt flyfast
One big textarea. Claude extracts destination, dates, budget from prose.
Thread · last 2
· Lisbon to Tokyo mid-June under €500 · 1m ago
· Madrid to Bali in September · yesterday
x-floom-shape: form html-to-pdf
Schema form, Run button. No composer. Run-log history, not threads.