This commit is contained in:
victor 2025-09-04 15:27:28 +02:00
parent f9ae2949e4
commit 41cb220119
8 changed files with 650 additions and 845 deletions

1009
wallet/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"]
[dependencies]
egui = "0.32"
eframe = { version = "0.32", default-features = false, features = [
"accesskit", # Make egui compatible with screen readers. NOTE: adds a lot of dependencies.
# "accesskit", # Make egui compatible with screen readers. NOTE: adds a lot of dependencies.
"default_fonts", # Embed the default egui fonts.
"glow", # Use the glow rendering backend. Alternative: "wgpu".
"persistence", # Enable restoring app state when restarting the app.
@ -21,10 +21,12 @@ log = "0.4.27"
# You only need serde if you want app persistence:
serde = { version = "1.0.219", features = ["derive"] }
egui_file = "0.23.1"
# native:
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
env_logger = "0.11.8"
k256 = "0.13.4"
# web:
[target.'cfg(target_arch = "wasm32")'.dependencies]

136
wallet/dist/index.html vendored
View File

@ -1,12 +1,19 @@
<!DOCTYPE html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!-- Disable zooming: -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<head>
<meta charset="utf-8" />
<title>My App</title>
<!-- change this to your project name -->
<title>eframe template</title>
<!-- config for our rust wasm binary. go to https://trunkrs.dev/assets/#rust for more customization -->
<script type="module">
import init, * as bindings from '/wallet-5d1b8085460fdcf6.js';
const wasm = await init({ module_or_path: '/wallet-5d1b8085460fdcf6_bg.wasm' });
import init, * as bindings from '/wallet-817ceb420679efc7.js';
const wasm = await init({ module_or_path: '/wallet-817ceb420679efc7_bg.wasm' });
window.wasmBindings = bindings;
@ -15,9 +22,123 @@ window.wasmBindings = bindings;
dispatchEvent(new CustomEvent("TrunkApplicationStarted", {detail: {wasm}}));
</script>
<link rel="modulepreload" href="/wallet-5d1b8085460fdcf6.js" crossorigin="anonymous" integrity="sha384-phKOKZTFYPix/VFiKjin1txUn2nxtPk30fPdrro5PTdkGPq/+5dP1m7tONQ21dTf"><link rel="preload" href="/wallet-5d1b8085460fdcf6_bg.wasm" crossorigin="anonymous" integrity="sha384-gEKa9S/QlWrheWFbm/lNELMj8L9QLKbE4RYtCB9dU2Jl3Y4OvRmehoHMdNb7JceO" as="fetch" type="application/wasm"></head>
<!-- this is the base url relative to which other urls will be constructed. trunk will insert this from the public-url option -->
<base href="/" />
<meta name="theme-color" media="(prefers-color-scheme: light)" content="white">
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#404040">
<style>
html {
/* Remove touch delay: */
touch-action: manipulation;
}
body {
/* Light mode background color for what is not covered by the egui canvas,
or where the egui canvas is translucent. */
background: #909090;
}
@media (prefers-color-scheme: dark) {
body {
/* Dark mode background color for what is not covered by the egui canvas,
or where the egui canvas is translucent. */
background: #404040;
}
}
/* Allow canvas to fill entire web page: */
html,
body {
overflow: hidden;
margin: 0 !important;
padding: 0 !important;
height: 100%;
width: 100%;
}
/* Make canvas fill entire document: */
canvas {
margin-right: auto;
margin-left: auto;
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.centered {
margin-right: auto;
margin-left: auto;
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #f0f0f0;
font-size: 24px;
font-family: Ubuntu-Light, Helvetica, sans-serif;
text-align: center;
}
/* ---------------------------------------------- */
/* Loading animation from https://loading.io/css/ */
.lds-dual-ring {
display: inline-block;
width: 24px;
height: 24px;
}
.lds-dual-ring:after {
content: " ";
display: block;
width: 24px;
height: 24px;
margin: 0px;
border-radius: 50%;
border: 3px solid #fff;
border-color: #fff transparent #fff transparent;
animation: lds-dual-ring 1.2s linear infinite;
}
@keyframes lds-dual-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
<link rel="modulepreload" href="/wallet-817ceb420679efc7.js" crossorigin="anonymous" integrity="sha384-6d8RKSfgbqKZ044wKpV3fQtbdvcpwUIHRJNZ5U/TQh0hA0gn9XWhqw4nstkFYXQN"><link rel="preload" href="/wallet-817ceb420679efc7_bg.wasm" crossorigin="anonymous" integrity="sha384-YCX09L5bwJK+gubBuzo6JIyHVHvIqyl1r/VCD3WWnXzfMaihUYP2Z0zVvHjy0ely" as="fetch" type="application/wasm"></head>
<body>
<div id="app"></div>
<!-- The WASM code will resize the canvas dynamically -->
<!-- the id is hardcoded in main.rs . so, make sure both match. -->
<canvas id="the_canvas_id"></canvas>
<!-- the loading spinner will be removed in main.rs -->
<div class="centered" id="loading_text">
<p style="font-size:16px">
Loading…
</p>
<div class="lds-dual-ring"></div>
</div>
<!--Register Service Worker. this will cache the wasm / js scripts for offline use (for PWA functionality). -->
<!-- Force refresh (Ctrl + F5) to load the latest files instead of cached files -->
<script>
// We disable caching during development so that we always view the latest version.
if ('serviceWorker' in navigator && window.location.hash !== "#dev") {
window.addEventListener('load', function () {
navigator.serviceWorker.register('sw.js');
});
}
</script>
<script>"use strict";
(function () {
@ -148,4 +269,7 @@ dispatchEvent(new CustomEvent("TrunkApplicationStarted", {detail: {wasm}}));
})()
</script></body>
</html>
<!-- Powered by egui: https://github.com/emilk/egui/ -->

View File

@ -197,24 +197,25 @@ function debugString(val) {
// TODO we could test for more things here, like `Set`s and `Map`s.
return className;
}
function __wbg_adapter_30(arg0, arg1, arg2) {
wasm.closure272_externref_shim(arg0, arg1, arg2);
}
function takeFromExternrefTable0(idx) {
const value = wasm.__wbindgen_export_1.get(idx);
wasm.__externref_table_dealloc(idx);
return value;
}
function __wbg_adapter_35(arg0, arg1) {
const ret = wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h0cb79540ee82b24b_multivalue_shim(arg0, arg1);
function __wbg_adapter_30(arg0, arg1) {
const ret = wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h27c24e5264968cd2_multivalue_shim(arg0, arg1);
if (ret[1]) {
throw takeFromExternrefTable0(ret[0]);
}
}
function __wbg_adapter_33(arg0, arg1, arg2) {
wasm.closure246_externref_shim(arg0, arg1, arg2);
}
function __wbg_adapter_38(arg0, arg1, arg2) {
wasm.closure350_externref_shim(arg0, arg1, arg2);
wasm.closure352_externref_shim(arg0, arg1, arg2);
}
const __wbindgen_enum_ResizeObserverBoxOptions = ["border-box", "content-box", "device-pixel-content-box"];
@ -1432,20 +1433,20 @@ function __wbg_get_imports() {
const ret = false;
return ret;
};
imports.wbg.__wbindgen_closure_wrapper2021 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 273, __wbg_adapter_30);
imports.wbg.__wbindgen_closure_wrapper1948 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 243, __wbg_adapter_30);
return ret;
};
imports.wbg.__wbindgen_closure_wrapper2023 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 273, __wbg_adapter_30);
imports.wbg.__wbindgen_closure_wrapper1950 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 243, __wbg_adapter_33);
return ret;
};
imports.wbg.__wbindgen_closure_wrapper2025 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 273, __wbg_adapter_35);
imports.wbg.__wbindgen_closure_wrapper1952 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 243, __wbg_adapter_33);
return ret;
};
imports.wbg.__wbindgen_closure_wrapper2265 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 351, __wbg_adapter_38);
imports.wbg.__wbindgen_closure_wrapper2288 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 353, __wbg_adapter_38);
return ret;
};
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {

View File

@ -1,11 +1,135 @@
<!DOCTYPE html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!-- Disable zooming: -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<head>
<meta charset="utf-8" />
<title>My App</title>
<link data-trunk rel="rust" />
<!-- change this to your project name -->
<title>eframe template</title>
<!-- config for our rust wasm binary. go to https://trunkrs.dev/assets/#rust for more customization -->
<link data-trunk rel="rust" data-wasm-opt="2" />
<!-- this is the base url relative to which other urls will be constructed. trunk will insert this from the public-url option -->
<base data-trunk-public-url />
<meta name="theme-color" media="(prefers-color-scheme: light)" content="white">
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#404040">
<style>
html {
/* Remove touch delay: */
touch-action: manipulation;
}
body {
/* Light mode background color for what is not covered by the egui canvas,
or where the egui canvas is translucent. */
background: #909090;
}
@media (prefers-color-scheme: dark) {
body {
/* Dark mode background color for what is not covered by the egui canvas,
or where the egui canvas is translucent. */
background: #404040;
}
}
/* Allow canvas to fill entire web page: */
html,
body {
overflow: hidden;
margin: 0 !important;
padding: 0 !important;
height: 100%;
width: 100%;
}
/* Make canvas fill entire document: */
canvas {
margin-right: auto;
margin-left: auto;
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.centered {
margin-right: auto;
margin-left: auto;
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #f0f0f0;
font-size: 24px;
font-family: Ubuntu-Light, Helvetica, sans-serif;
text-align: center;
}
/* ---------------------------------------------- */
/* Loading animation from https://loading.io/css/ */
.lds-dual-ring {
display: inline-block;
width: 24px;
height: 24px;
}
.lds-dual-ring:after {
content: " ";
display: block;
width: 24px;
height: 24px;
margin: 0px;
border-radius: 50%;
border: 3px solid #fff;
border-color: #fff transparent #fff transparent;
animation: lds-dual-ring 1.2s linear infinite;
}
@keyframes lds-dual-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<div id="app"></div>
<!-- The WASM code will resize the canvas dynamically -->
<!-- the id is hardcoded in main.rs . so, make sure both match. -->
<canvas id="the_canvas_id"></canvas>
<!-- the loading spinner will be removed in main.rs -->
<div class="centered" id="loading_text">
<p style="font-size:16px">
Loading…
</p>
<div class="lds-dual-ring"></div>
</div>
<!--Register Service Worker. this will cache the wasm / js scripts for offline use (for PWA functionality). -->
<!-- Force refresh (Ctrl + F5) to load the latest files instead of cached files -->
<script>
// We disable caching during development so that we always view the latest version.
if ('serviceWorker' in navigator && window.location.hash !== "#dev") {
window.addEventListener('load', function () {
navigator.serviceWorker.register('sw.js');
});
}
</script>
</body>
</html>
<!-- Powered by egui: https://github.com/emilk/egui/ -->

View File

@ -1,109 +1,125 @@
use egui_file::FileDialog;
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
#[derive(serde::Deserialize, serde::Serialize)]
#[serde(default)] // if we add new fields, give them default values when deserializing old state
pub struct TemplateApp {
// Example stuff:
label: String,
// Example stuff:
label: String,
#[serde(skip)] // This how you opt-out of serialization of a field
value: f32,
#[serde(skip)] // This how you opt-out of serialization of a field
value: f32,
#[serde(skip)]
file_dialog: Option<FileDialog>,
}
impl Default for TemplateApp {
fn default() -> Self {
Self {
// Example stuff:
label: "Hello World!".to_owned(),
value: 2.7,
}
fn default() -> Self {
Self {
// Example stuff:
label: "Hello World!".to_owned(),
value: 2.7,
file_dialog: None,
}
}
}
impl TemplateApp {
/// Called once before the first frame.
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
// This is also where you can customize the look and feel of egui using
// `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`.
/// Called once before the first frame.
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
// This is also where you can customize the look and feel of egui using
// `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`.
// Load previous app state (if any).
// Note that you must enable the `persistence` feature for this to work.
if let Some(storage) = cc.storage {
eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default()
} else {
Default::default()
}
// Load previous app state (if any).
// Note that you must enable the `persistence` feature for this to work.
if let Some(storage) = cc.storage {
eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default()
} else {
Default::default()
}
}
}
impl eframe::App for TemplateApp {
/// Called by the framework to save state before shutdown.
fn save(&mut self, storage: &mut dyn eframe::Storage) {
eframe::set_value(storage, eframe::APP_KEY, self);
}
/// Called by the framework to save state before shutdown.
fn save(&mut self, storage: &mut dyn eframe::Storage) {
eframe::set_value(storage, eframe::APP_KEY, self);
}
/// Called each time the UI needs repainting, which may be many times per second.
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
// Put your widgets into a `SidePanel`, `TopBottomPanel`, `CentralPanel`, `Window` or `Area`.
// For inspiration and more examples, go to https://emilk.github.io/egui
/// Called each time the UI needs repainting, which may be many times per second.
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
// Put your widgets into a `SidePanel`, `TopBottomPanel`, `CentralPanel`, `Window` or `Area`.
// For inspiration and more examples, go to https://emilk.github.io/egui
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
// The top panel is often a good place for a menu bar:
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
// The top panel is often a good place for a menu bar:
egui::MenuBar::new().ui(ui, |ui| {
// NOTE: no File->Quit on web pages!
let is_web = cfg!(target_arch = "wasm32");
if !is_web {
ui.menu_button("File", |ui| {
if ui.button("Quit").clicked() {
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
}
});
ui.add_space(16.0);
}
egui::widgets::global_theme_preference_buttons(ui);
});
});
egui::CentralPanel::default().show(ctx, |ui| {
// The central panel the region left after adding TopPanel's and SidePanel's
ui.heading("eframe template");
ui.horizontal(|ui| {
ui.label("Write something: ");
ui.text_edit_singleline(&mut self.label);
});
ui.add(egui::Slider::new(&mut self.value, 0.0..=10.0).text("value"));
if ui.button("Increment").clicked() {
self.value += 1.0;
egui::MenuBar::new().ui(ui, |ui| {
// NOTE: no File->Quit on web pages!
let is_web = cfg!(target_arch = "wasm32");
if !is_web {
ui.menu_button("File", |ui| {
if ui.button("Quit").clicked() {
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
}
});
ui.add_space(16.0);
}
ui.separator();
egui::widgets::global_theme_preference_buttons(ui);
});
});
ui.add(egui::github_link_file!(
"https://github.com/emilk/eframe_template/blob/main/",
"Source code."
));
egui::CentralPanel::default().show(ctx, |ui| {
// The central panel the region left after adding TopPanel's and SidePanel's
ui.heading("eframe template");
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
powered_by_egui_and_eframe(ui);
egui::warn_if_debug_build(ui);
});
});
}
ui.horizontal(|ui| {
ui.label("Write something: ");
ui.text_edit_singleline(&mut self.label);
});
if ui.button("Open File").clicked() {
let mut dialog = FileDialog::open_file(None);
dialog.open();
self.file_dialog = Some(dialog);
}
ui.add(egui::Slider::new(&mut self.value, 0.0..=10.0).text("value"));
if ui.button("Increment").clicked() {
self.value += 1.0;
}
ui.separator();
if let Some(dialog) = &mut self.file_dialog {
dialog.pick_file()
}
ui.add(egui::github_link_file!(
"https://github.com/emilk/eframe_template/blob/main/",
"Source code."
));
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
powered_by_egui_and_eframe(ui);
egui::warn_if_debug_build(ui);
});
});
}
}
fn powered_by_egui_and_eframe(ui: &mut egui::Ui) {
ui.horizontal(|ui| {
ui.spacing_mut().item_spacing.x = 0.0;
ui.label("Powered by ");
ui.hyperlink_to("egui", "https://github.com/emilk/egui");
ui.label(" and ");
ui.hyperlink_to(
"eframe",
"https://github.com/emilk/egui/tree/master/crates/eframe",
);
ui.label(".");
});
ui.horizontal(|ui| {
ui.spacing_mut().item_spacing.x = 0.0;
ui.label("Powered by ");
ui.hyperlink_to("egui", "https://github.com/emilk/egui");
ui.label(" and ");
ui.hyperlink_to(
"eframe",
"https://github.com/emilk/egui/tree/master/crates/eframe",
);
ui.label(".");
});
}

View File

@ -9,12 +9,7 @@ fn main() -> eframe::Result {
let native_options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size([400.0, 300.0])
.with_min_inner_size([300.0, 220.0])
.with_icon(
// NOTE: Adding an icon is optional
eframe::icon_data::from_png_bytes(&include_bytes!("../assets/icon-256.png")[..])
.expect("Failed to load icon"),
),
.with_min_inner_size([300.0, 220.0]),
..Default::default()
};
eframe::run_native(