From f79115974c05ed1bc58df72d382700dffec9cdc5 Mon Sep 17 00:00:00 2001 From: Akhileshrangani4 Date: Sat, 30 Nov 2024 15:53:17 -0500 Subject: [PATCH] feat: add dynamic file structure context in AI chat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- frontend/app/api/ai/route.ts | 43 +++++++++++++++++-- frontend/components/editor/AIChat/index.tsx | 3 +- .../components/editor/AIChat/lib/chatUtils.ts | 5 ++- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/frontend/app/api/ai/route.ts b/frontend/app/api/ai/route.ts index 01e189c..9df0801 100644 --- a/frontend/app/api/ai/route.ts +++ b/frontend/app/api/ai/route.ts @@ -2,11 +2,44 @@ import { currentUser } from "@clerk/nextjs" import { Anthropic } from "@anthropic-ai/sdk" import { TIERS } from "@/lib/tiers" 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({ 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) { try { const user = await currentUser() @@ -60,6 +93,7 @@ export async function POST(request: Request) { fileName, line, templateType, + files, } = await request.json() // Get template configuration @@ -70,16 +104,17 @@ export async function POST(request: Request) { ? ` Project Template: ${templateConfig.name} -File Structure: -${Object.entries(templateConfig.fileStructure) - .map(([path, info]) => `${path} - ${info.description}`) - .join("\n")} +Current File Structure: +${files ? formatFileStructure(files) : "No files available"} Conventions: ${templateConfig.conventions.join("\n")} Dependencies: ${JSON.stringify(templateConfig.dependencies, null, 2)} + +Scripts: +${JSON.stringify(templateConfig.scripts, null, 2)} ` : "" diff --git a/frontend/components/editor/AIChat/index.tsx b/frontend/components/editor/AIChat/index.tsx index d05813e..dcaa780 100644 --- a/frontend/components/editor/AIChat/index.tsx +++ b/frontend/components/editor/AIChat/index.tsx @@ -148,7 +148,8 @@ export default function AIChat({ abortControllerRef, activeFileContent, false, - templateType + templateType, + files ) } diff --git a/frontend/components/editor/AIChat/lib/chatUtils.ts b/frontend/components/editor/AIChat/lib/chatUtils.ts index 4aac71a..770fb84 100644 --- a/frontend/components/editor/AIChat/lib/chatUtils.ts +++ b/frontend/components/editor/AIChat/lib/chatUtils.ts @@ -1,3 +1,4 @@ +import { TFolder, TFile } from "@/lib/types" import React from "react" // Stringify content for chat message component @@ -91,7 +92,8 @@ export const handleSend = async ( abortControllerRef: React.MutableRefObject, activeFileContent: string, isEditMode: boolean = false, - templateType: string + templateType: string, + files: (TFile | TFolder)[] ) => { // Return if input is empty and context is null if (input.trim() === "" && !context) return @@ -142,6 +144,7 @@ export const handleSend = async ( activeFileContent: activeFileContent, isEditMode: isEditMode, templateType: templateType, + files: files, }), signal: abortControllerRef.current.signal, })