Files
pdfcraft/apps/pdfcraft-web/src/main.rs
DWAA1660andClaude Opus 5.5 9fa837e452 Issue #173: a failed ?file= startup fetch is reported in the app (#302)
When the browser app's `?file=` URL couldn't be fetched (an HTTP 404, a
network or CORS failure), the error went only to the developer console and
the user saw the ordinary welcome screen with no sign the PDF was tried.

The fetch now also queues `(name, error)` in a new `failed_inbox` next to
the existing `inbox`, and the app reports it on the next frame with the
notice it already uses for a delivered file that won't open: "Couldn't open
missing.pdf: HTTP 404". The console line stays for developers.

Test: a_failed_startup_url_is_reported_in_the_app (pickers.rs).


Claude-Session: https://claude.ai/code/session_01K3vVft4VXvvy8SpbbUceTW

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 12:49:25 -04:00

87 lines
3.9 KiB
Rust

//! PdfCraft in the browser. Build: `cd apps/pdfcraft-web && trunk build --release`
//! (trunk generates the JS loader; no handwritten JS — plan/execution-plan.md §1).
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
#[cfg(target_arch = "wasm32")]
fn main() {
use eframe::wasm_bindgen::JsCast;
use pdfcraft_ui_egui::PdfCraftApp;
let options = eframe::WebOptions { renderer: eframe::Renderer::Glow, ..Default::default() };
wasm_bindgen_futures::spawn_local(async move {
let Some(canvas) = web_sys::window()
.and_then(|w| w.document())
.and_then(|d| d.get_element_by_id("pdfcraft"))
.and_then(|e| e.dyn_into::<web_sys::HtmlCanvasElement>().ok())
else {
return;
};
let started = eframe::WebRunner::new()
.start(
canvas,
options,
Box::new(|cc| {
let mut app = PdfCraftApp::new();
// "printcraft": settings saved under the app's former name, PrintCraft.
if let Some(json) = cc.storage.and_then(|s| s.get_string("pdfcraft").or_else(|| s.get_string("printcraft"))) {
app.restore(&json);
}
// `?file=<url>` opens a PDF from a URL (same-origin or CORS-enabled).
if let Some(url) = query_param("file") {
let inbox = app.inbox.clone();
let failed = app.failed_inbox.clone();
let ctx = cc.egui_ctx.clone();
wasm_bindgen_futures::spawn_local(async move {
let name = url.rsplit('/').next().unwrap_or("document.pdf").split('?').next().unwrap_or("document.pdf").to_string();
match fetch_bytes(&url).await {
Ok(bytes) => {
if let Ok(mut q) = inbox.lock() {
q.push((name, bytes));
}
}
Err(e) => {
eframe::web_sys::console::error_1(&format!("PdfCraft: could not fetch {url}: {e}").into());
// Shown in the app too, not only in the console (#173).
if let Ok(mut q) = failed.lock() {
q.push((name, e));
}
}
}
ctx.request_repaint();
});
}
Ok(Box::new(app))
}),
)
.await;
if let Err(e) = started {
eframe::web_sys::console::error_1(&e);
}
});
}
#[cfg(target_arch = "wasm32")]
fn query_param(key: &str) -> Option<String> {
let search = web_sys::window()?.location().search().ok()?;
web_sys::UrlSearchParams::new_with_str(&search).ok()?.get(key)
}
#[cfg(target_arch = "wasm32")]
async fn fetch_bytes(url: &str) -> Result<Vec<u8>, String> {
use eframe::wasm_bindgen::JsCast;
let window = web_sys::window().ok_or("no window")?;
let resp = wasm_bindgen_futures::JsFuture::from(window.fetch_with_str(url)).await.map_err(|e| format!("{e:?}"))?;
let resp: web_sys::Response = resp.dyn_into().map_err(|_| "not a response")?;
if !resp.ok() {
return Err(format!("HTTP {}", resp.status()));
}
let buf = wasm_bindgen_futures::JsFuture::from(resp.array_buffer().map_err(|e| format!("{e:?}"))?).await.map_err(|e| format!("{e:?}"))?;
Ok(js_sys::Uint8Array::new(&buf).to_vec())
}
#[cfg(not(target_arch = "wasm32"))]
fn main() {
eprintln!("pdfcraft-web targets wasm32: run `trunk serve` or `trunk build --release` in apps/pdfcraft-web");
}