stillerman commited on
Commit
a1f6147
·
1 Parent(s): 484a0e5

layout improvements

Browse files
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>(destinationArticle || "Canada");
 
 
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<{id: string, name: string, author: string, likes: number, trendingScore: number}[]>([]);
 
 
 
 
 
 
 
 
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
- useEffect(() => {
59
- const fetchAllArticles = async () => {
60
- const response = await fetch(`${API_BASE}/get_all_articles`);
61
- const data = await response.json();
62
- setAllArticles(data);
63
- };
64
- fetchAllArticles();
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: any) => m.tags.includes('text-generation'))
85
- const modelList = filteredModels.map((m: any) => ({
 
 
 
86
  id: m.id,
87
  likes: m.likes,
88
  trendingScore: m.trendingScore,
89
- author: m.id.split('/')[0],
90
- name: m.id.split('/')[1],
91
- }));
 
92
  console.log("got model list", modelList);
93
  setModelList(modelList);
94
- }
 
 
 
 
 
 
 
95
 
96
  return (
97
- <div className="space-y-4">
98
  {!isGameStarted ? (
99
- <Card className="p-6">
100
- <div className="grid grid-cols-2 md:grid-cols-3 gap-6 mt-6">
101
- <div>
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="flex items-end gap-2">
130
- <div className="flex-1">
131
- <Label htmlFor="start-page" className="block mb-2">
132
- Target Page
133
- </Label>
134
- <VirtualizedCombobox
135
- options={allArticles}
136
- value={targetPage}
137
- onValueChange={(value) => setTargetPage(value)}
138
- // searchPlaceholder="e.g. Canada"
139
- />
 
 
 
140
  </div>
141
- <Button onClick={handleStartGame} className="mb-0.5">
142
- Start Game
143
- </Button>
144
- </div>
145
- </div>
146
 
147
- {player === "model" && (
148
- <div className="md:col-span-3 animate-in fade-in slide-in-from-top-5 duration-300 grid grid-cols-1 md:grid-cols-3 gap-4 mt-2">
149
- <div>
150
- <Label htmlFor="model-select" className="block mb-2">
151
- Select Model
152
- </Label>
153
- <Select value={selectedModel} onValueChange={setSelectedModel}>
154
- <SelectTrigger className="w-full">
155
- <SelectValue
156
- placeholder={`Select a model (${modelList.length} models available)`}
157
  />
158
- </SelectTrigger>
159
- <SelectContent>
160
- {modelList.map((model) => (
161
- <SelectItem key={model.id} value={model.id}>
162
- {model.id}
163
- </SelectItem>
164
- ))}
165
- </SelectContent>
166
- </Select>
167
- </div>
168
- <div>
169
- <Label htmlFor="max-tokens" className="block mb-2">
170
- Max Tokens
171
- </Label>
172
- <Input
173
- id="max-tokens"
174
- type="number"
175
- value={maxTokens}
176
- onChange={(e) =>
177
- setMaxTokens(Number.parseInt(e.target.value))
178
- }
179
- min={1}
180
- max={10000}
181
- />
182
- </div>
183
- <div>
184
- <Label htmlFor="max-links" className="block mb-2">
185
- Max Links
186
- </Label>
187
- <Input
188
- id="max-links"
189
- type="number"
190
- value={maxLinks}
191
- onChange={(e) => setMaxLinks(Number.parseInt(e.target.value))}
192
- min={1}
193
- max={1000}
194
- />
195
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  </div>
197
  )}
198
- </Card>
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"