feat: file path above each code snippet

This commit is contained in:
Akhileshrangani4 2024-12-01 14:28:48 -05:00
parent aa33ad3031
commit 89804c5906
6 changed files with 73 additions and 24 deletions

View File

@ -1,16 +1,22 @@
import { currentUser } from "@clerk/nextjs" import {
import { Anthropic } from "@anthropic-ai/sdk" ignoredFiles,
import { TIERS } from "@/lib/tiers" ignoredFolders,
} from "@/components/editor/AIChat/lib/ignored-paths"
import { templateConfigs } from "@/lib/templates" import { templateConfigs } from "@/lib/templates"
import { TIERS } from "@/lib/tiers"
import { TFile, TFolder } from "@/lib/types" import { TFile, TFolder } from "@/lib/types"
import { ignoredFiles, ignoredFolders } from "@/components/editor/AIChat/lib/ignored-paths" import { Anthropic } from "@anthropic-ai/sdk"
import { currentUser } from "@clerk/nextjs"
const anthropic = new Anthropic({ const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY!, apiKey: process.env.ANTHROPIC_API_KEY!,
}) })
// Format file structure for context // Format file structure for context
function formatFileStructure(items: (TFile | TFolder)[] | undefined, prefix = ""): string { function formatFileStructure(
items: (TFile | TFolder)[] | undefined,
prefix = ""
): string {
if (!items || !Array.isArray(items)) { if (!items || !Array.isArray(items)) {
return "No files available" return "No files available"
} }
@ -23,15 +29,23 @@ function formatFileStructure(items: (TFile | TFolder)[] | undefined, prefix = ""
return sortedItems return sortedItems
.map((item) => { .map((item) => {
if (item.type === "file" && !ignoredFiles.some( if (
(pattern) => item.name.endsWith(pattern.replace("*", "")) || item.name === pattern item.type === "file" &&
)) { !ignoredFiles.some(
(pattern) =>
item.name.endsWith(pattern.replace("*", "")) ||
item.name === pattern
)
) {
return `${prefix}├── ${item.name}` return `${prefix}├── ${item.name}`
} else if ( } else if (
item.type === "folder" && item.type === "folder" &&
!ignoredFolders.some((folder) => folder === item.name) !ignoredFolders.some((folder) => folder === item.name)
) { ) {
const folderContent = formatFileStructure(item.children, `${prefix}`) const folderContent = formatFileStructure(
item.children,
`${prefix}`
)
return `${prefix}├── ${item.name}/\n${folderContent}` return `${prefix}├── ${item.name}/\n${folderContent}`
} }
return null return null
@ -94,6 +108,7 @@ export async function POST(request: Request) {
line, line,
templateType, templateType,
files, files,
projectName,
} = await request.json() } = await request.json()
// Get template configuration // Get template configuration
@ -138,13 +153,23 @@ Instructions: ${messages[0].content}
Respond only with the modified code that can directly replace the existing code.` Respond only with the modified code that can directly replace the existing code.`
} else { } else {
systemMessage = `You are an intelligent programming assistant for a ${templateType} project. Please respond to the following request concisely. If your response includes code, please format it using triple backticks (\`\`\`) with the appropriate language identifier. For example: systemMessage = `You are an intelligent programming assistant for a ${templateType} project. Please respond to the following request concisely. When providing code:
\`\`\`python 1. Format it using triple backticks (\`\`\`) with the appropriate language identifier.
print("Hello, World!") 2. Always specify the complete file path in the format:
\`\`\` ${projectName}/filepath/to/file.ext
Provide a clear and concise explanation along with any code snippets. Keep your response brief and to the point. 3. If creating a new file, specify the path as:
${projectName}/filepath/to/file.ext (new file)
4. Format your code blocks as:
${projectName}/filepath/to/file.ext
\`\`\`language
code here
\`\`\`
If multiple files are involved, repeat the format for each file. Provide a clear and concise explanation along with any code snippets. Keep your response brief and to the point.
This is the project template: This is the project template:
${templateContext} ${templateContext}

View File

@ -21,6 +21,7 @@ export default function AIChat({
handleApplyCode, handleApplyCode,
mergeDecorationsCollection, mergeDecorationsCollection,
setMergeDecorationsCollection, setMergeDecorationsCollection,
projectName,
}: AIChatProps) { }: AIChatProps) {
// Initialize socket and messages // Initialize socket and messages
const { socket } = useSocket() const { socket } = useSocket()
@ -152,7 +153,8 @@ export default function AIChat({
activeFileContent, activeFileContent,
false, false,
templateType, templateType,
files files,
projectName
) )
} }

View File

@ -93,7 +93,8 @@ export const handleSend = async (
activeFileContent: string, activeFileContent: string,
isEditMode: boolean = false, isEditMode: boolean = false,
templateType: string, templateType: string,
files: (TFile | TFolder)[] files: (TFile | TFolder)[],
projectName: string
) => { ) => {
// Return if input is empty and context is null // Return if input is empty and context is null
if (input.trim() === "" && !context) return if (input.trim() === "" && !context) return
@ -145,6 +146,7 @@ export const handleSend = async (
isEditMode: isEditMode, isEditMode: isEditMode,
templateType: templateType, templateType: templateType,
files: files, files: files,
projectName: projectName,
}), }),
signal: abortControllerRef.current.signal, signal: abortControllerRef.current.signal,
}) })
@ -241,3 +243,9 @@ export const looksLikeCode = (text: string): boolean => {
return codeIndicators.some((pattern) => pattern.test(text)) return codeIndicators.some((pattern) => pattern.test(text))
} }
// 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)
}

View File

@ -1,11 +1,11 @@
import { Check, CornerUpLeft, X } from "lucide-react" import { Check, CornerUpLeft, X, FileText } from "lucide-react"
import monaco from "monaco-editor" import monaco from "monaco-editor"
import { Components } from "react-markdown" import { Components } from "react-markdown"
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism" import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism"
import { Button } from "../../../ui/button" import { Button } from "../../../ui/button"
import ApplyButton from "../ApplyButton" import ApplyButton from "../ApplyButton"
import { stringifyContent } from "./chatUtils" import { stringifyContent, isFilePath } from "./chatUtils"
// Create markdown components for chat message component // Create markdown components for chat message component
export const createMarkdownComponents = ( export const createMarkdownComponents = (
@ -126,8 +126,20 @@ export const createMarkdownComponents = (
) )
}, },
// Render markdown elements // Render markdown elements
p: ({ node, children, ...props }) => p: ({ node, children, ...props }) => {
renderMarkdownElement({ node, children, ...props }), const content = stringifyContent(children)
if (isFilePath(content)) {
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>
)
}
return renderMarkdownElement({ node, children, ...props })
},
h1: ({ node, children, ...props }) => h1: ({ node, children, ...props }) =>
renderMarkdownElement({ node, children, ...props }), renderMarkdownElement({ node, children, ...props }),
h2: ({ node, children, ...props }) => h2: ({ node, children, ...props }) =>

View File

@ -56,6 +56,7 @@ export interface AIChatProps {
files: (TFile | TFolder)[] files: (TFile | TFolder)[]
templateType: string templateType: string
templateConfig?: TemplateConfig templateConfig?: TemplateConfig
projectName: string
handleApplyCode: (mergedCode: string) => void handleApplyCode: (mergedCode: string) => void
mergeDecorationsCollection?: monaco.editor.IEditorDecorationsCollection mergeDecorationsCollection?: monaco.editor.IEditorDecorationsCollection
setMergeDecorationsCollection?: (collection: undefined) => void setMergeDecorationsCollection?: (collection: undefined) => void

View File

@ -1284,6 +1284,7 @@ export default function CodeEditor({
lastCopiedRangeRef={lastCopiedRangeRef} lastCopiedRangeRef={lastCopiedRangeRef}
files={files} files={files}
templateType={sandboxData.type} templateType={sandboxData.type}
projectName={sandboxData.name}
handleApplyCode={handleApplyCode} handleApplyCode={handleApplyCode}
mergeDecorationsCollection={mergeDecorationsCollection} mergeDecorationsCollection={mergeDecorationsCollection}
setMergeDecorationsCollection={setMergeDecorationsCollection} setMergeDecorationsCollection={setMergeDecorationsCollection}