Upload index.html with huggingface_hub
Browse files- index.html +295 -18
index.html
CHANGED
|
@@ -1,19 +1,296 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
</html>
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Speculative Decoding — EXD Ep5</title>
|
| 7 |
+
<style>
|
| 8 |
+
:root {
|
| 9 |
+
--bg:#0d1117; --surface:#161b22; --border:#30363d;
|
| 10 |
+
--text:#c9d1d9; --dim:#8b949e; --accent:#58a6ff;
|
| 11 |
+
--hit:#3fb950; --miss:#f85149; --draft:#bc8cff;
|
| 12 |
+
}
|
| 13 |
+
*{margin:0;padding:0;box-sizing:border-box}
|
| 14 |
+
body{
|
| 15 |
+
background:var(--bg);color:var(--text);
|
| 16 |
+
font:14px ui-monospace,SFMono-Regular,monospace;
|
| 17 |
+
display:flex;justify-content:center;padding:2rem 1rem;
|
| 18 |
+
}
|
| 19 |
+
.w{max-width:720px;width:100%}
|
| 20 |
+
h1{font-size:1.1rem;margin-bottom:.2rem}
|
| 21 |
+
.sub{font-size:.75rem;color:var(--dim);margin-bottom:1rem}
|
| 22 |
+
.controls{display:flex;gap:1rem;flex-wrap:wrap;align-items:flex-end;margin-bottom:1rem}
|
| 23 |
+
.cg{display:flex;flex-direction:column;gap:.2rem}
|
| 24 |
+
.cg label{font-size:.65rem;color:var(--dim);text-transform:uppercase}
|
| 25 |
+
.cg select{padding:.3rem .5rem;border-radius:4px;border:1px solid var(--border);background:var(--surface);color:var(--text);font-family:monospace;font-size:.8rem;cursor:pointer}
|
| 26 |
+
.btn-row{display:flex;gap:.5rem;align-items:center}
|
| 27 |
+
.btn{background:var(--surface);color:var(--accent);border:1px solid var(--accent);padding:.35rem .8rem;border-radius:5px;cursor:pointer;font-family:monospace;font-size:.75rem;font-weight:600}
|
| 28 |
+
.btn:hover{background:rgba(88,166,255,.12)}
|
| 29 |
+
.btn.pri{background:var(--accent);color:var(--bg)}
|
| 30 |
+
.btn:disabled{opacity:.4;cursor:not-allowed}
|
| 31 |
+
.step{font-size:.7rem;color:var(--dim);margin-left:.5rem}
|
| 32 |
+
.sec{font-size:.65rem;color:var(--dim);text-transform:uppercase;margin-bottom:.3rem}
|
| 33 |
+
.pipe{display:flex;gap:6px;padding:.7rem;margin-bottom:1rem;background:var(--surface);border:1px solid var(--border);border-radius:8px;min-height:44px;flex-wrap:wrap;align-items:center}
|
| 34 |
+
.ps{padding:.4rem .6rem;border-radius:6px;border:2px solid var(--border);font-size:.7rem;text-align:center;transition:all .3s}
|
| 35 |
+
.ps.on{border-color:var(--accent);box-shadow:0 0 8px rgba(88,166,255,.25)}
|
| 36 |
+
.ps .l{font-size:.55rem;color:var(--dim);display:block}
|
| 37 |
+
.ps .v{font-size:.75rem;display:block;margin-top:1px}
|
| 38 |
+
.tokens{display:flex;gap:5px;flex-wrap:wrap;margin-bottom:1rem;min-height:50px;align-items:center}
|
| 39 |
+
.tok{padding:5px 10px;border-radius:6px;font-size:.75rem;font-weight:600;border:2px solid transparent;text-align:center;transition:all .3s}
|
| 40 |
+
.tok .t{display:block;min-width:40px}
|
| 41 |
+
.tok .lb{font-size:.5rem;color:var(--dim);margin-top:2px}
|
| 42 |
+
.t-OK{background:var(--surface);border-color:var(--border);color:var(--text)}
|
| 43 |
+
.t-HIT{background:rgba(63,185,80,.12);border-color:var(--hit);color:var(--hit)}
|
| 44 |
+
.t-MISS{background:rgba(248,81,73,.12);border-color:var(--miss);color:var(--miss);text-decoration:line-through}
|
| 45 |
+
.t-DRAFT{background:rgba(188,140,255,.1);border-color:var(--draft);color:var(--draft)}
|
| 46 |
+
.info{font-size:.7rem;color:var(--dim);padding:.6rem .8rem;border-left:3px solid var(--accent);background:rgba(88,166,255,.04);border-radius:0 6px 6px 0;line-height:1.5;min-height:2.5em}
|
| 47 |
+
.info code{color:var(--accent);background:rgba(88,166,255,.1);padding:0 4px;border-radius:3px}
|
| 48 |
+
.stats{display:flex;gap:1rem;margin-bottom:1rem;padding:.6rem;background:var(--surface);border:1px solid var(--border);border-radius:8px;flex-wrap:wrap}
|
| 49 |
+
.stat{text-align:center;min-width:70px}
|
| 50 |
+
.stat .n{font-size:1.1rem;font-weight:700;color:var(--accent);font-family:monospace}
|
| 51 |
+
.stat .lb{font-size:.55rem;color:var(--dim)}
|
| 52 |
+
.stat.bad .n{color:var(--miss)}
|
| 53 |
+
</style>
|
| 54 |
+
</head>
|
| 55 |
+
<body>
|
| 56 |
+
<div class="w">
|
| 57 |
+
<h1>Speculative Decoding — Step by Step</h1>
|
| 58 |
+
<div class="sub">Draft model proposes tokens ahead, full model verifies · EXD Ep5</div>
|
| 59 |
+
|
| 60 |
+
<div class="controls">
|
| 61 |
+
<div class="cg">
|
| 62 |
+
<label>Spec Depth <span id="dv">2</span></label>
|
| 63 |
+
<select id="depth">
|
| 64 |
+
<option value="2" selected>2 tokens</option>
|
| 65 |
+
<option value="3">3 tokens</option>
|
| 66 |
+
</select>
|
| 67 |
+
</div>
|
| 68 |
+
<div class="btn-row">
|
| 69 |
+
<button class="btn" id="prev">← Prev</button>
|
| 70 |
+
<button class="btn pri" id="step">Next Step</button>
|
| 71 |
+
<button class="btn" id="reset">Reset</button>
|
| 72 |
+
<span class="step" id="st">Step 0</span>
|
| 73 |
+
</div>
|
| 74 |
+
</div>
|
| 75 |
+
|
| 76 |
+
<div class="sec">Pipeline</div>
|
| 77 |
+
<div class="pipe" id="pipe"></div>
|
| 78 |
+
|
| 79 |
+
<div class="sec">Tokens</div>
|
| 80 |
+
<div class="tokens" id="trow"></div>
|
| 81 |
+
|
| 82 |
+
<div class="stats" id="stats"></div>
|
| 83 |
+
|
| 84 |
+
<div class="info" id="info">
|
| 85 |
+
<strong>Speculative decoding</strong>: a lightweight draft model guesses tokens ahead.<br>
|
| 86 |
+
The full model verifies them all in <em>one forward pass</em>.<br>
|
| 87 |
+
<span style="color:var(--hit)">■ Green</span> = correct guess (free speed) ·
|
| 88 |
+
<span style="color:var(--miss)">■ Red</span> = wrong guess (wasted, struck out)<br>
|
| 89 |
+
<code>Space</code> = next, <code>←</code> = previous. Switch depth to compare.
|
| 90 |
+
</div>
|
| 91 |
+
</div>
|
| 92 |
+
|
| 93 |
+
<script>
|
| 94 |
+
var PREFIX = "The quick brown";
|
| 95 |
+
var d = function(id) { return document.getElementById(id); };
|
| 96 |
+
var idx = 0;
|
| 97 |
+
var depth = 2;
|
| 98 |
+
|
| 99 |
+
// ---- STATIC SCRIPTS ----
|
| 100 |
+
// Each script step: {phase, info, drafts: [{token,correct}] or null, confirm: [tokens_to_add_to_conf]}
|
| 101 |
+
// phase: "draft" | "verify" | "done"
|
| 102 |
+
// confirm only on verify steps: tokens to add to conf (hits + any miss correction, in order)
|
| 103 |
+
|
| 104 |
+
var SCRIPT_2 = [
|
| 105 |
+
{phase:"draft", info:"<strong>Draft model</strong> guesses 2 tokens ahead: <em>fox</em>, <em>jumps</em>",
|
| 106 |
+
drafts:[{token:"fox",correct:true},{token:"jumps",correct:true}]},
|
| 107 |
+
{phase:"verify", info:"<strong>Full model</strong> verifies: 2 hit(s). Speed gained!",
|
| 108 |
+
drafts:[{token:"fox",correct:true},{token:"jumps",correct:true}], confirm:["fox","jumps"]},
|
| 109 |
+
{phase:"draft", info:"<strong>Draft model</strong> guesses 2 tokens ahead: <em>over</em>, <em>the</em> — but one is wrong!",
|
| 110 |
+
drafts:[{token:"over",correct:true},{token:"???",correct:false}]},
|
| 111 |
+
{phase:"verify", info:"<strong>Full model</strong> verifies: 1 hit(s), 1 miss. Corrects <em>??? → the</em>.",
|
| 112 |
+
drafts:[{token:"over",correct:true},{token:"???",correct:false,correction:"the"}], confirm:["over","the"]},
|
| 113 |
+
{phase:"draft", info:"<strong>Draft model</strong> guesses 2 tokens ahead: <em>lazy</em>, <em>dog</em>",
|
| 114 |
+
drafts:[{token:"lazy",correct:true},{token:"dog",correct:true}]},
|
| 115 |
+
{phase:"verify", info:"<strong>Full model</strong> verifies: 2 hit(s). Done! 🎉",
|
| 116 |
+
drafts:[{token:"lazy",correct:true},{token:"dog",correct:true}], confirm:["lazy","dog"]},
|
| 117 |
+
{phase:"done", info:"<strong>Done!</strong> Generated 9 tokens in 3 forward passes (6 steps). Speculation saved ~50% of the passes.", drafts:[]},
|
| 118 |
+
];
|
| 119 |
+
|
| 120 |
+
var SCRIPT_3 = [
|
| 121 |
+
{phase:"draft", info:"<strong>Draft model</strong> guesses 3 tokens ahead: <em>fox</em>, <em>jumps</em>, <em>over</em>",
|
| 122 |
+
drafts:[{token:"fox",correct:true},{token:"jumps",correct:true},{token:"over",correct:true}]},
|
| 123 |
+
{phase:"verify", info:"<strong>Full model</strong> verifies: 3 hit(s) in one pass! 🚀",
|
| 124 |
+
drafts:[{token:"fox",correct:true},{token:"jumps",correct:true},{token:"over",correct:true}], confirm:["fox","jumps","over"]},
|
| 125 |
+
{phase:"draft", info:"<strong>Draft model</strong> guesses 3 tokens ahead: <em>the</em>, <em>lazy</em>, <em>dog</em> — but one is wrong!",
|
| 126 |
+
drafts:[{token:"the",correct:true},{token:"???",correct:false},{token:"dog",correct:true}]},
|
| 127 |
+
{phase:"verify", info:"<strong>Full model</strong> verifies: 1 hit(s), 1 miss. Corrects <em>??? → lazy</em>. Remaining drafts accepted. Done!",
|
| 128 |
+
drafts:[{token:"the",correct:true},{token:"???",correct:false,correction:"lazy"},{token:"dog",correct:true}], confirm:["the","lazy","dog"]},
|
| 129 |
+
{phase:"done", info:"<strong>Done!</strong> Generated 9 tokens in 2 forward passes (4 steps). Depth 3 got 3 tokens in one go, but a miss wasted extra work.", drafts:[]},
|
| 130 |
+
];
|
| 131 |
+
|
| 132 |
+
var script = SCRIPT_2.slice();
|
| 133 |
+
var conf = [];
|
| 134 |
+
var total_hits = 0, total_misses = 0;
|
| 135 |
+
|
| 136 |
+
function getScript() {
|
| 137 |
+
return Number(d("depth").value) === 3 ? SCRIPT_3 : SCRIPT_2;
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
function reset() {
|
| 141 |
+
var s = getScript();
|
| 142 |
+
script = s.map(function(x) { return JSON.parse(JSON.stringify(x)); });
|
| 143 |
+
conf = PREFIX.split(" ");
|
| 144 |
+
total_hits = 0;
|
| 145 |
+
total_misses = 0;
|
| 146 |
+
idx = -1;
|
| 147 |
+
depth = Number(d("depth").value);
|
| 148 |
+
d("dv").textContent = depth;
|
| 149 |
+
render();
|
| 150 |
+
d("st").textContent = "Step " + idx;
|
| 151 |
+
d("info").innerHTML = "Prefix: <strong>" + PREFIX + "</strong> → target: <strong>fox jumps over the lazy dog</strong>. Depth = " + depth + ". Click <code>Next Step</code>.";
|
| 152 |
+
updateButtons();
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
function nextStep() {
|
| 156 |
+
if (idx >= 0 && idx >= script.length) return;
|
| 157 |
+
if (idx === -1) {
|
| 158 |
+
// Initial state: just advance to first frame
|
| 159 |
+
idx = 0;
|
| 160 |
+
d("st").textContent = "Step " + idx;
|
| 161 |
+
d("info").innerHTML = script[0].info;
|
| 162 |
+
updateButtons();
|
| 163 |
+
render();
|
| 164 |
+
return;
|
| 165 |
+
}
|
| 166 |
+
var frame = script[idx];
|
| 167 |
+
depth = Number(d("depth").value);
|
| 168 |
+
d("dv").textContent = depth;
|
| 169 |
+
|
| 170 |
+
if (frame.phase === "verify" && frame.confirm) {
|
| 171 |
+
for (var i = 0; i < frame.confirm.length; i++) {
|
| 172 |
+
conf.push(frame.confirm[i]);
|
| 173 |
+
}
|
| 174 |
+
// Count hits/misses from drafts
|
| 175 |
+
if (frame.drafts) {
|
| 176 |
+
for (var i = 0; i < frame.drafts.length; i++) {
|
| 177 |
+
if (frame.drafts[i].correct) total_hits++;
|
| 178 |
+
else total_misses++;
|
| 179 |
+
}
|
| 180 |
+
}
|
| 181 |
+
}
|
| 182 |
+
idx++;
|
| 183 |
+
d("st").textContent = "Step " + idx;
|
| 184 |
+
d("info").innerHTML = frame.info;
|
| 185 |
+
updateButtons();
|
| 186 |
+
render();
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
function prevStep() {
|
| 190 |
+
if (idx <= -1) return;
|
| 191 |
+
var target = idx - 1;
|
| 192 |
+
// Replay from start up to target
|
| 193 |
+
conf = PREFIX.split(" ");
|
| 194 |
+
total_hits = 0;
|
| 195 |
+
total_misses = 0;
|
| 196 |
+
depth = Number(d("depth").value);
|
| 197 |
+
script = getScript().map(function(x) { return JSON.parse(JSON.stringify(x)); });
|
| 198 |
+
for (var i = 0; i < target; i++) {
|
| 199 |
+
var frame = script[i];
|
| 200 |
+
if (frame.phase === "verify" && frame.confirm) {
|
| 201 |
+
for (var j = 0; j < frame.confirm.length; j++) conf.push(frame.confirm[j]);
|
| 202 |
+
if (frame.drafts) {
|
| 203 |
+
for (var j = 0; j < frame.drafts.length; j++) {
|
| 204 |
+
if (frame.drafts[j].correct) total_hits++;
|
| 205 |
+
else total_misses++;
|
| 206 |
+
}
|
| 207 |
+
}
|
| 208 |
+
}
|
| 209 |
+
}
|
| 210 |
+
idx = target;
|
| 211 |
+
d("st").textContent = idx === -1 ? "Step 0" : "Step " + idx;
|
| 212 |
+
if (idx === -1) {
|
| 213 |
+
d("info").innerHTML = "Prefix: <strong>" + PREFIX + "</strong> → target: <strong>fox jumps over the lazy dog</strong>. Depth = " + depth + ".";
|
| 214 |
+
} else {
|
| 215 |
+
d("info").innerHTML = script[idx] ? script[idx].info : "";
|
| 216 |
+
}
|
| 217 |
+
updateButtons();
|
| 218 |
+
render();
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
function updateButtons() {
|
| 222 |
+
var pastEnd = idx >= 0 && idx >= script.length;
|
| 223 |
+
var atDone = idx >= 0 && idx < script.length && script[idx].phase === "done";
|
| 224 |
+
d("step").disabled = pastEnd || atDone;
|
| 225 |
+
d("prev").disabled = idx <= -1;
|
| 226 |
+
if (pastEnd) d("st").textContent = "Done";
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
function render() {
|
| 230 |
+
var cur = (idx >= 0 && idx < script.length) ? script[idx] : null;
|
| 231 |
+
var pipe = d("pipe");
|
| 232 |
+
var ph = idx === -1 ? "" : (cur ? cur.phase : "done");
|
| 233 |
+
var dm = ph === "draft" ? ' on' : '';
|
| 234 |
+
var fm = ph === "verify" ? ' on' : '';
|
| 235 |
+
var dn = (ph === "done" || idx >= script.length) ? ' on' : '';
|
| 236 |
+
pipe.innerHTML = '<div class="ps' + dm + '"><span class="l">Draft Model</span><span class="v">guesses ' + depth + ' tokens</span></div>' +
|
| 237 |
+
'<span style="color:var(--dim);margin:0 4px">→</span>' +
|
| 238 |
+
'<div class="ps' + fm + '"><span class="l">Full Model</span><span class="v">verifies in 1 pass</span></div>' +
|
| 239 |
+
'<span style="color:var(--dim);margin:0 4px">→</span>' +
|
| 240 |
+
'<div class="ps' + dn + '"><span class="l">' + ((ph === "done" || idx >= script.length) ? 'Done' : 'Repeat') + '</span><span class="v">' + ((ph === "done" || idx >= script.length) ? 'complete' : 'until EOS') + '</span></div>';
|
| 241 |
+
|
| 242 |
+
var html = "";
|
| 243 |
+
for (var i = 0; i < conf.length; i++) {
|
| 244 |
+
var isPrefix = i < PREFIX.split(" ").length;
|
| 245 |
+
html += '<div class="tok t-OK"><span class="t">' + conf[i] + '</span><span class="lb">' + (isPrefix ? 'prefix' : 'generated') + '</span></div>';
|
| 246 |
+
}
|
| 247 |
+
if (cur && cur.drafts && cur.drafts.length > 0) {
|
| 248 |
+
var isVerify = cur.phase === "verify";
|
| 249 |
+
if (isVerify) {
|
| 250 |
+
html += '<div style="position:relative;display:flex;gap:4px;align-items:center;padding:6px 6px 4px;border:2px dashed var(--accent);border-radius:8px;background:rgba(88,166,255,.04);margin-top:2px">';
|
| 251 |
+
html += '<span style="position:absolute;top:-8px;left:6px;font-size:.5rem;color:var(--accent);background:var(--surface);padding:0 4px">verified in 1 pass</span>';
|
| 252 |
+
}
|
| 253 |
+
for (var i = 0; i < cur.drafts.length; i++) {
|
| 254 |
+
var cls, label;
|
| 255 |
+
if (cur.phase === "draft") {
|
| 256 |
+
cls = "t-DRAFT";
|
| 257 |
+
label = "drafting...";
|
| 258 |
+
} else {
|
| 259 |
+
cls = cur.drafts[i].correct ? "t-HIT" : "t-MISS";
|
| 260 |
+
label = cur.drafts[i].correct ? "hit" : "miss";
|
| 261 |
+
}
|
| 262 |
+
if (!cur.drafts[i].correct && cur.drafts[i].correction) {
|
| 263 |
+
// Split card: left = struck-out miss, right = green correction
|
| 264 |
+
html += '<div class="tok ' + cls + '" style="display:flex;gap:8px;align-items:center;text-decoration:none">' +
|
| 265 |
+
'<div style="text-align:center"><span class="t" style="text-decoration:line-through">' + cur.drafts[i].token + '</span><span class="lb">' + label + '</span></div>' +
|
| 266 |
+
'<span style="color:var(--dim);font-size:.65rem">→</span>' +
|
| 267 |
+
'<div class="tok t-HIT" style="margin:0;text-decoration:none"><span class="t">' + cur.drafts[i].correction + '</span><span class="lb">corrected</span></div>' +
|
| 268 |
+
'</div>';
|
| 269 |
+
} else {
|
| 270 |
+
html += '<div class="tok ' + cls + '"><span class="t">' + cur.drafts[i].token + '</span><span class="lb">' + label + '</span></div>';
|
| 271 |
+
}
|
| 272 |
+
} // end draft tokens loop
|
| 273 |
+
if (isVerify) { html += '</div>'; }
|
| 274 |
+
}
|
| 275 |
+
d("trow").innerHTML = html || '<span style="color:var(--dim);font-size:.75rem">no tokens</span>';
|
| 276 |
+
|
| 277 |
+
var passes = idx > 0 ? Math.ceil(idx / 2) : 0;
|
| 278 |
+
d("stats").innerHTML =
|
| 279 |
+
'<div class="stat"><div class="n">' + conf.length + '</div><div class="lb">tokens out</div></div>' +
|
| 280 |
+
'<div class="stat"><div class="n">' + passes + '</div><div class="lb">forward passes</div></div>' +
|
| 281 |
+
'<div class="stat"><div class="n">' + total_hits + '</div><div class="lb">hits</div></div>' +
|
| 282 |
+
'<div class="stat' + (total_misses > 0 ? ' bad' : '') + '"><div class="n">' + total_misses + '</div><div class="lb">misses</div></div>';
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
d("step").onclick = nextStep;
|
| 286 |
+
d("prev").onclick = prevStep;
|
| 287 |
+
d("reset").onclick = reset;
|
| 288 |
+
d("depth").onchange = reset;
|
| 289 |
+
document.onkeydown = function(e) {
|
| 290 |
+
if (e.key === " " || e.key === "Enter") { e.preventDefault(); nextStep(); }
|
| 291 |
+
if (e.key === "ArrowLeft") { e.preventDefault(); prevStep(); }
|
| 292 |
+
};
|
| 293 |
+
reset();
|
| 294 |
+
</script>
|
| 295 |
+
</body>
|
| 296 |
</html>
|