Skip to content

Commit 0585010

Browse files
authored
🤖 Optimize Review tab syntax highlighting with CSS classes (#335)
## Problem When `useInlineStyles={false}` is enabled in react-syntax-highlighter, the library outputs CSS class names (`.token.keyword`, `.token.string`, etc.) instead of computing inline styles at runtime. This reduces JavaScript overhead, but requires actual CSS stylesheets to be loaded. ## Solution - Generated `prism-syntax.css` from vscDarkPlus theme with backgrounds removed - Imported CSS in DiffRenderer.tsx where syntax highlighting is used - CSS classes now available when `useInlineStyles={false}` prop is active ## Testing - ✅ All 661 tests pass - ✅ TypeScript compilation successful - Manual testing: Verify syntax highlighting still works in Review tab ## Related - Builds on commit 1397430 which added `useInlineStyles={false}` prop - Part of Review tab performance optimization work _Generated with `cmux`_
1 parent 975c18c commit 0585010

File tree

3 files changed

+451
-0
lines changed

3 files changed

+451
-0
lines changed

scripts/generate_prism_css.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#!/usr/bin/env bun
2+
3+
/**
4+
* Generates Prism CSS stylesheet from vscDarkPlus theme
5+
* Used for syntax highlighting when react-syntax-highlighter has useInlineStyles={false}
6+
*
7+
* Strips backgrounds to preserve diff backgrounds in Review tab
8+
* Omits font-family and font-size to inherit from parent components
9+
*/
10+
11+
import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism";
12+
import type { CSSProperties } from "react";
13+
14+
const OUTPUT_PATH = "src/styles/prism-syntax.css";
15+
16+
// Strip backgrounds like we do in syntaxHighlighting.ts
17+
const syntaxStyleNoBackgrounds: Record<string, CSSProperties> = {};
18+
for (const [key, value] of Object.entries(vscDarkPlus as Record<string, unknown>)) {
19+
if (typeof value === "object" && value !== null) {
20+
const { background, backgroundColor, ...rest } = value as Record<string, unknown>;
21+
if (Object.keys(rest).length > 0) {
22+
syntaxStyleNoBackgrounds[key] = rest as CSSProperties;
23+
}
24+
}
25+
}
26+
27+
// Convert CSS properties object to CSS string
28+
function cssPropertiesToString(props: CSSProperties): string {
29+
return Object.entries(props)
30+
.filter(([key]) => {
31+
// Skip font-family and font-size - we want to inherit these
32+
return key !== "fontFamily" && key !== "fontSize";
33+
})
34+
.map(([key, value]) => {
35+
// Convert camelCase to kebab-case
36+
const cssKey = key.replace(/([A-Z])/g, "-$1").toLowerCase();
37+
return ` ${cssKey}: ${value};`;
38+
})
39+
.join("\n");
40+
}
41+
42+
// Generate CSS content
43+
function generateCSS(): string {
44+
const lines: string[] = [
45+
"/**",
46+
" * Auto-generated Prism syntax highlighting styles",
47+
" * Based on VS Code Dark+ theme with backgrounds removed",
48+
" * Used when react-syntax-highlighter has useInlineStyles={false}",
49+
" *",
50+
" * Font family and size are intentionally omitted to inherit from parent.",
51+
" * ",
52+
" * To regenerate: bun run scripts/generate_prism_css.ts",
53+
" */",
54+
"",
55+
];
56+
57+
for (const [selector, props] of Object.entries(syntaxStyleNoBackgrounds)) {
58+
const cssRules = cssPropertiesToString(props);
59+
if (cssRules.trim().length > 0) {
60+
// Handle selectors that need .token prefix
61+
let cssSelector = selector;
62+
63+
// Add .token prefix for single-word selectors (token types)
64+
if (!/[ >[\]:.]/.test(selector) && !selector.startsWith("pre") && !selector.startsWith("code")) {
65+
cssSelector = `.token.${selector}`;
66+
}
67+
68+
lines.push(`${cssSelector} {`);
69+
lines.push(cssRules);
70+
lines.push("}");
71+
lines.push("");
72+
}
73+
}
74+
75+
return lines.join("\n");
76+
}
77+
78+
async function main() {
79+
console.log("Generating Prism CSS stylesheet...");
80+
81+
const css = generateCSS();
82+
83+
console.log(`Writing CSS to ${OUTPUT_PATH}...`);
84+
await Bun.write(OUTPUT_PATH, css);
85+
86+
console.log("✓ Prism CSS generated successfully");
87+
}
88+
89+
main().catch((error) => {
90+
console.error("Error generating Prism CSS:", error);
91+
process.exit(1);
92+
});
93+

src/components/shared/DiffRenderer.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
1010
import { syntaxStyleNoBackgrounds } from "@/styles/syntaxHighlighting";
1111
import { getLanguageFromPath } from "@/utils/git/languageDetector";
1212
import { Tooltip, TooltipWrapper } from "../Tooltip";
13+
import "@/styles/prism-syntax.css";
1314

1415
// Shared type for diff line types
1516
export type DiffLineType = "add" | "remove" | "context" | "header";
@@ -159,6 +160,7 @@ const HighlightedContent = React.memo<{ code: string; language: string }>(({ cod
159160
<SyntaxHighlighter
160161
language={language}
161162
style={syntaxStyleNoBackgrounds}
163+
useInlineStyles={false}
162164
PreTag="span"
163165
CodeTag="span"
164166
customStyle={{

0 commit comments

Comments
 (0)