diamond-in commited on
Commit
9f3f336
·
verified ·
1 Parent(s): 57be883

Update frontend/src/App.tsx

Browse files
Files changed (1) hide show
  1. frontend/src/App.tsx +78 -9
frontend/src/App.tsx CHANGED
@@ -1,9 +1,78 @@
1
- import React from 'react'
2
- import ReactDOM from 'react-dom/client'
3
- import App from './App'
4
-
5
- ReactDOM.createRoot(document.getElementById('root')!).render(
6
- <React.StrictMode>
7
- <App />
8
- </React.StrictMode>,
9
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useEffect, useState } from 'react';
2
+
3
+ export default function App() {
4
+ const [browserFrame, setBrowserFrame] = useState<string>("");
5
+ const [logs, setLogs] = useState<string[]>([]);
6
+ const [input, setInput] = useState("");
7
+
8
+ useEffect(() => {
9
+ const eventSource = new EventSource("/api/stream");
10
+ eventSource.onmessage = (event) => {
11
+ try {
12
+ const data = JSON.parse(event.data);
13
+ if (data.type === "frame") setBrowserFrame(data.data);
14
+ if (data.type === "log") setLogs(prev => [...prev, data.data].slice(-10));
15
+ } catch (e) {
16
+ console.error("SSE Parse Error", e);
17
+ }
18
+ };
19
+ return () => eventSource.close();
20
+ }, []);
21
+
22
+ const sendMessage = async () => {
23
+ if (!input) return;
24
+ setLogs(prev => [...prev, `You: ${input}`]);
25
+ await fetch("/api/chat", {
26
+ method: "POST",
27
+ headers: { "Content-Type": "application/json" },
28
+ body: JSON.stringify({ message: input })
29
+ });
30
+ setInput("");
31
+ };
32
+
33
+ return (
34
+ <div className="flex h-screen bg-zinc-950 text-zinc-200 font-sans">
35
+ <div className="w-80 flex flex-col border-r border-zinc-800 p-4 bg-zinc-900/50">
36
+ <h1 className="text-lg font-bold mb-4 text-blue-500 flex items-center gap-2">
37
+ <div className="w-3 h-3 bg-blue-500 rounded-full animate-pulse"></div>
38
+ Polymorphic Agent
39
+ </h1>
40
+ <div className="flex-1 overflow-y-auto space-y-2 mb-4 scrollbar-hide">
41
+ {logs.map((log, i) => (
42
+ <div key={i} className="p-2 bg-zinc-800/50 rounded border border-zinc-700/50 text-xs">
43
+ {log}
44
+ </div>
45
+ ))}
46
+ </div>
47
+ <div className="flex gap-2">
48
+ <input
49
+ className="flex-1 bg-zinc-800 border border-zinc-700 p-2 rounded text-sm focus:outline-none focus:border-blue-500"
50
+ value={input}
51
+ onChange={(e) => setInput(e.target.value)}
52
+ onKeyDown={(e) => e.key === 'Enter' && sendMessage()}
53
+ placeholder="Ask agent..."
54
+ />
55
+ <button onClick={sendMessage} className="bg-blue-600 hover:bg-blue-500 px-3 py-1 rounded text-sm transition-colors">
56
+ Send
57
+ </button>
58
+ </div>
59
+ </div>
60
+
61
+ <div className="flex-1 bg-black flex flex-col relative">
62
+ <div className="absolute top-2 left-2 z-10 bg-black/60 px-2 py-1 rounded text-[10px] uppercase tracking-widest text-zinc-500 border border-zinc-800">
63
+ Live Nix Environment View
64
+ </div>
65
+ <div className="flex-1 flex items-center justify-center">
66
+ {browserFrame ? (
67
+ <img src={`data:image/jpeg;base64,${browserFrame}`} className="max-w-full max-h-full object-contain shadow-2xl" />
68
+ ) : (
69
+ <div className="flex flex-col items-center gap-3">
70
+ <div className="w-8 h-8 border-4 border-blue-500/20 border-t-blue-500 rounded-full animate-spin"></div>
71
+ <div className="text-zinc-600 text-sm font-medium">Initializing Browser...</div>
72
+ </div>
73
+ )}
74
+ </div>
75
+ </div>
76
+ </div>
77
+ );
78
+ }