Files

5.8 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
quick 260329-ton execute 1
src/utils/markdown.ts
src/utils/markdown.test.ts
src/app.ts
src/ui/screens.ts
src/forest/reward.ts
true
truths artifacts key_links
AI-generated companion texts with *italic* render as <em> in the UI
AI-generated companion texts with **bold** render as <strong> in the UI
Plain text without markdown renders unchanged
HTML special characters in generated text are escaped (no XSS)
path provides exports
src/utils/markdown.ts renderInlineMarkdown() function
renderInlineMarkdown
path provides
src/utils/markdown.test.ts Unit tests for markdown rendering
from to via pattern
src/app.ts src/utils/markdown.ts innerHTML = renderInlineMarkdown(text) renderInlineMarkdown
from to via pattern
src/ui/screens.ts src/utils/markdown.ts innerHTML = renderInlineMarkdown(introText) renderInlineMarkdown
from to via pattern
src/forest/reward.ts src/utils/markdown.ts innerHTML = renderInlineMarkdown(comment) renderInlineMarkdown
Render inline Markdown (*italic*, **bold**) in AI-generated companion texts as HTML instead of displaying raw asterisks.

Purpose: AI-generated texts (greetings, letter intros, forest comments) contain Markdown formatting like f for emphasis, but .textContent displays them as plaintext with visible asterisks. Switching to .innerHTML with a safe inline markdown renderer fixes this.

Output: A renderInlineMarkdown() utility function used at all 3 AI-text insertion points.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/STATE.md @src/utils/markdown.ts (will be created) @src/app.ts @src/ui/screens.ts @src/forest/reward.ts Task 1: Create renderInlineMarkdown utility with tests src/utils/markdown.ts, src/utils/markdown.test.ts - renderInlineMarkdown("hello") returns "hello" (plain text unchanged) - renderInlineMarkdown("press *f*") returns "press <em>f</em>" (single asterisks become em) - renderInlineMarkdown("very **important**") returns "very <strong>important</strong>" (double asterisks become strong) - renderInlineMarkdown("**bold** and *italic*") handles both in one string - renderInlineMarkdown("a <script>alert(1)</script>") escapes HTML entities BEFORE markdown processing (XSS safe) - renderInlineMarkdown("2 * 3 * 4") does NOT wrap standalone asterisks with spaces (only wrapping content like *word*) - renderInlineMarkdown("") returns "" (empty string) Create src/utils/markdown.ts exporting a single function renderInlineMarkdown(text: string): string.
Implementation approach:
1. First escape HTML special chars: & < > " ' (using string replace chains, no DOM needed)
2. Then apply regex for **bold**: /\*\*(.+?)\*\*/g -> <strong>$1</strong>
3. Then apply regex for *italic*: /\*(.+?)\*/g -> <em>$1</em>
(Order matters: bold first so ** is not consumed as two single *)

No external dependencies. Pure function, no DOM.

Create src/utils/markdown.test.ts with vitest tests covering all behaviors above.
cd /home/dev/workspace/zauberwald && npx vitest run src/utils/markdown.test.ts All markdown rendering tests pass. Function handles bold, italic, mixed, HTML escaping, and edge cases. Task 2: Replace .textContent with .innerHTML + renderInlineMarkdown at all AI-text insertion points src/app.ts, src/ui/screens.ts, src/forest/reward.ts Import renderInlineMarkdown from "../utils/markdown" (or adjust relative path) in each file and replace .textContent with .innerHTML + renderInlineMarkdown() at these 3 specific locations:
1. src/app.ts line 227:
   BEFORE: textEl.textContent = text;
   AFTER:  textEl.innerHTML = renderInlineMarkdown(text);

2. src/ui/screens.ts line 422:
   BEFORE: companionText.textContent = introText;
   AFTER:  companionText.innerHTML = renderInlineMarkdown(introText);

3. src/forest/reward.ts line 207:
   BEFORE: companionTextEl.textContent = comment;
   AFTER:  companionTextEl.innerHTML = renderInlineMarkdown(comment);

IMPORTANT: Do NOT change any other .textContent assignments in these files. The other assignments use hardcoded German strings (no markdown) or set empty strings. Only the 3 lines above receive AI-generated text that may contain markdown.
cd /home/dev/workspace/zauberwald && npx vitest run && npx tsc --noEmit All 3 AI-text insertion points use innerHTML + renderInlineMarkdown. TypeScript compiles without errors. Existing tests still pass. - npx vitest run passes (all tests including new markdown tests) - npx tsc --noEmit passes (no type errors) - grep -n "renderInlineMarkdown" src/app.ts src/ui/screens.ts src/forest/reward.ts shows exactly 3 usage sites (plus imports)

<success_criteria>

  • AI-generated companion texts with italic or bold markdown render as styled HTML
  • Plain text and hardcoded strings are unaffected
  • HTML in generated text is escaped (no injection risk)
  • All existing tests continue to pass </success_criteria>
After completion, create `.planning/quick/260329-ton-markdown-in-companion-texten-zu-html-ren/260329-ton-SUMMARY.md`