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