Skip to content

Commit 5a8a51e

Browse files
implemented ide fragments with code editing
1 parent 20399cf commit 5a8a51e

File tree

17 files changed

+553
-197
lines changed

17 files changed

+553
-197
lines changed

app/api/chat/route.ts

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { getModelClient } from '@/lib/models'
33
import { LLMModel, LLMModelConfig } from '@/lib/models'
44
import { toPrompt } from '@/lib/prompt'
55
import ratelimit from '@/lib/ratelimit'
6-
import { retrieveRelevantCode } from '@/lib/retrieval'
76
import { fragmentSchema as schema } from '@/lib/schema'
87
import { Templates } from '@/lib/templates'
98
import { streamObject, LanguageModel, CoreMessage } from 'ai'
@@ -62,21 +61,7 @@ export async function POST(req: Request) {
6261
const modelClient = getModelClient(model, config)
6362

6463
try {
65-
const lastUserMessage = messages[messages.length - 1]?.content;
66-
let retrievedContext = '';
67-
if (typeof lastUserMessage === 'string') {
68-
const relevantCode = await retrieveRelevantCode(lastUserMessage);
69-
if (relevantCode.length > 0) {
70-
retrievedContext = `
71-
---
72-
Here is some relevant code from our knowledge base that might help:
73-
${relevantCode.join('\n---\n')}
74-
---
75-
`;
76-
}
77-
}
78-
79-
const systemPrompt = toPrompt(template) + retrievedContext;
64+
const systemPrompt = toPrompt(template);
8065

8166
const stream = await streamObject({
8267
model: modelClient as LanguageModel,

app/api/files/content/route.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { NextResponse } from 'next/server'
2+
import fs from 'fs'
3+
import path from 'path'
4+
5+
export async function GET(request: Request) {
6+
const { searchParams } = new URL(request.url)
7+
const filePath = searchParams.get('path')
8+
9+
if (!filePath) {
10+
return new NextResponse('File path is required', { status: 400 })
11+
}
12+
13+
try {
14+
const fullPath = path.join(process.cwd(), filePath)
15+
const content = fs.readFileSync(fullPath, 'utf-8')
16+
return new NextResponse(content)
17+
} catch (error) {
18+
return new NextResponse('File not found', { status: 404 })
19+
}
20+
}
21+
22+
export async function POST(request: Request) {
23+
const { path: filePath, content } = await request.json()
24+
25+
if (!filePath) {
26+
return new NextResponse('File path is required', { status: 400 })
27+
}
28+
29+
try {
30+
const fullPath = path.join(process.cwd(), filePath)
31+
fs.writeFileSync(fullPath, content, 'utf-8')
32+
return new NextResponse('File saved successfully')
33+
} catch (error) {
34+
return new NextResponse('Error saving file', { status: 500 })
35+
}
36+
}

app/api/files/route.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { NextResponse } from 'next/server'
2+
import fs from 'fs'
3+
import path from 'path'
4+
5+
interface FileSystemNode {
6+
name: string
7+
isDirectory: boolean
8+
children?: FileSystemNode[]
9+
}
10+
11+
function getFileTree(dir: string): FileSystemNode[] {
12+
const files = fs.readdirSync(dir)
13+
return files.map(file => {
14+
const filePath = path.join(dir, file)
15+
const stat = fs.statSync(filePath)
16+
const isDirectory = stat.isDirectory()
17+
const children = isDirectory ? getFileTree(filePath) : undefined
18+
return { name: file, isDirectory, children }
19+
})
20+
}
21+
22+
export async function GET() {
23+
const projectRoot = process.cwd()
24+
const fileTree = getFileTree(projectRoot)
25+
return NextResponse.json(fileTree)
26+
}

app/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const inter = Inter({ subsets: ['latin'] })
88

99
export const metadata: Metadata = {
1010
metadataBase: new URL('https://codingit.vercel.app'),
11-
title: 'CodinIT',
11+
title: 'CodinIT.dev',
1212
keywords: [
1313
'AI software engineer',
1414
'open source',

app/page.tsx

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { NavBar } from '@/components/navbar'
1010
import { Preview } from '@/components/preview'
1111
import { Sidebar } from '@/components/sidebar'
1212
import { useAuth } from '@/lib/auth'
13-
import { Project, createProject, saveMessage, getProjectMessages, generateProjectTitle } from '@/lib/database'
13+
import { Project, createProject, saveMessage, getProjectMessages, generateProjectTitle, getProject } from '@/lib/database'
1414
import { Message, toAISDKMessages, toMessageImage } from '@/lib/messages'
1515
import { LLMModelConfig } from '@/lib/models'
1616
import modelsList from '@/lib/models.json'
@@ -52,10 +52,15 @@ export default function Home() {
5252
const [currentProject, setCurrentProject] = useState<Project | null>(null)
5353
const [isLoadingProject, setIsLoadingProject] = useState(false)
5454

55-
const [sidebarOpen, setSidebarOpen] = useState(false)
56-
5755
const { session, userTeam } = useAuth(setAuthDialog, setAuthView)
5856

57+
const handleChatSelected = async (chatId: string) => {
58+
const project = await getProject(chatId);
59+
if (project) {
60+
setCurrentProject(project);
61+
}
62+
};
63+
5964
const filteredModels = modelsList.models.filter((model) => {
6065
if (process.env.NEXT_PUBLIC_HIDE_LOCAL_MODELS) {
6166
return model.providerId !== 'ollama'
@@ -318,18 +323,6 @@ export default function Home() {
318323
setCurrentPreview({ fragment: undefined, result: undefined })
319324
}
320325

321-
function handleProjectSelect(project: Project | null) {
322-
setCurrentProject(project)
323-
if (!project) {
324-
handleClearChat()
325-
}
326-
}
327-
328-
async function handleNewProject() {
329-
setCurrentProject(null)
330-
handleClearChat()
331-
}
332-
333326
return (
334327
<main className="flex min-h-screen max-h-screen">
335328
{supabase && (
@@ -344,6 +337,7 @@ export default function Home() {
344337
{session && (
345338
<Sidebar
346339
userPlan={userTeam?.tier}
340+
onChatSelected={handleChatSelected}
347341
/>
348342
)}
349343

components/code-view.tsx

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,38 @@
1-
// import "prismjs/plugins/line-numbers/prism-line-numbers.js";
2-
// import "prismjs/plugins/line-numbers/prism-line-numbers.css";
3-
import './code-theme.css'
4-
import Prism from 'prismjs'
5-
import 'prismjs/components/prism-javascript'
6-
import 'prismjs/components/prism-jsx'
7-
import 'prismjs/components/prism-python'
8-
import 'prismjs/components/prism-tsx'
9-
import 'prismjs/components/prism-typescript'
10-
import { useEffect } from 'react'
1+
import { useEffect, useState } from 'react'
2+
import { CodeEditor } from './code-editor'
3+
import { Button } from './ui/button'
4+
5+
export function CodeView({
6+
code: initialCode,
7+
lang,
8+
onSave,
9+
}: {
10+
code: string
11+
lang: string
12+
onSave: (content: string) => void
13+
}) {
14+
const [code, setCode] = useState(initialCode)
1115

12-
export function CodeView({ code, lang }: { code: string; lang: string }) {
1316
useEffect(() => {
14-
Prism.highlightAll()
15-
}, [code])
17+
setCode(initialCode)
18+
}, [initialCode])
19+
20+
function handleSave() {
21+
onSave(code)
22+
}
1623

1724
return (
18-
<pre
19-
className="p-4 pt-2"
20-
style={{
21-
fontSize: 12,
22-
backgroundColor: 'transparent',
23-
borderRadius: 0,
24-
margin: 0,
25-
}}
26-
>
27-
<code className={`language-${lang}`}>{code}</code>
28-
</pre>
25+
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
26+
<div style={{ flex: 1, overflow: 'auto' }}>
27+
<CodeEditor
28+
code={code}
29+
lang={lang}
30+
onChange={newCode => setCode(newCode || '')}
31+
/>
32+
</div>
33+
<div className="p-2 flex justify-end">
34+
<Button onClick={handleSave}>Save</Button>
35+
</div>
36+
</div>
2937
)
3038
}

components/file-tree.tsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { useState } from 'react'
2+
import {
3+
ChevronDown,
4+
ChevronRight,
5+
File as FileIcon,
6+
Folder,
7+
} from 'lucide-react'
8+
9+
export interface FileSystemNode {
10+
name: string
11+
isDirectory: boolean
12+
children?: FileSystemNode[]
13+
}
14+
15+
interface FileTreeProps {
16+
files: FileSystemNode[]
17+
onSelectFile: (path: string) => void
18+
}
19+
20+
export function FileTree({ files, onSelectFile }: FileTreeProps) {
21+
return (
22+
<div className="p-2">
23+
{files.map(file => (
24+
<FileTreeNode key={file.name} node={file} onSelectFile={onSelectFile} />
25+
))}
26+
</div>
27+
)
28+
}
29+
30+
interface FileTreeNodeProps {
31+
node: FileSystemNode
32+
onSelectFile: (path: string) => void
33+
level?: number
34+
}
35+
36+
function FileTreeNode({
37+
node,
38+
onSelectFile,
39+
level = 0,
40+
path = '',
41+
}: FileTreeNodeProps & { path?: string }) {
42+
const [isOpen, setIsOpen] = useState(false)
43+
44+
const isDirectory = node.isDirectory
45+
const hasChildren = node.children && node.children.length > 0
46+
const newPath = `${path}/${node.name}`
47+
48+
const handleToggle = () => {
49+
if (isDirectory) {
50+
setIsOpen(!isOpen)
51+
} else {
52+
onSelectFile(newPath)
53+
}
54+
}
55+
56+
return (
57+
<div>
58+
<div
59+
className="flex items-center cursor-pointer"
60+
style={{ paddingLeft: level * 16 }}
61+
onClick={handleToggle}
62+
>
63+
{isDirectory ? (
64+
<>
65+
{isOpen ? (
66+
<ChevronDown size={16} className="mr-1" />
67+
) : (
68+
<ChevronRight size={16} className="mr-1" />
69+
)}
70+
<Folder size={16} className="mr-1" />
71+
</>
72+
) : (
73+
<FileIcon size={16} className="mr-1" />
74+
)}
75+
<span>{node.name}</span>
76+
</div>
77+
{isOpen &&
78+
hasChildren &&
79+
node.children?.map(child => (
80+
<FileTreeNode
81+
key={child.name}
82+
node={child}
83+
onSelectFile={onSelectFile}
84+
level={level + 1}
85+
path={newPath}
86+
/>
87+
))}
88+
</div>
89+
)
90+
}

0 commit comments

Comments
 (0)