schemaui
schemaui turns JSON Schema documents into fully interactive terminal UIs
powered by ratatui, crossterm, and jsonschema.
The library parses rich schemas (nested sections, $ref, arrays, key/value
maps, pattern properties…) into a navigable form tree, renders it as a
keyboard-first editor, and validates the result after every edit so users always
see the full list of issues before saving.
Feature Highlights
- Schema fidelity – draft-07 compatible, including
$ref,definitions,patternProperties, enums, numeric ranges, and nested objects/arrays. - Sections & overlays – top-level properties become root tabs, nested objects are flattened into sections, and complex nodes (composites, key/value collections, array entries) open dedicated overlays with their own validators.
- Immediate validation – every keystroke can trigger
jsonschema::Validator, and all errors (field-scoped + global) are collected and displayed together. - Pluggable I/O –
io::inputingests JSON/YAML/TOML (feature-gated) whileio::outputcan emit to stdout and/or multiple files in any enabled format. - Batteries-included CLI –
schemaui-clioffers the same pipeline as the library, including multi-destination output, stdin/inline specs, and aggregated diagnostics. - Embedded Web UI – enabling the
webfeature bundles a browser UI and exposes helpers underschemaui::web::sessionso host applications can serve the experience without reimplementing the stack.
Quick Start
[]
= "0.3.1"
= "1"
use SchemaUI;
use json;
Architecture Snapshot
┌─────────────┐ parse/merge ┌───────────────┐ layout + typing ┌─────────────┐
│ io::input ├─────────────────▶│ schema::* ├────────────────────▶│ form::* │
└─────────────┘ │ (loader / │ │ (state, │
│ resolver / │ │ sections, │
┌─────────────┐ emit Value │ layout) │ FormState │ reducers) │
│ io::output ◀──────────────────┴───────────────┘ └────────┬────┘
└─────────────┘ focus/edits│
│
┌──────────▼────────┐
│ app::runtime │
│ (InputRouter, │
│ overlays, status) │
└──────────┬────────┘
│ draw
┌──────────▼────────┐
│ presentation::* │
│ (ratatui view) │
└───────────────────┘
This layout mirrors the actual modules under src/, making it easy to map any
code change to its architectural responsibility.
Input & Output Design
io::input::parse_document_strconverts JSON/YAML/TOML (viaserde_json,serde_yaml,toml) intoserde_json::Value. Feature flags (json,yaml,toml,all_formats) keep dependencies lean.schema_from_data_value/strinfers schemas from live configs, injecting draft-07 metadata and defaults so UIs load pre-existing values.schema_with_defaultsmerges canonical schemas with user data, propagating defaults throughproperties,patternProperties,additionalProperties,dependencies,dependentSchemas, arrays, and$reftargets without mutating the original tree.io::output::OutputOptionsencapsulates serialization format, pretty/compact toggle, and a vector ofOutputDestination::{Stdout, File}. Multiple destinations are supported; conflicts are caught before emission.SchemaUI::with_outputwires these options into the runtime so the finalserde_json::Valuecan be written automatically after the session ends.
Web UI Mode
The optional web feature bundles the files under web/dist/ directly into the
crate and exposes high-level helpers for hosting the browser UI. Basic usage:
use ;
# async
The helper spawns an Axum router that exposes /api/session, /api/save, and
/api/exit alongside the embedded static assets. Library users can either call
bind_session/serve_session for a turnkey flow or reuse
session_router/WebSessionBuilder to integrate the UI into an existing HTTP
stack. The official CLI (schemaui-cli web …) is merely a thin wrapper around
these APIs.
JSON Schema → TUI Mapping
schema::layout::build_form_schema walks the fully resolved schema and maps
each sub-tree to a FormSection/FieldSchema:
| Schema feature | Resulting control |
|---|---|
type: string, integer, number |
Inline text editors with numeric guards |
type: boolean |
Toggle/checkbox |
enum |
Popup selector (single or multi-select for array enums) |
| Arrays | Inline list summary + overlay editor per item |
patternProperties, propertyNames, additionalProperties |
Key/Value editor with schema-backed validation |
$ref, definitions |
Resolved before layout; treated like inline schemas |
oneOf / anyOf |
Variant chooser + overlay form, keeps inactive variants out of the final payload |
Root objects spawn tabs; nested objects become sections with breadcrumb titles.
Every field records its JSON pointer (for example /runtime/http/port) so focus
management and validation can map errors back precisely.
Validation Lifecycle
jsonschema::validator_forcompiles the complete schema once whenSchemaUI::runbegins.- Each edit dispatches
FormCommand::FieldEdited.FormEnginerebuilds the current document viaFormState::try_build_value, runs the validator, and feeds errors back intoFieldStateor the global status line. - Overlays (composite variants, key/value maps, list entries) spin up their own validators built from the sub-schema currently being edited. Nested overlays live on a stack, so each level validates in place before changes flow back to the parent form.
┌─────────────┐ parse schema ┌─────────────────┐ inflate state ┌────────────┐
│ SchemaUI::run├────────────▶│ domain::parse ├───────────────▶│ FormState │
└─────┬───────┘ │ (schema::layout)│ └─────┬──────┘
│ validator_for() └─────────────────┘ edits │
│ ┌──────▼─────────┐
└────────────────────────────────────────────────────── ▶│ app::runtime │
│ (status, input)│
└──────┬─────────┘
│ FormCommand
┌──────▼──────────┐
│ FormEngine │
│ + jsonschema │
└─────────────────┘
App is the sole owner of FormState; even overlay edits flow through
FormEngine so validation rules stay centralized.
TUI Building Blocks & Shortcuts
- Single source for shortcuts –
keymap/default.keymap.jsonlists every shortcut (context, combos, action). Theapp::keymap::keymap_source!()macro pulls this file into the binary,InputRouteruses it to classifyKeyEvents, and the runtime footer renders help text from the same data—keeping docs and behavior DRY. - Root tabs & sections – focus cycles with
Ctrl+J / Ctrl+L(roots) andCtrl+Tab / Ctrl+Shift+Tab(sections). OrdinaryTab/Shift+Tabwalk individual fields. - Fields – render labels, descriptions, and inline error messages. Enum/composite fields show the current selection; arrays summarize length and selected entry.
- Popups & overlays – pressing
Enteropens a popup for enums/oneOf selectors;Ctrl+Epushes a full-screen overlay editor for composites, key/value pairs, and array items. Overlays expose collection shortcuts (Ctrl+N,Ctrl+D,Ctrl+←/→,Ctrl+↑/↓),Ctrl+Ssaves the active level without closing, andEsc/Ctrl+Qpops a single overlay. - Status & help – the footer highlights dirty state, outstanding validation errors, and context-aware help text. When auto-validate is enabled, each edit updates these counters immediately.
| Context | Shortcut | Action |
|---|---|---|
| Navigation | Tab / Shift+Tab |
Move between fields |
Ctrl+Tab / Ctrl+Shift+Tab |
Switch sections | |
Ctrl+J / Ctrl+L |
Switch root tabs | |
| Selection | Enter |
Open popup / apply choice |
| Editing | Ctrl+E |
Launch composite editor |
| Status | Esc |
Clear status or close popup |
| Persistence | Ctrl+S |
Save + validate |
| Exit | Ctrl+Q / Ctrl+C |
Quit (requires confirmation if dirty) |
| Collections | Ctrl+N / Ctrl+D |
Add / remove entry |
Ctrl+←/→, Ctrl+↑/↓ |
Select / reorder entries | |
| Overlay | Ctrl+E (open), Ctrl+S (save in place), Esc / Ctrl+Q (pop), Ctrl+N/D/←/→/↑/↓ |
Manage nested overlays & list entries |
Keymap system
Put every shortcut into keymap/default.keymap.json, so runtime logic, help
overlays, and documentation all consume a single source of truth.
-
Format – each JSON object declares an
id, human-readabledescription,contexts(any of"default","collection","overlay"), anactiondiscriminated union, and a list of textualcombos. For example: -
Macro + parser –
app::keymap::keymap_source!()include_str!s the JSON,once_cell::sync::Lazyparses it once at startup, and each combo is compiled into aKeyPattern(key code, required modifiers, pretty display string). -
Integration –
InputRouter::classifydelegates tokeymap::classify_key, which returns theKeyActionembedded in the JSON.keymap::help_textfilters bindings byKeymapContext, concatenating snippets used byStatusLineand overlay instructions. -
Extending – to add a shortcut, edit the JSON, choose the contexts that should expose the help text, and wire the resulting
KeyActioninsideKeyBindingMapif a new semantic command is introduced.
Runtime Layers
| Layer | Module(s) | Responsibilities |
|---|---|---|
| Ingestion | io::input, schema::loader, schema::resolver |
Parse JSON/TOML/YAML, resolve $ref, and normalize metadata. |
| Layout typing | schema::layout |
Produce FormSchema (roots/sections/fields) from resolved schemas. |
| Form state | form::state, form::section, form::field |
Track focus, pointers, dirty flags, coercions, and errors. |
| Commands & reducers | form::actions, form::reducers, app::validation |
Define FormCommand, mutate state, and route validation results. |
| Runtime controller | app::runtime, app::overlay, app::popup, app::status, app::keymap |
Event loop, InputRouter dispatch, overlay lifecycle, help text, status updates. |
| Presentation | presentation::view, presentation::components::* |
Render tabs, field lists, popups, overlays, and footer via ratatui. |
Each module is kept under ~600 LOC (hard cap 800) to honor the KISS principle and make refactors manageable.
CLI (schemaui-cli)
# It will be installed to `~/.cargo/bin` and renamed to `schemaui`
# so you should use it like this: `schemaui -c xxx`
┌────────┐ clap args ┌──────────────┐ read stdin/files ┌─────────────┐
│ CLI ├─────────────▶│ InputSource ├─────────────────▶│ io::input │
└────┬───┘ └──────┬───────┘ └────┬────────┘
│ diagnostics │ schema/default Value │
┌────▼─────────┐ ┌──────▼──────┐ |
│Diagnostic │◀───────┤ FormatHint │ │
│Collector │ └──────┬──────┘ │
└────┬─────────┘ │ pass if clean │
│ │ │
┌────▼────────┐ build options └────────────┐ │
│Output logic ├────────────────────────────▶│ OutputOptions │
└────┬────────┘ └────────────┬─────┘
│ SchemaUI::new / with_* ┌───▼────────┐
└──────────────────────────────────────────────▶│ SchemaUI │
│ (library) │
└────────────┘
- Inputs –
--schema/--configaccept file paths, inline payloads, or-for stdin (but not both simultaneously). If only config is provided the CLI infers a schema viaschema_from_data_value. - Diagnostics –
DiagnosticCollectoraccumulates format issues, feature flag mismatches, stdin conflicts, and existing output files before execution. - Outputs –
-o/--outputis repeatable and may mix file paths with-for stdout. When no destination is set, the tool writes to/tmp/schemaui.jsonunless--no-temp-fileis passed. Extensions dictate formats; conflicting extensions are rejected. - Flags –
--no-prettytoggles compact output,--force/--yesallows overwriting files, and--titlewires through toSchemaUI::with_title.
Key Dependencies
| Crate | Purpose |
|---|---|
serde, serde_json, serde_yaml, toml |
Parsing and serializing schema/config data. |
schemars |
Draft-07 schema representation used by schema::layout. |
jsonschema |
Runtime validation for forms and overlays. |
ratatui |
Rendering widgets, layouts, overlays, and footer. |
crossterm |
Terminal events consumed by InputRouter. |
indexmap |
Order-preserving maps for schema traversal. |
once_cell |
Lazy parsing of the keymap JSON. |
clap, color-eyre (CLI) |
Argument parsing and ergonomic diagnostics. |
Documentation Map
README.md– overview + architecture snapshot.docs/en/structure_design.md– detailed schema/layout/runtime design with flow diagrams.docs/en/cli_usage.md– CLI-specific manual (inputs, outputs, piping, samples).
Development
- Run
cargo fmt && cargo testregularly; most modules embed their tests byinclude!ing files fromtests/so private APIs stay covered. - Keep modules below ~600 LOC (hard cap 800). Split helpers as soon as behavior grows to keep KISS intact.
- Prefer mature crates (
serde_*,schemars,jsonschema,ratatui,crossterm) over bespoke code unless the change is trivial. - Update
docs/*whenever pipelines, shortcuts, or CLI semantics evolve so user-facing documentation stays truthful.
References
Roadmap
- parse json schema at runtime and generate a TUI
- parse json schema at runtime and generate a Web UI
- parse json schema at compile time Then generate the code for TUI, expose necessary APIs for runtime.
- parse json schema at compile time Then generate the code for Web UI, expose necessary APIs for runtime.
- parse json schema at runtime and generate a Interactive CLI
- parse json schema at compile time Then generate the code for Interactive CLI, expose necessary APIs for runtime.
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Happy hacking!