@yagejs-addons/dialogue 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DialogueController-BMeNLi0v.d.cts +1204 -0
- package/dist/DialogueController-Cs5IUc-u.d.ts +1204 -0
- package/dist/chunk-7QVYU63E.js +7 -0
- package/dist/chunk-7QVYU63E.js.map +1 -0
- package/dist/chunk-CU47RPEB.js +410 -0
- package/dist/chunk-CU47RPEB.js.map +1 -0
- package/dist/chunk-GJQKZCOL.js +983 -0
- package/dist/chunk-GJQKZCOL.js.map +1 -0
- package/dist/index.cjs +3441 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +591 -0
- package/dist/index.d.ts +591 -0
- package/dist/index.js +2048 -0
- package/dist/index.js.map +1 -0
- package/dist/presenters.cjs +3149 -0
- package/dist/presenters.cjs.map +1 -0
- package/dist/presenters.d.cts +1817 -0
- package/dist/presenters.d.ts +1817 -0
- package/dist/presenters.js +2920 -0
- package/dist/presenters.js.map +1 -0
- package/dist/types-DSbBSlh7.d.cts +375 -0
- package/dist/types-DSbBSlh7.d.ts +375 -0
- package/dist/yaml.cjs +726 -0
- package/dist/yaml.cjs.map +1 -0
- package/dist/yaml.d.cts +23 -0
- package/dist/yaml.d.ts +23 -0
- package/dist/yaml.js +37 -0
- package/dist/yaml.js.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/markup.ts","../src/core/LineReveal.ts","../src/core/vars.ts","../src/core/expr.ts","../src/core/i18n.ts","../src/core/validate.ts","../src/core/expr-parse.ts","../src/core/formats/canonical.ts","../src/core/formats/compact.ts","../src/core/defineScript.ts","../src/core/runner.ts","../src/core/session.ts","../src/core/channels/voice.ts","../src/DialogueController.ts","../src/input/InputBinding.ts","../src/events.ts"],"sourcesContent":["/**\n * @yagejs-addons/dialogue — root entry (headless + non-pixi).\n *\n * This barrel MUST NOT transitively import `pixi.js` or `@yagejs/renderer`.\n * It re-exports the headless dialogue model (runner, session, types, markup,\n * i18n, the canonical JSON loader plus the `parseExpr` and compact-DSL\n * front-ends), engine-scoped events, the `DialogueController` (a `@yagejs/core`\n * Component), public types, and the `@yagejs/input` keyboard/pointer bindings.\n *\n * Pixi-backed presenters (chrome, text views, composites, avatars, factories,\n * default/textured themes, radial) live behind the `./presenters` subpath so\n * the headless import path never pulls a renderer.\n */\n\n// Headless dialogue model (engine-agnostic): runner, session, types, markup,\n// i18n, canonical format.\nexport * from \"./core/index.js\";\n\n// The thin YAGE host Component + its public option/bundle types.\nexport { DialogueController } from \"./DialogueController.js\";\nexport type {\n DialogueControllerOptions,\n DialogueBundle,\n} from \"./DialogueController.js\";\n\n// `Mountable` — the YAGE lifecycle (`mount(scene)` / `dispose()`) an extra\n// channel implements when it needs the scene (e.g. a CameraEffects channel).\n// Structurally pixi-free (`import type { Scene }` only), so re-exporting the\n// type keeps the root dist-grep at 0. The presenter trio's adapter contracts\n// (Chrome/Choice/Text presenters) stay behind `./presenters`.\nexport type { Mountable } from \"./chrome/DialogueUiAdapter.js\";\n\n// Engine-scoped lifecycle / command events.\nexport {\n DialogueStartedEvent,\n DialogueLineEvent,\n DialogueChoiceShownEvent,\n DialogueChoiceMadeEvent,\n DialogueCommandEvent,\n DialogueEndedEvent,\n DialogueRevealCompletedEvent,\n DialogueRevealMarkerEvent,\n DialogueSelectionChangedEvent,\n DialogueSkipUsedEvent,\n DialogueAutoAdvanceEvent,\n} from \"./events.js\";\n\n// @yagejs/input device bindings (pixi-free).\nexport {\n KeyboardInputBinding,\n PointerInputBinding,\n CompositeInputBinding,\n fullControls,\n DEFAULT_ACTIONS,\n FULL_ACTIONS,\n} from \"./input/index.js\";\nexport type {\n InputBinding,\n DialogueActions,\n PointerChoiceTarget,\n} from \"./input/index.js\";\n","/**\n * Inline-markup parser. Turns an authored string into styled {@link TextRun}s\n * plus {@link RevealToken}s, using a small BBCode-ish tag syntax that survives\n * translation (translators keep the tags, reorder the words):\n *\n * plain text\n * [b]bold[/b] [i]italic[/i]\n * [color=#ffcc00]hex[/color] [color=gold]named[/color]\n * [wave]animated[/wave] (effect span — OPEN vocabulary: any [name]..[/name];\n * the bundled view animates wave/shake/pulse/rainbow)\n * [speed=2]faster[/speed] [speed=0.5]slower[/speed]\n * [pause=400/] (self-closing reveal PAUSE — holds at its offset, in ms)\n * [sfx=ding/] (self-closing reveal MARKER — fires at its offset)\n * [expression=happy/] (self-named shortcut → props { expression: happy })\n * [shake amount=3/] (marker with explicit key=value props)\n * [shake=500 amount=3/] (shortcut + props compose → { shake: 500, amount: 3 })\n * \\[literal bracket]\n *\n * Tags nest; styles inherit down the stack (so [b][color=red]X[/color][/b]\n * is bold+red). A trailing `/` makes a tag **self-closing** — a zero-width\n * {@link RevealToken} (a `[pause=600/]` hold or a `[name k=v/]` marker) that the\n * reveal drains at its char offset, distinct from the styling tags (which never\n * end in `/`). Pause + markers share one ordered stream, so **source order is\n * drain order**: `[pause=600/][shake/]` holds then fires; `[shake/][pause=600/]`\n * fires then holds. A non-self-closing tag that isn't a built-in text attribute\n * opens an EFFECT span named after the tag (an open vocabulary the presenter\n * interprets); translators MUST keep a marker/pause's self-closing `/` so the\n * token survives a re-order.\n */\n\nimport type { ParsedText, RevealToken, RunStyle, TextRun } from \"./types.js\";\n\n/** The empty parse result (no runs / tokens, length 0). Shared so a presenter or\n * the session can present a contentless line (an empty choice prompt) without\n * re-constructing the shape — and without forgetting the required `tokens`\n * field. */\nexport const EMPTY_PARSED: ParsedText = Object.freeze({\n runs: Object.freeze([]) as readonly TextRun[],\n tokens: Object.freeze([]) as readonly RevealToken[],\n length: 0,\n});\n\nconst NAMED_COLORS: Record<string, number> = {\n black: 0x000000,\n white: 0xffffff,\n red: 0xff5a5a,\n green: 0x8ce06b,\n blue: 0x6fa3d9,\n yellow: 0xffe066,\n gold: 0xffd25a,\n orange: 0xf5a168,\n purple: 0xc9a4ff,\n pink: 0xff9ecb,\n gray: 0x9aa0a6,\n grey: 0x9aa0a6,\n};\n\nfunction parseColor(raw: string): number | undefined {\n const v = raw.trim().toLowerCase();\n if (v.startsWith(\"#\")) {\n const hex = v.slice(1);\n if (/^[0-9a-f]{6}$/.test(hex)) return parseInt(hex, 16);\n if (/^[0-9a-f]{3}$/.test(hex)) {\n // #rgb → #rrggbb\n const r = hex[0]!;\n const g = hex[1]!;\n const b = hex[2]!;\n return parseInt(`${r}${r}${g}${g}${b}${b}`, 16);\n }\n return undefined;\n }\n if (/^0x[0-9a-f]{6}$/.test(v)) return parseInt(v.slice(2), 16);\n return NAMED_COLORS[v];\n}\n\ninterface Frame {\n /** Originating tag name, so a closing tag pops the matching frame. */\n readonly name: string;\n /** This tag's own style delta (not pre-merged with parents). */\n readonly override: Partial<RunStyle>;\n}\n\n/**\n * Fold every open frame's delta outermost→innermost into one effective style.\n * Recomputing from deltas (rather than caching a pre-merged style per frame)\n * means closing any frame — even a crossed/mismatched one — yields the\n * correct inherited style for the text that follows.\n */\nfunction effectiveStyle(stack: readonly Frame[]): RunStyle {\n let style: RunStyle = {};\n for (const f of stack) style = mergeStyle(style, f.override);\n return style;\n}\n\n/** Merge a child override onto the inherited parent style. */\nfunction mergeStyle(parent: RunStyle, child: Partial<RunStyle>): RunStyle {\n const merged: { -readonly [K in keyof RunStyle]: RunStyle[K] } = {\n ...parent,\n ...stripUndefined(child),\n };\n // `speed` composes multiplicatively so nested [speed] tags stack.\n if (child.speed !== undefined) {\n merged.speed = (parent.speed ?? 1) * child.speed;\n }\n return merged;\n}\n\nfunction stripUndefined(s: Partial<RunStyle>): Partial<RunStyle> {\n const out: Partial<RunStyle> = {};\n for (const k of Object.keys(s) as (keyof RunStyle)[]) {\n if (s[k] !== undefined) (out as Record<string, unknown>)[k] = s[k];\n }\n return out;\n}\n\n// Groups: 1 closing `/`, 2 name, 3 `=value` (styled spans + a marker's self-named\n// shortcut — the shared `=`-separator), 4 space-separated `key=value` props, 5 a\n// trailing `/` marking a self-closing token (`[pause=N/]` or a marker). The value\n// (3) and prop values (4) both exclude whitespace and `/`, so the self-named\n// shortcut composes with explicit props (`[shake=500 amount=3/]` → group 3 `500`,\n// group 4 ` amount=3`) and the trailing slash stays unambiguous. Groups 4–5 are\n// additive: an existing styled/closing tag matches them empty.\nconst TAG_RE =\n /\\[(\\/?)([a-zA-Z]+)(?:=([^\\s\\]/]*))?((?:\\s+[A-Za-z_][\\w-]*=[^\\s\\]/]*)*)(\\/)?\\]/g;\n\n/**\n * Grapheme segmenter for all reveal bookkeeping. Pixi's `SplitText` /\n * `SplitBitmapText` create one glyph node per grapheme via\n * `CanvasTextMetrics.graphemeSegmenter`, which is `new Intl.Segmenter()`\n * (default \"grapheme\" granularity). We intentionally use the same segmentation\n * — without importing pixi (this file is in the pixi-free root entry) — so run\n * lengths, pause offsets, and per-glyph styles line up with rendered glyphs on\n * emoji / ZWJ sequences / combining marks.\n */\nconst GRAPHEME_SEGMENTER = new Intl.Segmenter();\n\n/**\n * Split a string into graphemes (user-perceived characters) — the unit the\n * renderer creates one glyph node per, and the unit every reveal-side count\n * (`ParsedText.length`, `TextRun.graphemeCount`, `PauseToken.atChar`) uses.\n */\nexport function splitGraphemes(text: string): string[] {\n const out: string[] = [];\n for (const s of GRAPHEME_SEGMENTER.segment(text)) out.push(s.segment);\n return out;\n}\n\nexport function parseMarkup(input: string): ParsedText {\n const runs: TextRun[] = [];\n const tokens: RevealToken[] = [];\n const stack: Frame[] = [];\n let charCount = 0;\n let buffer = \"\";\n\n const flush = (): void => {\n if (buffer.length === 0) return;\n // Segment once per flushed run (O(n) over the whole input in total).\n const graphemeCount = splitGraphemes(buffer).length;\n runs.push({ text: buffer, style: effectiveStyle(stack), graphemeCount });\n charCount += graphemeCount;\n buffer = \"\";\n };\n\n // Walk the string, copying literal text into `buffer` and acting on tags.\n let lastIndex = 0;\n TAG_RE.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = TAG_RE.exec(input)) !== null) {\n // Escape-awareness: count the contiguous backslashes immediately before the\n // `[`. An odd count means the bracket itself is escaped (`\\[b]` → literal\n // \"[b]\"), so emit the tag text verbatim — consuming the escaping backslash —\n // instead of acting on it. An even count is just escaped backslashes\n // (`\\\\[b]` → \"\\\" + a REAL [b] tag), handled by unescape() as usual.\n let backslashes = 0;\n for (let i = m.index - 1; i >= lastIndex && input[i] === \"\\\\\"; i--) backslashes++;\n if (backslashes % 2 === 1) {\n buffer += unescape(input.slice(lastIndex, m.index - 1)) + m[0];\n lastIndex = TAG_RE.lastIndex;\n continue;\n }\n const literal = input.slice(lastIndex, m.index);\n buffer += unescape(literal);\n lastIndex = TAG_RE.lastIndex;\n\n const closing = m[1] === \"/\";\n const name = m[2]!.toLowerCase();\n const arg = m[3];\n const propsStr = m[4];\n const selfClosing = m[5] === \"/\";\n\n // A tag carrying `key=value` props but no trailing `/` is neither a styled\n // span (those take no props) nor a self-closing marker (those require the\n // `/`). Emit it verbatim as literal text — restoring the pre-marker behavior\n // where a space-bearing `[name k=v]` simply didn't match the tag regex, so a\n // forgotten slash shows up as visible text instead of silently opening an\n // effect span over the rest of the line and dropping the props.\n if (propsStr && !selfClosing) {\n buffer += m[0];\n continue;\n }\n\n if (closing) {\n flush();\n // Pop the innermost frame opened by a tag of this name (BBCode rule);\n // a stray close with no match is ignored (permissive / forward-compatible).\n for (let i = stack.length - 1; i >= 0; i--) {\n if (stack[i]!.name === name) {\n stack.splice(i, 1);\n break;\n }\n }\n continue;\n }\n\n // Self-closing reveal token (`[name k=v/]`): the trailing `/` distinguishes\n // it from a styling tag of the same name (`[shake]…[/shake]` is an effect\n // span; `[shake/]` is a marker). `pause` is the one parser-reserved name —\n // it becomes a typed PauseToken (hold), everything else a MarkerToken.\n if (selfClosing) {\n flush();\n if (name === \"pause\") {\n const ms = Number(arg ?? \"0\");\n if (Number.isFinite(ms) && ms > 0) {\n tokens.push({ kind: \"pause\", atChar: charCount, ms });\n }\n } else {\n tokens.push({\n kind: \"marker\",\n atChar: charCount,\n name,\n props: markerProps(name, arg, propsStr),\n });\n }\n continue;\n }\n\n const override = styleForTag(name, arg);\n if (override) {\n flush();\n stack.push({ name, override });\n }\n // A built-in styling tag with a bad/missing arg (color/speed) → ignore; text flows.\n }\n buffer += unescape(input.slice(lastIndex));\n flush();\n\n return { runs: mergeAdjacent(runs), tokens, length: charCount };\n}\n\n/**\n * Build a marker's props from the Yarn-style forms. The self-named shortcut\n * `[name=val/]` (group 3) ≡ `[name name=val/]`, so it seeds `{ [name]: val }`,\n * and explicit space-separated `key=value` pairs (group 4) merge on top — the two\n * compose (`[shake=500 amount=3/]` → `{ shake: \"500\", amount: \"3\" }`), because the\n * value group excludes whitespace and so stops at the first space. `[name/]` →\n * `{}`. Keys lower-cased (like tag names); values kept verbatim (no whitespace).\n */\nfunction markerProps(\n name: string,\n arg: string | undefined,\n propsStr: string | undefined,\n): Record<string, string> {\n const props: Record<string, string> = {};\n if (arg !== undefined) props[name] = arg;\n if (propsStr) {\n for (const tok of propsStr.trim().split(/\\s+/)) {\n const eq = tok.indexOf(\"=\");\n if (eq > 0) props[tok.slice(0, eq).toLowerCase()] = tok.slice(eq + 1);\n }\n }\n return props;\n}\n\nfunction styleForTag(name: string, arg?: string): Partial<RunStyle> | null {\n switch (name) {\n case \"b\":\n case \"bold\":\n return { bold: true };\n case \"i\":\n case \"italic\":\n return { italic: true };\n case \"color\":\n case \"c\": {\n const color = arg ? parseColor(arg) : undefined;\n return color !== undefined ? { color } : null;\n }\n case \"speed\": {\n const s = Number(arg);\n return Number.isFinite(s) && s > 0 ? { speed: s } : null;\n }\n default:\n // Any other paired (non-self-closing) tag opens an effect span carrying its\n // name — an OPEN vocabulary the presenter interprets. The bundled text view\n // animates the BuiltinEffectIds; an unrecognized name renders as plain\n // styled text. (Built-in text attributes are handled above; a marker is the\n // self-closing `[name/]` form, parsed before styleForTag is reached.)\n return { effect: name };\n }\n}\n\n/** `\\[` → `[`, `\\]` → `]`, `\\\\` → `\\`. */\nfunction unescape(s: string): string {\n return s.replace(/\\\\([[\\]\\\\])/g, \"$1\");\n}\n\n/** Coalesce neighbouring runs that ended up with identical styles. */\nfunction mergeAdjacent(runs: readonly TextRun[]): TextRun[] {\n const out: TextRun[] = [];\n for (const run of runs) {\n const prev = out[out.length - 1];\n if (prev && sameStyle(prev.style, run.style)) {\n // Sum the per-flush counts rather than re-segmenting the joined text:\n // pause offsets were tallied per flush, so this keeps `length`/`atChar`/\n // run counts on one consistent basis. (A grapheme split across a tag\n // boundary — e.g. a combining mark right after `[/b]` — would join when\n // the renderer segments the full line; the cursor then finishes one\n // step past the last glyph, which the reveal clamps harmlessly.)\n out[out.length - 1] = {\n text: prev.text + run.text,\n style: prev.style,\n graphemeCount: prev.graphemeCount + run.graphemeCount,\n };\n } else {\n out.push(run);\n }\n }\n return out;\n}\n\nfunction sameStyle(a: RunStyle, b: RunStyle): boolean {\n return (\n !!a.bold === !!b.bold &&\n !!a.italic === !!b.italic &&\n a.color === b.color &&\n a.effect === b.effect &&\n (a.speed ?? 1) === (b.speed ?? 1)\n );\n}\n\n/** Strip every tag, returning plain text (useful for measuring / a11y / logs). */\nexport function stripMarkup(input: string): string {\n return parseMarkup(input)\n .runs.map((r) => r.text)\n .join(\"\");\n}\n\n/**\n * The name of the first bracketed `[tag]` in `input` that {@link parseMarkup}\n * would drop silently, or `null` when every tag is meaningful. Escape-aware:\n * `\\[x]` is literal text, not a tag, matching the parser.\n *\n * Because the effect vocabulary is open, every paired `[name]…[/name]` opens an\n * effect span and every `[name/]` is a marker — both are meaningful, so neither\n * is flagged. What remains droppable is a built-in styling tag with a malformed\n * argument (`[color=notacolor]`, `[speed=abc]`), which {@link styleForTag} can't\n * act on and discards — almost always a typo.\n *\n * The compact authoring front-end calls this on choice text, where `[..]` is\n * reserved for inline markup, to surface such a dropped tag instead of letting a\n * mistyped attribute vanish. (Say-line text is passed through untouched.)\n */\nexport function firstUnknownTag(input: string): string | null {\n TAG_RE.lastIndex = 0;\n let lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = TAG_RE.exec(input)) !== null) {\n // Same odd-backslash escape test parseMarkup uses: an escaped `\\[` is text.\n let backslashes = 0;\n for (let i = m.index - 1; i >= lastIndex && input[i] === \"\\\\\"; i--) backslashes++;\n lastIndex = TAG_RE.lastIndex;\n if (backslashes % 2 === 1) continue;\n // None of these is dropped, so none is a typo: a closing tag (`m[1]`) pops a\n // frame, a self-closing marker (`[sfx=ding/]`, `m[5]`) parses to a\n // MarkerToken, and a props-bearing tag with no slash (`[name k=v]`, `m[4]`)\n // is kept as literal text.\n if (m[1] === \"/\" || m[4] || m[5] === \"/\") continue;\n const name = m[2]!.toLowerCase();\n // Every other opening tag opens a span — a built-in text attribute or, for\n // any other name, an effect. The lone droppable case is a built-in styling\n // tag whose argument doesn't parse (styleForTag returns null).\n if (styleForTag(name, m[3]) === null) return name;\n }\n return null;\n}\n","/**\n * LineReveal — the headless typewriter clock. Given a {@link ParsedText} (markup\n * already parsed into runs + an ordered {@link RevealToken} stream), a base\n * `charsPerSec`, a per-line speed multiplier, and `update(dt)` ticks, it advances\n * a reveal cursor in **graphemes** (the unit `markup.ts` counts and the renderer\n * splits glyphs by), drains the tokens IN SOURCE ORDER (a `pause` holds, a\n * `marker` fires a {@link RevealBeat}), applies per-run `[speed]`, and fires\n * completion **exactly once** per line.\n *\n * It is renderer-free on purpose: a DOM-overlay or per-word presenter can drive\n * the same reveal logic and map the grapheme cursor onto its own rendering,\n * without pulling the renderer in. The default `DialogueTextView` consumes it\n * and keeps only the SplitText concerns — the code-unit→glyph prefix mapping and\n * per-glyph style fan-out — which LineReveal deliberately does NOT own.\n *\n * What it owns: the reveal cursor, the `ParsedText.tokens` drain (one ordered\n * index over pauses + markers), the hold-to-fast-forward multiplier, the per-line\n * and per-run (`RunStyle.speed`) speeds, and the fired-once completion. What it\n * does NOT own: anything that touches a glyph, a texture, or a layout. Counts are\n * graphemes throughout — it reads the pre-computed grapheme counts off\n * `ParsedText` (`length`, `TextRun.graphemeCount`, a token's `atChar`) and never\n * re-segments.\n */\n\nimport type { MarkerToken, ParsedText } from \"./types.js\";\n\n/**\n * A reveal-time beat the clock emits as the cursor advances: a per-grapheme\n * `tick` (one per revealed grapheme — raw, including whitespace; the host\n * filters) and a `marker` when the cursor reaches a {@link MarkerToken}'s\n * offset. `viaSkip` is true when the marker was drained by {@link\n * LineReveal.complete} (a skip / fast-forward) rather than reached during normal\n * typing, so a host can suppress a loud one-shot that only fired because of a\n * skip click. Ticks are NOT emitted on a skip (replaying dozens at once would\n * machine-gun).\n */\nexport type RevealBeat =\n | { readonly kind: \"tick\"; readonly index: number }\n | { readonly kind: \"marker\"; readonly marker: MarkerToken; readonly viaSkip: boolean };\n\nexport class LineReveal {\n private parsed: ParsedText | undefined;\n /** Reveal cursor, in graphemes (fractional while typing). */\n private cursor = 0;\n private pauseTimer = 0;\n /** Next un-drained token in `parsed.tokens` (one ordered cursor over pauses +\n * markers — source order is drain order). */\n private tokenIdx = 0;\n /** Graphemes already ticked (so each grapheme ticks exactly once). */\n private tickCount = 0;\n /** Hold-to-fast-forward rate (1 = normal). */\n private speedMul = 1;\n /** Per-line `say.speed` multiplier (1 = base). */\n private lineSpeed = 1;\n private done = false;\n private completed = false;\n /** Fired exactly once when the line finishes revealing. The consuming view\n * wires this to the session-owned reveal listener (NOT a public mutable\n * field a game could clobber). */\n private onComplete: (() => void) | undefined;\n /** Per-grapheme ticks + inline markers, wired by the consuming view to the\n * session-owned beat listener (like {@link onComplete}, never a public field). */\n private onBeat: ((beat: RevealBeat) => void) | undefined;\n\n /** @param charsPerSec base reveal rate (graphemes/second), scaled by the\n * hold, per-line, and per-run multipliers. */\n constructor(private readonly charsPerSec: number) {}\n\n /**\n * Register the completion listener — fires once per line, the moment the\n * cursor reaches the end (or synchronously from {@link begin} for an empty\n * line, or from {@link complete}). Pass `undefined` to clear.\n */\n setCompletionListener(listener: (() => void) | undefined): void {\n this.onComplete = listener;\n }\n\n /**\n * Register the reveal-beat listener — per-grapheme ticks and inline markers,\n * in char order, the moment the cursor reaches each. Session-owned (set once,\n * like {@link setCompletionListener}); pass `undefined` to clear.\n */\n setBeatListener(listener: ((beat: RevealBeat) => void) | undefined): void {\n this.onBeat = listener;\n }\n\n /**\n * Start revealing a new line. Resets the cursor, pauses, and the hold\n * multiplier (a stale fast-forward must not leak into the next line — an\n * active binding re-asserts it on its next poll). An **empty** line\n * (`parsed.length === 0`) is complete immediately and fires the completion\n * listener synchronously, matching the no-typewriter contract.\n */\n begin(parsed: ParsedText, lineSpeed = 1): void {\n this.parsed = parsed;\n this.lineSpeed = lineSpeed > 0 ? lineSpeed : 1;\n this.cursor = 0;\n this.pauseTimer = 0;\n this.tokenIdx = 0;\n this.tickCount = 0;\n this.speedMul = 1;\n this.done = parsed.length === 0;\n this.completed = false;\n // Drain any offset-0 tokens synchronously (a marker-only / length-0 line, a\n // line that opens with a marker, or a leading `[pause/]` that delays the\n // first glyph) — the beat listener is session-owned and wired before begin(),\n // like the completion listener. Tokens come before the empty-line completion:\n // they're part of the line, completion ends it.\n this.drainTokens();\n if (this.done) this.finish();\n }\n\n /** Hold-to-fast-forward multiplier (1 = normal, e.g. 4 while skip is held). */\n setSpeedMultiplier(m: number): void {\n this.speedMul = Math.max(1, m);\n }\n\n /** Advance the reveal cursor by `dt` (ms). Honours armed pauses and per-run\n * speed; fires completion once the cursor reaches the end. No-op after the\n * line is done or before the first {@link begin}. */\n update(dt: number): void {\n const parsed = this.parsed;\n if (!parsed || this.done) return;\n if (this.pauseTimer > 0) {\n this.pauseTimer = Math.max(0, this.pauseTimer - dt);\n } else {\n // A token sitting exactly at the current cursor (a leading token, or the\n // next one when resuming after a hold) drains before we advance — a marker\n // fires, a pause re-arms (and re-clamps), so the advance is skipped.\n this.drainTokens();\n if (this.pauseTimer === 0) {\n const rate =\n this.charsPerSec * this.speedMul * this.lineSpeed * this.runSpeedAt(this.cursor);\n this.cursor = Math.min(parsed.length, this.cursor + (rate * dt) / 1000);\n // Drain tokens up to the new cursor IN SOURCE ORDER: a marker fires, a\n // pause clamps the cursor back to its offset and stops the drain (so a\n // later token waits for the next hold-resume). emitTicks runs AFTER, so\n // ticks stop at the clamp and glyphs past a [pause] don't reveal early.\n this.drainTokens();\n this.emitTicks();\n }\n }\n if (this.cursor >= parsed.length && this.pauseTimer === 0) this.finish();\n }\n\n /** Reveal everything now (skip-to-end on a click/tap). Drains any not-yet-fired\n * markers in order so their consequences still happen (`viaSkip=true` lets a\n * host suppress a loud one-shot) and blows straight through pending pauses (a\n * skip ignores holds), but DISCARDS pending ticks — replaying dozens of\n * typewriter blips at once would machine-gun. Fires completion. */\n complete(): void {\n const parsed = this.parsed;\n if (!parsed) return;\n this.cursor = parsed.length;\n this.pauseTimer = 0;\n this.drainTokens(true);\n this.tickCount = parsed.length; // swallow the pending ticks\n this.finish();\n }\n\n /** Revealed grapheme count (fractional while typing). The view floors this to\n * map onto its glyph prefix table. */\n get revealed(): number {\n return this.cursor;\n }\n\n /** True once the line is fully revealed (also true for an empty line). */\n isComplete(): boolean {\n return this.done;\n }\n\n /** True while glyphs are still appearing. */\n isRevealing(): boolean {\n return !this.done;\n }\n\n private finish(): void {\n this.done = true;\n if (this.completed) return;\n this.completed = true;\n this.onComplete?.();\n }\n\n /**\n * Drain tokens whose offset the cursor has reached, IN SOURCE ORDER. A `marker`\n * emits a beat; a `pause` arms the hold, clamps the cursor to its offset, and\n * STOPS the drain for this frame (a one-frame advance can overshoot the offset,\n * so the clamp keeps glyphs past the beat from popping in early, and a later\n * token waits until the hold resumes). `viaSkip` (from {@link complete}) tags\n * drained markers and blows straight through pauses without holding. Monotonic\n * `tokenIdx` → each token is handled exactly once.\n */\n private drainTokens(viaSkip = false): void {\n const tokens = this.parsed?.tokens;\n if (!tokens) return;\n while (this.tokenIdx < tokens.length && this.cursor >= tokens[this.tokenIdx]!.atChar) {\n const tok = tokens[this.tokenIdx]!;\n this.tokenIdx++;\n if (tok.kind === \"marker\") {\n this.onBeat?.({ kind: \"marker\", marker: tok, viaSkip });\n } else if (!viaSkip && tok.ms > 0) {\n this.pauseTimer = tok.ms;\n this.cursor = Math.min(this.cursor, tok.atChar);\n return; // hold here this frame; later tokens wait\n }\n }\n }\n\n /** Emit a `tick` for each grapheme newly revealed since the last call (raw —\n * no whitespace test; the host filters). Multiple in order on a large-dt\n * frame; `tickCount` is monotonic so none repeat. */\n private emitTicks(): void {\n const next = Math.floor(this.cursor);\n for (let i = this.tickCount; i < next; i++) {\n this.onBeat?.({ kind: \"tick\", index: i });\n }\n this.tickCount = next;\n }\n\n /** Reveal speed multiplier for whichever run the cursor currently sits in. */\n private runSpeedAt(reveal: number): number {\n const parsed = this.parsed;\n if (!parsed) return 1;\n const at = Math.floor(reveal);\n let acc = 0;\n for (const run of parsed.runs) {\n if (at < acc + run.graphemeCount) return run.style.speed ?? 1;\n acc += run.graphemeCount;\n }\n return 1;\n }\n}\n","/**\n * {@link VariableStorage} implementations — the read/write bridge between a\n * conversation and game state. One **opaque** name namespace; scoping is\n * the host's policy. Three building blocks:\n *\n * • {@link MemoryVariableStorage} — the zero-config default. A plain Map; holds\n * dialogue-locals and seeded defaults, persists across plays.\n * • {@link cells} — first-class **two-way binding**: `{ gold: { get, set } }`\n * drives a value the *script* owns the arithmetic of (a read-only getter\n * throws on `set`). A bare `() => value` is the read-only shorthand.\n * • {@link compose} — layer several storages into one (reads/writes route to\n * the first that `has` the name; a brand-new name lands in the last —\n * so put a writable store last to catch seeds + locals).\n *\n * The interface lives in `types.ts`; this file is the concrete kit. Seed-if-\n * absent + persistence are policy of the *caller* (`session.play`), not the\n * storage — these just hold values.\n */\n\nimport type { VariableStorage, VarMap, VarValue } from \"./types.js\";\n\n/** Materialize a storage's enumerable variables into a plain map — backs\n * `{token}` interpolation params and `handle.getVars()`. */\nexport function materialize(storage: VariableStorage): VarMap {\n const out: VarMap = {};\n for (const [name, value] of storage.entries()) out[name] = value;\n return out;\n}\n\n/** The zero-config default storage: a Map-backed, fully-enumerable store. */\nexport class MemoryVariableStorage implements VariableStorage {\n private readonly map = new Map<string, VarValue>();\n\n constructor(initial?: Readonly<VarMap>) {\n if (initial) for (const [name, value] of Object.entries(initial)) this.map.set(name, value);\n }\n\n get(name: string): VarValue | undefined {\n return this.map.get(name);\n }\n set(name: string, value: VarValue): void {\n this.map.set(name, value);\n }\n has(name: string): boolean {\n return this.map.has(name);\n }\n entries(): Iterable<readonly [string, VarValue]> {\n return this.map.entries();\n }\n /** Drop everything — host-controlled reset (variables persist across plays by default). */\n clear(): void {\n this.map.clear();\n }\n}\n\n/** A two-way (or read-only) binding for one game-owned value. A bare function is\n * the read-only shorthand for `{ get }`. */\nexport type Cell =\n | { get(): VarValue; set?(value: VarValue): void }\n | (() => VarValue);\n\n/**\n * A {@link VariableStorage} over named accessors into game state. `has` is true\n * for exactly the declared names; `get` invokes the getter live; `set` writes\n * through the setter, or throws if the cell is read-only (a getter with no\n * setter). This is the seam for a value whose arithmetic the *script* owns\n * (`set gold = gold - 50`).\n */\nexport function cells(defs: Readonly<Record<string, Cell>>): VariableStorage {\n // Own-property checks only (like i18n's `interpolate`): a bare `name in defs`\n // walks the prototype chain, so `has(\"toString\")` / `has(\"constructor\")` would\n // report true and read an inherited Object.prototype member as a cell.\n const read = (name: string): VarValue => {\n const cell = defs[name]!;\n return typeof cell === \"function\" ? cell() : cell.get();\n };\n return {\n get(name) {\n return Object.hasOwn(defs, name) ? read(name) : undefined;\n },\n set(name, value) {\n if (!Object.hasOwn(defs, name)) {\n throw new Error(`dialogue: cells() has no accessor for \"${name}\"`);\n }\n const cell = defs[name]!;\n if (typeof cell === \"function\" || cell.set === undefined) {\n throw new Error(\n `dialogue: \"${name}\" is read-only (a cells getter without a setter)`,\n );\n }\n cell.set(value);\n },\n has(name) {\n return Object.hasOwn(defs, name);\n },\n *entries() {\n for (const name of Object.keys(defs)) yield [name, read(name)] as const;\n },\n };\n}\n\n/**\n * Layer storages into one. `get`/`has` consult them in order (first that `has`\n * the name wins); `set` writes through the first that `has` it, else the **last**\n * storage — so a brand-new name (a dialogue-local or a seeded default) lands in\n * whatever writable store you put last. Typical: `compose(cells(...game), new\n * MemoryVariableStorage())`.\n */\nexport function compose(...storages: readonly VariableStorage[]): VariableStorage {\n if (storages.length === 0) {\n throw new Error(\"dialogue: compose() needs at least one storage\");\n }\n const last = storages[storages.length - 1]!;\n return {\n get(name) {\n for (const s of storages) if (s.has(name)) return s.get(name);\n return undefined;\n },\n set(name, value) {\n for (const s of storages) {\n if (s.has(name)) {\n s.set(name, value);\n return;\n }\n }\n last.set(name, value);\n },\n has(name) {\n return storages.some((s) => s.has(name));\n },\n *entries() {\n const seen = new Set<string>();\n for (const s of storages) {\n for (const [name, value] of s.entries()) {\n if (!seen.has(name)) {\n seen.add(name);\n yield [name, value] as const;\n }\n }\n }\n },\n };\n}\n","/**\n * Expression evaluator. `Condition`s and `set` values are expression\n * *trees* — `literal | varRef | call | unary | binary | group` — evaluated\n * against an {@link EvalScope} (variable reads + installed functions). The\n * operator set mirrors Yarn Spinner so a future Yarn parser maps onto this IR\n * 1:1; the atomic `{ var, op, value }` comparison evaluates as the degenerate\n * one-level tree (lowered into a `binary` node in {@link evalCondition}).\n */\n\nimport { materialize } from \"./vars.js\";\nimport type {\n Condition,\n DialogueFunction,\n Expr,\n VariableStorage,\n VarMap,\n VarValue,\n} from \"./types.js\";\n\n/** What an expression evaluates against: per-name reads + function calls, plus\n * a materialized snapshot for the `(vars) => boolean` predicate escape hatch. */\nexport interface EvalScope {\n /** Read a variable (absent → `null`). */\n get(name: string): VarValue;\n /** Invoke an installed function with already-evaluated args. */\n call(fn: string, args: readonly VarValue[]): VarValue;\n /** Materialize the readable variables (for a predicate condition). */\n vars(): VarMap;\n}\n\n/** Build an {@link EvalScope} over a storage + the installed functions. */\nexport function createScope(\n storage: VariableStorage,\n functions: Readonly<Record<string, DialogueFunction>>,\n): EvalScope {\n return {\n get: (name) => storage.get(name) ?? null,\n call: (fn, args) => {\n const f = functions[fn];\n // play-time validation guarantees a function exists; guard anyway so a\n // hand-built runner fails loudly instead of throwing an opaque TypeError.\n if (!f) throw new Error(`dialogue: no function \"${fn}\" is installed`);\n return f(...args);\n },\n vars: () => materialize(storage),\n };\n}\n\n/** A condition (or none) holds against `scope`: an absent condition always holds;\n * otherwise it's evaluated via {@link evalCondition}. The one no-condition gate\n * shared by the runner and the session's preview walk. */\nexport function holds(condition: Condition | undefined, scope: EvalScope): boolean {\n return condition === undefined ? true : evalCondition(condition, scope);\n}\n\n/** A condition holds when its value is truthy. */\nexport function evalCondition(condition: Condition, scope: EvalScope): boolean {\n if (typeof condition === \"function\") return condition(scope.vars());\n if (typeof condition === \"string\") return truthy(scope.get(condition));\n if (isExpr(condition)) return truthy(evaluate(condition, scope));\n // Atomic { var, op, value } — the degenerate one-level tree. `truthy`/`falsy`\n // are presence checks; every other op lowers into a `binary` node so the\n // comparison runs through the single operator implementation (applyBinary).\n const { var: name, op, value } = condition;\n if (op === \"truthy\") return truthy(scope.get(name));\n if (op === \"falsy\") return !truthy(scope.get(name));\n return truthy(\n evaluate(\n {\n kind: \"binary\",\n op,\n left: { kind: \"varRef\", name },\n right: { kind: \"literal\", value: value as VarValue },\n },\n scope,\n ),\n );\n}\n\n/** Evaluate an expression tree to a single value. */\nexport function evaluate(expr: Expr, scope: EvalScope): VarValue {\n switch (expr.kind) {\n case \"literal\":\n return expr.value;\n case \"varRef\":\n return scope.get(expr.name);\n case \"group\":\n return evaluate(expr.expr, scope);\n case \"call\":\n return scope.call(\n expr.fn,\n (expr.args ?? []).map((a) => evaluate(a, scope)),\n );\n case \"unary\":\n return applyUnary(expr.op, evaluate(expr.operand, scope));\n case \"binary\": {\n // Short-circuit `and`/`or` (Yarn-faithful) via JS &&/|| so a guarded right\n // operand isn't evaluated when the left already decides — e.g.\n // `has_item(\"key\") and count(\"key\") > 0` won't call `count` (which may\n // throw) when the item is absent. `xor` and the rest need both operands.\n const { op } = expr;\n if (op === \"and\" || op === \"&&\") {\n return truthy(evaluate(expr.left, scope)) && truthy(evaluate(expr.right, scope));\n }\n if (op === \"or\" || op === \"||\") {\n return truthy(evaluate(expr.left, scope)) || truthy(evaluate(expr.right, scope));\n }\n return applyBinary(op, evaluate(expr.left, scope), evaluate(expr.right, scope));\n }\n }\n}\n\n/** True for an {@link Expr} node (discriminated by `kind`), so `Condition` can\n * tell a tree apart from the atomic `{ var, op, value }` shape. */\nexport function isExpr(value: unknown): value is Expr {\n return typeof value === \"object\" && value !== null && \"kind\" in value;\n}\n\n/** JS truthiness: `null` / `false` / `0` / `\"\"` are false. */\nexport function truthy(value: VarValue): boolean {\n return Boolean(value);\n}\n\nfunction applyUnary(op: \"not\" | \"!\" | \"-\", v: VarValue): VarValue {\n switch (op) {\n case \"not\":\n case \"!\":\n return !truthy(v);\n case \"-\":\n return -num(v);\n }\n}\n\nfunction applyBinary(op: string, l: VarValue, r: VarValue): VarValue {\n switch (op) {\n case \"==\":\n case \"eq\":\n case \"is\":\n return l === r;\n case \"!=\":\n case \"neq\":\n return l !== r;\n case \">\":\n case \"gt\":\n return num(l) > num(r);\n case \"<\":\n case \"lt\":\n return num(l) < num(r);\n case \">=\":\n case \"gte\":\n return num(l) >= num(r);\n case \"<=\":\n case \"lte\":\n return num(l) <= num(r);\n // `and`/`or` (and `&&`/`||`) are short-circuited in evaluate() and never\n // reach here; `xor` needs both operands, so it stays.\n case \"xor\":\n case \"^\":\n return truthy(l) !== truthy(r);\n case \"+\":\n // String concat when either side is a string; numeric otherwise.\n return typeof l === \"string\" || typeof r === \"string\"\n ? `${str(l)}${str(r)}`\n : num(l) + num(r);\n case \"-\":\n return num(l) - num(r);\n case \"*\":\n return num(l) * num(r);\n case \"/\":\n return num(l) / num(r);\n case \"%\":\n return num(l) % num(r);\n default:\n throw new Error(`dialogue: unknown binary operator \"${op}\"`);\n }\n}\n\nfunction num(v: unknown): number {\n return typeof v === \"number\" ? v : Number(v);\n}\n\nfunction str(v: VarValue): string {\n return v === null ? \"\" : String(v);\n}\n","/**\n * i18n seam. The runtime never reaches for a translation library directly —\n * it asks an {@link I18nAdapter}. Ship the identity adapter (literal text +\n * `{param}` interpolation) by default; wrap i18next / FormatJS / your own\n * string table in a ~10-line adapter to localise without touching the engine.\n */\n\nexport interface I18nAdapter {\n /** Current locale tag, e.g. \"en\", \"fr-CA\". Informational. */\n readonly locale: string;\n /**\n * Resolve a string. `key` is the translation key when the script provides\n * one; `fallback` is the authored literal text. `params` feed interpolation.\n * Implementations should return localised markup-bearing text.\n */\n t(key: string | undefined, fallback: string, params?: Readonly<Record<string, unknown>>): string;\n}\n\n/**\n * No-op adapter: returns the authored literal, interpolating `{name}` tokens\n * from `params`. This is what runs until a real i18n backend is plugged in.\n */\nexport class IdentityI18n implements I18nAdapter {\n constructor(readonly locale: string = \"en\") {}\n\n t(_key: string | undefined, fallback: string, params?: Readonly<Record<string, unknown>>): string {\n return params ? interpolate(fallback, params) : fallback;\n }\n}\n\n/** Matches an interpolation token `{name}` (word chars only). */\nconst TOKEN = /\\{(\\w+)\\}/g;\n\n/** Replace `{token}` with `params.token`; leaves unknown tokens untouched.\n * Own-property check only — `{constructor}`/`{toString}` must not stringify\n * inherited Object.prototype members. */\nexport function interpolate(text: string, params: Readonly<Record<string, unknown>>): string {\n return text.replace(TOKEN, (whole, name: string) =>\n Object.hasOwn(params, name) ? String(params[name]) : whole,\n );\n}\n\n/** Distinct `{token}` names in `text` — used by load-time validation to check\n * every interpolation target resolves to a declared var/external. */\nexport function tokensIn(text: string): string[] {\n const names = new Set<string>();\n for (const m of text.matchAll(TOKEN)) names.add(m[1]!);\n return [...names];\n}\n","/**\n * Two-stage validation for the storage model.\n *\n * • **Load-time** ({@link analyzeScript}, environment-free): walk the script\n * once, collecting the names it **reads** (conditions, `{token}`s, `set`\n * values), the names it **writes** (`set` targets), the **functions** it\n * calls, and the **command types** it fires. Type-check what's statically\n * knowable — an atomic numeric comparison against a declared non-number, a\n * literal `set` value whose type conflicts with the target's declared\n * default. Undeclared *references* are NOT rejected here: the installed\n * storage / functions may provide them, which is only known at play-time.\n * • **Play-time** ({@link validatePlay}): given the installed storage,\n * functions, and commands, throw on a *significant* mismatch — a read name\n * nothing provides, a called function with no implementation, a `set` target\n * that's a function (read-only), a command type with no handler/fallback, a\n * declared default whose type conflicts with the value the storage already\n * holds.\n *\n * Both throw hard — a dangling reference or an environment that can't satisfy the\n * script is a programming error, not a recoverable runtime condition.\n */\n\nimport { isExpr } from \"./expr.js\";\nimport { tokensIn } from \"./i18n.js\";\nimport type {\n BinaryOp,\n ChoiceStep,\n Command,\n CommandStep,\n Condition,\n DialogueFunction,\n DialogueScript,\n Expr,\n SayStep,\n VariableStorage,\n VarValue,\n} from \"./types.js\";\n\n/** A script reference is broken (load-time). */\nexport class DialogueScriptError extends Error {}\n/** The installed storage/functions/commands don't satisfy the script (play-time). */\nexport class DialoguePlayError extends Error {}\n\n/** Inferred type of a declared default; `\"null\"` (a `null` default) is untyped —\n * type checks are skipped for it. */\ntype ValueType = \"string\" | \"number\" | \"boolean\" | \"null\";\n\nconst NUMERIC_OPS: ReadonlySet<string> = new Set([\">\", \">=\", \"<\", \"<=\"]);\n/** Binary ops (symbol + word forms) whose operands must be numbers. `+` is\n * handled separately — it also accepts strings (concatenation). */\nconst NUMERIC_EXPR_OPS: ReadonlySet<string> = new Set([\n \">\", \"<\", \">=\", \"<=\", \"gt\", \"lt\", \"gte\", \"lte\", \"-\", \"*\", \"/\", \"%\",\n]);\n/** Built-in command types the runner handles — exempt from the \"must have a\n * handler\" check. Only `set` (runner-owned flow op); every other command type,\n * including a face change, needs a handler. (A mid-line face change is the\n * `[expression=…/]` reveal marker, not a command.) */\nconst BUILTIN_COMMANDS: ReadonlySet<string> = new Set([\"set\"]);\n\n/** What a binary operator requires of a literal operand, for the load-time type\n * walk. `null` = no constraint (equality / logical ops accept any type). */\nfunction operandRequirement(op: BinaryOp): \"number\" | \"numberOrString\" | null {\n if (op === \"+\") return \"numberOrString\"; // string concat OR numeric add\n return NUMERIC_EXPR_OPS.has(op) ? \"number\" : null;\n}\n\nexport interface ScriptAnalysis {\n /** Declared default types, keyed by name (drives seed-if-absent + typing). */\n readonly declaredTypes: ReadonlyMap<string, ValueType>;\n /** Names the script reads (conditions, tokens, `set` values, call args). */\n readonly readVars: ReadonlySet<string>;\n /** Names the script writes via `set` (need not be pre-provided — locals). */\n readonly setTargets: ReadonlySet<string>;\n /** Functions the script calls (`{ kind: \"call\" }`). */\n readonly calledFunctions: ReadonlySet<string>;\n /** Non-built-in command `type`s the script fires (for handler coverage). */\n readonly commandTypes: ReadonlySet<string>;\n}\n\nconst analysisCache = new WeakMap<DialogueScript, ScriptAnalysis>();\n\n/** Walk + type-check a script once (memoized on the frozen script object). */\nexport function analyzeScript(script: DialogueScript): ScriptAnalysis {\n const cached = analysisCache.get(script);\n if (cached) return cached;\n const analysis = computeAnalysis(script);\n analysisCache.set(script, analysis);\n return analysis;\n}\n\nfunction computeAnalysis(script: DialogueScript): ScriptAnalysis {\n const declaredTypes = new Map<string, ValueType>();\n for (const [name, value] of Object.entries(script.declare ?? {})) {\n declaredTypes.set(name, valueType(value));\n }\n\n const readVars = new Set<string>();\n const setTargets = new Set<string>();\n const calledFunctions = new Set<string>();\n const commandTypes = new Set<string>();\n\n // `where` is threaded so a wrong-type operand reports the same context the\n // atomic `{ var, op, value }` check uses.\n const collectExpr = (expr: Expr, where: string): void => {\n switch (expr.kind) {\n case \"literal\":\n return;\n case \"varRef\":\n readVars.add(expr.name);\n return;\n case \"call\":\n calledFunctions.add(expr.fn);\n for (const arg of expr.args ?? []) collectExpr(arg, where);\n return;\n case \"group\":\n collectExpr(expr.expr, where);\n return;\n case \"unary\":\n collectExpr(expr.operand, where);\n return;\n case \"binary\":\n collectExpr(expr.left, where);\n collectExpr(expr.right, where);\n checkBinaryOperands(expr, where);\n return;\n }\n };\n\n // Minimal parity with the atomic `{ var, op, value }` check, but on the tree:\n // a numeric/arithmetic operator with a literal operand of the wrong type, or\n // against a declared non-number var, is a script bug. Nothing deeper — no\n // single-type inference; the parser stays purely syntactic.\n const checkBinaryOperands = (\n expr: Extract<Expr, { kind: \"binary\" }>,\n where: string,\n ): void => {\n const req = operandRequirement(expr.op);\n if (!req) return;\n const expected = req === \"numberOrString\" ? \"a number or string\" : \"a number\";\n for (const operand of [expr.left, expr.right]) {\n if (operand.kind === \"literal\") {\n const t = valueType(operand.value);\n if (t === \"number\" || (req === \"numberOrString\" && t === \"string\")) continue;\n throw new DialogueScriptError(\n `${where}: operator \"${expr.op}\" expects ${expected}, got ${t}`,\n );\n }\n if (operand.kind === \"varRef\" && req === \"number\") {\n const t = declaredTypes.get(operand.name);\n if (t !== undefined && t !== \"number\" && t !== \"null\") {\n throw new DialogueScriptError(\n `${where}: operator \"${expr.op}\" needs a number; \"${operand.name}\" is ${t}`,\n );\n }\n }\n }\n };\n\n const checkTokens = (text: string | undefined): void => {\n if (!text) return;\n for (const token of tokensIn(text)) readVars.add(token);\n };\n\n const checkCondition = (condition: Condition | undefined, where: string): void => {\n if (condition === undefined || typeof condition === \"function\") return;\n if (typeof condition === \"string\") {\n readVars.add(condition);\n return;\n }\n if (isExpr(condition)) {\n collectExpr(condition, where);\n return;\n }\n // Atomic { var, op, value } — collect the operand and type-check numeric ops.\n readVars.add(condition.var);\n if (NUMERIC_OPS.has(condition.op)) {\n const t = declaredTypes.get(condition.var);\n if (t !== undefined && t !== \"number\" && t !== \"null\") {\n throw new DialogueScriptError(\n `${where}: operator \"${condition.op}\" needs a number; \"${condition.var}\" is ${t}`,\n );\n }\n if (typeof condition.value !== \"number\") {\n throw new DialogueScriptError(\n `${where}: operator \"${condition.op}\" compares against a number, ` +\n `got ${typeof condition.value}`,\n );\n }\n }\n };\n\n // A literal `set` value must match its target's declared default type (e.g.\n // `set gold = \"lots\"` against a numeric `gold`). `null` clears; an undeclared\n // target is a local with no type to clash against.\n const checkSetLiteralType = (target: string, value: unknown, where: string): void => {\n const declared = declaredTypes.get(target);\n if (\n declared !== undefined &&\n declared !== \"null\" &&\n value !== null &&\n typeof value !== declared\n ) {\n throw new DialogueScriptError(\n `${where}: set \"${target}\" expects ${declared}, got ${typeof value}`,\n );\n }\n };\n\n const checkCommands = (commands: readonly Command[] | undefined, where: string): void => {\n for (const cmd of commands ?? []) {\n if (cmd.type === \"set\") {\n const target = cmd[\"var\"];\n if (typeof target !== \"string\") {\n throw new DialogueScriptError(`${where}: set command has no string \"var\"`);\n }\n setTargets.add(target);\n const value = cmd[\"value\"];\n // A `set` with no `value` is malformed — it would write `undefined`,\n // poisoning the name (`has` true, `get` undefined) and defeating\n // seed-if-absent on the next play. Die here. (`value: null` is allowed —\n // an intentional clear.)\n if (value === undefined) {\n throw new DialogueScriptError(`${where}: set \"${target}\" has no value`);\n }\n if (isExpr(value)) {\n collectExpr(value, where);\n // A bare literal RHS (incl. a quoted-string literal from the pre-walk,\n // e.g. `set gold = \"'lots'\"`) is type-checked against the target like a\n // raw literal would be.\n if (value.kind === \"literal\") checkSetLiteralType(target, value.value, where);\n } else {\n // Raw literal value (number/boolean/null — strings were pre-walked to\n // an Expr): type-check against the target's declared default.\n checkSetLiteralType(target, value, where);\n }\n continue;\n }\n if (!BUILTIN_COMMANDS.has(cmd.type)) commandTypes.add(cmd.type);\n }\n };\n\n for (const speaker of Object.values(script.speakers ?? {})) {\n checkTokens(speaker.name);\n }\n\n for (const node of Object.values(script.nodes)) {\n const where = `node \"${node.id}\"`;\n for (const step of node.steps) {\n switch (step.kind) {\n case \"say\": {\n const s = step as SayStep;\n checkTokens(s.text);\n checkCommands(s.commands, `${where} say`);\n break;\n }\n case \"choice\": {\n const c = step as ChoiceStep;\n checkTokens(c.text);\n for (const opt of c.options) {\n checkTokens(opt.text);\n checkTokens(opt.disabledReason);\n checkCondition(opt.condition, `${where} choice option \"${opt.text}\"`);\n checkCommands(opt.commands, `${where} choice option \"${opt.text}\"`);\n }\n break;\n }\n case \"command\": {\n const cs = step as CommandStep;\n checkCommands(cs.commands, `${where} command`);\n checkCondition(cs.condition, `${where} command`);\n break;\n }\n default:\n break; // goto / end carry no references\n }\n }\n }\n\n return { declaredTypes, readVars, setTargets, calledFunctions, commandTypes };\n}\n\n/** The environment a `play()` installs, as far as validation cares. */\nexport interface PlayEnv {\n readonly storage: VariableStorage;\n readonly functions: Readonly<Record<string, DialogueFunction>>;\n readonly commands: Readonly<Record<string, unknown>>;\n readonly fallbackCommand: unknown;\n}\n\n/**\n * Play-time: the installed environment must satisfy the analyzed script. Runs\n * **before** seed-if-absent, so the declared-default/storage conflict check sees\n * the host-provided value (not the seed we're about to write).\n */\nexport function validatePlay(analysis: ScriptAnalysis, env: PlayEnv): void {\n // 1. A declared default must not conflict with a value the storage already\n // holds (the game-linked value wins, but a type clash is a script bug).\n for (const [name, type] of analysis.declaredTypes) {\n if (type === \"null\" || !env.storage.has(name)) continue;\n const current = env.storage.get(name);\n if (current !== undefined && current !== null && typeof current !== type) {\n throw new DialoguePlayError(\n `declared default for \"${name}\" is ${type} but storage already holds ${typeof current}`,\n );\n }\n }\n\n // 2. Every name the script reads must be provided — declared (it'll be seeded),\n // already in storage, OR written by a `set` somewhere in the script (a local\n // the script manages itself). The walk is flow-insensitive, so it can't (and\n // needn't) reason about read-before-write order: a read of an as-yet-\n // unset local is a script logic bug, not a missing binding — at runtime it\n // reads null. A typo (a name that is read but never declared/stored/written)\n // still dies here.\n for (const name of analysis.readVars) {\n if (\n !analysis.declaredTypes.has(name) &&\n !env.storage.has(name) &&\n !analysis.setTargets.has(name)\n ) {\n throw new DialoguePlayError(\n `script reads \"${name}\" but nothing provides it ` +\n `(no declared default, no storage value, no \\`set\\`; for an argument read use a function call)`,\n );\n }\n }\n\n // 3. Every function the script calls must be installed.\n for (const fn of analysis.calledFunctions) {\n if (!Object.hasOwn(env.functions, fn)) {\n throw new DialoguePlayError(\n `script calls function \"${fn}\" but no such function is installed`,\n );\n }\n }\n\n // 4. A `set` target must not be a function name (functions are read-only).\n for (const target of analysis.setTargets) {\n if (Object.hasOwn(env.functions, target)) {\n throw new DialoguePlayError(\n `set target \"${target}\" is a function (read-only); functions cannot be assigned`,\n );\n }\n }\n\n // 5. Every non-built-in command type must resolve to a handler or the fallback.\n if (env.fallbackCommand === undefined) {\n const unhandled = [...analysis.commandTypes].filter((t) => !Object.hasOwn(env.commands, t));\n if (unhandled.length > 0) {\n throw new DialoguePlayError(\n `no handler for command type(s): ${unhandled.join(\", \")} ` +\n `(add to commands, or set fallbackCommand)`,\n );\n }\n }\n}\n\nfunction valueType(value: VarValue): ValueType {\n if (value === null) return \"null\";\n if (typeof value === \"string\") return \"string\";\n if (typeof value === \"number\") return \"number\";\n return \"boolean\";\n}\n","/**\n * String → expression front-end. `parseExpr(\"str >= 8 and has_item('key')\")`\n * produces the same {@link Expr} tree a hand-authored JSON condition / `set`\n * value would — `literal | varRef | call | unary | binary | group`, no new node\n * kinds — so the evaluator (`expr.ts`) and the load-time walk (`validate.ts`)\n * are reused unchanged. This is purely a parser: it does no type-checking and\n * no name resolution (that stays in `validate.ts`), which keeps it reusable 1:1\n * for a future Yarn front-end.\n *\n * The operator set mirrors Yarn Spinner. v1 wires what the authoring examples\n * exercise: `or`/`||`, `and`/`&&`, `not`/`!`, the comparisons (`== != > < >= <=`\n * plus the word forms `eq neq gt lt gte lte is`), unary `-`, binary `+ -`, calls\n * `f(a, b)`, and parentheses. `xor`/`^` and `* / %` are reserved but not yet\n * wired (the IR + evaluator already accept them, so adding them later is purely\n * additive). Word-form operators normalise to their symbol equivalents in the IR\n * (`and` → `&&`, `eq` → `==`, `gt` → `>`, …), so `a and b` and `a && b` parse to\n * the identical tree.\n *\n * An identifier is `[A-Za-z_$]` followed by `[A-Za-z0-9_.$]` repeats — `.` and\n * `$` are included (so `$gold` and `quest.stage` each read as ONE name,\n * Yarn-forward) but `-` is excluded, so `hp-1` is `hp` minus `1` and an item id\n * like `'rusty-key'` must live in a quoted string literal.\n */\n\nimport { DialogueScriptError } from \"./validate.js\";\nimport type { BinaryOp, Expr, VarValue } from \"./types.js\";\n\n/**\n * A string expression failed to parse. Carries the 1-based source position.\n * Extends {@link DialogueScriptError} so the loaders' contract holds: a malformed\n * string condition / `set` value surfaced by `loadScript` / `loadYaml` is caught\n * by a single `catch (e instanceof DialogueScriptError)`, while `instanceof\n * DialogueExprError` (and `line` / `col`) still distinguish a parse error.\n */\nexport class DialogueExprError extends DialogueScriptError {\n readonly line: number;\n readonly col: number;\n constructor(message: string, line: number, col: number) {\n super(`${message} (at ${line}:${col})`);\n this.name = \"DialogueExprError\";\n this.line = line;\n this.col = col;\n }\n}\n\n/**\n * Parse a string into an {@link Expr} tree. Throws {@link DialogueExprError}\n * (with line/col) on an empty/blank source, a leftover trailing token, or a\n * dangling operator.\n */\nexport function parseExpr(src: string): Expr {\n const tokens = tokenize(src);\n return new Parser(tokens).parse();\n}\n\n// ── Tokenizer ───────────────────────────────────────────────────────────────\n\ntype TokenKind =\n // literals + names\n | \"number\"\n | \"string\"\n | \"ident\"\n | \"true\"\n | \"false\"\n | \"null\"\n // operators (canonical symbol forms; word forms map onto these)\n | \"&&\"\n | \"||\"\n | \"!\"\n | \"==\"\n | \"!=\"\n | \">\"\n | \"<\"\n | \">=\"\n | \"<=\"\n | \"+\"\n | \"-\"\n // reserved but unwired in v1 (kept out of the parser → using it errors)\n | \"xor\"\n // punctuation\n | \"(\"\n | \")\"\n | \",\"\n | \"eof\";\n\ninterface Token {\n readonly kind: TokenKind;\n /** Set for `number` (numeric value), `string`/`ident` (text). */\n readonly value?: VarValue;\n readonly line: number;\n readonly col: number;\n}\n\n/** Word-form keywords → the token kind they lex to. A variable literally named\n * one of these can't be referenced *bare in a string* — use the `{ var, op,\n * value }` form, `defineScript`, or rename. */\nconst KEYWORDS: Readonly<Record<string, TokenKind>> = {\n and: \"&&\",\n or: \"||\",\n not: \"!\",\n xor: \"xor\",\n is: \"==\",\n eq: \"==\",\n neq: \"!=\",\n gt: \">\",\n lt: \"<\",\n gte: \">=\",\n lte: \"<=\",\n true: \"true\",\n false: \"false\",\n null: \"null\",\n};\n\nconst isDigit = (c: string): boolean => c >= \"0\" && c <= \"9\";\nconst isIdentStart = (c: string): boolean =>\n (c >= \"A\" && c <= \"Z\") || (c >= \"a\" && c <= \"z\") || c === \"_\" || c === \"$\";\nconst isIdentPart = (c: string): boolean =>\n isIdentStart(c) || isDigit(c) || c === \".\";\n\nfunction tokenize(src: string): Token[] {\n const tokens: Token[] = [];\n let i = 0;\n let line = 1;\n let col = 1;\n\n const advance = (n = 1): void => {\n for (let k = 0; k < n; k++) {\n if (src[i] === \"\\n\") {\n line++;\n col = 1;\n } else {\n col++;\n }\n i++;\n }\n };\n\n while (i < src.length) {\n const c = src[i]!;\n\n // Whitespace (including newlines, for multi-line YAML block scalars).\n if (c === \" \" || c === \"\\t\" || c === \"\\n\" || c === \"\\r\") {\n advance();\n continue;\n }\n\n const startLine = line;\n const startCol = col;\n\n // Number: digits with an optional fractional part. A leading `-` is unary\n // minus, not part of the literal.\n if (isDigit(c)) {\n let text = \"\";\n while (i < src.length && isDigit(src[i]!)) {\n text += src[i];\n advance();\n }\n if (src[i] === \".\" && isDigit(src[i + 1] ?? \"\")) {\n text += \".\";\n advance();\n while (i < src.length && isDigit(src[i]!)) {\n text += src[i];\n advance();\n }\n }\n tokens.push({ kind: \"number\", value: Number(text), line: startLine, col: startCol });\n continue;\n }\n\n // String: single- or double-quoted, with `\\` escapes.\n if (c === \"'\" || c === '\"') {\n const quote = c;\n advance(); // opening quote\n let text = \"\";\n while (i < src.length && src[i] !== quote) {\n if (src[i] === \"\\\\\") {\n advance();\n if (i >= src.length) break;\n text += unescape(src[i]!);\n } else {\n text += src[i];\n }\n advance();\n }\n if (src[i] !== quote) {\n throw new DialogueExprError(\"unterminated string literal\", startLine, startCol);\n }\n advance(); // closing quote\n tokens.push({ kind: \"string\", value: text, line: startLine, col: startCol });\n continue;\n }\n\n // Identifier / keyword.\n if (isIdentStart(c)) {\n let text = \"\";\n while (i < src.length && isIdentPart(src[i]!)) {\n text += src[i];\n advance();\n }\n const keyword = KEYWORDS[text];\n if (keyword === undefined) {\n tokens.push({ kind: \"ident\", value: text, line: startLine, col: startCol });\n } else if (keyword === \"true\" || keyword === \"false\" || keyword === \"null\") {\n // Literal keywords carry a value; the operator keywords are bare.\n const value: VarValue = keyword === \"true\" ? true : keyword === \"false\" ? false : null;\n tokens.push({ kind: keyword, value, line: startLine, col: startCol });\n } else {\n tokens.push({ kind: keyword, line: startLine, col: startCol });\n }\n continue;\n }\n\n // Two-char operators.\n const two = src.slice(i, i + 2);\n if (two === \"&&\" || two === \"||\" || two === \"==\" || two === \"!=\" || two === \">=\" || two === \"<=\") {\n tokens.push({ kind: two, line: startLine, col: startCol });\n advance(2);\n continue;\n }\n\n // One-char operators / punctuation.\n if (c === \">\" || c === \"<\" || c === \"!\" || c === \"+\" || c === \"-\" || c === \"(\" || c === \")\" || c === \",\") {\n tokens.push({ kind: c, line: startLine, col: startCol });\n advance();\n continue;\n }\n\n throw new DialogueExprError(`unexpected character \"${c}\"`, startLine, startCol);\n }\n\n tokens.push({ kind: \"eof\", line, col });\n return tokens;\n}\n\nfunction unescape(c: string): string {\n switch (c) {\n case \"n\":\n return \"\\n\";\n case \"t\":\n return \"\\t\";\n case \"r\":\n return \"\\r\";\n default:\n return c; // \\\\ \\' \\\" and any other escaped char → the char itself\n }\n}\n\n// ── Parser (precedence climbing) ─────────────────────────────────────────────\n\n/** Left binding power per infix operator. Higher binds tighter. */\nconst INFIX_BP: Partial<Record<TokenKind, number>> = {\n \"||\": 1,\n \"&&\": 2,\n \"==\": 3,\n \"!=\": 3,\n \">\": 3,\n \"<\": 3,\n \">=\": 3,\n \"<=\": 3,\n \"+\": 4,\n \"-\": 4,\n};\n\nclass Parser {\n private pos = 0;\n constructor(private readonly tokens: Token[]) {}\n\n parse(): Expr {\n const expr = this.parseBinary(0);\n const t = this.peek();\n if (t.kind !== \"eof\") {\n throw new DialogueExprError(`unexpected trailing token \"${describe(t)}\"`, t.line, t.col);\n }\n return expr;\n }\n\n private parseBinary(minBp: number): Expr {\n let left = this.parseUnary();\n for (;;) {\n const t = this.peek();\n const bp = INFIX_BP[t.kind];\n if (bp === undefined || bp < minBp) break;\n this.next();\n const right = this.parseBinary(bp + 1); // left-associative\n // The INFIX_BP guard above admits only symbol kinds that are valid BinaryOps.\n left = { kind: \"binary\", op: t.kind as BinaryOp, left, right };\n }\n return left;\n }\n\n private parseUnary(): Expr {\n const t = this.peek();\n if (t.kind === \"!\" || t.kind === \"-\") {\n this.next();\n const operand = this.parseUnary();\n return { kind: \"unary\", op: t.kind === \"!\" ? \"!\" : \"-\", operand };\n }\n return this.parsePrimary();\n }\n\n private parsePrimary(): Expr {\n const t = this.peek();\n switch (t.kind) {\n case \"number\":\n case \"string\":\n this.next();\n return { kind: \"literal\", value: t.value as VarValue };\n case \"true\":\n case \"false\":\n case \"null\":\n this.next();\n return { kind: \"literal\", value: t.value as VarValue };\n case \"ident\": {\n this.next();\n const name = t.value as string;\n if (this.peek().kind === \"(\") {\n this.next(); // consume \"(\"\n const args = this.parseArgs();\n return { kind: \"call\", fn: name, args };\n }\n return { kind: \"varRef\", name };\n }\n case \"(\": {\n this.next();\n const inner = this.parseBinary(0);\n this.expect(\")\");\n return { kind: \"group\", expr: inner };\n }\n default:\n throw new DialogueExprError(`unexpected ${describe(t)}`, t.line, t.col);\n }\n }\n\n /** Parse a comma-separated argument list; the opening `(` is already consumed. */\n private parseArgs(): Expr[] {\n const args: Expr[] = [];\n if (this.peek().kind === \")\") {\n this.next();\n return args;\n }\n for (;;) {\n args.push(this.parseBinary(0));\n const t = this.peek();\n if (t.kind === \",\") {\n this.next();\n continue;\n }\n if (t.kind === \")\") {\n this.next();\n return args;\n }\n throw new DialogueExprError(`expected \",\" or \")\" in argument list, got ${describe(t)}`, t.line, t.col);\n }\n }\n\n private expect(kind: TokenKind): void {\n const t = this.peek();\n if (t.kind !== kind) {\n throw new DialogueExprError(`expected \"${kind}\", got ${describe(t)}`, t.line, t.col);\n }\n this.next();\n }\n\n private peek(): Token {\n return this.tokens[this.pos]!;\n }\n\n private next(): Token {\n return this.tokens[this.pos++]!;\n }\n}\n\n/** A human-readable token label for error messages. */\nfunction describe(t: Token): string {\n if (t.kind === \"eof\") return \"end of input\";\n if (t.kind === \"ident\") return `\"${String(t.value)}\"`;\n if (t.kind === \"string\") return `string \"${String(t.value)}\"`;\n if (t.kind === \"number\") return `number ${String(t.value)}`;\n return `\"${t.kind}\"`;\n}\n","/**\n * Canonical loader: validates + normalises a hand-authored / JSON\n * {@link DialogueScript} into a frozen, structurally-checked script the runner\n * can trust. Other \"common formats\" (a Yarn/ink-style screenplay parser) are\n * additional modules in this folder that emit the same canonical shape — the\n * runner only ever sees the canonical model.\n */\n\nimport { analyzeScript, DialogueScriptError } from \"../validate.js\";\nimport { parseExpr } from \"../expr-parse.js\";\nimport type {\n ChoiceOption,\n ChoiceStep,\n Command,\n CommandStep,\n Condition,\n DialogueScript,\n DialogueNode,\n Expr,\n NodeId,\n SayStep,\n Step,\n} from \"../types.js\";\n\nexport { DialogueScriptError };\n\nexport function loadScript(raw: DialogueScript): DialogueScript {\n if (!raw || typeof raw !== \"object\") {\n throw new DialogueScriptError(\"script must be an object\");\n }\n if (!raw.id) throw new DialogueScriptError(\"script.id is required\");\n if (!raw.nodes || typeof raw.nodes !== \"object\") {\n throw new DialogueScriptError(`script \"${raw.id}\" has no nodes`);\n }\n\n const nodeIds = Object.keys(raw.nodes);\n if (nodeIds.length === 0) {\n throw new DialogueScriptError(`script \"${raw.id}\" has no nodes`);\n }\n const start = raw.start ?? nodeIds[0]!;\n if (!raw.nodes[start]) {\n throw new DialogueScriptError(`start node \"${start}\" not found in \"${raw.id}\"`);\n }\n\n // Speakers are resolved by record KEY at runtime but presenters look actors\n // up by `SpeakerDef.id` — a mismatch silently splits nameplate vs actor\n // anchoring, so the two must agree (same rule as node key ↔ node.id).\n for (const [key, speaker] of Object.entries(raw.speakers ?? {})) {\n if (speaker.id !== key) {\n throw new DialogueScriptError(\n `speaker key \"${key}\" != speaker.id \"${speaker.id}\"`,\n );\n }\n }\n\n // Each node's id must match its key; every jump target must resolve.\n for (const [id, node] of Object.entries(raw.nodes)) {\n validateNode(raw, id, node);\n }\n\n // One pre-walk resolving every string condition and string `set` value to an\n // expression tree, so the frozen IR carries only `Expr`s and the runtime never\n // re-parses. A bare name (`\"gate\"`) becomes a `varRef` (a truthy read of that\n // variable); operator strings (`\"hp > 0 and not rude\"`) parse to comparison /\n // logical trees. Runs on every loader (JSON included). A malformed string\n // throws `DialogueExprError` with its position.\n const resolved = resolveExpressions(raw);\n\n const script = Object.freeze({ ...resolved, start });\n // Binding-free load-time walk: condition vars, set targets, and {token}s must\n // resolve to declared vars/externals; type-incompatible ops error. Memoized on\n // the frozen script, so the session's play-time re-check is free.\n analyzeScript(script);\n return script;\n}\n\nfunction validateNode(script: DialogueScript, id: string, node: DialogueNode): void {\n if (node.id !== id) {\n throw new DialogueScriptError(`node key \"${id}\" != node.id \"${node.id}\"`);\n }\n if (!Array.isArray(node.steps) || node.steps.length === 0) {\n throw new DialogueScriptError(`node \"${id}\" has no steps`);\n }\n for (const step of node.steps) validateStep(script, id, step);\n}\n\nfunction validateStep(script: DialogueScript, nodeId: string, step: Step): void {\n const targetExists = (t: string | undefined): void => {\n if (t !== undefined && !script.nodes[t]) {\n throw new DialogueScriptError(\n `node \"${nodeId}\": jump target \"${t}\" does not exist`,\n );\n }\n };\n // Node typos throw, so speaker typos must too — an unknown speaker would\n // otherwise silently render as a narrator line (and never find its actor).\n const speakerExists = (s: string | undefined): void => {\n if (s !== undefined && !script.speakers?.[s]) {\n throw new DialogueScriptError(\n `node \"${nodeId}\": speaker \"${s}\" is not in script.speakers`,\n );\n }\n };\n switch (step.kind) {\n case \"say\":\n if (typeof step.text !== \"string\") {\n throw new DialogueScriptError(`node \"${nodeId}\": say.text must be a string`);\n }\n speakerExists(step.speaker);\n break;\n case \"choice\":\n if (!Array.isArray(step.options) || step.options.length === 0) {\n throw new DialogueScriptError(`node \"${nodeId}\": choice has no options`);\n }\n speakerExists(step.speaker);\n for (const opt of step.options) targetExists(opt.target);\n break;\n case \"command\":\n targetExists(step.target);\n break;\n case \"goto\":\n // GotoStep types `target` as required, but hand-authored JSON can omit it\n // — and an undefined target silently ends the conversation at jump time.\n if (step.target === undefined) {\n throw new DialogueScriptError(`node \"${nodeId}\": goto has no target`);\n }\n targetExists(step.target);\n break;\n case \"end\":\n break;\n default:\n throw new DialogueScriptError(\n `node \"${nodeId}\": unknown step kind \"${(step as { kind: string }).kind}\"`,\n );\n }\n}\n\n// ── String → Expr pre-walk (unify) ──────────────────────────────────────────\n// Each rewrite helper returns a *new* value only when a string was parsed, else\n// `undefined` (so the caller keeps the original reference and skips cloning the\n// branch). String conditions/`set`-values are the only nodes rewritten; atomic\n// `{ var, op, value }`, predicate functions, and already-built `Expr`s pass\n// through untouched.\n\nfunction resolveExpressions(script: DialogueScript): DialogueScript {\n let nodesChanged = false;\n const nodes: Record<NodeId, DialogueNode> = {};\n for (const [id, node] of Object.entries(script.nodes)) {\n let stepsChanged = false;\n const steps = node.steps.map((step) => {\n const next = resolveStep(step);\n if (next !== step) stepsChanged = true;\n return next;\n });\n if (stepsChanged) {\n nodes[id] = { ...node, steps };\n nodesChanged = true;\n } else {\n nodes[id] = node;\n }\n }\n return nodesChanged ? { ...script, nodes } : script;\n}\n\nfunction resolveStep(step: Step): Step {\n switch (step.kind) {\n case \"say\":\n return resolveSay(step);\n case \"choice\":\n return resolveChoice(step);\n case \"command\":\n return resolveCommandStep(step);\n case \"goto\":\n case \"end\":\n return step;\n }\n}\n\nfunction resolveSay(step: SayStep): SayStep {\n const commands = rewriteCommands(step.commands);\n return commands ? { ...step, commands } : step;\n}\n\nfunction resolveCommandStep(step: CommandStep): CommandStep {\n const condition = rewriteCondition(step.condition);\n const commands = rewriteCommands(step.commands);\n if (!condition && !commands) return step;\n return {\n ...step,\n ...(condition ? { condition } : {}),\n ...(commands ? { commands } : {}),\n };\n}\n\nfunction resolveChoice(step: ChoiceStep): ChoiceStep {\n let changed = false;\n const options = step.options.map((opt) => {\n const next = rewriteOption(opt);\n if (next) {\n changed = true;\n return next;\n }\n return opt;\n });\n return changed ? { ...step, options } : step;\n}\n\nfunction rewriteOption(opt: ChoiceOption): ChoiceOption | undefined {\n const condition = rewriteCondition(opt.condition);\n const commands = rewriteCommands(opt.commands);\n if (!condition && !commands) return undefined;\n return {\n ...opt,\n ...(condition ? { condition } : {}),\n ...(commands ? { commands } : {}),\n };\n}\n\n/** A string condition → its `Expr` tree; anything else → `undefined` (no rewrite). */\nfunction rewriteCondition(condition: Condition | undefined): Expr | undefined {\n return typeof condition === \"string\" ? parseExpr(condition) : undefined;\n}\n\n/** Rewrite each `set` command whose value is a string into its `Expr` tree.\n * Returns a new array only when at least one command changed. */\nfunction rewriteCommands(\n commands: readonly Command[] | undefined,\n): readonly Command[] | undefined {\n if (!commands) return undefined;\n let changed = false;\n const out = commands.map((cmd) => {\n if (cmd.type === \"set\" && typeof cmd.value === \"string\") {\n changed = true;\n return { ...cmd, value: parseExpr(cmd.value) };\n }\n return cmd;\n });\n return changed ? out : undefined;\n}\n","/**\n * Compact authoring front-end — a small, line-oriented DSL for RPG-style\n * dialogue that compiles to the same {@link DialogueScript} IR as JSON / YAML.\n * `parseCompact(text)` produces the IR; `loadCompact(text)` runs it through\n * {@link loadScript}, so validation and the frozen model are identical to every\n * other loader. Pixi-free: it imports only the headless core (`parseExpr`,\n * `loadScript`, the markup tag guard).\n *\n * One statement per line. Leading whitespace is insignificant (indent nodes for\n * readability). Blank lines and `// comment` lines are ignored. Each non-blank\n * line is one of:\n *\n * # id script id (required, once) — the start node is the\n * first `::` node defined.\n * @ id Name [#hex] a speaker: an opaque id, a display name (may have\n * spaces), an optional nameplate colour (`#ffcc00` /\n * `#fc0`). `@` lines may appear before or after their use.\n * :: nodeId opens a node; following step lines belong to it.\n * speaker[ face]: text a spoken line — ONLY when the first token is a declared\n * `@`-speaker. `face` (a 2nd header token) becomes the\n * line's avatar `expression`. Otherwise the WHOLE line,\n * colons and all, is a narrator line.\n * text a narrator line (no declared speaker prefix).\n * ? text … a choice option; consecutive `?` lines coalesce into\n * one choice step (see below).\n * -> nodeId [if: cond] a jump — unconditional, or conditional (taken only if\n * `cond` holds, else fall through to the next step).\n * declare v = value a script-level variable default (a literal value).\n * set v = rhs write a variable. A bare number / `true` / `false` /\n * `null` stays a literal; anything else is parsed as an\n * expression (`set hp = hp - 1`), so the host reads it\n * back through the same evaluator JSON uses.\n * do type k=v … #flag a host command: `type` then `key=value` data and\n * `#flag` booleans (`do give-item id=key count=1 #blocking`).\n * end ends the conversation.\n *\n * **Per-line hints** ride the end of a `say` line: `view=` / `voice=` / `speed=`\n * / `auto=` set the first-class {@link SayStep} fields, and trailing `#key:value`\n * / bare `#flag` hashtags become {@link SayStep.meta} (Yarn-aligned — metadata is\n * trailing). A `say` line's text is otherwise passed to the markup parser\n * **verbatim**, so inline `[..]` markup (and any markup tokens a later release\n * adds) survives untouched.\n *\n * **Choices** carry their attributes as non-bracket sigils, in this order after\n * the text: `if: cond`, then `-> target` (or `target=node`), then `#once` /\n * `#disabled` / `#key:value` hashtags. They are lexed off and stripped before\n * the remaining choice text reaches markup — `[..]` is reserved for inline\n * markup there, so a bracketed token that markup doesn't recognize is reported\n * as an error (it is almost always a mistyped attribute that would otherwise be\n * dropped silently).\n *\n * Conditions and non-literal `set` values are parsed with the shared\n * {@link parseExpr}, so a malformed expression throws {@link DialogueExprError}\n * (a {@link DialogueScriptError} subtype) with its position.\n */\n\nimport { parseExpr } from \"../expr-parse.js\";\nimport { firstUnknownTag } from \"../markup.js\";\nimport { loadScript, DialogueScriptError } from \"./canonical.js\";\nimport type {\n ChoiceOption,\n Command,\n DialogueNode,\n DialogueScript,\n Expr,\n SayStep,\n SpeakerDef,\n Step,\n VarValue,\n} from \"../types.js\";\n\n/**\n * Parse compact-DSL source into a (mutable) {@link DialogueScript}. Throws\n * {@link DialogueScriptError} on a structural problem (with the 1-based line) and\n * {@link DialogueExprError} on a malformed condition / `set` expression.\n */\nexport function parseCompact(text: string): DialogueScript {\n const lines = text.split(/\\r\\n|\\r|\\n/);\n\n // Pass 1: collect every speaker so a `@` declaration may follow its first use\n // (line order is irrelevant for speaker resolution).\n const speakers: Record<string, SpeakerDef> = {};\n lines.forEach((raw, i) => {\n const line = raw.trim();\n if (!line.startsWith(\"@\")) return;\n const def = parseSpeaker(line, i + 1);\n if (Object.hasOwn(speakers, def.id)) {\n fail(i + 1, `duplicate speaker \"${def.id}\"`);\n }\n speakers[def.id] = def;\n });\n\n // Pass 2: the script id, nodes, and steps. The current choice run is buffered\n // and flushed into one choice step the moment a non-`?` line ends it.\n let id: string | undefined;\n const nodes: Record<string, DialogueNode> = {};\n const nodeOrder: string[] = [];\n let current: { id: string; steps: Step[] } | null = null;\n let choiceRun: ChoiceOption[] | null = null;\n const declares: Record<string, VarValue> = {};\n\n const flushChoice = (): void => {\n if (choiceRun && current) current.steps.push({ kind: \"choice\", options: choiceRun });\n choiceRun = null;\n };\n\n lines.forEach((raw, i) => {\n const lineNo = i + 1;\n const line = raw.trim();\n\n if (line === \"\" || line.startsWith(\"//\")) return;\n if (line.startsWith(\"@\")) return; // handled in pass 1\n\n if (line.startsWith(\"#\")) {\n flushChoice();\n const newId = line.slice(1).trim();\n if (!newId) fail(lineNo, \"'#' script id directive needs an id\");\n if (id !== undefined) fail(lineNo, `duplicate '#' script id directive (already \"${id}\")`);\n id = newId;\n return;\n }\n\n if (line.startsWith(\"::\")) {\n flushChoice();\n const nodeId = line.slice(2).trim();\n if (!nodeId) fail(lineNo, \"':: ' node directive needs an id\");\n if (Object.hasOwn(nodes, nodeId)) fail(lineNo, `duplicate node \"${nodeId}\"`);\n current = { id: nodeId, steps: [] };\n nodes[nodeId] = current;\n nodeOrder.push(nodeId);\n return;\n }\n\n if (line.startsWith(\"?\")) {\n if (!current) fail(lineNo, \"choice '?' appears before any ':: <node>'\");\n (choiceRun ??= []).push(parseChoice(line.slice(1).trim(), lineNo));\n return;\n }\n\n // Any other line ends a choice run.\n flushChoice();\n\n // `declare` is a script-level default — allowed anywhere, needs no node.\n const decl = parseDeclare(line);\n if (decl) {\n declares[decl.name] = decl.value;\n return;\n }\n\n // The remaining leaders are node steps.\n const node = current;\n if (!node) fail(lineNo, `dialogue line appears before any ':: <node>' (\"${line}\")`);\n\n if (line.startsWith(\"->\")) {\n node.steps.push(parseGoto(line, lineNo));\n return;\n }\n const set = parseSet(line);\n if (set) {\n node.steps.push(set);\n return;\n }\n const cmd = parseDo(line, lineNo);\n if (cmd) {\n node.steps.push(cmd);\n return;\n }\n if (line === \"end\") {\n node.steps.push({ kind: \"end\" });\n return;\n }\n node.steps.push(parseSay(line, lineNo, speakers));\n });\n\n flushChoice();\n\n if (id === undefined) throw new DialogueScriptError(\"compact: missing '# <id>' script directive\");\n if (nodeOrder.length === 0) {\n throw new DialogueScriptError(`compact: script \"${id}\" has no ':: <node>' nodes`);\n }\n\n return {\n id,\n start: nodeOrder[0]!,\n nodes,\n ...(Object.keys(speakers).length > 0 ? { speakers } : {}),\n ...(Object.keys(declares).length > 0 ? { declare: declares } : {}),\n };\n}\n\n/** Parse compact-DSL source and run it through {@link loadScript} — same\n * validated, frozen IR as the JSON and YAML loaders. */\nexport function loadCompact(text: string): DialogueScript {\n return loadScript(parseCompact(text));\n}\n\n// ── Speakers (`@ id Name [#hex]`) ────────────────────────────────────────────\n\nfunction parseSpeaker(line: string, lineNo: number): SpeakerDef {\n const tokens = line.slice(1).trim().split(/\\s+/).filter(Boolean);\n const id = tokens[0];\n if (!id) fail(lineNo, \"'@' speaker directive needs an id\");\n let nameTokens = tokens.slice(1);\n let color: number | undefined;\n const last = nameTokens[nameTokens.length - 1];\n if (last !== undefined && /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(last)) {\n color = hexColor(last);\n nameTokens = nameTokens.slice(0, -1);\n }\n return {\n id,\n name: nameTokens.length > 0 ? nameTokens.join(\" \") : id,\n ...(color !== undefined ? { color } : {}),\n };\n}\n\n/** `#rrggbb` / `#rgb` → a 0xRRGGBB number. */\nfunction hexColor(token: string): number {\n let hex = token.slice(1);\n if (hex.length === 3) hex = hex.replace(/./g, \"$&$&\");\n return parseInt(hex, 16);\n}\n\n// ── Goto (`-> nodeId [if: cond]`) ────────────────────────────────────────────\n\nfunction parseGoto(line: string, lineNo: number): Step {\n const m = /^->\\s*(\\S+)(?:\\s+if:\\s*(.+))?\\s*$/.exec(line);\n if (!m) fail(lineNo, \"'->' goto needs a target node id (optionally `-> node if: cond`)\");\n const target = m[1]!;\n // `-> node if: cond` is a conditional jump (a CommandStep with no commands):\n // take the jump only if the condition holds, else fall through to the next\n // step. Bare `-> node` is an unconditional GotoStep.\n if (m[2] !== undefined) {\n return { kind: \"command\", commands: [], condition: parseExpr(m[2].trim()), target };\n }\n return { kind: \"goto\", target };\n}\n\n// ── Declare (`declare v = value`) ────────────────────────────────────────────\n\n/** A script-level variable default, or `null` when the line is not a `declare`\n * (so `declare your intentions`, with no `=`, stays narrator text). The value\n * is a literal scalar — declare defaults are plain values, not expressions. */\nfunction parseDeclare(line: string): { name: string; value: VarValue } | null {\n const m = /^declare\\s+([A-Za-z_$][A-Za-z0-9_.$]*)\\s*=\\s*(\\S.*)$/.exec(line);\n if (!m) return null;\n return { name: m[1]!, value: scalar(m[2]!.trim()) };\n}\n\n// ── `set v = rhs` ────────────────────────────────────────────────────────────\n\n/** A `set` line, or `null` when the line is not a `set` (so it falls through to\n * the say-line reading — `set the table` with no `=` is narrator text). */\nfunction parseSet(line: string): Step | null {\n const m = /^set\\s+([A-Za-z_$][A-Za-z0-9_.$]*)\\s*=\\s*(\\S.*)$/.exec(line);\n if (!m) return null;\n return {\n kind: \"command\",\n commands: [{ type: \"set\", var: m[1]!, value: setValue(m[2]!.trim()) }],\n };\n}\n\n/** A bare number / `true` / `false` / `null` stays a literal; everything else\n * (identifiers, quoted strings, arithmetic) is an expression. A string literal\n * MUST go through `parseExpr` (→ an `Expr` literal), never be emitted as a raw\n * string: `loadScript`'s pre-walk re-parses any string `set` value, so a raw\n * `\"hi\"` would be misread as a variable reference. */\nfunction setValue(rhs: string): VarValue | Expr {\n const literal = numberBoolNull(rhs);\n // A non-literal RHS is an expression. `parseExpr` throws `DialogueExprError`\n // (a `DialogueScriptError`, carrying a source position) on a malformed value.\n return literal === NOT_LITERAL ? parseExpr(rhs) : literal;\n}\n\n// ── `do type k=v … #flag` ────────────────────────────────────────────────────\n\n/** A `do` command line, or `null` when the line does not fully match the\n * command shape (`do <type> (<k=v> | <#flag>)*`). A non-matching `do …` line\n * falls through to the say reading, so prose like `do you agree?` stays text. */\nfunction parseDo(line: string, lineNo: number): Step | null {\n if (!/^do(\\s|$)/.test(line)) return null;\n const tokens = splitArgs(line.slice(2).trim());\n const type = tokens[0];\n if (type === undefined || !/^[A-Za-z_][A-Za-z0-9_-]*$/.test(type)) return null;\n const command: Record<string, unknown> = { type };\n // `type` is the command's dispatch key (the leading token); a `type=` data key\n // OR a `#type` flag would overwrite it. Flag either form, but only fail once the\n // whole line is confirmed a valid command shape — a later bare token still\n // falls through to narrator, so `do spawn #type extra` stays text, not an error.\n let typeKeyCollision = false;\n for (const tok of tokens.slice(1)) {\n if (tok.startsWith(\"#\")) {\n const flag = tok.slice(1);\n if (!flag) return null;\n if (flag === \"type\") typeKeyCollision = true;\n else command[flag] = true;\n continue;\n }\n const eq = tok.indexOf(\"=\");\n if (eq <= 0) return null; // not `key=value` → not a command shape (falls through to narrator)\n const key = tok.slice(0, eq);\n if (key === \"type\") typeKeyCollision = true;\n else command[key] = scalar(tok.slice(eq + 1));\n }\n if (typeKeyCollision) {\n fail(lineNo, `'do' data key \"type\" collides with the command type (the leading token); rename it`);\n }\n return { kind: \"command\", commands: [command as Command] };\n}\n\n// ── Say / narrator lines ─────────────────────────────────────────────────────\n\nfunction parseSay(line: string, lineNo: number, speakers: Record<string, SpeakerDef>): SayStep {\n let speaker: string | undefined;\n let expression: string | undefined;\n let body = line;\n\n const colon = line.indexOf(\":\");\n if (colon !== -1) {\n const header = line.slice(0, colon).trim();\n const tokens = header.length > 0 ? header.split(/\\s+/) : [];\n const first = tokens[0];\n // A header is a speaker prefix ONLY when its first token is a declared\n // speaker; otherwise the colon belongs to narrator prose (\"Warning: …\").\n if (first !== undefined && Object.hasOwn(speakers, first)) {\n if (tokens.length > 2) {\n fail(lineNo, `speaker header \"${header}\" has too many tokens (use \"speaker [face]: text\")`);\n }\n speaker = first;\n expression = tokens[1];\n body = line.slice(colon + 1).trimStart();\n }\n }\n\n const { text, fields, meta } = peelSayHints(body, lineNo);\n return {\n kind: \"say\",\n ...(speaker !== undefined ? { speaker } : {}),\n ...(expression !== undefined ? { expression } : {}),\n text,\n ...fields,\n ...(meta ? { meta } : {}),\n };\n}\n\ninterface SayFields {\n view?: string;\n voice?: string;\n speed?: number;\n autoAdvanceMs?: number;\n /** i18n key from a `#line:id` hashtag (Yarn's localization tag). */\n key?: string;\n}\n\n/** Strip the trailing run of `#hashtag` / `key=value` hints off a say body; the\n * remainder is the verbatim line text. */\nfunction peelSayHints(\n body: string,\n lineNo: number,\n): { text: string; fields: SayFields; meta: Record<string, unknown> | undefined } {\n let rest = body;\n const fields: SayFields = {};\n const meta: Record<string, unknown> = {};\n let metaCount = 0;\n\n for (;;) {\n const hash = /(^|\\s)#(\\S+)\\s*$/.exec(rest);\n if (hash) {\n const tag = hash[2]!;\n const lk = lineKey(tag);\n if (lk !== undefined) fields.key = lk; // #line:id → SayStep.key (i18n)\n else metaCount += applyHashtag(meta, tag);\n rest = rest.slice(0, hash.index).replace(/\\s+$/, \"\");\n continue;\n }\n const hint = /(^|\\s)(view|voice|speed|auto)=(\\S+)\\s*$/.exec(rest);\n if (hint) {\n applySayField(fields, hint[2]!, hint[3]!, lineNo);\n rest = rest.slice(0, hint.index).replace(/\\s+$/, \"\");\n continue;\n }\n break;\n }\n\n return { text: rest, fields, meta: metaCount > 0 ? meta : undefined };\n}\n\nfunction applySayField(fields: SayFields, key: string, value: string, lineNo: number): void {\n switch (key) {\n case \"view\":\n fields.view = unquote(value);\n return;\n case \"voice\":\n fields.voice = unquote(value);\n return;\n case \"speed\":\n fields.speed = numberHint(value, lineNo, \"speed\");\n return;\n case \"auto\":\n fields.autoAdvanceMs = numberHint(value, lineNo, \"auto\");\n return;\n }\n}\n\nfunction numberHint(value: string, lineNo: number, key: string): number {\n const n = Number(value);\n if (!Number.isFinite(n)) fail(lineNo, `'${key}=' expects a number, got \"${value}\"`);\n return n;\n}\n\n// ── Choices (`? text [if: cond] [-> target] [#flags]`) ───────────────────────\n\nfunction parseChoice(body: string, lineNo: number): ChoiceOption {\n let rest = body;\n const meta: Record<string, unknown> = {};\n let metaCount = 0;\n let once = false;\n let disabled = false;\n let target: string | undefined;\n let condition: Expr | undefined;\n let key: string | undefined;\n\n // Peel attributes from the right, in reverse of the authored order: trailing\n // hashtags, then the target, then `if:` (which holds the rest of the line).\n for (;;) {\n const hash = /(^|\\s)#(\\S+)\\s*$/.exec(rest);\n if (!hash) break;\n const tag = hash[2]!;\n const lk = lineKey(tag);\n if (tag === \"once\") once = true;\n else if (tag === \"disabled\") disabled = true;\n else if (lk !== undefined) key = lk; // #line:id → ChoiceOption.key (i18n)\n else metaCount += applyHashtag(meta, tag);\n rest = rest.slice(0, hash.index).replace(/\\s+$/, \"\");\n }\n\n const arrow = /(^|\\s)->\\s*(\\S+)\\s*$/.exec(rest);\n const named = arrow ? null : /(^|\\s)target=(\\S+)\\s*$/.exec(rest);\n if (arrow) {\n target = arrow[2];\n rest = rest.slice(0, arrow.index).replace(/\\s+$/, \"\");\n } else if (named) {\n target = named[2];\n rest = rest.slice(0, named.index).replace(/\\s+$/, \"\");\n }\n\n const ifm = /(^|\\s)if:\\s*(.+)$/.exec(rest);\n if (ifm) {\n const condStr = ifm[2]!.trim();\n if (!condStr) fail(lineNo, \"choice 'if:' has no condition\");\n condition = parseExpr(condStr);\n rest = rest.slice(0, ifm.index).replace(/\\s+$/, \"\");\n }\n\n const text = rest.trim();\n if (!text) fail(lineNo, \"choice has no text\");\n const unknown = firstUnknownTag(text);\n if (unknown !== null) {\n fail(\n lineNo,\n `unrecognized markup tag \"[${unknown}]\" in choice text — '[..]' is for inline ` +\n `markup only; write choice attributes as 'if: …', '-> node', or '#flag'`,\n );\n }\n\n return {\n text,\n ...(key !== undefined ? { key } : {}),\n ...(condition !== undefined ? { condition } : {}),\n ...(target !== undefined ? { target } : {}),\n ...(once ? { once: true } : {}),\n ...(disabled ? { presentation: \"disabled\" as const } : {}),\n ...(metaCount > 0 ? { meta } : {}),\n };\n}\n\n// ── Shared scalar / token helpers ────────────────────────────────────────────\n\n/** A trailing `#key:value` → `meta[key] = scalar(value)`; a bare `#flag` →\n * `meta[flag] = true`. Returns 1 (a meta key was written) for the caller's\n * \"did anything land in meta\" tally. */\nfunction applyHashtag(meta: Record<string, unknown>, tag: string): 1 {\n const colon = tag.indexOf(\":\");\n if (colon === -1) meta[tag] = true;\n else meta[tag.slice(0, colon)] = scalar(tag.slice(colon + 1));\n return 1;\n}\n\n/** A `line:<id>` hashtag carries an i18n key (Yarn's `#line:` convention) →\n * the step's `key`, not `meta`. Returns the id, or `undefined` for any other\n * tag (which routes to `meta` as usual). */\nfunction lineKey(tag: string): string | undefined {\n const colon = tag.indexOf(\":\");\n return colon > 0 && tag.slice(0, colon) === \"line\" ? tag.slice(colon + 1) : undefined;\n}\n\n/** Sentinel: `numberBoolNull` returns this when the source is not one of the\n * bare literals (so `null`, a real value, stays distinguishable). */\nconst NOT_LITERAL = Symbol(\"not-literal\");\n\nfunction numberBoolNull(raw: string): VarValue | typeof NOT_LITERAL {\n if (/^-?\\d+(?:\\.\\d+)?$/.test(raw)) return Number(raw);\n if (raw === \"true\") return true;\n if (raw === \"false\") return false;\n if (raw === \"null\") return null;\n return NOT_LITERAL;\n}\n\n/** A command / hint / meta value: a quoted string, a bare number / bool / null,\n * else the bareword as a string (`rusty-key`). (Distinct from a `set` value,\n * which reads a bareword as a variable reference.) */\nfunction scalar(raw: string): VarValue {\n const lit = numberBoolNull(raw);\n return lit === NOT_LITERAL ? unquote(raw) : lit;\n}\n\n/** Drop one layer of matching surrounding quotes, if present. */\nfunction unquote(raw: string): string {\n if (\n raw.length >= 2 &&\n ((raw.startsWith('\"') && raw.endsWith('\"')) || (raw.startsWith(\"'\") && raw.endsWith(\"'\")))\n ) {\n return raw.slice(1, -1);\n }\n return raw;\n}\n\n/** Split on whitespace, but keep a `\"…\"` / `'…'` span (including its spaces)\n * attached to the token it sits in — so `msg=\"hello world\"` is one token. */\nfunction splitArgs(s: string): string[] {\n const out: string[] = [];\n let i = 0;\n while (i < s.length) {\n while (i < s.length && /\\s/.test(s[i]!)) i++;\n if (i >= s.length) break;\n let tok = \"\";\n while (i < s.length && !/\\s/.test(s[i]!)) {\n const c = s[i]!;\n if (c === '\"' || c === \"'\") {\n tok += c;\n i++;\n while (i < s.length && s[i] !== c) tok += s[i++];\n if (i < s.length) tok += s[i++]; // closing quote\n } else {\n tok += c;\n i++;\n }\n }\n out.push(tok);\n }\n return out;\n}\n\nfunction fail(lineNo: number, message: string): never {\n throw new DialogueScriptError(`compact: line ${lineNo}: ${message}`);\n}\n","/**\n * `defineScript` — the TS-first authoring path. An identity function at\n * runtime; at compile time it captures the script's declared variable value\n * types (from their {@link DialogueScript.declare} defaults), branding the\n * returned script so `play()` can hand back a typed {@link DialogueHandle}.\n *\n * JSON scripts (a plain {@link DialogueScript} literal) get the exact same\n * runtime rules via load-/play-time validation — the brand is a pure\n * compile-time convenience and never exists at runtime. Inference stays shallow:\n * it lives here and at the `play()` boundary, never threaded through the\n * session/controller internals.\n *\n * const script = defineScript({\n * id: \"shop\", start: \"n\",\n * declare: { greeted: false, gold: 0 }, // greeted: boolean, gold: number\n * nodes: { n: { id: \"n\", steps: [{ kind: \"end\" }] } },\n * });\n * const handle = controller.play(script); // content-only\n * handle.setVar(\"greeted\", true); // ^ key is typed to keyof declare\n */\n\nimport type {\n DialogueNode,\n DialogueScript,\n NodeId,\n SpeakerDef,\n SpeakerId,\n VarMap,\n VarValue,\n} from \"./types.js\";\n\ndeclare const VARS_BRAND: unique symbol;\n\n/** Phantom carrier of a script's captured variable types. Never present at\n * runtime — only `defineScript`'s cast asserts it. */\ninterface ScriptTypes<V extends VarMap> {\n readonly [VARS_BRAND]: V;\n}\n\n/** A {@link DialogueScript} branded with its declared variable types. */\nexport type TypedScript<V extends VarMap> = DialogueScript & ScriptTypes<V>;\n\n/** Widen a declared default's literal type to its base — a variable declared\n * `false` is a boolean (accepts `true`), `0` is a number, `\"x\"` is a string. A\n * `null` default is untyped, so it widens to the full {@link VarValue} union. */\ntype Widen<T extends VarValue> = T extends string\n ? string\n : T extends number\n ? number\n : T extends boolean\n ? boolean\n : VarValue;\n\ntype WidenVars<V extends VarMap> = { [K in keyof V]: Widen<V[K]> };\n\nexport function defineScript<V extends VarMap = Record<never, never>>(\n script: Omit<DialogueScript, \"declare\"> & {\n readonly id: string;\n readonly start?: NodeId;\n readonly nodes: Record<NodeId, DialogueNode>;\n readonly speakers?: Record<SpeakerId, SpeakerDef>;\n readonly declare?: V;\n },\n): TypedScript<WidenVars<V>> {\n return script as unknown as TypedScript<WidenVars<V>>;\n}\n\n/** Declared variable types captured for a script (the loose {@link VarMap} for a\n * plain JSON script). Drives the typed {@link DialogueHandle} `play()` returns. */\nexport type VarsOf<S> = S extends ScriptTypes<infer V> ? V : VarMap;\n","/**\n * DialogueRunner — the engine-agnostic state machine. It walks a normalised\n * {@link DialogueScript}, pausing on `say`/`choice` steps (which need player\n * input) and running `command`/`goto`/`end` steps straight through. All\n * presentation is delegated via callbacks, so the same runner drives a\n * renderer-based box, a ui-react box, or a headless test.\n *\n * Branching reads one {@link VariableStorage} namespace through an\n * {@link EvalScope} (per-name reads + installed functions): `set` / `ctx.setVar`\n * write storage, conditions and `set` values are evaluated as expression trees.\n * The runner resolves built-in commands (`set`) itself and surfaces every other\n * command to the host through `onCommand` — that's the seam where the game turns\n * `{ type: \"give-item\", id: \"key\" }` into an actual effect.\n */\n\nimport { createScope, evaluate, holds, isExpr, type EvalScope } from \"./expr.js\";\nimport { materialize } from \"./vars.js\";\nimport type {\n ChoiceOption,\n ChoiceStep,\n Command,\n CommandContext,\n Condition,\n DialogueFunction,\n DialogueScript,\n RunMode,\n SayStep,\n SpeakerDef,\n Step,\n VariableStorage,\n VarMap,\n VarValue,\n} from \"./types.js\";\n\n/** The runtime environment the session installs behind a running conversation:\n * the variable storage (read + guarded write) + the callable functions. */\nexport interface RunnerEnv {\n readonly storage: VariableStorage;\n readonly functions: Readonly<Record<string, DialogueFunction>>;\n /**\n * Surfaces a non-fatal runtime diagnostic — currently a `set` whose write the\n * storage rejected (a getter-only `cells` accessor). The runner ignores the\n * write and keeps the conversation running; the host (via the session →\n * controller) routes the message to the engine logger. Engine-agnostic: the\n * core never reaches for `console`/a logger directly.\n */\n readonly onError?: ((message: string, error: unknown) => void) | undefined;\n}\n\nexport interface ResolvedChoice {\n readonly index: number; // index into the original options array\n readonly option: ChoiceOption;\n /**\n * A visible-but-disabled row: the option's condition currently fails AND its\n * `presentation` is `\"disabled\"`, so it's shown greyed-out and non-selectable\n * instead of filtered. Omitted (falsy) for a normal, selectable option.\n * Default-`\"hidden\"` condition failures and spent `once` options aren't\n * returned at all.\n */\n readonly disabled?: boolean;\n}\n\nexport interface RunnerHandlers {\n /** A line is ready to display. Runner waits for `advance()`. */\n onSay(step: SayStep, speaker: SpeakerDef | undefined): void;\n /** Choices are ready. Runner waits for `choose(index)`. `prompt` pre-resolved by host. */\n onChoice(\n step: ChoiceStep,\n choices: readonly ResolvedChoice[],\n speaker: SpeakerDef | undefined,\n ): void;\n /**\n * A non-built-in command fired (give-item, play-sfx, …). May return a promise;\n * if the command is `blocking`, the runner waits for it.\n */\n onCommand(command: Command, ctx: CommandContext): void | Promise<void>;\n /** Conversation finished (ran off the end or hit an `end` step). */\n onEnd(): void;\n}\n\ntype RunnerState = \"idle\" | \"saying\" | \"choosing\" | \"awaiting-command\" | \"ended\";\n\nexport class DialogueRunner {\n /** `option.once` keys already picked — per-conversation **cursor** state, NOT\n * the variable storage. Fresh per runner, so a new `play()` starts it empty\n * (a re-played conversation re-shows its `once` options; {@link getChosenOnce}\n * exposes the set so a save cursor could capture/restore it). */\n private readonly chosenOnce = new Set<string>();\n private nodeId: string;\n private stepIndex = 0;\n private state: RunnerState = \"idle\";\n /** \"play\" normally; `skip()` flips it to \"skip\" to fast-forward the section. */\n private runMode: RunMode = \"play\";\n\n /** Storage (write through this so a read-only `cells` accessor throws) +\n * functions, wrapped once as the condition/`set`-value eval scope. */\n private readonly storage: VariableStorage;\n private readonly scope: EvalScope;\n private readonly onError: ((message: string, error: unknown) => void) | undefined;\n\n constructor(\n private readonly script: DialogueScript,\n /** The variable storage + functions (built by the session per play()). */\n env: RunnerEnv,\n private readonly handlers: RunnerHandlers,\n ) {\n this.nodeId = script.start;\n this.storage = env.storage;\n this.scope = createScope(env.storage, env.functions);\n this.onError = env.onError;\n }\n\n /** Snapshot of the storage's variables — the `handle.getVars()` /\n * future save-cursor view. */\n getVars(): Readonly<VarMap> {\n return materialize(this.storage);\n }\n\n // ── save seam (read-only cursor getters) ──────────────────────────────────\n // The runner's durable cursor is (nodeId, stepIndex, chosenOnce) + getVars().\n // These getters exist so a future `SnapshotContributor` can capture/restore a\n // conversation WITHOUT a breaking API change. Snapshot/restore itself is\n // deliberately NOT built yet — keep these read-only.\n\n /** Current node id (durable cursor; save seam). */\n getNodeId(): string {\n return this.nodeId;\n }\n\n /** Current step index within the node (durable cursor; save seam). */\n getStepIndex(): number {\n return this.stepIndex;\n }\n\n /** One-shot choice keys already picked (`option.once`); save seam. */\n getChosenOnce(): ReadonlySet<string> {\n return this.chosenOnce;\n }\n\n isEnded(): boolean {\n return this.state === \"ended\";\n }\n\n /** Begin at the start node. Idempotent guard against double-start. The cursor\n * (`nodeId`/`stepIndex`) is already at the start from the ctor + field init. */\n start(): void {\n if (this.state !== \"idle\") return;\n void this.run();\n }\n\n /** Advance past the current `say` line. No-op unless we're awaiting it. */\n advance(): void {\n if (this.state !== \"saying\") return;\n this.stepIndex++;\n void this.run();\n }\n\n /**\n * Fast-forward from the current line: run intervening commands in `skip` mode\n * (so the game can reconstruct world state idempotently) without presenting\n * any lines, stopping at the next choice or the end. No-op unless on a line.\n */\n async skip(): Promise<void> {\n if (this.state !== \"saying\") return;\n this.runMode = \"skip\";\n this.stepIndex++;\n await this.run();\n }\n\n /**\n * Public, **wait-state-free** entry the Session uses to fire a `say` line's\n * commands at show / after-reveal / advance time. Handles built-in `set`,\n * surfaces the rest with the current mode (or `mode`, when the Session fires the\n * displayed line's batches as part of its own skip), and awaits `blocking` ones.\n * Delegates to {@link executeBatch}; the runner's wait-state is untouched (the\n * Session gates its own input).\n */\n runCommands(commands: readonly Command[] | undefined, mode?: RunMode): Promise<void> {\n return this.executeBatch(commands, mode);\n }\n\n /** Pick choice `index` (the original option index). */\n async choose(index: number): Promise<void> {\n if (this.state !== \"choosing\") return;\n const step = this.currentStep();\n if (!step || step.kind !== \"choice\") return;\n const option = step.options[index];\n if (!option || !this.choiceEnabled(step, index, option)) return;\n\n if (option.once) this.chosenOnce.add(this.onceKey(step, index));\n // Hold a transient state so a second confirm during an awaited blocking\n // command is ignored, then run the option's commands before branching.\n this.state = \"awaiting-command\";\n await this.fireBatch(option.commands);\n if (this.isEnded()) return;\n\n if (option.target !== undefined) this.jump(option.target);\n else this.stepIndex++;\n void this.run();\n }\n\n // ── internal ────────────────────────────────────────────────────────────\n\n /** Run non-blocking steps until we hit one that needs input, or the end. */\n private async run(): Promise<void> {\n for (;;) {\n if (this.isEnded()) return;\n const step = this.currentStep();\n if (!step) {\n this.end();\n return;\n }\n if (await this.handleStep(step)) return; // returned true → blocking, wait\n }\n }\n\n /** @returns true if the step blocks (waiting for advance/choose/command/end). */\n private async handleStep(step: Step): Promise<boolean> {\n switch (step.kind) {\n case \"say\": {\n if (this.runMode === \"skip\") {\n // Fast-forward: run the line's commands (world reconstruction) but\n // present nothing, then move on. In `play` the Session fires these.\n await this.fireBatch(step.commands);\n if (this.isEnded()) return true;\n this.stepIndex++;\n return false;\n }\n this.state = \"saying\";\n this.handlers.onSay(step, this.speaker(step.speaker));\n return true;\n }\n case \"choice\": {\n const choices = this.resolveChoices(step);\n // Skip the step when nothing is *selectable* — a list of only-disabled\n // rows (or no rows at all) would soft-lock, so it falls through like the\n // all-hidden / all-condition-fail path. Visible-but-disabled requires at\n // least one enabled option.\n if (!choices.some((c) => !c.disabled)) {\n this.stepIndex++;\n return false;\n }\n // A choice needs the player, so any in-progress skip ends here.\n this.runMode = \"play\";\n this.state = \"choosing\";\n this.handlers.onChoice(step, choices, this.speaker(step.speaker));\n return true;\n }\n case \"command\": {\n await this.fireBatch(step.commands);\n if (this.state === \"ended\") return true;\n if (step.target !== undefined && this.test(step.condition)) {\n this.jump(step.target);\n return false;\n }\n this.stepIndex++;\n return false;\n }\n case \"goto\":\n this.jump(step.target);\n return false;\n case \"end\":\n this.end();\n return true;\n }\n }\n\n private jump(target: string): void {\n this.nodeId = target;\n this.stepIndex = 0;\n }\n\n private end(): void {\n if (this.state === \"ended\") return;\n this.state = \"ended\";\n this.handlers.onEnd();\n }\n\n private currentStep(): Step | undefined {\n return this.script.nodes[this.nodeId]?.steps[this.stepIndex];\n }\n\n private speaker(id: string | undefined): SpeakerDef | undefined {\n return id ? this.script.speakers?.[id] : undefined;\n }\n\n /**\n * Resolve a choice step to its visible rows. A spent `once` option is always\n * dropped (presentation governs condition failures only). A passing option is\n * enabled; a failing one is returned as a `disabled` row when its\n * `presentation` is `\"disabled\"`, else dropped (the default `\"hidden\"`).\n */\n private resolveChoices(step: ChoiceStep): ResolvedChoice[] {\n const out: ResolvedChoice[] = [];\n step.options.forEach((option, index) => {\n if (this.isSpent(step, index)) return;\n // The enabled test here is the visible-row companion to `choiceEnabled`\n // (the commit gate): both gate on `isSpent` + `test(condition)`. A new\n // \"disabled\" reason must be added to both, or a row could show as enabled\n // yet refuse to commit.\n if (this.test(option.condition)) out.push({ index, option });\n else if (option.presentation === \"disabled\") out.push({ index, option, disabled: true });\n });\n return out;\n }\n\n /** Whether option `index` can actually be picked — the gate `choose()` uses.\n * A spent `once` option or a failing condition refuses (a `\"disabled\"` row is\n * shown but still unpickable, so this stays the single selection authority). */\n private choiceEnabled(step: ChoiceStep, index: number, option: ChoiceOption): boolean {\n return !this.isSpent(step, index) && this.test(option.condition);\n }\n\n /** A `once` option already chosen this run — always dropped from the menu\n * regardless of `presentation`. Single source of truth for the once-gate,\n * shared by `resolveChoices` and `choiceEnabled`. Reads the option from\n * `step.options[index]`, so `(step, index)` is the only input. */\n private isSpent(step: ChoiceStep, index: number): boolean {\n const option = step.options[index];\n return option?.once === true && this.chosenOnce.has(this.onceKey(step, index));\n }\n\n private onceKey(step: ChoiceStep, index: number): string {\n // Stable across re-entry: node + step position + option index.\n return `${this.nodeId}#${this.stepIndex}#${index}#${step.options[index]?.text ?? \"\"}`;\n }\n\n /**\n * Fire an inline command batch (a `command` step or a chosen option). Manages\n * wait-state: enters `awaiting-command` up front when the batch contains a\n * blocking command, so a stray advance/confirm during the await is ignored; the\n * caller transitions out of the state afterwards. The work itself goes through\n * the wait-state-free {@link executeBatch}.\n */\n private async fireBatch(commands: readonly Command[] | undefined): Promise<void> {\n if (!commands || commands.length === 0) return;\n if (commands.some((c) => c.blocking)) this.state = \"awaiting-command\";\n await this.executeBatch(commands);\n }\n\n /**\n * The wait-state-free command executor, shared by {@link fireBatch} (inline\n * firing) and {@link runCommands} (the Session's line-timed firing). Applies\n * built-in `set`; surfaces the rest to the host with the current mode; awaits\n * `blocking` handlers and fire-and-forgets the others. Touches no wait-state.\n */\n private async executeBatch(\n commands: readonly Command[] | undefined,\n mode: RunMode = this.runMode,\n ): Promise<void> {\n if (!commands) return;\n for (const cmd of commands) {\n if (cmd.type === \"set\" && typeof cmd.var === \"string\") {\n // Built-in write: the value is a literal or an expression tree\n // (`gold - 50`). The storage rejects a write to a read-only `cells`\n // accessor — report it and keep going rather than let the throw escape\n // the async run()/choose() chain and wedge the conversation (same\n // contract as a throwing command handler below).\n const value = cmd.value;\n const next = isExpr(value) ? evaluate(value, this.scope) : (value as VarValue);\n try {\n this.storage.set(cmd.var, next);\n } catch (e) {\n this.onError?.(\n `ignored \"set ${cmd.var}\": ${e instanceof Error ? e.message : String(e)}`,\n e,\n );\n }\n continue;\n }\n let result: void | Promise<void>;\n try {\n result = this.handlers.onCommand(cmd, this.commandContext(mode));\n } catch {\n // A handler that throws *synchronously* must not wedge the conversation\n // either (same contract as the blocking-await catch below): swallow and\n // keep executing, so the caller's state transition still runs.\n continue;\n }\n if (!isPromise(result)) continue;\n if (cmd.blocking) {\n try {\n await result;\n } catch {\n // A blocking handler that throws must not wedge the conversation.\n }\n if (this.isEnded()) return;\n } else {\n // Swallow rejection so a fire-and-forget handler isn't an unhandled one.\n void Promise.resolve(result).catch(() => {});\n }\n }\n }\n\n /** The context handed to a command handler. `setVar` writes through the\n * conversation's storage (guarded by the session for staleness), the same\n * path as the `set` built-in — so the skill-check seam and `set` share one\n * guarded write. */\n private commandContext(mode: RunMode): CommandContext {\n return { mode, setVar: (name, value) => this.storage.set(name, value) };\n }\n\n private test(condition: Condition | undefined): boolean {\n return holds(condition, this.scope);\n }\n}\n\nfunction isPromise(v: unknown): v is Promise<unknown> {\n return (\n typeof v === \"object\" && v !== null && typeof (v as { then?: unknown }).then === \"function\"\n );\n}\n","/**\n * DialogueSession — the headless orchestrator. It binds a {@link DialogueRunner}\n * to a set of presentation *channels* (text / choices / avatar / chrome) and\n * sequences a conversation: resolve i18n + markup, drive the typewriter, gate on\n * reveal-completion, run the auto-advance clock, and book-keep choice selection.\n *\n * It is engine-agnostic (zero `@yagejs` imports): the channels are semantic\n * interfaces — `present(line)`, `highlight(i)`, `setSpeaking(bool)` — with no\n * pixels, no input, and no world entities. A YAGE host (`DialogueController`)\n * supplies concrete channels, an `InputBinding`, and pumps `update(dt)`; a\n * headless test can supply stubs. The public API is input-agnostic\n * (`advance / moveSelection / confirm / choose / setFastForward`) so any device\n * binding maps onto it.\n *\n * Observation is via callbacks (`onLine`, `onChoiceMade`, `onCommand`, …) so a\n * host can forward to engine events or a history recorder without the Session\n * knowing about either.\n */\n\nimport { loadScript } from \"./formats/canonical.js\";\nimport type { DialogueExtraChannel } from \"./channels/types.js\";\nimport { createScope, holds } from \"./expr.js\";\nimport { IdentityI18n, type I18nAdapter } from \"./i18n.js\";\nimport { EMPTY_PARSED, parseMarkup, stripMarkup } from \"./markup.js\";\nimport type { RevealBeat } from \"./LineReveal.js\";\nimport { DialogueRunner, type ResolvedChoice } from \"./runner.js\";\nimport { MemoryVariableStorage, materialize } from \"./vars.js\";\nimport { analyzeScript, validatePlay, DialoguePlayError } from \"./validate.js\";\nimport type { VarsOf } from \"./defineScript.js\";\nimport type {\n ChoiceStep,\n Command,\n CommandContext,\n CommandHandler,\n CommandTiming,\n DialogueFunction,\n DialogueHandle,\n DialoguePlayOptions,\n DialogueScript,\n MarkerToken,\n ParsedText,\n RunMode,\n SayStep,\n SpeakerDef,\n VariableStorage,\n VarMap,\n} from \"./types.js\";\n\n/** Stale-handle snapshot — frozen so a no-op `getVars()` can't be mutated. */\nconst EMPTY_VARS: Readonly<VarMap> = Object.freeze({});\n\n// The addon's own output types declare optional fields as `T | undefined`\n// (not bare `?: T`): producers can then assign possibly-undefined inputs\n// directly under `exactOptionalPropertyTypes`, and consumers test values, not\n// key presence — so the shapes stay spread-free to construct.\n\n/** One previewed line: speaker name + plain (markup-stripped) text. */\nexport interface PreviewedLine {\n readonly speaker?: string | undefined;\n readonly text: string;\n}\n\n/** Resolved speaker descriptor on a presented line (for nameplates / anchoring). */\nexport interface SpeakerView {\n readonly id: string;\n readonly name?: string | undefined;\n readonly color?: number | undefined;\n}\n\n/** A fully-resolved line (i18n + markup already applied) handed to presenters. */\nexport interface PresentedLine {\n /** Who's speaking (if anyone) — lets world presenters anchor to their actor. */\n readonly speaker?: SpeakerView | undefined;\n readonly text: ParsedText;\n /** Per-line reveal-speed multiplier (`say.speed`, default 1). */\n readonly speed: number;\n /** Opaque preset name for per-line layout/variant (presenter interprets). */\n readonly view?: string | undefined;\n readonly meta?: Readonly<Record<string, unknown>> | undefined;\n /** Voice-clip id (audio handler interprets; reveal may sync to it). */\n readonly voice?: string | undefined;\n}\n\n/** One choice row, resolved to a display label. Position = array index. */\nexport interface PresentedChoice {\n readonly label: string;\n readonly meta?: Readonly<Record<string, unknown>> | undefined;\n /** True for a visible-but-disabled row (its condition failed and its\n * `presentation` is `\"disabled\"`). Presenters render it greyed and\n * non-selectable; the Session's nav/confirm skip it. */\n readonly disabled?: boolean | undefined;\n /** i18n-resolved reason for a {@link disabled} row, shown beside it where the\n * layout allows (e.g. \"Requires the rusty key\"). */\n readonly disabledReason?: string | undefined;\n}\n\n/**\n * Body-text channel. Owns reveal timing (the Session only learns *that* a line\n * finished, via the reveal listener, never *when* a glyph appears). An\n * accessibility / no-typewriter presenter is\n * `present(){ draw(); this.revealListener?.() }`.\n */\nexport interface TextChannel {\n present(line: PresentedLine): void;\n /** Reveal everything immediately (skip-to-end). */\n completeReveal(): void;\n isRevealComplete(): boolean;\n isRevealing(): boolean;\n /** Hold-to-fast-forward rate flag (1 = normal). */\n setSpeedMultiplier(multiplier: number): void;\n /**\n * Show or hide the body text **without** disturbing reveal progress — a\n * cutscene can hide mid-typewriter and show again to resume mid-line. Purely\n * visual: the reveal cursor, timers, and the laid-out line are untouched.\n */\n setVisible(visible: boolean): void;\n update(dt: number): void;\n clear(): void;\n /**\n * Register the reveal-completed listener. The Session owns this seam — it\n * registers its handler once in the ctor — so a game can't clobber the wiring\n * by assigning a public field. Pass `undefined` to clear.\n */\n setRevealListener(listener: (() => void) | undefined): void;\n /**\n * Register the reveal-beat listener — per-grapheme typewriter ticks and inline\n * `[name k=v/]` markers, in char order, as the reveal cursor reaches each.\n * Session-owned like {@link setRevealListener} (registered once in the ctor);\n * pass `undefined` to clear. A no-typewriter presenter that reveals instantly\n * may omit beats; the bundled view forwards the headless {@link LineReveal}\n * clock's beats.\n */\n setBeatListener(listener: ((beat: RevealBeat) => void) | undefined): void;\n}\n\n/** Per-choice presentation context, so a choice list can route/anchor the same\n * way lines do (box list vs a bubble over `speaker`'s actor) and optionally\n * render the prompt itself. */\nexport interface ChoiceContext {\n readonly view?: string | undefined;\n readonly speaker?: SpeakerView | undefined;\n /** The (resolved + parsed) prompt, made available so a presenter can render\n * it itself (see {@link ChoiceChannel.ownsPrompt}). Empty when no prompt. */\n readonly prompt?: ParsedText | undefined;\n /** The choice step's opaque `meta` bag, passed straight through. A custom\n * presenter reads it to render extras the model doesn't own — e.g. the\n * timed-choice recipe's `{ timeoutMs }` for a countdown. */\n readonly meta?: Readonly<Record<string, unknown>> | undefined;\n}\n\n/** Choice channel. Selection nav lives in the Session; pointer/touch commits\n * come back through `onChoiceChosen(position)`. `context` carries the choice's\n * view/speaker/prompt so a composite presenter can route (box vs bubble). */\nexport interface ChoiceChannel {\n present(choices: readonly PresentedChoice[], context?: ChoiceContext): void;\n highlight(position: number): void;\n /** Show or hide the choice list without clearing it — state-preserving,\n * so the selection and laid-out rows survive a hide/show round-trip. */\n setVisible(visible: boolean): void;\n clear(): void;\n onChoiceChosen?: (position: number) => void;\n /**\n * If true for this `context`, the presenter draws the prompt itself (e.g. a\n * self-contained bubble panel), so the Session suppresses the chrome + body-\n * text prompt. Default false → the chrome/text show the prompt (box body).\n */\n ownsPrompt?(context?: ChoiceContext): boolean;\n}\n\n/** Avatar channel — who's talking + their expression + talk state. */\nexport interface AvatarChannel {\n setSpeaker(speaker: SpeakerDef | undefined): void;\n setExpression(expression: string | undefined): void;\n setSpeaking(speaking: boolean): void;\n /**\n * Optional per-line hook (mirrors {@link ChromeChannel.present}) so an avatar\n * can be **line-driven**: read the line's `meta` (e.g. `portrait` / `side` /\n * `presence`) to pick an image/side/presence, beyond what `setSpeaker` carries.\n * The Session calls it on each say/choice line alongside `setSpeaker`, and\n * with `undefined` when the conversation clears (stop/end). A reflowing in-box\n * avatar registers a text inset here so the body text reflows around it. Most\n * avatars (portrait, scene-figure) omit it.\n */\n present?(line: PresentedLine | undefined): void;\n /**\n * Optional inline-marker hook (sibling to {@link present} / {@link setVisible}).\n * The Session fans every `[name k=v/]` reveal marker here so an avatar can\n * interpret the ones it owns — the bundled portrait/scene presenters read\n * `[expression=…/]` and call their own `setExpression`. The Session name-matches\n * NOTHING; an avatar ignores markers it doesn't recognize. `viaSkip` markers\n * (drained by a skip) arrive here too — the avatar collapses to the last one.\n */\n marker?(marker: MarkerToken): void;\n /** Optional visibility gate — a portrait hides during a cutscene; a\n * scene-figure avatar (a world NPC the game owns) omits it and stays put. */\n setVisible?(visible: boolean): void;\n update(dt: number): void;\n}\n\n/** Chrome channel — frame / nameplate / continue caret (everything but body\n * text and choices). `present?` lets a chrome react to per-line variants;\n * `present(undefined)` means \"no line — clear the chrome's content\". */\nexport interface ChromeChannel {\n /** Set the speaker name, or `undefined` for **no name** — NOT a covert\n * hide-all; visibility is governed solely by {@link setVisible}. */\n setNameplate(name: string | undefined, color?: number): void;\n setContinueVisible(visible: boolean): void;\n /**\n * Show or hide the whole chrome — the honest visibility verb the Session\n * drives (a composite restores its active variant on show). State-preserving.\n */\n setVisible(visible: boolean): void;\n /**\n * React to a per-line variant (`meta.chrome`, `meta.position`). Called\n * **before** {@link TextChannel.present} for the same line, so a composite\n * selects its active variant and a layout owner commits the frame the text\n * then reads. `present(undefined)` means \"no line — clear the chrome's content\".\n */\n present?(line: PresentedLine | undefined): void;\n update(dt: number): void;\n}\n\n/**\n * The presentation channels a {@link DialogueSession} drives. Writing a custom\n * presenter (a DOM overlay, a ui-react chrome) means implementing these — so the\n * **call-order contract** the Session guarantees is documented here, on the\n * interfaces themselves:\n *\n * Per say line, the Session calls (in this order):\n * 1. `chrome.setNameplate(name)` / `chrome.setContinueVisible(false)`\n * 2. `avatar.setSpeaker` / `setExpression` / `setSpeaking`, then `avatar.present?(line)`\n * 3. `chrome.present?(line)` — **before** the text, so a composite picks the\n * active variant first and a layout owner commits the frame the text reads\n * 4. `text.present(line)` — render + start revealing\n * 5. (each channel's `setVisible(shown)` reflects the host-hidden lever)\n *\n * Then, exactly once, when the line finishes revealing, the text channel fires\n * the listener registered via {@link TextChannel.setRevealListener} (the\n * Session owns that seam — never a public field). For an **empty** line the\n * completion fires synchronously inside `text.present`.\n *\n * Per choice the order mirrors a line (chrome/avatar/text for the optional\n * prompt) and then `choices.present(options, context)`. If a presenter\n * {@link ChoiceChannel.ownsPrompt | owns the prompt}, the Session clears the\n * chrome + body instead of step 3/4.\n *\n * `setBox`/geometry is always applied **before** `present` for that line (a\n * presenter that lays out off a region reads the committed region in `present`).\n */\nexport interface DialogueChannels {\n readonly text: TextChannel;\n readonly choices: ChoiceChannel;\n readonly avatar?: AvatarChannel | undefined;\n readonly chrome?: ChromeChannel | undefined;\n}\n\nexport interface DialogueSessionOptions {\n readonly i18n?: I18nAdapter | undefined;\n /** Hold-to-fast-forward multiplier. Default 4. */\n readonly skipMultiplier?: number | undefined;\n /**\n * The variable storage installed for every `play()`. Persists across\n * plays. Omit for a zero-config {@link MemoryVariableStorage}; supply your own\n * or {@link compose} several to bridge game state. A per-`play()`\n * `overrides.storage` replaces it for that conversation.\n */\n readonly storage?: VariableStorage | undefined;\n /** Argument-capable read functions (`has_item(\"key\")`) for conditions/`set`\n * expressions. Per-`play()` `overrides.functions` merge on top. */\n readonly functions?: Readonly<Record<string, DialogueFunction>> | undefined;\n /** Command handlers (`type` → handler). Per-`play()` `overrides.commands`\n * merge on top (call site wins). */\n readonly commands?: Readonly<Record<string, CommandHandler>> | undefined;\n /** Catch-all for command types with no explicit handler. */\n readonly fallbackCommand?: CommandHandler | undefined;\n /** Non-fatal runtime diagnostics (e.g. a `set` to a read-only `cells`\n * accessor that was ignored). The controller routes these to the engine\n * logger; a bare session may handle or drop them. */\n readonly onError?: ((message: string, error: unknown) => void) | undefined;\n readonly onStarted?: (e: { scriptId: string }) => void;\n /** Plain (markup-stripped) line text — for logs / a11y / history. */\n readonly onLine?: (e: { speaker?: string | undefined; text: string }) => void;\n readonly onChoiceShown?: (e: { options: readonly string[] }) => void;\n readonly onChoiceMade?: (e: { index: number; text: string }) => void;\n /**\n * Observation hook fired for every non-built-in command (`set` is runner-owned\n * and never reaches it) — the host forwards it to {@link DialogueCommandEvent}.\n * The actual *handling* (and any `blocking` await) is the binding's `commands`\n * map / `fallbackCommand`, not this; this returns nothing.\n */\n readonly onCommand?: (command: Command, ctx: CommandContext) => void;\n readonly onEnded?: (e: { scriptId: string }) => void;\n /** A line finished its typewriter reveal — the \"typing finished\" hook\n * (audio blip, etc.). Plain (markup-stripped) text, like {@link onLine}. */\n readonly onRevealCompleted?: (e: { speaker?: string | undefined; text: string }) => void;\n /**\n * Per-grapheme typewriter tick — a direct CALLBACK only (NOT forwarded to an\n * entity event; it fires hundreds of times per line). `index` is the 0-based\n * grapheme index just revealed, raw (whitespace included — the host filters if\n * it only wants a blip on visible glyphs). Wire a typewriter SFX here. Not\n * fired on a skip / fast-forward (pending ticks are discarded).\n */\n readonly onRevealTick?: ((index: number) => void) | undefined;\n /**\n * An inline `[name k=v/]` marker reached its char offset during reveal — the\n * host forwards it to {@link DialogueRevealMarkerEvent}. `viaSkip` is true when\n * a skip/complete drained it (a host can suppress a loud one-shot that only\n * fired because the player skipped). The Session name-matches NO marker: the\n * avatar channel interprets `[expression=…/]` itself; every other name flows\n * opaquely to the host / registered channels.\n */\n readonly onRevealMarker?: (marker: MarkerToken, viaSkip: boolean) => void;\n /** The choice cursor moved — keyboard nav AND pointer hover both funnel here\n * `index` is the resolved option index, `text` its plain label. */\n readonly onSelectionChanged?: (e: { index: number; text: string }) => void;\n /** The player skipped the current section — for skip-used analytics. */\n readonly onSkipUsed?: (e: { scriptId: string }) => void;\n /** A line auto-advanced via the auto-advance clock — distinct from a\n * manual advance so a game can tell them apart. */\n readonly onAutoAdvance?: (e: { scriptId: string }) => void;\n}\n\ntype Mode = \"idle\" | \"saying\" | \"choosing\" | \"ended\";\n\nexport class DialogueSession {\n private readonly i18n: I18nAdapter;\n private readonly skipMul: number;\n /** Controller-installed environment (persists across plays); per-`play()`\n * overrides are layered on top into the resolved fields below. */\n private readonly defaultStorage: VariableStorage;\n private readonly defaultFunctions: Readonly<Record<string, DialogueFunction>>;\n private readonly defaultCommands: Readonly<Record<string, CommandHandler>>;\n private readonly defaultFallback: CommandHandler | undefined;\n\n // Resolved per play() (controller install + call-site overrides).\n private storage: VariableStorage | undefined;\n private functions: Readonly<Record<string, DialogueFunction>> = {};\n private commands: Readonly<Record<string, CommandHandler>> = {};\n private fallbackCommand: CommandHandler | undefined;\n\n // Fields use explicit `| undefined` (not `?`) so reassigning `undefined`\n // (e.g. `stop()` nulling the cursor) is legal under the repo's\n // `exactOptionalPropertyTypes`.\n private runner: DialogueRunner | undefined;\n private script: DialogueScript | undefined;\n private mode: Mode = \"idle\";\n private scriptId = \"\";\n\n private saying: SayStep | undefined;\n private autoTimer: number | undefined;\n /** Default auto-advance delay (ms) applied to lines without their own\n * `autoAdvanceMs`. `null` = off (manual advance). Set via {@link setAutoAdvance}. */\n private autoAdvanceDefault: number | null = null;\n private resolved: readonly ResolvedChoice[] = [];\n private selected = 0;\n /** Count of in-flight blocking line-command batches (show/afterReveal/advance).\n * Input is gated while > 0. An ownership counter (not a shared boolean) so an\n * overlapping batch resolving — e.g. the afterReveal batch finishing while a\n * long blocking `show` command is still awaited — can't drop a gate it\n * doesn't own. */\n private blockedCount = 0;\n /** True between an advance request and the runner stepping off the line —\n * guards against a second advance double-firing `advance`-timed commands. */\n private advancing = false;\n /** True once the current line's `advance`-timed commands have fired, so a\n * second advance() while the runner is still stepping (e.g. awaiting a\n * blocking command step) can't re-fire them against the stale line. */\n private advanceFired = false;\n /** True once the current line's `afterReveal`-timed commands have fired\n * (normally via handleRevealComplete; skip() fires them early when the line\n * hasn't finished revealing). */\n private afterRevealFired = false;\n /** Latched by the first confirm() until the runner produces its next state\n * (handleSay/handleChoice/handleEnd), so mashing confirm while the runner\n * awaits the option's blocking commands can't emit duplicate onChoiceMade\n * events (`mode` stays \"choosing\" for that whole window). */\n private confirming = false;\n /** Bumped by every stop()/play(). A suspended async continuation from a prior\n * conversation captures this and bails on resume if it changed, so it can't\n * drive (advance / show the caret on) the runner of a *new* conversation. */\n private generation = 0;\n\n // ── lifecycle levers — host-level, NOT conversation state ──────────────\n /** `setHidden` — visual only; gates every channel's `setVisible`. Survives\n * `stop()`/`play()` (a host that hides for a cutscene and forgets to unhide\n * gets what it asked for) — so it is deliberately NOT reset by `stop()`. */\n private hidden = false;\n /** `setPaused` — freezes the update loop AND the input-agnostic API. State is\n * left fully intact (no generation bump); also host-level, survives replays. */\n private paused = false;\n /** Whether the chrome / body text are part of the CURRENT choice's layout\n * (false when a self-contained bubble panel owns the prompt) — remembered so\n * {@link applyVisibility} can recompute after a hide toggle mid-choice. */\n private choiceShowsChrome = false;\n private choiceShowsBody = false;\n /** Plain (speaker, text) of the line on screen — for the reveal-completed\n * event, which fires after `present` has discarded the resolved string. */\n private currentLine: { speaker?: string | undefined; text: string } | undefined;\n /** The full {@link PresentedLine} on screen — handed to an extra channel's\n * `revealComplete` (the session discards the local `line` after present). */\n private currentPresented: PresentedLine | undefined;\n\n /** Host-registered extra channels (Voice / Shop / CameraEffects / History).\n * The session fans its cross-cutting stream to these alongside the typed trio\n * and folds their `isRevealComplete()` into the auto-advance gate. */\n private readonly extras: DialogueExtraChannel[] = [];\n\n constructor(\n private readonly channels: DialogueChannels,\n private readonly opts: DialogueSessionOptions = {},\n ) {\n this.i18n = opts.i18n ?? new IdentityI18n();\n this.skipMul = opts.skipMultiplier ?? 4;\n this.defaultStorage = opts.storage ?? new MemoryVariableStorage();\n this.defaultFunctions = opts.functions ?? {};\n this.defaultCommands = opts.commands ?? {};\n this.defaultFallback = opts.fallbackCommand;\n // register the reveal seam through a method the Session owns, so a game\n // can't silently clobber it by reassigning a public field.\n this.channels.text.setRevealListener(() => this.handleRevealComplete());\n // Same ownership for the per-line beat stream (ticks + inline markers).\n this.channels.text.setBeatListener((beat) => this.handleRevealBeat(beat));\n // Pointer/touch commit from a presenter that owns its own hit-testing.\n // Routes through confirmAt so a tap on a disabled row is refused (it would\n // otherwise commit the previously-highlighted enabled row).\n this.channels.choices.onChoiceChosen = (position) => this.confirmAt(position);\n }\n\n /**\n * Begin a conversation. `play(script)` is **content-only** — the storage,\n * functions, and commands are installed on the session; `overrides` layers\n * per-conversation specifics on top (a scoped `storage`, extra `functions` /\n * `commands`). Declared defaults seed into the storage **only if absent**\n * (game-linked values win); variables persist across plays. Returns a\n * generation-stamped {@link DialogueHandle} for live `setVar` / `getVars`.\n */\n play<S extends DialogueScript>(\n rawScript: S,\n overrides?: DialoguePlayOptions,\n ): DialogueHandle<VarsOf<S>> {\n // Validate up front — load + analyze + resolve env + validatePlay are all\n // synchronous and read-only w.r.t. session state. Doing them BEFORE stop()\n // makes play() atomic: an invalid script / environment throws and leaves any\n // running conversation untouched, instead of abandoning it on a play() that\n // never starts.\n const script = loadScript(rawScript);\n const analysis = analyzeScript(script);\n\n // Resolve the environment: controller install + call-site overrides\n // (storage replaces; functions/commands merge, call site winning).\n const storage = overrides?.storage ?? this.defaultStorage;\n const functions = { ...this.defaultFunctions, ...overrides?.functions };\n const commands = { ...this.defaultCommands, ...overrides?.commands };\n const fallbackCommand = overrides?.fallbackCommand ?? this.defaultFallback;\n\n // Hard error on an environment that can't satisfy the script — runs before\n // seeding so the declared-default/storage conflict check sees the host value.\n validatePlay(analysis, { storage, functions, commands, fallbackCommand });\n\n // Seed-if-absent: a declared default applies only when the storage\n // doesn't already hold the name — never clobber a game-linked value. Done\n // BEFORE stop() and wrapped, so a storage that can't accept the write (a pure\n // read-only `cells()` with no writable slot for the name) fails as a clean\n // `DialoguePlayError` while any running conversation is left untouched —\n // play() stays atomic. Seeds are idempotent, so a partial seed before the\n // throw is harmless on retry.\n for (const [name, value] of Object.entries(script.declare ?? {})) {\n if (storage.has(name)) continue;\n try {\n storage.set(name, value);\n } catch (cause) {\n const detail = cause instanceof Error ? cause.message : String(cause);\n throw new DialoguePlayError(\n `cannot seed declared default \"${name}\": ${detail} ` +\n `(a read-only / pure cells() storage has no writable slot — ` +\n `compose it with a MemoryVariableStorage)`,\n );\n }\n }\n\n // Past the point of failure — commit. Abandon any in-flight conversation:\n // `stop()` bumps the generation, so a still-pending async continuation from\n // the previous line (e.g. an awaited `afterReveal`/`advance` command) bails on\n // resume instead of stepping this new runner — important when `play()` restarts\n // an ambient/eavesdrop loop while a line is mid-flight.\n this.stop();\n\n this.script = script;\n this.scriptId = script.id;\n this.storage = storage;\n this.functions = functions;\n this.commands = commands;\n this.fallbackCommand = fallbackCommand;\n\n // Stamp this runner's callbacks with the current generation. A later\n // stop()/play() bumps it, so a *prior* runner resuming an async step (e.g.\n // a blocking command awaited inside skip() or a command step) finds itself\n // stale and no-ops instead of driving handleSay/handleChoice on — and so\n // corrupting — the new conversation's session state.\n const gen = this.generation;\n const live = () => gen === this.generation;\n\n // The runner writes through a generation-guarded view of the storage: a\n // stale `set` / `ctx.setVar` (from an abandoned conversation's awaited\n // blocking command) no-ops instead of mutating the now-shared persistent\n // store. Reads pass straight through.\n const guarded: VariableStorage = {\n get: (name) => storage.get(name),\n set: (name, value) => {\n if (live()) storage.set(name, value);\n },\n has: (name) => storage.has(name),\n entries: () => storage.entries(),\n };\n\n const runner = new DialogueRunner(\n script,\n { storage: guarded, functions, onError: this.opts.onError },\n {\n onSay: (step, speaker) => {\n if (live()) this.handleSay(step, speaker);\n },\n onChoice: (step, choices, speaker) => {\n if (live()) this.handleChoice(step, choices, speaker);\n },\n onCommand: (command, ctx) =>\n live() ? this.handleCommand(command, ctx) : undefined,\n onEnd: () => {\n if (live()) this.handleEnd();\n },\n },\n );\n this.runner = runner;\n this.opts.onStarted?.({ scriptId: this.scriptId });\n runner.start();\n\n // Typed, generation-stamped handle: after stop()/replay it no-ops\n // (setVar via the guarded view) / returns an empty snapshot (getVars).\n return {\n setVar: (name, value) => guarded.set(name, value),\n getVars: () =>\n (gen === this.generation ? materialize(storage) : EMPTY_VARS) as Readonly<\n VarsOf<S>\n >,\n };\n }\n\n isActive(): boolean {\n return this.mode === \"saying\" || this.mode === \"choosing\";\n }\n\n isChoosing(): boolean {\n return this.mode === \"choosing\";\n }\n\n /**\n * Register an extra channel (Voice / Shop / CameraEffects / History) — the\n * open-ended companion to the built-in trio. It receives the cross-cutting\n * stream (`present` / `command` / `clear` / `setVisible` / `setPaused` /\n * `completeReveal` / `update`) and can gate auto-advance via\n * `isRevealComplete()`. Returns a disposer that unregisters **and** disposes\n * it. On register the channel catches up the current `setVisible` / `setPaused`\n * lever state ONLY — no content replay (replaying `present` would re-trigger a\n * voice clip). Safe to call mid-conversation.\n */\n addChannel(ch: DialogueExtraChannel): () => void {\n this.extras.push(ch);\n // Catch up the host-level levers so a mid-line registration matches state.\n try {\n ch.setVisible?.(!this.hidden);\n } catch (error) {\n this.opts.onError?.(\"dialogue: channel setVisible() failed\", error);\n }\n try {\n ch.setPaused?.(this.paused);\n } catch (error) {\n this.opts.onError?.(\"dialogue: channel setPaused() failed\", error);\n }\n // Idempotent: a second call no-ops (so a host that disposes twice can't\n // double-`dispose()` the channel — the splice already no-ops on re-call).\n let disposed = false;\n return () => {\n if (disposed) return;\n disposed = true;\n const i = this.extras.indexOf(ch);\n if (i >= 0) this.extras.splice(i, 1);\n try {\n ch.dispose?.();\n } catch (error) {\n this.opts.onError?.(\"dialogue: channel dispose() failed\", error);\n }\n };\n }\n\n // ── lifecycle levers ──────────────────────────────────────────────────\n\n /**\n * Hide or show the whole dialogue UI — purely visual, state-preserving.\n * Drives every channel's `setVisible`; the conversation keeps running\n * underneath (reveal, timers, cursor intact). Host-level and **persistent**:\n * it survives `stop()` and the next `play()`, so a cutscene that hides and\n * forgets to unhide stays hidden — call `setHidden(false)` to restore.\n */\n setHidden(hidden: boolean): void {\n this.hidden = hidden;\n this.applyVisibility();\n }\n\n /** True while the UI is hidden via {@link setHidden}. */\n isHidden(): boolean {\n return this.hidden;\n }\n\n /**\n * Freeze or resume the conversation — `update()` no-ops (reveal,\n * auto-advance clock, caret blink, avatar anim all freeze since they are\n * dt-driven) and the input-agnostic API (`advance`/`confirm`/`choose`/\n * `moveSelection`/`selectAt`/`skip`) no-ops. State is left fully intact: no\n * generation bump, and `lineBlocked`/`advancing`/`autoTimer` survive. It does\n * NOT block host-driven writes (`handle.setVar` / `ctx.setVar` / storage) —\n * only player-facing time + input freeze. Host-level and persistent like hide.\n */\n setPaused(paused: boolean): void {\n this.paused = paused;\n // A voice channel pauses its clip here; a CameraEffects channel freezes.\n for (const ch of this.extras) {\n try {\n ch.setPaused?.(paused);\n } catch (error) {\n this.opts.onError?.(\"dialogue: channel setPaused() failed\", error);\n }\n }\n }\n\n /** True while the conversation is frozen via {@link setPaused}. */\n isPaused(): boolean {\n return this.paused;\n }\n\n /**\n * Push the current desired visibility to every channel: a channel shows when\n * it has content for the current mode AND the host hasn't hidden the UI. The\n * single visibility authority — `setHidden` and every line/choice transition\n * route through here, so the host-hidden lever composes cleanly with per-line\n * content and a custom chrome (which may not implement the optional `present`)\n * is still reliably hidden by the explicit `setVisible(false)`.\n */\n private applyVisibility(): void {\n const shown = !this.hidden;\n const saying = this.mode === \"saying\";\n const choosing = this.mode === \"choosing\";\n this.channels.text.setVisible(shown && (saying || (choosing && this.choiceShowsBody)));\n this.channels.choices.setVisible(shown && choosing);\n this.channels.chrome?.setVisible(shown && (saying || (choosing && this.choiceShowsChrome)));\n this.channels.avatar?.setVisible?.(shown && (saying || choosing));\n // Extras track the host-hidden lever directly (they aren't mode-bound\n // content): the whole UI shown/hidden, not per-line visibility.\n for (const ch of this.extras) {\n try {\n ch.setVisible?.(shown);\n } catch (error) {\n this.opts.onError?.(\"dialogue: channel setVisible() failed\", error);\n }\n }\n }\n\n /** Clear every presentation channel's content — text, choices, chrome\n * (nameplate + caret + line), the avatar, and the registered extras. The\n * shared teardown for {@link stop} and the ended state; it touches no session\n * bookkeeping or visibility (the caller resets its own state, then\n * {@link goIdle} reasserts visibility). */\n private clearAllChannels(): void {\n this.channels.text.clear();\n this.channels.choices.clear();\n this.channels.chrome?.setContinueVisible(false);\n this.channels.chrome?.setNameplate(undefined); // clear the name (content), not a hide\n this.channels.chrome?.present?.(undefined); // clear the chrome's line content\n this.channels.avatar?.setSpeaker(undefined);\n this.channels.avatar?.setSpeaking(false);\n this.channels.avatar?.present?.(undefined); // clear any line-driven avatar + its inset\n // Per-conversation reset for the extras (a voice channel stops its clip).\n // clear(), NOT dispose() — extras survive across plays; they're disposed only\n // by the addChannel disposer / controller onDestroy.\n for (const ch of this.extras) {\n try {\n ch.clear?.();\n } catch (error) {\n this.opts.onError?.(\"dialogue: channel clear() failed\", error);\n }\n }\n }\n\n /** Drop to a quiescent presentation state (`mode` \"idle\" or \"ended\"): reset the\n * per-line/choice bookkeeping, clear every channel, and reassert visibility\n * (idle/ended → nothing shown, honestly via each channel's `setVisible`,\n * preserving the host-hidden lever). The caller owns any further reset —\n * {@link stop} also abandons the runner + timing latches. */\n private goIdle(mode: \"idle\" | \"ended\"): void {\n this.mode = mode;\n this.saying = undefined;\n this.resolved = [];\n this.confirming = false;\n this.currentLine = undefined;\n this.currentPresented = undefined;\n this.choiceShowsChrome = false;\n this.choiceShowsBody = false;\n this.clearAllChannels();\n this.applyVisibility();\n }\n\n /** Abandon the current conversation and reset to idle (clears all visuals).\n * Useful for ambient/eavesdrop dialogue that should stop when out of range. */\n stop(): void {\n // Abandon the in-flight runner and timing latches; bumping the generation\n // makes any still-pending async continuation bail on resume.\n this.generation++;\n this.runner = undefined;\n this.autoTimer = undefined;\n this.blockedCount = 0;\n this.advancing = false;\n this.advanceFired = false;\n this.afterRevealFired = false;\n this.goIdle(\"idle\");\n }\n\n update(dt: number): void {\n // Pause freezes everything dt-driven for free: bail before any channel\n // update so the typewriter, caret blink, avatar anim, and the auto-advance\n // clock all halt — and crucially WITHOUT touching lineBlocked/advancing/\n // autoTimer, so state resumes intact.\n if (this.paused) return;\n this.channels.text.update(dt);\n this.channels.chrome?.update(dt);\n this.channels.avatar?.update(dt);\n for (const ch of this.extras) {\n try {\n ch.update?.(dt);\n } catch (error) {\n this.opts.onError?.(\"dialogue: channel update() failed\", error);\n }\n }\n if (!this.runner || this.mode !== \"saying\") return;\n if (this.autoTimer !== undefined && this.allRevealsComplete()) {\n this.autoTimer -= dt;\n // Consume the timer only when advance() can actually act — a blocking\n // line-command (or an in-flight advance) would silently refuse it, and\n // nothing re-arms the timer afterwards (ambient soft-lock). Leave it\n // expired (≤ 0) and retry next frame; it still fires exactly once.\n if (this.autoTimer <= 0 && !this.lineBlocked && !this.advancing) {\n this.autoTimer = undefined;\n this.opts.onAutoAdvance?.({ scriptId: this.scriptId });\n // Route through advance() (not runner.advance() directly) so auto-advance\n // fires the line's `advance`-timed commands and honours the in-flight\n // guards — exactly like a manual advance.\n this.advance();\n }\n }\n }\n\n /** True while any blocking line-command batch is awaited (input is gated). */\n private get lineBlocked(): boolean {\n return this.blockedCount > 0;\n }\n\n /**\n * The auto-advance gate: the text reveal AND every registered extra channel\n * that *gates* (implements `isRevealComplete`) report complete. The clock is\n * armed on the text reveal alone (see `handleRevealComplete`/`setAutoAdvance`)\n * but only counts down once this is true — so a voice clip outlasting the\n * typewriter holds the line for `max(clipEnd, revealEnd)` with no duration\n * plumbing. A channel without the method never gates (a pure observer).\n */\n private allRevealsComplete(): boolean {\n if (!this.channels.text.isRevealComplete()) return false;\n for (const ch of this.extras) {\n if (ch.isRevealComplete && !ch.isRevealComplete()) return false;\n }\n return true;\n }\n\n /**\n * The storage read view for `{token}` interpolation at *this* present-time.\n * Materialized per evaluation so an earlier command's `set` shows up on a\n * later line; already-shown lines never re-render.\n */\n private readView(): VarMap {\n return this.storage ? materialize(this.storage) : {};\n }\n\n // ── input-agnostic API ────────────────────────────────────────────────────\n\n /** Primary action. Saying → reveal-all if typing, else next line. Choosing → confirm. */\n advance(): void {\n if (this.paused) return; // frozen: input is inert\n if (this.lineBlocked || this.advancing) return; // a line-command is in flight\n if (this.mode === \"saying\") {\n if (this.channels.text.isRevealing()) {\n this.channels.text.completeReveal();\n // The player skipped the typewriter — let extras cut in step (a voice\n // channel stops/rings its clip per its onSkip policy).\n for (const ch of this.extras) {\n try {\n ch.completeReveal?.();\n } catch (error) {\n this.opts.onError?.(\"dialogue: channel completeReveal() failed\", error);\n }\n }\n } else void this.advanceLine();\n } else if (this.mode === \"choosing\") {\n this.confirm();\n }\n }\n\n /** Fire any `advance`-timed line commands, then step the runner off the line.\n * `advancing` is held for the whole turn so a second advance can't re-fire\n * the (possibly non-blocking) `advance` commands before the runner steps. */\n private async advanceLine(): Promise<void> {\n const gen = this.generation;\n this.advancing = true;\n try {\n // Fire the line's `advance` batch at most once: the runner can still be\n // mid-step (awaiting a blocking command step) after this advance resolves,\n // with `saying` still holding the old line — a second advance() must not\n // re-fire that stale line's commands.\n if (!this.advanceFired) {\n this.advanceFired = true;\n await this.fireLineCommands(\"advance\");\n }\n // Bail if stop()/play() swapped the conversation while we awaited — `mode`\n // can be \"saying\" again for a *different* runner (e.g. an ambient loop\n // restarting mid blocking-command), which would skip its first line.\n if (gen !== this.generation || this.mode !== \"saying\") return;\n this.runner?.advance();\n } finally {\n if (gen === this.generation) this.advancing = false;\n }\n }\n\n /**\n * Fast-forward the current section: run intervening commands (in skip mode)\n * without presenting, stopping at the next choice or the end. No-op unless a\n * line is showing.\n */\n skip(): void {\n if (this.paused) return; // frozen: input is inert\n if (this.mode !== \"saying\" || this.lineBlocked || this.advancing) return;\n this.opts.onSkipUsed?.({ scriptId: this.scriptId });\n // The player fast-forwarded the section — cut extras in step. Done here (not\n // only at the next present) so a skip landing on a choice / the end, where no\n // present supersedes the clip, still cuts a voice channel immediately.\n for (const ch of this.extras) {\n try {\n ch.completeReveal?.();\n } catch (error) {\n this.opts.onError?.(\"dialogue: channel completeReveal() failed\", error);\n }\n }\n void this.skipLine();\n }\n\n /**\n * Fire the *displayed* line's not-yet-fired batches in skip mode — the runner\n * fires every skipped line's commands for world reconstruction, so dropping\n * the current line's `afterReveal`/`advance` batches would diverge from\n * normal play — then fast-forward the runner.\n */\n private async skipLine(): Promise<void> {\n const gen = this.generation;\n this.advancing = true; // gate input for the whole skip turn\n try {\n if (!this.afterRevealFired) {\n this.afterRevealFired = true;\n await this.fireLineCommands(\"afterReveal\", \"skip\");\n if (gen !== this.generation || this.mode !== \"saying\") return;\n }\n if (!this.advanceFired) {\n this.advanceFired = true;\n await this.fireLineCommands(\"advance\", \"skip\");\n if (gen !== this.generation || this.mode !== \"saying\") return;\n }\n await this.runner?.skip();\n } finally {\n if (gen === this.generation) this.advancing = false;\n }\n }\n\n /**\n * Side-effect-free lookahead: the lines a node would show along its linear\n * path — following `goto` and conditional `command` jumps using the *current*\n * variable snapshot — stopping at the first choice or the end. Runs no\n * commands and mutates nothing. For a \"skip with a summary\" affordance.\n */\n preview(nodeId: string, limit = 64): PreviewedLine[] {\n const script = this.script;\n const storage = this.storage;\n if (!script || !storage) return [];\n // Interpolation reads a materialized snapshot; conditions evaluate through a\n // scope (per-name reads + functions). Both off the current storage, for this\n // side-effect-free lookahead.\n const view = materialize(storage);\n const scope = createScope(storage, this.functions);\n const out: PreviewedLine[] = [];\n let node = nodeId;\n let i = 0;\n for (let guard = 0; guard < limit; guard++) {\n const step = script.nodes[node]?.steps[i];\n if (!step) break;\n if (step.kind === \"say\") {\n const speaker = step.speaker\n ? script.speakers?.[step.speaker]\n : undefined;\n const name = speaker\n ? this.i18n.t(speaker.nameKey, speaker.name, view)\n : undefined;\n out.push({\n speaker: name,\n text: stripMarkup(this.i18n.t(step.key, step.text, view)),\n });\n i++;\n } else if (step.kind === \"command\") {\n if (step.target !== undefined && holds(step.condition, scope)) {\n node = step.target;\n i = 0;\n } else {\n i++;\n }\n } else if (step.kind === \"goto\") {\n node = step.target;\n i = 0;\n } else {\n break; // choice or end — stop the linear preview\n }\n }\n return out;\n }\n\n /** Move the choice cursor by `delta`, skipping disabled rows and wrapping.\n * No-op outside a choice, and a zero `delta` is a no-op (no cursor move, no\n * event). A move that steps over disabled rows fires exactly one\n * selection-changed event, for the row it lands on. */\n moveSelection(delta: number): void {\n if (this.paused) return; // frozen: input is inert\n if (this.mode !== \"choosing\" || this.confirming) return;\n if (this.resolved.length === 0 || delta === 0) return;\n const dir: 1 | -1 = delta < 0 ? -1 : 1;\n let pos = this.selected;\n for (let i = 0; i < Math.abs(delta); i++) {\n pos = this.nextEnabled(pos, dir);\n }\n if (pos === this.selected) return; // no enabled row to move to — no event\n this.selected = pos;\n this.channels.choices.highlight(this.selected);\n this.emitSelectionChanged();\n }\n\n /** Highlight a choice by absolute position (e.g. pointer hover). No wrap;\n * a disabled row is skipped (the cursor stays put). */\n selectAt(position: number): void {\n if (this.paused) return; // frozen: input is inert\n if (this.mode !== \"choosing\" || this.confirming) return;\n const n = this.resolved.length;\n if (n === 0 || position < 0 || position >= n || position === this.selected)\n return;\n if (this.resolved[position]?.disabled) return; // can't highlight a disabled row\n this.selected = position;\n this.channels.choices.highlight(this.selected);\n this.emitSelectionChanged();\n }\n\n /** The next enabled choice position from `from` in direction `dir` (±1),\n * wrapping. Returns `from` when no other enabled row exists (so a single\n * enabled option among disabled ones never moves). */\n private nextEnabled(from: number, dir: 1 | -1): number {\n const n = this.resolved.length;\n let pos = from;\n for (let i = 0; i < n; i++) {\n pos = (pos + dir + n) % n;\n if (pos === from) break; // wrapped all the way around\n if (!this.resolved[pos]?.disabled) return pos;\n }\n return from;\n }\n\n /** Fire onSelectionChanged for the currently-highlighted choice (keyboard nav\n * and pointer hover both land here — one canonical selection event). */\n private emitSelectionChanged(): void {\n const chosen = this.resolved[this.selected];\n if (!chosen) return;\n this.opts.onSelectionChanged?.({\n index: chosen.index,\n text: stripMarkup(\n this.i18n.t(chosen.option.key, chosen.option.text, this.readView()),\n ),\n });\n }\n\n /** Commit the highlighted choice. */\n confirm(): void {\n this.commit(this.selected);\n }\n\n /** Commit by original option index (e.g. a direct pointer hit, or the\n * timed-choice recipe firing its default). Refuses an unknown or disabled\n * option. */\n choose(optionIndex: number): void {\n this.commit(this.resolved.findIndex((c) => c.index === optionIndex));\n }\n\n /** Commit by display position (a pointer hit on a row). The position-keyed\n * counterpart to {@link choose}; refuses an out-of-range or disabled row.\n * Used by the pointer binding and the presenter pointer-commit seam. */\n confirmAt(position: number): void {\n this.commit(position);\n }\n\n /**\n * The single choice-commit authority: every commit path routes here after\n * translating its argument to a display position. It guards (paused / not\n * choosing / already confirming / a missing or disabled row), latches against a\n * double-commit, fires `onChoiceMade`, and steps the runner. The latch (not the\n * runner) is what guarantees a single commit: `mode` stays \"choosing\" while the\n * runner awaits the option's blocking commands, so without it a second confirm\n * would emit a duplicate `onChoiceMade`.\n */\n private commit(position: number): void {\n if (this.paused) return; // frozen: input is inert\n if (this.mode !== \"choosing\" || this.confirming) return;\n const chosen = this.resolved[position];\n if (!chosen || chosen.disabled) return; // never commit a missing or disabled row\n this.selected = position;\n this.confirming = true;\n const text = this.i18n.t(chosen.option.key, chosen.option.text, this.readView());\n this.opts.onChoiceMade?.({ index: chosen.index, text });\n this.runner?.choose(chosen.index);\n }\n\n /** Toggle hold-to-fast-forward; the text channel scales its reveal rate. */\n setFastForward(on: boolean): void {\n this.channels.text.setSpeedMultiplier(on ? this.skipMul : 1);\n }\n\n /**\n * Default auto-advance: lines without their own `autoAdvanceMs` advance `ms`\n * after they finish revealing; `null` turns it off (manual advance). A\n * per-line `autoAdvanceMs` always overrides this. Toggling it while a line is\n * already sitting revealed arms/clears its timer immediately.\n */\n setAutoAdvance(ms: number | null): void {\n this.autoAdvanceDefault = ms;\n if (\n this.mode === \"saying\" &&\n this.saying?.autoAdvanceMs === undefined &&\n this.channels.text.isRevealComplete()\n ) {\n this.autoTimer = ms ?? undefined;\n }\n }\n\n // ── runner handlers ─────────────────────────────────────────────────────\n\n private handleSay(step: SayStep, speaker: SpeakerDef | undefined): void {\n this.mode = \"saying\";\n this.saying = step;\n this.autoTimer = undefined;\n this.advanceFired = false;\n this.afterRevealFired = false;\n this.confirming = false;\n // Materialize the read view once for this line (an external getter fires\n // exactly once per present, shared by text + nameplate).\n const view = this.readView();\n const resolved = this.i18n.t(step.key, step.text, view);\n const line: PresentedLine = {\n speaker: this.speakerView(speaker, view),\n text: parseMarkup(resolved),\n speed: step.speed ?? 1,\n view: step.view,\n meta: step.meta,\n voice: step.voice,\n };\n\n this.channels.choices.clear();\n this.channels.chrome?.setContinueVisible(false);\n this.channels.chrome?.setNameplate(\n this.speakerName(speaker, view),\n speaker?.color,\n );\n\n this.channels.avatar?.setSpeaker(speaker);\n this.channels.avatar?.setExpression(step.expression);\n this.channels.avatar?.setSpeaking(true);\n // Line-driven avatars read meta here. Before chrome/text present so a\n // reflowing in-box avatar can register its text inset first (the text view\n // then wraps to the narrowed region).\n this.channels.avatar?.present?.(line);\n\n this.channels.chrome?.present?.(line);\n this.channels.text.present(line);\n\n // Fan the say line out to the extras — a voice channel reads `line.voice`\n // and starts its clip here; a history channel buffers it. After the text\n // channel, before the line's `show` commands. NOT done for choice prompts\n // (only say lines carry a voice / a reveal to gate on).\n this.currentPresented = line;\n for (const ch of this.extras) {\n try {\n ch.present?.(line);\n } catch (error) {\n this.opts.onError?.(\"dialogue: channel present() failed\", error);\n }\n }\n\n const plain = stripMarkup(resolved);\n this.currentLine = { speaker: this.speakerName(speaker, view), text: plain };\n this.opts.onLine?.({ speaker: this.currentLine.speaker, text: plain });\n\n // A saying line shows the chrome + body text (gated by the host-hidden lever).\n this.applyVisibility();\n\n // Line commands fire by timing (the runner leaves them to us in play mode).\n void this.fireLineCommands(\"show\");\n }\n\n private handleChoice(\n step: ChoiceStep,\n choices: readonly ResolvedChoice[],\n speaker: SpeakerDef | undefined,\n ): void {\n this.mode = \"choosing\";\n this.resolved = choices;\n // Start on the first ENABLED row. The runner skips a step with zero\n // selectable rows, so one always exists — assert it rather than silently\n // masking a regression (a -1 would seed the highlight on a disabled row).\n const firstEnabled = choices.findIndex((c) => !c.disabled);\n if (firstEnabled < 0) {\n throw new Error(\"dialogue: choice step presented with no enabled option\");\n }\n this.selected = firstEnabled;\n this.confirming = false;\n const view = this.readView();\n\n // Treat the choice like a line so the chrome switches to the right variant\n // (a composite box/bubble chrome otherwise leaves the previous speaker's\n // bubble up behind a frameless choice list).\n const line: PresentedLine = {\n speaker: this.speakerView(speaker, view),\n text: step.text\n ? parseMarkup(this.i18n.t(step.key, step.text, view))\n : EMPTY_PARSED,\n speed: 1,\n view: step.view,\n meta: step.meta,\n };\n\n // Drive the avatar like a say-line would: the choice's speaker owns the\n // portrait (a stale say-speaker must not linger through the choice). No\n // expression of its own and no talk-state — choices don't reveal.\n this.channels.avatar?.setSpeaker(speaker);\n this.channels.avatar?.setExpression(undefined);\n this.channels.avatar?.setSpeaking(false);\n // Line-driven avatars read the choice line's meta too (before the body\n // prompt presents, so an in-box avatar's inset reflows the prompt).\n this.channels.avatar?.present?.(line);\n\n const ctx: ChoiceContext = {\n view: step.view,\n speaker: line.speaker,\n prompt: line.text,\n meta: step.meta,\n };\n // A self-contained presenter (e.g. a bubble panel) draws its own frame +\n // prompt; then we hide the chrome and don't type the prompt into the body.\n const presenterOwnsPrompt =\n this.channels.choices.ownsPrompt?.(ctx) ?? false;\n // Remembered so a hide toggle mid-choice recomputes the right visibility.\n // The body shows only when the prompt actually types into it — matching the\n // `if (step.text)` present/clear branch below (an empty prompt clears it).\n this.choiceShowsChrome = !presenterOwnsPrompt;\n this.choiceShowsBody = !presenterOwnsPrompt && Boolean(step.text);\n\n this.channels.chrome?.setContinueVisible(false);\n if (presenterOwnsPrompt) {\n // Clear the chrome's content honestly (no covert nameplate overload); the\n // explicit setVisible(false) from applyVisibility does the actual hide.\n this.channels.chrome?.present?.(undefined);\n this.channels.text.clear();\n } else {\n // Always set (undefined when no speaker) so a stale nameplate doesn't linger.\n this.channels.chrome?.setNameplate(\n this.speakerName(speaker, view),\n speaker?.color,\n );\n this.channels.chrome?.present?.(line);\n // Prompt (optional) types into the body region above the options.\n if (step.text) this.channels.text.present(line);\n else this.channels.text.clear();\n }\n\n const labels = choices.map((c) =>\n stripMarkup(this.i18n.t(c.option.key, c.option.text, view)),\n );\n const presented: PresentedChoice[] = choices.map((c, i) => ({\n label: labels[i]!,\n meta: c.option.meta,\n disabled: c.disabled,\n // i18n-resolve the reason (interpolating {tokens}) only for disabled rows\n // that carry one; there's no separate i18n key for it.\n disabledReason:\n c.disabled && c.option.disabledReason !== undefined\n ? stripMarkup(this.i18n.t(undefined, c.option.disabledReason, view))\n : undefined,\n }));\n this.channels.choices.present(presented, ctx);\n this.channels.choices.highlight(this.selected);\n this.applyVisibility();\n this.opts.onChoiceShown?.({ options: labels });\n }\n\n private handleCommand(\n command: Command,\n ctx: CommandContext,\n ): void | Promise<void> {\n // Observation first (the host emits DialogueCommandEvent); then the binding's\n // handler does the actual work. Returning its (possibly async) result lets a\n // blocking command pause the runner until the game finishes handling it.\n // validateBinding guarantees a handler/fallback exists for every command type.\n // The Session does NO channel-specific name-matching: a mid-line face change\n // is the `[expression=…/]` reveal marker (read by the avatar channel), not a\n // command.\n this.opts.onCommand?.(command, ctx);\n // Fan the command out to extras (a shop channel reacts to `buy` here). `set`\n // is runner-owned and never reaches this handler.\n for (const ch of this.extras) {\n try {\n ch.command?.(command, ctx);\n } catch (error) {\n this.opts.onError?.(\"dialogue: channel command() failed\", error);\n }\n }\n const handler = this.commands[command.type] ?? this.fallbackCommand;\n return handler?.(command, ctx);\n }\n\n private handleEnd(): void {\n this.goIdle(\"ended\");\n this.opts.onEnded?.({ scriptId: this.scriptId });\n }\n\n private async handleRevealComplete(): Promise<void> {\n if (this.mode !== \"saying\") return;\n const gen = this.generation;\n this.channels.avatar?.setSpeaking(false);\n // `afterReveal` commands run before the continue caret invites advancing.\n // At most once per line — skip() may have fired them early (while a\n // blocking one was awaited, the typewriter can have finished underneath).\n if (!this.afterRevealFired) {\n this.afterRevealFired = true;\n await this.fireLineCommands(\"afterReveal\");\n }\n // Bail if stop()/play() swapped the conversation while we awaited, else we'd\n // show the continue caret on the new conversation's still-revealing line.\n if (gen !== this.generation || this.mode !== \"saying\") return;\n this.channels.chrome?.setContinueVisible(true);\n // The \"typing finished\" hook — after afterReveal commands settle, as the\n // continue caret appears. Carries the line so a game needn't track it.\n if (this.currentLine) this.opts.onRevealCompleted?.(this.currentLine);\n // Fan reveal-completion out to extras (a history channel commits the line\n // once it's fully shown). The PresentedLine, since the plain currentLine\n // dropped the markup/meta.\n const presented = this.currentPresented;\n if (presented) {\n for (const ch of this.extras) {\n try {\n ch.revealComplete?.(presented);\n } catch (error) {\n this.opts.onError?.(\"dialogue: channel revealComplete() failed\", error);\n }\n }\n }\n // Per-line `autoAdvanceMs` wins; otherwise fall back to the session default.\n const auto = this.saying?.autoAdvanceMs ?? this.autoAdvanceDefault;\n if (auto !== null) this.autoTimer = auto;\n }\n\n /**\n * Fan one reveal beat out — the per-line typewriter stream. Extras (Voice /\n * CameraEffects / a typewriter-SFX channel) see the WHOLE stream via\n * `revealBeat?`. A `tick` then reaches the host's `onRevealTick` callback; a\n * `marker` reaches the avatar channel (which interprets `[expression=…/]`\n * itself — the Session name-matches nothing) and the host's `onRevealMarker`.\n */\n private handleRevealBeat(beat: RevealBeat): void {\n for (const ch of this.extras) {\n try {\n ch.revealBeat?.(beat);\n } catch (error) {\n this.opts.onError?.(\"dialogue: channel revealBeat() failed\", error);\n }\n }\n if (beat.kind === \"tick\") {\n this.opts.onRevealTick?.(beat.index);\n return;\n }\n // marker: the avatar reads the names it owns; the host gets every one.\n this.channels.avatar?.marker?.(beat.marker);\n this.opts.onRevealMarker?.(beat.marker, beat.viaSkip);\n }\n\n /**\n * Fire the current line's commands matching `at`, via the runner's command\n * pipeline (so `set`/blocking behave identically). While a blocking one is\n * awaited, `lineBlocked` gates input so the player can't advance through it.\n * `mode` overrides the runner's run mode (skip() fires the displayed line's\n * batches in skip mode).\n */\n private async fireLineCommands(at: CommandTiming, mode?: RunMode): Promise<void> {\n const all = this.saying?.commands;\n if (!all || !this.runner) return;\n const batch = all.filter((c) => (c.at ?? \"show\") === at);\n if (batch.length === 0) return;\n const gen = this.generation;\n const blocking = batch.some((c) => c.blocking);\n if (blocking) this.blockedCount++;\n try {\n await this.runner.runCommands(batch, mode);\n } finally {\n // Release only the gate this batch took, and only if still the same\n // conversation — stop()/play() mid-await already reset the counter for\n // the new conversation.\n if (blocking && gen === this.generation) this.blockedCount--;\n }\n }\n\n private speakerName(\n speaker: SpeakerDef | undefined,\n view: VarMap,\n ): string | undefined {\n if (!speaker) return undefined;\n return this.i18n.t(speaker.nameKey, speaker.name, view);\n }\n\n private speakerView(\n speaker: SpeakerDef | undefined,\n view: VarMap,\n ): SpeakerView | undefined {\n if (!speaker) return undefined;\n return {\n id: speaker.id,\n name: this.speakerName(speaker, view),\n color: speaker.color,\n };\n }\n}\n","/**\n * Voice-over as a registered {@link DialogueExtraChannel}. It plays a line's\n * `voice` clip and gates auto-advance until the clip ends — so a line\n * auto-advances at `max(clipEnd, revealEnd)` with no duration plumbing, and a\n * short line never moves on while its long clip is still talking.\n *\n * The addon owns **no audio**: the host supplies `play` (wired over\n * `@yagejs/audio` in the game), which starts a clip and returns a handle. This\n * module imports neither audio nor a renderer, so it stays on the pixi-free root\n * entry alongside the rest of the headless model.\n */\n\nimport type { DialogueExtraChannel } from \"./types.js\";\nimport type { PresentedLine } from \"../session.js\";\n\n/**\n * The host-owned playback handle returned by {@link VoiceChannelOptions.play}.\n * `pause` / `resume` are optional — a host that can't pause a clip degrades to a\n * no-op (the conversation still pauses; the clip just keeps playing).\n */\nexport interface VoiceHandle {\n /** Stop the clip immediately and release it. */\n stop(): void;\n /** Pause playback (optional). */\n pause?(): void;\n /** Resume playback (optional). */\n resume?(): void;\n}\n\nexport interface VoiceChannelOptions {\n /**\n * Start the clip for `id` (the line's `voice`) and return a {@link VoiceHandle}.\n * Call `onEnded` when the clip finishes **naturally** — that releases the\n * auto-advance gate. The addon imports no audio; a YAGE host wires this over\n * `@yagejs/audio`, e.g.\n *\n * play: (id, onEnded) => {\n * const sound = audio.play(id, { onEnd: onEnded });\n * return { stop: () => sound.stop(), pause: () => sound.pause(), resume: () => sound.resume() };\n * }\n */\n play(id: string, onEnded: () => void): VoiceHandle;\n /**\n * What a skip does to a still-playing clip. `\"cut\"` (default) stops it and\n * releases the gate the moment the player completes the typewriter or\n * fast-forwards the section; `\"ring\"` lets the clip play out — auto-advance\n * keeps waiting for `onEnded`, a manual advance still works (it is never gated).\n */\n onSkip?: \"cut\" | \"ring\";\n /**\n * Pause the clip when the conversation pauses ({@link DialogueSession.setPaused}).\n * Default `true` — a paused conversation stops talking, the least-surprising\n * default now that the channel knows a clip is mid-flight. Set `false` to let a\n * clip play through a pause. `pause` / `resume` on the handle are optional, so\n * this is a no-op when the host omits them.\n */\n pauseWithConversation?: boolean;\n /**\n * Safety budget (ms). If a clip's `onEnded` never arrives within this many ms\n * of starting, the gate is force-released and {@link onError} is called — so a\n * wedged host (a clip that silently fails to report its end) can't soft-lock\n * auto-advance. Omit (or `0`) to disable the cap.\n */\n livenessMs?: number;\n /** Diagnostics sink for the liveness cap — route it to the engine logger, the\n * same seam as the session's `onError`. */\n onError?: (message: string, error: unknown) => void;\n}\n\n/**\n * Build a voice-over {@link DialogueExtraChannel}. Register it on a controller:\n *\n * const voice = createVoiceChannel({ play: (id, onEnded) => host.playClip(id, onEnded) });\n * controller.addChannel(voice);\n *\n * Hardened against two real failure modes:\n * - **generation guard** — a late `onEnded` from a clip that has since been\n * superseded (the next line started) can't ungate the new line.\n * - **liveness cap** — an optional budget force-releases the gate if a clip\n * never reports its end, so the conversation can't soft-lock.\n *\n * On a mid-line save/restore the host re-presents the current line, so `present`\n * fires again here — it stops any active clip first, so a restore restarts the\n * line's clip cleanly (the restore-safety property).\n */\nexport function createVoiceChannel(opts: VoiceChannelOptions): DialogueExtraChannel {\n const { play, onSkip = \"cut\", livenessMs, onError } = opts;\n const pauseClip = opts.pauseWithConversation ?? true;\n\n let active: VoiceHandle | undefined;\n // `done` = the gate is satisfied (no clip in flight). Starts satisfied so a\n // line without `voice` never gates auto-advance.\n let done = true;\n // Bumped on every (re)present; a clip's `onEnded` captures the token at start\n // and no-ops if it no longer matches — a superseded clip can't ungate the line\n // that replaced it.\n let startToken = 0;\n // ms the current clip has been playing (liveness cap only). Frozen while\n // paused for free: the session fans `update(dt)` only when not paused.\n let elapsed = 0;\n\n const stop = (): void => {\n active?.stop();\n active = undefined;\n done = true;\n };\n\n return {\n present(line: PresentedLine): void {\n active?.stop();\n active = undefined;\n startToken++;\n elapsed = 0;\n const id = line.voice;\n if (id === undefined) {\n done = true; // no clip → nothing to wait for\n return;\n }\n done = false;\n const token = startToken;\n try {\n active = play(id, () => {\n if (token !== startToken) return; // generation guard: a superseded clip\n done = true;\n active = undefined;\n });\n } catch (error) {\n // The host's play() threw before returning a handle — no clip started, so\n // release the gate (this line can't soft-lock waiting on a clip that never\n // ran) and surface the failure (the session's present fan-out routes it to\n // onError).\n done = true;\n throw error;\n }\n },\n completeReveal(): void {\n if (onSkip === \"ring\") return; // let the clip ring out; the gate holds\n stop();\n },\n setPaused(paused: boolean): void {\n if (!pauseClip) return;\n if (paused) active?.pause?.();\n else active?.resume?.();\n },\n update(dt: number): void {\n // `done` already covers every no-clip case (voiceless line, natural end,\n // clear, a throwing play()); only an in-flight clip (done=false) ticks the\n // budget. An `active === undefined` guard here would wrongly freeze the\n // timer if play() ever left `active` unset.\n if (done || !livenessMs) return;\n elapsed += dt;\n if (elapsed >= livenessMs) {\n // The host never reported the clip's end — release the gate so\n // auto-advance can't soft-lock. Leave the (wedged) handle in place: a\n // later present()/clear() still stops it, and its late onEnded is\n // token-guarded.\n done = true;\n onError?.(\n `voice clip exceeded the ${livenessMs}ms liveness budget without ` +\n `reporting its end; releasing the auto-advance gate`,\n new Error(\"voice clip liveness cap\"),\n );\n }\n },\n clear(): void {\n stop();\n },\n dispose(): void {\n stop();\n },\n isRevealComplete(): boolean {\n return done;\n },\n };\n}\n","/**\n * DialogueController — the thin YAGE host. It owns no dialogue logic; it just:\n *\n * • mounts the presenters onto the scene,\n * • builds a headless {@link DialogueSession} over them,\n * • forwards the session's observation callbacks to engine events,\n * • attaches an {@link InputBinding} (keyboard by default) and pumps it,\n * • pumps `session.update(dt)` each frame.\n *\n * All the sequencing, reveal-gating, choice book-keeping, and i18n live in the\n * session (engine-agnostic). The presenter bundle usually comes from a factory\n * (`createBoxDialogue(theme)`), spread-and-overridden as needed:\n *\n * host.add(new DialogueController({ ...createBoxDialogue(theme), avatar, storage }));\n */\n\nimport { Component, LoggerKey, type Logger } from \"@yagejs/core\";\nimport { InputManagerKey } from \"@yagejs/input\";\nimport {\n DialogueSession,\n type CommandHandler,\n type DialogueExtraChannel,\n type DialogueFunction,\n type DialogueHandle,\n type DialoguePlayOptions,\n type DialogueScript,\n type I18nAdapter,\n type PreviewedLine,\n type VariableStorage,\n type VarsOf,\n} from \"./core/index.js\";\nimport type {\n ChromePresenter,\n ChoicePresenter,\n Mountable,\n TextPresenter,\n} from \"./chrome/DialogueUiAdapter.js\";\nimport type { AvatarPresenter } from \"./avatar/AvatarPresenter.js\";\nimport { KeyboardInputBinding, type InputBinding } from \"./input/index.js\";\nimport {\n DialogueAutoAdvanceEvent,\n DialogueChoiceMadeEvent,\n DialogueChoiceShownEvent,\n DialogueCommandEvent,\n DialogueEndedEvent,\n DialogueLineEvent,\n DialogueRevealCompletedEvent,\n DialogueRevealMarkerEvent,\n DialogueSelectionChangedEvent,\n DialogueSkipUsedEvent,\n DialogueStartedEvent,\n} from \"./events.js\";\n\n/** The presenter trio a factory assembles (see `createBoxDialogue`).\n * Optional fields are `T | undefined` so factories and games can assign\n * possibly-undefined theme values directly (exactOptionalPropertyTypes). */\nexport interface DialogueBundle {\n readonly chrome: ChromePresenter;\n readonly text: TextPresenter;\n readonly choices: ChoicePresenter;\n readonly avatar?: AvatarPresenter | undefined;\n /** Hold-to-fast-forward multiplier. Default 4. */\n readonly skipMultiplier?: number | undefined;\n}\n\nexport interface DialogueControllerOptions<TStorage extends VariableStorage = VariableStorage>\n extends DialogueBundle {\n readonly i18n?: I18nAdapter | undefined;\n /**\n * The variable storage installed for every `play()`. Persists across\n * plays. Omit for a zero-config `MemoryVariableStorage`; supply your own (or\n * `compose(cells(...), new MemoryVariableStorage())`) to bridge game state. A\n * per-`play()` `overrides.storage` replaces it for that conversation.\n */\n readonly storage?: TStorage | undefined;\n /** Argument-capable read functions (`has_item(\"key\")`) shared across plays. */\n readonly functions?: Readonly<Record<string, DialogueFunction>> | undefined;\n /** Command handlers (`type` → handler) shared across plays; per-`play()`\n * `overrides.commands` merge on top (call site wins). */\n readonly commands?: Readonly<Record<string, CommandHandler>> | undefined;\n /** Catch-all for command types with no explicit handler. */\n readonly fallbackCommand?: CommandHandler | undefined;\n /** Device → session binding. Omit for the default keyboard binding. */\n readonly input?: InputBinding;\n /**\n * Extra channels registered on the session at mount (Voice / Shop /\n * CameraEffects / History) — the open-ended companion to the presenter trio.\n * Each is wired via {@link DialogueController.addChannel}; one that also\n * implements {@link Mountable} (it needs the scene) is mounted in `onAdd` and\n * disposed in `onDestroy`. A factory bundle can pre-wire e.g. a voice channel\n * here; a game can also add one live with {@link DialogueController.addChannel}.\n */\n readonly channels?: readonly DialogueExtraChannel[] | undefined;\n /**\n * Per-grapheme typewriter tick — a direct callback (NOT an entity event; it\n * fires hundreds of times per line). `index` is the raw grapheme index revealed\n * (whitespace included — filter if you only want a blip on visible glyphs).\n * Wire a typewriter SFX here. Inline `[name k=v/]` markers, by contrast, come\n * through {@link DialogueRevealMarkerEvent} on the entity bus.\n */\n readonly onRevealTick?: ((index: number) => void) | undefined;\n /** Called once when a conversation ends (in addition to the scene event). */\n readonly onEnded?: () => void;\n}\n\nexport class DialogueController<\n TStorage extends VariableStorage = VariableStorage,\n> extends Component {\n private readonly input = this.service(InputManagerKey);\n private readonly binding: InputBinding;\n private session!: DialogueSession;\n /** Captured at onAdd (the scene is gone by the time a stale play() arrives). */\n private logger: Logger | undefined;\n /** Set by onDestroy — the presenters are disposed, so play() must refuse. */\n private destroyed = false;\n /** Input focus. When false, `update()` keeps pumping the session (an\n * ambient conversation stays alive) but the binding is NOT polled, so this\n * instance doesn't consume device input. NOT `Component.enabled` (which would\n * also freeze the session). */\n private inputEnabled = true;\n /** Pause. Mirrors the session's pause so the binding poll is also gated\n * while frozen — a paused conversation neither updates nor consumes input.\n * Also the source of truth re-applied to the session in `onAdd` when a host\n * set it before the component was added (the session didn't exist yet). */\n private paused = false;\n /** Hidden. Mirrors the session's hide so a `setHidden` issued before the\n * component was added isn't lost — it's re-applied once the session exists. */\n private hidden = false;\n /** Disposers for every registered extra channel (ctor `channels` + live\n * `addChannel`). `onDestroy` runs them all — each idempotent — to unregister\n * and dispose (unmounting the Mountable ones). */\n private readonly channelDisposers = new Set<() => void>();\n\n constructor(private readonly opts: DialogueControllerOptions<TStorage>) {\n super();\n this.binding = opts.input ?? new KeyboardInputBinding();\n }\n\n onAdd(): void {\n this.logger = this.context.tryResolve(LoggerKey);\n // One diagnostics seam shared by the session's onError AND presenter-level\n // warnings (e.g. a missing actor) — both land on the engine Logger, never\n // console.warn.\n const warn = (message: string): void => this.logger?.warn(\"dialogue\", message);\n\n this.opts.chrome.mount(this.scene);\n this.opts.choices.mount(this.scene);\n this.opts.avatar?.mount(this.scene);\n this.opts.text.mount(this.scene);\n\n // Inject the diagnostics sink into any presenter that reports through it.\n this.opts.chrome.setDiagnostics?.(warn);\n this.opts.text.setDiagnostics?.(warn);\n this.opts.choices.setDiagnostics?.(warn);\n\n this.session = new DialogueSession(\n {\n text: this.opts.text,\n choices: this.opts.choices,\n avatar: this.opts.avatar,\n chrome: this.opts.chrome,\n },\n {\n i18n: this.opts.i18n,\n skipMultiplier: this.opts.skipMultiplier,\n // Controller-installed environment — persists across plays.\n storage: this.opts.storage,\n functions: this.opts.functions,\n commands: this.opts.commands,\n fallbackCommand: this.opts.fallbackCommand,\n onStarted: (e) => this.entity.emit(DialogueStartedEvent, e),\n onLine: (e) => this.entity.emit(DialogueLineEvent, e),\n onChoiceShown: (e) =>\n this.entity.emit(DialogueChoiceShownEvent, { options: e.options }),\n onChoiceMade: (e) => this.entity.emit(DialogueChoiceMadeEvent, e),\n // Observation only — the `commands` map does the work; this mirrors\n // every non-built-in command onto the scene event bus.\n onCommand: (command, ctx) =>\n this.entity.emit(DialogueCommandEvent, { command, mode: ctx.mode }),\n // Route non-fatal runtime diagnostics (e.g. a `set` to a read-only cell)\n // through the engine logger rather than crashing or silently dropping.\n onError: (message) => warn(message),\n onEnded: (e) => {\n this.entity.emit(DialogueEndedEvent, e);\n this.opts.onEnded?.();\n },\n // Observation events — the controller is the one canonical path that\n // turns the session's callbacks into entity→scene events (no matching\n // controller callback options).\n onRevealCompleted: (e) => this.entity.emit(DialogueRevealCompletedEvent, e),\n onSelectionChanged: (e) => this.entity.emit(DialogueSelectionChangedEvent, e),\n onSkipUsed: (e) => this.entity.emit(DialogueSkipUsedEvent, e),\n onAutoAdvance: (e) => this.entity.emit(DialogueAutoAdvanceEvent, e),\n // Inline markers fan to an entity event; per-grapheme ticks stay a direct\n // callback (forwarded verbatim — undefined when the host wires none).\n onRevealMarker: (marker, viaSkip) =>\n this.entity.emit(DialogueRevealMarkerEvent, { marker, viaSkip }),\n onRevealTick: this.opts.onRevealTick,\n },\n );\n this.binding.bind(this.input, this.session);\n\n // Re-apply any lifecycle lever a host set BEFORE the component was added:\n // setPaused/setHidden could only update the controller mirror back then (no\n // session existed), so sync the freshly-created session to match. Without\n // this, a \"configure then add\" host gets a half-applied state (e.g. paused\n // input but a still-ticking reveal). setInputEnabled needs no sync — it's\n // controller-only and read live in update().\n if (this.paused) this.session.setPaused(true);\n if (this.hidden) this.session.setHidden(true);\n\n // Register any pre-wired extra channels (a factory bundle can include a\n // voice channel). Same path as a live addChannel: mount the scene-needing\n // ones, hand each to the session, and track its disposer for onDestroy.\n for (const ch of this.opts.channels ?? []) this.addChannel(ch);\n }\n\n onDestroy(): void {\n this.destroyed = true;\n // Stop first: bumps the session generation so an in-flight blocking-command\n // continuation bails instead of presenting onto presenters we're about to\n // dispose; also clears visuals (and fans clear() to the extras) while valid.\n this.session?.stop();\n // Tear down every registered extra channel — unregister + dispose, which\n // unmounts the Mountable ones. A snapshot copy: each disposer mutates the set.\n for (const dispose of [...this.channelDisposers]) dispose();\n this.binding.dispose?.();\n this.opts.text.dispose();\n this.opts.choices.dispose();\n this.opts.chrome.dispose();\n this.opts.avatar?.dispose();\n }\n\n /**\n * Begin a conversation. `play(script)` is **content-only** — storage,\n * functions, and commands are installed on the controller. `overrides` layers\n * per-conversation specifics on top (a scoped `storage`, extra\n * `functions`/`commands`). Returns a {@link DialogueHandle} for live `setVar` /\n * `getVars`, or `undefined` if the controller was removed.\n */\n play<S extends DialogueScript>(\n script: S,\n overrides?: DialoguePlayOptions,\n ): DialogueHandle<VarsOf<S>> | undefined {\n // A stale reference calling play() after the component was removed (e.g. a\n // game keeping the controller in an interact closure past\n // `DialogueEndedEvent → host.destroy()`) would run a new conversation into\n // disposed presenters: orphan entities, frozen reveal, no error. Refuse.\n if (this.destroyed) {\n this.logger?.warn(\n \"dialogue\",\n \"DialogueController.play() ignored: the component has been removed/destroyed.\",\n { scriptId: script.id },\n );\n return undefined;\n }\n if (!this.session) {\n throw new Error(\n \"DialogueController.play() called before the component was added to an entity (onAdd has not run yet).\",\n );\n }\n return this.session.play(script, overrides);\n }\n\n isActive(): boolean {\n return this.session?.isActive() ?? false;\n }\n\n /** Abandon the current conversation and reset to idle. */\n stop(): void {\n this.session?.stop();\n }\n\n /**\n * Register an extra channel live — Voice / Shop / CameraEffects / History.\n * Mounts it if it needs the scene ({@link Mountable}), hands it to the session\n * (where it joins the cross-cutting stream and can gate auto-advance), and\n * returns a disposer that unregisters + disposes it. The `channels` ctor option\n * registers a bundle the same way at mount. Returns a no-op disposer if the\n * controller was destroyed; **throws** if called before the component is added\n * to an entity (use the `channels` ctor option to pre-wire a channel) — mirrors\n * {@link play}.\n */\n addChannel(ch: DialogueExtraChannel): () => void {\n if (this.destroyed) {\n this.logger?.warn(\n \"dialogue\",\n \"DialogueController.addChannel() ignored: the component has been removed/destroyed.\",\n );\n return () => {};\n }\n // Before onAdd there is no session/scene to mount onto — and no Logger yet\n // (it's captured in onAdd), so a warn here couldn't surface. Throw loudly like\n // play() does, and point at the `channels` ctor option (the pre-onAdd path).\n if (!this.session) {\n throw new Error(\n \"DialogueController.addChannel() called before the component was added to an entity (onAdd has not run yet). Use the `channels` constructor option to pre-wire a channel.\",\n );\n }\n if (isMountable(ch)) {\n try {\n ch.mount(this.scene);\n } catch (error) {\n this.logger?.warn(\n \"dialogue\",\n `extra channel mount() failed: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n const unregister = this.session.addChannel(ch);\n const dispose = (): void => {\n if (!this.channelDisposers.delete(dispose)) return; // idempotent\n unregister(); // splices the session's extras + calls ch.dispose?.()\n };\n this.channelDisposers.add(dispose);\n return dispose;\n }\n\n /** Fast-forward the current section to the next choice or the end. */\n skip(): void {\n this.session?.skip();\n }\n\n /**\n * Auto-advance lines after they finish revealing (`ms`), or `null` to disable\n * (manual advance). A per-line `autoAdvanceMs` still overrides this. Toggle it\n * live for a VN-style \"auto\" control.\n */\n setAutoAdvance(ms: number | null): void {\n this.session?.setAutoAdvance(ms);\n }\n\n /**\n * Hide or show the whole dialogue UI without ending the conversation —\n * for a cutscene takeover (`setHidden(true)` while the camera pans, then\n * `setHidden(false)` to restore the exact line + caret). Purely visual; the\n * conversation keeps its state. **Persistent**: it survives `stop()`/`play()`,\n * so a host that hides and forgets to unhide stays hidden.\n */\n setHidden(hidden: boolean): void {\n this.hidden = hidden;\n this.session?.setHidden(hidden);\n }\n\n /**\n * Freeze or resume the conversation — a pause menu. While paused the\n * reveal, auto-advance, caret blink, and avatar anim all halt, input is inert,\n * and no state is lost (an in-flight blocking command keeps running). Also\n * gates this controller's input binding so a frozen conversation consumes no\n * device input. Does NOT block host-driven `handle.setVar` / storage writes.\n */\n setPaused(paused: boolean): void {\n this.paused = paused;\n this.session?.setPaused(paused);\n }\n\n /**\n * Set whether this controller consumes device input — the focus seam for\n * the multi-instance story. `setInputEnabled(false)` keeps the conversation\n * fully alive (it still updates, reveals, auto-advances) but stops polling its\n * binding, so an ambient conversation doesn't steal the advance key. Switch\n * focus between two conversations with `a.setInputEnabled(true);\n * b.setInputEnabled(false)`. (YAGE input is non-consuming, so two *enabled*\n * controllers both advance on one press — focus is the game's policy.)\n */\n setInputEnabled(enabled: boolean): void {\n this.inputEnabled = enabled;\n }\n\n /**\n * Primary action, host-driven (the input-agnostic seam): while saying,\n * reveal-all if still typing else advance to the next line; while choosing,\n * confirm the highlighted option. Lets a host (cutscene script, custom input,\n * or a test) drive the conversation without synthesising device input — the\n * same call the default {@link InputBinding} makes.\n */\n advance(): void {\n this.session?.advance();\n }\n\n /** Move the choice cursor by `delta` (wraps). No-op outside a choice. */\n moveSelection(delta: number): void {\n this.session?.moveSelection(delta);\n }\n\n /** Commit a choice by its original option index. No-op outside a choice. */\n choose(optionIndex: number): void {\n this.session?.choose(optionIndex);\n }\n\n /** True while a choice is being presented. */\n isChoosing(): boolean {\n return this.session?.isChoosing() ?? false;\n }\n\n /** Side-effect-free lookahead of the lines a node would show. */\n preview(nodeId: string): PreviewedLine[] {\n return this.session?.preview(nodeId) ?? [];\n }\n\n update(dt: number): void {\n // The session keeps pumping even when this instance lacks input focus (an\n // ambient conversation stays alive and animating); it no-ops internally when\n // paused. The binding is polled only when focused AND not paused, so a\n // backgrounded or frozen conversation consumes no device input.\n this.session?.update(dt);\n if (this.inputEnabled && !this.paused) this.binding.poll();\n }\n}\n\n/** Whether an extra channel also needs the scene — it implements {@link Mountable}\n * (a `mount(scene)`), beyond the `DialogueExtraChannel` hooks. A plain observer\n * (Voice / Shop) has no `mount`. */\nfunction isMountable(\n ch: DialogueExtraChannel,\n): ch is DialogueExtraChannel & Mountable {\n return typeof (ch as { mount?: unknown }).mount === \"function\";\n}\n","/**\n * Input is externalised so it's obviously *optional* and obviously *swappable*.\n * A `DialogueSession` exposes an input-agnostic API (`advance / moveSelection /\n * setFastForward`); an {@link InputBinding} is whatever maps a device onto it.\n * The default {@link KeyboardInputBinding} polls the YAGE `InputManager` action\n * map. An ambient (auto-advancing) conversation simply attaches no binding; a\n * touch/gamepad/pointer binding is a parallel implementation of this interface.\n */\n\nimport type { InputManager } from \"@yagejs/input\";\nimport type { DialogueSession } from \"../core/session.js\";\n\nexport interface DialogueActions {\n /** Tap → reveal-all if typing, else next line / confirm choice. */\n readonly advance: readonly string[];\n /** Hold → fast-forward the typewriter. */\n readonly speed: readonly string[];\n readonly up: readonly string[];\n readonly down: readonly string[];\n /** Tap → skip the current section to the next choice/end. Unbound by default. */\n readonly skip?: readonly string[];\n}\n\nexport const DEFAULT_ACTIONS: DialogueActions = {\n advance: [\"interact\"],\n speed: [\"attack\"],\n up: [\"move-up\"],\n down: [\"move-down\"],\n};\n\n/** Keyboard actions with skip bound (the game maps `skip` → KeyX in main.ts). */\nexport const FULL_ACTIONS: DialogueActions = {\n ...DEFAULT_ACTIONS,\n skip: [\"skip\"],\n};\n\nexport interface InputBinding {\n /**\n * Wire a device to a session. Called by the host once both exist. A binding\n * has a single owner: re-binding re-targets it (implementations must release\n * any resources held for the previous bind) — don't share one instance\n * between two live controllers.\n */\n bind(input: InputManager, session: DialogueSession): void;\n /** Poll the device and drive the session. Called once per frame by the host. */\n poll(): void;\n /** Optional teardown (e.g. unsubscribe pointer listeners). */\n dispose?(): void;\n}\n\n/**\n * A presenter that can resolve a pointer point to a choice position — lets a\n * pointer binding pick/hover choices without owning their geometry. Coords are\n * in `pointerSpace` (\"screen\" default; a bubble/world list uses \"world\").\n */\nexport interface PointerChoiceTarget {\n /** Position of the choice row under this point, or undefined. Omit for no\n * pointer hit-testing. */\n choiceAtPoint?(x: number, y: number): number | undefined;\n readonly pointerSpace?: \"screen\" | \"world\";\n}\n\n/** Fan a single session out to several device bindings (keyboard + pointer …). */\nexport class CompositeInputBinding implements InputBinding {\n constructor(private readonly bindings: readonly InputBinding[]) {}\n bind(input: InputManager, session: DialogueSession): void {\n for (const b of this.bindings) b.bind(input, session);\n }\n poll(): void {\n for (const b of this.bindings) b.poll();\n }\n dispose(): void {\n for (const b of this.bindings) b.dispose?.();\n }\n}\n\n/** Keyboard/gamepad action-map binding (the default). */\nexport class KeyboardInputBinding implements InputBinding {\n private input?: InputManager;\n private session?: DialogueSession;\n /** Latch so a held skip fires once per hold, not every frame past threshold. */\n private skipFired = false;\n\n /**\n * @param skipHoldMs Hold the `skip` action this long before it fires (the\n * classic \"hold to skip\" confirm). `0` (default) fires on press.\n */\n constructor(\n private readonly actions: DialogueActions = DEFAULT_ACTIONS,\n private readonly skipHoldMs = 0,\n ) {}\n\n bind(input: InputManager, session: DialogueSession): void {\n this.input = input;\n this.session = session;\n }\n\n poll(): void {\n const { input, session } = this;\n if (!input || !session) return;\n session.setFastForward(held(input, this.actions.speed));\n this.pollSkip(input, session);\n if (justPressed(input, this.actions.advance)) session.advance();\n if (justPressed(input, this.actions.up)) session.moveSelection(-1);\n else if (justPressed(input, this.actions.down)) session.moveSelection(1);\n }\n\n /** Fire skip once the action has been held `skipHoldMs` (hold-to-confirm),\n * re-arming only after it's released. */\n private pollSkip(input: InputManager, session: DialogueSession): void {\n const skip = this.actions.skip;\n if (!skip) return;\n const ready = skip.some(\n (a) => input.isPressed(a) && input.isHeldFor(a, this.skipHoldMs),\n );\n if (ready) {\n if (!this.skipFired) {\n session.skip();\n this.skipFired = true;\n }\n } else if (!held(input, skip)) {\n this.skipFired = false;\n }\n }\n}\n\nfunction justPressed(input: InputManager, actions: readonly string[]): boolean {\n return actions.some((a) => input.isJustPressed(a));\n}\n\nfunction held(input: InputManager, actions: readonly string[]): boolean {\n return actions.some((a) => input.isPressed(a));\n}\n\n/**\n * Mouse/touch binding. A tap during a line advances (reveal-all, then next);\n * a tap on a choice row picks it, and hover highlights it — provided a\n * {@link PointerChoiceTarget} is supplied so the binding can hit-test rows.\n * Works for both mouse and touch since it rides the unified pointer stream.\n */\nexport class PointerInputBinding implements InputBinding {\n private input?: InputManager;\n private session?: DialogueSession;\n // Explicit `| undefined` so `dispose()` can null it (exactOptionalPropertyTypes).\n private unsub: (() => void) | undefined;\n /** A primary-button press happened since the last poll (consumed in poll). */\n private clicked = false;\n\n // Explicit `| undefined` (not `?:`) so the ctor can assign the possibly-\n // undefined argument under `exactOptionalPropertyTypes`.\n private readonly choices: PointerChoiceTarget | undefined;\n\n /** Pointer position at the last hover hit-test, so an unmoved pointer\n * doesn't re-run the hit-test every frame. */\n private lastX = Number.NaN;\n private lastY = Number.NaN;\n /** Whether the previous poll saw a choice up (a fresh choice set must be\n * hit-tested even under a stationary pointer). */\n private wasChoosing = false;\n\n constructor(choices?: PointerChoiceTarget) {\n this.choices = choices;\n }\n\n bind(input: InputManager, session: DialogueSession): void {\n // Self-heal a re-bind (component re-add, or a binding instance reused by a\n // second controller): release the previous pointer subscription, which\n // would otherwise leak past dispose() and keep driving the old session.\n this.unsub?.();\n this.input = input;\n this.session = session;\n this.unsub = input.onPointerDown((info) => {\n if (info.button === 0) this.clicked = true; // primary button / touch only\n });\n }\n\n /** Pointer position in the choice presenter's coordinate space. */\n private pointer(input: InputManager): { x: number; y: number } {\n return this.choices?.pointerSpace === \"world\"\n ? input.getPointerPosition()\n : input.getPointerScreenPosition();\n }\n\n poll(): void {\n const { input, session } = this;\n if (!input || !session) return;\n\n // Hover-highlight the choice under the pointer. Hit-test only when the\n // result could have changed: the pointer moved (world coords also move\n // with the camera), or a choice set just came up under a still pointer.\n const choosing = session.isChoosing();\n if (choosing && this.choices?.choiceAtPoint) {\n const p = this.pointer(input);\n if (!this.wasChoosing || p.x !== this.lastX || p.y !== this.lastY) {\n this.lastX = p.x;\n this.lastY = p.y;\n const hovered = this.choices.choiceAtPoint(p.x, p.y);\n if (hovered !== undefined) session.selectAt(hovered);\n }\n }\n this.wasChoosing = choosing;\n\n const clicked = this.clicked;\n this.clicked = false;\n if (!clicked) return;\n if (choosing) {\n const p = this.pointer(input);\n const hit = this.choices?.choiceAtPoint?.(p.x, p.y);\n // confirmAt commits the tapped row and refuses a disabled one — using\n // selectAt + confirm would refuse to move onto a disabled row, then\n // wrongly commit whatever was highlighted before.\n if (hit !== undefined) session.confirmAt(hit);\n // Tap off the list does nothing (keyboard nav still available).\n } else {\n session.advance();\n }\n }\n\n dispose(): void {\n this.unsub?.();\n this.unsub = undefined;\n }\n}\n\n/**\n * The full control set in one binding: keyboard/gamepad (advance / fast-forward\n * hold / choice nav / skip) **and** mouse/touch (tap to advance, tap/hover\n * choices). Pass a scene's choice presenter so the pointer can hit-test rows;\n * `skipHoldMs` adds the classic \"hold to skip\" confirm.\n */\nexport function fullControls(\n choices?: PointerChoiceTarget,\n options: { actions?: DialogueActions; skipHoldMs?: number } = {},\n): InputBinding {\n const { actions = FULL_ACTIONS, skipHoldMs = 0 } = options;\n return new CompositeInputBinding([\n new KeyboardInputBinding(actions, skipHoldMs),\n new PointerInputBinding(choices),\n ]);\n}\n","import { defineEvent } from \"@yagejs/core\";\nimport type { Command, MarkerToken, RunMode } from \"./core/types.js\";\n\n/**\n * Lifecycle + command events the {@link DialogueController} emits from its host\n * entity. A scene listens with `this.on(DialogueEndedEvent, …)` (events bubble\n * entity → scene). `DialogueCommandEvent` is the main game hook: every script\n * command that isn't a built-in (`set`) arrives here for the game to interpret.\n */\nexport const DialogueStartedEvent = defineEvent<{ scriptId: string }>(\"dialogue:started\");\n\nexport const DialogueLineEvent = defineEvent<{\n speaker?: string | undefined;\n /** Plain (markup-stripped) text — handy for logs, a11y, history. */\n text: string;\n}>(\"dialogue:line\");\n\nexport const DialogueChoiceShownEvent = defineEvent<{ options: readonly string[] }>(\n \"dialogue:choice-shown\",\n);\n\nexport const DialogueChoiceMadeEvent = defineEvent<{ index: number; text: string }>(\n \"dialogue:choice-made\",\n);\n\nexport const DialogueCommandEvent = defineEvent<{ command: Command; mode: RunMode }>(\n \"dialogue:command\",\n);\n\nexport const DialogueEndedEvent = defineEvent<{ scriptId: string }>(\"dialogue:ended\");\n\n/**\n * Lifecycle observation events. These are the moments games\n * actually hook — a \"typing finished\" blip, a choice-hover tick, skip-used\n * analytics, an auto-advance beat — emitted by the controller from the session's\n * observation callbacks (the one canonical observation path; there are no\n * matching controller callback options).\n */\n\n/** A line finished its typewriter reveal — the \"typing finished\" hook. Plain\n * (markup-stripped) text, mirroring {@link DialogueLineEvent}. */\nexport const DialogueRevealCompletedEvent = defineEvent<{\n speaker?: string | undefined;\n text: string;\n}>(\"dialogue:reveal-completed\");\n\n/** The choice cursor moved (keyboard nav OR pointer hover) — `index` is the\n * original option index, `text` its plain label. */\nexport const DialogueSelectionChangedEvent = defineEvent<{ index: number; text: string }>(\n \"dialogue:selection-changed\",\n);\n\n/** The player skipped the current section (skip-used analytics). */\nexport const DialogueSkipUsedEvent = defineEvent<{ scriptId: string }>(\n \"dialogue:skip-used\",\n);\n\n/** A line advanced on its own via the auto-advance clock (vs a manual advance). */\nexport const DialogueAutoAdvanceEvent = defineEvent<{ scriptId: string }>(\n \"dialogue:auto-advance\",\n);\n\n/**\n * An inline `[name k=v/]` reveal marker reached its char offset during the\n * current line's typewriter — the game hook for positional effects\n * (`[sfx=ding/]` → play a sound). `viaSkip` is true when a skip / complete\n * drained it, so a loud one-shot can be suppressed. The avatar channel handles\n * `[expression=…/]` itself; the addon name-matches no marker, so every other\n * name flows here opaquely. Per-grapheme typewriter *ticks* are deliberately NOT\n * an event (they fire hundreds of times per line) — wire `onRevealTick` on the\n * controller instead.\n */\nexport const DialogueRevealMarkerEvent = defineEvent<{\n marker: MarkerToken;\n viaSkip: boolean;\n}>(\"dialogue:reveal-marker\");\n"],"mappings":"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","names":["unescape","import_core"]}
|