Initial web studio functionality
This commit is contained in:
parent
82af952962
commit
f25366e349
80 changed files with 5097 additions and 267 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -46,6 +46,7 @@ yalc.lock
|
|||
/backend/target
|
||||
/backend/out
|
||||
/bin
|
||||
/**/target/
|
||||
|
||||
# packaged teasers
|
||||
*.zip
|
||||
|
|
460
backend/Cargo.lock
generated
460
backend/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -8,6 +8,7 @@ license = "MIT"
|
|||
repository = "https://github.com/NGnius/kaylon"
|
||||
|
||||
[dependencies]
|
||||
caylon-config = { version = "0.1.0", path = "./caylon-config" }
|
||||
usdpl-back = { version = "0.10.0"}
|
||||
|
||||
clap = { version = "3.2", features = ["derive", "std"], default-features = false }
|
||||
|
|
105
backend/caylon-config/Cargo.lock
generated
Normal file
105
backend/caylon-config/Cargo.lock
generated
Normal file
|
@ -0,0 +1,105 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "caylon-config"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
15
backend/caylon-config/Cargo.toml
Normal file
15
backend/caylon-config/Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "caylon-config"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0" }
|
||||
|
||||
log = { version = "0.4" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct AboutConfig {
|
||||
pub name: String,
|
||||
pub version: String,
|
|
@ -1,6 +1,6 @@
|
|||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[serde(tag = "action")]
|
||||
pub enum TopLevelActionConfig {
|
||||
#[serde(rename = "sequence")]
|
||||
|
@ -17,7 +17,7 @@ pub enum TopLevelActionConfig {
|
|||
Json(JsonAction),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[serde(tag = "action")]
|
||||
pub enum ActionConfig {
|
||||
#[serde(rename = "command")]
|
||||
|
@ -30,25 +30,25 @@ pub enum ActionConfig {
|
|||
Json(JsonAction),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct SequenceAction {
|
||||
pub steps: Vec<ActionConfig>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct CommandAction {
|
||||
pub run: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct MirrorAction;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct JavascriptAction {
|
||||
pub run: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct JsonAction {
|
||||
pub jmespath: String,
|
||||
}
|
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
|||
|
||||
use super::{ElementConfig, AboutConfig};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[serde(tag = "api-version")]
|
||||
pub enum BaseConfig {
|
||||
#[serde(rename = "v0.0.0")]
|
||||
|
@ -55,4 +55,8 @@ impl BaseConfig {
|
|||
Self::V0 {items, ..} => items,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assemble(items: Vec<ElementConfig>, about: AboutConfig) -> Self {
|
||||
Self::V0 { items, about }
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
|||
|
||||
use super::TopLevelActionConfig;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct ButtonConfig {
|
||||
pub title: String,
|
||||
pub on_click: TopLevelActionConfig,
|
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
|||
|
||||
use super::{ButtonConfig, ToggleConfig, SliderConfig, ReadingConfig, ResultDisplayConfig, EventDisplayConfig};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[serde(tag = "element")]
|
||||
pub enum ElementConfig {
|
||||
#[serde(rename = "button")]
|
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
|||
|
||||
use super::TopLevelActionConfig;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct EventDisplayConfig {
|
||||
pub title: String,
|
||||
/// Type of event to listen for
|
||||
|
@ -11,7 +11,7 @@ pub struct EventDisplayConfig {
|
|||
pub on_event: TopLevelActionConfig,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub enum EventType {
|
||||
#[serde(rename = "achievement")]
|
||||
Achievement,
|
|
@ -25,7 +25,7 @@ pub use transformer::{TransformAction, TransformTypeAction, ReplaceTransformActi
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
|
||||
#[test]
|
||||
fn dump_test() {
|
||||
let conf = BaseConfig::V0 {
|
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
|||
|
||||
use super::TopLevelActionConfig;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct ReadingConfig {
|
||||
pub title: String,
|
||||
/// Period in milliseconds, or None/null for non-repeating actions
|
|
@ -1,6 +1,6 @@
|
|||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct ResultDisplayConfig {
|
||||
pub title: String,
|
||||
/// Index of element who's action's result will be used
|
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
|||
|
||||
use super::TopLevelActionConfig;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct SliderConfig {
|
||||
pub title: String,
|
||||
pub min: u64,
|
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
|||
|
||||
use super::TopLevelActionConfig;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct ToggleConfig {
|
||||
pub title: String,
|
||||
pub description: Option<String>,
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct TransformAction {
|
||||
pub transformer: TransformTypeAction,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[serde(tag = "rule")]
|
||||
pub enum TransformTypeAction {
|
||||
#[serde(rename = "replace")]
|
||||
|
@ -18,13 +18,13 @@ pub enum TransformTypeAction {
|
|||
Log(LogTransformAction),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct ReplaceTransformAction {
|
||||
/// Regex
|
||||
pub patterns: Vec<PatternConfig>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct PatternConfig {
|
||||
/// Regex
|
||||
pub pattern: String,
|
||||
|
@ -43,17 +43,17 @@ pub struct PatternConfig {
|
|||
pub x: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct ExpandTransformAction {
|
||||
pub format: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct LogTransformAction {
|
||||
pub level: LogLevel,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub enum LogLevel {
|
||||
DEBUG,
|
||||
INFO,
|
2
backend/caylon-studio/.gitignore
vendored
Normal file
2
backend/caylon-studio/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/dist/
|
||||
/target/
|
2265
backend/caylon-studio/Cargo.lock
generated
Normal file
2265
backend/caylon-studio/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
41
backend/caylon-studio/Cargo.toml
Normal file
41
backend/caylon-studio/Cargo.toml
Normal file
|
@ -0,0 +1,41 @@
|
|||
[package]
|
||||
name = "caylon-studio"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Template for starting a Yew project using Trunk"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/yewstack/yew-trunk-minimal-template"
|
||||
license = "MIT"
|
||||
keywords = ["yew", "trunk"]
|
||||
categories = ["gui", "wasm", "web-programming"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[[bin]]
|
||||
name = "frontend"
|
||||
|
||||
[[bin]]
|
||||
name = "backend"
|
||||
|
||||
[dependencies]
|
||||
caylon-config = { version = "0.1.0", path = "../caylon-config" }
|
||||
reqwest = { version = "0.11.8", features = ["json"] }
|
||||
serde = { version = "1.0.132", features = ["derive"] }
|
||||
serde_json = { version = "1.0" }
|
||||
uuid = { version = "1.0.0", features = ["serde"] }
|
||||
futures = "0.3"
|
||||
bytes = "1.0"
|
||||
log = "0.4"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
yew = { version = "0.20", features = [ "csr", "hydration" ] }
|
||||
wasm-bindgen-futures = "0.4"
|
||||
wasm-log = "0.3"
|
||||
web-sys = { version = "0.3", features = [ "HtmlSelectElement", "HtmlInputElement" ] }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
yew = { version = "0.20", features = [ "ssr" ] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
actix-web = { version = "4.3" }
|
||||
actix-files = { version = "0.6" }
|
||||
clap = { version = "3.1.7", features = ["derive"] }
|
||||
simplelog = { version = "0.12" }
|
5
backend/caylon-studio/README.md
Normal file
5
backend/caylon-studio/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# caylon-studio
|
||||
|
||||
## Running
|
||||
|
||||
Execute `run.sh` in your terminal. You will need cargo, Rust, and the wasm toolchain installed.
|
14
backend/caylon-studio/index.html
Normal file
14
backend/caylon-studio/index.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Caylon Studio</title>
|
||||
<link data-trunk rel="rust" data-bin="frontend" />
|
||||
<link data-trunk rel="sass" href="index.scss" />
|
||||
<!--<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/bulma@0.9.0/css/bulma.min.css"
|
||||
/>-->
|
||||
</head>
|
||||
</html>
|
290
backend/caylon-studio/index.scss
Normal file
290
backend/caylon-studio/index.scss
Normal file
|
@ -0,0 +1,290 @@
|
|||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
width: 98%;
|
||||
margin: 0;
|
||||
padding: 1%;
|
||||
}
|
||||
|
||||
div.work-view {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.json-view {
|
||||
padding: 0;
|
||||
margin: auto 0;
|
||||
display: inline-block;
|
||||
width: 40%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.json-input {
|
||||
margin: auto;
|
||||
width: 98%;
|
||||
}
|
||||
|
||||
div.elements-view {
|
||||
padding: 0;
|
||||
margin: auto;
|
||||
display: inline-block;
|
||||
width: 60%;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* Element styling */
|
||||
|
||||
// *
|
||||
|
||||
div.caylon-element {}
|
||||
|
||||
// actions/*
|
||||
|
||||
div.caylon-action-config {}
|
||||
|
||||
// actions/command.rs
|
||||
|
||||
div.caylon-command-action-edit {}
|
||||
|
||||
// actions/javascript.rs
|
||||
|
||||
div.caylon-javascript-action-edit {}
|
||||
|
||||
// actions/json.rs
|
||||
|
||||
div.caylon-json-action-edit {}
|
||||
|
||||
// actions/mirror.rs
|
||||
|
||||
div.caylon-mirror-action-edit {}
|
||||
|
||||
// actions/transform.rs
|
||||
|
||||
div.caylon-transformer-replace-item {}
|
||||
|
||||
div.caylon-transformer-replace {}
|
||||
|
||||
div.caylon-transformer-expand {}
|
||||
|
||||
div.caylon-transformer-log {}
|
||||
|
||||
div.caylon-transformer-action-edit {}
|
||||
|
||||
div.caylon-transformer-type-action-edit {}
|
||||
|
||||
// add_element.rs
|
||||
|
||||
div.add-element {}
|
||||
|
||||
button.add-element-button {}
|
||||
|
||||
// button.rs
|
||||
|
||||
div.caylon-button {}
|
||||
|
||||
// fake/button.rs
|
||||
|
||||
button.fake-button {}
|
||||
|
||||
// edit/*
|
||||
|
||||
div.caylon-edit {}
|
||||
|
||||
label.caylon-label-edit {}
|
||||
|
||||
input.caylon-input-editor {}
|
||||
|
||||
// edit/action_editor.rs
|
||||
|
||||
div.caylon-action-edit {}
|
||||
|
||||
div.caylon-sequence-action-edit {}
|
||||
|
||||
div.caylon-sequence-command-action {}
|
||||
|
||||
div.caylon-sequence-transform-action {}
|
||||
|
||||
div.caylon-sequence-javascript-action {}
|
||||
|
||||
div.caylon-sequence-json-action {}
|
||||
|
||||
div.caylon-sequence-action-edit-item {}
|
||||
|
||||
div.caylon-sequence-action-edit {}
|
||||
|
||||
div.caylon-action-config {}
|
||||
|
||||
div.caylon-editor {}
|
||||
|
||||
// edit/always_str.rs
|
||||
|
||||
div.caylon-always-str-edit {}
|
||||
|
||||
input.caylon-always-str-input {}
|
||||
|
||||
// edit/always_usize.rs
|
||||
|
||||
div.caylon-always-usize-edit {}
|
||||
|
||||
input.caylon-always-usize-input {}
|
||||
|
||||
// edit/optional_str.rs
|
||||
|
||||
div.caylon-option-str-edit {}
|
||||
|
||||
input.caylon-option-str-input {}
|
||||
|
||||
// edit/optional_u64.rs
|
||||
|
||||
div.caylon-option-u64-edit {}
|
||||
|
||||
input.caylon-option-u64-input {}
|
||||
|
||||
// elements.rs
|
||||
|
||||
div.elements-view {}
|
||||
|
||||
div.elements-toolbar {}
|
||||
|
||||
div.elements-list {}
|
||||
|
||||
div.elements-item {}
|
||||
|
||||
// event_display.rs
|
||||
|
||||
div.caylon-event-display {}
|
||||
|
||||
// reading_display.rs
|
||||
|
||||
div.caylon-reading-display {}
|
||||
|
||||
// reading_display.rs
|
||||
|
||||
div.caylon-reading-display {}
|
||||
|
||||
// remove_element.rs
|
||||
|
||||
div.remove-element {}
|
||||
|
||||
button.remove-element-button {}
|
||||
|
||||
// result_display.rs
|
||||
|
||||
div.caylon-result-display {}
|
||||
|
||||
// fake/display.rs
|
||||
|
||||
div.fake-slider {}
|
||||
|
||||
span.fake-slider-title {}
|
||||
|
||||
input.fake-slider-content {}
|
||||
|
||||
// slider.rs
|
||||
|
||||
div.caylon-slider {}
|
||||
|
||||
// fake/slider.rs
|
||||
|
||||
div.fake-slider {}
|
||||
|
||||
span.fake-slider-title {}
|
||||
|
||||
input.fake-slider-input {}
|
||||
|
||||
// toggle.rs
|
||||
|
||||
div.caylon-toggle {}
|
||||
|
||||
// fake/toggle.rs
|
||||
// adapted from https://www.w3schools.com/howto/howto_css_switch.asp
|
||||
.fake-toggle-button {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.fake-toggle-button input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.toggle-round {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.toggle-round:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
input:checked + .toggle-round {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
input:focus + .toggle-round {
|
||||
box-shadow: 0 0 1px #2196F3;
|
||||
}
|
||||
|
||||
input:checked + .toggle-round:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
.toggle-round {
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
.toggle-round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* End element styling */
|
||||
|
||||
div.footer {
|
||||
width: 98%;
|
||||
height: 5%;
|
||||
margin: auto;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.footer-elem {
|
||||
display: inline-block;
|
||||
padding: 0% 4%;
|
||||
}
|
||||
|
||||
div.header {
|
||||
width: 98%;
|
||||
height: 10%;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header-elem {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.hit-count {
|
||||
margin: auto;
|
||||
}
|
3
backend/caylon-studio/run.sh
Executable file
3
backend/caylon-studio/run.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
trunk build ./index.html && cargo run --bin backend
|
9
backend/caylon-studio/src/api/get_hits.rs
Normal file
9
backend/caylon-studio/src/api/get_hits.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use actix_web::{get, web, Responder};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
pub(crate) static INDEX_HITS: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
#[get("/stats/hits")]
|
||||
pub async fn hits() -> impl Responder {
|
||||
web::Json(INDEX_HITS.load(Ordering::Relaxed))
|
||||
}
|
43
backend/caylon-studio/src/api/get_index.rs
Normal file
43
backend/caylon-studio/src/api/get_index.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use actix_web::{get, web, HttpResponse, Responder};
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures::stream::{self, Stream, StreamExt};
|
||||
|
||||
type BoxedError = Box<dyn std::error::Error + 'static>;
|
||||
|
||||
pub struct IndexPage {
|
||||
before: String,
|
||||
after: String,
|
||||
}
|
||||
|
||||
impl IndexPage {
|
||||
async fn render(&self) -> impl Stream<Item = Result<Bytes, BoxedError>> + Send {
|
||||
let renderer = yew::ServerRenderer::<crate::ui::App>::new();
|
||||
let before = self.before.clone();
|
||||
let after = self.after.clone();
|
||||
|
||||
stream::once(async move { before })
|
||||
.chain(renderer.render_stream())
|
||||
.chain(stream::once(async move { after }))
|
||||
.map(|m| Result::<_, BoxedError>::Ok(m.into()))
|
||||
}
|
||||
|
||||
pub fn load(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
|
||||
let index_html = std::fs::read_to_string(path.as_ref())?;
|
||||
let (index_before, index_after) = index_html.split_once("<body>").unwrap().to_owned();
|
||||
let (mut index_before, index_after) = (index_before.to_owned(), index_after.to_owned());
|
||||
index_before.push_str("<body>");
|
||||
log::trace!("<body> before: {}", index_before);
|
||||
log::trace!("<body> after: {}", index_after);
|
||||
Ok(Self {
|
||||
before: index_before.to_owned(),
|
||||
after: index_after.to_owned(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
pub async fn index(page: web::Data<IndexPage>) -> impl Responder {
|
||||
super::get_hits::INDEX_HITS.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
|
||||
HttpResponse::Ok().streaming(page.render().await)
|
||||
}
|
8
backend/caylon-studio/src/api/get_resources.rs
Normal file
8
backend/caylon-studio/src/api/get_resources.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use actix_web::{get, web, Responder};
|
||||
|
||||
#[get("/{name}")]
|
||||
pub async fn resource(path: web::Path<String>) -> impl Responder {
|
||||
//println!("GET resource {}", path);
|
||||
let filepath = std::path::PathBuf::from("dist").join(&*path);
|
||||
actix_files::NamedFile::open_async(filepath).await
|
||||
}
|
7
backend/caylon-studio/src/api/mod.rs
Normal file
7
backend/caylon-studio/src/api/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
pub(crate) mod get_hits;
|
||||
mod get_index;
|
||||
mod get_resources;
|
||||
|
||||
pub use get_hits::hits;
|
||||
pub use get_index::{index, IndexPage};
|
||||
pub use get_resources::resource;
|
27
backend/caylon-studio/src/bin/backend.rs
Normal file
27
backend/caylon-studio/src/bin/backend.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use actix_web::{web, App, HttpServer};
|
||||
|
||||
use simplelog::{ColorChoice, LevelFilter, TermLogger, TerminalMode};
|
||||
|
||||
use caylon_studio::api::{hits, index, resource, IndexPage};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
TermLogger::init(
|
||||
LevelFilter::Debug,
|
||||
Default::default(),
|
||||
TerminalMode::Mixed,
|
||||
ColorChoice::Auto,
|
||||
)
|
||||
.unwrap();
|
||||
log::info!("Starting HTTP server @ 127.0.0.1:8080");
|
||||
HttpServer::new(|| {
|
||||
App::new()
|
||||
.app_data(web::Data::new(IndexPage::load("dist/index.html").unwrap()))
|
||||
.service(index)
|
||||
.service(resource)
|
||||
.service(hits)
|
||||
})
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.run()
|
||||
.await
|
||||
}
|
13
backend/caylon-studio/src/bin/frontend.rs
Normal file
13
backend/caylon-studio/src/bin/frontend.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use caylon_studio::ui::App;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn main() {
|
||||
wasm_log::init(wasm_log::Config::default());
|
||||
log::info!("Hydrating UI");
|
||||
yew::Renderer::<App>::new().hydrate();
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn main() {
|
||||
compile_error!("frontend is for browsers (wasm32)");
|
||||
}
|
3
backend/caylon-studio/src/lib.rs
Normal file
3
backend/caylon-studio/src/lib.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod api;
|
||||
pub mod ui;
|
26
backend/caylon-studio/src/ui/app.rs
Normal file
26
backend/caylon-studio/src/ui/app.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use super::element::ElementsComponent;
|
||||
use super::FooterComponent;
|
||||
use super::JsonViewComponent;
|
||||
use super::TitleComponent;
|
||||
use super::{JsonContext, JsonCtx};
|
||||
|
||||
#[function_component(App)]
|
||||
pub fn app() -> Html {
|
||||
let json_ctx = use_reducer(JsonCtx::init);
|
||||
log::debug!("App render");
|
||||
html! {
|
||||
<main>
|
||||
<TitleComponent />
|
||||
<ContextProvider<JsonContext> context={json_ctx}>
|
||||
<div class={classes!("work-view")}>
|
||||
<JsonViewComponent />
|
||||
<ElementsComponent />
|
||||
</div>
|
||||
//<h1>{ "Hello World!" }</h1>
|
||||
</ContextProvider<JsonContext>>
|
||||
<FooterComponent />
|
||||
</main>
|
||||
}
|
||||
}
|
194
backend/caylon-studio/src/ui/element/add_element.rs
Normal file
194
backend/caylon-studio/src/ui/element/add_element.rs
Normal file
|
@ -0,0 +1,194 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use super::super::{JsonContext, JsonCtxAction};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct AddElementProps {
|
||||
pub json_ctx: JsonContext,
|
||||
}
|
||||
|
||||
pub enum AddElementMsg {
|
||||
SelectCb(SelectedElementType),
|
||||
AddClick,
|
||||
ReDraw,
|
||||
NoOp,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum SelectedElementType {
|
||||
Button,
|
||||
Toggle,
|
||||
Slider,
|
||||
ReadingDisplay,
|
||||
ResultDisplay,
|
||||
EventDisplay,
|
||||
}
|
||||
|
||||
pub struct AddElementComponent {
|
||||
selected: SelectedElementType,
|
||||
}
|
||||
|
||||
impl Component for AddElementComponent {
|
||||
type Message = AddElementMsg;
|
||||
type Properties = AddElementProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self {
|
||||
selected: SelectedElementType::EventDisplay, // should be last <option> in <select>
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
AddElementMsg::SelectCb(elem) => {
|
||||
self.selected = elem;
|
||||
false
|
||||
}
|
||||
AddElementMsg::AddClick => {
|
||||
let next_index = ctx.props().json_ctx.json.items().len();
|
||||
let item = match &self.selected {
|
||||
SelectedElementType::Button => {
|
||||
caylon_config::ElementConfig::Button(caylon_config::ButtonConfig {
|
||||
title: format!("Element {}", next_index),
|
||||
on_click: default_top_level_action(),
|
||||
})
|
||||
}
|
||||
SelectedElementType::Toggle => {
|
||||
caylon_config::ElementConfig::Toggle(caylon_config::ToggleConfig {
|
||||
title: format!("Element {}", next_index),
|
||||
description: None, // TODO
|
||||
on_toggle: default_top_level_action(),
|
||||
})
|
||||
}
|
||||
SelectedElementType::Slider => {
|
||||
caylon_config::ElementConfig::Slider(caylon_config::SliderConfig {
|
||||
title: format!("Element {}", next_index),
|
||||
min: 0,
|
||||
max: 10,
|
||||
notches: None,
|
||||
on_set: default_top_level_action(),
|
||||
})
|
||||
}
|
||||
SelectedElementType::ReadingDisplay => {
|
||||
caylon_config::ElementConfig::ReadingDisplay(caylon_config::ReadingConfig {
|
||||
title: format!("Element {}", next_index),
|
||||
period_ms: None,
|
||||
on_period: default_top_level_action(),
|
||||
})
|
||||
}
|
||||
SelectedElementType::ResultDisplay => {
|
||||
caylon_config::ElementConfig::ResultDisplay(
|
||||
caylon_config::ResultDisplayConfig {
|
||||
title: format!("Element {}", next_index),
|
||||
result_of: 0,
|
||||
},
|
||||
)
|
||||
}
|
||||
SelectedElementType::EventDisplay => {
|
||||
caylon_config::ElementConfig::EventDisplay(
|
||||
caylon_config::EventDisplayConfig {
|
||||
title: format!("Element {}", next_index),
|
||||
event: caylon_config::EventType::GameStart,
|
||||
on_event: default_top_level_action(),
|
||||
},
|
||||
)
|
||||
}
|
||||
};
|
||||
ctx.props().json_ctx.dispatch(JsonCtxAction::InsertElement {
|
||||
index: next_index,
|
||||
item,
|
||||
});
|
||||
true
|
||||
}
|
||||
AddElementMsg::NoOp => false,
|
||||
AddElementMsg::ReDraw => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
log::debug!("Add element rendered");
|
||||
let cb = ctx.link().callback(|#[allow(unused_variables)] event: Event| {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
log::info!("Element dropdown select");
|
||||
let elem = event.target_unchecked_into::<web_sys::HtmlSelectElement>();
|
||||
let new_item = match &elem.value() as &str {
|
||||
"button" => SelectedElementType::Button,
|
||||
"toggle" => SelectedElementType::Toggle,
|
||||
"slider" => SelectedElementType::Slider,
|
||||
"reading-display" => SelectedElementType::ReadingDisplay,
|
||||
"result-display" => SelectedElementType::ResultDisplay,
|
||||
"event-display" => SelectedElementType::EventDisplay,
|
||||
_ => SelectedElementType::ReadingDisplay,
|
||||
};
|
||||
AddElementMsg::SelectCb(new_item)
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
AddElementMsg::NoOp
|
||||
}
|
||||
});
|
||||
html! {
|
||||
<div class={classes!("add-element")} onload={ctx.link().callback(|_| {
|
||||
log::info!("Add element dropdown loaded, redrawing");
|
||||
AddElementMsg::ReDraw
|
||||
})}>
|
||||
<label>{"Add a new... "}</label>
|
||||
<select onchange={cb} autocomplete={"off"}>
|
||||
<option value={"button"}
|
||||
selected={self.selected == SelectedElementType::Button}
|
||||
>
|
||||
{"Button"}
|
||||
</option>
|
||||
<option value={"toggle"}
|
||||
selected={self.selected == SelectedElementType::Toggle}
|
||||
>
|
||||
{"Toggle"}
|
||||
</option>
|
||||
<option value={"slider"}
|
||||
selected={self.selected == SelectedElementType::Slider}
|
||||
>
|
||||
{"Slider"}
|
||||
</option>
|
||||
<option value={"reading-display"}
|
||||
selected={self.selected == SelectedElementType::ReadingDisplay}
|
||||
>
|
||||
{"Reading Display"}
|
||||
</option>
|
||||
<option value={"result-display"}
|
||||
selected={self.selected == SelectedElementType::ResultDisplay}
|
||||
>
|
||||
{"Result Display"}
|
||||
</option>
|
||||
<option value={"event-display"}
|
||||
selected={self.selected == SelectedElementType::EventDisplay}
|
||||
>
|
||||
{"Event Display"}
|
||||
</option>
|
||||
</select>
|
||||
<button onclick={ctx.link()
|
||||
.callback(
|
||||
|_| AddElementMsg::AddClick
|
||||
)} class={classes!("add-element-button")}>
|
||||
{ "+" }
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_top_level_action() -> caylon_config::TopLevelActionConfig {
|
||||
caylon_config::TopLevelActionConfig::Sequence(caylon_config::SequenceAction {
|
||||
steps: vec![
|
||||
caylon_config::ActionConfig::Command(caylon_config::CommandAction {
|
||||
run: "echo \"Hello world!\"".to_owned(),
|
||||
}),
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
/*fn default_action() -> caylon_config::ActionConfig {
|
||||
caylon_config::ActionConfig::Command(
|
||||
caylon_config::CommandAction { run: "echo \"Hello world!\"".to_owned() }
|
||||
)
|
||||
}*/
|
72
backend/caylon-studio/src/ui/element/button.rs
Normal file
72
backend/caylon-studio/src/ui/element/button.rs
Normal file
|
@ -0,0 +1,72 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use super::super::{JsonContext, JsonCtxAction};
|
||||
use super::ElementMessage;
|
||||
use caylon_config::ButtonConfig;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ButtonProps {
|
||||
pub index: usize,
|
||||
pub config: ButtonConfig,
|
||||
pub json_ctx: JsonContext,
|
||||
}
|
||||
|
||||
pub struct ButtonComponent;
|
||||
|
||||
impl Component for ButtonComponent {
|
||||
type Message = ElementMessage;
|
||||
type Properties = ButtonProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
let mut new_config = ctx.props().config.clone();
|
||||
let index = ctx.props().index;
|
||||
let update_needed = match msg {
|
||||
ElementMessage::SetTitle(title) => {
|
||||
new_config.title = title;
|
||||
true
|
||||
}
|
||||
ElementMessage::SetDescription(_desc) => false,
|
||||
ElementMessage::SetAction(action) => {
|
||||
new_config.on_click = action;
|
||||
true
|
||||
}
|
||||
ElementMessage::SetPeriod(_) => false,
|
||||
ElementMessage::SetResultOf(_) => false,
|
||||
ElementMessage::NoOp => false,
|
||||
//_ => false,
|
||||
};
|
||||
if update_needed {
|
||||
ctx.props().json_ctx.dispatch(JsonCtxAction::UpdateElement {
|
||||
index,
|
||||
new_item: caylon_config::ElementConfig::Button(new_config),
|
||||
});
|
||||
}
|
||||
update_needed
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
//let theme = &ctx.props().theme;
|
||||
let props = ctx.props();
|
||||
let callback = ctx.link().callback(|msg: Self::Message| msg);
|
||||
html! {
|
||||
<div class={classes!("caylon-button", "caylon-element")}>
|
||||
// TODO editing
|
||||
<super::edit::AlwaysStringComponent
|
||||
title={"Title"}
|
||||
value={props.config.title.clone()}
|
||||
callback={ctx.link().callback(|n_title: String| ElementMessage::SetTitle(n_title))}
|
||||
/>
|
||||
<super::edit::ActionComponent
|
||||
index={props.index}
|
||||
config={props.config.on_click.clone()}
|
||||
{callback}
|
||||
/>
|
||||
<super::fake::FakeButtonComponent config={props.config.clone()} />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
230
backend/caylon-studio/src/ui/element/edit/action_editor.rs
Normal file
230
backend/caylon-studio/src/ui/element/edit/action_editor.rs
Normal file
|
@ -0,0 +1,230 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
//use super::super::super::{JsonContext, JsonCtxAction};
|
||||
use super::super::ElementMessage;
|
||||
use caylon_config::{TopLevelActionConfig, ActionConfig};
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum SelectedActionConfig {
|
||||
Command,
|
||||
Transform,
|
||||
Javascript,
|
||||
Json
|
||||
}
|
||||
|
||||
fn selected(act: &ActionConfig) -> SelectedActionConfig {
|
||||
match act {
|
||||
ActionConfig::Command(_) => SelectedActionConfig::Command,
|
||||
ActionConfig::Transform(_) => SelectedActionConfig::Transform,
|
||||
ActionConfig::Javascript(_) => SelectedActionConfig::Javascript,
|
||||
ActionConfig::Json(_) => SelectedActionConfig::Json,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ActionProps {
|
||||
pub index: usize,
|
||||
pub config: TopLevelActionConfig,
|
||||
pub callback: super::EditCallback,
|
||||
}
|
||||
|
||||
pub struct ActionComponent;
|
||||
|
||||
impl Component for ActionComponent {
|
||||
type Message = ();
|
||||
type Properties = ActionProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let props = ctx.props();
|
||||
let inner_action = match &props.config {
|
||||
TopLevelActionConfig::Sequence(seq) => {
|
||||
let action_items = seq.steps
|
||||
.clone()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(index, step)| {
|
||||
let selected = selected(&step);
|
||||
let item = match step {
|
||||
ActionConfig::Command(cmd) =>
|
||||
{
|
||||
let cb = props.callback.clone();
|
||||
let config = seq.clone();
|
||||
html! {
|
||||
<div class={classes!("caylon-sequence-command-action", "caylon-sequence-action-edit")}>
|
||||
<super::actions::CommandActionComponent
|
||||
config={cmd.clone()}
|
||||
callback={
|
||||
ctx.link().callback(
|
||||
move |x| {
|
||||
let mut new_seq = config.clone();
|
||||
new_seq.steps[index] = ActionConfig::Command(x);
|
||||
cb.emit(ElementMessage::SetAction(TopLevelActionConfig::Sequence(new_seq)))
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
},
|
||||
ActionConfig::Transform(t) =>
|
||||
{
|
||||
let cb = props.callback.clone();
|
||||
let config = seq.clone();
|
||||
html! {
|
||||
<div class={classes!("caylon-sequence-transform-action", "caylon-sequence-action-edit")}>
|
||||
<super::actions::TransformActionComponent
|
||||
config={t.clone()}
|
||||
callback={
|
||||
ctx.link().callback(
|
||||
move |x| {
|
||||
let mut new_seq = config.clone();
|
||||
new_seq.steps[index] = ActionConfig::Transform(x);
|
||||
cb.emit(ElementMessage::SetAction(TopLevelActionConfig::Sequence(new_seq)))
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
},
|
||||
ActionConfig::Javascript(js) =>
|
||||
{
|
||||
let cb = props.callback.clone();
|
||||
let config = seq.clone();
|
||||
html! {
|
||||
<div class={classes!("caylon-sequence-javascript-action", "caylon-sequence-action-edit")}>
|
||||
<super::actions::JavascriptActionComponent
|
||||
config={js.clone()}
|
||||
callback={
|
||||
ctx.link().callback(
|
||||
move |x| {
|
||||
let mut new_seq = config.clone();
|
||||
new_seq.steps[index] = ActionConfig::Javascript(x);
|
||||
cb.emit(ElementMessage::SetAction(TopLevelActionConfig::Sequence(new_seq)))
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
},
|
||||
ActionConfig::Json(json) =>
|
||||
{
|
||||
let cb = props.callback.clone();
|
||||
let config = seq.clone();
|
||||
html! {
|
||||
<div class={classes!("caylon-sequence-json-action", "caylon-sequence-action-edit")}>
|
||||
<super::actions::JsonActionComponent
|
||||
config={json.clone()}
|
||||
callback={
|
||||
ctx.link().callback(
|
||||
move |x| {
|
||||
let mut new_seq = config.clone();
|
||||
new_seq.steps[index] = ActionConfig::Json(x);
|
||||
cb.emit(ElementMessage::SetAction(TopLevelActionConfig::Sequence(new_seq)))
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
},
|
||||
};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let cb = props.callback.clone();
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let moved_seq = seq.clone();
|
||||
let dropdown_cb = ctx.link().callback(move |#[allow(unused_variables)] event: Event| {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
log::debug!("Transformer dropdown select");
|
||||
let elem = event.target_unchecked_into::<web_sys::HtmlSelectElement>();
|
||||
let new_sel = match &elem.value() as &str {
|
||||
"command" => SelectedActionConfig::Command,
|
||||
"transform" => SelectedActionConfig::Transform,
|
||||
"javascript" => SelectedActionConfig::Javascript,
|
||||
"json" => SelectedActionConfig::Json,
|
||||
_ => SelectedActionConfig::Json,
|
||||
};
|
||||
let new_item = default_action_config(new_sel);
|
||||
let mut new_conf = moved_seq.clone();
|
||||
new_conf.steps[index] = new_item;
|
||||
cb.emit(ElementMessage::SetAction(TopLevelActionConfig::Sequence(new_conf)));
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{ }
|
||||
});
|
||||
html! {
|
||||
<div class={classes!("caylon-sequence-action-edit-item")}>
|
||||
// dropdown to change action
|
||||
<label>{"Action"}</label>
|
||||
<select onchange={dropdown_cb} autocomplete={"off"}>
|
||||
<option value={"command"}
|
||||
selected={selected == SelectedActionConfig::Command}
|
||||
>
|
||||
{"Command"}
|
||||
</option>
|
||||
<option value={"transform"}
|
||||
selected={selected == SelectedActionConfig::Transform}
|
||||
>
|
||||
{"Transform"}
|
||||
</option>
|
||||
<option value={"javascript"}
|
||||
selected={selected == SelectedActionConfig::Javascript}
|
||||
>
|
||||
{"Javascript"}
|
||||
</option><option value={"json"}
|
||||
selected={selected == SelectedActionConfig::Json}
|
||||
>
|
||||
{"JSON"}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
{item}
|
||||
// TODO remove button
|
||||
</div>
|
||||
}
|
||||
}).collect::<Html>();
|
||||
// TODO add button
|
||||
html! {
|
||||
<div class={classes!("caylon-sequence-action-edit", "caylon-action-config")}>
|
||||
{action_items}
|
||||
</div>
|
||||
}
|
||||
},
|
||||
_ => html! {<span>{"//TODO"}</span>},
|
||||
/*TopLevelActionConfig::Command(cmd) =>
|
||||
html! {<super::actions::CommandActionComponent config={cmd.clone()} callback={ctx.link().callback(move |x| cb.emit(ElementMessage::SetAction(TopLevelActionConfig::Command(x))))} />},
|
||||
TopLevelActionConfig::Transform(t) => html! {<span>{"//TODO"}</span>},
|
||||
TopLevelActionConfig::Mirror(mir) => html! {<span>{"//TODO"}</span>},
|
||||
TopLevelActionConfig::Javascript(js) => html! {<span>{"//TODO"}</span>},
|
||||
TopLevelActionConfig::Json(json) => html! {<span>{"//TODO"}</span>},*/
|
||||
};
|
||||
html! {
|
||||
<div class={classes!("caylon-action-edit", "caylon-editor")}>
|
||||
// TODO editing
|
||||
{inner_action}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn default_action_config(selected: SelectedActionConfig) -> ActionConfig {
|
||||
match selected {
|
||||
SelectedActionConfig::Command => ActionConfig::Command(
|
||||
caylon_config::CommandAction { run: "echo \"Hello caylon world!\"".to_owned() }
|
||||
),
|
||||
SelectedActionConfig::Transform => ActionConfig::Transform(caylon_config::TransformAction {
|
||||
transformer: caylon_config::TransformTypeAction::Log(caylon_config::LogTransformAction {
|
||||
level: caylon_config::LogLevel::INFO
|
||||
})
|
||||
}),
|
||||
SelectedActionConfig::Javascript => ActionConfig::Javascript(
|
||||
caylon_config::JavascriptAction { run: "console.log(\"Hello caylon world!\")".to_owned() }
|
||||
),
|
||||
SelectedActionConfig::Json => ActionConfig::Json(
|
||||
caylon_config::JsonAction { jmespath: "".to_owned() }
|
||||
),
|
||||
}
|
||||
}
|
42
backend/caylon-studio/src/ui/element/edit/actions/command.rs
Normal file
42
backend/caylon-studio/src/ui/element/edit/actions/command.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
//use super::super::super::{JsonContext, JsonCtxAction};
|
||||
use super::super::AlwaysStringComponent;
|
||||
use caylon_config::CommandAction;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct CommandActionProps {
|
||||
pub config: CommandAction,
|
||||
pub callback: Callback<CommandAction>,
|
||||
}
|
||||
|
||||
pub struct CommandActionComponent;
|
||||
|
||||
impl Component for CommandActionComponent {
|
||||
type Message = ();
|
||||
type Properties = CommandActionProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let props = ctx.props();
|
||||
//props.callback.emit(ElementMessage::NoOp);
|
||||
let cb = props.callback.clone();
|
||||
let config = props.config.clone();
|
||||
html! {
|
||||
<div class={classes!("caylon-command-action-edit", "caylon-action-config")}>
|
||||
<AlwaysStringComponent
|
||||
title={"Run"}
|
||||
value={props.config.run.clone()}
|
||||
callback={ctx.link().callback(move |run: String| {
|
||||
let mut new_conf = config.clone();
|
||||
new_conf.run = run;
|
||||
cb.emit(new_conf)
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
//use super::super::super::{JsonContext, JsonCtxAction};
|
||||
use super::super::AlwaysStringComponent;
|
||||
use caylon_config::JavascriptAction;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct JavascriptActionProps {
|
||||
pub config: JavascriptAction,
|
||||
pub callback: Callback<JavascriptAction>,
|
||||
}
|
||||
|
||||
pub struct JavascriptActionComponent;
|
||||
|
||||
impl Component for JavascriptActionComponent {
|
||||
type Message = ();
|
||||
type Properties = JavascriptActionProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let props = ctx.props();
|
||||
//props.callback.emit(ElementMessage::NoOp);
|
||||
let cb = props.callback.clone();
|
||||
let config = props.config.clone();
|
||||
html! {
|
||||
<div class={classes!("caylon-javascript-action-edit", "caylon-action-config")}>
|
||||
<AlwaysStringComponent
|
||||
title={"Run"}
|
||||
value={props.config.run.clone()}
|
||||
callback={ctx.link().callback(move |run: String| {
|
||||
let mut new_conf = config.clone();
|
||||
new_conf.run = run;
|
||||
cb.emit(new_conf)
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
42
backend/caylon-studio/src/ui/element/edit/actions/json.rs
Normal file
42
backend/caylon-studio/src/ui/element/edit/actions/json.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
//use super::super::super::{JsonContext, JsonCtxAction};
|
||||
use super::super::AlwaysStringComponent;
|
||||
use caylon_config::JsonAction;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct JsonActionProps {
|
||||
pub config: JsonAction,
|
||||
pub callback: Callback<JsonAction>,
|
||||
}
|
||||
|
||||
pub struct JsonActionComponent;
|
||||
|
||||
impl Component for JsonActionComponent {
|
||||
type Message = ();
|
||||
type Properties = JsonActionProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let props = ctx.props();
|
||||
//props.callback.emit(ElementMessage::NoOp);
|
||||
let cb = props.callback.clone();
|
||||
let config = props.config.clone();
|
||||
html! {
|
||||
<div class={classes!("caylon-json-action-edit", "caylon-action-config")}>
|
||||
<AlwaysStringComponent
|
||||
title={"JMESPath"}
|
||||
value={props.config.jmespath.clone()}
|
||||
callback={ctx.link().callback(move |run: String| {
|
||||
let mut new_conf = config.clone();
|
||||
new_conf.jmespath = run;
|
||||
cb.emit(new_conf)
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
28
backend/caylon-studio/src/ui/element/edit/actions/mirror.rs
Normal file
28
backend/caylon-studio/src/ui/element/edit/actions/mirror.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
//use super::super::super::{JsonContext, JsonCtxAction};
|
||||
use caylon_config::MirrorAction;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct MirrorActionProps {
|
||||
pub config: MirrorAction,
|
||||
pub callback: Callback<MirrorAction>,
|
||||
}
|
||||
|
||||
pub struct MirrorActionComponent;
|
||||
|
||||
impl Component for MirrorActionComponent {
|
||||
type Message = ();
|
||||
type Properties = MirrorActionProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<div class={classes!("caylon-mirror-action-edit", "caylon-action-config")}>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
11
backend/caylon-studio/src/ui/element/edit/actions/mod.rs
Normal file
11
backend/caylon-studio/src/ui/element/edit/actions/mod.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
mod command;
|
||||
mod javascript;
|
||||
mod json;
|
||||
mod mirror;
|
||||
mod transform;
|
||||
|
||||
pub use command::{CommandActionProps, CommandActionComponent};
|
||||
pub use javascript::{JavascriptActionProps, JavascriptActionComponent};
|
||||
pub use json::{JsonActionProps, JsonActionComponent};
|
||||
pub use mirror::{MirrorActionProps, MirrorActionComponent};
|
||||
pub use transform::{TransformActionProps, TransformActionComponent};
|
232
backend/caylon-studio/src/ui/element/edit/actions/transform.rs
Normal file
232
backend/caylon-studio/src/ui/element/edit/actions/transform.rs
Normal file
|
@ -0,0 +1,232 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
//use super::super::super::{JsonContext, JsonCtxAction};
|
||||
use super::super::AlwaysStringComponent;
|
||||
use caylon_config::{TransformAction, TransformTypeAction};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use caylon_config::PatternConfig;
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum SelectedTransformer {
|
||||
Replace,
|
||||
Expand,
|
||||
Log,
|
||||
}
|
||||
|
||||
fn selected(trans: &TransformTypeAction) -> SelectedTransformer {
|
||||
match trans {
|
||||
TransformTypeAction::Replace(_) => SelectedTransformer::Replace,
|
||||
TransformTypeAction::Expand(_) => SelectedTransformer::Expand,
|
||||
TransformTypeAction::Log(_) => SelectedTransformer::Log,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct TransformActionProps {
|
||||
pub config: TransformAction,
|
||||
pub callback: Callback<TransformAction>,
|
||||
}
|
||||
|
||||
pub struct TransformActionComponent;
|
||||
|
||||
impl Component for TransformActionComponent {
|
||||
type Message = ();
|
||||
type Properties = TransformActionProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let props = ctx.props();
|
||||
//props.callback.emit(ElementMessage::NoOp);
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let cb = props.callback.clone();
|
||||
let config = props.config.clone();
|
||||
let selected = selected(&config.transformer);
|
||||
let dropdown_cb = ctx.link().callback(move |#[allow(unused_variables)] event: Event| {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
log::debug!("Transformer dropdown select");
|
||||
let elem = event.target_unchecked_into::<web_sys::HtmlSelectElement>();
|
||||
let mut new_config = config.clone();
|
||||
new_config.transformer = match &elem.value() as &str {
|
||||
"replace" => default_replace_transformer(),
|
||||
"expand" => default_expand_transformer(),
|
||||
"log" => default_log_transformer(),
|
||||
_ => default_log_transformer(),
|
||||
};
|
||||
cb.emit(new_config);
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{ }
|
||||
});
|
||||
//let config = props.config.clone();
|
||||
let editor = match &props.config.transformer {
|
||||
TransformTypeAction::Replace(rep) => {
|
||||
// TODO allow for more than one pattern
|
||||
//let moved_rep = rep.clone();
|
||||
let items = rep.patterns.iter()
|
||||
.enumerate()
|
||||
.map(|(index, pattern)| {
|
||||
let cb_for_p = props.callback.clone();
|
||||
let cb_for_f = props.callback.clone();
|
||||
let moved_rep_p = rep.clone();
|
||||
let moved_rep_f = rep.clone();
|
||||
html! {
|
||||
<div class={classes!("caylon-transformer-replace-item")}>
|
||||
<AlwaysStringComponent
|
||||
title={"Regex pattern"}
|
||||
value={pattern.pattern.clone()}
|
||||
callback={ctx.link().callback(move |val: String| {
|
||||
let mut new_conf = moved_rep_p.clone();
|
||||
new_conf.patterns[index].pattern = val;
|
||||
cb_for_p.emit(TransformAction {
|
||||
transformer: TransformTypeAction::Replace(new_conf)
|
||||
})
|
||||
})}
|
||||
/>
|
||||
<AlwaysStringComponent
|
||||
title={"Format"}
|
||||
value={pattern.pattern.clone()}
|
||||
callback={ctx.link().callback(move |val: String| {
|
||||
let mut new_conf = moved_rep_f.clone();
|
||||
new_conf.patterns[index].format = val;
|
||||
cb_for_f.emit(TransformAction {
|
||||
transformer: TransformTypeAction::Replace(new_conf)
|
||||
})
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}).collect::<Html>();
|
||||
html! {
|
||||
<div class={classes!("caylon-transformer-replace")}>
|
||||
{items}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
TransformTypeAction::Expand(exp) => {
|
||||
let cb = props.callback.clone();
|
||||
let moved_exp = exp.clone();
|
||||
html! {
|
||||
<div class={classes!("caylon-transformer-expand")}>
|
||||
<AlwaysStringComponent
|
||||
title={"Format"}
|
||||
value={exp.format.clone()}
|
||||
callback={ctx.link().callback(move |val: String| {
|
||||
let mut new_conf = moved_exp.clone();
|
||||
new_conf.format = val;
|
||||
cb.emit(TransformAction {
|
||||
transformer: TransformTypeAction::Expand(new_conf)
|
||||
})
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
TransformTypeAction::Log(log) => {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let cb = props.callback.clone();
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let moved_log = log.clone();
|
||||
let log_cb = ctx.link().callback(move |#[allow(unused_variables)] event: Event| {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
log::debug!("Transformer log dropdown select");
|
||||
let elem = event.target_unchecked_into::<web_sys::HtmlSelectElement>();
|
||||
let mut new_conf = moved_log.clone();
|
||||
new_conf.level = match &elem.value() as &str {
|
||||
"debug" => caylon_config::LogLevel::DEBUG,
|
||||
"info" => caylon_config::LogLevel::INFO,
|
||||
"warn" => caylon_config::LogLevel::WARN,
|
||||
"error" => caylon_config::LogLevel::ERROR,
|
||||
_ => caylon_config::LogLevel::ERROR,
|
||||
};
|
||||
cb.emit(TransformAction {
|
||||
transformer: TransformTypeAction::Log(new_conf)
|
||||
})
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{ }
|
||||
});
|
||||
html! {
|
||||
<div class={classes!("caylon-transformer-log")}>
|
||||
<label>{"Log level"}</label>
|
||||
<select onchange={log_cb} autocomplete={"off"}>
|
||||
<option value={"debug"}
|
||||
selected={log.level == caylon_config::LogLevel::DEBUG}
|
||||
>
|
||||
{"Debug"}
|
||||
</option>
|
||||
<option value={"info"}
|
||||
selected={log.level == caylon_config::LogLevel::INFO}
|
||||
>
|
||||
{"Info"}
|
||||
</option>
|
||||
<option value={"warn"}
|
||||
selected={log.level == caylon_config::LogLevel::WARN}
|
||||
>
|
||||
{"Warn"}
|
||||
</option><option value={"error"}
|
||||
selected={log.level == caylon_config::LogLevel::ERROR}
|
||||
>
|
||||
{"Error"}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
};
|
||||
html! {
|
||||
<div class={classes!("caylon-transformer-action-edit", "caylon-action-config")}>
|
||||
<label>{"Type"}</label>
|
||||
<select onchange={dropdown_cb} autocomplete={"off"}>
|
||||
<option value={"replace"}
|
||||
selected={selected == SelectedTransformer::Replace}
|
||||
>
|
||||
{"Replace"}
|
||||
</option>
|
||||
<option value={"expand"}
|
||||
selected={selected == SelectedTransformer::Expand}
|
||||
>
|
||||
{"Expand"}
|
||||
</option>
|
||||
<option value={"log"}
|
||||
selected={selected == SelectedTransformer::Log}
|
||||
>
|
||||
{"Log"}
|
||||
</option>
|
||||
</select>
|
||||
<div class={classes!("caylon-transformer-type-action-edit")}>
|
||||
{editor}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn default_replace_transformer() -> TransformTypeAction {
|
||||
TransformTypeAction::Replace(caylon_config::ReplaceTransformAction { patterns: vec![
|
||||
PatternConfig {
|
||||
pattern: "regex".to_owned(),
|
||||
format: "$1".to_owned(),
|
||||
i: None,
|
||||
m: None,
|
||||
s: None,
|
||||
u: None,
|
||||
x: None,
|
||||
}
|
||||
] })
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn default_expand_transformer() -> TransformTypeAction {
|
||||
TransformTypeAction::Expand(caylon_config::ExpandTransformAction { format: "$CAYLON_VALUE".into() })
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn default_log_transformer() -> TransformTypeAction {
|
||||
TransformTypeAction::Log(caylon_config::LogTransformAction { level: caylon_config::LogLevel::INFO })
|
||||
}
|
45
backend/caylon-studio/src/ui/element/edit/always_str.rs
Normal file
45
backend/caylon-studio/src/ui/element/edit/always_str.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct AlwaysStringProps {
|
||||
pub title: Option<&'static str>,
|
||||
pub value: String,
|
||||
pub callback: Callback<String>,
|
||||
}
|
||||
|
||||
pub struct AlwaysStringComponent;
|
||||
|
||||
impl Component for AlwaysStringComponent {
|
||||
type Message = ();
|
||||
type Properties = AlwaysStringProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let props = ctx.props();
|
||||
let callback = props.callback.clone();
|
||||
html! {
|
||||
<div class={classes!("caylon-always-str-edit", "caylon-editor")}>
|
||||
{props.title.map(|t| html!{ <label class={classes!("caylon-label-edit")}>{t}</label>})}
|
||||
<input type="text"
|
||||
value={props.value.clone()}
|
||||
onchange={ctx.link().callback(move |#[allow(unused_variables)] event: Event| {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
log::debug!("Always str input change");
|
||||
let elem = event.target_unchecked_into::<web_sys::HtmlInputElement>();
|
||||
callback.emit(elem.value());
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
callback.emit("[UNREACHABLE]".into())
|
||||
}
|
||||
})}
|
||||
class={classes!("caylon-always-str-input", "caylon-input-editor")}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
50
backend/caylon-studio/src/ui/element/edit/always_usize.rs
Normal file
50
backend/caylon-studio/src/ui/element/edit/always_usize.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct AlwaysUsizeProps {
|
||||
pub title: Option<&'static str>,
|
||||
pub value: usize,
|
||||
pub callback: Callback<usize>,
|
||||
}
|
||||
|
||||
pub struct AlwaysUsizeComponent;
|
||||
|
||||
impl Component for AlwaysUsizeComponent {
|
||||
type Message = ();
|
||||
type Properties = AlwaysUsizeProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let props = ctx.props();
|
||||
let callback = props.callback.clone();
|
||||
html! {
|
||||
<div class={classes!("caylon-always-usize-edit", "caylon-editor")}>
|
||||
{props.title.map(|t| html!{ <label class={classes!("caylon-label-edit")}>{t}</label>})}
|
||||
<input type="number"
|
||||
value={props.value.to_string()}
|
||||
onchange={ctx.link().callback(move |#[allow(unused_variables)] event: Event| {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
log::debug!("Always usize input change");
|
||||
let elem = event.target_unchecked_into::<web_sys::HtmlInputElement>();
|
||||
let result: Result<usize, _> = elem.value().parse();
|
||||
match result {
|
||||
Ok(value) => callback.emit(value),
|
||||
Err(e) => log::warn!("Failed to parse always usize: {}", e),
|
||||
}
|
||||
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
callback.emit(usize::MAX)
|
||||
}
|
||||
})}
|
||||
class={classes!("caylon-always-usize-input", "caylon-input-editor")}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
15
backend/caylon-studio/src/ui/element/edit/mod.rs
Normal file
15
backend/caylon-studio/src/ui/element/edit/mod.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
mod action_editor;
|
||||
mod always_str;
|
||||
mod always_usize;
|
||||
mod optional_str;
|
||||
mod optional_u64;
|
||||
|
||||
pub mod actions;
|
||||
|
||||
pub use action_editor::{ActionComponent, ActionProps};
|
||||
pub use always_str::{AlwaysStringComponent, AlwaysStringProps};
|
||||
pub use always_usize::{AlwaysUsizeComponent, AlwaysUsizeProps};
|
||||
pub use optional_str::{OptionStringComponent, OptionStringProps};
|
||||
pub use optional_u64::{OptionU64Component, OptionU64Props};
|
||||
|
||||
type EditCallback = yew::prelude::Callback<super::ElementMessage>;
|
46
backend/caylon-studio/src/ui/element/edit/optional_str.rs
Normal file
46
backend/caylon-studio/src/ui/element/edit/optional_str.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct OptionStringProps {
|
||||
pub title: Option<&'static str>,
|
||||
pub value: Option<String>,
|
||||
pub callback: Callback<Option<String>>,
|
||||
}
|
||||
|
||||
pub struct OptionStringComponent;
|
||||
|
||||
impl Component for OptionStringComponent {
|
||||
type Message = ();
|
||||
type Properties = OptionStringProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let props = ctx.props();
|
||||
let callback = props.callback.clone();
|
||||
html! {
|
||||
<div class={classes!("caylon-option-str-edit", "caylon-editor")}>
|
||||
{props.title.map(|t| html!{ <label class={classes!("caylon-label-edit")}>{t}</label>})}
|
||||
<input type="text"
|
||||
value={props.value.clone().unwrap_or("".into())}
|
||||
onchange={ctx.link().callback(move |#[allow(unused_variables)] event: Event| {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
log::debug!("Option str input change");
|
||||
let elem = event.target_unchecked_into::<web_sys::HtmlInputElement>();
|
||||
let value = elem.value();
|
||||
callback.emit(if value.is_empty() { None } else { Some(value) });
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
callback.emit(Some("[UNREACHABLE]".into()))
|
||||
}
|
||||
})}
|
||||
class={classes!("caylon-option-str-input", "caylon-input-editor")}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
46
backend/caylon-studio/src/ui/element/edit/optional_u64.rs
Normal file
46
backend/caylon-studio/src/ui/element/edit/optional_u64.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct OptionU64Props {
|
||||
pub title: Option<&'static str>,
|
||||
pub value: Option<u64>,
|
||||
pub callback: Callback<Option<u64>>,
|
||||
}
|
||||
|
||||
pub struct OptionU64Component;
|
||||
|
||||
impl Component for OptionU64Component {
|
||||
type Message = ();
|
||||
type Properties = OptionU64Props;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let props = ctx.props();
|
||||
let callback = props.callback.clone();
|
||||
html! {
|
||||
<div class={classes!("caylon-option-u64-edit", "caylon-editor")}>
|
||||
{props.title.map(|t| html!{ <label class={classes!("caylon-label-edit")}>{t}</label>})}
|
||||
<input type="number"
|
||||
value={props.value.unwrap_or(0).to_string()}
|
||||
onchange={ctx.link().callback(move |#[allow(unused_variables)] event: Event| {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
log::debug!("Option u64 input change");
|
||||
let elem = event.target_unchecked_into::<web_sys::HtmlInputElement>();
|
||||
let value: Option<u64> = elem.value().parse().ok();
|
||||
callback.emit(value);
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
callback.emit(Some(u64::MAX))
|
||||
}
|
||||
})}
|
||||
class={classes!("caylon-option-u64-input", "caylon-input-editor")}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
65
backend/caylon-studio/src/ui/element/elements.rs
Normal file
65
backend/caylon-studio/src/ui/element/elements.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use super::super::JsonContext;
|
||||
use caylon_config::ElementConfig;
|
||||
|
||||
#[function_component]
|
||||
pub fn ElementsComponent() -> Html {
|
||||
log::debug!("Elements render");
|
||||
let json_ctx = use_context::<JsonContext>().expect("Missing JSON context");
|
||||
let elem_view = if json_ctx.json.items().is_empty() {
|
||||
html! {
|
||||
{"--- No elements ---"}
|
||||
}
|
||||
} else {
|
||||
let elements_html = json_ctx
|
||||
.json
|
||||
.items()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, elem)| element_impl(i, elem, &json_ctx))
|
||||
.collect::<Html>();
|
||||
html! {
|
||||
{elements_html}
|
||||
}
|
||||
};
|
||||
html! {
|
||||
<div class={classes!("elements-view")}>
|
||||
<div class={classes!("elements-toolbar")}>
|
||||
<super::AddElementComponent {json_ctx}/>
|
||||
</div>
|
||||
<div class={classes!("elements-list")}>
|
||||
{elem_view}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
fn element_impl(index: usize, elem: &ElementConfig, ctx: &JsonContext) -> Html {
|
||||
let inner = match elem {
|
||||
ElementConfig::Button(button) => html! {
|
||||
<super::button::ButtonComponent {index} config={button.to_owned()} json_ctx={ctx.to_owned()}/>
|
||||
},
|
||||
ElementConfig::Toggle(toggle) => {
|
||||
html! {<super::toggle::ToggleComponent {index} config={toggle.to_owned()} json_ctx={ctx.to_owned()} />}
|
||||
}
|
||||
ElementConfig::Slider(slider) => {
|
||||
html! {<super::slider::SliderComponent {index} config={slider.to_owned()} json_ctx={ctx.to_owned()} />}
|
||||
}
|
||||
ElementConfig::ReadingDisplay(disp) => {
|
||||
html! {<super::reading_display::ReadingDisplayComponent {index} config={disp.to_owned()} json_ctx={ctx.to_owned()} />}
|
||||
}
|
||||
ElementConfig::ResultDisplay(disp) => {
|
||||
html! {<super::result_display::ResultDisplayComponent {index} config={disp.to_owned()} json_ctx={ctx.to_owned()} />}
|
||||
}
|
||||
ElementConfig::EventDisplay(disp) => {
|
||||
html! {<super::event_display::EventDisplayComponent {index} config={disp.to_owned()} json_ctx={ctx.to_owned()} />}
|
||||
} //_ => html!{{format!("elem #{} //TODO", index)}},
|
||||
};
|
||||
html! {
|
||||
<div class={classes!("elements-item")}>
|
||||
{inner}
|
||||
<super::RemoveElementComponent {index} />
|
||||
</div>
|
||||
}
|
||||
}
|
66
backend/caylon-studio/src/ui/element/event_display.rs
Normal file
66
backend/caylon-studio/src/ui/element/event_display.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use super::super::{JsonContext, JsonCtxAction};
|
||||
use super::ElementMessage;
|
||||
use caylon_config::EventDisplayConfig;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct EventDisplayProps {
|
||||
pub index: usize,
|
||||
pub config: EventDisplayConfig,
|
||||
pub json_ctx: JsonContext,
|
||||
}
|
||||
|
||||
pub struct EventDisplayComponent;
|
||||
|
||||
impl Component for EventDisplayComponent {
|
||||
type Message = ElementMessage;
|
||||
type Properties = EventDisplayProps;
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
let mut new_config = ctx.props().config.clone();
|
||||
let index = ctx.props().index;
|
||||
let update_needed = match msg {
|
||||
ElementMessage::SetTitle(title) => {
|
||||
new_config.title = title;
|
||||
true
|
||||
}
|
||||
ElementMessage::SetDescription(_desc) => false,
|
||||
ElementMessage::SetAction(action) => {
|
||||
new_config.on_event = action;
|
||||
true
|
||||
}
|
||||
ElementMessage::SetPeriod(_) => false,
|
||||
ElementMessage::SetResultOf(_) => false,
|
||||
ElementMessage::NoOp => false,
|
||||
//_ => false,
|
||||
};
|
||||
if update_needed {
|
||||
ctx.props().json_ctx.dispatch(JsonCtxAction::UpdateElement {
|
||||
index,
|
||||
new_item: caylon_config::ElementConfig::EventDisplay(new_config),
|
||||
});
|
||||
}
|
||||
update_needed
|
||||
}
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let props = ctx.props();
|
||||
let callback = ctx.link().callback(|msg: Self::Message| msg);
|
||||
html! {
|
||||
<div class={classes!("caylon-event-display", "caylon-element")}>
|
||||
<super::edit::AlwaysStringComponent
|
||||
title={"Title"}
|
||||
value={props.config.title.clone()}
|
||||
callback={ctx.link().callback(|n_title: String| ElementMessage::SetTitle(n_title))}
|
||||
/>
|
||||
<super::edit::ActionComponent index={props.index} config={props.config.on_event.clone()} {callback}/>
|
||||
<super::fake::FakeDisplayComponent title={props.config.title.clone()} />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
15
backend/caylon-studio/src/ui/element/fake/button.rs
Normal file
15
backend/caylon-studio/src/ui/element/fake/button.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use caylon_config::ButtonConfig;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct FakeButtonProps {
|
||||
pub config: ButtonConfig,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn FakeButtonComponent(props: &FakeButtonProps) -> Html {
|
||||
html! {
|
||||
<button type="button" class={classes!("fake-button")}>{props.config.title.clone()}</button>
|
||||
}
|
||||
}
|
19
backend/caylon-studio/src/ui/element/fake/display.rs
Normal file
19
backend/caylon-studio/src/ui/element/fake/display.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
//use caylon_config::DisplayConfig;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct FakeDisplayProps {
|
||||
pub title: String,
|
||||
pub content: Option<String>,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn FakeDisplayComponent(props: &FakeDisplayProps) -> Html {
|
||||
html! {
|
||||
<div class={classes!("fake-display")}>
|
||||
<span class={classes!("fake-display-title")}>{props.title.clone()}</span>
|
||||
<span class={classes!("fake-display-content")}>{props.content.clone().unwrap_or_else(|| "[info]".into())}</span>
|
||||
</div>
|
||||
}
|
||||
}
|
9
backend/caylon-studio/src/ui/element/fake/mod.rs
Normal file
9
backend/caylon-studio/src/ui/element/fake/mod.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
mod button;
|
||||
mod display;
|
||||
mod slider;
|
||||
mod toggle;
|
||||
|
||||
pub use button::*;
|
||||
pub use display::*;
|
||||
pub use slider::*;
|
||||
pub use toggle::*;
|
18
backend/caylon-studio/src/ui/element/fake/slider.rs
Normal file
18
backend/caylon-studio/src/ui/element/fake/slider.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use caylon_config::SliderConfig;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct FakeSliderProps {
|
||||
pub config: SliderConfig,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn FakeSliderComponent(props: &FakeSliderProps) -> Html {
|
||||
html! {
|
||||
<div class={classes!("fake-slider")}>
|
||||
<span class={classes!("fake-slider-title")}>{props.config.title.clone()}</span>
|
||||
<input type="range" min={props.config.min.to_string()} max={props.config.max.to_string()} value={0} class={classes!("fake-slider-input")} />
|
||||
</div>
|
||||
}
|
||||
}
|
24
backend/caylon-studio/src/ui/element/fake/toggle.rs
Normal file
24
backend/caylon-studio/src/ui/element/fake/toggle.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use caylon_config::ToggleConfig;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct FakeToggleProps {
|
||||
pub config: ToggleConfig,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn FakeToggleComponent(props: &FakeToggleProps) -> Html {
|
||||
html! {
|
||||
<div class={classes!("fake-toggle")}>
|
||||
<span class={classes!("fake-toggle-title")}>{props.config.title.clone()}</span>
|
||||
{props.config.description.clone().map(|desc| html! {
|
||||
<span class={classes!("fake-toggle-description")}>{desc}</span>
|
||||
})}
|
||||
<label class={classes!("fake-toggle-button")}>
|
||||
<input type={"checkbox"} />
|
||||
<span class={classes!("toggle-round")}></span>
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
}
|
22
backend/caylon-studio/src/ui/element/mod.rs
Normal file
22
backend/caylon-studio/src/ui/element/mod.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
//! HTML versions of UI elements that Caylon supports.
|
||||
//! These do not aim to exactly clone the Steam Deck style,
|
||||
//! as long as they give the right impression.
|
||||
|
||||
mod add_element;
|
||||
mod button;
|
||||
mod elements;
|
||||
mod event_display;
|
||||
mod msg_common;
|
||||
mod reading_display;
|
||||
mod remove_element;
|
||||
mod result_display;
|
||||
mod slider;
|
||||
mod toggle;
|
||||
|
||||
pub mod fake;
|
||||
pub mod edit;
|
||||
|
||||
pub use add_element::AddElementComponent;
|
||||
pub use elements::ElementsComponent;
|
||||
pub use msg_common::{ElementMessage, ElementCtx, ElementContext};
|
||||
pub use remove_element::{RemoveElementComponent, RemoveElementProps};
|
105
backend/caylon-studio/src/ui/element/msg_common.rs
Normal file
105
backend/caylon-studio/src/ui/element/msg_common.rs
Normal file
|
@ -0,0 +1,105 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use caylon_config::{ElementConfig, TopLevelActionConfig};
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
pub enum ElementMessage {
|
||||
SetTitle(String),
|
||||
SetDescription(Option<String>),
|
||||
SetAction(TopLevelActionConfig),
|
||||
SetPeriod(Option<u64>),
|
||||
SetResultOf(usize),
|
||||
NoOp,
|
||||
}
|
||||
|
||||
// unused (it's a bad idea)
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct ElementCtx(ElementConfig);
|
||||
|
||||
pub type ElementContext = UseReducerHandle<ElementCtx>;
|
||||
|
||||
impl Eq for ElementCtx {}
|
||||
|
||||
impl ElementCtx {
|
||||
pub fn init(element: ElementConfig) -> Self {
|
||||
Self(element)
|
||||
}
|
||||
}
|
||||
|
||||
impl Reducible for ElementCtx {
|
||||
type Action = ElementMessage;
|
||||
|
||||
fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
|
||||
let new_config = match self.0.clone() {
|
||||
ElementConfig::Button(mut button) => {
|
||||
match action {
|
||||
ElementMessage::SetTitle(title) => button.title = title,
|
||||
ElementMessage::SetDescription(_) => {},
|
||||
ElementMessage::SetAction(action) => button.on_click = action,
|
||||
ElementMessage::SetPeriod(_) => {},
|
||||
ElementMessage::SetResultOf(_) => {},
|
||||
ElementMessage::NoOp => {},
|
||||
}
|
||||
ElementConfig::Button(button)
|
||||
},
|
||||
ElementConfig::Toggle(mut toggle) => {
|
||||
match action {
|
||||
ElementMessage::SetTitle(title) => toggle.title = title,
|
||||
ElementMessage::SetDescription(desc) => toggle.description = desc,
|
||||
ElementMessage::SetAction(action) => toggle.on_toggle = action,
|
||||
ElementMessage::SetPeriod(_) => {},
|
||||
ElementMessage::SetResultOf(_) => {},
|
||||
ElementMessage::NoOp => {},
|
||||
}
|
||||
ElementConfig::Toggle(toggle)
|
||||
},
|
||||
ElementConfig::Slider(mut slider) => {
|
||||
match action {
|
||||
ElementMessage::SetTitle(title) => slider.title = title,
|
||||
ElementMessage::SetDescription(_) => {},
|
||||
ElementMessage::SetAction(action) => slider.on_set = action,
|
||||
ElementMessage::SetPeriod(_) => {},
|
||||
ElementMessage::SetResultOf(_) => {},
|
||||
ElementMessage::NoOp => {},
|
||||
}
|
||||
ElementConfig::Slider(slider)
|
||||
},
|
||||
ElementConfig::ReadingDisplay(mut disp) => {
|
||||
match action {
|
||||
ElementMessage::SetTitle(title) => disp.title = title,
|
||||
ElementMessage::SetDescription(_) => {},
|
||||
ElementMessage::SetAction(action) => disp.on_period = action,
|
||||
ElementMessage::SetPeriod(period) => disp.period_ms = period,
|
||||
ElementMessage::SetResultOf(_) => {},
|
||||
ElementMessage::NoOp => {},
|
||||
}
|
||||
ElementConfig::ReadingDisplay(disp)
|
||||
},
|
||||
ElementConfig::ResultDisplay(mut disp) => {
|
||||
match action {
|
||||
ElementMessage::SetTitle(title) => disp.title = title,
|
||||
ElementMessage::SetDescription(_) => {},
|
||||
ElementMessage::SetAction(_) => {},
|
||||
ElementMessage::SetPeriod(_) => {},
|
||||
ElementMessage::SetResultOf(result) => disp.result_of = result,
|
||||
ElementMessage::NoOp => {},
|
||||
}
|
||||
ElementConfig::ResultDisplay(disp)
|
||||
},
|
||||
ElementConfig::EventDisplay(mut disp) => {
|
||||
match action {
|
||||
ElementMessage::SetTitle(title) => disp.title = title,
|
||||
ElementMessage::SetDescription(_) => {},
|
||||
ElementMessage::SetAction(action) => disp.on_event = action,
|
||||
ElementMessage::SetPeriod(_) => {},
|
||||
ElementMessage::SetResultOf(_) => {},
|
||||
ElementMessage::NoOp => {},
|
||||
}
|
||||
ElementConfig::EventDisplay(disp)
|
||||
},
|
||||
};
|
||||
Rc::new(Self::init(new_config))
|
||||
}
|
||||
}
|
71
backend/caylon-studio/src/ui/element/reading_display.rs
Normal file
71
backend/caylon-studio/src/ui/element/reading_display.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use super::super::{JsonContext, JsonCtxAction};
|
||||
use super::ElementMessage;
|
||||
use caylon_config::ReadingConfig;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ReadingDisplayProps {
|
||||
pub index: usize,
|
||||
pub config: ReadingConfig,
|
||||
pub json_ctx: JsonContext,
|
||||
}
|
||||
|
||||
pub struct ReadingDisplayComponent;
|
||||
|
||||
impl Component for ReadingDisplayComponent {
|
||||
type Message = ElementMessage;
|
||||
type Properties = ReadingDisplayProps;
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
let mut new_config = ctx.props().config.clone();
|
||||
let index = ctx.props().index;
|
||||
let update_needed = match msg {
|
||||
ElementMessage::SetTitle(title) => {
|
||||
new_config.title = title;
|
||||
true
|
||||
}
|
||||
ElementMessage::SetDescription(_desc) => false,
|
||||
ElementMessage::SetAction(_action) => false,
|
||||
ElementMessage::SetPeriod(period) => {
|
||||
new_config.period_ms = period;
|
||||
true
|
||||
},
|
||||
ElementMessage::SetResultOf(_) => false,
|
||||
ElementMessage::NoOp => false,
|
||||
//_ => false,
|
||||
};
|
||||
if update_needed {
|
||||
ctx.props().json_ctx.dispatch(JsonCtxAction::UpdateElement {
|
||||
index,
|
||||
new_item: caylon_config::ElementConfig::ReadingDisplay(new_config),
|
||||
});
|
||||
}
|
||||
update_needed
|
||||
}
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let props = ctx.props();
|
||||
let callback = ctx.link().callback(|msg: Self::Message| msg);
|
||||
html! {
|
||||
<div class={classes!("caylon-reading-display", "caylon-element")}>
|
||||
<super::edit::AlwaysStringComponent
|
||||
title={"Title"}
|
||||
value={props.config.title.clone()}
|
||||
callback={ctx.link().callback(|n_title: String| ElementMessage::SetTitle(n_title))}
|
||||
/>
|
||||
<super::edit::OptionU64Component
|
||||
title={"Period"}
|
||||
value={props.config.period_ms}
|
||||
callback={ctx.link().callback(|n_period| ElementMessage::SetPeriod(n_period))}
|
||||
/>
|
||||
<super::edit::ActionComponent index={props.index} config={props.config.on_period.clone()} {callback}/>
|
||||
<super::fake::FakeDisplayComponent title={ctx.props().config.title.clone()} />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
22
backend/caylon-studio/src/ui/element/remove_element.rs
Normal file
22
backend/caylon-studio/src/ui/element/remove_element.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use super::super::{JsonContext, JsonCtxAction};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct RemoveElementProps {
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
|
||||
#[function_component]
|
||||
pub fn RemoveElementComponent(props: &RemoveElementProps) -> Html {
|
||||
let json_ctx = use_context::<JsonContext>().expect("Missing JSON context");
|
||||
let index = props.index;
|
||||
html! {
|
||||
<div class={classes!("remove-element")}>
|
||||
<button onclick={Callback::from(move |_| json_ctx.dispatch(JsonCtxAction::RemoveElement { index }))} class={classes!("remove-element-button")}>
|
||||
{ "-" }
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
}
|
71
backend/caylon-studio/src/ui/element/result_display.rs
Normal file
71
backend/caylon-studio/src/ui/element/result_display.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use super::super::{JsonContext, JsonCtxAction};
|
||||
use super::ElementMessage;
|
||||
use caylon_config::ResultDisplayConfig;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ResultDisplayProps {
|
||||
pub index: usize,
|
||||
pub config: ResultDisplayConfig,
|
||||
pub json_ctx: JsonContext,
|
||||
}
|
||||
|
||||
pub struct ResultDisplayComponent;
|
||||
|
||||
impl Component for ResultDisplayComponent {
|
||||
type Message = ElementMessage;
|
||||
type Properties = ResultDisplayProps;
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
let mut new_config = ctx.props().config.clone();
|
||||
let index = ctx.props().index;
|
||||
let update_needed = match msg {
|
||||
ElementMessage::SetTitle(title) => {
|
||||
new_config.title = title;
|
||||
true
|
||||
}
|
||||
ElementMessage::SetDescription(_desc) => false,
|
||||
ElementMessage::SetAction(_action) => false,
|
||||
ElementMessage::SetPeriod(_) => false,
|
||||
ElementMessage::SetResultOf(result) => {
|
||||
new_config.result_of = result;
|
||||
true
|
||||
},
|
||||
ElementMessage::NoOp => false,
|
||||
//_ => false,
|
||||
};
|
||||
if update_needed {
|
||||
ctx.props().json_ctx.dispatch(JsonCtxAction::UpdateElement {
|
||||
index,
|
||||
new_item: caylon_config::ElementConfig::ResultDisplay(new_config),
|
||||
});
|
||||
}
|
||||
update_needed
|
||||
}
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let props = ctx.props();
|
||||
//let callback = ctx.link().callback(|msg: Self::Message| msg);
|
||||
html! {
|
||||
<div class={classes!("caylon-result-display", "caylon-element")}>
|
||||
// TODO editing
|
||||
<super::edit::AlwaysStringComponent
|
||||
title={"Title"}
|
||||
value={props.config.title.clone()}
|
||||
callback={ctx.link().callback(|n_title: String| ElementMessage::SetTitle(n_title))}
|
||||
/>
|
||||
<super::edit::AlwaysUsizeComponent
|
||||
title={"Result Of"}
|
||||
value={props.config.result_of}
|
||||
callback={ctx.link().callback(|n_result| ElementMessage::SetResultOf(n_result))}
|
||||
/>
|
||||
<super::fake::FakeDisplayComponent title={ctx.props().config.title.clone()} content={"[result]".to_owned()}/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
67
backend/caylon-studio/src/ui/element/slider.rs
Normal file
67
backend/caylon-studio/src/ui/element/slider.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use super::super::{JsonContext, JsonCtxAction};
|
||||
use super::ElementMessage;
|
||||
use caylon_config::SliderConfig;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct SliderProps {
|
||||
pub index: usize,
|
||||
pub config: SliderConfig,
|
||||
pub json_ctx: JsonContext,
|
||||
}
|
||||
|
||||
pub struct SliderComponent;
|
||||
|
||||
impl Component for SliderComponent {
|
||||
type Message = ElementMessage;
|
||||
type Properties = SliderProps;
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
let mut new_config = ctx.props().config.clone();
|
||||
let index = ctx.props().index;
|
||||
let update_needed = match msg {
|
||||
ElementMessage::SetTitle(title) => {
|
||||
new_config.title = title;
|
||||
true
|
||||
}
|
||||
ElementMessage::SetDescription(_desc) => false,
|
||||
ElementMessage::SetAction(action) => {
|
||||
new_config.on_set = action;
|
||||
true
|
||||
}
|
||||
ElementMessage::SetPeriod(_) => false,
|
||||
ElementMessage::SetResultOf(_) => false,
|
||||
ElementMessage::NoOp => false,
|
||||
//_ => false,
|
||||
};
|
||||
if update_needed {
|
||||
ctx.props().json_ctx.dispatch(JsonCtxAction::UpdateElement {
|
||||
index,
|
||||
new_item: caylon_config::ElementConfig::Slider(new_config),
|
||||
});
|
||||
}
|
||||
update_needed
|
||||
}
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let props = ctx.props();
|
||||
let callback = ctx.link().callback(|msg: Self::Message| msg);
|
||||
html! {
|
||||
<div class={classes!("caylon-slider", "caylon-element")}>
|
||||
// TODO editing
|
||||
<super::edit::AlwaysStringComponent
|
||||
title={"Title"}
|
||||
value={props.config.title.clone()}
|
||||
callback={ctx.link().callback(|n_title: String| ElementMessage::SetTitle(n_title))}
|
||||
/>
|
||||
<super::edit::ActionComponent index={props.index} config={props.config.on_set.clone()} {callback}/>
|
||||
<super::fake::FakeSliderComponent config={ctx.props().config.clone()} />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
70
backend/caylon-studio/src/ui/element/toggle.rs
Normal file
70
backend/caylon-studio/src/ui/element/toggle.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use super::super::{JsonContext, JsonCtxAction};
|
||||
use super::ElementMessage;
|
||||
use caylon_config::ToggleConfig;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ToggleProps {
|
||||
pub index: usize,
|
||||
pub config: ToggleConfig,
|
||||
pub json_ctx: JsonContext,
|
||||
}
|
||||
|
||||
pub struct ToggleComponent;
|
||||
|
||||
impl Component for ToggleComponent {
|
||||
type Message = ElementMessage;
|
||||
type Properties = ToggleProps;
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
let mut new_config = ctx.props().config.clone();
|
||||
let index = ctx.props().index;
|
||||
let update_needed = match msg {
|
||||
ElementMessage::SetTitle(title) => {
|
||||
new_config.title = title;
|
||||
true
|
||||
}
|
||||
ElementMessage::SetDescription(desc) => {
|
||||
new_config.description = desc;
|
||||
true
|
||||
}
|
||||
ElementMessage::SetAction(action) => {
|
||||
new_config.on_toggle = action;
|
||||
true
|
||||
}
|
||||
ElementMessage::SetPeriod(_) => false,
|
||||
ElementMessage::SetResultOf(_) => false,
|
||||
ElementMessage::NoOp => false,
|
||||
//_ => false,
|
||||
};
|
||||
if update_needed {
|
||||
ctx.props().json_ctx.dispatch(JsonCtxAction::UpdateElement {
|
||||
index,
|
||||
new_item: caylon_config::ElementConfig::Toggle(new_config),
|
||||
});
|
||||
}
|
||||
update_needed
|
||||
}
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let props = ctx.props();
|
||||
let callback = ctx.link().callback(|msg: Self::Message| msg);
|
||||
html! {
|
||||
<div class={classes!("caylon-toggle", "caylon-element")}>
|
||||
// TODO editing
|
||||
<super::edit::AlwaysStringComponent
|
||||
title={"Title"}
|
||||
value={props.config.title.clone()}
|
||||
callback={ctx.link().callback(|n_title: String| ElementMessage::SetTitle(n_title))}
|
||||
/>
|
||||
<super::edit::ActionComponent index={props.index} config={props.config.on_toggle.clone()} {callback}/>
|
||||
<super::fake::FakeToggleComponent config={ctx.props().config.clone()} />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
27
backend/caylon-studio/src/ui/footer_bar.rs
Normal file
27
backend/caylon-studio/src/ui/footer_bar.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
#[function_component]
|
||||
pub fn FooterComponent() -> Html {
|
||||
log::debug!("Footer render");
|
||||
html! {
|
||||
<div class={classes!("footer")}>
|
||||
<span class={classes!("footer-elem")}>
|
||||
{"Javascript required (though it's mostly WASM)"}
|
||||
</span>
|
||||
<span class={classes!("footer-elem")}>
|
||||
{" Made for "}
|
||||
<a href={"https://github.com/NGnius/caylon"}>{"Caylon"}</a>
|
||||
{" by "}
|
||||
<a href={"http://ngni.us"}>{"NGnius"}</a>
|
||||
</span>
|
||||
<span class={classes!("footer-elem")}>
|
||||
<a href={"https://liberapay.com/NGnius"}>{"Donate"}</a>
|
||||
</span>
|
||||
<span class={classes!("footer-elem")}>
|
||||
<Suspense fallback={super::hit_counter::fallback()}>
|
||||
<super::HitCounterComponent />
|
||||
</Suspense>
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
}
|
26
backend/caylon-studio/src/ui/hit_counter.rs
Normal file
26
backend/caylon-studio/src/ui/hit_counter.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
async fn fetch_hits() -> u64 {
|
||||
// URL only valid server-side, after the TLS reverse-proxy
|
||||
//let resp = reqwest::get("http://localhost:8080/stats/hits").await.unwrap();
|
||||
//let hits = resp.json::<u64>().await.unwrap();
|
||||
let hits = crate::api::get_hits::INDEX_HITS.load(std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
hits
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn HitCounterComponent() -> HtmlResult {
|
||||
let hits = use_prepared_state!(async move |_| -> u64 { fetch_hits().await }, ())?.unwrap();
|
||||
|
||||
Ok(html! {
|
||||
<span class={classes!("hit-count")}>{"Hit #"}{hits}</span>
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fallback() -> Html {
|
||||
html! {
|
||||
<span class={classes!("hit-count")}>{"Hit ..."}</span>
|
||||
}
|
||||
}
|
74
backend/caylon-studio/src/ui/json_context.rs
Normal file
74
backend/caylon-studio/src/ui/json_context.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
use std::rc::Rc;
|
||||
use yew::prelude::*;
|
||||
|
||||
pub enum JsonCtxAction {
|
||||
InsertElement {
|
||||
item: caylon_config::ElementConfig,
|
||||
index: usize,
|
||||
},
|
||||
RemoveElement {
|
||||
index: usize,
|
||||
},
|
||||
UpdateElement {
|
||||
new_item: caylon_config::ElementConfig,
|
||||
index: usize,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct JsonCtx {
|
||||
pub json: caylon_config::BaseConfig,
|
||||
}
|
||||
|
||||
pub type JsonContext = UseReducerHandle<JsonCtx>;
|
||||
|
||||
impl Eq for JsonCtx {}
|
||||
|
||||
impl JsonCtx {
|
||||
pub fn init() -> Self {
|
||||
Self {
|
||||
json: minimal_config(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Reducible for JsonCtx {
|
||||
type Action = JsonCtxAction;
|
||||
|
||||
fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
|
||||
let json_base = match action {
|
||||
JsonCtxAction::InsertElement { item, index } => {
|
||||
let mut items = self.json.items().to_owned();
|
||||
items.insert(index, item);
|
||||
caylon_config::BaseConfig::assemble(items, self.json.get_about().to_owned())
|
||||
}
|
||||
JsonCtxAction::RemoveElement { index } => {
|
||||
let mut items = self.json.items().to_owned();
|
||||
items.remove(index);
|
||||
caylon_config::BaseConfig::assemble(items, self.json.get_about().to_owned())
|
||||
}
|
||||
JsonCtxAction::UpdateElement { new_item, index } => {
|
||||
let mut items = self.json.items().to_owned();
|
||||
if items.len() > index {
|
||||
items[index] = new_item;
|
||||
}
|
||||
caylon_config::BaseConfig::assemble(items, self.json.get_about().to_owned())
|
||||
}
|
||||
};
|
||||
Rc::new(Self { json: json_base })
|
||||
}
|
||||
}
|
||||
|
||||
fn minimal_config() -> caylon_config::BaseConfig {
|
||||
caylon_config::BaseConfig::V0 {
|
||||
items: vec![],
|
||||
about: caylon_config::AboutConfig {
|
||||
name: env!("CARGO_PKG_NAME").to_owned(),
|
||||
version: env!("CARGO_PKG_VERSION").to_owned(),
|
||||
description: "Studio-generated UI layout for Caylon".to_owned(),
|
||||
url: Some("https://caylon.ngni.us".to_owned()),
|
||||
authors: vec!["NGnius".to_owned()],
|
||||
license: Some("MIT".to_owned()),
|
||||
},
|
||||
}
|
||||
}
|
20
backend/caylon-studio/src/ui/json_view.rs
Normal file
20
backend/caylon-studio/src/ui/json_view.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
use serde_json::to_string_pretty;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[function_component]
|
||||
pub fn JsonViewComponent() -> Html {
|
||||
log::debug!("Json view render");
|
||||
let json_ctx = use_context::<super::JsonContext>().expect("Missing JSON context");
|
||||
let pretty_json = to_string_pretty(&json_ctx.json).expect("Invalid JSON");
|
||||
let line_count = pretty_json.chars().filter(|&c| c == '\n').count() + 2;
|
||||
/*html! {
|
||||
<pre>
|
||||
{pretty_json.clone()}
|
||||
</pre>
|
||||
}*/
|
||||
html! {
|
||||
<div class={classes!("json-view")}>
|
||||
<textarea class={classes!("json-input")} rows={line_count.to_string()} value={pretty_json} readonly={true}/>
|
||||
</div>
|
||||
}
|
||||
}
|
14
backend/caylon-studio/src/ui/mod.rs
Normal file
14
backend/caylon-studio/src/ui/mod.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
mod app;
|
||||
pub mod element;
|
||||
mod footer_bar;
|
||||
mod hit_counter;
|
||||
mod json_context;
|
||||
mod json_view;
|
||||
mod title_bar;
|
||||
|
||||
pub use app::App;
|
||||
pub use footer_bar::FooterComponent;
|
||||
pub use hit_counter::HitCounterComponent;
|
||||
pub use json_context::{JsonContext, JsonCtx, JsonCtxAction};
|
||||
pub use json_view::JsonViewComponent;
|
||||
pub use title_bar::TitleComponent;
|
13
backend/caylon-studio/src/ui/title_bar.rs
Normal file
13
backend/caylon-studio/src/ui/title_bar.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
#[function_component]
|
||||
pub fn TitleComponent() -> Html {
|
||||
log::debug!("Header render");
|
||||
html! {
|
||||
<div class={classes!("header")}>
|
||||
<span class={classes!("header-elem")}>
|
||||
<h1>{"Caylon Studio"}</h1>
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
mod api;
|
||||
mod cli;
|
||||
mod config;
|
||||
mod consts;
|
||||
mod runtime;
|
||||
|
||||
|
@ -20,7 +19,7 @@ fn main() -> Result<(), ()> {
|
|||
|
||||
let filepath = cli_args.config.unwrap_or(consts::FILEPATH.into());
|
||||
|
||||
let kaylon_conf = config::BaseConfig::load(&filepath);
|
||||
let kaylon_conf = caylon_config::BaseConfig::load(&filepath);
|
||||
let (executor, sender) = runtime::RuntimeExecutor::new(kaylon_conf, filepath);
|
||||
|
||||
log::info!("Starting back-end ({} v{})", consts::PACKAGE_NAME, consts::PACKAGE_VERSION);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use usdpl_back::core::serdes::Primitive;
|
||||
|
||||
use crate::config::{ElementConfig, ActionConfig, TopLevelActionConfig};
|
||||
use caylon_config::{ElementConfig, ActionConfig, TopLevelActionConfig};
|
||||
|
||||
pub type ActError = String;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::process::Command;
|
|||
|
||||
use usdpl_back::core::serdes::Primitive;
|
||||
|
||||
use crate::config::CommandAction;
|
||||
use caylon_config::CommandAction;
|
||||
use super::{SeqAct, ActError};
|
||||
|
||||
/// Runs a CLI command in Bash
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::sync::mpsc::{Sender, self};
|
|||
|
||||
use usdpl_back::core::serdes::Primitive;
|
||||
|
||||
use crate::config::JavascriptAction;
|
||||
use caylon_config::JavascriptAction;
|
||||
use super::{SeqAct, ActError};
|
||||
use crate::runtime::{RuntimeIO, JavascriptCommand, Javascript};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use usdpl_back::core::serdes::Primitive;
|
|||
|
||||
use jmespath::{Expression, Variable};
|
||||
|
||||
use crate::config::JsonAction;
|
||||
use caylon_config::JsonAction;
|
||||
use super::{SeqAct, ActError};
|
||||
|
||||
pub struct JsonActor {
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::time::Duration;
|
|||
|
||||
use usdpl_back::core::serdes::Primitive;
|
||||
|
||||
use crate::config::ReadingConfig;
|
||||
use caylon_config::ReadingConfig;
|
||||
use super::{Act, SeqAct, ActError, TopLevelActorType};
|
||||
use crate::runtime::{RouterCommand, RuntimeIO};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use usdpl_back::core::serdes::Primitive;
|
||||
|
||||
use crate::config::SequenceAction;
|
||||
use caylon_config::SequenceAction;
|
||||
use super::{SeqAct, ActError, ActorType};
|
||||
|
||||
pub struct SequenceActor {
|
||||
|
|
|
@ -3,7 +3,7 @@ use usdpl_back::core::serdes::Primitive;
|
|||
|
||||
use crate::runtime::primitive_utils;
|
||||
|
||||
use crate::config::{TransformAction, TransformTypeAction, ReplaceTransformAction, ExpandTransformAction, LogTransformAction, LogLevel, PatternConfig};
|
||||
use caylon_config::{TransformAction, TransformTypeAction, ReplaceTransformAction, ExpandTransformAction, LogTransformAction, LogLevel, PatternConfig};
|
||||
use super::{SeqAct, ActError};
|
||||
|
||||
/// Changes the output or input of an act
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::sync::mpsc::Sender;
|
|||
use usdpl_back::core::serdes::Primitive;
|
||||
|
||||
use crate::api::SteamEvent;
|
||||
use crate::config::{AboutConfig, ElementConfig};
|
||||
use caylon_config::{AboutConfig, ElementConfig};
|
||||
|
||||
/// An API operation for the executor to perform
|
||||
pub enum QueueAction {
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::sync::mpsc::{self, Receiver, Sender};
|
|||
|
||||
use usdpl_back::core::serdes::Primitive;
|
||||
|
||||
use crate::config::{BaseConfig, ElementConfig};
|
||||
use caylon_config::{BaseConfig, ElementConfig};
|
||||
use crate::api::SteamEvent;
|
||||
use super::{QueueItem, QueueAction, Act, SeqAct};
|
||||
use super::{ResultRouter, RouterCommand, JavascriptRouter, JavascriptCommand};
|
||||
|
|
14
main.py
14
main.py
|
@ -11,6 +11,18 @@ class Plugin:
|
|||
# Asyncio-compatible long-running code, executed in a task when the plugin is loaded
|
||||
async def _main(self):
|
||||
# startup
|
||||
self.backend_proc = subprocess.Popen([PARENT_DIR + "/bin/backend", "--config", "./caylon.json"])
|
||||
self.backend_proc = subprocess.Popen(
|
||||
[PARENT_DIR + "/bin/backend", "--config", "./caylon.json"],
|
||||
env = dict(os.environ))
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def _unload(self):
|
||||
# shutdown
|
||||
if self.backend_proc is not None:
|
||||
self.backend_proc.terminate()
|
||||
try:
|
||||
self.backend_proc.wait(timeout=5) # 5 seconds timeout
|
||||
except subprocess.TimeoutExpired:
|
||||
self.backend_proc.kill()
|
||||
self.backend_proc = None
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
"typescript": "^4.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"decky-frontend-lib": "~3.18.10",
|
||||
"decky-frontend-lib": "~3.18.11",
|
||||
"react-icons": "^4.7.1",
|
||||
"usdpl-front": "file:src/usdpl_front"
|
||||
},
|
||||
|
|
|
@ -8,7 +8,7 @@ specifiers:
|
|||
'@rollup/plugin-typescript': ^8.5.0
|
||||
'@types/react': 16.14.0
|
||||
'@types/webpack': ^5.28.0
|
||||
decky-frontend-lib: ~3.18.10
|
||||
decky-frontend-lib: ~3.18.11
|
||||
react-icons: ^4.7.1
|
||||
rollup: ^2.79.1
|
||||
rollup-plugin-import-assets: ^1.1.1
|
||||
|
@ -18,7 +18,7 @@ specifiers:
|
|||
usdpl-front: file:src/usdpl_front
|
||||
|
||||
dependencies:
|
||||
decky-frontend-lib: 3.18.10
|
||||
decky-frontend-lib: 3.18.11
|
||||
react-icons: 4.7.1
|
||||
usdpl-front: file:src/usdpl_front
|
||||
|
||||
|
@ -380,8 +380,8 @@ packages:
|
|||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001456
|
||||
electron-to-chromium: 1.4.302
|
||||
caniuse-lite: 1.0.30001457
|
||||
electron-to-chromium: 1.4.305
|
||||
node-releases: 2.0.10
|
||||
update-browserslist-db: 1.0.10_browserslist@4.21.5
|
||||
dev: true
|
||||
|
@ -395,8 +395,8 @@ packages:
|
|||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/caniuse-lite/1.0.30001456:
|
||||
resolution: {integrity: sha512-XFHJY5dUgmpMV25UqaD4kVq2LsiaU5rS8fb0f17pCoXQiQslzmFgnfOxfvo1bTpTqf7dwG/N/05CnLCnOEKmzA==}
|
||||
/caniuse-lite/1.0.30001457:
|
||||
resolution: {integrity: sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==}
|
||||
dev: true
|
||||
|
||||
/chrome-trace-event/1.0.3:
|
||||
|
@ -420,8 +420,8 @@ packages:
|
|||
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
|
||||
dev: true
|
||||
|
||||
/decky-frontend-lib/3.18.10:
|
||||
resolution: {integrity: sha512-2mgbA3sSkuwQR/FnmhXVrcW6LyTS95IuL6muJAmQCruhBvXapDtjk1TcgxqMZxFZwGD1IPnemPYxHZll6IgnZw==}
|
||||
/decky-frontend-lib/3.18.11:
|
||||
resolution: {integrity: sha512-SLb3qWJc6CLNRqcbNyJsA8D6sXf7aix8/h6VqQTWjVmel6c3SkOR6b9KMvgFRzXumfRt7XGasBnZJmyCwH4BCQ==}
|
||||
dev: false
|
||||
|
||||
/deepmerge/4.3.0:
|
||||
|
@ -429,8 +429,8 @@ packages:
|
|||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/electron-to-chromium/1.4.302:
|
||||
resolution: {integrity: sha512-Uk7C+7aPBryUR1Fwvk9VmipBcN9fVsqBO57jV2ZjTm+IZ6BMNqu7EDVEg2HxCNufk6QcWlFsBkhQyQroB2VWKw==}
|
||||
/electron-to-chromium/1.4.305:
|
||||
resolution: {integrity: sha512-WETy6tG0CT5gm1O+xCbyapWNsCcmIvrn4NHViIGYo2AT8FV2qUCXdaB+WqYxSv/vS5mFqhBYnfZAAkVArjBmUg==}
|
||||
dev: true
|
||||
|
||||
/enhanced-resolve/5.12.0:
|
||||
|
|
|
@ -21,7 +21,7 @@ export async function initBackend() {
|
|||
// init usdpl
|
||||
await init_embedded();
|
||||
init_usdpl(USDPL_PORT);
|
||||
console.log("USDPL started for framework: " + target_usdpl());
|
||||
console.log("CAYLON: USDPL started for framework: " + target_usdpl());
|
||||
//setReady(true);
|
||||
}
|
||||
|
||||
|
|
Reference in a new issue