The artifact was reworked after real incidents: a week of lost data, a
student taught out of order, and spelling diagnoses the model invented.
This takes the new export in verbatim; the port catches up in the
commits that follow.
Copied byte-identical from the bundle:
lib/ lexicon.js and sync.js are new; gate.js gains enforcement,
hangul.js letter-level marking, srs.js recall evidence,
conjugation.js deconjugate(); blocks.js now takes the last
block, closes gloss at "=", and parses recall, ::result and
::confirmed
data/ curriculum.json v5 — six 다지기 phase reviews; the 371
roadmap words are unchanged and no band moves
prompt/ English-only rule, recall, LETTER-LEVEL CHECK, marking
audit-gate.mjs, run-checks.sh, fixtures/ — the word gate measured
against 54 real tutor messages
CI runs run-checks.sh in place of validate.mjs alone, and `npm run check`
gains the audit. Baselines: validate PASS 0/0; audit 7 of 41 and 2 of 13.
types/lib/ declares the new API, and test/lib/ pins it: letterCheck on
the prompt's own 짧다/빫다 case, deconjugation, the roadmap-first order
that keeps 마셔 out of Phase 1, sync's three gates, and recall evidence —
including the two ways lib's evidence is looser than PORT.md, pinned as
they are so the call site that tightens them is visibly needed.
TaskHost gains a plain recall renderer so the tree typechecks against the
wider Task union.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
64 lines
2.1 KiB
TypeScript
64 lines
2.1 KiB
TypeScript
/* Declarations for lib/hangul.js — the module itself ships unchanged. */
|
|
|
|
export const CHO: string;
|
|
export const JUNG: string;
|
|
export const JONG: string;
|
|
|
|
/** [initialIndex, medialIndex, finalIndex], or null if not a syllable block. */
|
|
export function decompose(ch: string): [number, number, number] | null;
|
|
export function compose(i: number, m: number, f?: number): string;
|
|
|
|
export const isJamo: {
|
|
initial(c: string): boolean;
|
|
medial(c: string): boolean;
|
|
final(c: string): boolean;
|
|
};
|
|
|
|
/**
|
|
* A 두벌식 IME. Hold one per input: feed jamo with key(), literal text with
|
|
* text(), Backspace with back(). Each call returns the full new value.
|
|
*/
|
|
export class Composer {
|
|
cho: string | null;
|
|
jung: string | null;
|
|
jong: string | null;
|
|
reset(): void;
|
|
readonly empty: boolean;
|
|
/** The syllable currently being assembled, as text. */
|
|
render(): string;
|
|
key(value: string, jamo: string): string;
|
|
back(value: string): string;
|
|
/** Commit the buffer and append literal text (space, punctuation). */
|
|
text(value: string, t: string): string;
|
|
}
|
|
|
|
export const KEYBOARD: {
|
|
rows: string[][];
|
|
shift: Record<string, string>;
|
|
};
|
|
|
|
/* ── letter-level marking ───────────────────────────────────────────── */
|
|
|
|
/** Two-consonant batchim clusters, unpacked: "ㄼ" → "ㄹ+ㅂ". */
|
|
export const CLUSTER: Record<string, string>;
|
|
|
|
/** ["first consonant", "vowel", "batchim"] — the three slots of a syllable. */
|
|
export const SLOT: string[];
|
|
|
|
/** "짧다" → "짧=ㅉ+ㅏ+ㄼ(ㄹ+ㅂ) · 다=ㄷ+ㅏ" */
|
|
export function spellOut(word: string): string;
|
|
|
|
/** The exact jamo difference, naming what was correct as well as what was
|
|
wrong. "" when either side is empty; "identical" when they match. */
|
|
export function letterDiff(expected: string, written: string): string;
|
|
|
|
export interface LetterRow {
|
|
/** The item as the student saw it — for a recall task, the English. */
|
|
prompt: string;
|
|
expected: string;
|
|
written: string;
|
|
}
|
|
|
|
/** The LETTER-LEVEL CHECK block for a marking message; "" when nothing differs. */
|
|
export function letterCheck(rows: LetterRow[]): string;
|