From 4e1c5cac270f9e7d84d3b562159fdbb29d0dce10 Mon Sep 17 00:00:00 2001 From: Akhileshrangani4 Date: Sun, 1 Dec 2024 18:52:02 -0500 Subject: [PATCH] feat: Add clickable file paths in AI chat responses - Detect file paths with dots in directory names (e.g. next/styles/SignIn.module.css) - Create new files when path ends with "(new file)" - Use existing socket connection and file management system --- .../components/editor/AIChat/ChatMessage.tsx | 4 +- frontend/components/editor/AIChat/index.tsx | 2 + .../components/editor/AIChat/lib/chatUtils.ts | 8 ++- .../editor/AIChat/lib/markdownComponents.tsx | 59 ++++++++++++++++--- .../components/editor/AIChat/types/index.ts | 4 +- frontend/components/editor/index.tsx | 1 + 6 files changed, 66 insertions(+), 12 deletions(-) diff --git a/frontend/components/editor/AIChat/ChatMessage.tsx b/frontend/components/editor/AIChat/ChatMessage.tsx index caded21..8e93c17 100644 --- a/frontend/components/editor/AIChat/ChatMessage.tsx +++ b/frontend/components/editor/AIChat/ChatMessage.tsx @@ -19,6 +19,7 @@ export default function ChatMessage({ editorRef, mergeDecorationsCollection, setMergeDecorationsCollection, + selectFile, }: MessageProps) { // State for expanded message index const [expandedMessageIndex, setExpandedMessageIndex] = useState< @@ -115,8 +116,9 @@ export default function ChatMessage({ activeFileContent, editorRef, handleApplyCode, + selectFile, mergeDecorationsCollection, - setMergeDecorationsCollection + setMergeDecorationsCollection, ) return ( diff --git a/frontend/components/editor/AIChat/index.tsx b/frontend/components/editor/AIChat/index.tsx index 5d3ff69..ed2b6a9 100644 --- a/frontend/components/editor/AIChat/index.tsx +++ b/frontend/components/editor/AIChat/index.tsx @@ -19,6 +19,7 @@ export default function AIChat({ files, templateType, handleApplyCode, + selectFile, mergeDecorationsCollection, setMergeDecorationsCollection, projectName, @@ -225,6 +226,7 @@ export default function AIChat({ editorRef={editorRef} mergeDecorationsCollection={mergeDecorationsCollection} setMergeDecorationsCollection={setMergeDecorationsCollection} + selectFile={selectFile} /> ))} {isLoading && } diff --git a/frontend/components/editor/AIChat/lib/chatUtils.ts b/frontend/components/editor/AIChat/lib/chatUtils.ts index 1e3e956..70d50e3 100644 --- a/frontend/components/editor/AIChat/lib/chatUtils.ts +++ b/frontend/components/editor/AIChat/lib/chatUtils.ts @@ -1,4 +1,4 @@ -import { TFolder, TFile } from "@/lib/types" +import { TFile, TFolder } from "@/lib/types" import React from "react" // Stringify content for chat message component @@ -246,6 +246,8 @@ export const looksLikeCode = (text: string): boolean => { // Add this new function after looksLikeCode function export const isFilePath = (text: string): boolean => { - // Match patterns like project/path/to/file.ext or project/path/to/file.ext (new file) - return /^[a-zA-Z0-9_-]+\/[\w/-]+\.[a-zA-Z0-9]+(\s+\(new file\))?$/.test(text) + // Match patterns like next/styles/SignIn.module.css or path/to/file.ext (new file) + const pattern = + /^(?:[a-zA-Z0-9_.-]+\/)*[a-zA-Z0-9_.-]+\.[a-zA-Z0-9]+(\s+\(new file\))?$/ + return pattern.test(text) } diff --git a/frontend/components/editor/AIChat/lib/markdownComponents.tsx b/frontend/components/editor/AIChat/lib/markdownComponents.tsx index 0a6dcd3..36130d5 100644 --- a/frontend/components/editor/AIChat/lib/markdownComponents.tsx +++ b/frontend/components/editor/AIChat/lib/markdownComponents.tsx @@ -1,11 +1,13 @@ -import { Check, CornerUpLeft, X, FileText } from "lucide-react" +import { useSocket } from "@/context/SocketContext" +import { TTab } from "@/lib/types" +import { Check, CornerUpLeft, FileText, X } from "lucide-react" import monaco from "monaco-editor" import { Components } from "react-markdown" import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism" import { Button } from "../../../ui/button" import ApplyButton from "../ApplyButton" -import { stringifyContent, isFilePath } from "./chatUtils" +import { isFilePath, stringifyContent } from "./chatUtils" // Create markdown components for chat message component export const createMarkdownComponents = ( @@ -16,6 +18,7 @@ export const createMarkdownComponents = ( activeFileContent: string, editorRef: any, handleApplyCode: (mergedCode: string) => void, + selectFile: (tab: TTab) => void, mergeDecorationsCollection?: monaco.editor.IEditorDecorationsCollection, setMergeDecorationsCollection?: (collection: undefined) => void ): Components => ({ @@ -128,16 +131,58 @@ export const createMarkdownComponents = ( // Render markdown elements p: ({ node, children, ...props }) => { const content = stringifyContent(children) - + const { socket } = useSocket() + if (isFilePath(content)) { + const isNewFile = content.endsWith("(new file)") + const filePath = ( + isNewFile ? content.replace(" (new file)", "") : content + ) + .split("/") + .filter((part, index) => index !== 0) + .join("/") + + const handleFileClick = () => { + if (isNewFile) { + socket?.emit( + "createFile", + { + name: filePath, + }, + (response: any) => { + if (response.success) { + const tab: TTab = { + id: filePath, + name: filePath.split("/").pop() || "", + saved: true, + type: "file", + } + selectFile(tab) + } + } + ) + } else { + const tab: TTab = { + id: filePath, + name: filePath.split("/").pop() || "", + saved: true, + type: "file", + } + selectFile(tab) + } + } + return ( -
- - {content} +
+ + {content}
) } - + return renderMarkdownElement({ node, children, ...props }) }, h1: ({ node, children, ...props }) => diff --git a/frontend/components/editor/AIChat/types/index.ts b/frontend/components/editor/AIChat/types/index.ts index 39ed75e..5eb7362 100644 --- a/frontend/components/editor/AIChat/types/index.ts +++ b/frontend/components/editor/AIChat/types/index.ts @@ -1,5 +1,5 @@ import { TemplateConfig } from "@/lib/templates" -import { TFile, TFolder } from "@/lib/types" +import { TFile, TFolder, TTab } from "@/lib/types" import * as monaco from "monaco-editor" import { Socket } from "socket.io-client" @@ -60,6 +60,7 @@ export interface AIChatProps { handleApplyCode: (mergedCode: string) => void mergeDecorationsCollection?: monaco.editor.IEditorDecorationsCollection setMergeDecorationsCollection?: (collection: undefined) => void + selectFile: (tab: TTab) => void } // Chat input props interface @@ -115,6 +116,7 @@ export interface MessageProps { editorRef: any mergeDecorationsCollection?: monaco.editor.IEditorDecorationsCollection setMergeDecorationsCollection?: (collection: undefined) => void + selectFile: (tab: TTab) => void } // Context tabs props interface diff --git a/frontend/components/editor/index.tsx b/frontend/components/editor/index.tsx index 049b623..c470f17 100644 --- a/frontend/components/editor/index.tsx +++ b/frontend/components/editor/index.tsx @@ -1288,6 +1288,7 @@ export default function CodeEditor({ handleApplyCode={handleApplyCode} mergeDecorationsCollection={mergeDecorationsCollection} setMergeDecorationsCollection={setMergeDecorationsCollection} + selectFile={selectFile} />