From 64f5bb0243f7e5130d1a339d1e5f60be55c1344c Mon Sep 17 00:00:00 2001 From: yangdx Date: Tue, 22 Apr 2025 17:21:24 +0800 Subject: [PATCH] Add typography to webui and fix markdown render problem --- lightrag_webui/bun.lock | 23 ++ lightrag_webui/package.json | 1 + .../src/components/retrieval/ChatMessage.tsx | 21 +- lightrag_webui/tailwind.config.js | 272 ++++++++++++++++++ lightrag_webui/vite.config.ts | 5 +- 5 files changed, 313 insertions(+), 9 deletions(-) create mode 100644 lightrag_webui/tailwind.config.js diff --git a/lightrag_webui/bun.lock b/lightrag_webui/bun.lock index 0b11243d..30bcec95 100644 --- a/lightrag_webui/bun.lock +++ b/lightrag_webui/bun.lock @@ -55,6 +55,7 @@ "sonner": "^1.7.4", "tailwind-merge": "^3.0.2", "tailwind-scrollbar": "^4.0.1", + "typography": "^0.16.24", "zustand": "^5.0.3", }, "devDependencies": { @@ -659,10 +660,16 @@ "commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], + "compass-vertical-rhythm": ["compass-vertical-rhythm@1.4.5", "", { "dependencies": { "convert-css-length": "^1.0.1", "object-assign": "^4.1.0", "parse-unit": "^1.0.1" } }, "sha512-bJo3IYX7xmmZCDYjrT2XolaiNjGZ4E2JvUGxpdU0ecbH4ZLK786wvc8aHKVrGrKct9JlkmJbUi8YLrQWvOc+uA=="], + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], "confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], + "console-polyfill": ["console-polyfill@0.1.2", "", {}, "sha512-oHLGQmf0q2yuuqfTXuzAB5UMqgPH1cHdwLkjfCqRTG2eupc52jbXT1OtOlREv+yXmXRi3wqywAevz3qMSk90Hg=="], + + "convert-css-length": ["convert-css-length@1.0.2", "", { "dependencies": { "console-polyfill": "^0.1.2", "parse-unit": "^1.0.1" } }, "sha512-ecV7j3hXyXN1X2XfJBzhMR0o1Obv0v3nHmn0UiS3ACENrzbxE/EknkiunS/fCwQva0U62X1GChi8GaPh4oTlLg=="], + "convert-source-map": ["convert-source-map@1.9.0", "", {}, "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="], "cookie": ["cookie@1.0.2", "https://registry.npmmirror.com/cookie/-/cookie-1.0.2.tgz", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="], @@ -757,6 +764,8 @@ "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="], + "decode-named-character-reference": ["decode-named-character-reference@1.0.2", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg=="], "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], @@ -925,6 +934,8 @@ "graphology-utils": ["graphology-utils@2.5.2", "", { "peerDependencies": { "graphology-types": ">=0.23.0" } }, "sha512-ckHg8MXrXJkOARk56ZaSCM1g1Wihe2d6iTmz1enGOz4W/l831MBCKSayeFQfowgF8wd+PQ4rlch/56Vs/VZLDQ=="], + "gray-percentage": ["gray-percentage@2.0.0", "", {}, "sha512-T0i4bwJoXbweuBM7bJwil9iHVAwXxmS9IFsEy27cXvRYxHwR2YVSBSXBjJw4EDKUvLpfjANeT5PrvTuAH1XnTw=="], + "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], @@ -1101,8 +1112,12 @@ "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + "lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="], + "lodash.isnumber": ["lodash.isnumber@3.0.3", "", {}, "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="], + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], @@ -1229,6 +1244,8 @@ "mnemonist": ["mnemonist@0.39.8", "", { "dependencies": { "obliterator": "^2.0.1" } }, "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ=="], + "modularscale": ["modularscale@1.0.2", "", { "dependencies": { "lodash.isnumber": "^3.0.0" } }, "sha512-xfu46hZcAL9xU8S/RihHE49rmDYRAg36lQez49pcO6/aLBJ7cfVr5DH7Obo2PGKTCSAyy4iTAsWZa9apECK9mQ=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], @@ -1269,6 +1286,8 @@ "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + "parse-unit": ["parse-unit@1.0.1", "", {}, "sha512-hrqldJHokR3Qj88EIlV/kAyAi/G5R2+R56TBANxNMy0uPlYcttx0jnMW6Yx5KsKPSbC3KddM/7qQm3+0wEXKxg=="], + "path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="], "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], @@ -1491,6 +1510,10 @@ "typescript-eslint": ["typescript-eslint@8.24.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.24.1", "@typescript-eslint/parser": "8.24.1", "@typescript-eslint/utils": "8.24.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-cw3rEdzDqBs70TIcb0Gdzbt6h11BSs2pS0yaq7hDWDBtCCSei1pPSUXE9qUdQ/Wm9NgFg8mKtMt1b8fTHIl1jA=="], + "typography": ["typography@0.16.24", "", { "dependencies": { "compass-vertical-rhythm": "^1.4.5", "decamelize": "^1.2.0", "gray-percentage": "^2.0.0", "lodash": "^4.13.1", "modularscale": "^1.0.2", "object-assign": "^4.1.0", "typography-normalize": "^0.16.19" } }, "sha512-o5jNctzGoJm2XgdqivJdpkF6lQkcQo8v1biMGY+rLSpBHhpCKdQv5em9S3R6igApxVYtbhNBJbV95vK9oPwRKQ=="], + + "typography-normalize": ["typography-normalize@0.16.19", "", {}, "sha512-vtnSv/uGBZVbd4e/ZhZB9HKBgKKlWQUXw74+ADIHHxzKp27CEf8PSR8TX1zF2qSyQ9/qMdqLwXYz8yRQFq9JLQ=="], + "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], diff --git a/lightrag_webui/package.json b/lightrag_webui/package.json index ef3d197e..f043aaa7 100644 --- a/lightrag_webui/package.json +++ b/lightrag_webui/package.json @@ -64,6 +64,7 @@ "sonner": "^1.7.4", "tailwind-merge": "^3.0.2", "tailwind-scrollbar": "^4.0.1", + "typography": "^0.16.24", "zustand": "^5.0.3" }, "devDependencies": { diff --git a/lightrag_webui/src/components/retrieval/ChatMessage.tsx b/lightrag_webui/src/components/retrieval/ChatMessage.tsx index 54c29937..c8b514eb 100644 --- a/lightrag_webui/src/components/retrieval/ChatMessage.tsx +++ b/lightrag_webui/src/components/retrieval/ChatMessage.tsx @@ -24,9 +24,6 @@ export type MessageWithError = Message & { export const ChatMessage = ({ message }: { message: MessageWithError }) => { const { t } = useTranslation() - // Remove extra spaces around bold text - message.content = message.content.replace(/\* {3}/g, '').replace(/ {4}\*\*/g, '**') - const handleCopyMarkdown = useCallback(async () => { if (message.content) { try { @@ -47,14 +44,22 @@ export const ChatMessage = ({ message }: { message: MessageWithError }) => { : 'bg-muted' }`} > -
+      

{children}

, + h1: ({ children }) =>

{children}

, + h2: ({ children }) =>

{children}

, + h3: ({ children }) =>

{children}

, + h4: ({ children }) =>

{children}

, + ul: ({ children }) =>
    {children}
, + ol: ({ children }) =>
    {children}
, + li: ({ children }) =>
  • {children}
  • }} > {message.content} @@ -70,7 +75,7 @@ export const ChatMessage = ({ message }: { message: MessageWithError }) => { {/* Explicit size */} )} -
    + {message.content === '' && } {/* Check for empty string specifically */} ) @@ -246,7 +251,7 @@ const CodeHighlight = ({ className, children, node, ...props }: CodeHighlightPro ) : ( // Handle inline code {children} diff --git a/lightrag_webui/tailwind.config.js b/lightrag_webui/tailwind.config.js new file mode 100644 index 00000000..07e9254d --- /dev/null +++ b/lightrag_webui/tailwind.config.js @@ -0,0 +1,272 @@ +/** @type {import('tailwindcss').Config} */ +export default { + darkMode: ['class'], + content: [ + './pages/**/*.{ts,tsx}', + './components/**/*.{ts,tsx}', + './app/**/*.{ts,tsx}', + './src/**/*.{ts,tsx}', + ], + theme: { + container: { + center: true, + padding: '2rem', + screens: { + '2xl': '1400px', + }, + }, + extend: { + colors: { + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', + }, + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', + }, + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)', + }, + keyframes: { + 'accordion-down': { + from: { height: 0 }, + to: { height: 'var(--radix-accordion-content-height)' }, + }, + 'accordion-up': { + from: { height: 'var(--radix-accordion-content-height)' }, + to: { height: 0 }, + }, + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out', + }, + typography: { + DEFAULT: { + css: { + maxWidth: '100%', + color: 'var(--tw-prose-body)', + '[class~="lead"]': { + color: 'var(--tw-prose-lead)', + }, + a: { + color: 'var(--tw-prose-links)', + textDecoration: 'underline', + fontWeight: '500', + }, + strong: { + color: 'var(--tw-prose-bold)', + fontWeight: '600', + }, + 'ol[type="A"]': { + listStyleType: 'upper-alpha', + }, + 'ol[type="a"]': { + listStyleType: 'lower-alpha', + }, + 'ol[type="A" s]': { + listStyleType: 'upper-alpha', + }, + 'ol[type="a" s]': { + listStyleType: 'lower-alpha', + }, + 'ol[type="I"]': { + listStyleType: 'upper-roman', + }, + 'ol[type="i"]': { + listStyleType: 'lower-roman', + }, + 'ol[type="I" s]': { + listStyleType: 'upper-roman', + }, + 'ol[type="i" s]': { + listStyleType: 'lower-roman', + }, + 'ol[type="1"]': { + listStyleType: 'decimal', + }, + 'ol > li': { + position: 'relative', + paddingLeft: '1.75em', + }, + 'ol > li::before': { + content: 'counter(list-item, var(--list-counter-style, decimal)) "."', + position: 'absolute', + fontWeight: '400', + color: 'var(--tw-prose-counters)', + left: '0', + }, + 'ul > li': { + position: 'relative', + paddingLeft: '1.75em', + }, + 'ul > li::before': { + content: '""', + position: 'absolute', + backgroundColor: 'var(--tw-prose-bullets)', + borderRadius: '50%', + width: '0.375em', + height: '0.375em', + top: 'calc(0.875em - 0.1875em)', + left: '0.25em', + }, + hr: { + borderColor: 'var(--tw-prose-hr)', + borderTopWidth: 1, + marginTop: '3em', + marginBottom: '3em', + }, + blockquote: { + fontWeight: '500', + fontStyle: 'italic', + color: 'var(--tw-prose-quotes)', + borderLeftWidth: '0.25rem', + borderLeftColor: 'var(--tw-prose-quote-borders)', + quotes: '"\\201C""\\201D""\\2018""\\2019"', + marginTop: '1.6em', + marginBottom: '1.6em', + paddingLeft: '1em', + }, + h1: { + color: 'var(--tw-prose-headings)', + fontWeight: '800', + fontSize: '2.25em', + marginTop: '0', + marginBottom: '0.8888889em', + lineHeight: '1.1111111', + }, + h2: { + color: 'var(--tw-prose-headings)', + fontWeight: '700', + fontSize: '1.5em', + marginTop: '2em', + marginBottom: '1em', + lineHeight: '1.3333333', + }, + h3: { + color: 'var(--tw-prose-headings)', + fontWeight: '600', + fontSize: '1.25em', + marginTop: '1.6em', + marginBottom: '0.6em', + lineHeight: '1.6', + }, + h4: { + color: 'var(--tw-prose-headings)', + fontWeight: '600', + marginTop: '1.5em', + marginBottom: '0.5em', + lineHeight: '1.5', + }, + 'figure > *': { + margin: '0', + }, + figcaption: { + color: 'var(--tw-prose-captions)', + fontSize: '0.875em', + lineHeight: '1.4285714', + marginTop: '0.8571429em', + }, + code: { + color: 'var(--tw-prose-code)', + fontWeight: '600', + fontSize: '0.875em', + }, + 'code::before': { + content: '""', + }, + 'code::after': { + content: '""', + }, + 'a code': { + color: 'var(--tw-prose-links)', + }, + 'h1 code': { + color: 'inherit', + }, + 'h2 code': { + color: 'inherit', + fontSize: '0.875em', + }, + 'h3 code': { + color: 'inherit', + fontSize: '0.9em', + }, + 'h4 code': { + color: 'inherit', + }, + 'blockquote code': { + color: 'inherit', + }, + 'thead': { + color: 'var(--tw-prose-headings)', + fontWeight: '600', + borderBottomWidth: '1px', + borderBottomColor: 'var(--tw-prose-th-borders)', + }, + 'thead th': { + verticalAlign: 'bottom', + paddingRight: '0.5714286em', + paddingBottom: '0.5714286em', + paddingLeft: '0.5714286em', + }, + 'tbody tr': { + borderBottomWidth: '1px', + borderBottomColor: 'var(--tw-prose-td-borders)', + }, + 'tbody tr:last-child': { + borderBottomWidth: '0', + }, + 'tbody td': { + verticalAlign: 'baseline', + paddingTop: '0.5714286em', + paddingRight: '0.5714286em', + paddingBottom: '0.5714286em', + paddingLeft: '0.5714286em', + }, + p: { + marginTop: '1.25em', + marginBottom: '1.25em', + }, + }, + }, + }, + }, + }, + plugins: [ + // Using ES module imports + // Note: This assumes these packages support ES module imports + // If issues occur, may need to fallback to require() and disable ESLint rules + import('tailwindcss-animate').then(module => module.default), + import('@tailwindcss/typography').then(module => module.default), + ], +} diff --git a/lightrag_webui/vite.config.ts b/lightrag_webui/vite.config.ts index 52030dc4..ea76f7b1 100644 --- a/lightrag_webui/vite.config.ts +++ b/lightrag_webui/vite.config.ts @@ -36,7 +36,10 @@ export default defineConfig({ // Mermaid-related modules 'mermaid-vendor': ['mermaid'], - + + // Typography-related modules + 'typography-vendor': ['typography'], + // Markdown-related modules 'markdown-vendor': [ 'react-markdown',