# LaTeX Rendering Implementation Plan > **For Hermes:** Use subagent-driven-development skill to implement this plan task-by-task. **Goal:** Add reliable LaTeX/math rendering to Hermes Web UI chat Markdown messages so formulas render visually instead of appearing as raw TeX, including explicit fenced math blocks like ```latex. **Architecture:** Keep the existing `markdown-it` renderer in `MarkdownRenderer.vue` and add a KaTeX-backed math plugin there, plus a small fence override for explicit LaTeX code fences. Load KaTeX styles globally once, cover inline, display, and fenced math in component tests, then verify with a production build and a local Web UI smoke test. **Tech Stack:** Vue 3, TypeScript, Vite, markdown-it, KaTeX, Vitest, @vue/test-utils. --- ## Acceptance Criteria - Chat messages render inline math like `$x^2 + y^2 = z^2$` as KaTeX HTML. - Chat messages render block math like `$$\n\\int_0^1 x^2 dx = \\frac{1}{3}\n$$` as display KaTeX HTML. - Chat messages render explicit fenced math blocks like ```latex, ```tex, or ```math as display KaTeX HTML. - Existing Markdown features still work: code fences, Mermaid fences, headings, local file links, mentions. - Ordinary fenced code blocks stay literal and are not rendered as math. - Invalid/unsupported math syntax does not crash the chat renderer. - `npm run test -- tests/client/markdown-rendering.test.ts` passes. - `npm run build` passes. - Local `hermes-web-ui.service` can be restarted onto the built fork and displays formulas in the real panel. ## Recommended Dependency Choice Use `markdown-it-katex` + `katex` because the app already uses `markdown-it`, and this is the smallest change surface. Install as runtime dependencies, not dev-only, because the renderer runs in the browser bundle: ```bash npm install katex markdown-it-katex npm install -D @types/katex ``` If TypeScript reports no module declaration for `markdown-it-katex`, add a local declaration file instead of weakening `tsconfig`. --- ## Task 1: Add KaTeX dependencies **Objective:** Make the required renderer and styles available to the client bundle. **Files:** - Modify: `package.json` - Modify: `package-lock.json` or lockfile generated by npm if present **Step 1: Install packages** Run from repo root: ```bash cd /home/werserk/2-kira/hermes-web-ui npm install katex markdown-it-katex npm install -D @types/katex ``` **Expected:** `package.json` includes `katex` and `markdown-it-katex`; lockfile is updated. **Step 2: Verify dependency tree** ```bash npm ls katex markdown-it-katex ``` **Expected:** both packages resolve without `UNMET DEPENDENCY`. **Step 3: Commit** ```bash git add package.json package-lock.json git commit -m "chore: add latex rendering dependencies" ``` --- ## Task 2: Add TypeScript module declaration if needed **Objective:** Keep TypeScript strict and avoid `any` leaking into the renderer setup. **Files:** - Create if needed: `packages/client/src/types/markdown-it-katex.d.ts` **Step 1: Run typecheck to discover whether declaration is needed** ```bash npm run build ``` **Expected before implementation:** build may still fail later because code is not changed yet. For this task, only check whether TypeScript knows the `markdown-it-katex` module after it is imported in Task 3. **Step 2: If TS2307/TS7016 appears, create declaration** Create `packages/client/src/types/markdown-it-katex.d.ts`: ```ts declare module 'markdown-it-katex' { import type MarkdownIt from 'markdown-it' interface MarkdownItKatexOptions { throwOnError?: boolean errorColor?: string strict?: boolean | string | ((errorCode: string) => boolean | string) output?: 'html' | 'mathml' | 'htmlAndMathml' trust?: boolean | ((context: unknown) => boolean) macros?: Record } const markdownItKatex: MarkdownIt.PluginWithOptions export default markdownItKatex } ``` **Step 3: Commit only if file was needed** ```bash git add packages/client/src/types/markdown-it-katex.d.ts git commit -m "chore: add markdown-it-katex types" ``` --- ## Task 3: Wire KaTeX into MarkdownRenderer **Objective:** Render math in the same path that currently renders Markdown chat messages. **Files:** - Modify: `packages/client/src/components/hermes/chat/MarkdownRenderer.vue` **Step 1: Import the plugin** Near existing Markdown imports: ```ts import markdownItKatex from 'markdown-it-katex' ``` **Step 2: Register the plugin after creating `md`** After the `new MarkdownItConstructor(...)` block and before custom fence renderer setup: ```ts md.use(markdownItKatex, { throwOnError: false, errorColor: '#cc3344', output: 'htmlAndMathml', }) ``` **Step 3: Preserve existing Mermaid fence behavior** Do not move or remove this existing code: ```ts const defaultFenceRenderer = md.renderer.rules.fence?.bind(md.renderer.rules) md.renderer.rules.fence = (tokens, idx, options, env, self) => { const token = tokens[idx] if (isMermaidFence(token.info)) { return renderMermaidPlaceholder(token.content) } if (defaultFenceRenderer) { return defaultFenceRenderer(tokens, idx, options, env, self) } return self.renderToken(tokens, idx, options) } ``` **Step 4: Run focused test suite** ```bash npm run test -- tests/client/markdown-rendering.test.ts ``` **Expected:** existing tests still pass or only fail because new math tests are not yet added. **Step 5: Commit** ```bash git add packages/client/src/components/hermes/chat/MarkdownRenderer.vue git commit -m "feat: enable latex rendering in chat markdown" ``` --- ## Task 4: Load and tune KaTeX CSS **Objective:** Ensure rendered math is visually correct in the Web UI. **Files:** - Modify: `packages/client/src/main.ts` - Optionally modify: `packages/client/src/styles/global.scss` **Step 1: Import KaTeX CSS once globally** In `packages/client/src/main.ts`, add after global styles import: ```ts import 'katex/dist/katex.min.css' ``` Current import area should become: ```ts import App from './App.vue' import './styles/global.scss' import 'katex/dist/katex.min.css' ``` **Step 2: Add Hermes-specific layout tweaks only if needed** If visual smoke test shows cramped block equations, append to `packages/client/src/styles/global.scss`: ```scss .markdown-body { .katex-display { overflow-x: auto; overflow-y: hidden; padding: 0.25rem 0; } } ``` Do not restyle KaTeX itself unless there is a concrete visual bug. **Step 3: Commit** ```bash git add packages/client/src/main.ts packages/client/src/styles/global.scss git commit -m "style: load katex styles for markdown math" ``` --- ## Task 5: Add regression tests for math rendering **Objective:** Prove formulas render, code fences stay literal, and malformed math is safe. **Files:** - Modify: `tests/client/markdown-rendering.test.ts` **Step 1: Add inline math test** Inside `describe('MarkdownRenderer', () => { ... })`: ```ts it('renders inline latex math with katex', () => { const wrapper = mount(MarkdownRenderer, { props: { content: 'Pythagoras: $x^2 + y^2 = z^2$.', }, }) const body = wrapper.find('.markdown-body') expect(body.find('.katex').exists()).toBe(true) expect(body.html()).toContain('x') expect(body.html()).toContain('z') expect(body.text()).not.toContain('$x^2 + y^2 = z^2$') }) ``` **Step 2: Add block math test** ```ts it('renders display latex math with katex', () => { const wrapper = mount(MarkdownRenderer, { props: { content: '$$\n\\int_0^1 x^2 dx = \\frac{1}{3}\n$$', }, }) const body = wrapper.find('.markdown-body') expect(body.find('.katex-display').exists()).toBe(true) expect(body.find('.katex').exists()).toBe(true) expect(body.text()).not.toContain('$$') }) ``` **Step 3: Add fenced-code non-rendering test** ```ts it('does not render latex inside fenced code blocks', () => { const wrapper = mount(MarkdownRenderer, { props: { content: '```md\n$x^2 + y^2 = z^2$\n```', }, }) expect(wrapper.find('.markdown-body').find('.katex').exists()).toBe(false) expect(wrapper.find('code.hljs').text()).toContain('$x^2 + y^2 = z^2$') }) ``` **Step 4: Add invalid math safety test** ```ts it('keeps rendering when latex syntax is invalid', () => { const wrapper = mount(MarkdownRenderer, { props: { content: 'Before $\\notacommand{ after', }, }) expect(wrapper.find('.markdown-body').text()).toContain('Before') }) ``` **Step 5: Run focused tests** ```bash npm run test -- tests/client/markdown-rendering.test.ts ``` **Expected:** all tests in the file pass. **Step 6: Commit** ```bash git add tests/client/markdown-rendering.test.ts git commit -m "test: cover latex rendering in markdown" ``` --- ## Task 6: Run build and broader regression checks **Objective:** Catch TypeScript, Vite, and existing client regressions before deploying locally. **Files:** - No source changes expected unless checks fail. **Step 1: Run focused tests** ```bash npm run test -- tests/client/markdown-rendering.test.ts tests/client/markdown-rendering-mermaid-import-timeout.test.ts ``` **Expected:** pass. **Step 2: Run all tests if practical** ```bash npm run test ``` **Expected:** pass. If unrelated pre-existing failures appear, record them in the PR body with evidence. **Step 3: Run production build** ```bash npm run build ``` **Expected:** pass and update `dist/` only as build output. Do not commit `dist/` unless the package/release workflow requires it. **Step 4: Commit fixes only if needed** ```bash git add git commit -m "fix: stabilize latex markdown rendering" ``` --- ## Task 7: Deploy to Maxim's local Hermes Web UI instance **Objective:** Start using the feature on `https://hermes.kiraproject.ru/` before upstream PR review completes. **Files:** - Local runtime install under npm/global prefix may change outside the repo. - Do not commit generated deployment artifacts unless intentionally part of the repo. **Step 1: Build from the fork checkout** ```bash cd /home/werserk/2-kira/hermes-web-ui npm run build ``` **Step 2: Replace currently installed package using npm link or local global install** Preferred reversible approach: ```bash cd /home/werserk/2-kira/hermes-web-ui npm install -g --prefix /home/werserk/.npm-global . ``` **Step 3: Restart the Web UI service** ```bash systemctl --user restart hermes-web-ui.service systemctl --user status hermes-web-ui.service --no-pager --lines=20 ``` **Step 4: Smoke test HTTP response** ```bash curl -sS -I --max-time 5 http://127.0.0.1:8648/ | head -20 ``` **Expected:** HTTP 200/304 or another successful static response, not connection refused. **Step 5: Browser smoke test** Open the chat panel and send/render a message containing: ```md Inline: $x^2 + y^2 = z^2$ Block: $$ \\int_0^1 x^2 dx = \\frac{1}{3} $$ Code fence should stay literal: ```md $x^2 + y^2 = z^2$ ``` ``` **Expected:** first two formulas render visually; fenced formula stays literal. --- ## Task 8: Prepare upstream PR **Objective:** Make the contribution easy for upstream maintainers to review. **Files:** - No code changes required unless PR polish finds an issue. **Step 1: Push branch** ```bash git push origin feat/latex-rendering ``` **Step 2: Create PR draft** ```bash gh pr create \ --repo EKKOLearnAI/hermes-web-ui \ --head kira-project-lab:feat/latex-rendering \ --base main \ --title "feat(chat): render LaTeX math in Markdown messages" \ --body "$(cat <<'EOF' ## Summary - Adds KaTeX-backed LaTeX rendering to chat Markdown messages. - Supports inline `$...$` and display `$$...$$` math. - Keeps math inside fenced code blocks literal. ## Test Plan - [ ] npm run test -- tests/client/markdown-rendering.test.ts - [ ] npm run test -- tests/client/markdown-rendering-mermaid-import-timeout.test.ts - [ ] npm run build - [ ] Manual browser smoke test with inline, block, and fenced math ## Notes This uses the existing markdown-it rendering path and keeps HTML disabled. EOF )" \ --draft ``` **Step 3: Attach visual proof** Add before/after screenshots in the PR body or a PR comment: - Before: raw `$...$` / `$$...$$` text. - After: rendered KaTeX inline and display equations. --- ## Risks and Guardrails - **Delimiter ambiguity:** `$` is common in shell snippets and prices. The fenced-code test protects code blocks, but inline text like `cost is $5 and $6` may be parsed depending on plugin behavior. If this is noisy, switch to `markdown-it-texmath` with explicit delimiter options or configure delimiters to prioritize `\(...\)` / `\[...\]`. - **Security:** keep `html: false` in `markdown-it`; do not enable arbitrary HTML. KaTeX should run with `trust: false` unless a clear need appears. - **Bundle size:** KaTeX adds client bundle weight. Acceptable for this feature, but mention it in PR if maintainers ask. - **CSS scope:** KaTeX CSS is global. Import once in `main.ts`; avoid duplicating it inside component styles. - **Invalid math:** `throwOnError: false` should prevent chat crashes. Regression test required. ## Definition of Done - Branch `feat/latex-rendering` contains committed implementation. - Local Web UI renders inline and block formulas. - Focused Markdown tests pass. - Production build passes. - PR draft exists against `EKKOLearnAI/hermes-web-ui:main` or is ready to create.