From bc64c031461b65f816bdbd63b836032114179bec Mon Sep 17 00:00:00 2001 From: Hiram Chirino Date: Thu, 3 Oct 2024 11:13:12 -0400 Subject: [PATCH] Format ai agent messages as markdown, also auto scroll when new messages come in. --- client/package.json | 1 + client/src/app/components/ai-assistant.tsx | 26 +++++++++--- client/src/components/ui/scroll-area.tsx | 6 ++- client/tailwind.config.js | 2 +- package-lock.json | 46 ++++++++++++++++++++++ 5 files changed, 72 insertions(+), 9 deletions(-) diff --git a/client/package.json b/client/package.json index 696c1df1..bcb60fa8 100644 --- a/client/package.json +++ b/client/package.json @@ -64,6 +64,7 @@ "devDependencies": { "@hey-api/openapi-ts": "^0.53.5", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", + "@tailwindcss/typography": "^0.5.15", "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.4.3", diff --git a/client/src/app/components/ai-assistant.tsx b/client/src/app/components/ai-assistant.tsx index 085d1161..f0e9671e 100644 --- a/client/src/app/components/ai-assistant.tsx +++ b/client/src/app/components/ai-assistant.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useState } from "react"; +import React, { useContext, useEffect, useRef, useState } from "react"; import { Button } from "@/components/ui/button"; import { Card, @@ -12,6 +12,7 @@ import { ScrollArea } from "@/components/ui/scroll-area"; import { MessageCircle, Send, X } from "lucide-react"; import { ChatMessage, ChatState } from "../client"; import { useCompletionMutation } from "../queries/ai"; +import ReactMarkdown from "react-markdown"; interface Conversation { isEnabled: boolean; @@ -106,16 +107,29 @@ export function AIAssistant({ viewing }: { viewing?: string }) { }); }; + const scrollAreaRef = useRef(null as HTMLDivElement | null); + const scrollToBottom = () => { + setTimeout(() => { + if (scrollAreaRef?.current) { + const scrollArea = scrollAreaRef.current; + scrollArea.scrollTop = scrollArea.scrollHeight; + } + }, 0); + }; + + useEffect(() => { + scrollToBottom(); + }, [completionMutation.isPending]); + if (!isEnabled) { return null; } - return ( <> {/* Floating button */} {!isOpen && ( - +
Hello there! How can I help you today? @@ -153,13 +167,13 @@ export function AIAssistant({ viewing }: { viewing?: string }) { }`} > - {m.content} + {m.content}
))} diff --git a/client/src/components/ui/scroll-area.tsx b/client/src/components/ui/scroll-area.tsx index 32cf968d..1f428b3b 100644 --- a/client/src/components/ui/scroll-area.tsx +++ b/client/src/components/ui/scroll-area.tsx @@ -10,11 +10,13 @@ const ScrollArea = React.forwardRef< React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - + {children} diff --git a/client/tailwind.config.js b/client/tailwind.config.js index 014252b7..926ec26e 100644 --- a/client/tailwind.config.js +++ b/client/tailwind.config.js @@ -73,5 +73,5 @@ module.exports = { }, }, }, - plugins: [require("tailwindcss-animate")], + plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")], }; diff --git a/package-lock.json b/package-lock.json index 2cc9ba5b..e117d1fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -105,6 +105,7 @@ "devDependencies": { "@hey-api/openapi-ts": "^0.53.5", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", + "@tailwindcss/typography": "^0.5.15", "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.4.3", @@ -5255,6 +5256,36 @@ "url": "https://www.patreon.com/athan" } }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.15.tgz", + "integrity": "sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20" + } + }, + "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@tanstack/eslint-plugin-query": { "version": "4.38.0", "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-4.38.0.tgz", @@ -13932,11 +13963,25 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -17094,6 +17139,7 @@ "version": "8.0.7", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz", "integrity": "sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==", + "license": "MIT", "dependencies": { "@types/hast": "^2.0.0", "@types/prop-types": "^15.0.0",