feat(quick-260329-ton): replace textContent with innerHTML + renderInlineMarkdown at all AI-text insertion points

- src/app.ts: greeting text now renders inline markdown
- src/ui/screens.ts: letter intro text now renders inline markdown
- src/forest/reward.ts: forest comment now renders inline markdown
- All 3 sites use innerHTML with XSS-safe renderInlineMarkdown
This commit is contained in:
2026-03-29 21:26:04 +02:00
parent e10bdf88dc
commit d038cc3196
3 changed files with 6 additions and 3 deletions
+2 -1
View File
@@ -1,6 +1,7 @@
import { speakText } from "./audio/speech"; import { speakText } from "./audio/speech";
import { companions } from "./companion/characters"; import { companions } from "./companion/characters";
import { getGreeting } from "./companion/companion"; import { getGreeting } from "./companion/companion";
import { renderInlineMarkdown } from "./utils/markdown";
import { initRewardScreen, startPreGeneration } from "./forest/reward"; import { initRewardScreen, startPreGeneration } from "./forest/reward";
import { initForestScreen, renderForestScene } from "./forest/scene"; import { initForestScreen, renderForestScene } from "./forest/scene";
import { getProgress, openDB, saveProgress } from "./storage/db"; import { getProgress, openDB, saveProgress } from "./storage/db";
@@ -224,7 +225,7 @@ export async function initApp(): Promise<void> {
// Fetch greeting async (shows immediately with avatar, text fills in) // Fetch greeting async (shows immediately with avatar, text fills in)
getGreeting(progress.selectedCharacter, db).then((text) => { getGreeting(progress.selectedCharacter, db).then((text) => {
textEl.textContent = text; textEl.innerHTML = renderInlineMarkdown(text);
speakText(text); speakText(text);
}); });
} }
+2 -1
View File
@@ -22,6 +22,7 @@ import { getRandomFallbackForestComment } from "../companion/fallbacks";
import type { CompanionType, ForestElement } from "../types"; import type { CompanionType, ForestElement } from "../types";
import { playRewardSound } from "../audio/sounds"; import { playRewardSound } from "../audio/sounds";
import { speakText } from "../audio/speech"; import { speakText } from "../audio/speech";
import { renderInlineMarkdown } from "../utils/markdown";
// --- Element types for random selection --- // --- Element types for random selection ---
@@ -204,7 +205,7 @@ export async function initRewardScreen(
playRewardSound(); playRewardSound();
// 7. Show companion comment + speak it aloud // 7. Show companion comment + speak it aloud
companionTextEl.textContent = comment; companionTextEl.innerHTML = renderInlineMarkdown(comment);
speakText(comment); speakText(comment);
// 8. Wire buttons // 8. Wire buttons
+2 -1
View File
@@ -20,6 +20,7 @@ import { getLevelByNumber } from "../game/levels";
import { getLetterIntro } from "../companion/companion"; import { getLetterIntro } from "../companion/companion";
import { getWordsForLevel } from "../game/words"; import { getWordsForLevel } from "../game/words";
import { playCorrectSound } from "../audio/sounds"; import { playCorrectSound } from "../audio/sounds";
import { renderInlineMarkdown } from "../utils/markdown";
export function initWelcomeScreen( export function initWelcomeScreen(
db: IDBDatabase, db: IDBDatabase,
@@ -419,7 +420,7 @@ export function initLessonScreen(
if (cancelled) return; if (cancelled) return;
// Show companion intro text // Show companion intro text
companionText.textContent = introText; companionText.innerHTML = renderInlineMarkdown(introText);
companionArea.style.display = "flex"; companionArea.style.display = "flex";
// Show the letter // Show the letter