Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/web-katex-math.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Render LaTeX display math (`$$…$$`) in the web chat via KaTeX. Single `$` is intentionally left as literal text, so prices, env vars, and shell paths (e.g. `$PATH`, `$5/$10`, `$HOME/bin`) are never swallowed as a formula.
1 change: 1 addition & 0 deletions apps/kimi-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@fontsource-variable/jetbrains-mono": "^5.2.8",
"@xterm/addon-fit": "^0.11.0",
"@xterm/xterm": "^6.0.0",
"katex": "^0.16.22",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Refresh the Nix pnpm dependency hash

In the Nix packaging path, flake.nix includes pnpm-lock.yaml in the fixed-output fetchPnpmDeps source and pins it with the hash at flake.nix:153; adding katex changes the lockfile/dependency set but this commit leaves that hash unchanged, so nix build will fail with a fixed-output hash mismatch before the app builds. Please update the pnpmDeps hash together with this new dependency.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked this one: no flake.nix hash update is needed for the current diff. katex@0.16.47 was already present in pnpm-lock.yaml via markstream-vue on main; this PR only adds it as a direct importer dependency for apps/kimi-web, so the fetched dependency store content is unchanged. The Nix CI also confirms it: nix build .#kimi-code passed on commit 961c172706 (job https://github.com/MoonshotAI/kimi-code/actions/runs/28083761728/job/83144618679).

"markstream-vue": "1.0.3",
"shiki": "^4.2.0",
"stream-markdown": "^0.0.16",
Expand Down
34 changes: 33 additions & 1 deletion apps/kimi-web/src/components/chat/Markdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
<script setup lang="ts">
import { computed, inject, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { MarkdownRender } from 'markstream-vue';
import { MarkdownRender, enableKatex } from 'markstream-vue';
import type { MarkdownIt } from 'markstream-vue';
import { useIsDark } from '../../composables/useIsDark';
import type { FilePreviewRequest } from '../../types';
import { collectFilePathAliases, findFilePathLinks } from '../../lib/filePathLinks';
Expand All @@ -13,6 +14,23 @@ import { copyTextToClipboard } from '../../lib/clipboard';
// Terminal Pro. Importing the same file from multiple components is a no-op
// after the first (Vite dedups the CSS import).
import 'markstream-vue/index.px.css';
// KaTeX math: markstream renders `$$…$$` display math only after the optional
// katex peer is enabled, and its stylesheet (+ bundled fonts) is what gives
// formulas their layout. enableKatex() registers the default `import('katex')`
// loader; it runs once on first import of this module and is safe at module
// scope. Without the CSS the math renders unstyled, so both must travel
// together.
import 'katex/dist/katex.min.css';
enableKatex();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Guard literal dollar text before enabling inline math

When KaTeX is enabled globally here, ordinary assistant text that contains two literal dollar-prefixed tokens, such as Check $PATH before $HOME or costs $5 and $10, is now parsed as a single $...$ inline formula between the first two dollars and rendered as math instead of readable prose; before this patch those dollars stayed literal. Please add a guard/escape for common literal-dollar patterns or otherwise avoid enabling single-dollar math globally without that protection.

Useful? React with 👍 / 👎.


// Only `$$…$$` display math is rendered; single `$` inline math is disabled so
// prices, env vars, and shell paths (`$5`, `$PATH`, `$HOME/bin`) stay literal
// without any escaping or code-detection gymnastics. `math_block` (the $$ rule)
// is left enabled.
function disableInlineMath(md: MarkdownIt): MarkdownIt {
md.inline.ruler.disable('math');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve inline-scanned $$ display math

When a model writes display math in the common paragraph form, e.g. Here is $$E=mc^2$$, this disables markstream's whole inline math rule, which is also the rule that scans $$...$$ inside inline text; math_block only catches line-start/block forms. As a result those $$ formulas remain raw even though the change promises $$…$$ display math, so please avoid disabling the whole inline rule if the intent is only to suppress single-dollar parsing.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I am going to keep this out of this PR intentionally: the current change only guarantees the block/display forms that math_block catches ($$...$$ on its own line / block form), while avoiding the complexity of reintroducing a custom inline math scanner just to keep paragraph-form Here is $$...$$. Re-enabling the built-in inline rule would also bring back single-$ false positives, which this PR is deliberately avoiding. I have linked #972 for the remaining delimiter policy work; paragraph-form $$...$$ / inline delimiters can be handled there if we decide the product should support them.

return md;
}

const { t } = useI18n();

Expand Down Expand Up @@ -353,6 +371,7 @@ function copyDiff(code: string, idx: number) {
<MarkdownRender
v-if="seg.kind === 'md'"
:content="seg.text"
:custom-markdown-it="disableInlineMath"
mode="chat"
:code-renderer="renderPlan.codeRenderer"
:is-dark="isDark"
Expand Down Expand Up @@ -575,6 +594,19 @@ function copyDiff(code: string, idx: number) {
text-decoration: underline;
}

/* KaTeX math. Colour already inherits (--text) since KaTeX draws with
currentColor, so the only skinning needed is layout: let a wide display
formula scroll inside its own box instead of overflowing the chat column and
breaking the mobile layout. Inline math stays in the text flow. */
.md :deep(.katex-display) {
overflow-x: auto;
overflow-y: hidden;
/* room for the horizontal scrollbar so it doesn't clip the bottom of the
formula (e.g. integral/sum subscripts) */
padding: 2px 0 6px;
margin: 0.6em 0;
}

/* Blockquote */
.md :deep(blockquote) {
margin: 0.5em 0;
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading