/** * Learn view: the letter ↔ rhythm chart. Tap any entry to hear (or have the * robot tap) its rhythm — the whole point is that a letter *is* a rhythm. */ import { el, clear, morsePattern } from "./dom.js"; import { CHAR_TO_MORSE, morseToTokens } from "../lib/morse.js"; import { tokensToOnsets } from "../lib/wire.js"; const LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""); const DIGITS = "0123456789".split(""); export function createLearn(ctx) { const root = el("section.view#view-learn"); function playChar(ch) { const pattern = CHAR_TO_MORSE[ch]; if (!pattern) return; const onsets = tokensToOnsets(morseToTokens(pattern), ctx.timing()); if (ctx.state.emitter === "robot" && ctx.reachy) { ctx.tapper().tap(onsets); } else { ctx.synth().play(onsets, { clickMs: ctx.timing().clickMs }); } } function grid(title, chars) { const g = el("div.chart-grid"); chars.forEach((ch) => { const cell = el("button.chart-cell", { onclick: () => { playChar(ch); cell.classList.remove("ping"); void cell.offsetWidth; cell.classList.add("ping"); } }, [ el("span.cell-char", {}, ch), morsePattern(CHAR_TO_MORSE[ch]), ]); g.append(cell); }); return el("div.chart-section", {}, [el("h3", {}, title), g]); } const emitterNote = el("div.status muted"); function refresh() { emitterNote.textContent = ctx.state.emitter === "robot" && ctx.reachy ? "Tapping plays on the robot's antennas." : "Tapping plays a beep on this device. (Switch sender in Compose.)"; } root.append( el("h2", {}, "Learn Morse"), el("p.muted", {}, "A letter is just a rhythm of short and long beats. Tap any tile to feel it."), emitterNote, grid("Letters", LETTERS), grid("Numbers", DIGITS), el("div.legend", {}, [ el("span.legend-item", {}, [morsePattern("."), " dot: one tap"]), el("span.legend-item", {}, [morsePattern("-"), " dash: quick double tap"]), ]), ); refresh(); return { node: root, onShow: refresh }; } void clear;