Initial setup
This commit is contained in:
commit
1fbcfefe1e
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
dist
|
||||
target
|
||||
node_modules
|
1218
Cargo.lock
generated
Normal file
1218
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "worldwideportal"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
im = "15.1.0"
|
||||
wasm-bindgen = "0.2.92"
|
||||
web-sys = "0.3.69"
|
||||
yew = { version = "0.21.0", features = ["csr"] }
|
10
index.html
Normal file
10
index.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<link data-trunk rel="css" href="node_modules/@xterm/xterm/css/xterm.css"/>
|
||||
<script data-trunk src="node_modules/@xterm/xterm/lib/xterm.js"></script>
|
||||
<script data-trunk src="node_modules/@xterm/addon-fit/lib/addon-fit.js"></script>
|
||||
<link data-trunk rel="css" href="styles.css"/>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
30
package-lock.json
generated
Normal file
30
package-lock.json
generated
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "yew-test",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "yew-test",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-fit": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz",
|
||||
"integrity": "sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/xterm": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
|
||||
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A=="
|
||||
}
|
||||
}
|
||||
}
|
15
package.json
Normal file
15
package.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "yew-test",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.5.0"
|
||||
}
|
||||
}
|
30
src/main.rs
Normal file
30
src/main.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use yew::prelude::*;
|
||||
|
||||
pub mod term_view;
|
||||
use crate::term_view::*;
|
||||
|
||||
#[function_component(App)]
|
||||
fn app() -> Html {
|
||||
let frames_handle: UseStateHandle<Rc<RegisteredTermFrames>> =
|
||||
use_state(|| RegisteredTermFrames::new().into());
|
||||
let frames = RegisteredTermFrameLens {
|
||||
get: (*frames_handle).clone(),
|
||||
set: Callback::from(move |s| frames_handle.set(s)),
|
||||
};
|
||||
html! {
|
||||
<div class="vpane toplevel">
|
||||
<TermView terminal={TermFrame(0)} frames={frames.clone()}/>
|
||||
<TermView terminal={TermFrame(1)} frames={frames.clone()}/>
|
||||
<div class="hpane">
|
||||
<TermView terminal={TermFrame(2)} frames={frames.clone()}/>
|
||||
<TermView terminal={TermFrame(3)} {frames}/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
yew::Renderer::<App>::new().render();
|
||||
}
|
97
src/term_view.rs
Normal file
97
src/term_view.rs
Normal file
@ -0,0 +1,97 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use im::hashmap::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::{Element, Node};
|
||||
use yew::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[derive(PartialEq)]
|
||||
pub type Terminal;
|
||||
#[wasm_bindgen(constructor)]
|
||||
fn new() -> Terminal;
|
||||
#[wasm_bindgen(method)]
|
||||
fn open(this: &Terminal, element: &Element);
|
||||
#[wasm_bindgen(method)]
|
||||
fn write(this: &Terminal, data: &str);
|
||||
// Todo: Can we do this with interfaces somehow?
|
||||
#[wasm_bindgen(method)]
|
||||
fn loadAddon(this: &Terminal, addon: &FitAddon);
|
||||
#[derive(PartialEq)]
|
||||
pub type FitAddon;
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_namespace=FitAddon)]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(constructor)]
|
||||
fn new() -> FitAddon;
|
||||
#[wasm_bindgen(method)]
|
||||
fn fit(this: &FitAddon);
|
||||
}
|
||||
|
||||
#[derive(Eq, Ord, Hash, PartialEq, PartialOrd, Clone)]
|
||||
pub struct TermFrame(pub u64);
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct TermFrameData {
|
||||
pub id: TermFrame,
|
||||
pub term: Terminal,
|
||||
pub fit: FitAddon,
|
||||
pub node: Node,
|
||||
}
|
||||
|
||||
pub type RegisteredTermFrames = HashMap<TermFrame, Rc<TermFrameData>>;
|
||||
#[derive(Properties, PartialEq, Clone)]
|
||||
pub struct RegisteredTermFrameLens {
|
||||
pub get: Rc<RegisteredTermFrames>,
|
||||
pub set: Callback<Rc<RegisteredTermFrames>, ()>,
|
||||
}
|
||||
|
||||
fn get_or_make_term_frame(
|
||||
frame: &TermFrame,
|
||||
frames: &RegisteredTermFrameLens,
|
||||
) -> Rc<TermFrameData> {
|
||||
if let Some(tfd) = frames.get.get(frame) {
|
||||
return tfd.clone();
|
||||
}
|
||||
|
||||
let mut new_frames: RegisteredTermFrames = (*frames.get).clone();
|
||||
let term = Terminal::new();
|
||||
let fit = FitAddon::new();
|
||||
let element = web_sys::window()
|
||||
.and_then(|w| w.document())
|
||||
.and_then(|d| d.create_element("div").ok())
|
||||
.expect("Can create element for term");
|
||||
element.set_class_name("hterminal");
|
||||
term.open(&element);
|
||||
term.loadAddon(&fit);
|
||||
for i in 0..100 {
|
||||
term.write(&format!("{} Hello world\r\n", i));
|
||||
}
|
||||
let new_data: Rc<TermFrameData> = TermFrameData {
|
||||
id: frame.clone(),
|
||||
term,
|
||||
fit,
|
||||
node: element.into(),
|
||||
}
|
||||
.into();
|
||||
new_frames.insert(frame.clone(), new_data.clone());
|
||||
frames.set.emit(new_frames.into());
|
||||
new_data
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct TermViewProps {
|
||||
pub terminal: TermFrame,
|
||||
pub frames: RegisteredTermFrameLens,
|
||||
}
|
||||
|
||||
#[function_component(TermView)]
|
||||
pub fn term_view(props: &TermViewProps) -> Html {
|
||||
let term = get_or_make_term_frame(&props.terminal, &props.frames);
|
||||
term.fit.fit();
|
||||
html! {
|
||||
{Html::VRef(term.node.clone())}
|
||||
}
|
||||
}
|
33
styles.css
Normal file
33
styles.css
Normal file
@ -0,0 +1,33 @@
|
||||
html {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
body {
|
||||
border: solid black 10px;
|
||||
margin: 0px;
|
||||
background-color: black;
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
}
|
||||
.vpane {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
}
|
||||
.hpane {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
}
|
||||
.toplevel {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
}
|
||||
.hterminal {
|
||||
flex: 1 1 0;
|
||||
min-width: 10px;
|
||||
min-height: 10px;
|
||||
border: solid grey 1px;
|
||||
padding: 2px;
|
||||
}
|
Loading…
Reference in New Issue
Block a user