From 0d98b4016c6296b38a4d081991ccd223f2109fc4 Mon Sep 17 00:00:00 2001 From: Matthew Gordon Date: Thu, 12 Jun 2025 10:53:10 -0300 Subject: [PATCH] Add some error handling --- src/lib.rs | 2 +- src/wasm/mod.rs | 34 +++++++++++++++++++++++++--------- web/pteropus-frame.js | 23 ++++++++++++++++++----- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f7359f0..98df44e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,6 @@ pub fn main() { #[cfg(target_arch = "wasm32")] #[wasm_bindgen] -pub async fn init_pteropus() -> wasm::PteropusCanvas { +pub async fn init_pteropus() -> Result { run(App::new(), Rc::new(Model::new())).await } diff --git a/src/wasm/mod.rs b/src/wasm/mod.rs index 9dba6ee..e423d97 100644 --- a/src/wasm/mod.rs +++ b/src/wasm/mod.rs @@ -9,7 +9,7 @@ use { use crate::{ app::{App, Model}, mvu, - mvu::{Event, File, MvuApp, Size2i}, + mvu::{Event, MvuApp, Size2i}, }; struct FrameTimer { @@ -52,9 +52,23 @@ pub struct PteropusCanvas { model: Rc, } +#[wasm_bindgen(getter_with_clone)] +#[derive(Clone)] +pub struct PteropusInitError { + pub message: String, +} + +impl PteropusInitError { + fn new>(message: M) -> Self { + Self { + message: message.into(), + } + } +} + #[wasm_bindgen] impl PteropusCanvas { - async fn init(&self) { + async fn init(&self) -> Result<(), PteropusInitError> { let html_canvas = self.get_canvas(); let size = Size2i { width: html_canvas.client_width() as u32, @@ -62,15 +76,17 @@ impl PteropusCanvas { }; let instance = wgpu::Instance::default(); let surface_target = SurfaceTarget::Canvas(html_canvas); - let surface = instance - .create_surface(surface_target) - .expect("create graphics surface for canvas"); - + let surface = instance.create_surface(surface_target).map_err(|_| { + PteropusInitError::new( + "Could not initialize canvas. This browser may not support WebGPU.", + ) + })?; self.app .lock() .expect("get app mutex") .init(&instance, surface, size, Box::new(FrameTimer::new())) .await; + Ok(()) } #[wasm_bindgen] @@ -134,11 +150,11 @@ impl PteropusCanvas { } } -pub async fn run(app: App, model: Rc) -> PteropusCanvas { +pub async fn run(app: App, model: Rc) -> Result { let app = Rc::new(Mutex::new(app)); std::panic::set_hook(Box::new(console_error_panic_hook::hook)); console_log::init_with_level(log::Level::Info).expect("Couldn't initialize logger"); let pteropus_canvas = PteropusCanvas { app, model }; - pteropus_canvas.init().await; - pteropus_canvas + pteropus_canvas.init().await?; + Ok(pteropus_canvas) } diff --git a/web/pteropus-frame.js b/web/pteropus-frame.js index 5977c99..427f120 100644 --- a/web/pteropus-frame.js +++ b/web/pteropus-frame.js @@ -1,4 +1,4 @@ -import init, {init_pteropus} from '../pkg/pteropus.js'; +import init, {init_pteropus, PteropusInitError} from '../pkg/pteropus.js'; function fileDragOverHandler(event) { event.preventDefault(); @@ -7,13 +7,26 @@ function fileDragOverHandler(event) { async function run() { await init(); - let pteropus = await init_pteropus(); + try { + let pteropus = await init_pteropus(); + } catch(e) { + const errorParagraph = document.createElement("p"); + if(e instanceof PteropusInitError) { + const errorMessage = document.createTextNode(e.message); + errorParagraph.appendChild(errorMessage); + } else { + const errorMessage = document.createTextNode("Something unexpected happened."); + errorParagraph.appendChild(errorMessage); + } + document.getElementById("whole-window").replaceWith(errorParagraph); + return; + } let test_file_data = null; let needs_resize = true; let clear_scene = false; - let mainCanvas = document.getElementById("pteropus-canvas"); + const mainCanvas = document.getElementById("pteropus-canvas"); mainCanvas.addEventListener("drop", async (event) => { event.preventDefault(); test_file_data = event.dataTransfer.files[0] @@ -24,7 +37,7 @@ async function run() { }); resizeObserver.observe(mainCanvas); - let fileUploadButton = document.getElementById("file-upload-file-input"); + const fileUploadButton = document.getElementById("file-upload-file-input"); fileUploadButton.addEventListener("change", () => { const files = fileUploadButton.files; if(files.length > 0) { @@ -32,7 +45,7 @@ async function run() { } }); - let clearSceneButton = document.getElementById("clear-scene-button"); + const clearSceneButton = document.getElementById("clear-scene-button"); clearSceneButton.addEventListener("click", () => { clear_scene = true; });