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
This commit is contained in:
parent
89804c5906
commit
d840aad3e9
@ -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 (
|
||||
|
@ -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 && <LoadingDots />}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,12 +131,54 @@ 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 (
|
||||
<div className="relative group flex items-center gap-2 px-2 py-1 bg-secondary/50 rounded-md my-2 text-xs w-fit">
|
||||
<FileText className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="font-mono">{content}</span>
|
||||
<div
|
||||
onClick={handleFileClick}
|
||||
className="group flex items-center gap-2 px-2 py-1 bg-secondary/50 rounded-md my-2 text-xs hover:bg-secondary cursor-pointer w-fit"
|
||||
>
|
||||
<FileText className="h-4 w-4" />
|
||||
<span className="font-mono group-hover:underline">{content}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -1288,6 +1288,7 @@ export default function CodeEditor({
|
||||
handleApplyCode={handleApplyCode}
|
||||
mergeDecorationsCollection={mergeDecorationsCollection}
|
||||
setMergeDecorationsCollection={setMergeDecorationsCollection}
|
||||
selectFile={selectFile}
|
||||
/>
|
||||
</ResizablePanel>
|
||||
</>
|
||||
|
Loading…
x
Reference in New Issue
Block a user