Files
Syplheed-Reborn/crates/sylpheed-viewer/index.html
MechaCat02 f8127e73b0 feat: initialise workspace — Milestone 1 asset explorer
Three-crate Cargo workspace structured per PROJECT.md spec:
- crates/sylpheed-formats  — Xbox 360 format parsers (no Bevy)
- crates/sylpheed-viewer   — Bevy 0.15 asset viewer + egui UI
- crates/sylpheed-cli      — CLI tools (extract/list/sniff/texture)

Milestone 1 features:
- XISO disc image reading via xdvdfs 0.8
- XPR2 texture container parsing + Morton de-tiling
- D3DFORMAT → wgpu TextureFormat mapping (DXT1/3/5, DXN, ARGB)
- Custom Bevy AssetLoader for .xpr files
- Orbit camera (LMB orbit, RMB pan, scroll zoom)
- egui file browser + RE notes panel
- CLI: extract / list / sniff / texture info / texture export
- GitHub Actions CI (Linux, macOS, Windows, WASM)
- Trunk WASM build config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 21:04:07 +01:00

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-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>