Skip to content

Latest commit

 

History

506 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Crates.io Documentation License Crates.io Total Downloads

schemaui Web UI: schema navigation, form editor, and live JSON preview
Web UI — schema tree + form + live JSON preview

TUI — terminal demo (asciinema)
Sliders: stepped, fractional, marked
Sliders — integer & fractional steps, x-slider-marks
Range sliders with two handles
Range sliders — two handles, one shared track
Select, segmented control, radio group, switch
Choices — select, segmented, radio, switch
Custom color picker
Appearance — custom color picker via x-control: "color"

English | 中文文档

schemaui turns JSON Schema documents into interactive Web and TUI editors — and into a structured question UI for AI agents. The browser path ships as an embedded SPA; the terminal path is powered by ratatui, crossterm, and jsonschema; both share the same schema → form → validate pipeline.

The library parses rich schemas (nested sections, $ref, arrays, key/value maps, pattern properties…) into a navigable form tree, renders it as a browser form or a keyboard-first TUI, and validates after every edit so users always see the full list of issues before saving.

CLI available: schemaui-cli installs the schemaui binary. Prefer the CLI? Jump to CLI installation and usage.

AI Agent Integration

schemaui doubles as an interactive question UI for AI coding agents (Claude Code, Codex, zcode, …). Agent processes have no TTY, so a TUI would render nowhere and hang — instead the agent spawns a Web UI bound to 0.0.0.0, hands you a browser form with live validation, and reads your answers from a stable JSON file under .schemaui/answers/.

The agent-facing skill lives in its own repository: a2ui-ask — Interactive UI for AI agents: turn structured questions into browser forms. It ships the SKILL.md, prompt templates, cross-platform helper scripts (Python / Bash / PowerShell), and five runnable example forms.

Install it any of three ways:

# 1. one line, via skills.sh (recommended)
npx skills add YuniqueUnic/a2ui-ask

# 2. or just tell your agent:
#    "install the skill at https://github.com/YuniqueUnic/a2ui-ask"

# 3. or clone manually (global / per-project)
git clone https://github.com/YuniqueUnic/a2ui-ask.git ~/.claude/skills/a2ui-ask

5-minute smoke test from an a2ui-ask clone, with schemaui on your PATH (opens your browser, blocks until Save & Exit):

python3 scripts/ask.py \
  --schema examples/env-schema.json \
  --config examples/env-defaults.json \
  --title "Deployment Config"

The script prints SCHEMAUI_URL=… and SCHEMAUI_ANSWER=…, wakes your browser, and writes the answer to .schemaui/answers/<topic>-<timestamp>.json — an audit trail both the agent and you can re-read later. Full wiring guide: a2ui-ask README.

Surfaces at a Glance

Surface Who uses it How to start
Web UI Browser editing, AI agents, any device on the LAN schemaui web -s schema.json -c config.yaml
Library API Embed in your Rust app schemaui::web::session (Web) or SchemaUI::run (TUI)
TUI (default CLI mode) Terminal workflows, SSH, scripts schemaui -s schema.json -c config.yaml or schemaui tui …

The Web UI and TUI demos sit side-by-side in the header above: schema tree + form + live JSON preview in the browser, and the same pipeline running as a keyboard-first terminal UI (asciinema).

Feature Highlights

  • AI-agent ready – the a2ui-ask skill ships prompts, a SKILL.md, and helper scripts so coding agents ask structured questions through the Web UI and read answers from files (see AI Agent Integration).
  • Embedded Web UI – enabling the web feature bundles a browser UI and exposes helpers under schemaui::web::session so host applications can serve the experience without reimplementing the stack.
  • 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::input ingests JSON/YAML/TOML (feature-gated) while io::output can emit to stdout and/or multiple files in any enabled format.
  • Batteries-included CLI – schemaui-cli offers the same pipeline as the library, including multi-destination output, stdin/inline specs, and aggregated diagnostics.

Web UI Mode

Enable the web feature to ship a browser UI (static assets under web/dist/ are embedded in the crate). The same schema/config pipeline used by the TUI feeds a three-pane experience:

  1. Navigation – nested schema tree / breadcrumbs for multi-level objects
  2. Form editor – required flags, enums, numbers, arrays, live field errors
  3. JSON preview – pretty-printed document that updates as you edit

Screenshot: see the header above (docs/web.mix.png). Driving schemaui from an AI agent? Use AI Agent Integration — agent processes have no TTY, so the Web UI is the only surface that works for them.

CLI (fastest path)

schemaui-cli enables web by default. Same I/O flags as TUI; only host/port are Web-specific:

# random free port on 127.0.0.1; print final JSON to stdout
schemaui web \
  --schema ./schema.json \
  --config ./defaults.json \
  --host 127.0.0.1 --port 0 \
  -o -

# fixed port + write result to disk
schemaui web -s ./schema.json -c ./config.yaml -p 8787 -o ./out.json
Flag Default Meaning
--host / -l 127.0.0.1 Bind address (--bind / --listen aliases)
--port / -p 0 Bind port (0 = ephemeral free port)

Workflow in the browser: edit fields → Save keeps the session open → Save & Exit (or Exit) shuts down the temporary server and emits the value through configured -o destinations. Note that -o is greedy: put every other flag before it, and pass multiple destinations space-separated in one occurrence (-o ./out.json -) rather than repeating the flag.

Full CLI manual: docs/en/cli_usage.md.

Library (embed in your app)

Requires the web feature (cargo add schemaui --features web), which is why this example is ignored — doctests build against the default feature set.

use std::net::IpAddr;
use std::time::{Duration, Instant};

use schemaui::SessionOutcome;
use schemaui::web::session::{
    ServeOptions,
    WebSessionBuilder,
    bind_session,
};

async fn run() -> anyhow::Result<()> {
  let schema = serde_json::json!({
      "$schema": "http://json-schema.org/draft-07/schema#",
      "type": "object",
      "properties": {
          "host": {"type": "string", "default": "127.0.0.1"},
          "port": {"type": "integer", "default": 8080}
      },
      "required": ["host", "port"]
  });

  let config = WebSessionBuilder::new(schema)
      .with_title("Service Config")
      // Optional: close the session if nobody answers within 10 minutes.
      .with_deadline(Some(Instant::now() + Duration::from_secs(600)))
      .build()?;
  let addr = ServeOptions::new(IpAddr::from([127, 0, 0, 1]), 0).socket_addr();
  let session = bind_session(config, addr).await?;
  println!("visit http://{}/", session.local_addr());

  match session.run().await? {
      SessionOutcome::Completed(value) => {
          println!("final JSON: {}", serde_json::to_string_pretty(&value)?);
      }
      SessionOutcome::TimedOut => {
          eprintln!("session timed out; nothing was saved");
      }
  }
  Ok(())
}

bind_session / serve_session spawn an Axum router serving the versioned /api/v1/* contract (session bootstrap, validate, preview, save, exit — see Pluggable Frontends & Theming below) plus static assets, embedded by default. For custom HTTP stacks, reuse session_router / WebSessionBuilder instead of the turnkey helpers. The CLI schemaui web … command is a thin wrapper around these APIs.

Architecture notes: docs/en/web-ui-architecture-and-refactor-spec.md.

Pluggable Frontends & Theming

The Web session speaks a versioned, framework-agnostic HTTP contract at /api/v1/* — bootstrap (GET /session, GET /schema), POST /validate, POST /preview, POST /save, POST /exit, plus GET /theme.css when a theme is configured. The bundled React SPA is the reference client, not the only possible one:

# reskin the bundled UI by overriding its design tokens
schemaui web -s schema.json --web-theme examples/themes/midnight.css

# swap the frontend entirely for a directory containing your own index.html
schemaui web -s schema.json --frontend examples/frontend

examples/frontend/ is a framework-free, build-step-free index.html that drives a real session through /api/v1/* — proof the contract works for any stack, not just the bundled one. examples/themes/midnight.css is a worked --web-theme example; both are covered by contract tests (src/tests/web/theme_tests.rs).

During development, run a real session on a fixed port and point web/ui's Vite dev server at it instead of the embedded build — no CORS layer needed, vite.config.ts proxies /api/* to http://127.0.0.1:8787:

schemaui web -s schema.json -p 8787 &   # in one terminal
cd web/ui && pnpm dev                   # in another; edits hot-reload against it

Design record: docs/en/web-v1-theming-and-wasm.md.

WebAssembly, Playground & Edge API

schemaui-wasm compiles the same schema → UiAst → validate → render pipeline to WebAssembly, so it runs with no server at all — in a static site, inside a non-Rust host, or on an edge worker. Five exports mirror the HTTP contract byte-for-byte (a shared test fixture in src/tests/web/wasm_parity_tests.rs asserts they agree): buildUiAst, validate, render, schemaWithDefaults, schemaFromData, plus parseDocument for accepting JSON/YAML/TOML input with no server to have already parsed it.

just build-wasm         # -> schemaui-wasm/pkg (wasm-bindgen "web" target)
just build-playground   # -> web/playground-dist (static, no server needed)

Playground. The same React SPA that ships with the CLI, pointed at a WasmBackend transport instead of an HTTP one — one frontend, two backends, so a gap in the contract shows up as something the Playground can't do rather than going unnoticed. Paste or drop a schema (web/ui/src/playground/), fill the generated form entirely client-side, and export the result — no network request leaves the tab. Deployed to GitHub Pages from .github/workflows/deploy-playground.yml on every push to main.

Edge API. edge/ is a stateless Cloudflare Worker over the same wasm artifact, for callers that want the contract over HTTP without a Rust dependency or a wasm loader — schema in, UiAst/validation/rendered document out, no session and no storage. Deploy with just deploy-edge (needs CLOUDFLARE_API_TOKEN / CLOUDFLARE_ACCOUNT_ID); .github/workflows/deploy-edge.yml does the same on push to main once those secrets are configured, and fails loudly rather than silently no-op-ing when they are not.

Stored, shareable hosted sessions are an explicit non-goal for now — see the "Rejected" section of the design record for why.

Design record: docs/en/decisions/0005-wasm-core-and-hosting.md.

Config Schema Auto-Detection

When you launch the CLI with --config and omit --schema, schemaui-cli now resolves the schema in this order:

  1. Explicit --schema
  2. A schema declaration embedded in the config document
  3. Fallback inference via schema_from_data_value

Supported declarations:

  • JSON: root $schema
  • TOML: #:schema ./schema.json
  • YAML: # yaml-language-server: $schema=...
  • YAML fallback: # @schema ...

Both local and remote schema references are supported. Relative local paths are resolved against the config file directory, while inline/stdin configs fall back to the current working directory. JSON $schema metadata is stripped from the in-memory defaults before validation/output so editor hints do not leak into the final config payload.

Remote http(s) schema loading exists only in schemaui-cli, and the CLI enables the remote-schema feature by default. Disable it if you want a local-only binary surface; the schemaui library crate does not enable any remote schema loading by default.

Feature defaults are intentionally split by audience:

  • schemaui-cli defaults to the convenient, batteries-included path: TUI + Web + remote schema loading.
  • schemaui defaults to tui + json, so library consumers keep JSON support without pulling in Web or remote/network-related surface area by default.
  • json, yaml, and toml are real code-level gates. Keep at least one of them enabled; disabling all three triggers a clear compile-time error.

References:

Quick Start

[dependencies]
schemaui = "0.16.2"
serde_json = "1"
use anyhow::Result;
use schemaui::prelude::*;
use serde_json::json;

fn main() -> Result<()> {
    let schema = json!({
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "Service Runtime",
        "type": "object",
        "properties": {
            "metadata": {
                "type": "object",
                "properties": {
                    "serviceName": {"type": "string"},
                    "environment": {
                        "type": "string",
                        "enum": ["dev", "staging", "prod"]
                    }
                },
                "required": ["serviceName"]
            },
            "runtime": {
                "type": "object",
                "properties": {
                    "http": {
                        "type": "object",
                        "properties": {
                            "host": {"type": "string", "default": "0.0.0.0"},
                            "port": {"type": "integer", "minimum": 1024, "maximum": 65535}
                        }
                    }
                }
            }
        },
        "required": ["metadata", "runtime"]
    });

    let config = serde_json::json!({
        "metadata": { "name": "demo-service" },
        "runtime": { "http": { "host": "127.0.0.1", "port": 8080 } }
    });

    let value = SchemaUI::new(config)
        .with_schema(schema)
        .with_title("SchemaUI Demo")
        .with_description("Edit an existing config against a validation schema")
        .run(FrontendOptions::Tui(UiOptions::default()))?;
    println!("{}", serde_json::to_string_pretty(&value)?);
    Ok(())
}

Public API surface

For library integrations, the main entry points are:

  • High-level runtime: SchemaUI, DocumentInput, FrontendOptions, ServeOptions, and UiOptions
  • TUI runtime: crate::tui::session::TuiFrontend for custom frontend injection via SchemaUI::run_with_frontend
  • TUI state: crate::tui::state::* (for example FormState, FormCommand, FormEngine, SectionState)
  • Schema backend: crate::ui_ast::build_ui_ast together with crate::tui::model::form_schema_from_ui_ast (builds FormSchema from the canonical UI AST)

Architecture Snapshot

┌─────────────┐   parse/merge    ┌───────────────┐   layout + typing      ┌───────────────┐
│ io::input   ├─────────────────▶│ schema        ├───────────────────────▶│ tui::state    │
└─────────────┘                  │ (loader /     │                        │ (FormState,   │
                                 │ resolver /    │                        │ sections,     │
┌─────────────┐   emit Value     │ build_form_   │   FormSchema           │ reducers)     │
│ io::output  ◀──────────────────┴────pipeline───┘                        └────────┬──────┘
└─────────────┘                                                      focus/edits│
                                                                                │
                                                                     ┌──────────▼──────────┐
                                                                     │ tui::app::runtime   │
                                                                     │ (InputRouter,       │
                                                                     │ overlays, status)   │
                                                                     └──────────┬──────────┘
                                                                                │ draw
                                                                     ┌──────────▼──────────┐
                                                                     │ tui::view::*        │
                                                                     │ (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_str converts JSON/YAML/TOML (via serde_json, serde_yaml, toml) into serde_json::Value. Feature flags (json, yaml, toml) keep dependencies lean, and the same gates also drive DocumentFormat parsing/probing at compile time.
  • schema_from_data_value/str infers schemas from live configs, injecting draft-07 metadata and defaults so UIs load pre-existing values.
  • schema_with_defaults merges canonical schemas with user data, propagating defaults through properties, patternProperties, additionalProperties, dependencies, dependentSchemas, arrays, and $ref targets without mutating the original tree.
  • io::output::OutputOptions encapsulates serialization format, pretty/compact toggle, and a vector of OutputDestination::{Stdout, File}. Multiple destinations are supported; conflicts are caught before emission.
  • OutputOptions::render turns the final serde_json::Value into JSON/YAML/TOML text, and OutputOptions::write sends that payload to stdout/files explicitly after SchemaUI::run* returns.

JSON Schema → TUI Mapping

build_ui_ast resolves the schema into the canonical UI AST, and form_schema_from_ui_ast maps that tree into FormSection/FieldSchema for the TUI runtime:

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_for compiles the complete schema once when SchemaUI::run begins.
  • Each edit dispatches FormCommand::FieldEdited. FormEngine rebuilds the current document via FormState::try_build_value, runs the validator, and feeds errors back into FieldState or 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.json lists every shortcut (context, combos, action). The app::keymap::keymap_source!() macro pulls this file into the binary, InputRouter uses it to classify KeyEvents, 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) and Ctrl+Tab / Ctrl+Shift+Tab (sections). Ordinary Tab/Shift+Tab walk 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 Enter opens a popup for enums/oneOf selectors; Ctrl+E pushes a full-screen overlay editor for composites, key/value pairs, and array items. Overlays expose collection shortcuts (Ctrl+N, Ctrl+D, Ctrl+←/→, Ctrl+↑/↓), Ctrl+S saves the active level without closing, and Esc / Ctrl+Q pops 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.

Generated shortcut reference

Default context

Shortcut Action Kind
Tab / Down Next field command
BackTab / Up Previous field command
Ctrl+Tab Next section command
Ctrl+Shift+Tab Previous section command
Ctrl+L Next root tab command
Ctrl+J Previous root tab command
Enter Open popup / apply selection command
Ctrl+E Open composite editor command
Ctrl+S Save & validate (overlays stay open) command
Ctrl+Q / Ctrl+C Quit (confirm if dirty) command
Esc Cancel / clear status (overlays: pop current level) command
Ctrl+? / Ctrl+H Show help and error summary command

Collection context

Shortcut Action Kind
Ctrl+E Open composite editor command
Ctrl+N Add entry command
Ctrl+D Remove entry command
Ctrl+Left Select previous entry command
Ctrl+Right Select next entry command
Ctrl+Up Move entry up command
Ctrl+Down Move entry down command
Ctrl+? / Ctrl+H Show help and error summary command

Overlay context

Shortcut Action Kind
Tab / Down Next field command
BackTab / Up Previous field command
Ctrl+N Add entry command
Ctrl+D Remove entry command
Ctrl+Left Select previous entry command
Ctrl+Right Select next entry command
Ctrl+Up Move entry up command
Ctrl+Down Move entry down command
Ctrl+S Save & validate (overlays stay open) command
Esc Cancel / clear status (overlays: pop current level) command
Ctrl+? / Ctrl+H Show help and error summary command

Popup context

Shortcut Action Kind
Esc Close popup command
Up Select previous popup option command
Down Select next popup option command
Space Toggle popup option command
Enter Apply popup selection command

Help context

Shortcut Action Kind
Esc / Ctrl+H / Ctrl+? Close help command
Tab Next error page command
BackTab Previous error page command
Up / k Scroll shortcuts up command
Down / j Scroll shortcuts down command
PageUp Page shortcuts up command
PageDown Page shortcuts down command
Home Jump shortcuts to top command
End Jump shortcuts to bottom command
h Scroll error text left command
l Scroll error text right command

Text field context

Shortcut Action Kind
Left Move cursor left local edit
Right Move cursor right local edit
Home Jump to line start local edit
End Jump to line end local edit
Backspace Delete previous character local edit
Delete Delete next character local edit
Ctrl+W Delete previous word local edit
Ctrl+Z Undo text edit local edit
Ctrl+Y Redo text edit local edit

Numeric field context

Shortcut Action Kind
Left Step value down local edit
Right Step value up local edit
Shift+Left Fast step value down local edit
Shift+Right Fast step value up local edit
Backspace Delete previous character local edit
Delete Delete next character local edit
Ctrl+Z Undo numeric edit local edit
Ctrl+Y Redo numeric edit local edit

Boolean field context

Shortcut Action Kind
Space / Left / Right Toggle boolean value local edit
Enter Open popup / apply selection command

Enum field context

Shortcut Action Kind
Up / Left Previous enum option local edit
Down / Right Next enum option local edit
Enter Open popup / apply selection command

Multi-select field context

Shortcut Action Kind
Enter Open popup / apply selection command

Composite field context

Shortcut Action Kind
Left Previous composite variant local edit
Right Next composite variant local edit
Enter Open popup / apply selection command

Array buffer field context

Shortcut Action Kind
Backspace Delete previous array buffer character local edit
Delete Clear array buffer local edit

Keymap system

Put every shortcut into keymap/default.keymap.json, so runtime logic, help overlays, and generated README shortcut references all consume a single source of truth.

  • Format – each JSON object declares an id, human-readable description, bilingual descriptionZh, contexts (any of "default", "collection", "overlay", "popup", "help", "text", "numeric", "boolean", "enum", "multiSelect", "composite", "arrayBuffer"), an action discriminated union, and a list of textual combos. For example:

    {
      "id": "list.move.up",
      "description": "Move entry up",
      "descriptionZh": "条目上移",
      "contexts": ["collection", "overlay"],
      "action": { "kind": "ListMove", "delta": -1 },
      "combos": ["Ctrl+Up"]
    }
  • Macro + parser – app::keymap::keymap_source!() include_str!s the JSON, once_cell::sync::Lazy parses it once at startup, and each combo is compiled into a KeyPattern (key code, required modifiers, pretty display string).

  • Integration – InputRouter::classify delegates to keymap::classify_key, which returns the KeyAction embedded in the JSON. keymap::help_text filters bindings by KeymapContext, concatenating snippets used by StatusLine and overlay instructions.

  • Generated docs – build.rs parses keymap/default.keymap.json and refreshes the shortcut blocks in README.md and README.ZH.md using explicit HTML markers, so normal Cargo builds keep the bilingual reference in sync with runtime behavior.

  • Extending – to add a shortcut, edit the JSON, choose the contexts that should expose the help text, and wire the resulting KeyAction inside KeyBindingMap if 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 ui_ast::build_ui_ast, tui::model::form_schema_from_ui_ast Produce FormSchema (roots/sections/fields) from the canonical UI AST.
Form state tui::state::{form_state, section, field} Track focus, pointers, dirty flags, coercions, and errors.
Commands & reducers tui::state::{actions, reducers}, tui::app::validation Define FormCommand, mutate state, and route validation results.
Runtime controller tui::app::{runtime, overlay, popup, status, keymap} Event loop, InputRouter dispatch, overlay lifecycle, help text, status updates.
Presentation tui::view and tui::view::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)

Install

The installed binary is always named schemaui, so the normal entry point is schemaui -c ./config.json.

Choose one of the supported channels:

Cargo (cargo install)

Build from crates.io with Cargo.

cargo install schemaui-cli

Cargo binstall

Fetch prebuilt GitHub release binaries through cargo-binstall.

cargo binstall schemaui-cli

Homebrew

Install from the repository tap on macOS or Linux.

brew install YuniqueUnic/schemaui/schemaui

Scoop

Install on Windows from the repository-hosted Scoop manifest.

scoop install https://raw.githubusercontent.com/YuniqueUnic/schemaui/main/packaging/scoop/schemaui-cli.json

Direct download

Download the matching archive from https://github.com/YuniqueUnic/schemaui/releases/latest, extract schemaui / schemaui.exe, and place it on your PATH.

winget manifests

Use the versioned manifests in packaging/winget with winget install --manifest <dir>, or submit them upstream to the community repository.

Modes (subcommands)

The installed binary is schemaui. If you omit a mode subcommand, it defaults to the TUI. Explicit modes:

Command Purpose
schemaui web Interactive browser editor (embedded HTTP server)
schemaui / schemaui tui Interactive terminal editor (default)
schemaui web-snapshot Precompute Web session snapshots JSON/TS (no UI)
schemaui tui-snapshot Precompute TUI FormSchema/layout artifacts (no UI)
schemaui completion <shell> Shell completions (completion feature)
# Web UI (see also Web UI Mode above)
schemaui web --schema ./schema.json --config ./defaults.yaml --port 0 -o -

# equivalent TUI launches
schemaui --schema ./schema.json --config ./defaults.yaml
schemaui tui --schema ./schema.json --config ./defaults.yaml

Typical TUI pipeline

schemaui \
  --schema ./schema.json \
  --config ./defaults.yaml \
  -o - ./config.toml ./config.json
┌────────┐  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)  │
                                                     └────────────┘

Shared I/O contract (TUI and Web)

  • Inputs – --schema / --config accept file paths, file:// or http(s) URLs (CLI remote-schema), inline payloads, or - for stdin (not both schema and config on stdin at once). Config-only runs infer a schema via schema_from_data_value (or embedded $schema / #:schema / YAML modeline).
  • Diagnostics – DiagnosticCollector gathers format issues, feature-flag mismatches, stdin conflicts, and existing output files before the UI runs.
  • Outputs – -o/--output takes one or more space-separated destinations in a single occurrence and may mix file paths with - for stdout (because the flag is greedy, place it after all other flags and do not repeat it). With no destination, the tool writes to stdout; use --temp-file <PATH> for an explicit fallback file. Extensions pick the format; conflicting extensions are rejected.
  • Common flags – --no-pretty (compact), --force / --yes (overwrite), --title / --description (forwarded to the UI).
  • Web-only flags – --host / -l (default 127.0.0.1), --port / -p (default 0 = free port).
  • Shell completion – schemaui completion <bash|zsh|fish|powershell> (requires the completion feature).

Deep dive: docs/en/cli_usage.md · Chinese: docs/zh/cli_usage.zh.md.

Key Dependencies

Crate Purpose
serde, serde_json, serde_yaml, toml Parsing and serializing schema/config data.
schemars Draft-07 schema representation used by the schema module.
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, clap_complete, anyhow (CLI) Argument parsing, shell completion, and ergonomic diagnostics.

Documentation Map

  • README.md – overview + architecture snapshot (source of truth).
  • README.ZH.md – Chinese overview kept in sync with this README.
  • a2ui-ask – the AI-agent skill: prompt templates, helper scripts, examples, and tests (see AI Agent Integration).
  • docs/en/structure_design.md – detailed schema/layout/runtime design with flow diagrams.
  • docs/zh/structure_design.md – Chinese mirror of the architecture guide.
  • docs/en/cli_usage.md – CLI-specific manual (inputs, outputs, piping, TUI/Web modes, samples).
  • docs/zh/cli_usage.zh.md – Chinese mirror of the CLI usage guide.
  • docs/en/control-hints.md – the x-control / x-slider-marks / x-visible-when presentation hints that choose a field's control from the schema.
  • docs/en/web-ui-architecture-and-refactor-spec.md – Web UI architecture.
  • docs/en/web-v1-theming-and-wasm.md – the Web Session API v1 contract, theming, pluggable frontends, and the WASM/Playground/Edge API plan.
  • docs/en/decisions/ – the individual ADRs behind that plan (contract versioning, theming, WASM/hosting).
  • docs/web.mix.png – Web UI screenshot (schema form + live JSON preview); web.mix.dark.png is the dark-mode variant used automatically by the README.
  • docs/web.controls.*.png – per-control-family screenshots (sliders, ranges, choices, appearance) captured from examples/controls-gallery.schema.json.

Development

  • Run cargo fmt && cargo test regularly; most modules embed their tests by include!ing files from tests/ 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

  1. https://github.com/rjsf-team/react-jsonschema-form
  2. https://ui-schema.bemit.codes/examples

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

at your option.

links

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Happy hacking!

Star History

Star History Chart

About

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

Topics

Resources

Stars

92 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages