mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-25 10:39:01 -06:00
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>
87 lines
3.9 KiB
Rust
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");
|
|
}
|