Initial web studio functionality
This commit is contained in:
parent
82af952962
commit
f25366e349
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -46,6 +46,7 @@ yalc.lock
|
||||||
/backend/target
|
/backend/target
|
||||||
/backend/out
|
/backend/out
|
||||||
/bin
|
/bin
|
||||||
|
/**/target/
|
||||||
|
|
||||||
# packaged teasers
|
# packaged teasers
|
||||||
*.zip
|
*.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"
|
repository = "https://github.com/NGnius/kaylon"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
caylon-config = { version = "0.1.0", path = "./caylon-config" }
|
||||||
usdpl-back = { version = "0.10.0"}
|
usdpl-back = { version = "0.10.0"}
|
||||||
|
|
||||||
clap = { version = "3.2", features = ["derive", "std"], default-features = false }
|
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};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct AboutConfig {
|
pub struct AboutConfig {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub version: String,
|
pub version: String,
|
|
@ -1,6 +1,6 @@
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
#[serde(tag = "action")]
|
#[serde(tag = "action")]
|
||||||
pub enum TopLevelActionConfig {
|
pub enum TopLevelActionConfig {
|
||||||
#[serde(rename = "sequence")]
|
#[serde(rename = "sequence")]
|
||||||
|
@ -17,7 +17,7 @@ pub enum TopLevelActionConfig {
|
||||||
Json(JsonAction),
|
Json(JsonAction),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
#[serde(tag = "action")]
|
#[serde(tag = "action")]
|
||||||
pub enum ActionConfig {
|
pub enum ActionConfig {
|
||||||
#[serde(rename = "command")]
|
#[serde(rename = "command")]
|
||||||
|
@ -30,25 +30,25 @@ pub enum ActionConfig {
|
||||||
Json(JsonAction),
|
Json(JsonAction),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct SequenceAction {
|
pub struct SequenceAction {
|
||||||
pub steps: Vec<ActionConfig>,
|
pub steps: Vec<ActionConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct CommandAction {
|
pub struct CommandAction {
|
||||||
pub run: String,
|
pub run: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct MirrorAction;
|
pub struct MirrorAction;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct JavascriptAction {
|
pub struct JavascriptAction {
|
||||||
pub run: String,
|
pub run: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct JsonAction {
|
pub struct JsonAction {
|
||||||
pub jmespath: String,
|
pub jmespath: String,
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use super::{ElementConfig, AboutConfig};
|
use super::{ElementConfig, AboutConfig};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
#[serde(tag = "api-version")]
|
#[serde(tag = "api-version")]
|
||||||
pub enum BaseConfig {
|
pub enum BaseConfig {
|
||||||
#[serde(rename = "v0.0.0")]
|
#[serde(rename = "v0.0.0")]
|
||||||
|
@ -55,4 +55,8 @@ impl BaseConfig {
|
||||||
Self::V0 {items, ..} => items,
|
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;
|
use super::TopLevelActionConfig;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct ButtonConfig {
|
pub struct ButtonConfig {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub on_click: TopLevelActionConfig,
|
pub on_click: TopLevelActionConfig,
|
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use super::{ButtonConfig, ToggleConfig, SliderConfig, ReadingConfig, ResultDisplayConfig, EventDisplayConfig};
|
use super::{ButtonConfig, ToggleConfig, SliderConfig, ReadingConfig, ResultDisplayConfig, EventDisplayConfig};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
#[serde(tag = "element")]
|
#[serde(tag = "element")]
|
||||||
pub enum ElementConfig {
|
pub enum ElementConfig {
|
||||||
#[serde(rename = "button")]
|
#[serde(rename = "button")]
|
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use super::TopLevelActionConfig;
|
use super::TopLevelActionConfig;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct EventDisplayConfig {
|
pub struct EventDisplayConfig {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
/// Type of event to listen for
|
/// Type of event to listen for
|
||||||
|
@ -11,7 +11,7 @@ pub struct EventDisplayConfig {
|
||||||
pub on_event: TopLevelActionConfig,
|
pub on_event: TopLevelActionConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub enum EventType {
|
pub enum EventType {
|
||||||
#[serde(rename = "achievement")]
|
#[serde(rename = "achievement")]
|
||||||
Achievement,
|
Achievement,
|
|
@ -25,7 +25,7 @@ pub use transformer::{TransformAction, TransformTypeAction, ReplaceTransformActi
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dump_test() {
|
fn dump_test() {
|
||||||
let conf = BaseConfig::V0 {
|
let conf = BaseConfig::V0 {
|
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use super::TopLevelActionConfig;
|
use super::TopLevelActionConfig;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct ReadingConfig {
|
pub struct ReadingConfig {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
/// Period in milliseconds, or None/null for non-repeating actions
|
/// Period in milliseconds, or None/null for non-repeating actions
|
|
@ -1,6 +1,6 @@
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct ResultDisplayConfig {
|
pub struct ResultDisplayConfig {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
/// Index of element who's action's result will be used
|
/// Index of element who's action's result will be used
|
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use super::TopLevelActionConfig;
|
use super::TopLevelActionConfig;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct SliderConfig {
|
pub struct SliderConfig {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub min: u64,
|
pub min: u64,
|
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use super::TopLevelActionConfig;
|
use super::TopLevelActionConfig;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct ToggleConfig {
|
pub struct ToggleConfig {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct TransformAction {
|
pub struct TransformAction {
|
||||||
pub transformer: TransformTypeAction,
|
pub transformer: TransformTypeAction,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
#[serde(tag = "rule")]
|
#[serde(tag = "rule")]
|
||||||
pub enum TransformTypeAction {
|
pub enum TransformTypeAction {
|
||||||
#[serde(rename = "replace")]
|
#[serde(rename = "replace")]
|
||||||
|
@ -18,13 +18,13 @@ pub enum TransformTypeAction {
|
||||||
Log(LogTransformAction),
|
Log(LogTransformAction),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct ReplaceTransformAction {
|
pub struct ReplaceTransformAction {
|
||||||
/// Regex
|
/// Regex
|
||||||
pub patterns: Vec<PatternConfig>,
|
pub patterns: Vec<PatternConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct PatternConfig {
|
pub struct PatternConfig {
|
||||||
/// Regex
|
/// Regex
|
||||||
pub pattern: String,
|
pub pattern: String,
|
||||||
|
@ -43,17 +43,17 @@ pub struct PatternConfig {
|
||||||
pub x: Option<bool>,
|
pub x: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct ExpandTransformAction {
|
pub struct ExpandTransformAction {
|
||||||
pub format: String,
|
pub format: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct LogTransformAction {
|
pub struct LogTransformAction {
|
||||||
pub level: LogLevel,
|
pub level: LogLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub enum LogLevel {
|
pub enum LogLevel {
|
||||||
DEBUG,
|
DEBUG,
|
||||||
INFO,
|
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 api;
|
||||||
mod cli;
|
mod cli;
|
||||||
mod config;
|
|
||||||
mod consts;
|
mod consts;
|
||||||
mod runtime;
|
mod runtime;
|
||||||
|
|
||||||
|
@ -20,7 +19,7 @@ fn main() -> Result<(), ()> {
|
||||||
|
|
||||||
let filepath = cli_args.config.unwrap_or(consts::FILEPATH.into());
|
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);
|
let (executor, sender) = runtime::RuntimeExecutor::new(kaylon_conf, filepath);
|
||||||
|
|
||||||
log::info!("Starting back-end ({} v{})", consts::PACKAGE_NAME, consts::PACKAGE_VERSION);
|
log::info!("Starting back-end ({} v{})", consts::PACKAGE_NAME, consts::PACKAGE_VERSION);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use usdpl_back::core::serdes::Primitive;
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
use crate::config::{ElementConfig, ActionConfig, TopLevelActionConfig};
|
use caylon_config::{ElementConfig, ActionConfig, TopLevelActionConfig};
|
||||||
|
|
||||||
pub type ActError = String;
|
pub type ActError = String;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::process::Command;
|
||||||
|
|
||||||
use usdpl_back::core::serdes::Primitive;
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
use crate::config::CommandAction;
|
use caylon_config::CommandAction;
|
||||||
use super::{SeqAct, ActError};
|
use super::{SeqAct, ActError};
|
||||||
|
|
||||||
/// Runs a CLI command in Bash
|
/// Runs a CLI command in Bash
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::sync::mpsc::{Sender, self};
|
||||||
|
|
||||||
use usdpl_back::core::serdes::Primitive;
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
use crate::config::JavascriptAction;
|
use caylon_config::JavascriptAction;
|
||||||
use super::{SeqAct, ActError};
|
use super::{SeqAct, ActError};
|
||||||
use crate::runtime::{RuntimeIO, JavascriptCommand, Javascript};
|
use crate::runtime::{RuntimeIO, JavascriptCommand, Javascript};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
use jmespath::{Expression, Variable};
|
use jmespath::{Expression, Variable};
|
||||||
|
|
||||||
use crate::config::JsonAction;
|
use caylon_config::JsonAction;
|
||||||
use super::{SeqAct, ActError};
|
use super::{SeqAct, ActError};
|
||||||
|
|
||||||
pub struct JsonActor {
|
pub struct JsonActor {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::time::Duration;
|
||||||
|
|
||||||
use usdpl_back::core::serdes::Primitive;
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
use crate::config::ReadingConfig;
|
use caylon_config::ReadingConfig;
|
||||||
use super::{Act, SeqAct, ActError, TopLevelActorType};
|
use super::{Act, SeqAct, ActError, TopLevelActorType};
|
||||||
use crate::runtime::{RouterCommand, RuntimeIO};
|
use crate::runtime::{RouterCommand, RuntimeIO};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use usdpl_back::core::serdes::Primitive;
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
use crate::config::SequenceAction;
|
use caylon_config::SequenceAction;
|
||||||
use super::{SeqAct, ActError, ActorType};
|
use super::{SeqAct, ActError, ActorType};
|
||||||
|
|
||||||
pub struct SequenceActor {
|
pub struct SequenceActor {
|
||||||
|
|
|
@ -3,7 +3,7 @@ use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
use crate::runtime::primitive_utils;
|
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};
|
use super::{SeqAct, ActError};
|
||||||
|
|
||||||
/// Changes the output or input of an act
|
/// Changes the output or input of an act
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::sync::mpsc::Sender;
|
||||||
use usdpl_back::core::serdes::Primitive;
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
use crate::api::SteamEvent;
|
use crate::api::SteamEvent;
|
||||||
use crate::config::{AboutConfig, ElementConfig};
|
use caylon_config::{AboutConfig, ElementConfig};
|
||||||
|
|
||||||
/// An API operation for the executor to perform
|
/// An API operation for the executor to perform
|
||||||
pub enum QueueAction {
|
pub enum QueueAction {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::sync::mpsc::{self, Receiver, Sender};
|
||||||
|
|
||||||
use usdpl_back::core::serdes::Primitive;
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
use crate::config::{BaseConfig, ElementConfig};
|
use caylon_config::{BaseConfig, ElementConfig};
|
||||||
use crate::api::SteamEvent;
|
use crate::api::SteamEvent;
|
||||||
use super::{QueueItem, QueueAction, Act, SeqAct};
|
use super::{QueueItem, QueueAction, Act, SeqAct};
|
||||||
use super::{ResultRouter, RouterCommand, JavascriptRouter, JavascriptCommand};
|
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
|
# Asyncio-compatible long-running code, executed in a task when the plugin is loaded
|
||||||
async def _main(self):
|
async def _main(self):
|
||||||
# startup
|
# 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:
|
while True:
|
||||||
await asyncio.sleep(1)
|
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"
|
"typescript": "^4.9.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"decky-frontend-lib": "~3.18.10",
|
"decky-frontend-lib": "~3.18.11",
|
||||||
"react-icons": "^4.7.1",
|
"react-icons": "^4.7.1",
|
||||||
"usdpl-front": "file:src/usdpl_front"
|
"usdpl-front": "file:src/usdpl_front"
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,7 +8,7 @@ specifiers:
|
||||||
'@rollup/plugin-typescript': ^8.5.0
|
'@rollup/plugin-typescript': ^8.5.0
|
||||||
'@types/react': 16.14.0
|
'@types/react': 16.14.0
|
||||||
'@types/webpack': ^5.28.0
|
'@types/webpack': ^5.28.0
|
||||||
decky-frontend-lib: ~3.18.10
|
decky-frontend-lib: ~3.18.11
|
||||||
react-icons: ^4.7.1
|
react-icons: ^4.7.1
|
||||||
rollup: ^2.79.1
|
rollup: ^2.79.1
|
||||||
rollup-plugin-import-assets: ^1.1.1
|
rollup-plugin-import-assets: ^1.1.1
|
||||||
|
@ -18,7 +18,7 @@ specifiers:
|
||||||
usdpl-front: file:src/usdpl_front
|
usdpl-front: file:src/usdpl_front
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
decky-frontend-lib: 3.18.10
|
decky-frontend-lib: 3.18.11
|
||||||
react-icons: 4.7.1
|
react-icons: 4.7.1
|
||||||
usdpl-front: file:src/usdpl_front
|
usdpl-front: file:src/usdpl_front
|
||||||
|
|
||||||
|
@ -380,8 +380,8 @@ packages:
|
||||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite: 1.0.30001456
|
caniuse-lite: 1.0.30001457
|
||||||
electron-to-chromium: 1.4.302
|
electron-to-chromium: 1.4.305
|
||||||
node-releases: 2.0.10
|
node-releases: 2.0.10
|
||||||
update-browserslist-db: 1.0.10_browserslist@4.21.5
|
update-browserslist-db: 1.0.10_browserslist@4.21.5
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -395,8 +395,8 @@ packages:
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/caniuse-lite/1.0.30001456:
|
/caniuse-lite/1.0.30001457:
|
||||||
resolution: {integrity: sha512-XFHJY5dUgmpMV25UqaD4kVq2LsiaU5rS8fb0f17pCoXQiQslzmFgnfOxfvo1bTpTqf7dwG/N/05CnLCnOEKmzA==}
|
resolution: {integrity: sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/chrome-trace-event/1.0.3:
|
/chrome-trace-event/1.0.3:
|
||||||
|
@ -420,8 +420,8 @@ packages:
|
||||||
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
|
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/decky-frontend-lib/3.18.10:
|
/decky-frontend-lib/3.18.11:
|
||||||
resolution: {integrity: sha512-2mgbA3sSkuwQR/FnmhXVrcW6LyTS95IuL6muJAmQCruhBvXapDtjk1TcgxqMZxFZwGD1IPnemPYxHZll6IgnZw==}
|
resolution: {integrity: sha512-SLb3qWJc6CLNRqcbNyJsA8D6sXf7aix8/h6VqQTWjVmel6c3SkOR6b9KMvgFRzXumfRt7XGasBnZJmyCwH4BCQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/deepmerge/4.3.0:
|
/deepmerge/4.3.0:
|
||||||
|
@ -429,8 +429,8 @@ packages:
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/electron-to-chromium/1.4.302:
|
/electron-to-chromium/1.4.305:
|
||||||
resolution: {integrity: sha512-Uk7C+7aPBryUR1Fwvk9VmipBcN9fVsqBO57jV2ZjTm+IZ6BMNqu7EDVEg2HxCNufk6QcWlFsBkhQyQroB2VWKw==}
|
resolution: {integrity: sha512-WETy6tG0CT5gm1O+xCbyapWNsCcmIvrn4NHViIGYo2AT8FV2qUCXdaB+WqYxSv/vS5mFqhBYnfZAAkVArjBmUg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/enhanced-resolve/5.12.0:
|
/enhanced-resolve/5.12.0:
|
||||||
|
|
|
@ -21,7 +21,7 @@ export async function initBackend() {
|
||||||
// init usdpl
|
// init usdpl
|
||||||
await init_embedded();
|
await init_embedded();
|
||||||
init_usdpl(USDPL_PORT);
|
init_usdpl(USDPL_PORT);
|
||||||
console.log("USDPL started for framework: " + target_usdpl());
|
console.log("CAYLON: USDPL started for framework: " + target_usdpl());
|
||||||
//setReady(true);
|
//setReady(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue