`trunk build --release` reached the asset pipeline for the first time and failed there: found more than one target artifact: ["sylpheed_viewer", "sylpheed-viewer"] The crate declares both a [[bin]] `sylpheed-viewer` (src/main.rs) and a [lib] `sylpheed_viewer` cdylib (src/lib.rs), and the index.html link named neither, so trunk refused to guess. Trunk's error offers two ways out and THEY ARE NOT EQUIVALENT. Measured, both exiting 0: data-target-name="sylpheed_viewer" 1_478 bytes, 1 app symbol data-bin="sylpheed-viewer" 21_298_268 bytes, 2_998 app symbols Selecting the lib "succeeds" while linking nothing, because there is no wasm entry point in it -- no wasm-bindgen dependency, no import, no `#[wasm_bindgen(start)]`. The linker drops the whole app and trunk emits an empty module. That would have turned this job GREEN on a bundle that cannot start, which is worse than the red it replaced. `main()` is a valid wasm entry: it calls `sylpheed_viewer::run()` and its only native-specific code is already `#[cfg(not(target_arch = "wasm32"))]`. With the bin selected, trunk injects a real init -- `import init`, an integrity-checked module preload, `__wbindgen_start`, and the `TrunkApplicationStarted` event. The lib.rs docs claimed this file was the WASM entry point "called from `wasm_bindgen` init on the web". Nothing ever called it. That comment is what made the lib look like the right target, so it is corrected here rather than left to mislead the next reader. Verified locally with trunk 0.21.7 on x86_64. The exit code does not distinguish these two cases -- only the artifact does. Refs #11
229 lines
6.7 KiB
HTML
229 lines
6.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Project Sylpheed: Arc of Deception — Asset Viewer</title>
|
|
<style>
|
|
:root {
|
|
--bg: #0a0c12;
|
|
--surface: #12151f;
|
|
--accent: #4fc3f7;
|
|
--accent2: #7c4dff;
|
|
--text: #e0e6f0;
|
|
--text-dim: #6b7a99;
|
|
--border: #1e2438;
|
|
}
|
|
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
body {
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
font-family: 'Courier New', monospace;
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* ── Header bar ── */
|
|
header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
padding: 8px 20px;
|
|
background: var(--surface);
|
|
border-bottom: 1px solid var(--border);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.logo {
|
|
font-size: 11px;
|
|
font-weight: bold;
|
|
letter-spacing: 3px;
|
|
text-transform: uppercase;
|
|
color: var(--accent);
|
|
}
|
|
|
|
.logo span {
|
|
color: var(--accent2);
|
|
}
|
|
|
|
.build-badge {
|
|
font-size: 10px;
|
|
padding: 2px 8px;
|
|
border: 1px solid var(--accent2);
|
|
border-radius: 3px;
|
|
color: var(--accent2);
|
|
letter-spacing: 1px;
|
|
}
|
|
|
|
/* ── Main layout ── */
|
|
main {
|
|
flex: 1;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* ── Bevy canvas ── */
|
|
#bevy-canvas {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: block;
|
|
background: var(--bg);
|
|
/* Prevent context menu on right-click (used for panning) */
|
|
touch-action: none;
|
|
}
|
|
|
|
/* ── Loading overlay ── */
|
|
#loading {
|
|
position: absolute;
|
|
inset: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: var(--bg);
|
|
z-index: 10;
|
|
gap: 24px;
|
|
transition: opacity 0.5s ease;
|
|
}
|
|
|
|
#loading.hidden {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.loading-title {
|
|
font-size: 22px;
|
|
letter-spacing: 6px;
|
|
text-transform: uppercase;
|
|
color: var(--accent);
|
|
}
|
|
|
|
.loading-title em {
|
|
color: var(--text-dim);
|
|
font-style: normal;
|
|
}
|
|
|
|
.progress-bar-container {
|
|
width: 360px;
|
|
height: 2px;
|
|
background: var(--border);
|
|
border-radius: 2px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.progress-bar-fill {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, var(--accent2), var(--accent));
|
|
border-radius: 2px;
|
|
width: 0%;
|
|
animation: progress-anim 2.5s ease-in-out forwards;
|
|
}
|
|
|
|
@keyframes progress-anim {
|
|
0% { width: 0%; }
|
|
40% { width: 55%; }
|
|
70% { width: 75%; }
|
|
90% { width: 88%; }
|
|
/* The remaining % is filled by JS when WASM is ready */
|
|
}
|
|
|
|
.loading-status {
|
|
font-size: 11px;
|
|
color: var(--text-dim);
|
|
letter-spacing: 2px;
|
|
}
|
|
|
|
/* ── Warning banner for WASM limitations ── */
|
|
#wasm-note {
|
|
padding: 6px 20px;
|
|
background: #1a1500;
|
|
border-top: 1px solid #3d3000;
|
|
font-size: 11px;
|
|
color: #b8a040;
|
|
letter-spacing: 0.5px;
|
|
flex-shrink: 0;
|
|
text-align: center;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<div class="logo">PROJECT <span>SYLPHEED</span> // ASSET VIEWER</div>
|
|
<div class="build-badge">WEB BUILD</div>
|
|
<div class="build-badge" style="border-color: var(--accent); color: var(--accent);">
|
|
MILESTONE 1
|
|
</div>
|
|
</header>
|
|
|
|
<main>
|
|
<!-- Loading overlay -->
|
|
<div id="loading">
|
|
<div class="loading-title">
|
|
PROJECT <em>SYLPHEED</em>
|
|
</div>
|
|
<div>
|
|
<div class="progress-bar-container">
|
|
<div class="progress-bar-fill" id="progress-fill"></div>
|
|
</div>
|
|
</div>
|
|
<div class="loading-status" id="loading-status">
|
|
INITIALIZING WASM RUNTIME...
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bevy renders into this canvas -->
|
|
<!-- The id must match the canvas option in lib.rs WindowPlugin -->
|
|
<canvas id="bevy-canvas"></canvas>
|
|
</main>
|
|
|
|
<div id="wasm-note">
|
|
⚠ Web build: asset loading is limited. For full ISO extraction support, use the native build.
|
|
Pre-extract assets with: <code>xdvdfs unpack game.iso ./assets/</code>
|
|
</div>
|
|
|
|
<!-- Trunk injects the compiled WASM module here -->
|
|
<link data-trunk rel="rust" data-bin="sylpheed-viewer" data-wasm-opt="z" />
|
|
|
|
<script>
|
|
// Update loading status messages as WASM initializes
|
|
const statusEl = document.getElementById('loading-status');
|
|
const loadingEl = document.getElementById('loading');
|
|
const fillEl = document.getElementById('progress-fill');
|
|
|
|
const messages = [
|
|
[400, 'LOADING WASM MODULE...'],
|
|
[900, 'INITIALIZING BEVY ECS...'],
|
|
[1500, 'SETTING UP RENDER PIPELINE...'],
|
|
[2000, 'REGISTERING ASSET LOADERS...'],
|
|
[2400, 'READY'],
|
|
];
|
|
|
|
messages.forEach(([delay, msg]) => {
|
|
setTimeout(() => { statusEl.textContent = msg; }, delay);
|
|
});
|
|
|
|
// Once the canvas gets its first frame, hide the loading overlay
|
|
const observer = new MutationObserver(() => {
|
|
const canvas = document.getElementById('bevy-canvas');
|
|
if (canvas && canvas.width > 0) {
|
|
fillEl.style.width = '100%';
|
|
setTimeout(() => { loadingEl.classList.add('hidden'); }, 300);
|
|
observer.disconnect();
|
|
}
|
|
});
|
|
observer.observe(document.getElementById('bevy-canvas'), {
|
|
attributes: true, attributeFilter: ['width', 'height']
|
|
});
|
|
|
|
// Prevent right-click context menu on the canvas (RMB = pan)
|
|
document.getElementById('bevy-canvas').addEventListener(
|
|
'contextmenu', e => e.preventDefault()
|
|
);
|
|
</script>
|
|
</body>
|
|
</html>
|