diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 58d78031..33300bd4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -44,6 +44,7 @@ "react-icons": "^5.5.0", "react-markdown": "^10.1.0", "react-syntax-highlighter": "^15.6.1", + "react-textarea-autosize": "^8.5.9", "rehype-mathjax": "^7.1.0", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", @@ -8473,6 +8474,23 @@ "react": ">= 0.14.0" } }, + "node_modules/react-textarea-autosize": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz", + "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -10126,6 +10144,51 @@ } } }, + "node_modules/use-composed-ref": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz", + "integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", + "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-latest": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz", + "integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==", + "license": "MIT", + "dependencies": { + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sidecar": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index bc9eb72c..fd996c33 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -45,6 +45,7 @@ "react-icons": "^5.5.0", "react-markdown": "^10.1.0", "react-syntax-highlighter": "^15.6.1", + "react-textarea-autosize": "^8.5.9", "rehype-mathjax": "^7.1.0", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", diff --git a/frontend/src/app/chat/page.tsx b/frontend/src/app/chat/page.tsx index 322073a5..01ee43c7 100644 --- a/frontend/src/app/chat/page.tsx +++ b/frontend/src/app/chat/page.tsx @@ -17,6 +17,7 @@ import { Zap, } from "lucide-react"; import { useEffect, useRef, useState } from "react"; +import TextareaAutosize from "react-textarea-autosize"; import { filterAccentClasses } from "@/components/knowledge-filter-panel"; import { MarkdownRenderer } from "@/components/markdown-renderer"; import { ProtectedRoute } from "@/components/protected-route"; @@ -132,6 +133,7 @@ function ChatPage() { const [availableFilters, setAvailableFilters] = useState< KnowledgeFilterData[] >([]); + const [textareaHeight, setTextareaHeight] = useState(40); const [filterSearchTerm, setFilterSearchTerm] = useState(""); const [selectedFilterIndex, setSelectedFilterIndex] = useState(0); const [isFilterHighlighted, setIsFilterHighlighted] = useState(false); @@ -2024,14 +2026,16 @@ function ChatPage() { setIsFilterDropdownOpen(true); setFilterSearchTerm(""); setSelectedFilterIndex(0); - + // Get button position for popover anchoring - const button = document.querySelector('[data-filter-button]') as HTMLElement; + const button = document.querySelector( + "[data-filter-button]", + ) as HTMLElement; if (button) { const rect = button.getBoundingClientRect(); setAnchorPosition({ x: rect.left + rect.width / 2, - y: rect.top + rect.height / 2 - 12 + y: rect.top + rect.height / 2 - 12, }); } } else { @@ -2256,18 +2260,29 @@ function ChatPage() { )} -