diff --git a/.gitignore b/.gitignore
index cb8c9e0..1ce99de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
/target
-.direnv
\ No newline at end of file
+.direnv
+dist
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index ab10e89..594941c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -393,6 +393,8 @@ dependencies = [
"strum",
"strum_macros",
"wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
]
[[package]]
diff --git a/dist/index.html b/dist/index.html
deleted file mode 100644
index 81790f4..0000000
--- a/dist/index.html
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
- dyesub-tool
-
-
-
\ No newline at end of file
diff --git a/dyesub-tool/.gitignore b/dyesub-tool/.gitignore
deleted file mode 100644
index 9bfc447..0000000
--- a/dyesub-tool/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-layout.json
-output.svg
-dist
\ No newline at end of file
diff --git a/dyesub-tool/Cargo.toml b/dyesub-tool/Cargo.toml
index eea0e69..d3f1121 100644
--- a/dyesub-tool/Cargo.toml
+++ b/dyesub-tool/Cargo.toml
@@ -14,3 +14,5 @@ serde_json = "1.0.105"
strum = "0.25.0"
strum_macros = "0.25.2"
wasm-bindgen = "0.2.87"
+wasm-bindgen-futures = "0.4.37"
+web-sys = { version = "0.3.64", features = ["FileList", "Blob"] }
diff --git a/dyesub-tool/src/main.rs b/dyesub-tool/src/main.rs
index 779ad7b..890fc29 100644
--- a/dyesub-tool/src/main.rs
+++ b/dyesub-tool/src/main.rs
@@ -1,18 +1,145 @@
+#![feature(async_closure)]
+
mod render;
pub mod svg;
-use leptos::*;
-use wasm_bindgen::JsCast;
+use leptos::{*, ev::SubmitEvent, html::Input};
+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