Skip to content

Commit 9911362

Browse files
authored
Merge pull request #66 from PathOnAI/frontend-v2
Frontend v2
2 parents e576fbe + b5e8282 commit 9911362

13 files changed

+564
-1668
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import React from 'react';
2+
import { Button } from "@/components/ui/button";
3+
import { Input } from "@/components/ui/input";
4+
import { Label } from "@/components/ui/label";
5+
import { Info, ChevronDown, ChevronUp } from "lucide-react";
6+
7+
interface SearchParams {
8+
startingUrl: string;
9+
goal: string;
10+
algorithm: 'bfs' | 'dfs';
11+
maxDepth: number;
12+
}
13+
14+
interface ControlPanelProps {
15+
searchParams: SearchParams;
16+
handleParamChange: (param: keyof SearchParams, value: string | boolean | number) => void;
17+
handleStart: () => void;
18+
disconnect: () => void;
19+
isSearching: boolean;
20+
connected: boolean;
21+
}
22+
23+
const ControlPanel: React.FC<ControlPanelProps> = ({
24+
searchParams,
25+
handleParamChange,
26+
handleStart,
27+
disconnect,
28+
isSearching,
29+
connected,
30+
}) => {
31+
const [showParameters, setShowParameters] = React.useState(true);
32+
33+
return (
34+
<div className="bg-white dark:bg-slate-800 shadow-sm border-b sticky top-0 z-10">
35+
<div className="py-3 px-4 max-w-full">
36+
<div className="flex justify-between items-center">
37+
<h1 className="text-2xl font-bold text-sky-950 dark:text-sky-100">Visual Tree Search</h1>
38+
39+
<div className="flex gap-2">
40+
<Button
41+
onClick={handleStart}
42+
disabled={isSearching}
43+
className="bg-cyan-600 hover:bg-cyan-700 text-white disabled:bg-cyan-300 dark:disabled:bg-cyan-900"
44+
>
45+
Start
46+
</Button>
47+
<Button
48+
onClick={disconnect}
49+
disabled={!connected}
50+
variant="destructive"
51+
className="bg-rose-600 hover:bg-rose-700 text-white"
52+
>
53+
End
54+
</Button>
55+
</div>
56+
</div>
57+
58+
<div className="mt-3 bg-sky-50 dark:bg-sky-900/20 border border-sky-200 dark:border-sky-800 rounded-lg overflow-hidden">
59+
<div
60+
className="p-3 flex justify-between items-center cursor-pointer hover:bg-sky-100 dark:hover:bg-sky-900/30 transition-colors"
61+
onClick={() => setShowParameters(!showParameters)}
62+
>
63+
<div className="flex items-start gap-2">
64+
<Info className="h-5 w-5 text-cyan-600 dark:text-cyan-400 flex-shrink-0 mt-0.5" />
65+
<div>
66+
<h3 className="font-medium text-sky-800 dark:text-sky-300">How to use this playground</h3>
67+
<p className="text-sm text-sky-700 dark:text-sky-400">
68+
Configure your search parameters and visualize web browsing automation with tree search algorithms.
69+
</p>
70+
</div>
71+
</div>
72+
{showParameters ? <ChevronUp className="text-cyan-600" /> : <ChevronDown className="text-cyan-600" />}
73+
</div>
74+
75+
{showParameters && (
76+
<div className="p-4 border-t border-sky-200 dark:border-sky-800 bg-white/90 dark:bg-slate-800/90">
77+
{/* Instructions */}
78+
<div className="mb-4 ml-1 text-sm text-slate-700 dark:text-slate-300">
79+
<ol className="list-decimal list-inside space-y-1">
80+
<li>Click the &quot;Start&quot; button above to connect and begin the search.</li>
81+
<li>Configure your search parameters below.</li>
82+
<li>The tree of possible actions will appear on the right, while the resulting web page will display on the left.</li>
83+
<li>You can drag the divider to resize the panels as needed.</li>
84+
</ol>
85+
</div>
86+
87+
{/* Parameters Grid */}
88+
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mt-4">
89+
<div className="space-y-2">
90+
<Label htmlFor="startingUrl" className="text-slate-700 dark:text-slate-300 font-medium">Starting URL</Label>
91+
<Input
92+
id="startingUrl"
93+
value={searchParams.startingUrl}
94+
onChange={(e) => handleParamChange('startingUrl', e.target.value)}
95+
className="border-slate-300 dark:border-slate-600 focus:ring-cyan-500 focus:border-cyan-500"
96+
/>
97+
</div>
98+
99+
<div className="space-y-2">
100+
<Label htmlFor="goal" className="text-slate-700 dark:text-slate-300 font-medium">Goal</Label>
101+
<Input
102+
id="goal"
103+
value={searchParams.goal}
104+
onChange={(e) => handleParamChange('goal', e.target.value)}
105+
className="border-slate-300 dark:border-slate-600 focus:ring-cyan-500 focus:border-cyan-500"
106+
/>
107+
</div>
108+
109+
<div className="space-y-2">
110+
<Label htmlFor="algorithm" className="text-slate-700 dark:text-slate-300 font-medium">Algorithm</Label>
111+
<select
112+
id="algorithm"
113+
value={searchParams.algorithm}
114+
onChange={(e) => handleParamChange('algorithm', e.target.value as 'bfs' | 'dfs')}
115+
className="w-full p-2 border rounded bg-white dark:bg-slate-900 border-slate-300 dark:border-slate-600 focus:ring-cyan-500 focus:border-cyan-500"
116+
>
117+
<option value="bfs">Breadth-First Search (BFS)</option>
118+
<option value="dfs">Depth-First Search (DFS)</option>
119+
</select>
120+
</div>
121+
122+
<div className="space-y-2">
123+
<Label htmlFor="maxDepth" className="text-slate-700 dark:text-slate-300 font-medium">Max Depth</Label>
124+
<Input
125+
id="maxDepth"
126+
type="number"
127+
min={1}
128+
max={10}
129+
value={searchParams.maxDepth}
130+
onChange={(e) => handleParamChange('maxDepth', parseInt(e.target.value))}
131+
className="border-slate-300 dark:border-slate-600 focus:ring-cyan-500 focus:border-cyan-500"
132+
/>
133+
</div>
134+
</div>
135+
</div>
136+
)}
137+
</div>
138+
</div>
139+
</div>
140+
);
141+
};
142+
143+
export default ControlPanel;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from 'react';
2+
3+
interface LiveBrowserViewProps {
4+
liveBrowserUrl: string | null;
5+
width: string;
6+
}
7+
8+
const LiveBrowserView: React.FC<LiveBrowserViewProps> = ({ liveBrowserUrl, width }) => {
9+
return (
10+
<div
11+
className="bg-white dark:bg-slate-800 rounded-l-lg overflow-hidden"
12+
style={{ width }}
13+
>
14+
<div className="p-3 border-b border-slate-200 dark:border-slate-700 bg-gradient-to-r from-sky-50 to-white dark:from-slate-900 dark:to-slate-800">
15+
<h2 className="text-lg font-semibold text-sky-950 dark:text-sky-100 flex items-center">
16+
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 mr-2 text-cyan-500" viewBox="0 0 20 20" fill="currentColor">
17+
<path fillRule="evenodd" d="M4.083 9h1.946c.089-1.546.383-2.97.837-4.118A6.004 6.004 0 004.083 9zM10 2a8 8 0 100 16 8 8 0 000-16zm0 2c-.076 0-.232.032-.465.262-.238.234-.497.623-.737 1.182-.389.907-.673 2.142-.766 3.556h3.936c-.093-1.414-.377-2.649-.766-3.556-.24-.56-.5-.948-.737-1.182C10.232 4.032 10.076 4 10 4zm3.971 5c-.089-1.546-.383-2.97-.837-4.118A6.004 6.004 0 0115.917 9h-1.946zm-2.003 2H8.032c.093 1.414.377 2.649.766 3.556.24.56.5.948.737 1.182.233.23.389.262.465.262.076 0 .232-.032.465-.262.238-.234.498-.623.737-1.182.389-.907.673-2.142.766-3.556zm1.166 4.118c.454-1.147.748-2.572.837-4.118h1.946a6.004 6.004 0 01-2.783 4.118zm-6.268 0C6.412 13.97 6.118 12.546 6.03 11H4.083a6.004 6.004 0 002.783 4.118z" clipRule="evenodd" />
18+
</svg>
19+
Live Browser View
20+
</h2>
21+
</div>
22+
<div className="h-[calc(100%-48px)] w-full overflow-auto">
23+
{liveBrowserUrl ? (
24+
<iframe
25+
src={liveBrowserUrl}
26+
className="w-full h-full"
27+
title="Live Browser View"
28+
/>
29+
) : (
30+
<div className="h-full w-full flex items-center justify-center bg-gradient-to-r from-sky-50 to-white dark:from-slate-900 dark:to-slate-800 text-slate-500 dark:text-slate-400">
31+
<p>Browser view will appear here when search starts</p>
32+
</div>
33+
)}
34+
</div>
35+
</div>
36+
);
37+
};
38+
39+
export default LiveBrowserView;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
3+
interface Message {
4+
content: string;
5+
type: 'incoming' | 'outgoing';
6+
timestamp: string;
7+
}
8+
9+
interface MessageLogPanelProps {
10+
messages: Message[];
11+
messagesEndRef?: React.RefObject<HTMLDivElement | null>;
12+
}
13+
14+
const MessageLogPanel: React.FC<MessageLogPanelProps> = ({ messages, messagesEndRef }) => {
15+
return (
16+
<div className="bg-white dark:bg-slate-800 rounded-lg shadow-md border border-slate-200 dark:border-slate-700 p-3 mt-4">
17+
<h2 className="text-lg font-semibold mb-2 text-sky-950 dark:text-sky-100 flex items-center">
18+
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 mr-2 text-cyan-500" viewBox="0 0 20 20" fill="currentColor">
19+
<path fillRule="evenodd" d="M18 10c0 3.866-3.582 7-8 7a8.841 8.841 0 01-4.083-.98L2 17l1.338-3.123C2.493 12.767 2 11.434 2 10c0-3.866 3.582-7 8-7s8 3.134 8 7zM7 9H5v2h2V9zm8 0h-2v2h2V9zM9 9h2v2H9V9z" clipRule="evenodd" />
20+
</svg>
21+
Message Log
22+
</h2>
23+
<div className="h-[150px] overflow-y-auto border border-slate-200 dark:border-slate-700 rounded-md p-2 bg-gradient-to-r from-sky-50 to-white dark:from-slate-900 dark:to-slate-800">
24+
{messages.map((msg, index) => (
25+
<div key={index} className={`mb-2 ${msg.type === 'outgoing' ? 'text-cyan-600 dark:text-cyan-400' : 'text-slate-800 dark:text-slate-200'}`}>
26+
<div className="text-xs text-slate-500 dark:text-slate-400 mb-1">{msg.timestamp} - {msg.type}</div>
27+
<pre className="whitespace-pre-wrap bg-white dark:bg-slate-800 p-2 rounded text-sm border border-slate-200 dark:border-slate-700">{msg.content}</pre>
28+
</div>
29+
))}
30+
{messagesEndRef && <div ref={messagesEndRef} />}
31+
</div>
32+
</div>
33+
);
34+
};
35+
36+
export default MessageLogPanel;

visual-tree-search-app/components/Sidebar.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@ const Sidebar = () => {
1616
icon: Home
1717
},
1818
{
19-
name: 'Playground',
20-
href: '/playground',
19+
name: 'MCTS',
20+
href: '/MCTSAgent',
2121
icon: LayoutDashboard
2222
},
2323
{
24-
name: 'D3 Playground',
25-
href: '/d3-playground',
24+
name: 'LATS',
25+
href: '/LATSAgent',
2626
icon: Network
2727
},
2828
{
29-
name: 'Tree Search',
30-
href: '/tree-search-playground',
29+
name: 'BFS/DFS',
30+
href: '/SimpleSearchAgent',
3131
icon: Search
3232
},
3333
];
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import React, { useEffect, useRef } from 'react';
2+
import * as d3 from 'd3';
3+
import { useTheme } from 'next-themes';
4+
5+
interface Message {
6+
content: string | {
7+
type: string;
8+
tree?: Array<{
9+
id: number;
10+
parent_id: number | null;
11+
action: string;
12+
description: string | null;
13+
}>;
14+
};
15+
type: 'incoming' | 'outgoing';
16+
timestamp: string;
17+
}
18+
19+
interface SimpleSearchVisualProps {
20+
messages: Message[];
21+
}
22+
23+
const SimpleSearchVisual: React.FC<SimpleSearchVisualProps> = ({ messages }) => {
24+
const svgRef = useRef<SVGSVGElement>(null);
25+
const containerRef = useRef<HTMLDivElement>(null);
26+
const { theme } = useTheme();
27+
28+
// Simple placeholder visualization
29+
useEffect(() => {
30+
if (!svgRef.current || !messages.length) return;
31+
32+
// Clear previous content
33+
const svg = d3.select(svgRef.current);
34+
svg.selectAll("*").remove();
35+
36+
// Create a simple placeholder visualization
37+
const width = 400;
38+
const height = 700;
39+
const margin = { top: 20, right: 20, bottom: 20, left: 20 };
40+
41+
// Filter tree_update messages
42+
const treeUpdates = messages.filter(msg => {
43+
try {
44+
const data = typeof msg.content === 'string' ? JSON.parse(msg.content) : msg.content;
45+
return data.type === 'tree_update';
46+
} catch {
47+
return false;
48+
}
49+
});
50+
51+
// Draw placeholder circles for each tree update
52+
const g = svg.append("g")
53+
.attr("transform", `translate(${margin.left},${margin.top})`);
54+
55+
g.selectAll("circle")
56+
.data(treeUpdates)
57+
.enter()
58+
.append("circle")
59+
.attr("cx", (d, i) => (i % 3) * 100 + 50)
60+
.attr("cy", (d, i) => Math.floor(i / 3) * 100 + 50)
61+
.attr("r", 20)
62+
.attr("fill", theme === 'dark' ? "#4B5563" : "#9CA3AF")
63+
.attr("stroke", theme === 'dark' ? "#374151" : "#E5E7EB");
64+
65+
// Add placeholder text
66+
g.append("text")
67+
.attr("x", width / 2)
68+
.attr("y", height / 2)
69+
.attr("text-anchor", "middle")
70+
.attr("fill", theme === 'dark' ? "#FFFFFF" : "#111827")
71+
.text(`Tree Updates: ${treeUpdates.length}`);
72+
73+
}, [messages, theme]);
74+
75+
return (
76+
<div className="w-[30%] bg-white dark:bg-slate-800 rounded-r-lg overflow-hidden">
77+
<div className="p-3 border-b border-slate-200 dark:border-slate-700 bg-gradient-to-r from-sky-50 to-white dark:from-slate-900 dark:to-slate-800">
78+
<h2 className="text-lg font-semibold text-sky-950 dark:text-sky-100 flex items-center">
79+
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 mr-2 text-cyan-500" viewBox="0 0 20 20" fill="currentColor">
80+
<path d="M7 3a1 1 0 000 2h6a1 1 0 100-2H7zM4 7a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1zM2 11a2 2 0 012-2h12a2 2 0 012 2v4a2 2 0 01-2 2H4a2 2 0 01-2-2v-4z" />
81+
</svg>
82+
Tree Visualization
83+
</h2>
84+
</div>
85+
<div ref={containerRef} className="h-[calc(100%-48px)] w-full overflow-auto bg-gradient-to-r from-sky-50 to-white dark:from-slate-900 dark:to-slate-800">
86+
<svg
87+
ref={svgRef}
88+
width="400"
89+
height="700"
90+
className="overflow-visible"
91+
></svg>
92+
</div>
93+
</div>
94+
);
95+
};
96+
97+
export default SimpleSearchVisual;

0 commit comments

Comments
 (0)