diff --git a/src/app/dashboard/[org]/[repo]/chat/ChatPage.tsx b/src/app/dashboard/[org]/[repo]/chat/ChatPage.tsx index 2227f38..662388a 100644 --- a/src/app/dashboard/[org]/[repo]/chat/ChatPage.tsx +++ b/src/app/dashboard/[org]/[repo]/chat/ChatPage.tsx @@ -1,10 +1,10 @@ -// components/ChatPage.tsx "use client"; import React, { useMemo } from "react"; import { Chat } from "./components/Chat"; import { api } from "~/trpc/react"; import LoadingIndicator from "../components/LoadingIndicator"; +import { useSearchParams } from "next/navigation"; interface ChatPageProps { org: string; @@ -21,6 +21,11 @@ const ChatPage: React.FC = ({ org, repo }) => { repo, }); + const searchParams = useSearchParams(); + const selectedFilePath = searchParams.get("file_path") + ? decodeURIComponent(searchParams.get("file_path")!) + : undefined; + const memoizedContextItems = useMemo( () => contextItems ?? [], [contextItems], @@ -40,6 +45,7 @@ const ChatPage: React.FC = ({ org, repo }) => { contextItems={memoizedContextItems} org={org} repo={repo} + selectedFilePath={selectedFilePath} /> ); diff --git a/src/app/dashboard/[org]/[repo]/chat/components/Chat.tsx b/src/app/dashboard/[org]/[repo]/chat/components/Chat.tsx index b16debf..deb997f 100644 --- a/src/app/dashboard/[org]/[repo]/chat/components/Chat.tsx +++ b/src/app/dashboard/[org]/[repo]/chat/components/Chat.tsx @@ -1,4 +1,3 @@ -// components/Chat.tsx import { useState, useEffect, useRef } from "react"; import { type Message, useChat } from "ai/react"; import { type Project } from "~/server/db/tables/projects.table"; @@ -22,6 +21,7 @@ interface ChatProps { contextItems: ContextItem[]; org: string; repo: string; + selectedFilePath?: string; } export interface CodeFile { @@ -44,7 +44,7 @@ const STARTING_MESSAGE = { "Hi, I'm JACoB. I can answer questions about your codebase. Ask me anything!", }; -export function Chat({ contextItems, org, repo }: ChatProps) { +export function Chat({ contextItems, org, repo, selectedFilePath }: ChatProps) { const [artifactContent, setArtifactContent] = useState(null); const [artifactFileName, setArtifactFileName] = useState(""); const [artifactLanguage, setArtifactLanguage] = useState(""); @@ -81,7 +81,6 @@ export function Chat({ contextItems, org, repo }: ChatProps) { initialMessages: savedMessages, onResponse: async (response) => { console.log("onResponse", response); - // turn off the loading indicator setHasStartedStreaming(true); }, onError: (error) => { @@ -115,9 +114,6 @@ export function Chat({ contextItems, org, repo }: ChatProps) { const lastMessage = messages[messages.length - 1]; if (!lastMessage) return; if (lastMessage.role === "assistant" && lastMessage.content.length > 0) { - // this is a workaround, but if the last message is an assistant message and there is - // more than a 1000ms gap between the last message and the onFinished call, then we know - // that the system is creating an artifact and we should show the loading card setIsCreatingArtifact(true); } }, [messages]); @@ -126,7 +122,6 @@ export function Chat({ contextItems, org, repo }: ChatProps) { useEffect(() => { if (model) { - // when the model changes, move the messages from the previous model to the new model setSavedMessages(messages); } }, [model, messages]); @@ -163,6 +158,20 @@ export function Chat({ contextItems, org, repo }: ChatProps) { } }, [codeContent]); + useEffect(() => { + if (selectedFilePath) { + const matchingFile = contextItems.find( + (item) => item.file === selectedFilePath, + ); + if (matchingFile) { + setSelectedFiles([selectedFilePath]); + void refetchCodeContent(); + } else { + toast.error("Selected file not found in the codebase context."); + } + } + }, [selectedFilePath, contextItems, refetchCodeContent]); + const handleSearchResultSelect = (filePath: string) => { setSelectedFiles([filePath]); void refetchCodeContent(); diff --git a/src/app/dashboard/[org]/[repo]/code-visualizer/codebase/CodebaseDetails.tsx b/src/app/dashboard/[org]/[repo]/code-visualizer/codebase/CodebaseDetails.tsx index 76f467f..dd60eb0 100644 --- a/src/app/dashboard/[org]/[repo]/code-visualizer/codebase/CodebaseDetails.tsx +++ b/src/app/dashboard/[org]/[repo]/code-visualizer/codebase/CodebaseDetails.tsx @@ -10,6 +10,7 @@ import { faChevronDown, faCopy, faCheck, + faComment, } from "@fortawesome/free-solid-svg-icons"; import Mermaid from "./Mermaid"; import Markdown, { type Components } from "react-markdown"; @@ -25,6 +26,7 @@ import { } from "react-syntax-highlighter/dist/cjs/styles/prism"; import { faClipboard } from "@fortawesome/free-solid-svg-icons"; import { toast } from "react-toastify"; +import { useRouter } from "next/navigation"; interface CodebaseDetailsProps { item: ContextItem; @@ -42,21 +44,20 @@ const copyToClipboard = async (text: string) => { toast.success("Copied to clipboard"); }; -// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-redundant-type-constituents -export const renderers: Partial = { +export const renderers: Partial = { code: ({ inline, className, - theme, children, + theme, ...props }: { - inline: boolean; - className: string; - theme: "light" | "dark"; + inline?: boolean; + className?: string; children: React.ReactNode; + theme?: "light" | "dark"; }) => { - const match = /language-(\w+)/.exec(className || ""); + const match = /language-(\w+)/.exec(className ?? ""); if (!inline && match) { return (
@@ -70,21 +71,18 @@ export const renderers: Partial = { style={theme === "dark" ? oneDark : oneLight} language={match[1]} PreTag="div" - {...props} > {String(children).replace(/\n$/, "")}
); } else if (inline) { - // Render inline code with `` instead of `
` return ( {children} ); } else { - // Fallback for non-highlighted code return ( {children} @@ -105,6 +103,7 @@ const CodebaseDetails: React.FC = ({ theme, }) => { const [copyStatus, setCopyStatus] = useState(false); + const router = useRouter(); const handleCopy = () => { navigator.clipboard @@ -118,8 +117,19 @@ const CodebaseDetails: React.FC = ({ }); }; + const handleSendToChat = () => { + if (item.file) { + const encodedFilePath = encodeURIComponent(item.file); + router.push( + `/dashboard/${item.file.split("/")[1]}/${item.file.split("/")[2]}/chat?file_path=${encodedFilePath}`, + ); + } else { + toast.error("No file selected to send to chat."); + } + }; + return ( -
+
-
+

{item.overview}

{item.diagram && }
= ({ ) : null}
+ +
+ +
); };