forked from 1b-agent/fieldcraft-upstream
The desktop's ModelApp in a web page through eframe's web runner (WebGPU, WebGL2 fallback, `?webgl` to force it), built with Trunk. The browser services sit behind the UI's host-service seams: File > Open uses the browser's file picker, dropped files are read asynchronously, and both go into the page's files; Save, Save As and Write Deck hand the written file out as a download. Autosaves go to the recovery folder as on the desktop and are mirrored to local storage, so File > Recover offers what a closed or crashed page left. `?sample=NAME` opens a sample. A loading screen with the app icon, and a message instead of the app when neither WebGPU nor WebGL2 is available. `cargo xtask wasm` (and so `cargo xtask ci`) now checks fieldcraft-web for wasm32 with the library crates. docs/web.md: running it locally, what works in the browser and what does not. Part of #119 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
55 lines
2.9 KiB
HTML
55 lines
2.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
|
<title>FieldCraft</title>
|
|
<meta name="description" content="Open-source CAE modeling in Rust: mesh a part, set up the analysis and solve it, in your browser.">
|
|
<link data-trunk rel="rust" data-bin="fieldcraft-web" data-wasm-opt="z" data-weak-refs />
|
|
<link data-trunk rel="icon" type="image/svg+xml" href="../../assets/app-icon/icon-small.svg" />
|
|
<link data-trunk rel="copy-file" href="../../assets/app-icon/icon.iconset/[email protected]" data-target-path="icons" />
|
|
<link rel="apple-touch-icon" href="icons/[email protected]">
|
|
<style>
|
|
html, body { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: #262c37; }
|
|
#fieldcraft_canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
|
|
#fieldcraft_loading { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
gap: 16px; padding: 0 16px; text-align: center; color: #c3cad6; font: 14px system-ui, sans-serif; pointer-events: none; }
|
|
#fieldcraft_loading img { width: 96px; height: 96px; }
|
|
#fieldcraft_loading p { margin: 0; max-width: 32em; }
|
|
#fieldcraft_loading.failed { pointer-events: auto; }
|
|
#fieldcraft_loading.failed img, #fieldcraft_loading.failed .spinner { display: none; }
|
|
.spinner { width: 24px; height: 24px; border-radius: 50%; border: 3px solid #4a5468; border-top-color: #c3cad6; animation: spin 0.9s linear infinite; }
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
@media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<canvas id="fieldcraft_canvas"></canvas>
|
|
<div id="fieldcraft_loading">
|
|
<img src="icons/[email protected]" alt="">
|
|
<div class="spinner"></div>
|
|
<p>Loading FieldCraft…</p>
|
|
<noscript><p>FieldCraft needs JavaScript and WebAssembly.</p></noscript>
|
|
</div>
|
|
<script>
|
|
// The page could not load or start the app (no WebAssembly, a failed download): say so
|
|
// instead of loading forever. Once the app runs it removes the loading screen.
|
|
(function () {
|
|
function fail(why) {
|
|
var el = document.getElementById("fieldcraft_loading");
|
|
if (!el || el.classList.contains("failed")) return;
|
|
el.classList.add("failed");
|
|
el.textContent = "";
|
|
["FieldCraft could not start: " + why, "It needs a current browser with WebAssembly and WebGPU or WebGL2."].forEach(function (t) {
|
|
var p = document.createElement("p");
|
|
p.textContent = t;
|
|
el.appendChild(p);
|
|
});
|
|
}
|
|
if (typeof WebAssembly !== "object") fail("this browser has no WebAssembly.");
|
|
addEventListener("unhandledrejection", function (e) { fail(String(e.reason && e.reason.message || e.reason)); });
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|