Skip to content

Commit 0bd526b

Browse files
Merge pull request Gerome-Elassaad#21 from Gerome-Elassaad/copilot/fix-11-2
Implement enhanced in-app code editor with file management and syntax highlighting
2 parents 8c727f4 + ca5fea4 commit 0bd526b

File tree

12 files changed

+781
-186
lines changed

12 files changed

+781
-186
lines changed

app/api/files/content/route.ts

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
import { NextRequest, NextResponse } from 'next/server'
2+
import { createServerClient } from '@/lib/supabase-server'
3+
4+
// Mock file content storage - in a real implementation this would integrate with project storage
5+
const mockFileContents: Record<string, string> = {
6+
'/src/App.tsx': `import React from 'react'
7+
import { Button } from './components/Button'
8+
import { Card } from './components/Card'
9+
10+
function App() {
11+
return (
12+
<div className="app">
13+
<h1>Welcome to CodingIT</h1>
14+
<Card>
15+
<Button onClick={() => console.log('Hello!')}>
16+
Click me
17+
</Button>
18+
</Card>
19+
</div>
20+
)
21+
}
22+
23+
export default App`,
24+
25+
'/src/components/Button.tsx': `import React from 'react'
26+
27+
interface ButtonProps {
28+
children: React.ReactNode
29+
onClick?: () => void
30+
variant?: 'primary' | 'secondary'
31+
}
32+
33+
export function Button({ children, onClick, variant = 'primary' }: ButtonProps) {
34+
return (
35+
<button
36+
className={\`btn btn--\${variant}\`}
37+
onClick={onClick}
38+
>
39+
{children}
40+
</button>
41+
)
42+
}`,
43+
44+
'/src/components/Card.tsx': `import React from 'react'
45+
46+
interface CardProps {
47+
children: React.ReactNode
48+
className?: string
49+
}
50+
51+
export function Card({ children, className = '' }: CardProps) {
52+
return (
53+
<div className={\`card \${className}\`}>
54+
{children}
55+
</div>
56+
)
57+
}`,
58+
59+
'/src/utils/helpers.ts': `export function formatDate(date: Date): string {
60+
return date.toLocaleDateString('en-US', {
61+
year: 'numeric',
62+
month: 'long',
63+
day: 'numeric'
64+
})
65+
}
66+
67+
export function debounce<T extends (...args: any[]) => void>(
68+
func: T,
69+
wait: number
70+
): (...args: Parameters<T>) => void {
71+
let timeout: NodeJS.Timeout
72+
return (...args: Parameters<T>) => {
73+
clearTimeout(timeout)
74+
timeout = setTimeout(() => func(...args), wait)
75+
}
76+
}`,
77+
78+
'/package.json': `{
79+
"name": "my-project",
80+
"version": "1.0.0",
81+
"scripts": {
82+
"dev": "vite",
83+
"build": "vite build",
84+
"preview": "vite preview"
85+
},
86+
"dependencies": {
87+
"react": "^18.0.0",
88+
"react-dom": "^18.0.0"
89+
},
90+
"devDependencies": {
91+
"@types/react": "^18.0.0",
92+
"typescript": "^5.0.0",
93+
"vite": "^4.0.0"
94+
}
95+
}`,
96+
97+
'/README.md': `# My Project
98+
99+
This is a sample project created with CodingIT.
100+
101+
## Getting Started
102+
103+
1. Install dependencies: \`npm install\`
104+
2. Start development server: \`npm run dev\`
105+
3. Build for production: \`npm run build\`
106+
107+
## Features
108+
109+
- React with TypeScript
110+
- Component-based architecture
111+
- Utility functions
112+
- Modern development setup
113+
114+
Happy coding! 🚀`
115+
}
116+
117+
export async function GET(request: NextRequest) {
118+
try {
119+
// Skip authentication in development for now
120+
if (process.env.NODE_ENV === 'development') {
121+
const { searchParams } = new URL(request.url)
122+
const path = searchParams.get('path')
123+
124+
if (!path) {
125+
return NextResponse.json({ error: 'Path parameter is required' }, { status: 400 })
126+
}
127+
128+
const content = mockFileContents[path] || `// File: ${path}\n// Content not found or empty file\n`
129+
130+
return new NextResponse(content, {
131+
headers: {
132+
'Content-Type': 'text/plain',
133+
},
134+
})
135+
}
136+
137+
const supabase = createServerClient()
138+
const { data: { session } } = await supabase.auth.getSession()
139+
140+
if (!session) {
141+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
142+
}
143+
144+
const { searchParams } = new URL(request.url)
145+
const path = searchParams.get('path')
146+
147+
if (!path) {
148+
return NextResponse.json({ error: 'Path parameter is required' }, { status: 400 })
149+
}
150+
151+
// TODO: In a real implementation, fetch the actual file content from project storage
152+
const content = mockFileContents[path] || `// File: ${path}\n// Content not found or empty file\n`
153+
154+
return new NextResponse(content, {
155+
headers: {
156+
'Content-Type': 'text/plain',
157+
},
158+
})
159+
} catch (error) {
160+
console.error('Error fetching file content:', error)
161+
return NextResponse.json({ error: 'Failed to fetch file content' }, { status: 500 })
162+
}
163+
}
164+
165+
export async function POST(request: NextRequest) {
166+
try {
167+
// Skip authentication in development for now
168+
if (process.env.NODE_ENV === 'development') {
169+
const { path, content } = await request.json()
170+
171+
if (!path || content === undefined) {
172+
return NextResponse.json(
173+
{ error: 'Path and content are required' },
174+
{ status: 400 }
175+
)
176+
}
177+
178+
mockFileContents[path] = content
179+
180+
return NextResponse.json({ success: true, message: 'File saved successfully' })
181+
}
182+
183+
const supabase = createServerClient()
184+
const { data: { session } } = await supabase.auth.getSession()
185+
186+
if (!session) {
187+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
188+
}
189+
190+
const { path, content } = await request.json()
191+
192+
if (!path || content === undefined) {
193+
return NextResponse.json(
194+
{ error: 'Path and content are required' },
195+
{ status: 400 }
196+
)
197+
}
198+
199+
// TODO: In a real implementation, save the file content to project storage
200+
mockFileContents[path] = content
201+
202+
return NextResponse.json({ success: true, message: 'File saved successfully' })
203+
} catch (error) {
204+
console.error('Error saving file content:', error)
205+
return NextResponse.json({ error: 'Failed to save file content' }, { status: 500 })
206+
}
207+
}
208+
209+
export async function DELETE(request: NextRequest) {
210+
try {
211+
// Skip authentication in development for now
212+
if (process.env.NODE_ENV === 'development') {
213+
const { searchParams } = new URL(request.url)
214+
const path = searchParams.get('path')
215+
216+
if (!path) {
217+
return NextResponse.json({ error: 'Path parameter is required' }, { status: 400 })
218+
}
219+
220+
delete mockFileContents[path]
221+
222+
return NextResponse.json({ success: true, message: 'File deleted successfully' })
223+
}
224+
225+
const supabase = createServerClient()
226+
const { data: { session } } = await supabase.auth.getSession()
227+
228+
if (!session) {
229+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
230+
}
231+
232+
const { searchParams } = new URL(request.url)
233+
const path = searchParams.get('path')
234+
235+
if (!path) {
236+
return NextResponse.json({ error: 'Path parameter is required' }, { status: 400 })
237+
}
238+
239+
// TODO: In a real implementation, delete the file from project storage
240+
delete mockFileContents[path]
241+
242+
return NextResponse.json({ success: true, message: 'File deleted successfully' })
243+
} catch (error) {
244+
console.error('Error deleting file:', error)
245+
return NextResponse.json({ error: 'Failed to delete file' }, { status: 500 })
246+
}
247+
}

app/api/files/route.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { NextRequest, NextResponse } from 'next/server'
2+
import { createServerClient } from '@/lib/supabase-server'
3+
import { FileSystemNode } from '@/components/file-tree'
4+
5+
// Mock file system for now - in a real implementation this would integrate with project storage
6+
const mockFileTree: FileSystemNode[] = [
7+
{
8+
name: 'src',
9+
isDirectory: true,
10+
children: [
11+
{
12+
name: 'components',
13+
isDirectory: true,
14+
children: [
15+
{ name: 'Button.tsx', isDirectory: false },
16+
{ name: 'Card.tsx', isDirectory: false },
17+
]
18+
},
19+
{
20+
name: 'utils',
21+
isDirectory: true,
22+
children: [
23+
{ name: 'helpers.ts', isDirectory: false },
24+
]
25+
},
26+
{ name: 'App.tsx', isDirectory: false },
27+
{ name: 'index.ts', isDirectory: false },
28+
]
29+
},
30+
{
31+
name: 'public',
32+
isDirectory: true,
33+
children: [
34+
{ name: 'favicon.ico', isDirectory: false },
35+
{ name: 'logo.png', isDirectory: false },
36+
]
37+
},
38+
{ name: 'package.json', isDirectory: false },
39+
{ name: 'README.md', isDirectory: false },
40+
]
41+
42+
export async function GET(request: NextRequest) {
43+
try {
44+
// Skip authentication in development for now
45+
if (process.env.NODE_ENV === 'development') {
46+
return NextResponse.json(mockFileTree)
47+
}
48+
49+
const supabase = createServerClient()
50+
const { data: { session } } = await supabase.auth.getSession()
51+
52+
if (!session) {
53+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
54+
}
55+
56+
// TODO: In a real implementation, fetch the actual file tree from the project storage
57+
// This could integrate with GitHub repos or project-specific file storage
58+
59+
return NextResponse.json(mockFileTree)
60+
} catch (error) {
61+
console.error('Error fetching file tree:', error)
62+
return NextResponse.json({ error: 'Failed to fetch file tree' }, { status: 500 })
63+
}
64+
}

app/page.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,11 @@ export default function Home() {
287287
}, [])
288288

289289
function logout() {
290-
supabase
291-
? supabase.auth.signOut()
292-
: console.warn('Supabase is not initialized')
290+
if (supabase) {
291+
supabase.auth.signOut()
292+
} else {
293+
console.warn('Supabase is not initialized')
294+
}
293295
}
294296

295297
function handleLanguageModelChange(e: LLMModelConfig) {

app/test-editor/page.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
'use client'
2+
3+
import { FragmentCode } from '@/components/fragment-code'
4+
5+
export default function TestCodeEditor() {
6+
return (
7+
<div className="h-screen bg-background">
8+
<div className="p-4">
9+
<h1 className="text-2xl font-bold mb-4">Code Editor Test</h1>
10+
<p className="text-muted-foreground mb-4">
11+
Testing the enhanced code editor with file tree and syntax highlighting.
12+
</p>
13+
</div>
14+
<div className="h-[calc(100vh-120px)]">
15+
<FragmentCode />
16+
</div>
17+
</div>
18+
)
19+
}

0 commit comments

Comments
 (0)