fusor-rs/hypercmd

Write reactive CLI applications in plain HTML.

Rust

3

18 commits

updated Oct 5, 2026

See the code

See what people are saying

README

hypercmd

Build terminal apps with HTML and Rust

Write your terminal UI in HTML files. Keep state and logic in Rust. No widget trees built by hand, and no markup inside Rust macros.

hypercmd is a terminal renderer for fusor. fusor compiles your HTML and Rust; hypercmd lays the result out in terminal cells, handles the keyboard and keeps the screen in sync. When a signal changes, the bindings that read it run again and the next frame shows the result.

In development · v0.1: this is experimental software. APIs will change; don't rely on it for production yet. See status and known limitations.

Get started · How it works · Examples · Documentation · Status · Contributing

The file browser example running in a terminal

A terminal app in two files

The view is an HTML file. Bindings and event handlers are Rust expressions:

<!-- ui/app.html -->
<App state="{{ Counter::new() }}">
  <main>
    <h1>Counter</h1>
    <p>Count: {{ state.count.get() }}</p>
    <div class="actions">
      <button on:click="state.increment()">Increment</button>
      <button on:click="state.count.set(0)">Reset</button>
    </div>
  </main>
</App>

The Rust module owns the state, includes the HTML with template! and runs the app:

// src/main.rs
use fusor::{Signal, signal};

struct Counter {
    count: Signal<i32>,
}

impl Counter {
    fn new() -> Self {
        Self { count: signal(0) }
    }

    fn increment(&self) {
        self.count.update(|n| *n += 1);
    }
}

fusor::template!(backend = "hypercmd", "ui/app.html");

fn main() -> Result<(), hypercmd::Error> {
    hypercmd::native::run(hypercmd_app()?)
}

<App> makes the struct's fields and methods available as state and generates hypercmd_app(). Tab moves focus between the buttons; Enter or Space presses one. The paragraph reads the count signal, so its text updates when the count changes. The compiler checks the expressions in the HTML along with the rest of the module. A small stylesheet lays the buttons out in a row:

/* ui/terminal.css */
main { padding: 1ch; gap: 1ch; }
.actions { flex-direction: row; gap: 1ch; }

Get started

hypercmd runs on macOS and Linux. To build apps you need Rust 1.85 or newer, installed with rustup. Then install the CLI:

curl -fsSL https://raw.githubusercontent.com/fusor-rs/hypercmd/main/install.sh | sh

The installer downloads the latest release, verifies its checksum, and puts hypercmd in .hypercmd/bin under your home directory; it prints the line to add to your PATH. The script is install.sh if you want to read it first. With Rust installed, cargo install hypercmd-cli works too.

Run hypercmd upgrade to upgrade the CLI. Interactive commands warn when a newer release is available, checking at most once a day. See CLI upgrades.

Create and run an app:

hypercmd new my-app
cd my-app
hypercmd run

hypercmd new writes the counter above and checks that it builds. Edit ui/app.html for the screen and src/main.rs for its state, then hypercmd run again. See the CLI reference for commands and options.

Author an application

hypercmd new sets up an ordinary Cargo package. To add hypercmd to an existing one, depend on the runtime and the build helper, and declare the package's templates and styles:

[dependencies]
hypercmd = "=0.1.2"
fusor = { package = "fusor-core", version = "=0.1.5", default-features = false }
fusor-components = { version = "=0.1.5", default-features = false }

[build-dependencies]
hypercmd-build = "=0.1.2"

[package.metadata.hypercmd]
entry = "ui/app.html"        # the <App> template
templates = ["ui"]           # directories of component templates
styles = ["ui/terminal.css"]

Compile the templates from build.rs:

fn main() -> Result<(), Box<dyn std::error::Error>> {
    hypercmd_build::compile_app()
}

Reusable components can also be mounted as a root with hypercmd::mount::<Component>(inputs).

To run the examples from a checkout, use just: just demo opens the file browser in the current folder.

How it works

  • HTML stays HTML. Components use native markup with Rust expressions for text, attributes, conditions, lists, routes and events. fusor's compiler lowers them; rustc checks them.
  • Updates follow signal reads. A binding tracks the signals it reads and updates its own node. hypercmd lays out and paints a frame only when the scene changed.
  • CSS for the terminal. A bounded stylesheet profile: flexbox layout, ch and percentage sizes, padding, gaps, colors, emphasis and scrollable overflow. Unsupported markup or CSS fails at build time instead of rendering differently.
  • Keyboard first. Focus order, Tab and arrow navigation, multiline grapheme-aware editing, bracketed paste, mouse scrolling and visible focus come built in.
  • Ownership handles cleanup. Removing a view disposes its listeners, stops its tasks and timers, and asks its background workers to cancel. The terminal is restored on exit, on panic and around Ctrl+Z.
  • One component, two renderers. A library can compile the same HTML and Rust for the terminal and for fusor's browser renderer.

Examples

  • File browser: folders and text previews, filtering, routing between screens and filesystem reads in background workers.
  • Job inspector: routing, async views, timers and a reusable row component.
  • Shared job controls: one component used unchanged by a terminal app and a browser app.

Documentation

The Markdown guides also power a site through docs-base. The landing page and docs share a browser workspace. Run just site, then just preview to serve both on port 4187. See the site guide.

GuideCovers
CLI referenceInstallation, creating apps, checks, running and building
HTML and CSS referenceSupported HTML, CSS, layout, scrolling and Unicode policy
ControlsKeyboard, focus, buttons, text and checkbox bindings
Async viewsAsync/Await, coherent publication and cancellation
RoutingScreens, nested views and in-memory history
ServicesOwner-scoped tasks, timers and background workers
Native executionTerminal session, signals and resource limits
ConsumptionPackaging, shared components and browser reuse

Status and known limitations

hypercmd is v0.1 and experimental. Until 1.0, each minor release may change the HTML profile and the Rust APIs.

Platform status

Platform or capabilityStatus
macOS nativeAutomated OS PTY coverage
Linux nativeCI gate
Windows nativeExecution unavailable
Browser component reuseChromium acceptance gate
Non-browser Wasm hostUnimplemented
AccessibilityKeyboard operation and visible focus; screen-reader testing pending
Terminal emulatorsManual compatibility testing pending

Not supported yet:

  • Margins, positioning, grids, merged table cells, media and SVG. See the terminal profile for the exact subset.
  • Nested <Async> boundaries, and editable controls or router outlets inside an async view.
  • East Asian ambiguous-width characters as two cells; they measure as one.

How is this different from Ratatui?

Ratatui is an immediate-mode library: your code draws widgets into a buffer every frame and handles events itself. hypercmd retains a scene built from HTML templates, updates it through signals, and owns layout, focus and input. It uses Ratatui's buffer and styles for terminal output.

How is this different from Ink and Textual?

Ink renders React components in the terminal with flexbox layout, in JavaScript. Textual is a Python framework with widgets styled by its own CSS dialect. hypercmd shares their ideas of flexbox and CSS in the terminal, but components are HTML templates backed by Rust structs, compiled and type-checked ahead of time, with fine-grained signals updating individual nodes.

Contributing

hypercmd is an open source project in early development. The contributing guide covers the setup, checks and workflow. The runtime and build helper live in crates/; runnable applications live in examples/.

hypercmd is built with the help of AI tools, mainly Claude Code, under the same rules the project asks of contributors: see Using AI tools.

License

Licensed under the MIT License.

cli
cli-app
html
rust

fusor-rs/hypercmd

Write reactive CLI applications in plain HTML.

Rust

3

18 commits

updated Oct 5, 2026

See the code

See what people are saying

README

hypercmd

Build terminal apps with HTML and Rust

Write your terminal UI in HTML files. Keep state and logic in Rust. No widget trees built by hand, and no markup inside Rust macros.

hypercmd is a terminal renderer for fusor. fusor compiles your HTML and Rust; hypercmd lays the result out in terminal cells, handles the keyboard and keeps the screen in sync. When a signal changes, the bindings that read it run again and the next frame shows the result.

In development · v0.1: this is experimental software. APIs will change; don't rely on it for production yet. See status and known limitations.

Get started · How it works · Examples · Documentation · Status · Contributing

The file browser example running in a terminal

A terminal app in two files

The view is an HTML file. Bindings and event handlers are Rust expressions:

<!-- ui/app.html -->
<App state="{{ Counter::new() }}">
  <main>
    <h1>Counter</h1>
    <p>Count: {{ state.count.get() }}</p>
    <div class="actions">
      <button on:click="state.increment()">Increment</button>
      <button on:click="state.count.set(0)">Reset</button>
    </div>
  </main>
</App>

The Rust module owns the state, includes the HTML with template! and runs the app:

// src/main.rs
use fusor::{Signal, signal};

struct Counter {
    count: Signal<i32>,
}

impl Counter {
    fn new() -> Self {
        Self { count: signal(0) }
    }

    fn increment(&self) {
        self.count.update(|n| *n += 1);
    }
}

fusor::template!(backend = "hypercmd", "ui/app.html");

fn main() -> Result<(), hypercmd::Error> {
    hypercmd::native::run(hypercmd_app()?)
}

<App> makes the struct's fields and methods available as state and generates hypercmd_app(). Tab moves focus between the buttons; Enter or Space presses one. The paragraph reads the count signal, so its text updates when the count changes. The compiler checks the expressions in the HTML along with the rest of the module. A small stylesheet lays the buttons out in a row:

/* ui/terminal.css */
main { padding: 1ch; gap: 1ch; }
.actions { flex-direction: row; gap: 1ch; }

Get started

hypercmd runs on macOS and Linux. To build apps you need Rust 1.85 or newer, installed with rustup. Then install the CLI:

curl -fsSL https://raw.githubusercontent.com/fusor-rs/hypercmd/main/install.sh | sh

The installer downloads the latest release, verifies its checksum, and puts hypercmd in .hypercmd/bin under your home directory; it prints the line to add to your PATH. The script is install.sh if you want to read it first. With Rust installed, cargo install hypercmd-cli works too.

Run hypercmd upgrade to upgrade the CLI. Interactive commands warn when a newer release is available, checking at most once a day. See CLI upgrades.

Create and run an app:

hypercmd new my-app
cd my-app
hypercmd run

hypercmd new writes the counter above and checks that it builds. Edit ui/app.html for the screen and src/main.rs for its state, then hypercmd run again. See the CLI reference for commands and options.

Author an application

hypercmd new sets up an ordinary Cargo package. To add hypercmd to an existing one, depend on the runtime and the build helper, and declare the package's templates and styles:

[dependencies]
hypercmd = "=0.1.2"
fusor = { package = "fusor-core", version = "=0.1.5", default-features = false }
fusor-components = { version = "=0.1.5", default-features = false }

[build-dependencies]
hypercmd-build = "=0.1.2"

[package.metadata.hypercmd]
entry = "ui/app.html"        # the <App> template
templates = ["ui"]           # directories of component templates
styles = ["ui/terminal.css"]

Compile the templates from build.rs:

fn main() -> Result<(), Box<dyn std::error::Error>> {
    hypercmd_build::compile_app()
}

Reusable components can also be mounted as a root with hypercmd::mount::<Component>(inputs).

To run the examples from a checkout, use just: just demo opens the file browser in the current folder.

How it works

  • HTML stays HTML. Components use native markup with Rust expressions for text, attributes, conditions, lists, routes and events. fusor's compiler lowers them; rustc checks them.
  • Updates follow signal reads. A binding tracks the signals it reads and updates its own node. hypercmd lays out and paints a frame only when the scene changed.
  • CSS for the terminal. A bounded stylesheet profile: flexbox layout, ch and percentage sizes, padding, gaps, colors, emphasis and scrollable overflow. Unsupported markup or CSS fails at build time instead of rendering differently.
  • Keyboard first. Focus order, Tab and arrow navigation, multiline grapheme-aware editing, bracketed paste, mouse scrolling and visible focus come built in.
  • Ownership handles cleanup. Removing a view disposes its listeners, stops its tasks and timers, and asks its background workers to cancel. The terminal is restored on exit, on panic and around Ctrl+Z.
  • One component, two renderers. A library can compile the same HTML and Rust for the terminal and for fusor's browser renderer.

Examples

  • File browser: folders and text previews, filtering, routing between screens and filesystem reads in background workers.
  • Job inspector: routing, async views, timers and a reusable row component.
  • Shared job controls: one component used unchanged by a terminal app and a browser app.

Documentation

The Markdown guides also power a site through docs-base. The landing page and docs share a browser workspace. Run just site, then just preview to serve both on port 4187. See the site guide.

GuideCovers
CLI referenceInstallation, creating apps, checks, running and building
HTML and CSS referenceSupported HTML, CSS, layout, scrolling and Unicode policy
ControlsKeyboard, focus, buttons, text and checkbox bindings
Async viewsAsync/Await, coherent publication and cancellation
RoutingScreens, nested views and in-memory history
ServicesOwner-scoped tasks, timers and background workers
Native executionTerminal session, signals and resource limits
ConsumptionPackaging, shared components and browser reuse

Status and known limitations

hypercmd is v0.1 and experimental. Until 1.0, each minor release may change the HTML profile and the Rust APIs.

Platform status

Platform or capabilityStatus
macOS nativeAutomated OS PTY coverage
Linux nativeCI gate
Windows nativeExecution unavailable
Browser component reuseChromium acceptance gate
Non-browser Wasm hostUnimplemented
AccessibilityKeyboard operation and visible focus; screen-reader testing pending
Terminal emulatorsManual compatibility testing pending

Not supported yet:

  • Margins, positioning, grids, merged table cells, media and SVG. See the terminal profile for the exact subset.
  • Nested <Async> boundaries, and editable controls or router outlets inside an async view.
  • East Asian ambiguous-width characters as two cells; they measure as one.

How is this different from Ratatui?

Ratatui is an immediate-mode library: your code draws widgets into a buffer every frame and handles events itself. hypercmd retains a scene built from HTML templates, updates it through signals, and owns layout, focus and input. It uses Ratatui's buffer and styles for terminal output.

How is this different from Ink and Textual?

Ink renders React components in the terminal with flexbox layout, in JavaScript. Textual is a Python framework with widgets styled by its own CSS dialect. hypercmd shares their ideas of flexbox and CSS in the terminal, but components are HTML templates backed by Rust structs, compiled and type-checked ahead of time, with fine-grained signals updating individual nodes.

Contributing

hypercmd is an open source project in early development. The contributing guide covers the setup, checks and workflow. The runtime and build helper live in crates/; runnable applications live in examples/.

hypercmd is built with the help of AI tools, mainly Claude Code, under the same rules the project asks of contributors: see Using AI tools.

License

Licensed under the MIT License.

cli
cli-app
html
rust

Languages

Rust

83.2%

JavaScript

8.0%

HTML

4.6%

CSS

2.6%