Spaces:
Running
Running
Commit
·
a1f6147
1
Parent(s):
484a0e5
layout improvements
Browse files- package.json +1 -0
- src/components/play-tab.tsx +230 -112
- src/components/ui/separator.tsx +29 -0
- yarn.lock +7 -0
package.json
CHANGED
@@ -17,6 +17,7 @@
|
|
17 |
"@radix-ui/react-label": "^2.1.4",
|
18 |
"@radix-ui/react-popover": "^1.1.11",
|
19 |
"@radix-ui/react-select": "^2.2.2",
|
|
|
20 |
"@radix-ui/react-slot": "^1.2.0",
|
21 |
"@radix-ui/react-tabs": "^1.1.9",
|
22 |
"@radix-ui/react-tooltip": "^1.2.5",
|
|
|
17 |
"@radix-ui/react-label": "^2.1.4",
|
18 |
"@radix-ui/react-popover": "^1.1.11",
|
19 |
"@radix-ui/react-select": "^2.2.2",
|
20 |
+
"@radix-ui/react-separator": "^1.1.5",
|
21 |
"@radix-ui/react-slot": "^1.2.0",
|
22 |
"@radix-ui/react-tabs": "^1.1.9",
|
23 |
"@radix-ui/react-tooltip": "^1.2.5",
|
src/components/play-tab.tsx
CHANGED
@@ -16,7 +16,14 @@ import {
|
|
16 |
} from "@/components/ui/select";
|
17 |
import { API_BASE } from "@/lib/constants";
|
18 |
import { VirtualizedCombobox } from "./ui/virtualized-combobox";
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
|
21 |
export default function PlayTab({
|
22 |
startArticle,
|
@@ -30,12 +37,23 @@ export default function PlayTab({
|
|
30 |
const [maxHops, setMaxHops] = useState<number>(20);
|
31 |
const [isGameStarted, setIsGameStarted] = useState<boolean>(false);
|
32 |
const [startPage, setStartPage] = useState<string>(startArticle || "Dogs");
|
33 |
-
const [targetPage, setTargetPage] = useState<string>(
|
|
|
|
|
34 |
const [maxTokens, setMaxTokens] = useState<number>(1024);
|
35 |
const [maxLinks, setMaxLinks] = useState<number>(200);
|
36 |
const [isServerConnected, setIsServerConnected] = useState<boolean>(false);
|
37 |
-
const [modelList, setModelList] = useState<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
const [allArticles, setAllArticles] = useState<string[]>([]);
|
|
|
39 |
// Server connection check
|
40 |
useEffect(() => {
|
41 |
fetchAvailableModels();
|
@@ -55,14 +73,14 @@ export default function PlayTab({
|
|
55 |
return () => clearInterval(interval);
|
56 |
}, []);
|
57 |
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
|
67 |
const handleStartGame = () => {
|
68 |
setIsGameStarted(true);
|
@@ -80,122 +98,222 @@ export default function PlayTab({
|
|
80 |
const response = await fetch(
|
81 |
"https://huggingface.co/api/models?inference_provider=all&pipeline_tag=text-generation"
|
82 |
);
|
83 |
-
const models = await response.json()
|
84 |
-
const filteredModels = models.filter((m:
|
85 |
-
|
|
|
|
|
|
|
86 |
id: m.id,
|
87 |
likes: m.likes,
|
88 |
trendingScore: m.trendingScore,
|
89 |
-
author: m.id.split(
|
90 |
-
name: m.id.split(
|
91 |
-
|
|
|
92 |
console.log("got model list", modelList);
|
93 |
setModelList(modelList);
|
94 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
|
96 |
return (
|
97 |
-
<div className="space-y-
|
98 |
{!isGameStarted ? (
|
99 |
-
<
|
100 |
-
<
|
101 |
-
<
|
102 |
-
<Label htmlFor="player-select" className="block mb-2">
|
103 |
-
Player
|
104 |
-
</Label>
|
105 |
-
<Tabs
|
106 |
-
defaultValue="me"
|
107 |
-
value={player}
|
108 |
-
onValueChange={handlePlayerChange}
|
109 |
-
className="w-full"
|
110 |
-
>
|
111 |
-
<TabsList className="grid w-full grid-cols-2">
|
112 |
-
<TabsTrigger value="me">Me</TabsTrigger>
|
113 |
-
<TabsTrigger value="model">Model</TabsTrigger>
|
114 |
-
</TabsList>
|
115 |
-
</Tabs>
|
116 |
-
</div>
|
117 |
-
<div>
|
118 |
-
<Label htmlFor="start-page" className="block mb-2">
|
119 |
-
Start Page
|
120 |
-
</Label>
|
121 |
-
<VirtualizedCombobox
|
122 |
-
options={allArticles}
|
123 |
-
value={startPage}
|
124 |
-
onValueChange={(value) => setStartPage(value)}
|
125 |
-
// searchPlaceholder="e.g. Dogs"
|
126 |
-
/>
|
127 |
-
</div>
|
128 |
|
129 |
-
<div className="
|
130 |
-
<div
|
131 |
-
<
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
|
|
|
|
|
|
140 |
</div>
|
141 |
-
<Button onClick={handleStartGame} className="mb-0.5">
|
142 |
-
Start Game
|
143 |
-
</Button>
|
144 |
-
</div>
|
145 |
-
</div>
|
146 |
|
147 |
-
|
148 |
-
|
149 |
-
<div>
|
150 |
-
<
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
/>
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
</
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
</div>
|
197 |
)}
|
198 |
-
</
|
199 |
) : (
|
200 |
<GameComponent
|
201 |
player={player}
|
|
|
16 |
} from "@/components/ui/select";
|
17 |
import { API_BASE } from "@/lib/constants";
|
18 |
import { VirtualizedCombobox } from "./ui/virtualized-combobox";
|
19 |
+
import { Info, Shuffle } from "lucide-react";
|
20 |
+
import {
|
21 |
+
Tooltip,
|
22 |
+
TooltipContent,
|
23 |
+
TooltipProvider,
|
24 |
+
TooltipTrigger,
|
25 |
+
} from "@/components/ui/tooltip";
|
26 |
+
import { Separator } from "@/components/ui/separator";
|
27 |
|
28 |
export default function PlayTab({
|
29 |
startArticle,
|
|
|
37 |
const [maxHops, setMaxHops] = useState<number>(20);
|
38 |
const [isGameStarted, setIsGameStarted] = useState<boolean>(false);
|
39 |
const [startPage, setStartPage] = useState<string>(startArticle || "Dogs");
|
40 |
+
const [targetPage, setTargetPage] = useState<string>(
|
41 |
+
destinationArticle || "Canada"
|
42 |
+
);
|
43 |
const [maxTokens, setMaxTokens] = useState<number>(1024);
|
44 |
const [maxLinks, setMaxLinks] = useState<number>(200);
|
45 |
const [isServerConnected, setIsServerConnected] = useState<boolean>(false);
|
46 |
+
const [modelList, setModelList] = useState<
|
47 |
+
{
|
48 |
+
id: string;
|
49 |
+
name: string;
|
50 |
+
author: string;
|
51 |
+
likes: number;
|
52 |
+
trendingScore: number;
|
53 |
+
}[]
|
54 |
+
>([]);
|
55 |
const [allArticles, setAllArticles] = useState<string[]>([]);
|
56 |
+
|
57 |
// Server connection check
|
58 |
useEffect(() => {
|
59 |
fetchAvailableModels();
|
|
|
73 |
return () => clearInterval(interval);
|
74 |
}, []);
|
75 |
|
76 |
+
useEffect(() => {
|
77 |
+
const fetchAllArticles = async () => {
|
78 |
+
const response = await fetch(`${API_BASE}/get_all_articles`);
|
79 |
+
const data = await response.json();
|
80 |
+
setAllArticles(data);
|
81 |
+
};
|
82 |
+
fetchAllArticles();
|
83 |
+
}, []);
|
84 |
|
85 |
const handleStartGame = () => {
|
86 |
setIsGameStarted(true);
|
|
|
98 |
const response = await fetch(
|
99 |
"https://huggingface.co/api/models?inference_provider=all&pipeline_tag=text-generation"
|
100 |
);
|
101 |
+
const models = await response.json();
|
102 |
+
const filteredModels = models.filter((m: { tags: string[] }) =>
|
103 |
+
m.tags.includes("text-generation")
|
104 |
+
);
|
105 |
+
const modelList = filteredModels.map(
|
106 |
+
(m: { id: string; likes: number; trendingScore: number }) => ({
|
107 |
id: m.id,
|
108 |
likes: m.likes,
|
109 |
trendingScore: m.trendingScore,
|
110 |
+
author: m.id.split("/")[0],
|
111 |
+
name: m.id.split("/")[1],
|
112 |
+
})
|
113 |
+
);
|
114 |
console.log("got model list", modelList);
|
115 |
setModelList(modelList);
|
116 |
+
};
|
117 |
+
|
118 |
+
const selectRandomArticle = (setter: (article: string) => void) => {
|
119 |
+
if (allArticles.length > 0) {
|
120 |
+
const randomIndex = Math.floor(Math.random() * allArticles.length);
|
121 |
+
setter(allArticles[randomIndex]);
|
122 |
+
}
|
123 |
+
};
|
124 |
|
125 |
return (
|
126 |
+
<div className="space-y-6">
|
127 |
{!isGameStarted ? (
|
128 |
+
<div className="space-y-6">
|
129 |
+
<Card className="p-6">
|
130 |
+
<h3 className="text-lg font-medium mb-4">Game Setup</h3>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
131 |
|
132 |
+
<div className="space-y-6">
|
133 |
+
<div>
|
134 |
+
<h4 className="text-sm font-medium mb-3">Player Mode</h4>
|
135 |
+
<Tabs
|
136 |
+
defaultValue="me"
|
137 |
+
value={player}
|
138 |
+
onValueChange={handlePlayerChange}
|
139 |
+
className="w-full"
|
140 |
+
>
|
141 |
+
<TabsList className="grid w-full grid-cols-2">
|
142 |
+
<TabsTrigger value="me">I'll Play</TabsTrigger>
|
143 |
+
<TabsTrigger value="model">AI Will Play</TabsTrigger>
|
144 |
+
</TabsList>
|
145 |
+
</Tabs>
|
146 |
</div>
|
|
|
|
|
|
|
|
|
|
|
147 |
|
148 |
+
<Separator className="my-4" />
|
149 |
+
|
150 |
+
<div className="grid grid-cols-1 md:grid-cols-1 gap-6">
|
151 |
+
<div>
|
152 |
+
<h4 className="text-sm font-medium mb-3">Start Page</h4>
|
153 |
+
<div className="flex items-center">
|
154 |
+
<VirtualizedCombobox
|
155 |
+
options={allArticles}
|
156 |
+
value={startPage}
|
157 |
+
onValueChange={(value) => setStartPage(value)}
|
158 |
/>
|
159 |
+
|
160 |
+
<Button
|
161 |
+
variant="outline"
|
162 |
+
size="sm"
|
163 |
+
onClick={() => selectRandomArticle(setStartPage)}
|
164 |
+
className="h-9 ml-2 whitespace-nowrap"
|
165 |
+
>
|
166 |
+
<Shuffle className="h-3.5 w-3.5 mr-1" />
|
167 |
+
Random
|
168 |
+
</Button>
|
169 |
+
{/* absorb rest of width */}
|
170 |
+
<div className="flex-1" />
|
171 |
+
</div>
|
172 |
+
</div>
|
173 |
+
|
174 |
+
<div>
|
175 |
+
<h4 className="text-sm font-medium mb-3">Target Page</h4>
|
176 |
+
<div className="flex items-center">
|
177 |
+
<VirtualizedCombobox
|
178 |
+
options={allArticles}
|
179 |
+
value={targetPage}
|
180 |
+
onValueChange={(value) => setTargetPage(value)}
|
181 |
+
/>
|
182 |
+
|
183 |
+
<Button
|
184 |
+
variant="outline"
|
185 |
+
size="sm"
|
186 |
+
onClick={() => selectRandomArticle(setTargetPage)}
|
187 |
+
className="h-9 ml-2 whitespace-nowrap"
|
188 |
+
>
|
189 |
+
<Shuffle className="h-3.5 w-3.5 mr-1" />
|
190 |
+
Random
|
191 |
+
</Button>
|
192 |
+
{/* absorb rest of width */}
|
193 |
+
<div className="flex-1" />
|
194 |
+
</div>
|
195 |
+
</div>
|
196 |
</div>
|
197 |
+
|
198 |
+
{player === "model" && (
|
199 |
+
<>
|
200 |
+
<Separator className="my-4" />
|
201 |
+
<div className="animate-in fade-in slide-in-from-top-5 duration-300">
|
202 |
+
<h4 className="text-sm font-medium mb-3">Model Settings</h4>
|
203 |
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
204 |
+
<div>
|
205 |
+
<Label
|
206 |
+
htmlFor="model-select"
|
207 |
+
className="flex items-center gap-1 text-sm mb-2"
|
208 |
+
>
|
209 |
+
Select Model
|
210 |
+
</Label>
|
211 |
+
<Select
|
212 |
+
value={selectedModel}
|
213 |
+
onValueChange={setSelectedModel}
|
214 |
+
>
|
215 |
+
<SelectTrigger className="w-full">
|
216 |
+
<SelectValue
|
217 |
+
placeholder={`Select a model (${modelList.length} available)`}
|
218 |
+
/>
|
219 |
+
</SelectTrigger>
|
220 |
+
<SelectContent>
|
221 |
+
{modelList.map((model) => (
|
222 |
+
<SelectItem key={model.id} value={model.id}>
|
223 |
+
{model.id}
|
224 |
+
</SelectItem>
|
225 |
+
))}
|
226 |
+
</SelectContent>
|
227 |
+
</Select>
|
228 |
+
</div>
|
229 |
+
|
230 |
+
<div>
|
231 |
+
<Label
|
232 |
+
htmlFor="max-tokens"
|
233 |
+
className="flex items-center gap-1 text-sm mb-2"
|
234 |
+
>
|
235 |
+
Max Tokens
|
236 |
+
<TooltipProvider>
|
237 |
+
<Tooltip>
|
238 |
+
<TooltipTrigger>
|
239 |
+
<Info className="h-3.5 w-3.5 text-muted-foreground" />
|
240 |
+
</TooltipTrigger>
|
241 |
+
<TooltipContent>
|
242 |
+
<p className="max-w-xs">
|
243 |
+
Maximum number of tokens the model can
|
244 |
+
generate per response.
|
245 |
+
</p>
|
246 |
+
</TooltipContent>
|
247 |
+
</Tooltip>
|
248 |
+
</TooltipProvider>
|
249 |
+
</Label>
|
250 |
+
<Input
|
251 |
+
id="max-tokens"
|
252 |
+
type="number"
|
253 |
+
value={maxTokens}
|
254 |
+
onChange={(e) =>
|
255 |
+
setMaxTokens(Number.parseInt(e.target.value))
|
256 |
+
}
|
257 |
+
min={1}
|
258 |
+
max={10000}
|
259 |
+
/>
|
260 |
+
</div>
|
261 |
+
|
262 |
+
<div>
|
263 |
+
<Label
|
264 |
+
htmlFor="max-links"
|
265 |
+
className="flex items-center gap-1 text-sm mb-2"
|
266 |
+
>
|
267 |
+
Max Links
|
268 |
+
<TooltipProvider>
|
269 |
+
<Tooltip>
|
270 |
+
<TooltipTrigger>
|
271 |
+
<Info className="h-3.5 w-3.5 text-muted-foreground" />
|
272 |
+
</TooltipTrigger>
|
273 |
+
<TooltipContent>
|
274 |
+
<p className="max-w-xs">
|
275 |
+
Maximum number of links the model can consider
|
276 |
+
per page.
|
277 |
+
</p>
|
278 |
+
</TooltipContent>
|
279 |
+
</Tooltip>
|
280 |
+
</TooltipProvider>
|
281 |
+
</Label>
|
282 |
+
<Input
|
283 |
+
id="max-links"
|
284 |
+
type="number"
|
285 |
+
value={maxLinks}
|
286 |
+
onChange={(e) =>
|
287 |
+
setMaxLinks(Number.parseInt(e.target.value))
|
288 |
+
}
|
289 |
+
min={1}
|
290 |
+
max={1000}
|
291 |
+
/>
|
292 |
+
</div>
|
293 |
+
</div>
|
294 |
+
</div>
|
295 |
+
</>
|
296 |
+
)}
|
297 |
+
</div>
|
298 |
+
</Card>
|
299 |
+
|
300 |
+
<div className="flex justify-center">
|
301 |
+
<Button
|
302 |
+
onClick={handleStartGame}
|
303 |
+
size="lg"
|
304 |
+
className="px-8"
|
305 |
+
variant="default"
|
306 |
+
>
|
307 |
+
Start Game
|
308 |
+
</Button>
|
309 |
+
</div>
|
310 |
+
|
311 |
+
{!isServerConnected && (
|
312 |
+
<div className="text-center p-2 bg-yellow-100 text-yellow-800 rounded-md text-sm">
|
313 |
+
Server connection issue. Some features may be unavailable.
|
314 |
</div>
|
315 |
)}
|
316 |
+
</div>
|
317 |
) : (
|
318 |
<GameComponent
|
319 |
player={player}
|
src/components/ui/separator.tsx
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as React from "react"
|
2 |
+
import * as SeparatorPrimitive from "@radix-ui/react-separator"
|
3 |
+
|
4 |
+
import { cn } from "@/lib/utils"
|
5 |
+
|
6 |
+
const Separator = React.forwardRef<
|
7 |
+
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
8 |
+
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
9 |
+
>(
|
10 |
+
(
|
11 |
+
{ className, orientation = "horizontal", decorative = true, ...props },
|
12 |
+
ref
|
13 |
+
) => (
|
14 |
+
<SeparatorPrimitive.Root
|
15 |
+
ref={ref}
|
16 |
+
decorative={decorative}
|
17 |
+
orientation={orientation}
|
18 |
+
className={cn(
|
19 |
+
"shrink-0 bg-border",
|
20 |
+
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
21 |
+
className
|
22 |
+
)}
|
23 |
+
{...props}
|
24 |
+
/>
|
25 |
+
)
|
26 |
+
)
|
27 |
+
Separator.displayName = SeparatorPrimitive.Root.displayName
|
28 |
+
|
29 |
+
export { Separator }
|
yarn.lock
CHANGED
@@ -791,6 +791,13 @@
|
|
791 |
aria-hidden "^1.2.4"
|
792 |
react-remove-scroll "^2.6.3"
|
793 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
794 |
"@radix-ui/[email protected]", "@radix-ui/react-slot@^1.2.0":
|
795 |
version "1.2.0"
|
796 |
resolved "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz"
|
|
|
791 |
aria-hidden "^1.2.4"
|
792 |
react-remove-scroll "^2.6.3"
|
793 |
|
794 |
+
"@radix-ui/react-separator@^1.1.5":
|
795 |
+
version "1.1.5"
|
796 |
+
resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-1.1.5.tgz#e124eb11c5ce5b5be45e08b8d07dbc2049733581"
|
797 |
+
integrity sha512-wdsy1P9VLR3L7mrznIHsMX4DEpn5tgqRDY4fjzIlz/Dw1QsumepxFtEPeK6ML5VwCcSMC8xw3won9W5Y9XDmcA==
|
798 |
+
dependencies:
|
799 |
+
"@radix-ui/react-primitive" "2.1.1"
|
800 |
+
|
801 |
"@radix-ui/[email protected]", "@radix-ui/react-slot@^1.2.0":
|
802 |
version "1.2.0"
|
803 |
resolved "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz"
|