Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI overhaul #37

Merged
merged 12 commits into from
Sep 24, 2024
23 changes: 19 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ leptos = { version = "0.6.14", features = ["csr"] }
console_error_panic_hook = "0.1.7"
hex-conservative = "0.1.1"
js-sys = "0.3.70"
web-sys = { version = "0.3.70", features = ["Navigator", "Clipboard"] }
either = "1.13.0"
wasm-bindgen-futures = "0.4.43"
gloo-timers = { version = "0.3.0", features = ["futures"] }

[dev-dependencies]
wasm-bindgen-test = "0.3.43"
12 changes: 0 additions & 12 deletions src/assets/js/copy_button.js

This file was deleted.

65 changes: 64 additions & 1 deletion src/assets/style/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,67 @@ p {
@keyframes grow {
0% {max-height: 0px; padding-top: 0px; padding-bottom: 0px}
100% {max-height: 91px;}
}
}

.tabs {
overflow: hidden;
border: 1px solid #ccc;
background-color: #f1f1f1;

button {
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;

&:hover {
background-color: #ddd; // Add hover effect
}

&.active {
background-color: #bbb; // Darker color for active tabs
}
}
}

.error-box {
border: 1px dotted #ff0000;
color: #ff0000;
word-break: break-word;
margin-top: 10px;
padding: 10px;
overflow-x: scroll;

scrollbar-color: $background-dark #b00000;
scrollbar-width: thin;
}

.button-row {
display: flex;
}

.button-col {
display: flex;
flex-direction: column;
}

.submit-button, .push-button, .pop-button, .copy-button {
border: 1px solid #5134E0;
padding: 4px;
border-radius: 4px;
display: flex;
justify-content: center;
align-items: center;
color: #ccc;
background: #191C21;

&:hover {
background: #5134e0;
}
}

input:invalid {
background-color: lightpink;
}
169 changes: 23 additions & 146 deletions src/components/app.rs
Original file line number Diff line number Diff line change
@@ -1,157 +1,34 @@
use leptos::*;
use wasm_bindgen::prelude::*;
use std::collections::HashMap;

use super::analysis::Analysis;
use super::examples::{ExampleProgramDescription, SelectExampleProgram};
use super::merkle::{self, MerkleExplorer};
use super::parser::ParseError;
use crate::components::run_window::{HashedData, RunWindow, SignedData, SigningKeys};
use leptos::{component, create_rw_signal, provide_context, view, IntoView, RwSignal};
use simfony::str::WitnessName;

use crate::function::Runner;
use crate::util;
use super::program_window::{ProgramWindow, TxEnv};

#[wasm_bindgen(module = "/src/assets/js/button_effects.js")]
extern "C" {
fn button_success_animation();
fn button_fail_animation();
}

#[wasm_bindgen(module = "/src/assets/js/copy_button.js")]
extern "C" {
fn copy_program(text: &str);
}
#[derive(Copy, Clone)]
pub struct ProgramWrapper(pub RwSignal<String>);

#[wasm_bindgen(module = "/src/assets/js/badger.js")]
extern "C" {
fn load_badger();
fn laser_eyes();
fn hide_badger(val: bool);
fn hide_badger_timed();
}
#[derive(Copy, Clone)]
pub struct WitnessWrapper(pub RwSignal<HashMap<WitnessName, simfony::value::Value>>);

#[component]
pub fn App() -> impl IntoView {
create_effect(move |_| {
load_badger();
});

let (program_str, set_program_str) = create_signal("".to_string());
let (run_result, set_run_result) = create_signal::<Option<Result<String, String>>>(None);
let (is_running, set_is_running) = create_signal(false);
let (name, set_name) = create_signal::<Option<String>>(None);
let (graph_toggle, set_graph_toggle) = create_signal(false);

let program_result = Signal::derive(move || util::program_from_string(&program_str.get()));
let program = Signal::derive(move || program_result.get().ok().map(|x| x.simplicity));
let parse_error = Signal::derive(move || program_result.get().err());

create_effect(move |_| match parse_error.get() {
Some(_) => hide_badger(true),
None => hide_badger(false),
});

let update_program_str = move |s: String| {
set_program_str.set(s);
set_run_result.set(None);
};
let run_program = move || {
let program = match program_result.get() {
Ok(program) => program,
Err(..) => return,
};
set_is_running.set(true);
let simplicity = program.simplicity.clone();
let mut runner = Runner::for_program(program);
match runner.run() {
Ok(_) => {
set_run_result.set(Some(Ok("Program success".to_string())));
laser_eyes();
button_success_animation();
merkle::reload_graph(simplicity);
set_is_running.set(false);
}
Err(error) => {
hide_badger_timed();
set_run_result.set(Some(Err(error.to_string())));
button_fail_animation();
set_is_running.set(false);
}
}
};
let program_text = create_rw_signal("".to_string());
provide_context(ProgramWrapper(program_text));
let witness_values = create_rw_signal(HashMap::new());
provide_context(WitnessWrapper(witness_values));
let tx_env = TxEnv::new(0, 0);
provide_context(tx_env);
let signing_keys = SigningKeys::new(1);
provide_context(signing_keys);
let signed_data = SignedData::new(tx_env.environment());
provide_context(signed_data);
let hashed_data = HashedData::new(1);
provide_context(hashed_data);

view! {
<div class="input-page">
<div class="page-header">
<img class="header-icon" src="/images/simplicity_logo.svg" />
</div>

<div class="container center intro">
<h1 class="intro-title">Simfony IDE</h1>
<p class="intro-text text-grey">
<a href="https://github.com/BlockstreamResearch/simfony" target="blank">Simfony</a>
" is a high-level language for writing Bitcoin smart contracts."
</p>
<p class="intro-text text-grey">
"Simfony looks and feels like "
<a href="https://www.rust-lang.org" target="blank">Rust</a>
". Just how Rust compiles down to assembly language, Simfony compiles down to "
<a href="https://github.com/BlockstreamResearch/simplicity" target="blank">Simplicity</a>
" bytecode. Developers write Simfony, full nodes execute Simplicity."
</p>
</div>

<div class="container">
<ParseError maybe_error=parse_error/>

<div class="program-input">
<div class="program-input-header">
<canvas id="badger-canvas" width="3200" height="3200" style="width: 1600px; height: 1600px;"></canvas>
<div class="program-input-intro">
<h2>Program</h2>
<p>Select an example program or enter your own program below.</p>
</div>
<SelectExampleProgram update_program_str=update_program_str set_name=set_name/>
</div>

<div class="program-input-field-container">
<div class="copy-holder">
<span id="copy-button-success">Program copied</span>
<span class="copy-button" on:click=move |_| copy_program(&program_str.get())><i class="far fa-copy"></i></span>
</div>
<textarea class="program-input-field"
on:keydown=move |event: web_sys::KeyboardEvent| {
if event.ctrl_key() && event.key_code() == 13 { // 13 is the Enter key
run_program();
}
}
prop:value=move || program_str.get()
on:input=move |event| update_program_str(event_target_value(&event))
placeholder="Enter your program here"
rows="15" cols="80"
spellcheck="false"
/>
</div>

<div class="flex program-input-footer">
<ExampleProgramDescription name=name/>
<div class="run-button">
<button
on:click=move |_| run_program()
disabled=move || parse_error.get().is_some() | is_running.get()
>
"Run program"
</button>
</div>
</div>
</div>

<Analysis
program=program
run_result=run_result/>
<MerkleExplorer
run_result=run_result
graph_toggle=graph_toggle
set_graph_toggle=set_graph_toggle/>
</div>
</div>
<ProgramWindow />
<RunWindow />
}
}
57 changes: 57 additions & 0 deletions src/components/apply_changes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use leptos::{
create_rw_signal, spawn_local, view, Fragment, IntoView, RwSignal, SignalGet, SignalSet, View,
};

#[derive(Copy, Clone, Debug)]
pub struct ApplyChanges {
apply_succeeded: RwSignal<Option<bool>>,
}

impl Default for ApplyChanges {
fn default() -> Self {
Self {
apply_succeeded: create_rw_signal(None),
}
}
}

impl ApplyChanges {
pub fn set_success(self, success: bool) {
spawn_local(async move {
self.apply_succeeded.set(Some(success));
gloo_timers::future::TimeoutFuture::new(500).await;
self.apply_succeeded.set(None);
});
}
}

impl IntoView for ApplyChanges {
fn into_view(self) -> View {
let apply_button_view = move || -> Fragment {
match self.apply_succeeded.get() {
None => view! {
Apply changes
<i></i>
},
Some(true) => view! {
Applied
<i class="fas fa-check"></i>
},
Some(false) => view! {
Error
<i class="fas fa-times"></i>
},
}
};

view! {
<button
class="submit-button"
type="submit"
>
{apply_button_view}
</button>
}
.into_view()
}
}
Loading
Loading