Basic KLE layout loader

main
Elnu 1 year ago
parent b4a4a3f38a
commit 09d93fac79

2
Cargo.lock generated

@ -393,6 +393,8 @@ dependencies = [
"strum", "strum",
"strum_macros", "strum_macros",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
] ]
[[package]] [[package]]

@ -14,3 +14,5 @@ serde_json = "1.0.105"
strum = "0.25.0" strum = "0.25.0"
strum_macros = "0.25.2" strum_macros = "0.25.2"
wasm-bindgen = "0.2.87" wasm-bindgen = "0.2.87"
wasm-bindgen-futures = "0.4.37"
web-sys = { version = "0.3.64", features = ["FileList", "Blob"] }

@ -1,18 +1,145 @@
#![feature(async_closure)]
mod render; mod render;
pub mod svg; pub mod svg;
use leptos::*; use leptos::{*, ev::SubmitEvent, html::Input};
use wasm_bindgen::JsCast; use wasm_bindgen::{JsCast, JsValue};
use web_sys::File;
use strum_macros::IntoStaticStr;
use derive_more::From;
#[derive(Clone)]
struct ResultMessageData {
title: String,
message: View,
colorway: Colorway,
}
#[derive(Default, Clone, Copy, IntoStaticStr)]
enum Colorway {
#[default]
#[strum(serialize = "plain")]
Plain,
#[strum(serialize = "info")]
Info,
#[strum(serialize = "ok")]
Ok,
#[strum(serialize = "warn")]
Warn,
#[strum(serialize = "bad")]
Bad,
}
#[component]
fn ResultMessage(cx: Scope, message: ReadSignal<Option<ResultMessageData>>) -> impl IntoView {
view! { cx,
<Show when=move || message().is_some() fallback=move |_| {}>
<div class=move || {
let colorway_str: &str = message().unwrap().colorway.into();
colorway_str.to_owned() + " box"
}>
<h3>{move || message().unwrap().title}</h3>
<p>{move || message().unwrap().message}</p>
</div>
</Show>
}
}
#[derive(From, IntoStaticStr)]
enum ReadKleError {
#[strum(serialize = "No file chosen")]
NoFile,
#[strum(serialize = "Failed to open file")]
TextAwait(JsValue),
#[strum(serialize = "Failed to parse file to string")]
ParseToString,
#[strum(serialize = "Failed to parse KLE JSON")]
Serde(serde_json::Error)
}
impl ToString for ReadKleError {
fn to_string(&self) -> String {
if let Self::TextAwait(error) = self {
if let Some(error) = error.as_string() {
return error;
}
} else if let Self::Serde(error) = self {
return error.to_string();
}
"".to_string()
}
}
async fn read_kle_from_file(file: &ReadSignal<Option<File>>) -> Result<kle_serial::Keyboard, ReadKleError> {
let file = match file() {
Some(file) => file,
None => return Err(ReadKleError::NoFile),
};
let file_contents = match wasm_bindgen_futures::JsFuture::from(file.text()).await?.as_string() {
Some(contents) => contents,
None => return Err(ReadKleError::ParseToString),
};
let keyboard = serde_json::from_str(&file_contents)?;
Ok(keyboard)
}
#[component]
fn KeyboardFromFile(cx: Scope, set_keyboard: WriteSignal<Option<kle_serial::Keyboard>>) -> impl IntoView {
let file_input = create_node_ref::<Input>(cx);
let (file, set_file) = create_signal(cx, Option::<File>::None);
let (result_message, set_result_message) = create_signal(cx, Option::<ResultMessageData>::None);
let on_submit = move |e: SubmitEvent| {
e.prevent_default();
spawn_local(async move {
match read_kle_from_file(&file).await {
Ok(keyboard) => {
set_result_message(Some(ResultMessageData {
title: "Success".to_owned(),
message: view! { cx,
"Loaded KLE layout "<b>{&keyboard.metadata.name}</b>" successfully!"
}.into_view(cx),
colorway: Colorway::Ok,
}));
set_keyboard(Some(keyboard));
},
Err(err) => set_result_message(Some(ResultMessageData {
message: view! { cx,
{
err.to_string()
}
}.into_view(cx),
title: <ReadKleError as Into<&str>>::into(err).to_string(),
colorway: Colorway::Bad,
}))
}
});
};
view! { cx,
<h3>"Load KLE JSON from file"</h3>
<ResultMessage message=result_message />
<form class="f-row align-items:center" on:submit=on_submit>
<input
type="file"
accept="application/json"
node_ref=file_input
on:change=move |_| {
set_file(file_input().unwrap().files().map(|files| files.get(0)).flatten());
}
/>
<Show when=move || file().is_some() fallback=move |_| {}>
<input type="submit" value="Load" />
</Show>
</form>
}
}
#[component] #[component]
fn App(cx: Scope) -> impl IntoView { fn App(cx: Scope) -> impl IntoView {
let (count, set_count) = create_signal(cx, 0); let (keyboard, set_keyboard) = create_signal(cx, None);
view! { cx, view! { cx,
<button on:click=move |_| set_count(count.get() + 1)> <KeyboardFromFile set_keyboard />
"Click me: "
{move || count.get()}
</button>
} }
} }

@ -55,7 +55,7 @@ Updating `cargoHash`:
version = "0.1.0"; version = "0.1.0";
buildAndTestSubdir = "dyesub-tool"; buildAndTestSubdir = "dyesub-tool";
buildInputs = with pkgs; [ trunk ]; buildInputs = with pkgs; [ trunk ];
cargoHash = "sha256-Bxc2koMKq8cGFiJtA0qL1geWQls1k2PgtByg7LZwdlc="; cargoHash = "sha256-UcFVv4lqmZW4Nwjps2u2cNU323hk8ezifeGU9dEZ7Ao=";
meta = meta // { meta = meta // {
description = "A tool for generating dye sublimation transfer sheet SVGs for Japanese thumb shift keycaps."; description = "A tool for generating dye sublimation transfer sheet SVGs for Japanese thumb shift keycaps.";
}; };

Loading…
Cancel
Save