japanese-learning-avatar / avatar /transcript-harness.html
WolfDavid's picture
feat(02-08): word-lookup popover (reading, glosses, level, dictionary form) with published numbers
89a4bf6
Raw History Blame
4.19 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Transcript harness</title>
<link rel="stylesheet" href="./transcript.css" />
<style>
body {
margin: 16px;
font: 16px/1.4 system-ui, sans-serif;
}
/* Border and padding only: the column's height and overflow are transcript.css's, so
the harness measures the shape the app ships rather than one of its own. */
#transcript-text {
border: 1px solid #ccc;
padding: 8px;
}
</style>
</head>
<body>
<!--
THE STANDALONE LAYER FOR THE TRANSCRIPT (plan 02-07). Open it off a static server
with no Python app and no host framework: the golden fixture's token records go
through avatar/transcript.js into real <ruby><rt> DOM, exactly as stage.html does
for the avatar. If this page renders ruby and the
deployed page does not, the fault is the host, not the renderer.
window.__renderFixture(number, who) takes the 1-BASED sentence number of the 02-05
table (#9 = 日本語を勉強しています。, #13 = 田中さんは東京に住んでいます。). That is the ONE
convention every prose reference and every test in 02-07 / 02-08 / 02-09 uses -
never pass a 0-based index. window.__fixtureUnits(number) returns the same
sentence's token records for an oracle to compute against.
Query parameters: ?narrow=1 sets #transcript-text to a 300 px SCROLLING column - the
phone shape plan 02-08's popover has to stay inside, and the only way the container's
own scroll event (which closes the card) can fire at all.
-->
<div id="reading-controls" class="reading-controls">
<label for="furigana-mode">Furigana</label>
<select id="furigana-mode" aria-label="Furigana mode">
<option value="always" selected>always</option>
<option value="above">above my level</option>
<option value="never">never</option>
</select>
<label for="level-select">My level</label>
<select id="level-select" aria-label="JLPT level">
<option value="N5" selected>N5</option>
<option value="N4">N4</option>
<option value="N3">N3</option>
<option value="N2">N2</option>
</select>
</div>
<div id="transcript-text" class="transcript" aria-live="polite"></div>
<script type="module">
import { createTranscript } from './transcript.js';
const params = new URLSearchParams(location.search);
const container = document.getElementById('transcript-text');
if (params.get('narrow') === '1') {
container.style.width = '300px';
container.style.maxHeight = '360px';
container.style.overflowY = 'auto';
}
const t = createTranscript(container);
t.bindPointer();
window.__transcript = t;
// The same objects the renderer writes to, not copies: a test reads live numbers.
window.__transcriptDebug = t.debug.furigana;
window.__popoverDebug = t.debug.popover;
window.__transcriptReady = false;
document.getElementById('furigana-mode').addEventListener('change', (e) => {
t.setMode(e.target.value);
});
document.getElementById('level-select').addEventListener('change', (e) => {
t.setLevel(e.target.value);
});
// The static server serves the repo root; the golden fixture is one level up from avatar/.
const response = await fetch('../tests/fixtures/sentences.json');
if (!response.ok) throw new Error(`golden fixture: HTTP ${response.status}`);
const fixture = await response.json();
const sentences = fixture.sentences;
window.__fixtures = sentences;
window.__fixtureUnits = (number) => sentences[number - 1].units;
window.__renderFixture = (number, who = 'avatar') => {
const { text, units } = sentences[number - 1];
const { lineId } = t.addLine({ who, text });
t.setTokens(lineId, units);
return lineId;
};
window.__transcriptReady = true;
</script>
</body>
</html>