From 91f950d45380da79b6caec174e3ad5c851bd9272 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Sat, 29 Nov 2025 23:21:40 +1000 Subject: [PATCH] feat: add syntax highlighting and improve README readability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Syntax Highlighting:** - Add react-syntax-highlighter with Solarized Light theme - Proper language detection from markdown code blocks - Beautiful syntax highlighting for all code examples - Improved inline code styling with subtle borders **Enhanced Readability:** - Larger base typography with prose-lg - Better contrast for text colors (zinc-700/zinc-300) - Improved heading spacing and hierarchy - Enhanced table styling with hover effects and better spacing - Table headers with uppercase, bold styling - Table cells with generous padding (px-6 py-4/py-3) - Row hover effects for better interaction - Better blockquote styling with blue accents - Improved list spacing with leading-relaxed - Enhanced image borders and shadows **Table Improvements:** - Professional header styling with background colors - Better cell padding and spacing - Hover effects on rows - Improved borders and shadows - Responsive overflow handling All code blocks now have beautiful Solarized Light syntax highlighting, and the overall typography is more readable and professional. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- apps/web/package.json | 2 + apps/web/src/components/Markdown.tsx | 129 ++++++++++++++++++--------- pnpm-lock.yaml | 81 +++++++++++++++++ 3 files changed, 172 insertions(+), 40 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 72ec1b3..cb5c089 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -17,11 +17,13 @@ "@tpmjs/types": "workspace:*", "@tpmjs/ui": "workspace:*", "@tpmjs/utils": "workspace:*", + "@types/react-syntax-highlighter": "^15.5.13", "next": "^16.0.4", "next-themes": "^0.4.6", "react": "^19.0.0", "react-dom": "^19.0.0", "react-markdown": "^10.1.0", + "react-syntax-highlighter": "^16.1.0", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", diff --git a/apps/web/src/components/Markdown.tsx b/apps/web/src/components/Markdown.tsx index 8caef0a..a6bc1cc 100644 --- a/apps/web/src/components/Markdown.tsx +++ b/apps/web/src/components/Markdown.tsx @@ -1,6 +1,9 @@ 'use client'; import ReactMarkdown from 'react-markdown'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +// eslint-disable-next-line import/no-internal-modules +import { solarizedlight } from 'react-syntax-highlighter/dist/esm/styles/prism'; import rehypeRaw from 'rehype-raw'; import rehypeSanitize from 'rehype-sanitize'; import remarkGfm from 'remark-gfm'; @@ -18,63 +21,71 @@ interface MarkdownProps { export function Markdown({ content, className = '' }: MarkdownProps): React.ReactElement { return (
{ + const match = /language-(\w+)/.exec(className || ''); + const language = match ? match[1] : ''; const isInline = !className; + if (isInline) { return ( {children} ); } + return ( - - {children} - - ); - }, - // Better styled pre blocks - pre: ({ children, ...props }) => { - return ( -
-                {children}
-              
+ {String(children).replace(/\n$/, '')} + + ); + }, + // Pre wrapper for code blocks (SyntaxHighlighter handles its own styling) + pre: ({ children, ...props }) => { + return ( +
+
{children}
+
); }, // Make links open in new tab with better styling @@ -92,12 +103,12 @@ export function Markdown({ content, className = '' }: MarkdownProps): React.Reac ); }, - // Better table styling + // Better table styling with improved readability table: ({ children, ...props }) => { return ( -
+
{children} @@ -105,6 +116,44 @@ export function Markdown({ content, className = '' }: MarkdownProps): React.Reac ); }, + // Table header with better styling + thead: ({ children, ...props }) => { + return ( + + {children} + + ); + }, + // Table header cells with better spacing + th: ({ children, ...props }) => { + return ( + + ); + }, + // Table data cells with better spacing + td: ({ children, ...props }) => { + return ( + + ); + }, + // Table rows with hover effect + tr: ({ children, ...props }) => { + return ( + + {children} + + ); + }, }} > {content} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b7e802..f3ca6fa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,9 @@ importers: '@tpmjs/utils': specifier: workspace:* version: link:../../packages/utils + '@types/react-syntax-highlighter': + specifier: ^15.5.13 + version: 15.5.13 next: specifier: ^16.0.4 version: 16.0.4(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -74,6 +77,9 @@ importers: react-markdown: specifier: ^10.1.0 version: 10.1.0(@types/react@19.2.7)(react@19.2.0) + react-syntax-highlighter: + specifier: ^16.1.0 + version: 16.1.0(react@19.2.0) rehype-raw: specifier: ^7.0.0 version: 7.0.0 @@ -1950,11 +1956,17 @@ packages: '@types/node@22.19.1': resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==} + '@types/prismjs@1.26.5': + resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: '@types/react': ^19.2.0 + '@types/react-syntax-highlighter@15.5.13': + resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} + '@types/react@19.2.7': resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} @@ -2907,6 +2919,9 @@ packages: fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fault@1.0.4: + resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + fd-package-json@2.0.0: resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} @@ -2953,6 +2968,10 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + format@0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + formatly@0.3.0: resolution: {integrity: sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==} engines: {node: '>=18.3.0'} @@ -3129,6 +3148,12 @@ packages: hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + + highlightjs-vue@1.0.0: + resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} + html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} @@ -3531,6 +3556,9 @@ packages: loupe@3.2.1: resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lowlight@1.20.0: + resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -4072,6 +4100,10 @@ packages: typescript: optional: true + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} @@ -4131,6 +4163,12 @@ packages: '@types/react': '>=18' react: '>=18' + react-syntax-highlighter@16.1.0: + resolution: {integrity: sha512-E40/hBiP5rCNwkeBN1vRP+xow1X0pndinO+z3h7HLsHyjztbyjfzNWNKuAsJj+7DLam9iT4AaaOZnueCU+Nplg==} + engines: {node: '>= 16.20.2'} + peerDependencies: + react: '>= 0.14.0' + react@19.2.0: resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} engines: {node: '>=0.10.0'} @@ -4166,6 +4204,9 @@ packages: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} + refractor@5.0.0: + resolution: {integrity: sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==} + regexp-tree@0.1.27: resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} hasBin: true @@ -6348,10 +6389,16 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/prismjs@1.26.5': {} + '@types/react-dom@19.2.3(@types/react@19.2.7)': dependencies: '@types/react': 19.2.7 + '@types/react-syntax-highlighter@15.5.13': + dependencies: + '@types/react': 19.2.7 + '@types/react@19.2.7': dependencies: csstype: 3.2.3 @@ -7704,6 +7751,10 @@ snapshots: dependencies: reusify: 1.1.0 + fault@1.0.4: + dependencies: + format: 0.2.2 + fd-package-json@2.0.0: dependencies: walk-up-path: 4.0.0 @@ -7752,6 +7803,8 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + format@0.2.2: {} + formatly@0.3.0: dependencies: fd-package-json: 2.0.0 @@ -7997,6 +8050,10 @@ snapshots: dependencies: hermes-estree: 0.25.1 + highlight.js@10.7.3: {} + + highlightjs-vue@1.0.0: {} + html-url-attributes@3.0.1: {} html-void-elements@3.0.0: {} @@ -8361,6 +8418,11 @@ snapshots: loupe@3.2.1: {} + lowlight@1.20.0: + dependencies: + fault: 1.0.4 + highlight.js: 10.7.3 + lru-cache@10.4.3: {} lru-cache@11.2.2: {} @@ -9128,6 +9190,8 @@ snapshots: transitivePeerDependencies: - magicast + prismjs@1.30.0: {} + process@0.11.10: {} prompts@2.4.2: @@ -9204,6 +9268,16 @@ snapshots: transitivePeerDependencies: - supports-color + react-syntax-highlighter@16.1.0(react@19.2.0): + dependencies: + '@babel/runtime': 7.28.4 + highlight.js: 10.7.3 + highlightjs-vue: 1.0.0 + lowlight: 1.20.0 + prismjs: 1.30.0 + react: 19.2.0 + refractor: 5.0.0 + react@19.2.0: {} read-cache@1.0.0: @@ -9251,6 +9325,13 @@ snapshots: get-proto: 1.0.1 which-builtin-type: 1.2.1 + refractor@5.0.0: + dependencies: + '@types/hast': 3.0.4 + '@types/prismjs': 1.26.5 + hastscript: 9.0.1 + parse-entities: 4.0.2 + regexp-tree@0.1.27: {} regexp.prototype.flags@1.5.4:
+ {children} + + {children} +