<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>binpack inspector</title>
<style>
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--bg: #0e0e0f;
--bg2: #161618;
--bg3: #1e1e21;
--border: rgba(255, 255, 255, 0.07);
--border2: rgba(255, 255, 255, 0.12);
--text: #e8e6e0;
--text2: #888880;
--text3: #555550;
--green: #7ec98a;
--red: #e0706b;
--blue: #7aaddc;
}
body {
font-family: "JetBrains Mono", "Fira Mono", "Cascadia Code", monospace;
background: var(--bg);
color: var(--text);
min-height: 100vh;
padding: 2.5rem 2rem;
}
.shell {
max-width: 1100px;
margin: 0 auto;
}
.header {
display: flex;
align-items: baseline;
gap: 14px;
margin-bottom: 2rem;
border-bottom: 0.5px solid var(--border);
padding-bottom: 1rem;
}
.header h1 {
font-size: 13px;
font-weight: 500;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--text);
}
.header span {
font-size: 11px;
color: var(--text3);
}
.controls {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
margin-bottom: 1.25rem;
}
.file-label {
display: inline-flex;
align-items: center;
gap: 8px;
cursor: pointer;
background: var(--bg2);
border: 0.5px solid var(--border2);
border-radius: 5px;
padding: 7px 14px;
font-family: inherit;
font-size: 11px;
color: var(--text2);
transition:
border-color 0.15s,
color 0.15s;
white-space: nowrap;
}
.file-label:hover {
border-color: rgba(255, 255, 255, 0.25);
color: var(--text);
}
#file-input {
display: none;
}
#file-name-display {
font-size: 11px;
color: var(--text3);
}
#parse-button {
font-family: inherit;
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
padding: 7px 18px;
cursor: pointer;
background: var(--bg2);
border: 0.5px solid var(--border2);
border-radius: 5px;
color: var(--text);
transition:
background 0.1s,
border-color 0.15s;
}
#parse-button:hover {
background: var(--bg3);
border-color: rgba(255, 255, 255, 0.25);
}
#example-button {
font-family: inherit;
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
padding: 7px 18px;
cursor: pointer;
background: transparent;
border: 0.5px solid var(--border2);
border-radius: 5px;
color: var(--blue);
transition:
background 0.1s,
border-color 0.15s;
}
#example-button:hover {
background: var(--bg3);
border-color: rgba(255, 255, 255, 0.25);
}
#parse-button:disabled {
opacity: 0.35;
cursor: not-allowed;
}
#example-button:disabled {
opacity: 0.35;
cursor: not-allowed;
}
.hint-row {
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
margin-top: -0.4rem;
margin-bottom: 1.1rem;
font-size: 11px;
color: var(--text3);
}
.hint-row a {
color: var(--blue);
text-decoration: none;
}
.hint-row a:hover {
text-decoration: underline;
}
kbd {
background: var(--bg2);
border: 0.5px solid var(--border2);
border-radius: 4px;
padding: 2px 6px;
font-size: 10px;
font-family: inherit;
color: var(--text);
}
.status-bar {
font-size: 11px;
color: var(--text3);
letter-spacing: 0.02em;
padding: 7px 0;
border-top: 0.5px solid var(--border);
border-bottom: 0.5px solid var(--border);
margin-bottom: 1.5rem;
}
.status-bar.ok {
color: var(--green);
}
.status-bar.err {
color: var(--red);
}
.metrics {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
margin-bottom: 1.5rem;
}
.metric {
background: var(--bg2);
border: 0.5px solid var(--border);
border-radius: 6px;
padding: 12px 14px;
}
.metric-label {
font-size: 10px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text3);
margin-bottom: 5px;
}
.metric-value {
font-size: 22px;
font-weight: 500;
color: var(--text);
}
.table-wrap {
overflow-x: auto;
border: 0.5px solid var(--border);
border-radius: 6px;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 11px;
}
thead th {
padding: 8px 10px;
text-align: left;
font-size: 10px;
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text3);
background: var(--bg2);
border-bottom: 0.5px solid var(--border);
white-space: nowrap;
}
tbody tr {
border-bottom: 0.5px solid var(--border);
}
tbody tr:last-child {
border-bottom: none;
}
tbody tr:hover {
background: var(--bg2);
}
tbody td {
padding: 7px 10px;
color: var(--text);
vertical-align: middle;
}
tbody td:first-child {
color: var(--text3);
font-size: 10px;
}
.fen-cell {
max-width: 220px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--text2) !important;
}
.score-cell {
color: var(--blue) !important;
}
.result-cell {
color: var(--green) !important;
}
.empty {
padding: 3rem;
text-align: center;
font-size: 11px;
color: var(--text3);
letter-spacing: 0.04em;
}
</style>
</head>
<body>
<div class="shell">
<div class="header">
<h1>binpack inspector</h1>
<span>sfbinpack format</span>
</div>
<div class="controls">
<label class="file-label" for="file-input">
<svg width="12" height="12" viewBox="0 0 16 16" fill="none">
<path
d="M2 13V3C2 2.45 2.45 2 3 2H9L14 7V13C14 13.55 13.55 14 13 14H3C2.45 14 2 13.55 2 13Z"
stroke="currentColor"
stroke-width="1.2"
/>
<polyline
points="9,2 9,7 14,7"
stroke="currentColor"
stroke-width="1.2"
fill="none"
/>
</svg>
choose .binpack
</label>
<input type="file" id="file-input" accept=".binpack,.bin" />
<button id="open-file-button" class="file-label">
<svg width="12" height="12" viewBox="0 0 16 16" fill="none">
<path
d="M2 13V3C2 2.45 2.45 2 3 2H9L14 7V13C14 13.55 13.55 14 13 14H3C2.45 14 2 13.55 2 13Z"
stroke="currentColor"
stroke-width="1.2"
/>
<polyline
points="9,2 9,7 14,7"
stroke="currentColor"
stroke-width="1.2"
fill="none"
/>
</svg>
open (file system api)
</button>
<span id="file-name-display">no file selected</span>
<button id="parse-button">parse</button>
<button id="example-button">use example</button>
</div>
<div class="hint-row">
<span>quick test:</span>
<a href="./examples/ep1.binpack" download>download example binpack</a>
</div>
<div class="status-bar" id="status">awaiting wasm module...</div>
<div class="status-bar">
browser mode: reads one BINP chunk at a time with <code>File.slice()</code>
</div>
<div class="status-bar" id="navigation-hint" style="display: none; text-align: center;">
<kbd>←</kbd> previous chunk | <kbd>→</kbd> next chunk | <kbd>↑</kbd> previous | <kbd>↓</kbd> next
</div>
<div class="metrics" id="metrics-row" style="display: none">
<div class="metric">
<div class="metric-label">bytes read</div>
<div class="metric-value" id="m-bytes">—</div>
</div>
<div class="metric">
<div class="metric-label">entries read</div>
<div class="metric-value" id="m-total">—</div>
</div>
<div class="metric">
<div class="metric-label">preview count</div>
<div class="metric-value" id="m-preview">—</div>
</div>
</div>
<div class="table-wrap">
<div class="empty" id="preview-empty">parse a file to see entries</div>
<table id="preview-table" style="display: none">
<thead>
<tr>
<th>#</th>
<th>FEN</th>
<th>UCI</th>
<th>Score</th>
<th>Ply</th>
<th>Result</th>
<th>Continuation</th>
<th>Offset</th>
</tr>
</thead>
<tbody id="preview-tbody"></tbody>
</table>
</div>
</div>
<script type="module" src="app.js"></script>
</body>
</html>