feat: add dynamic file structure context in AI chat
- Improved file structure formatting with tree-like visualization - Added filtering for ignored files and folders - Added scripts section to template context - Fixed folder hierarchy display with proper indentation - Maintains sorting with folders first, then files alphabetically - Now uses actual project files instead of template structure Example output: ├── app/ │ ├── api/ │ └── page.tsx ├── components/ └── package.json
This commit is contained in:
parent
1bb518b1af
commit
f79115974c
@ -2,11 +2,44 @@ import { currentUser } from "@clerk/nextjs"
|
|||||||
import { Anthropic } from "@anthropic-ai/sdk"
|
import { Anthropic } from "@anthropic-ai/sdk"
|
||||||
import { TIERS } from "@/lib/tiers"
|
import { TIERS } from "@/lib/tiers"
|
||||||
import { templateConfigs } from "@/lib/templates"
|
import { templateConfigs } from "@/lib/templates"
|
||||||
|
import { TFile, TFolder } from "@/lib/types"
|
||||||
|
import { ignoredFiles, ignoredFolders } from "@/components/editor/AIChat/lib/ignored-paths"
|
||||||
|
|
||||||
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
|
||||||
|
function formatFileStructure(items: (TFile | TFolder)[] | undefined, prefix = ""): string {
|
||||||
|
if (!items || !Array.isArray(items)) {
|
||||||
|
return "No files available"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort items to show folders first, then files
|
||||||
|
const sortedItems = [...items].sort((a, b) => {
|
||||||
|
if (a.type === b.type) return a.name.localeCompare(b.name)
|
||||||
|
return a.type === "folder" ? -1 : 1
|
||||||
|
})
|
||||||
|
|
||||||
|
return sortedItems
|
||||||
|
.map((item) => {
|
||||||
|
if (item.type === "file" && !ignoredFiles.some(
|
||||||
|
(pattern) => item.name.endsWith(pattern.replace("*", "")) || item.name === pattern
|
||||||
|
)) {
|
||||||
|
return `${prefix}├── ${item.name}`
|
||||||
|
} else if (
|
||||||
|
item.type === "folder" &&
|
||||||
|
!ignoredFolders.some((folder) => folder === item.name)
|
||||||
|
) {
|
||||||
|
const folderContent = formatFileStructure(item.children, `${prefix}│ `)
|
||||||
|
return `${prefix}├── ${item.name}/\n${folderContent}`
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
try {
|
try {
|
||||||
const user = await currentUser()
|
const user = await currentUser()
|
||||||
@ -60,6 +93,7 @@ export async function POST(request: Request) {
|
|||||||
fileName,
|
fileName,
|
||||||
line,
|
line,
|
||||||
templateType,
|
templateType,
|
||||||
|
files,
|
||||||
} = await request.json()
|
} = await request.json()
|
||||||
|
|
||||||
// Get template configuration
|
// Get template configuration
|
||||||
@ -70,16 +104,17 @@ export async function POST(request: Request) {
|
|||||||
? `
|
? `
|
||||||
Project Template: ${templateConfig.name}
|
Project Template: ${templateConfig.name}
|
||||||
|
|
||||||
File Structure:
|
Current File Structure:
|
||||||
${Object.entries(templateConfig.fileStructure)
|
${files ? formatFileStructure(files) : "No files available"}
|
||||||
.map(([path, info]) => `${path} - ${info.description}`)
|
|
||||||
.join("\n")}
|
|
||||||
|
|
||||||
Conventions:
|
Conventions:
|
||||||
${templateConfig.conventions.join("\n")}
|
${templateConfig.conventions.join("\n")}
|
||||||
|
|
||||||
Dependencies:
|
Dependencies:
|
||||||
${JSON.stringify(templateConfig.dependencies, null, 2)}
|
${JSON.stringify(templateConfig.dependencies, null, 2)}
|
||||||
|
|
||||||
|
Scripts:
|
||||||
|
${JSON.stringify(templateConfig.scripts, null, 2)}
|
||||||
`
|
`
|
||||||
: ""
|
: ""
|
||||||
|
|
||||||
|
@ -148,7 +148,8 @@ export default function AIChat({
|
|||||||
abortControllerRef,
|
abortControllerRef,
|
||||||
activeFileContent,
|
activeFileContent,
|
||||||
false,
|
false,
|
||||||
templateType
|
templateType,
|
||||||
|
files
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { TFolder, TFile } from "@/lib/types"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
|
|
||||||
// Stringify content for chat message component
|
// Stringify content for chat message component
|
||||||
@ -91,7 +92,8 @@ export const handleSend = async (
|
|||||||
abortControllerRef: React.MutableRefObject<AbortController | null>,
|
abortControllerRef: React.MutableRefObject<AbortController | null>,
|
||||||
activeFileContent: string,
|
activeFileContent: string,
|
||||||
isEditMode: boolean = false,
|
isEditMode: boolean = false,
|
||||||
templateType: string
|
templateType: string,
|
||||||
|
files: (TFile | TFolder)[]
|
||||||
) => {
|
) => {
|
||||||
// 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
|
||||||
@ -142,6 +144,7 @@ export const handleSend = async (
|
|||||||
activeFileContent: activeFileContent,
|
activeFileContent: activeFileContent,
|
||||||
isEditMode: isEditMode,
|
isEditMode: isEditMode,
|
||||||
templateType: templateType,
|
templateType: templateType,
|
||||||
|
files: files,
|
||||||
}),
|
}),
|
||||||
signal: abortControllerRef.current.signal,
|
signal: abortControllerRef.current.signal,
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user