← Back to PRs

#22695: fix(export): restore HTML template placeholders broken by formatter (#22595)

by lailoo open 2026-02-21 14:35 View on GitHub →
size: S experienced-contributor
## Summary - **Bug**: `/export-session` generates broken HTML — vendored JS not embedded, page non-functional - **Root cause 1**: `template.html` placeholders `{{MARKED_JS}}`, `{{HIGHLIGHT_JS}}`, `{{JS}}` were reformatted into multi-line `{ { MARKED_JS; } }` by a code formatter, so `.replace("{{MARKED_JS}}", ...)` silently matched nothing - **Root cause 2**: `highlight.min.js` contains literal `$&` (R language regex pattern), which JavaScript's `String.replace()` interprets as "insert matched substring" — causing `{{HIGHLIGHT_JS}}` to be re-inserted into the output even after replacement - **Fix**: Restore placeholders to single-line form with `<!-- prettier-ignore -->` guards, and switch all `.replace()` calls to use arrow-function replacers to prevent `$` special pattern injection Fixes #22595 ## Problem Two independent bugs prevented `/export-session` from producing functional HTML: ### Bug 1: Formatter broke template placeholders The export-session HTML template had its mustache-style placeholders broken by a formatter (likely Prettier): ```html <!-- Before (broken by formatter) --> <script> { { MARKED_JS; } } </script> <!-- After (fixed) --> <!-- prettier-ignore --> <script>{{MARKED_JS}}</script> ``` ### Bug 2: `$&` in vendor JS causes replacement injection `highlight.min.js` contains `$&` as part of R language regex patterns. When used as a plain string replacement value in `String.replace("{{HIGHLIGHT_JS}}", hljsJs)`, JavaScript interprets `$&` as "insert the matched substring" — re-inserting `{{HIGHLIGHT_JS}}` back into the output. Fix: use arrow-function replacers which bypass `$` special pattern interpretation: ```typescript // Before (buggy) template.replace("{{HIGHLIGHT_JS}}", hljsJs) // After (fixed) template.replace("{{HIGHLIGHT_JS}}", () => hljsJs) ``` **Before fix:** - HTML file ~20 KB (vendor JS missing) - Browser console: `ReferenceError: MARKED_JS is not defined` (3 errors) - Page completely non-functional — blank dark background <img width="2956" height="878" alt="image" src="https://github.com/user-attachments/assets/c300f208-a9d9-4fcb-9a3a-40ce94354bf4" /> <img width="1742" height="478" alt="image" src="https://github.com/user-attachments/assets/98b1f577-3dab-4f7d-addd-7a4f98503e88" /> **After fix:** - HTML file ~240 KB (all vendor JS embedded) - Session renders correctly with markdown, code highlighting, sidebar tree, stats <img width="1635" height="1066" alt="image" src="https://github.com/user-attachments/assets/4daf8dee-324e-4ce2-b742-0b4eec56917b" /> ## Changes - `src/auto-reply/reply/export-html/template.html` — Restore `{{MARKED_JS}}`, `{{HIGHLIGHT_JS}}`, `{{JS}}` to single-line form; add `<!-- prettier-ignore -->` comments to prevent future formatter breakage - `src/auto-reply/reply/commands-export-session.ts` — Switch all 5 `.replace()` calls in `generateHtml()` to use arrow-function replacers, preventing `$&` pattern injection from vendor JS content - `src/auto-reply/reply/export-session-html.test.ts` — New: 2 regression tests verifying placeholders exist and full pipeline produces valid self-contained HTML - `CHANGELOG.md` — Add fix entry ## Test plan - [x] New test: template.html contains single-line `{{MARKED_JS}}`, `{{HIGHLIGHT_JS}}`, `{{JS}}` placeholders - [x] New test: full generateHtml pipeline — all placeholders replaced, vendor JS embedded, CSS themed, HTML structure intact - [x] All 2 new tests pass - [x] Generated HTML verified in browser (before: blank page with 3 JS errors; after: fully functional session viewer) ## Effect on User Experience **Before:** `/export-session` produces a broken HTML file (~20 KB). Opening it shows a blank dark page with JavaScript errors in the console. Users cannot view exported sessions. **After:** `/export-session` produces a fully functional self-contained HTML file (~240 KB) with embedded JS libraries, rendering the session with markdown, syntax highlighting, sidebar navigation, and session stats.

Most Similar PRs