diff --git a/Cargo.lock b/Cargo.lock index 558d3e6..e9471d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -273,6 +273,29 @@ dependencies = [ "memchr", ] +[[package]] +name = "alacritty_terminal" +version = "0.24.1-dev" +source = "git+https://github.com/alacritty/alacritty?rev=cacdb5bb3b72bad2c729227537979d95af75978f#cacdb5bb3b72bad2c729227537979d95af75978f" +dependencies = [ + "base64 0.22.1", + "bitflags 2.5.0", + "home", + "libc", + "log", + "miow", + "parking_lot 0.12.2", + "piper", + "polling", + "regex-automata", + "rustix-openpty", + "serde", + "signal-hook", + "unicode-width", + "vte", + "windows-sys 0.52.0", +] + [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -1240,6 +1263,12 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + [[package]] name = "darling" version = "0.20.8" @@ -1674,6 +1703,7 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" name = "fastwave" version = "0.1.0" dependencies = [ + "alacritty_terminal", "futures", "once_cell", "reqwest", @@ -1793,6 +1823,7 @@ version = "0.1.0" dependencies = [ "gloo-file", "shared", + "unicode-segmentation", "wasm-bindgen-test", "web-sys", "wellen", @@ -2409,12 +2440,27 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "hsluv" version = "0.1.0" @@ -2718,7 +2764,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "windows-sys 0.52.0", ] @@ -3172,6 +3218,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "miow" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "359f76430b20a79f9e20e115b3428614e654f04fab314482fc0fda0ebd3c6044" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "moon" version = "0.1.0" @@ -3966,6 +4021,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkg-config" version = "0.3.30" @@ -3999,6 +4065,21 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polling" +version = "3.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "postcard" version = "1.0.8" @@ -4496,6 +4577,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustix-openpty" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25c3aad9fc1424eb82c88087789a7d938e1829724f3e4043163baf0d13cfc12" +dependencies = [ + "errno", + "libc", + "rustix", +] + [[package]] name = "rustls" version = "0.21.11" @@ -4906,6 +4998,16 @@ dependencies = [ "dirs 4.0.0", ] +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -6079,6 +6181,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.8.0" @@ -6127,6 +6235,30 @@ dependencies = [ "libc", ] +[[package]] +name = "vte" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40eb22ae96f050e0c0d6f7ce43feeae26c348fc4dea56928ca81537cfaa6188b" +dependencies = [ + "bitflags 2.5.0", + "cursor-icon", + "log", + "serde", + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "walkdir" version = "2.5.0" diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml index f463636..be972aa 100644 --- a/frontend/Cargo.toml +++ b/frontend/Cargo.toml @@ -14,6 +14,7 @@ wasm-bindgen-test = "0.3.19" unexpected_cfgs = { level = "allow", check-cfg = ['cfg(FASTWAVE_PLATFORM)'] } [dependencies] +unicode-segmentation = "1.10" zoon.workspace = true wellen.workspace = true shared = { path = "../shared", features = ["frontend"] } diff --git a/frontend/src/header_panel.rs b/frontend/src/header_panel.rs index 872025d..c64281d 100644 --- a/frontend/src/header_panel.rs +++ b/frontend/src/header_panel.rs @@ -1,6 +1,7 @@ use crate::{platform, theme::*, Filename, Layout, Mode}; use std::sync::Arc; use zoon::*; +use crate::term::TERM_OPEN; pub struct HeaderPanel { hierarchy: Mutable>>, @@ -37,6 +38,7 @@ impl HeaderPanel { .item(self.load_button()) .item(self.layout_switcher()) .item(self.mode_switcher()) + .item(self.open_terminal()) .item(self.open_konata_file()), ) } @@ -212,4 +214,26 @@ impl HeaderPanel { .on_hovered_change(move |is_hovered| hovered.set_neq(is_hovered)) .on_press(move || Task::start(platform::open_konata_file())) } + + fn open_terminal(&self) -> impl Element { + let (hovered, hovered_signal) = Mutable::new_and_signal(false); + Button::new() + .s(Padding::new().x(20).y(10)) + .s(Background::new().color_signal( + hovered_signal.map_bool(|| COLOR_MEDIUM_SLATE_BLUE, || COLOR_SLATE_BLUE), + )) + .s(Align::new().left()) + .s(RoundedCorners::all(15)) + .label( + El::new() + .s(Font::new().no_wrap()) + .child("Open Terminal"), + ) + .on_hovered_change(move |is_hovered| hovered.set_neq(is_hovered)) + .on_press(move || { + let term_open = TERM_OPEN.get(); + TERM_OPEN.set(!term_open); + + }) + } } diff --git a/frontend/src/main.rs b/frontend/src/main.rs index ba113b5..5327a5c 100644 --- a/frontend/src/main.rs +++ b/frontend/src/main.rs @@ -1,4 +1,5 @@ use shared::DiagramConnectorMessage; +use term::TERM_OPEN; use std::{mem, sync::Arc}; use zoon::*; @@ -23,6 +24,9 @@ use command_panel::CommandPanel; pub mod theme; use theme::*; +pub mod term; +use shared::term::{TerminalDownMsg, TerminalScreen}; + #[derive(Clone, Copy, Default)] enum Layout { Tree, @@ -98,8 +102,10 @@ fn main() { .unwrap_throw() .set_component_text(&component_id, &text), } - }) - .await + }).await; + platform::listen_term_update(|down_msg| { + term::TERMINAL_STATE.set(down_msg); + }).await; }); } @@ -181,4 +187,20 @@ fn root() -> impl Element { } }))) .item(CommandPanel::new()) + .item_signal( + TERM_OPEN.signal_cloned().map( + |term_open| { + match term_open { + true => + El::new() + .s(Height::fill().max(400).min(400)) + .s(Padding::all(5)) + .child(term::root()), + false => + El::new() + .child("") + } + } + ) + ) } diff --git a/frontend/src/platform.rs b/frontend/src/platform.rs index 8d3a9c4..e220221 100644 --- a/frontend/src/platform.rs +++ b/frontend/src/platform.rs @@ -29,6 +29,8 @@ type DiagramConnectorPath = String; type DiagramConnectorName = String; type ComponentId = String; +use shared::term::{TerminalDownMsg, TerminalScreen}; + pub async fn show_window() { platform::show_window().await } @@ -72,6 +74,10 @@ pub async fn unload_signal(signal_ref: wellen::SignalRef) { platform::unload_signal(signal_ref).await } +pub async fn send_char(c : String) { + platform::send_char(c).await +} + pub async fn add_decoders(decoder_paths: Vec) -> AddedDecodersCount { let count = platform::add_decoders(decoder_paths).await; if count > 0 { @@ -112,6 +118,12 @@ pub async fn listen_diagram_connectors_messages( platform::listen_diagram_connectors_messages(on_message).await; } +pub async fn listen_term_update( + on_message: impl FnMut(TerminalDownMsg) + 'static, +) { + platform::listen_term_update(on_message).await; +} + pub async fn notify_diagram_connector_text_change( diagram_connector: DiagramConnectorName, component_id: ComponentId, diff --git a/frontend/src/platform/tauri.rs b/frontend/src/platform/tauri.rs index 37b4c39..7e81f20 100644 --- a/frontend/src/platform/tauri.rs +++ b/frontend/src/platform/tauri.rs @@ -1,4 +1,5 @@ use shared::DiagramConnectorMessage; +use shared::term::{TerminalDownMsg, TerminalScreen}; use zoon::*; pub(super) async fn show_window() { @@ -57,6 +58,12 @@ pub(super) async fn unload_signal(signal_ref: wellen::SignalRef) { .unwrap_throw() } +pub(super) async fn send_char(c : String) { + tauri_glue::send_char(c) + .await + .unwrap_throw() +} + pub(super) async fn add_decoders( decoder_paths: Vec, ) -> super::AddedDecodersCount { @@ -97,6 +104,14 @@ pub(super) async fn listen_diagram_connectors_messages( tauri_glue::listen_diagram_connectors_messages(Closure::new(on_message).into_js_value()).await } +pub(super) async fn listen_term_update( + mut on_message: impl FnMut(TerminalDownMsg) + 'static, +) { + let on_message = + move |message: JsValue| on_message(serde_wasm_bindgen::from_value(message).unwrap_throw()); + tauri_glue::listen_term_update(Closure::new(on_message).into_js_value()).await +} + pub(super) async fn notify_diagram_connector_text_change( diagram_connector: super::DiagramConnectorName, component_id: super::ComponentId, @@ -142,6 +157,9 @@ mod tauri_glue { #[wasm_bindgen(catch)] pub async fn unload_signal(signal_ref_index: usize) -> Result<(), JsValue>; + #[wasm_bindgen(catch)] + pub async fn send_char(c : String) -> Result<(), JsValue>; + #[wasm_bindgen(catch)] pub async fn add_decoders( decoder_paths: Vec, @@ -160,6 +178,8 @@ mod tauri_glue { pub async fn listen_diagram_connectors_messages(on_event: JsValue); + pub async fn listen_term_update(on_event: JsValue); + #[wasm_bindgen(catch)] pub async fn notify_diagram_connector_text_change( diagram_connector: super::super::DiagramConnectorName, diff --git a/frontend/src/term.rs b/frontend/src/term.rs new file mode 100644 index 0000000..8b013b0 --- /dev/null +++ b/frontend/src/term.rs @@ -0,0 +1,142 @@ +use std::ops::Index; + +use chrono::format; +use zoon::*; +use zoon::{println, eprintln, *}; +use shared::term::{TerminalDownMsg, TerminalScreen, TerminalUpMsg}; +use unicode_segmentation::UnicodeSegmentation; + +// use tokio::time::timeout; +pub static TERM_OPEN: Lazy> = Lazy::new(|| {false.into()}); + +pub const TERMINAL_COLOR: Oklch = color!("oklch(20% 0.125 262.26)"); + +pub static TERMINAL_STATE: Lazy> = + Lazy::new(|| { + Mutable::new(TerminalDownMsg::TermNotStarted) + }); + +pub fn root() -> impl Element { + let terminal = + El::new() + .s(Width::fill()) + .s(Height::fill()) + .s(Background::new().color(TERMINAL_COLOR)) + .s(RoundedCorners::all(7)) + .s(Font::new().family([ + FontFamily::new("Lucida Console"), + FontFamily::new("Courier"), + FontFamily::new("monospace") + ])) + .update_raw_el(|raw_el| { + raw_el.global_event_handler(|event: events::KeyDown| { + send_char( + (&event).key().as_str(), + (&event).ctrl_key(), + ); + }) + }) + .child_signal(TERMINAL_STATE.signal_cloned().map( + |down_msg| { + match down_msg { + TerminalDownMsg::FullTermUpdate(term) => { + make_grid_with_newlines(&term) + }, + TerminalDownMsg::TermNotStarted => { + "Term not yet started!".to_string() + }, + TerminalDownMsg::BackendTermStartFailure(msg) => { + format!("Error: BackendTermStartFailure: {}", msg) + } + } + } + ) + ) + ; + let root = Column::new() + .s(Width::fill()) + .s(Height::fill()) + .s(Align::new().top()) + .item(terminal); + root +} + +fn send_char( + s : &str, + has_control : bool, + ) { + match process_str(s, has_control) { + Some(c) => { + let send_c = c.clone(); + Task::start(async move { + crate::platform::send_char(send_c.to_string()).await; + }); + } + None => {} + } + +} + + +fn make_grid_with_newlines(term: &TerminalScreen) -> String { + let mut formatted = String::with_capacity(term.content.len() + (term.content.len() / term.cols as usize)); + + term.content.chars().enumerate().for_each(|(i, c)| { + formatted.push(c); + if (i + 1) as u16 % term.cols == 0 { + formatted.push('\n'); + } + }); + + formatted +} + + +fn process_str(s: &str, has_ctrl: bool) -> Option { + match s { + "Enter" => {return Some('\n');} + "Escape" => {return Some('\x1B');} + "Backspace" => {return Some('\x08');} + "ArrowUp" => {return Some('\x10');} + "ArrowDown" => {return Some('\x0E');} + "ArrowLeft" => {return Some('\x02');} + "ArrowRight" => {return Some('\x06');} + "Control" => {return None;} + "Shift" => {return None;} + "Meta" => {return None;} + "Alt" => {return None;} + _ => {} + } + + let mut graphemes = s.graphemes(true); + let first = graphemes.next(); + + if let Some(g) = first { + if g.len() == 1 { + if let Some(c) = g.chars().next() { + let c = process_for_ctrl_char(c, has_ctrl); + return Some(c); + } + } + } + + None +} + +// Helper function to process control characters + +fn is_lowercase_alpha(c: char) -> bool { + char_is_between_inclusive(c, 'a', 'z') +} + +fn process_for_ctrl_char(c: char, has_ctrl: bool) -> char { + if has_ctrl { + (c as u8 & 0x1F) as char + } else { + c + } +} + +fn char_is_between_inclusive(c : char, lo_char : char, hi_char : char) -> bool { + c >= lo_char && c <= hi_char +} diff --git a/frontend/typescript/bundles/tauri_glue.js b/frontend/typescript/bundles/tauri_glue.js index de96555..575181f 100644 --- a/frontend/typescript/bundles/tauri_glue.js +++ b/frontend/typescript/bundles/tauri_glue.js @@ -1 +1 @@ -var ae=Object.defineProperty;var q=(n,e)=>{for(var t in e)ae(n,t,{get:e[t],enumerable:!0})};var M={};q(M,{Channel:()=>k,PluginListener:()=>R,Resource:()=>y,addPluginListener:()=>se,convertFileSrc:()=>le,invoke:()=>i,isTauri:()=>oe,transformCallback:()=>T});function c(n,e,t,r){if(t==="a"&&!r)throw new TypeError("Private accessor was defined without a getter");if(typeof e=="function"?n!==e||!r:!e.has(n))throw new TypeError("Cannot read private member from an object whose class did not declare it");return t==="m"?r:t==="a"?r.call(n):r?r.value:e.get(n)}function _(n,e,t,r,a){if(r==="m")throw new TypeError("Private method is not writable");if(r==="a"&&!a)throw new TypeError("Private accessor was defined without a setter");if(typeof e=="function"?n!==e||!a:!e.has(n))throw new TypeError("Cannot write private member to an object whose class did not declare it");return r==="a"?a.call(n,t):a?a.value=t:e.set(n,t),t}var b,A,f,L;function T(n,e=!1){return window.__TAURI_INTERNALS__.transformCallback(n,e)}var k=class{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,b.set(this,()=>{}),A.set(this,0),f.set(this,{}),this.id=T(({message:e,id:t})=>{if(t===c(this,A,"f")){_(this,A,t+1,"f"),c(this,b,"f").call(this,e);let r=Object.keys(c(this,f,"f"));if(r.length>0){let a=t+1;for(let u of r.sort())if(parseInt(u)===a){let s=c(this,f,"f")[u];delete c(this,f,"f")[u],c(this,b,"f").call(this,s),a+=1}else break;_(this,A,a,"f")}}else c(this,f,"f")[t.toString()]=e})}set onmessage(e){_(this,b,e,"f")}get onmessage(){return c(this,b,"f")}toJSON(){return`__CHANNEL__:${this.id}`}};b=new WeakMap,A=new WeakMap,f=new WeakMap;var R=class{constructor(e,t,r){this.plugin=e,this.event=t,this.channelId=r}async unregister(){return i(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}};async function se(n,e,t){let r=new k;return r.onmessage=t,i(`plugin:${n}|register_listener`,{event:e,handler:r}).then(()=>new R(n,e,r.id))}async function i(n,e={},t){return window.__TAURI_INTERNALS__.invoke(n,e,t)}function le(n,e="asset"){return window.__TAURI_INTERNALS__.convertFileSrc(n,e)}var y=class{get rid(){return c(this,L,"f")}constructor(e){L.set(this,void 0),_(this,L,e,"f")}async close(){return i("plugin:resources|close",{rid:this.rid})}};L=new WeakMap;function oe(){return"isTauri"in window&&!!window.isTauri}var E={};q(E,{TauriEvent:()=>l,emit:()=>x,emitTo:()=>P,listen:()=>p,once:()=>w});var l;(function(n){n.WINDOW_RESIZED="tauri://resize",n.WINDOW_MOVED="tauri://move",n.WINDOW_CLOSE_REQUESTED="tauri://close-requested",n.WINDOW_DESTROYED="tauri://destroyed",n.WINDOW_FOCUS="tauri://focus",n.WINDOW_BLUR="tauri://blur",n.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",n.WINDOW_THEME_CHANGED="tauri://theme-changed",n.WINDOW_CREATED="tauri://window-created",n.WEBVIEW_CREATED="tauri://webview-created",n.DRAG="tauri://drag",n.DROP="tauri://drop",n.DROP_OVER="tauri://drop-over",n.DROP_CANCELLED="tauri://drag-cancelled"})(l||(l={}));async function J(n,e){await i("plugin:event|unlisten",{event:n,eventId:e})}async function p(n,e,t){var r;let a=typeof t?.target=="string"?{kind:"AnyLabel",label:t.target}:(r=t?.target)!==null&&r!==void 0?r:{kind:"Any"};return i("plugin:event|listen",{event:n,target:a,handler:T(e)}).then(u=>async()=>J(n,u))}async function w(n,e,t){return p(n,r=>{e(r),J(n,r.id).catch(()=>{})},t)}async function x(n,e){await i("plugin:event|emit",{event:n,payload:e})}async function P(n,e,t){await i("plugin:event|emit_to",{target:typeof n=="string"?{kind:"AnyLabel",label:n}:n,event:e,payload:t})}var F=class{constructor(e,t){this.type="Logical",this.width=e,this.height=t}},h=class{constructor(e,t){this.type="Physical",this.width=e,this.height=t}toLogical(e){return new F(this.width/e,this.height/e)}},O=class{constructor(e,t){this.type="Logical",this.x=e,this.y=t}},d=class{constructor(e,t){this.type="Physical",this.x=e,this.y=t}toLogical(e){return new O(this.x/e,this.y/e)}};var I=class n extends y{constructor(e){super(e)}static async new(e,t,r){return i("plugin:image|new",{rgba:g(e),width:t,height:r}).then(a=>new n(a))}static async fromBytes(e){return i("plugin:image|from_bytes",{bytes:g(e)}).then(t=>new n(t))}static async fromPath(e){return i("plugin:image|from_path",{path:e}).then(t=>new n(t))}async rgba(){return i("plugin:image|rgba",{rid:this.rid}).then(e=>new Uint8Array(e))}async size(){return i("plugin:image|size",{rid:this.rid})}};function g(n){return n==null?null:typeof n=="string"?n:n instanceof Uint8Array?Array.from(n):n instanceof ArrayBuffer?Array.from(new Uint8Array(n)):n instanceof I?n.rid:n}var V;(function(n){n[n.Critical=1]="Critical",n[n.Informational=2]="Informational"})(V||(V={}));var G=class{constructor(e){this._preventDefault=!1,this.event=e.event,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}},K;(function(n){n.None="none",n.Normal="normal",n.Indeterminate="indeterminate",n.Paused="paused",n.Error="error"})(K||(K={}));function H(){return new m(window.__TAURI_INTERNALS__.metadata.currentWindow.label,{skip:!0})}function z(){return window.__TAURI_INTERNALS__.metadata.windows.map(n=>new m(n.label,{skip:!0}))}var U=["tauri://created","tauri://error"],m=class{constructor(e,t={}){var r;this.label=e,this.listeners=Object.create(null),t?.skip||i("plugin:window|create",{options:{...t,parent:typeof t.parent=="string"?t.parent:(r=t.parent)===null||r===void 0?void 0:r.label,label:e}}).then(async()=>this.emit("tauri://created")).catch(async a=>this.emit("tauri://error",a))}static getByLabel(e){var t;return(t=z().find(r=>r.label===e))!==null&&t!==void 0?t:null}static getCurrent(){return H()}static getAll(){return z()}static async getFocusedWindow(){for(let e of z())if(await e.isFocused())return e;return null}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(t),1)}):p(e,t,{target:{kind:"Window",label:this.label}})}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(t),1)}):w(e,t,{target:{kind:"Window",label:this.label}})}async emit(e,t){if(U.includes(e)){for(let r of this.listeners[e]||[])r({event:e,id:-1,payload:t});return Promise.resolve()}return x(e,t)}async emitTo(e,t,r){if(U.includes(t)){for(let a of this.listeners[t]||[])a({event:t,id:-1,payload:r});return Promise.resolve()}return P(e,t,r)}_handleTauriEvent(e,t){return U.includes(e)?(e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t],!0):!1}async scaleFactor(){return i("plugin:window|scale_factor",{label:this.label})}async innerPosition(){return i("plugin:window|inner_position",{label:this.label}).then(({x:e,y:t})=>new d(e,t))}async outerPosition(){return i("plugin:window|outer_position",{label:this.label}).then(({x:e,y:t})=>new d(e,t))}async innerSize(){return i("plugin:window|inner_size",{label:this.label}).then(({width:e,height:t})=>new h(e,t))}async outerSize(){return i("plugin:window|outer_size",{label:this.label}).then(({width:e,height:t})=>new h(e,t))}async isFullscreen(){return i("plugin:window|is_fullscreen",{label:this.label})}async isMinimized(){return i("plugin:window|is_minimized",{label:this.label})}async isMaximized(){return i("plugin:window|is_maximized",{label:this.label})}async isFocused(){return i("plugin:window|is_focused",{label:this.label})}async isDecorated(){return i("plugin:window|is_decorated",{label:this.label})}async isResizable(){return i("plugin:window|is_resizable",{label:this.label})}async isMaximizable(){return i("plugin:window|is_maximizable",{label:this.label})}async isMinimizable(){return i("plugin:window|is_minimizable",{label:this.label})}async isClosable(){return i("plugin:window|is_closable",{label:this.label})}async isVisible(){return i("plugin:window|is_visible",{label:this.label})}async title(){return i("plugin:window|title",{label:this.label})}async theme(){return i("plugin:window|theme",{label:this.label})}async center(){return i("plugin:window|center",{label:this.label})}async requestUserAttention(e){let t=null;return e&&(e===V.Critical?t={type:"Critical"}:t={type:"Informational"}),i("plugin:window|request_user_attention",{label:this.label,value:t})}async setResizable(e){return i("plugin:window|set_resizable",{label:this.label,value:e})}async setMaximizable(e){return i("plugin:window|set_maximizable",{label:this.label,value:e})}async setMinimizable(e){return i("plugin:window|set_minimizable",{label:this.label,value:e})}async setClosable(e){return i("plugin:window|set_closable",{label:this.label,value:e})}async setTitle(e){return i("plugin:window|set_title",{label:this.label,value:e})}async maximize(){return i("plugin:window|maximize",{label:this.label})}async unmaximize(){return i("plugin:window|unmaximize",{label:this.label})}async toggleMaximize(){return i("plugin:window|toggle_maximize",{label:this.label})}async minimize(){return i("plugin:window|minimize",{label:this.label})}async unminimize(){return i("plugin:window|unminimize",{label:this.label})}async show(){return i("plugin:window|show",{label:this.label})}async hide(){return i("plugin:window|hide",{label:this.label})}async close(){return i("plugin:window|close",{label:this.label})}async destroy(){return i("plugin:window|destroy",{label:this.label})}async setDecorations(e){return i("plugin:window|set_decorations",{label:this.label,value:e})}async setShadow(e){return i("plugin:window|set_shadow",{label:this.label,value:e})}async setEffects(e){return i("plugin:window|set_effects",{label:this.label,value:e})}async clearEffects(){return i("plugin:window|set_effects",{label:this.label,value:null})}async setAlwaysOnTop(e){return i("plugin:window|set_always_on_top",{label:this.label,value:e})}async setAlwaysOnBottom(e){return i("plugin:window|set_always_on_bottom",{label:this.label,value:e})}async setContentProtected(e){return i("plugin:window|set_content_protected",{label:this.label,value:e})}async setSize(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");let t={};return t[`${e.type}`]={width:e.width,height:e.height},i("plugin:window|set_size",{label:this.label,value:t})}async setMinSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");let t=null;return e&&(t={},t[`${e.type}`]={width:e.width,height:e.height}),i("plugin:window|set_min_size",{label:this.label,value:t})}async setMaxSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");let t=null;return e&&(t={},t[`${e.type}`]={width:e.width,height:e.height}),i("plugin:window|set_max_size",{label:this.label,value:t})}async setPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");let t={};return t[`${e.type}`]={x:e.x,y:e.y},i("plugin:window|set_position",{label:this.label,value:t})}async setFullscreen(e){return i("plugin:window|set_fullscreen",{label:this.label,value:e})}async setFocus(){return i("plugin:window|set_focus",{label:this.label})}async setIcon(e){return i("plugin:window|set_icon",{label:this.label,value:g(e)})}async setSkipTaskbar(e){return i("plugin:window|set_skip_taskbar",{label:this.label,value:e})}async setCursorGrab(e){return i("plugin:window|set_cursor_grab",{label:this.label,value:e})}async setCursorVisible(e){return i("plugin:window|set_cursor_visible",{label:this.label,value:e})}async setCursorIcon(e){return i("plugin:window|set_cursor_icon",{label:this.label,value:e})}async setCursorPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");let t={};return t[`${e.type}`]={x:e.x,y:e.y},i("plugin:window|set_cursor_position",{label:this.label,value:t})}async setIgnoreCursorEvents(e){return i("plugin:window|set_ignore_cursor_events",{label:this.label,value:e})}async startDragging(){return i("plugin:window|start_dragging",{label:this.label})}async startResizeDragging(e){return i("plugin:window|start_resize_dragging",{label:this.label,value:e})}async setProgressBar(e){return i("plugin:window|set_progress_bar",{label:this.label,value:e})}async setVisibleOnAllWorkspaces(e){return i("plugin:window|set_visible_on_all_workspaces",{label:this.label,value:e})}async onResized(e){return this.listen(l.WINDOW_RESIZED,t=>{t.payload=pe(t.payload),e(t)})}async onMoved(e){return this.listen(l.WINDOW_MOVED,t=>{t.payload=W(t.payload),e(t)})}async onCloseRequested(e){return this.listen(l.WINDOW_CLOSE_REQUESTED,t=>{let r=new G(t);Promise.resolve(e(r)).then(()=>{if(!r.isPreventDefault())return this.destroy()})})}async onDragDropEvent(e){let t=await this.listen(l.DRAG,s=>{e({...s,payload:{type:"dragged",paths:s.payload.paths,position:W(s.payload.position)}})}),r=await this.listen(l.DROP,s=>{e({...s,payload:{type:"dropped",paths:s.payload.paths,position:W(s.payload.position)}})}),a=await this.listen(l.DROP_OVER,s=>{e({...s,payload:{type:"dragOver",position:W(s.payload.position)}})}),u=await this.listen(l.DROP_CANCELLED,s=>{e({...s,payload:{type:"cancelled"}})});return()=>{t(),r(),a(),u()}}async onFocusChanged(e){let t=await this.listen(l.WINDOW_FOCUS,a=>{e({...a,payload:!0})}),r=await this.listen(l.WINDOW_BLUR,a=>{e({...a,payload:!1})});return()=>{t(),r()}}async onScaleChanged(e){return this.listen(l.WINDOW_SCALE_FACTOR_CHANGED,e)}async onThemeChanged(e){return this.listen(l.WINDOW_THEME_CHANGED,e)}},Z;(function(n){n.AppearanceBased="appearanceBased",n.Light="light",n.Dark="dark",n.MediumLight="mediumLight",n.UltraDark="ultraDark",n.Titlebar="titlebar",n.Selection="selection",n.Menu="menu",n.Popover="popover",n.Sidebar="sidebar",n.HeaderView="headerView",n.Sheet="sheet",n.WindowBackground="windowBackground",n.HudWindow="hudWindow",n.FullScreenUI="fullScreenUI",n.Tooltip="tooltip",n.ContentBackground="contentBackground",n.UnderWindowBackground="underWindowBackground",n.UnderPageBackground="underPageBackground",n.Mica="mica",n.Blur="blur",n.Acrylic="acrylic",n.Tabbed="tabbed",n.TabbedDark="tabbedDark",n.TabbedLight="tabbedLight"})(Z||(Z={}));var Y;(function(n){n.FollowsWindowActiveState="followsWindowActiveState",n.Active="active",n.Inactive="inactive"})(Y||(Y={}));function W(n){return new d(n.x,n.y)}function pe(n){return new h(n.width,n.height)}function N(){return new v(H(),window.__TAURI_INTERNALS__.metadata.currentWebview.label,{skip:!0})}function X(){return window.__TAURI_INTERNALS__.metadata.webviews.map(n=>new v(m.getByLabel(n.windowLabel),n.label,{skip:!0}))}var j=["tauri://created","tauri://error"],v=class{constructor(e,t,r){this.window=e,this.label=t,this.listeners=Object.create(null),r?.skip||i("plugin:webview|create_webview",{windowLabel:e.label,label:t,options:r}).then(async()=>this.emit("tauri://created")).catch(async a=>this.emit("tauri://error",a))}static getByLabel(e){var t;return(t=X().find(r=>r.label===e))!==null&&t!==void 0?t:null}static getCurrent(){return N()}static getAll(){return X()}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(t),1)}):p(e,t,{target:{kind:"Webview",label:this.label}})}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(t),1)}):w(e,t,{target:{kind:"Webview",label:this.label}})}async emit(e,t){if(j.includes(e)){for(let r of this.listeners[e]||[])r({event:e,id:-1,payload:t});return Promise.resolve()}return x(e,t)}async emitTo(e,t,r){if(j.includes(t)){for(let a of this.listeners[t]||[])a({event:t,id:-1,payload:r});return Promise.resolve()}return P(e,t,r)}_handleTauriEvent(e,t){return j.includes(e)?(e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t],!0):!1}async position(){return i("plugin:webview|webview_position",{label:this.label}).then(({x:e,y:t})=>new d(e,t))}async size(){return i("plugin:webview|webview_size",{label:this.label}).then(({width:e,height:t})=>new h(e,t))}async close(){return i("plugin:webview|close",{label:this.label})}async setSize(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");let t={};return t[`${e.type}`]={width:e.width,height:e.height},i("plugin:webview|set_webview_size",{label:this.label,value:t})}async setPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");let t={};return t[`${e.type}`]={x:e.x,y:e.y},i("plugin:webview|set_webview_position",{label:this.label,value:t})}async setFocus(){return i("plugin:webview|set_webview_focus",{label:this.label})}async setZoom(e){return i("plugin:webview|set_webview_zoom",{label:this.label,value:e})}async reparent(e){return i("plugin:webview|set_webview_focus",{label:this.label,window:typeof e=="string"?e:e.label})}async onDragDropEvent(e){let t=await this.listen(l.DRAG,s=>{e({...s,payload:{type:"dragged",paths:s.payload.paths,position:$(s.payload.position)}})}),r=await this.listen(l.DROP,s=>{e({...s,payload:{type:"dropped",paths:s.payload.paths,position:$(s.payload.position)}})}),a=await this.listen(l.DROP_CANCELLED,s=>{e({...s,payload:{type:"dragOver",position:$(s.payload.position)}})}),u=await this.listen(l.DROP_CANCELLED,s=>{e({...s,payload:{type:"cancelled"}})});return()=>{t(),r(),a(),u()}}};function $(n){return new d(n.x,n.y)}function ye(){let n=N();return new C(n.label,{skip:!0})}function B(){return window.__TAURI_INTERNALS__.metadata.webviews.map(n=>new C(n.label,{skip:!0}))}var C=class n{constructor(e,t={}){var r;this.label=e,this.listeners=Object.create(null),t?.skip||i("plugin:webview|create_webview_window",{options:{...t,parent:typeof t.parent=="string"?t.parent:(r=t.parent)===null||r===void 0?void 0:r.label,label:e}}).then(async()=>this.emit("tauri://created")).catch(async a=>this.emit("tauri://error",a))}static getByLabel(e){var t;let r=(t=B().find(a=>a.label===e))!==null&&t!==void 0?t:null;return r?new n(r.label,{skip:!0}):null}static getCurrent(){return ye()}static getAll(){return B().map(e=>new n(e.label,{skip:!0}))}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(t),1)}):p(e,t,{target:{kind:"WebviewWindow",label:this.label}})}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(t),1)}):w(e,t,{target:{kind:"WebviewWindow",label:this.label}})}};we(C,[m,v]);function we(n,e){(Array.isArray(e)?e:[e]).forEach(t=>{Object.getOwnPropertyNames(t.prototype).forEach(r=>{var a;typeof n.prototype=="object"&&n.prototype&&r in n.prototype||Object.defineProperty(n.prototype,r,(a=Object.getOwnPropertyDescriptor(t.prototype,r))!==null&&a!==void 0?a:Object.create(null))})})}var ee;(function(n){n[n.Audio=1]="Audio",n[n.Cache=2]="Cache",n[n.Config=3]="Config",n[n.Data=4]="Data",n[n.LocalData=5]="LocalData",n[n.Document=6]="Document",n[n.Download=7]="Download",n[n.Picture=8]="Picture",n[n.Public=9]="Public",n[n.Video=10]="Video",n[n.Resource=11]="Resource",n[n.Temp=12]="Temp",n[n.AppConfig=13]="AppConfig",n[n.AppData=14]="AppData",n[n.AppLocalData=15]="AppLocalData",n[n.AppCache=16]="AppCache",n[n.AppLog=17]="AppLog",n[n.Desktop=18]="Desktop",n[n.Executable=19]="Executable",n[n.Font=20]="Font",n[n.Home=21]="Home",n[n.Runtime=22]="Runtime",n[n.Template=23]="Template"})(ee||(ee={}));var fe,ke;fe=new WeakMap,ke=new WeakMap;var Q;(function(n){n.Add="Add",n.Advanced="Advanced",n.Bluetooth="Bluetooth",n.Bookmarks="Bookmarks",n.Caution="Caution",n.ColorPanel="ColorPanel",n.ColumnView="ColumnView",n.Computer="Computer",n.EnterFullScreen="EnterFullScreen",n.Everyone="Everyone",n.ExitFullScreen="ExitFullScreen",n.FlowView="FlowView",n.Folder="Folder",n.FolderBurnable="FolderBurnable",n.FolderSmart="FolderSmart",n.FollowLinkFreestanding="FollowLinkFreestanding",n.FontPanel="FontPanel",n.GoLeft="GoLeft",n.GoRight="GoRight",n.Home="Home",n.IChatTheater="IChatTheater",n.IconView="IconView",n.Info="Info",n.InvalidDataFreestanding="InvalidDataFreestanding",n.LeftFacingTriangle="LeftFacingTriangle",n.ListView="ListView",n.LockLocked="LockLocked",n.LockUnlocked="LockUnlocked",n.MenuMixedState="MenuMixedState",n.MenuOnState="MenuOnState",n.MobileMe="MobileMe",n.MultipleDocuments="MultipleDocuments",n.Network="Network",n.Path="Path",n.PreferencesGeneral="PreferencesGeneral",n.QuickLook="QuickLook",n.RefreshFreestanding="RefreshFreestanding",n.Refresh="Refresh",n.Remove="Remove",n.RevealFreestanding="RevealFreestanding",n.RightFacingTriangle="RightFacingTriangle",n.Share="Share",n.Slideshow="Slideshow",n.SmartBadge="SmartBadge",n.StatusAvailable="StatusAvailable",n.StatusNone="StatusNone",n.StatusPartiallyAvailable="StatusPartiallyAvailable",n.StatusUnavailable="StatusUnavailable",n.StopProgressFreestanding="StopProgressFreestanding",n.StopProgress="StopProgress",n.TrashEmpty="TrashEmpty",n.TrashFull="TrashFull",n.User="User",n.UserAccounts="UserAccounts",n.UserGroup="UserGroup",n.UserGuest="UserGuest"})(Q||(Q={}));var o=M.invoke,xe=E.listen;async function zn(){return await o("show_window")}async function Un(){return await o("pick_and_load_waveform")}async function Vn(){return await o("load_file_with_selected_vars")}async function Gn(){return await o("get_hierarchy")}async function Hn(n,e,t,r,a,u){return await o("load_signal_and_get_timeline",{signal_ref_index:n,timeline_zoom:e,timeline_viewport_width:t,timeline_viewport_x:r,block_height:a,var_format:u})}async function jn(n){return await o("unload_signal",{signal_ref_index:n})}async function $n(n){return await o("add_decoders",{decoder_paths:n})}async function Nn(){return await o("remove_all_decoders")}async function Qn(n){return await o("add_diagram_connectors",{diagram_connector_paths:n})}async function qn(){return await o("remove_all_diagram_connectors")}async function Jn(n){return await xe("diagram_connector_message",e=>n(e.payload))}async function Kn(n,e,t){return await o("notify_diagram_connector_text_change",{diagram_connector:n,component_id:e,text:t})}async function Zn(){return await o("open_konata_file")}export{$n as add_decoders,Qn as add_diagram_connectors,Gn as get_hierarchy,Jn as listen_diagram_connectors_messages,Vn as load_file_with_selected_vars,Hn as load_signal_and_get_timeline,Kn as notify_diagram_connector_text_change,Zn as open_konata_file,Un as pick_and_load_waveform,Nn as remove_all_decoders,qn as remove_all_diagram_connectors,zn as show_window,jn as unload_signal}; +var se=Object.defineProperty;var q=(n,e)=>{for(var t in e)se(n,t,{get:e[t],enumerable:!0})};var M={};q(M,{Channel:()=>k,PluginListener:()=>R,Resource:()=>y,addPluginListener:()=>le,convertFileSrc:()=>oe,invoke:()=>i,isTauri:()=>ue,transformCallback:()=>T});function c(n,e,t,r){if(t==="a"&&!r)throw new TypeError("Private accessor was defined without a getter");if(typeof e=="function"?n!==e||!r:!e.has(n))throw new TypeError("Cannot read private member from an object whose class did not declare it");return t==="m"?r:t==="a"?r.call(n):r?r.value:e.get(n)}function _(n,e,t,r,a){if(r==="m")throw new TypeError("Private method is not writable");if(r==="a"&&!a)throw new TypeError("Private accessor was defined without a setter");if(typeof e=="function"?n!==e||!a:!e.has(n))throw new TypeError("Cannot write private member to an object whose class did not declare it");return r==="a"?a.call(n,t):a?a.value=t:e.set(n,t),t}var b,A,f,L;function T(n,e=!1){return window.__TAURI_INTERNALS__.transformCallback(n,e)}var k=class{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,b.set(this,()=>{}),A.set(this,0),f.set(this,{}),this.id=T(({message:e,id:t})=>{if(t===c(this,A,"f")){_(this,A,t+1,"f"),c(this,b,"f").call(this,e);let r=Object.keys(c(this,f,"f"));if(r.length>0){let a=t+1;for(let u of r.sort())if(parseInt(u)===a){let s=c(this,f,"f")[u];delete c(this,f,"f")[u],c(this,b,"f").call(this,s),a+=1}else break;_(this,A,a,"f")}}else c(this,f,"f")[t.toString()]=e})}set onmessage(e){_(this,b,e,"f")}get onmessage(){return c(this,b,"f")}toJSON(){return`__CHANNEL__:${this.id}`}};b=new WeakMap,A=new WeakMap,f=new WeakMap;var R=class{constructor(e,t,r){this.plugin=e,this.event=t,this.channelId=r}async unregister(){return i(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}};async function le(n,e,t){let r=new k;return r.onmessage=t,i(`plugin:${n}|register_listener`,{event:e,handler:r}).then(()=>new R(n,e,r.id))}async function i(n,e={},t){return window.__TAURI_INTERNALS__.invoke(n,e,t)}function oe(n,e="asset"){return window.__TAURI_INTERNALS__.convertFileSrc(n,e)}var y=class{get rid(){return c(this,L,"f")}constructor(e){L.set(this,void 0),_(this,L,e,"f")}async close(){return i("plugin:resources|close",{rid:this.rid})}};L=new WeakMap;function ue(){return"isTauri"in window&&!!window.isTauri}var E={};q(E,{TauriEvent:()=>l,emit:()=>x,emitTo:()=>P,listen:()=>p,once:()=>w});var l;(function(n){n.WINDOW_RESIZED="tauri://resize",n.WINDOW_MOVED="tauri://move",n.WINDOW_CLOSE_REQUESTED="tauri://close-requested",n.WINDOW_DESTROYED="tauri://destroyed",n.WINDOW_FOCUS="tauri://focus",n.WINDOW_BLUR="tauri://blur",n.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",n.WINDOW_THEME_CHANGED="tauri://theme-changed",n.WINDOW_CREATED="tauri://window-created",n.WEBVIEW_CREATED="tauri://webview-created",n.DRAG="tauri://drag",n.DROP="tauri://drop",n.DROP_OVER="tauri://drop-over",n.DROP_CANCELLED="tauri://drag-cancelled"})(l||(l={}));async function J(n,e){await i("plugin:event|unlisten",{event:n,eventId:e})}async function p(n,e,t){var r;let a=typeof t?.target=="string"?{kind:"AnyLabel",label:t.target}:(r=t?.target)!==null&&r!==void 0?r:{kind:"Any"};return i("plugin:event|listen",{event:n,target:a,handler:T(e)}).then(u=>async()=>J(n,u))}async function w(n,e,t){return p(n,r=>{e(r),J(n,r.id).catch(()=>{})},t)}async function x(n,e){await i("plugin:event|emit",{event:n,payload:e})}async function P(n,e,t){await i("plugin:event|emit_to",{target:typeof n=="string"?{kind:"AnyLabel",label:n}:n,event:e,payload:t})}var F=class{constructor(e,t){this.type="Logical",this.width=e,this.height=t}},h=class{constructor(e,t){this.type="Physical",this.width=e,this.height=t}toLogical(e){return new F(this.width/e,this.height/e)}},O=class{constructor(e,t){this.type="Logical",this.x=e,this.y=t}},d=class{constructor(e,t){this.type="Physical",this.x=e,this.y=t}toLogical(e){return new O(this.x/e,this.y/e)}};var I=class n extends y{constructor(e){super(e)}static async new(e,t,r){return i("plugin:image|new",{rgba:g(e),width:t,height:r}).then(a=>new n(a))}static async fromBytes(e){return i("plugin:image|from_bytes",{bytes:g(e)}).then(t=>new n(t))}static async fromPath(e){return i("plugin:image|from_path",{path:e}).then(t=>new n(t))}async rgba(){return i("plugin:image|rgba",{rid:this.rid}).then(e=>new Uint8Array(e))}async size(){return i("plugin:image|size",{rid:this.rid})}};function g(n){return n==null?null:typeof n=="string"?n:n instanceof Uint8Array?Array.from(n):n instanceof ArrayBuffer?Array.from(new Uint8Array(n)):n instanceof I?n.rid:n}var V;(function(n){n[n.Critical=1]="Critical",n[n.Informational=2]="Informational"})(V||(V={}));var G=class{constructor(e){this._preventDefault=!1,this.event=e.event,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}},K;(function(n){n.None="none",n.Normal="normal",n.Indeterminate="indeterminate",n.Paused="paused",n.Error="error"})(K||(K={}));function H(){return new m(window.__TAURI_INTERNALS__.metadata.currentWindow.label,{skip:!0})}function z(){return window.__TAURI_INTERNALS__.metadata.windows.map(n=>new m(n.label,{skip:!0}))}var U=["tauri://created","tauri://error"],m=class{constructor(e,t={}){var r;this.label=e,this.listeners=Object.create(null),t?.skip||i("plugin:window|create",{options:{...t,parent:typeof t.parent=="string"?t.parent:(r=t.parent)===null||r===void 0?void 0:r.label,label:e}}).then(async()=>this.emit("tauri://created")).catch(async a=>this.emit("tauri://error",a))}static getByLabel(e){var t;return(t=z().find(r=>r.label===e))!==null&&t!==void 0?t:null}static getCurrent(){return H()}static getAll(){return z()}static async getFocusedWindow(){for(let e of z())if(await e.isFocused())return e;return null}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(t),1)}):p(e,t,{target:{kind:"Window",label:this.label}})}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(t),1)}):w(e,t,{target:{kind:"Window",label:this.label}})}async emit(e,t){if(U.includes(e)){for(let r of this.listeners[e]||[])r({event:e,id:-1,payload:t});return Promise.resolve()}return x(e,t)}async emitTo(e,t,r){if(U.includes(t)){for(let a of this.listeners[t]||[])a({event:t,id:-1,payload:r});return Promise.resolve()}return P(e,t,r)}_handleTauriEvent(e,t){return U.includes(e)?(e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t],!0):!1}async scaleFactor(){return i("plugin:window|scale_factor",{label:this.label})}async innerPosition(){return i("plugin:window|inner_position",{label:this.label}).then(({x:e,y:t})=>new d(e,t))}async outerPosition(){return i("plugin:window|outer_position",{label:this.label}).then(({x:e,y:t})=>new d(e,t))}async innerSize(){return i("plugin:window|inner_size",{label:this.label}).then(({width:e,height:t})=>new h(e,t))}async outerSize(){return i("plugin:window|outer_size",{label:this.label}).then(({width:e,height:t})=>new h(e,t))}async isFullscreen(){return i("plugin:window|is_fullscreen",{label:this.label})}async isMinimized(){return i("plugin:window|is_minimized",{label:this.label})}async isMaximized(){return i("plugin:window|is_maximized",{label:this.label})}async isFocused(){return i("plugin:window|is_focused",{label:this.label})}async isDecorated(){return i("plugin:window|is_decorated",{label:this.label})}async isResizable(){return i("plugin:window|is_resizable",{label:this.label})}async isMaximizable(){return i("plugin:window|is_maximizable",{label:this.label})}async isMinimizable(){return i("plugin:window|is_minimizable",{label:this.label})}async isClosable(){return i("plugin:window|is_closable",{label:this.label})}async isVisible(){return i("plugin:window|is_visible",{label:this.label})}async title(){return i("plugin:window|title",{label:this.label})}async theme(){return i("plugin:window|theme",{label:this.label})}async center(){return i("plugin:window|center",{label:this.label})}async requestUserAttention(e){let t=null;return e&&(e===V.Critical?t={type:"Critical"}:t={type:"Informational"}),i("plugin:window|request_user_attention",{label:this.label,value:t})}async setResizable(e){return i("plugin:window|set_resizable",{label:this.label,value:e})}async setMaximizable(e){return i("plugin:window|set_maximizable",{label:this.label,value:e})}async setMinimizable(e){return i("plugin:window|set_minimizable",{label:this.label,value:e})}async setClosable(e){return i("plugin:window|set_closable",{label:this.label,value:e})}async setTitle(e){return i("plugin:window|set_title",{label:this.label,value:e})}async maximize(){return i("plugin:window|maximize",{label:this.label})}async unmaximize(){return i("plugin:window|unmaximize",{label:this.label})}async toggleMaximize(){return i("plugin:window|toggle_maximize",{label:this.label})}async minimize(){return i("plugin:window|minimize",{label:this.label})}async unminimize(){return i("plugin:window|unminimize",{label:this.label})}async show(){return i("plugin:window|show",{label:this.label})}async hide(){return i("plugin:window|hide",{label:this.label})}async close(){return i("plugin:window|close",{label:this.label})}async destroy(){return i("plugin:window|destroy",{label:this.label})}async setDecorations(e){return i("plugin:window|set_decorations",{label:this.label,value:e})}async setShadow(e){return i("plugin:window|set_shadow",{label:this.label,value:e})}async setEffects(e){return i("plugin:window|set_effects",{label:this.label,value:e})}async clearEffects(){return i("plugin:window|set_effects",{label:this.label,value:null})}async setAlwaysOnTop(e){return i("plugin:window|set_always_on_top",{label:this.label,value:e})}async setAlwaysOnBottom(e){return i("plugin:window|set_always_on_bottom",{label:this.label,value:e})}async setContentProtected(e){return i("plugin:window|set_content_protected",{label:this.label,value:e})}async setSize(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");let t={};return t[`${e.type}`]={width:e.width,height:e.height},i("plugin:window|set_size",{label:this.label,value:t})}async setMinSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");let t=null;return e&&(t={},t[`${e.type}`]={width:e.width,height:e.height}),i("plugin:window|set_min_size",{label:this.label,value:t})}async setMaxSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");let t=null;return e&&(t={},t[`${e.type}`]={width:e.width,height:e.height}),i("plugin:window|set_max_size",{label:this.label,value:t})}async setPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");let t={};return t[`${e.type}`]={x:e.x,y:e.y},i("plugin:window|set_position",{label:this.label,value:t})}async setFullscreen(e){return i("plugin:window|set_fullscreen",{label:this.label,value:e})}async setFocus(){return i("plugin:window|set_focus",{label:this.label})}async setIcon(e){return i("plugin:window|set_icon",{label:this.label,value:g(e)})}async setSkipTaskbar(e){return i("plugin:window|set_skip_taskbar",{label:this.label,value:e})}async setCursorGrab(e){return i("plugin:window|set_cursor_grab",{label:this.label,value:e})}async setCursorVisible(e){return i("plugin:window|set_cursor_visible",{label:this.label,value:e})}async setCursorIcon(e){return i("plugin:window|set_cursor_icon",{label:this.label,value:e})}async setCursorPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");let t={};return t[`${e.type}`]={x:e.x,y:e.y},i("plugin:window|set_cursor_position",{label:this.label,value:t})}async setIgnoreCursorEvents(e){return i("plugin:window|set_ignore_cursor_events",{label:this.label,value:e})}async startDragging(){return i("plugin:window|start_dragging",{label:this.label})}async startResizeDragging(e){return i("plugin:window|start_resize_dragging",{label:this.label,value:e})}async setProgressBar(e){return i("plugin:window|set_progress_bar",{label:this.label,value:e})}async setVisibleOnAllWorkspaces(e){return i("plugin:window|set_visible_on_all_workspaces",{label:this.label,value:e})}async onResized(e){return this.listen(l.WINDOW_RESIZED,t=>{t.payload=he(t.payload),e(t)})}async onMoved(e){return this.listen(l.WINDOW_MOVED,t=>{t.payload=W(t.payload),e(t)})}async onCloseRequested(e){return this.listen(l.WINDOW_CLOSE_REQUESTED,t=>{let r=new G(t);Promise.resolve(e(r)).then(()=>{if(!r.isPreventDefault())return this.destroy()})})}async onDragDropEvent(e){let t=await this.listen(l.DRAG,s=>{e({...s,payload:{type:"dragged",paths:s.payload.paths,position:W(s.payload.position)}})}),r=await this.listen(l.DROP,s=>{e({...s,payload:{type:"dropped",paths:s.payload.paths,position:W(s.payload.position)}})}),a=await this.listen(l.DROP_OVER,s=>{e({...s,payload:{type:"dragOver",position:W(s.payload.position)}})}),u=await this.listen(l.DROP_CANCELLED,s=>{e({...s,payload:{type:"cancelled"}})});return()=>{t(),r(),a(),u()}}async onFocusChanged(e){let t=await this.listen(l.WINDOW_FOCUS,a=>{e({...a,payload:!0})}),r=await this.listen(l.WINDOW_BLUR,a=>{e({...a,payload:!1})});return()=>{t(),r()}}async onScaleChanged(e){return this.listen(l.WINDOW_SCALE_FACTOR_CHANGED,e)}async onThemeChanged(e){return this.listen(l.WINDOW_THEME_CHANGED,e)}},Z;(function(n){n.AppearanceBased="appearanceBased",n.Light="light",n.Dark="dark",n.MediumLight="mediumLight",n.UltraDark="ultraDark",n.Titlebar="titlebar",n.Selection="selection",n.Menu="menu",n.Popover="popover",n.Sidebar="sidebar",n.HeaderView="headerView",n.Sheet="sheet",n.WindowBackground="windowBackground",n.HudWindow="hudWindow",n.FullScreenUI="fullScreenUI",n.Tooltip="tooltip",n.ContentBackground="contentBackground",n.UnderWindowBackground="underWindowBackground",n.UnderPageBackground="underPageBackground",n.Mica="mica",n.Blur="blur",n.Acrylic="acrylic",n.Tabbed="tabbed",n.TabbedDark="tabbedDark",n.TabbedLight="tabbedLight"})(Z||(Z={}));var Y;(function(n){n.FollowsWindowActiveState="followsWindowActiveState",n.Active="active",n.Inactive="inactive"})(Y||(Y={}));function W(n){return new d(n.x,n.y)}function he(n){return new h(n.width,n.height)}function N(){return new v(H(),window.__TAURI_INTERNALS__.metadata.currentWebview.label,{skip:!0})}function X(){return window.__TAURI_INTERNALS__.metadata.webviews.map(n=>new v(m.getByLabel(n.windowLabel),n.label,{skip:!0}))}var j=["tauri://created","tauri://error"],v=class{constructor(e,t,r){this.window=e,this.label=t,this.listeners=Object.create(null),r?.skip||i("plugin:webview|create_webview",{windowLabel:e.label,label:t,options:r}).then(async()=>this.emit("tauri://created")).catch(async a=>this.emit("tauri://error",a))}static getByLabel(e){var t;return(t=X().find(r=>r.label===e))!==null&&t!==void 0?t:null}static getCurrent(){return N()}static getAll(){return X()}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(t),1)}):p(e,t,{target:{kind:"Webview",label:this.label}})}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(t),1)}):w(e,t,{target:{kind:"Webview",label:this.label}})}async emit(e,t){if(j.includes(e)){for(let r of this.listeners[e]||[])r({event:e,id:-1,payload:t});return Promise.resolve()}return x(e,t)}async emitTo(e,t,r){if(j.includes(t)){for(let a of this.listeners[t]||[])a({event:t,id:-1,payload:r});return Promise.resolve()}return P(e,t,r)}_handleTauriEvent(e,t){return j.includes(e)?(e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t],!0):!1}async position(){return i("plugin:webview|webview_position",{label:this.label}).then(({x:e,y:t})=>new d(e,t))}async size(){return i("plugin:webview|webview_size",{label:this.label}).then(({width:e,height:t})=>new h(e,t))}async close(){return i("plugin:webview|close",{label:this.label})}async setSize(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");let t={};return t[`${e.type}`]={width:e.width,height:e.height},i("plugin:webview|set_webview_size",{label:this.label,value:t})}async setPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");let t={};return t[`${e.type}`]={x:e.x,y:e.y},i("plugin:webview|set_webview_position",{label:this.label,value:t})}async setFocus(){return i("plugin:webview|set_webview_focus",{label:this.label})}async setZoom(e){return i("plugin:webview|set_webview_zoom",{label:this.label,value:e})}async reparent(e){return i("plugin:webview|set_webview_focus",{label:this.label,window:typeof e=="string"?e:e.label})}async onDragDropEvent(e){let t=await this.listen(l.DRAG,s=>{e({...s,payload:{type:"dragged",paths:s.payload.paths,position:$(s.payload.position)}})}),r=await this.listen(l.DROP,s=>{e({...s,payload:{type:"dropped",paths:s.payload.paths,position:$(s.payload.position)}})}),a=await this.listen(l.DROP_CANCELLED,s=>{e({...s,payload:{type:"dragOver",position:$(s.payload.position)}})}),u=await this.listen(l.DROP_CANCELLED,s=>{e({...s,payload:{type:"cancelled"}})});return()=>{t(),r(),a(),u()}}};function $(n){return new d(n.x,n.y)}function we(){let n=N();return new C(n.label,{skip:!0})}function B(){return window.__TAURI_INTERNALS__.metadata.webviews.map(n=>new C(n.label,{skip:!0}))}var C=class n{constructor(e,t={}){var r;this.label=e,this.listeners=Object.create(null),t?.skip||i("plugin:webview|create_webview_window",{options:{...t,parent:typeof t.parent=="string"?t.parent:(r=t.parent)===null||r===void 0?void 0:r.label,label:e}}).then(async()=>this.emit("tauri://created")).catch(async a=>this.emit("tauri://error",a))}static getByLabel(e){var t;let r=(t=B().find(a=>a.label===e))!==null&&t!==void 0?t:null;return r?new n(r.label,{skip:!0}):null}static getCurrent(){return we()}static getAll(){return B().map(e=>new n(e.label,{skip:!0}))}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(t),1)}):p(e,t,{target:{kind:"WebviewWindow",label:this.label}})}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(t),1)}):w(e,t,{target:{kind:"WebviewWindow",label:this.label}})}};ge(C,[m,v]);function ge(n,e){(Array.isArray(e)?e:[e]).forEach(t=>{Object.getOwnPropertyNames(t.prototype).forEach(r=>{var a;typeof n.prototype=="object"&&n.prototype&&r in n.prototype||Object.defineProperty(n.prototype,r,(a=Object.getOwnPropertyDescriptor(t.prototype,r))!==null&&a!==void 0?a:Object.create(null))})})}var ee;(function(n){n[n.Audio=1]="Audio",n[n.Cache=2]="Cache",n[n.Config=3]="Config",n[n.Data=4]="Data",n[n.LocalData=5]="LocalData",n[n.Document=6]="Document",n[n.Download=7]="Download",n[n.Picture=8]="Picture",n[n.Public=9]="Public",n[n.Video=10]="Video",n[n.Resource=11]="Resource",n[n.Temp=12]="Temp",n[n.AppConfig=13]="AppConfig",n[n.AppData=14]="AppData",n[n.AppLocalData=15]="AppLocalData",n[n.AppCache=16]="AppCache",n[n.AppLog=17]="AppLog",n[n.Desktop=18]="Desktop",n[n.Executable=19]="Executable",n[n.Font=20]="Font",n[n.Home=21]="Home",n[n.Runtime=22]="Runtime",n[n.Template=23]="Template"})(ee||(ee={}));var ke,ve;ke=new WeakMap,ve=new WeakMap;var Q;(function(n){n.Add="Add",n.Advanced="Advanced",n.Bluetooth="Bluetooth",n.Bookmarks="Bookmarks",n.Caution="Caution",n.ColorPanel="ColorPanel",n.ColumnView="ColumnView",n.Computer="Computer",n.EnterFullScreen="EnterFullScreen",n.Everyone="Everyone",n.ExitFullScreen="ExitFullScreen",n.FlowView="FlowView",n.Folder="Folder",n.FolderBurnable="FolderBurnable",n.FolderSmart="FolderSmart",n.FollowLinkFreestanding="FollowLinkFreestanding",n.FontPanel="FontPanel",n.GoLeft="GoLeft",n.GoRight="GoRight",n.Home="Home",n.IChatTheater="IChatTheater",n.IconView="IconView",n.Info="Info",n.InvalidDataFreestanding="InvalidDataFreestanding",n.LeftFacingTriangle="LeftFacingTriangle",n.ListView="ListView",n.LockLocked="LockLocked",n.LockUnlocked="LockUnlocked",n.MenuMixedState="MenuMixedState",n.MenuOnState="MenuOnState",n.MobileMe="MobileMe",n.MultipleDocuments="MultipleDocuments",n.Network="Network",n.Path="Path",n.PreferencesGeneral="PreferencesGeneral",n.QuickLook="QuickLook",n.RefreshFreestanding="RefreshFreestanding",n.Refresh="Refresh",n.Remove="Remove",n.RevealFreestanding="RevealFreestanding",n.RightFacingTriangle="RightFacingTriangle",n.Share="Share",n.Slideshow="Slideshow",n.SmartBadge="SmartBadge",n.StatusAvailable="StatusAvailable",n.StatusNone="StatusNone",n.StatusPartiallyAvailable="StatusPartiallyAvailable",n.StatusUnavailable="StatusUnavailable",n.StopProgressFreestanding="StopProgressFreestanding",n.StopProgress="StopProgress",n.TrashEmpty="TrashEmpty",n.TrashFull="TrashFull",n.User="User",n.UserAccounts="UserAccounts",n.UserGroup="UserGroup",n.UserGuest="UserGuest"})(Q||(Q={}));var o=M.invoke,ae=E.listen;async function zn(){return await o("show_window")}async function Un(){return await o("pick_and_load_waveform")}async function Vn(){return await o("load_file_with_selected_vars")}async function Gn(){return await o("get_hierarchy")}async function Hn(n,e,t,r,a,u){return await o("load_signal_and_get_timeline",{signal_ref_index:n,timeline_zoom:e,timeline_viewport_width:t,timeline_viewport_x:r,block_height:a,var_format:u})}async function jn(n){return await o("unload_signal",{signal_ref_index:n})}async function $n(n){return await o("send_char",{c:n})}async function Nn(n){return await o("add_decoders",{decoder_paths:n})}async function Qn(){return await o("remove_all_decoders")}async function qn(n){return await o("add_diagram_connectors",{diagram_connector_paths:n})}async function Jn(){return await o("remove_all_diagram_connectors")}async function Kn(n){return await ae("diagram_connector_message",e=>n(e.payload))}async function Zn(n){return await ae("term_content",e=>n(e.payload))}async function Yn(n,e,t){return await o("notify_diagram_connector_text_change",{diagram_connector:n,component_id:e,text:t})}async function Xn(){return await o("open_konata_file")}export{Nn as add_decoders,qn as add_diagram_connectors,Gn as get_hierarchy,Kn as listen_diagram_connectors_messages,Zn as listen_term_update,Vn as load_file_with_selected_vars,Hn as load_signal_and_get_timeline,Yn as notify_diagram_connector_text_change,Xn as open_konata_file,Un as pick_and_load_waveform,Qn as remove_all_decoders,Jn as remove_all_diagram_connectors,$n as send_char,zn as show_window,jn as unload_signal}; diff --git a/frontend/typescript/tauri_glue/tauri_glue.ts b/frontend/typescript/tauri_glue/tauri_glue.ts index 3f37098..3f64351 100644 --- a/frontend/typescript/tauri_glue/tauri_glue.ts +++ b/frontend/typescript/tauri_glue/tauri_glue.ts @@ -38,20 +38,20 @@ export async function get_hierarchy(): Promise { } export async function load_signal_and_get_timeline( - signal_ref_index: number, + signal_ref_index: number, timeline_zoom: number, timeline_viewport_width: number, - timeline_viewport_x: number, + timeline_viewport_x: number, block_height: number, var_format: VarFormat, ): Promise { - return await invoke("load_signal_and_get_timeline", { - signal_ref_index, - timeline_zoom, + return await invoke("load_signal_and_get_timeline", { + signal_ref_index, + timeline_zoom, timeline_viewport_width, timeline_viewport_x, - block_height, - var_format + block_height, + var_format }); } @@ -59,6 +59,10 @@ export async function unload_signal(signal_ref_index: number): Promise { return await invoke("unload_signal", { signal_ref_index }); } +export async function send_char(c : string): Promise { + return await invoke("send_char", { c }); +} + export async function add_decoders(decoder_paths: Array): Promise { return await invoke("add_decoders", { decoder_paths }); } @@ -79,6 +83,10 @@ export async function listen_diagram_connectors_messages(on_message: (message: a return await listen("diagram_connector_message", (message) => on_message(message.payload)); } +export async function listen_term_update(on_message: (message: any) => void) { + return await listen("term_content", (message) => on_message(message.payload)); +} + export async function notify_diagram_connector_text_change(diagram_connector: DiagramConnectorName, component_id: ComponentId, text: string): Promise { return await invoke("notify_diagram_connector_text_change", { diagram_connector, component_id, text }); } diff --git a/shared/src/lib.rs b/shared/src/lib.rs index 3d07aaf..6bc697c 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -7,6 +7,7 @@ mod signal_to_timeline; pub use signal_to_timeline::signal_to_timeline; pub mod wellen_helpers; +pub mod term; #[derive(Serialize, Deserialize, Debug, Default)] #[serde(crate = "serde")] diff --git a/shared/src/term.rs b/shared/src/term.rs new file mode 100644 index 0000000..1f2c2b9 --- /dev/null +++ b/shared/src/term.rs @@ -0,0 +1,25 @@ +use moonlight::*; + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(crate = "serde")] +pub enum TerminalUpMsg { + RequestFullTermState, + RequestIncrementalTermStateUpdate, + SendCharacter(char), +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(crate = "serde")] +pub enum TerminalDownMsg { + FullTermUpdate(TerminalScreen), + BackendTermStartFailure(String), + TermNotStarted +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +#[serde(crate = "serde")] +pub struct TerminalScreen { + pub cols : u16, + pub rows : u16, + pub content : String, +} diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 23c8d60..4917733 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -17,6 +17,7 @@ tauri-build = { version = "2.0.3", features = [] } [dependencies] wellen.workspace = true +alacritty_terminal = { git = "https://github.com/alacritty/alacritty", rev = "cacdb5bb3b72bad2c729227537979d95af75978f" } shared = { path = "../shared", features = ["backend"] } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } diff --git a/src-tauri/f.txt b/src-tauri/f.txt new file mode 100644 index 0000000..e009d34 --- /dev/null +++ b/src-tauri/f.txt @@ -0,0 +1,2 @@ +Hello, I'm typing a file in vim!! + diff --git a/src-tauri/src/aterm.rs b/src-tauri/src/aterm.rs new file mode 100644 index 0000000..45b68a6 --- /dev/null +++ b/src-tauri/src/aterm.rs @@ -0,0 +1,116 @@ +use std::result; +use std::sync::{mpsc, Arc}; + +use alacritty_terminal::event::{Event, EventListener}; +use alacritty_terminal::event_loop::{EventLoop, Notifier}; +use alacritty_terminal::sync::FairMutex; +use alacritty_terminal::term::{self, Term}; +use alacritty_terminal::term::cell::Cell; +use alacritty_terminal::{tty, Grid}; +use tauri::Emitter; +use shared::term::{TerminalDownMsg, TerminalScreen}; + +use crate::terminal_size; + +#[derive(Clone)] +pub struct EventProxy(mpsc::Sender); +impl EventListener for EventProxy { + fn send_event(&self, event: Event) { + let _ = self.0.send(event.clone()); + } +} + +pub struct ATerm { + pub term: Arc>>, + + pub rows : u16, + pub cols : u16, + + /// Use tx to write things to terminal instance from outside world + pub tx: Notifier, + + /// Use rx to read things from terminal instance. + /// Rx only has data when terminal state has changed, + /// otherwise, `std::sync::mpsc::recv` will block and sleep + /// until there is data. + pub rx: mpsc::Receiver<(u64, Event)>, +} + +impl ATerm { + pub fn new() -> result::Result { + let (rows, cols) = (21, 85); + let id = 1; + let pty_config = tty::Options { + shell: Some(tty::Shell::new("/bin/bash".to_string(), vec![])), + ..tty::Options::default() + }; + let config = term::Config::default(); + let terminal_size = terminal_size::TerminalSize::new(rows, cols); + let pty = tty::new(&pty_config, terminal_size.into(), id)?; + let (event_sender, event_receiver) = mpsc::channel(); + let event_proxy = EventProxy(event_sender); + let term = Term::new::( + config, + &terminal_size.into(), + event_proxy.clone(), + ); + let term = Arc::new(FairMutex::new(term)); + let pty_event_loop = EventLoop::new(term.clone(), event_proxy, pty, false, false)?; + let notifier = Notifier(pty_event_loop.channel()); + let (pty_proxy_sender, pty_proxy_receiver) = std::sync::mpsc::channel(); + // Start pty event loop + pty_event_loop.spawn(); + std::thread::Builder::new() + .name(format!("pty_event_subscription_{}", id)) + .spawn(move || loop { + if let Ok(event) = event_receiver.recv() { + if let Event::Exit = event { + break; + } + else { + if let Some(app_handle) = crate::APP_HANDLE.read().unwrap().clone() { + let term = crate::TERM.lock().unwrap(); + let content = terminal_instance_to_string(&term); + let payload = TerminalScreen { + cols: term.cols, + rows: term.rows, + content: content + }; + let payload = TerminalDownMsg::FullTermUpdate(payload); + let payload = serde_json::json!(payload); + app_handle.emit("term_content", payload).unwrap(); + } + } + } + })?; + Ok(ATerm { + term, + rows, + cols, + tx: notifier, + rx: pty_proxy_receiver, + }) + } +} + +pub fn terminal_instance_to_string(terminal_instance: &ATerm) -> String { + let (rows, cols) = (terminal_instance.rows, terminal_instance.cols); + let term = terminal_instance.term.lock(); + let grid = term.grid().clone(); + + return term_grid_to_string(&grid, rows, cols); +} + +fn term_grid_to_string(grid: &Grid, rows: u16, cols: u16) -> String { + let mut term_content = String::with_capacity((rows*cols) as usize); + + // Populate string from grid + for indexed in grid.display_iter() { + let x = indexed.point.column.0 as usize; + let y = indexed.point.line.0 as usize; + if y < rows as usize && x < cols as usize { + term_content.push(indexed.c); + } + } + return term_content; +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 270ef68..bac3a91 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -8,6 +8,7 @@ use tauri_plugin_dialog::DialogExt; use tokio::time::sleep; use wasmtime::AsContextMut; use wellen::simple::Waveform; +use tauri::Emitter; type Filename = String; type JavascriptCode = String; @@ -21,15 +22,25 @@ type RemovedDiagramConnectorsCount = usize; type DiagramConnectorPath = String; type DiagramConnectorName = String; type ComponentId = String; +use alacritty_terminal::event::Notify; +use shared::term::{TerminalDownMsg, TerminalScreen}; mod component_manager; +mod aterm; +mod terminal_size; +use std::sync::Mutex; pub static APP_HANDLE: Lazy>>> = Lazy::new(<_>::default); pub static WAVEFORM: Lazy>>>> = Lazy::new(<_>::default); +static TERM: Lazy> = Lazy::new(|| { + Mutex::new(aterm::ATerm::new().expect("Failed to initialize ATerm")) +}); + #[derive(Default)] struct Store { waveform: Arc>>, + val : Arc>, } #[tauri::command(rename_all = "snake_case")] @@ -144,6 +155,17 @@ async fn unload_signal(signal_ref_index: usize, store: tauri::State<'_, Store>) Ok(()) } +#[tauri::command(rename_all = "snake_case")] +async fn send_char(c : String) -> Result<(), ()> { + if c.len() == 1 { + let term = TERM.lock().unwrap(); + term.tx.notify(c.into_bytes()); + Ok(()) + } else { + Err(()) + } +} + #[tauri::command(rename_all = "snake_case")] async fn add_decoders(decoder_paths: Vec) -> Result { Ok(component_manager::decoders::add_decoders(decoder_paths).await) @@ -279,6 +301,7 @@ pub fn run() { get_hierarchy, load_signal_and_get_timeline, unload_signal, + send_char, add_decoders, remove_all_decoders, add_diagram_connectors, @@ -288,6 +311,27 @@ pub fn run() { ]) .setup(|app| { *APP_HANDLE.write().unwrap() = Some(app.handle().to_owned()); + println!("Setting up yay!"); + + std::thread::spawn(move || { + // Simulate emitting a message after a delay + std::thread::sleep(std::time::Duration::from_secs(1)); + + //tart term and send initial update to backend + if let Some(app_handle) = crate::APP_HANDLE.read().unwrap().clone() { + let term = crate::TERM.lock().unwrap(); + let content = crate::aterm::terminal_instance_to_string(&term); + let payload = TerminalScreen { + cols: term.cols, + rows: term.rows, + content: content + }; + let payload = TerminalDownMsg::FullTermUpdate(payload); + let payload = serde_json::json!(payload); + app_handle.emit("term_content", payload).unwrap(); + } + }); + Ok(()) }) .run(tauri::generate_context!()) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 69c3a72..6203dc8 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,6 +1,8 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +mod terminal_size; + fn main() { app_lib::run(); } diff --git a/src-tauri/src/terminal_size.rs b/src-tauri/src/terminal_size.rs new file mode 100644 index 0000000..055cb07 --- /dev/null +++ b/src-tauri/src/terminal_size.rs @@ -0,0 +1,55 @@ +use alacritty_terminal::event::{WindowSize}; +use alacritty_terminal::grid::{Dimensions}; +use alacritty_terminal::index::{Column, Line}; + +#[derive(Clone, Copy, Debug)] +pub struct TerminalSize { + pub cell_width: u16, + pub cell_height: u16, + pub num_cols: u16, + pub num_lines: u16, +} + +impl TerminalSize { + pub fn new(rows : u16, cols : u16) -> Self { + Self { + cell_width: 1, + cell_height: 1, + num_cols: cols, + num_lines: rows, + } + } +} + +impl Dimensions for TerminalSize { + fn total_lines(&self) -> usize { + self.screen_lines() + } + + fn screen_lines(&self) -> usize { + self.num_lines as usize + } + + fn columns(&self) -> usize { + self.num_cols as usize + } + + fn last_column(&self) -> Column { + Column(self.num_cols as usize - 1) + } + + fn bottommost_line(&self) -> Line { + Line(self.num_lines as i32 - 1) + } +} + +impl From for WindowSize { + fn from(size: TerminalSize) -> Self { + Self { + num_lines: size.num_lines, + num_cols: size.num_cols, + cell_width: size.cell_width, + cell_height: size.cell_height, + } + } +}