-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #56 from DioxusLabs/jk/add-tui
Move native-core and tui out of dioxuslabs/dioxus and into this crate
- Loading branch information
Showing
84 changed files
with
16,772 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,58 @@ | ||
[workspace] | ||
members = ["packages/blitz", "packages/blitz-core"] | ||
members = [ | ||
"packages/blitz", | ||
"packages/blitz-core", | ||
"packages/dioxus-tui", | ||
"packages/native-core", | ||
"packages/native-core-macro", | ||
"packages/plasmo", | ||
] | ||
|
||
[workspace.package] | ||
version = "0.5.0-alpha.0" | ||
|
||
[workspace.dependencies] | ||
blitz = { path = "packages/blitz", version = "0.5.0-alpha.0" } | ||
blitz-core = { path = "packages/blitz-core", version = "0.5.0-alpha.0" } | ||
dioxus-tui = { path = "packages/dioxus-tui", version = "0.5.0-alpha.0" } | ||
dioxus-native-core = { path = "packages/native-core", version = "0.5.0-alpha.0" } | ||
dioxus-native-core-macro = { path = "packages/native-core-macro", version = "0.5.0-alpha.0" } | ||
plasmo = { path = "packages/plasmo", version = "0.5.0-alpha.0" } | ||
|
||
dioxus = { version = "0.5.0-alpha.0" } | ||
dioxus-core = { version = "0.5.0-alpha.0" } | ||
dioxus-hot-reload = { version = "0.5.0-alpha.0" } | ||
dioxus-html = { version = "0.5.0-alpha.0" } | ||
|
||
tracing = "0.1.37" | ||
tracing-futures = "0.2.5" | ||
toml = "0.8" | ||
tokio = "1.28" | ||
slab = "0.4.2" | ||
futures-channel = "0.3.21" | ||
futures-util = { version = "0.3", default-features = false } | ||
rustc-hash = "1.1.0" | ||
wasm-bindgen = "0.2.92" | ||
html_parser = "0.7.0" | ||
thiserror = "1.0.40" | ||
prettyplease = { package = "prettier-please", version = "0.2", features = [ | ||
"verbatim", | ||
] } | ||
manganis-cli-support = { version = "0.2.1", features = ["html"] } | ||
manganis = { version = "0.2.1" } | ||
|
||
lru = "0.12.2" | ||
async-trait = "0.1.77" | ||
axum = "0.7.0" | ||
axum-server = { version = "0.6.0", default-features = false } | ||
tower = "0.4.13" | ||
http = "1.0.0" | ||
tower-http = "0.5.1" | ||
hyper = "1.0.0" | ||
hyper-rustls = "0.26.0" | ||
serde_json = "1.0.61" | ||
serde = "1.0.61" | ||
axum_session = "0.12.1" | ||
axum_session_auth = "0.12.1" | ||
axum-extra = "0.9.2" | ||
reqwest = "0.11.24" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/target | ||
Cargo.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
esque | ||
Tui |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
[package] | ||
name = "dioxus-tui" | ||
version = { workspace = true } | ||
authors = ["Jonathan Kelley, Evan Almloff"] | ||
edition = "2021" | ||
description = "TUI-based renderer for Dioxus" | ||
repository = "https://github.com/DioxusLabs/dioxus/" | ||
homepage = "https://dioxuslabs.com/learn/0.4/getting_started/tui" | ||
keywords = ["dom", "ui", "gui", "react", "terminal"] | ||
license = "MIT OR Apache-2.0" | ||
|
||
[dependencies] | ||
dioxus-core = { workspace = true, features = ["serialize"] } | ||
dioxus-html = { workspace = true } | ||
dioxus-native-core = { workspace = true, features = ["dioxus"] } | ||
dioxus-native-core-macro = { workspace = true } | ||
dioxus-hot-reload = { workspace = true, optional = true } | ||
plasmo = { workspace = true } | ||
|
||
crossterm = "0.26.0" | ||
tokio = { workspace = true, features = ["full"] } | ||
futures = "0.3.19" | ||
taffy = "0.3.12" | ||
|
||
[dev-dependencies] | ||
dioxus = { workspace = true } | ||
tokio = { version = "1" } | ||
criterion = "0.3.5" | ||
|
||
[[bench]] | ||
name = "update" | ||
harness = false | ||
|
||
[features] | ||
default = ["hot-reload"] | ||
hot-reload = ["dioxus-hot-reload"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
<div align="center"> | ||
<h1>Dioxus TUI</h1> | ||
<p> | ||
<strong>Beautiful terminal user interfaces in Rust with <a href="https://dioxuslabs.com/">Dioxus </a>.</strong> | ||
</p> | ||
</div> | ||
|
||
<div align="center"> | ||
<!-- Crates version --> | ||
<a href="https://crates.io/crates/dioxus"> | ||
<img src="https://img.shields.io/crates/v/dioxus.svg?style=flat-square" | ||
alt="Crates.io version" /> | ||
</a> | ||
<!-- Downloads --> | ||
<a href="https://crates.io/crates/dioxus"> | ||
<img src="https://img.shields.io/crates/d/dioxus.svg?style=flat-square" | ||
alt="Download" /> | ||
</a> | ||
<!-- docs --> | ||
<a href="https://docs.rs/dioxus"> | ||
<img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square" | ||
alt="docs.rs docs" /> | ||
</a> | ||
<!-- CI --> | ||
<a href="https://github.com/jkelleyrtp/dioxus/actions"> | ||
<img src="https://github.com/dioxuslabs/dioxus/actions/workflows/main.yml/badge.svg" | ||
alt="CI status" /> | ||
</a> | ||
|
||
<!--Awesome --> | ||
<a href="https://github.com/dioxuslabs/awesome-dioxus"> | ||
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome Page" /> | ||
</a> | ||
<!-- Discord --> | ||
<a href="https://discord.gg/XgGxMSkvUM"> | ||
<img src="https://img.shields.io/discord/899851952891002890.svg?logo=discord&style=flat-square" alt="Discord Link" /> | ||
</a> | ||
</div> | ||
|
||
<br/> | ||
|
||
Leverage React-like patterns, CSS, HTML, and Rust to build beautiful, portable, terminal user interfaces with Dioxus. | ||
|
||
```rust | ||
fn app() -> Element { | ||
rsx!{ | ||
div { | ||
width: "100%", | ||
height: "10px", | ||
background_color: "red", | ||
justify_content: "center", | ||
align_items: "center", | ||
"Hello world!" | ||
} | ||
}) | ||
} | ||
``` | ||
|
||
![demo app](examples/example.png) | ||
|
||
## Background | ||
|
||
You can use Html-like semantics with inline styles, tree hierarchy, components, and more in your [`text-based user interface (TUI)`](https://en.wikipedia.org/wiki/Text-based_user_interface) application. | ||
|
||
Dioxus TUI is essentially a port of [Ink](https://github.com/vadimdemedes/ink) but for [`Rust`](https://www.rust-lang.org/) and [`Dioxus`](https://dioxuslabs.com/). Dioxus TUI doesn't depend on Node.js or any other JavaScript runtime, so your binaries are portable and beautiful. | ||
|
||
## Limitations | ||
|
||
- **Subset of Html** | ||
Terminals can only render a subset of HTML. We support as much as we can. | ||
- **Particular frontend design** | ||
Terminals and browsers are and look different. Therefore, the same design might not be the best to cover both renderers. | ||
|
||
## Status | ||
|
||
**WARNING: Dioxus TUI is currently under construction!** | ||
|
||
Rendering a VirtualDom works fine, but the ecosystem of hooks is not yet ready. Additionally, some bugs in the flexbox implementation might be quirky at times. | ||
|
||
## Features | ||
|
||
Dioxus TUI features: | ||
|
||
- [x] Flexbox-based layout system | ||
- [ ] CSS selectors | ||
- [x] inline CSS support | ||
- [x] Built-in focusing system | ||
|
||
* [x] Widgets<sup>1</sup> | ||
* [ ] Support for events, hooks, and callbacks<sup>2</sup> | ||
* [ ] Html tags<sup>3</sup> | ||
|
||
<sup>1</sup> Currently only a subset of the input element is implemented as a component (not an element). The `Input` component supports sliders, text, numbers, passwords, buttons, and checkboxes. | ||
<sup>2</sup> Basic keyboard, mouse, and focus events are implemented. | ||
<sup>3</sup> Currently, most HTML tags don't translate into any meaning inside of Dioxus TUI. So an `input` _element_ won't mean anything nor does it have any additional functionality. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; | ||
use dioxus::prelude::*; | ||
use dioxus_tui::{Config, TuiContext}; | ||
|
||
criterion_group!(mbenches, tui_update); | ||
criterion_main!(mbenches); | ||
|
||
/// This benchmarks the cache performance of the TUI for small edits by changing one box at a time. | ||
fn tui_update(c: &mut Criterion) { | ||
{ | ||
let mut group = c.benchmark_group("Update boxes"); | ||
|
||
for size in 1..=20usize { | ||
let parameter_string = format!("{}", (size).pow(2)); | ||
group.bench_with_input( | ||
BenchmarkId::new("size", parameter_string), | ||
&size, | ||
|b, size| { | ||
b.iter(|| { | ||
dioxus_tui::launch_cfg_with_props( | ||
app, | ||
GridProps { | ||
size: *size, | ||
update_count: 1, | ||
}, | ||
Config::default().with_headless(), | ||
) | ||
}) | ||
}, | ||
); | ||
} | ||
} | ||
|
||
{ | ||
let mut group = c.benchmark_group("Update many boxes"); | ||
|
||
for update_count in 1..=20usize { | ||
let update_count = update_count * 20; | ||
let parameter_string = update_count.to_string(); | ||
group.bench_with_input( | ||
BenchmarkId::new("update count", parameter_string), | ||
&update_count, | ||
|b, update_count| { | ||
b.iter(|| { | ||
dioxus_tui::launch_cfg_with_props( | ||
app, | ||
GridProps { | ||
size: 20, | ||
update_count: *update_count, | ||
}, | ||
Config::default().with_headless(), | ||
) | ||
}) | ||
}, | ||
); | ||
} | ||
} | ||
} | ||
|
||
#[derive(Props, PartialEq, Clone)] | ||
struct BoxProps { | ||
x: usize, | ||
y: usize, | ||
hue: f32, | ||
alpha: f32, | ||
} | ||
#[allow(non_snake_case)] | ||
fn Box(props: BoxProps) -> Element { | ||
let count = use_signal(|| 0); | ||
|
||
let x = props.x * 2; | ||
let y = props.y * 2; | ||
let hue = props.hue; | ||
let display_hue = props.hue as u32 / 10; | ||
let count = count(); | ||
let alpha = props.alpha + (count % 100) as f32; | ||
|
||
rsx! { | ||
div { | ||
left: "{x}%", | ||
top: "{y}%", | ||
width: "100%", | ||
height: "100%", | ||
background_color: "hsl({hue}, 100%, 50%, {alpha}%)", | ||
align_items: "center", | ||
p{"{display_hue:03}"} | ||
} | ||
} | ||
} | ||
|
||
#[derive(Props, PartialEq, Clone)] | ||
struct GridProps { | ||
size: usize, | ||
update_count: usize, | ||
} | ||
#[allow(non_snake_case)] | ||
fn Grid(props: GridProps) -> Element { | ||
let size = props.size; | ||
let mut count = use_signal(|| 0); | ||
let mut counts = use_signal(|| vec![0; size * size]); | ||
|
||
let ctx: TuiContext = consume_context(); | ||
if count() + props.update_count >= (size * size) { | ||
ctx.quit(); | ||
} else { | ||
for _ in 0..props.update_count { | ||
counts.with_mut(|c| { | ||
let i = count(); | ||
c[i] += 1; | ||
c[i] %= 360; | ||
}); | ||
count.with_mut(|i| { | ||
*i += 1; | ||
*i %= size * size; | ||
}); | ||
} | ||
} | ||
|
||
rsx! { | ||
div{ | ||
width: "100%", | ||
height: "100%", | ||
flex_direction: "column", | ||
for x in 0..size { | ||
div { | ||
width: "100%", | ||
height: "100%", | ||
flex_direction: "row", | ||
for y in 0..size { | ||
Box { | ||
key: "{x}-{y}", | ||
x: x, | ||
y: y, | ||
alpha: 100.0, | ||
hue: y as f32*100.0/size as f32 + counts.read()[x*size + y] as f32, | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn app(props: GridProps) -> Element { | ||
rsx! { | ||
div{ | ||
width: "100%", | ||
height: "100%", | ||
Grid{ | ||
size: props.size, | ||
update_count: props.update_count, | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.