Skip to content

Commit af4cf0c

Browse files
updated fixes for github importing & prompts
1 parent 3a9afa9 commit af4cf0c

File tree

21 files changed

+1373
-127
lines changed

21 files changed

+1373
-127
lines changed

.eslintrc.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": ["next/core-web-vitals"]
3+
}

app/api/files/content/route.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ if (!E2B_API_KEY) {
88

99
const sandboxTimeout = 10 * 60 * 1000
1010

11-
async function getSandbox(sessionID: string) {
12-
const sandbox = await Sandbox.create({
11+
async function getSandbox(sessionID: string, template?: string) {
12+
const sandbox = await Sandbox.create(template || 'code-interpreter-v1', {
1313
apiKey: E2B_API_KEY,
1414
metadata: {
1515
sessionID,
16+
template: template || 'code-interpreter-v1',
1617
},
1718
timeoutMs: sandboxTimeout,
1819
})
@@ -24,6 +25,7 @@ export async function GET(req: Request) {
2425
const { searchParams } = new URL(req.url)
2526
const sessionID = searchParams.get('sessionID')
2627
const path = searchParams.get('path')
28+
const template = searchParams.get('template')
2729

2830
if (!sessionID || !path) {
2931
return NextResponse.json(
@@ -32,7 +34,7 @@ export async function GET(req: Request) {
3234
)
3335
}
3436

35-
const sandbox = await getSandbox(sessionID)
37+
const sandbox = await getSandbox(sessionID, template || undefined)
3638
const content = await sandbox.files.read(path)
3739
return NextResponse.json({ content })
3840
} catch (error: any) {
@@ -45,7 +47,7 @@ export async function GET(req: Request) {
4547

4648
export async function POST(req: Request) {
4749
try {
48-
const { sessionID, path, content } = await req.json()
50+
const { sessionID, path, content, template } = await req.json()
4951

5052
if (!sessionID || !path || content === undefined) {
5153
return NextResponse.json(
@@ -54,7 +56,7 @@ export async function POST(req: Request) {
5456
)
5557
}
5658

57-
const sandbox = await getSandbox(sessionID)
59+
const sandbox = await getSandbox(sessionID, template || undefined)
5860
await sandbox.files.write(path, content)
5961
return NextResponse.json({ success: true })
6062
} catch (error: any) {

app/api/files/route.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@ if (!E2B_API_KEY) {
3535

3636
const sandboxTimeout = 10 * 60 * 1000
3737

38-
async function getSandbox(sessionID: string) {
39-
const sandbox = await Sandbox.create({
38+
async function getSandbox(sessionID: string, template?: string) {
39+
const sandbox = await Sandbox.create(template || 'code-interpreter-v1', {
4040
apiKey: E2B_API_KEY,
4141
metadata: {
4242
sessionID,
43+
template: template || 'code-interpreter-v1',
4344
},
4445
timeoutMs: sandboxTimeout,
4546
})
@@ -49,6 +50,7 @@ async function getSandbox(sessionID: string) {
4950
export async function GET(request: NextRequest) {
5051
const searchParams = request.nextUrl.searchParams
5152
const sessionID = searchParams.get('sessionID')
53+
const template = searchParams.get('template')
5254

5355
if (!sessionID) {
5456
return NextResponse.json(
@@ -58,7 +60,7 @@ export async function GET(request: NextRequest) {
5860
}
5961

6062
try {
61-
const sandbox = await getSandbox(sessionID)
63+
const sandbox = await getSandbox(sessionID, template || undefined)
6264
const fileTree = await listFilesRecursively(sandbox, '/')
6365
return NextResponse.json(fileTree)
6466
} catch (error) {
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 { Sandbox, FileType } from '@e2b/code-interpreter'
3+
import { FileSystemNode } from '@/components/file-tree'
4+
5+
async function listFilesRecursively(
6+
sandbox: Sandbox,
7+
path: string,
8+
): Promise<FileSystemNode[]> {
9+
const files = await sandbox.files.list(path)
10+
const nodes: FileSystemNode[] = []
11+
12+
for (const file of files) {
13+
const fullPath = `${path}/${file.name}`
14+
if (file.type === FileType.DIR) {
15+
nodes.push({
16+
name: file.name,
17+
isDirectory: true,
18+
children: await listFilesRecursively(sandbox, fullPath),
19+
})
20+
} else {
21+
nodes.push({
22+
name: file.name,
23+
isDirectory: false,
24+
path: fullPath,
25+
})
26+
}
27+
}
28+
return nodes
29+
}
30+
31+
const E2B_API_KEY = process.env.E2B_API_KEY
32+
if (!E2B_API_KEY) {
33+
throw new Error('E2B_API_KEY environment variable not found')
34+
}
35+
36+
const sandboxTimeout = 10 * 60 * 1000
37+
38+
export async function GET(request: NextRequest) {
39+
const searchParams = request.nextUrl.searchParams
40+
const sandboxId = searchParams.get('sandboxId')
41+
42+
if (!sandboxId) {
43+
return NextResponse.json(
44+
{ error: 'sandboxId is required' },
45+
{ status: 400 },
46+
)
47+
}
48+
49+
try {
50+
// Connect to existing sandbox by ID
51+
const sandbox = await Sandbox.connect(sandboxId, {
52+
apiKey: E2B_API_KEY,
53+
})
54+
55+
const fileTree = await listFilesRecursively(sandbox, '/')
56+
return NextResponse.json(fileTree)
57+
} catch (error) {
58+
console.error('Error fetching file tree:', error)
59+
return NextResponse.json(
60+
{ error: 'Failed to fetch file tree' },
61+
{ status: 500 },
62+
)
63+
}
64+
}

app/api/files/sandbox/route.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { NextResponse } from 'next/server'
2+
import { Sandbox } from '@e2b/code-interpreter'
3+
4+
const E2B_API_KEY = process.env.E2B_API_KEY
5+
if (!E2B_API_KEY) {
6+
throw new Error('E2B_API_KEY environment variable not found')
7+
}
8+
9+
const sandboxTimeout = 10 * 60 * 1000
10+
11+
export async function GET(req: Request) {
12+
try {
13+
const { searchParams } = new URL(req.url)
14+
const sandboxId = searchParams.get('sandboxId')
15+
const path = searchParams.get('path')
16+
17+
if (!sandboxId || !path) {
18+
return NextResponse.json(
19+
{ error: 'sandboxId and path are required' },
20+
{ status: 400 },
21+
)
22+
}
23+
24+
// Connect to existing sandbox by ID
25+
const sandbox = await Sandbox.connect(sandboxId, {
26+
apiKey: E2B_API_KEY,
27+
})
28+
29+
const content = await sandbox.files.read(path)
30+
return NextResponse.json({ content })
31+
} catch (error: any) {
32+
return NextResponse.json(
33+
{ error: error.message || 'An unexpected error occurred' },
34+
{ status: 500 },
35+
)
36+
}
37+
}
38+
39+
export async function POST(req: Request) {
40+
try {
41+
const { sandboxId, path, content } = await req.json()
42+
43+
if (!sandboxId || !path || content === undefined) {
44+
return NextResponse.json(
45+
{ error: 'sandboxId, path and content are required' },
46+
{ status: 400 },
47+
)
48+
}
49+
50+
// Connect to existing sandbox by ID
51+
const sandbox = await Sandbox.connect(sandboxId, {
52+
apiKey: E2B_API_KEY,
53+
})
54+
55+
await sandbox.files.write(path, content)
56+
return NextResponse.json({ success: true })
57+
} catch (error: any) {
58+
return NextResponse.json(
59+
{ error: error.message || 'An unexpected error occurred' },
60+
{ status: 500 },
61+
)
62+
}
63+
}

app/api/integrations/github/repos/[owner]/[repo]/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export async function GET(
3636
headers: {
3737
'Authorization': `Bearer ${integration.connection_data.access_token}`,
3838
'Accept': 'application/vnd.github.v3+json',
39+
'User-Agent': 'CodingIT-App/1.0',
3940
},
4041
}
4142
)

app/api/integrations/github/repos/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export async function GET(request: NextRequest) {
3434
headers: {
3535
'Authorization': `Bearer ${integration.connection_data.access_token}`,
3636
'Accept': 'application/vnd.github.v3+json',
37+
'User-Agent': 'CodingIT-App/1.0',
3738
},
3839
})
3940

app/layout.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { GeistSans } from 'geist/font/sans'
66
import { GeistMono } from 'geist/font/mono'
77
import { Analytics } from "@vercel/analytics/next"
88
import { SpeedInsights } from "@vercel/speed-insights/next"
9+
import Script from 'next/script'
910

1011
export const metadata: Metadata = {
1112
metadataBase: new URL('https://codingit.vercel.app'),
@@ -50,17 +51,18 @@ export default function RootLayout({
5051
>
5152
<head>
5253
{/* Google tag (gtag.js) */}
53-
<script async src="https://www.googletagmanager.com/gtag/js?id=G-8NNCCEN53X"></script>
54-
<script
55-
dangerouslySetInnerHTML={{
56-
__html: `
57-
window.dataLayer = window.dataLayer || [];
58-
function gtag(){dataLayer.push(arguments);}
59-
gtag('js', new Date());
60-
gtag('config', 'G-CV2G2EG0KJ');
61-
`,
62-
}}
54+
<Script
55+
src="https://www.googletagmanager.com/gtag/js?id=G-8NNCCEN53X"
56+
strategy="afterInteractive"
6357
/>
58+
<Script id="google-analytics" strategy="afterInteractive">
59+
{`
60+
window.dataLayer = window.dataLayer || [];
61+
function gtag(){dataLayer.push(arguments);}
62+
gtag('js', new Date());
63+
gtag('config', 'G-CV2G2EG0KJ');
64+
`}
65+
</Script>
6466
</head>
6567
<body>
6668
<SpeedInsights />

app/page.tsx

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ export default function Home() {
149149
}
150150

151151
loadProjectMessages()
152-
}, [currentProject])
152+
}, [currentProject, supabase])
153153

154154
useEffect(() => {
155155
async function saveMessagesToDb() {
@@ -164,7 +164,7 @@ export default function Home() {
164164
if (messages.length > 0 && currentProject && session) {
165165
saveMessagesToDb()
166166
}
167-
}, [messages, currentProject, session])
167+
}, [messages, currentProject, session, supabase])
168168

169169
useEffect(() => {
170170
if (object) {
@@ -223,20 +223,18 @@ export default function Home() {
223223
stop()
224224
}
225225

226-
// Create new project if none exists
227-
if (!currentProject) {
228-
const title = await generateProjectTitle(chatInput)
229-
if (supabase) {
230-
const newProject = await createProject(supabase, title, selectedTemplate === 'auto' ? undefined : selectedTemplate)
231-
if (newProject) {
232-
setCurrentProject(newProject)
233-
}
234-
}
235-
}
236-
237-
const content: Message['content'] = [{ type: 'text', text: chatInput }]
238-
const images = await toMessageImage(files)
226+
// Clear input and files immediately for better UX
227+
const currentInput = chatInput
228+
const currentFiles = [...files]
229+
setChatInput('')
230+
setFiles([])
231+
setCurrentTab('code')
239232

233+
// Create message content immediately
234+
const content: Message['content'] = [{ type: 'text', text: currentInput }]
235+
236+
// Process images asynchronously
237+
const images = await toMessageImage(currentFiles)
240238
if (images.length > 0) {
241239
images.forEach((image) => {
242240
content.push({ type: 'image', image })
@@ -250,6 +248,7 @@ export default function Home() {
250248
const updatedMessages = [...messages, newMessage]
251249
setMessages(updatedMessages)
252250

251+
// Start submission immediately
253252
submit({
254253
userID: session?.user?.id,
255254
teamID: userTeam?.id,
@@ -259,9 +258,20 @@ export default function Home() {
259258
config: languageModel,
260259
})
261260

262-
setChatInput('')
263-
setFiles([])
264-
setCurrentTab('code')
261+
// Handle project creation asynchronously without blocking
262+
if (!currentProject) {
263+
try {
264+
const title = await generateProjectTitle(currentInput)
265+
if (supabase) {
266+
const newProject = await createProject(supabase, title, selectedTemplate === 'auto' ? undefined : selectedTemplate)
267+
if (newProject) {
268+
setCurrentProject(newProject)
269+
}
270+
}
271+
} catch (error) {
272+
console.error('Error creating project:', error)
273+
}
274+
}
265275

266276
posthog.capture('chat_submit', {
267277
template: selectedTemplate,

components/chat-input.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,8 @@ export function ChatInput({
142142
function onEnter(e: React.KeyboardEvent<HTMLFormElement>) {
143143
if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) {
144144
e.preventDefault()
145-
if (e.currentTarget.checkValidity()) {
145+
if (input.trim() && !isLoading && !isErrored) {
146146
handleSubmit(e)
147-
} else {
148-
e.currentTarget.reportValidity()
149147
}
150148
}
151149
}
@@ -202,7 +200,7 @@ export function ChatInput({
202200
className="text-normal px-3 resize-none ring-0 bg-inherit w-full m-0 outline-none"
203201
required={true}
204202
placeholder={placeholder}
205-
disabled={isErrored}
203+
disabled={isErrored || isLoading}
206204
value={input}
207205
onChange={handleInputChange}
208206
onPaste={isMultiModal ? handlePaste : undefined}
@@ -246,7 +244,7 @@ export function ChatInput({
246244
<Tooltip delayDuration={0}>
247245
<TooltipTrigger asChild>
248246
<Button
249-
disabled={isErrored}
247+
disabled={isErrored || isLoading || !input.trim()}
250248
variant="default"
251249
size="icon"
252250
type="submit"

0 commit comments

Comments
 (0)