Skip to content

Commit e464757

Browse files
author
randypet80
committed
Agent query: I noticed there's an error with the server startup - it seems there's an undefined SQL reference. Would you like me to fix this database connection issue first, or would you prefer a different approach to building the initial prototype?
Add initial project structure, including client and server components, AI assistant functionality, and UI components. Screenshot: https://storage.googleapis.com/screenshot-production-us-central1/17b8444f-665a-4ee2-a61b-7dc60ab4fe76/804c6c11-a0ba-4daa-a788-4fe9e2cd927b.jpg
1 parent 88caff8 commit e464757

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+15335
-0
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules
2+
dist
3+
.DS_Store
4+
server/public
5+
vite.config.ts.*
6+
*.tar.gz

.replit

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
modules = ["nodejs-20", "web", "postgresql-16"]
2+
run = "npm run dev"
3+
hidden = [".config", ".git", "generated-icon.png", "node_modules", "dist"]
4+
5+
[nix]
6+
channel = "stable-24_05"
7+
8+
[deployment]
9+
deploymentTarget = "cloudrun"
10+
build = ["npm", "run", "build"]
11+
run = ["npm", "run", "start"]
12+
13+
[[ports]]
14+
localPort = 5000
15+
externalPort = 80
16+
17+
[workflows]
18+
runButton = "Project"
19+
20+
[[workflows.workflow]]
21+
name = "Project"
22+
mode = "parallel"
23+
author = "agent"
24+
25+
[[workflows.workflow.tasks]]
26+
task = "workflow.run"
27+
args = "Start application"
28+
29+
[[workflows.workflow]]
30+
name = "Start application"
31+
author = "agent"
32+
33+
[workflows.workflow.metadata]
34+
agentRequireRestartOnSave = false
35+
36+
[[workflows.workflow.tasks]]
37+
task = "packager.installForAll"
38+
39+
[[workflows.workflow.tasks]]
40+
task = "shell.exec"
41+
args = "npm run dev"
42+
waitForPort = 5000

client/index.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
</head>
7+
<body>
8+
<div id="root"></div>
9+
<script type="module" src="/src/main.tsx"></script>
10+
</body>
11+
</html>

client/src/App.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Switch, Route } from "wouter";
2+
import { Card, CardContent } from "@/components/ui/card";
3+
import { AlertCircle } from "lucide-react";
4+
import EditorPage from "./pages/EditorPage";
5+
6+
function App() {
7+
return (
8+
<Switch>
9+
<Route path="/" component={EditorPage} />
10+
<Route component={NotFound} />
11+
</Switch>
12+
);
13+
}
14+
15+
function NotFound() {
16+
return (
17+
<div className="min-h-screen w-full flex items-center justify-center bg-background">
18+
<Card className="w-full max-w-md mx-4">
19+
<CardContent className="pt-6">
20+
<div className="flex mb-4 gap-2">
21+
<AlertCircle className="h-8 w-8 text-destructive" />
22+
<h1 className="text-2xl font-bold">404 Page Not Found</h1>
23+
</div>
24+
<p className="mt-4 text-sm text-muted-foreground">
25+
The page you're looking for doesn't exist.
26+
</p>
27+
</CardContent>
28+
</Card>
29+
</div>
30+
);
31+
}
32+
33+
export default App;

client/src/components/AIAssistant.tsx

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { useState } from "react";
2+
import { ScrollArea } from "@/components/ui/scroll-area";
3+
import { Button } from "@/components/ui/button";
4+
import { Textarea } from "@/components/ui/textarea";
5+
import { Send } from "lucide-react";
6+
import { analyzeCode } from "@/lib/ai";
7+
import { useToast } from "@/hooks/use-toast";
8+
9+
interface AIAssistantProps {
10+
file: string | null;
11+
}
12+
13+
interface Message {
14+
role: "user" | "assistant";
15+
content: string;
16+
}
17+
18+
export default function AIAssistant({ file }: AIAssistantProps) {
19+
const [messages, setMessages] = useState<Message[]>([]);
20+
const [input, setInput] = useState("");
21+
const [isLoading, setIsLoading] = useState(false);
22+
const { toast } = useToast();
23+
24+
const sendMessage = async () => {
25+
if (!input.trim() || !file) return;
26+
27+
const userMessage = input.trim();
28+
setInput("");
29+
setMessages((prev) => [...prev, { role: "user", content: userMessage }]);
30+
setIsLoading(true);
31+
32+
try {
33+
const response = await analyzeCode(file, userMessage);
34+
setMessages((prev) => [
35+
...prev,
36+
{ role: "assistant", content: response },
37+
]);
38+
} catch (error) {
39+
toast({
40+
title: "Error",
41+
description: "Failed to get AI response",
42+
variant: "destructive",
43+
});
44+
} finally {
45+
setIsLoading(false);
46+
}
47+
};
48+
49+
return (
50+
<div className="h-full flex flex-col p-4">
51+
<div className="font-semibold mb-4">AI Assistant</div>
52+
53+
<ScrollArea className="flex-1 pr-4">
54+
<div className="space-y-4">
55+
{messages.map((message, i) => (
56+
<div
57+
key={i}
58+
className={`flex ${
59+
message.role === "assistant" ? "justify-start" : "justify-end"
60+
}`}
61+
>
62+
<div
63+
className={`max-w-[80%] rounded-lg p-3 ${
64+
message.role === "assistant"
65+
? "bg-secondary"
66+
: "bg-primary text-primary-foreground"
67+
}`}
68+
>
69+
{message.content}
70+
</div>
71+
</div>
72+
))}
73+
</div>
74+
</ScrollArea>
75+
76+
<div className="mt-4 flex gap-2">
77+
<Textarea
78+
value={input}
79+
onChange={(e) => setInput(e.target.value)}
80+
onKeyDown={(e) => {
81+
if (e.key === "Enter" && !e.shiftKey) {
82+
e.preventDefault();
83+
sendMessage();
84+
}
85+
}}
86+
placeholder="Ask about your code..."
87+
className="min-h-[80px]"
88+
disabled={isLoading}
89+
/>
90+
<Button
91+
className="self-end"
92+
onClick={sendMessage}
93+
disabled={isLoading || !input.trim()}
94+
>
95+
<Send className="h-4 w-4" />
96+
</Button>
97+
</div>
98+
</div>
99+
);
100+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import {
2+
CommandDialog,
3+
CommandEmpty,
4+
CommandGroup,
5+
CommandInput,
6+
CommandItem,
7+
CommandList,
8+
} from "@/components/ui/command";
9+
import { useEffect, useState } from "react";
10+
import { Search } from "lucide-react";
11+
import { Button } from "@/components/ui/button";
12+
13+
export default function CommandPalette() {
14+
const [open, setOpen] = useState(false);
15+
16+
useEffect(() => {
17+
const down = (e: KeyboardEvent) => {
18+
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
19+
e.preventDefault();
20+
setOpen((open) => !open);
21+
}
22+
};
23+
document.addEventListener("keydown", down);
24+
return () => document.removeEventListener("keydown", down);
25+
}, []);
26+
27+
const runCommand = (command: () => void) => {
28+
setOpen(false);
29+
command();
30+
};
31+
32+
return (
33+
<>
34+
<Button
35+
variant="outline"
36+
className="relative h-8 w-full justify-start max-w-sm text-sm text-muted-foreground"
37+
onClick={() => setOpen(true)}
38+
>
39+
<Search className="mr-2 h-4 w-4" />
40+
Search commands...
41+
<kbd className="pointer-events-none absolute right-1.5 top-1.5 hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex">
42+
<span className="text-xs"></span>K
43+
</kbd>
44+
</Button>
45+
<CommandDialog open={open} onOpenChange={setOpen}>
46+
<CommandInput placeholder="Type a command or search..." />
47+
<CommandList>
48+
<CommandEmpty>No results found.</CommandEmpty>
49+
<CommandGroup heading="Actions">
50+
<CommandItem
51+
onSelect={() => runCommand(() => console.log("New File"))}
52+
>
53+
New File
54+
</CommandItem>
55+
<CommandItem
56+
onSelect={() => runCommand(() => console.log("New Folder"))}
57+
>
58+
New Folder
59+
</CommandItem>
60+
<CommandItem
61+
onSelect={() => runCommand(() => console.log("Save"))}
62+
>
63+
Save
64+
</CommandItem>
65+
</CommandGroup>
66+
</CommandList>
67+
</CommandDialog>
68+
</>
69+
);
70+
}

client/src/components/Editor.tsx

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { useEffect, useRef } from "react";
2+
import * as monaco from "monaco-editor";
3+
import { Button } from "@/components/ui/button";
4+
import { Brain } from "lucide-react";
5+
6+
interface EditorProps {
7+
file: string | null;
8+
onAIToggle: () => void;
9+
}
10+
11+
export default function Editor({ file, onAIToggle }: EditorProps) {
12+
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
13+
const containerRef = useRef<HTMLDivElement>(null);
14+
15+
useEffect(() => {
16+
if (!containerRef.current) return;
17+
18+
editorRef.current = monaco.editor.create(containerRef.current, {
19+
value: "",
20+
language: "typescript",
21+
theme: "vs-dark",
22+
minimap: { enabled: true },
23+
automaticLayout: true,
24+
fontSize: 14,
25+
lineNumbers: "on",
26+
scrollBeyondLastLine: false,
27+
roundedSelection: false,
28+
padding: { top: 16 },
29+
});
30+
31+
return () => {
32+
editorRef.current?.dispose();
33+
};
34+
}, []);
35+
36+
useEffect(() => {
37+
if (!file || !editorRef.current) return;
38+
39+
// TODO: Load file content
40+
fetch(`/api/files/${encodeURIComponent(file)}`)
41+
.then((res) => res.text())
42+
.then((content) => {
43+
editorRef.current?.setValue(content);
44+
const ext = file.split(".").pop() || "";
45+
const language = getLanguageFromExt(ext);
46+
monaco.editor.setModelLanguage(
47+
editorRef.current!.getModel()!,
48+
language
49+
);
50+
});
51+
}, [file]);
52+
53+
return (
54+
<div className="h-full relative">
55+
<div ref={containerRef} className="h-full w-full" />
56+
<Button
57+
variant="ghost"
58+
size="icon"
59+
className="absolute top-2 right-2"
60+
onClick={onAIToggle}
61+
>
62+
<Brain className="h-5 w-5" />
63+
</Button>
64+
</div>
65+
);
66+
}
67+
68+
function getLanguageFromExt(ext: string): string {
69+
const map: Record<string, string> = {
70+
js: "javascript",
71+
jsx: "javascript",
72+
ts: "typescript",
73+
tsx: "typescript",
74+
py: "python",
75+
java: "java",
76+
cpp: "cpp",
77+
c: "c",
78+
html: "html",
79+
css: "css",
80+
json: "json",
81+
md: "markdown",
82+
};
83+
return map[ext] || "plaintext";
84+
}

0 commit comments

Comments
 (0)