jurmy24 commited on
Commit
3a909c0
·
1 Parent(s): 743ae7d

implement victors viewer which is wayy better

Browse files
.DS_Store ADDED
Binary file (6.15 kB). View file
 
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
src/components/Logo.tsx ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { cn } from '@/lib/utils';
4
+
5
+ interface LogoProps extends React.HTMLAttributes<HTMLDivElement> {
6
+ iconOnly?: boolean;
7
+ }
8
+
9
+ const Logo: React.FC<LogoProps> = ({ className, iconOnly = false }) => {
10
+ return (
11
+ <div className={cn("flex items-center gap-2", className)}>
12
+ <img
13
+ src="/lovable-uploads/5e648747-34b7-4d8f-93fd-4dbd00aeeefc.png"
14
+ alt="LiveLab Logo"
15
+ className="h-8 w-8"
16
+ />
17
+ {!iconOnly && (
18
+ <span className="text-xl font-bold text-white">LiveLab</span>
19
+ )}
20
+ </div>
21
+ );
22
+ };
23
+
24
+ export default Logo;
src/components/landing/ActionList.tsx ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { Button } from "@/components/ui/button";
4
+ import { ArrowRight } from "lucide-react";
5
+ import { Action } from './types';
6
+
7
+ interface ActionListProps {
8
+ actions: Action[];
9
+ robotModel: string;
10
+ }
11
+
12
+ const ActionList: React.FC<ActionListProps> = ({ actions, robotModel }) => {
13
+ const isLeKiwi = robotModel === "LeKiwi";
14
+ const isDisabled = !robotModel || isLeKiwi;
15
+
16
+ return (
17
+ <div className="pt-6">
18
+ {!robotModel && (
19
+ <p className="text-center text-gray-400 mb-4">
20
+ Please select a robot model to continue.
21
+ </p>
22
+ )}
23
+ {isLeKiwi && (
24
+ <p className="text-center text-yellow-500 mb-4">
25
+ LeKiwi model is not yet supported. Please select another model to continue.
26
+ </p>
27
+ )}
28
+ <div className="space-y-4">
29
+ {actions.map((action, index) => (
30
+ <div
31
+ key={index}
32
+ className={`flex items-center justify-between p-4 bg-gray-800 rounded-lg border border-gray-700 transition-opacity ${
33
+ isDisabled ? "opacity-50" : ""
34
+ }`}
35
+ >
36
+ <div>
37
+ <h3 className="font-semibold text-lg">{action.title}</h3>
38
+ <p className="text-gray-400 text-sm">
39
+ {action.description}
40
+ </p>
41
+ </div>
42
+ <Button
43
+ onClick={action.handler}
44
+ size="icon"
45
+ className={`${action.color} text-white`}
46
+ disabled={isDisabled}
47
+ >
48
+ <ArrowRight className="w-5 h-5" />
49
+ </Button>
50
+ </div>
51
+ ))}
52
+ </div>
53
+ </div>
54
+ );
55
+ };
56
+
57
+ export default ActionList;
src/components/landing/LandingHeader.tsx ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ const LandingHeader = () => {
3
+ return <div className="text-center space-y-4 max-w-lg w-full">
4
+ <img src="/lovable-uploads/5e648747-34b7-4d8f-93fd-4dbd00aeeefc.png" alt="LiveLab Logo" className="mx-auto h-20 w-20" />
5
+ <h1 className="text-5xl font-bold tracking-tight">LiveLab</h1>
6
+ <p className="text-xl text-gray-400">LeRobot but on HFSpace.</p>
7
+ </div>;
8
+ };
9
+ export default LandingHeader;
src/components/landing/PermissionModal.tsx ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from "react";
3
+ import { Button } from "@/components/ui/button";
4
+ import {
5
+ Dialog,
6
+ DialogContent,
7
+ DialogHeader,
8
+ DialogTitle,
9
+ DialogDescription,
10
+ } from "@/components/ui/dialog";
11
+ import { Camera, Mic } from "lucide-react";
12
+
13
+ interface PermissionModalProps {
14
+ open: boolean;
15
+ onOpenChange: (open: boolean) => void;
16
+ onPermissionsResult: (allow: boolean) => void;
17
+ }
18
+
19
+ const PermissionModal: React.FC<PermissionModalProps> = ({ open, onOpenChange, onPermissionsResult }) => {
20
+ return (
21
+ <Dialog open={open} onOpenChange={onOpenChange}>
22
+ <DialogContent className="bg-gray-900 border-gray-800 text-white sm:max-w-[480px] p-8">
23
+ <DialogHeader>
24
+ <div className="flex justify-center items-center gap-4 mb-4">
25
+ <Camera className="w-8 h-8 text-orange-500" />
26
+ <Mic className="w-8 h-8 text-orange-500" />
27
+ </div>
28
+ <DialogTitle className="text-white text-center text-2xl font-bold">
29
+ Enable Camera & Microphone
30
+ </DialogTitle>
31
+ </DialogHeader>
32
+ <div className="text-center space-y-6 py-4">
33
+ <DialogDescription className="text-gray-400 text-base leading-relaxed">
34
+ LiveLab requires access to your camera and microphone for a fully
35
+ immersive telepresence experience. This enables real-time video
36
+ feedback and voice command capabilities.
37
+ </DialogDescription>
38
+ <div className="flex flex-col sm:flex-row gap-4 justify-center pt-2">
39
+ <Button
40
+ onClick={() => onPermissionsResult(true)}
41
+ className="w-full sm:w-auto bg-orange-500 hover:bg-orange-600 text-white px-10 py-6 text-lg transition-all shadow-md shadow-orange-500/30 hover:shadow-lg hover:shadow-orange-500/40"
42
+ >
43
+ Allow Access
44
+ </Button>
45
+ <Button
46
+ onClick={() => onPermissionsResult(false)}
47
+ variant="outline"
48
+ className="w-full sm:w-auto border-gray-500 hover:border-gray-200 px-10 py-6 text-lg text-zinc-500 bg-zinc-900 hover:bg-zinc-800"
49
+ >
50
+ Continue without
51
+ </Button>
52
+ </div>
53
+ </div>
54
+ </DialogContent>
55
+ </Dialog>
56
+ );
57
+ };
58
+
59
+ export default PermissionModal;
src/components/landing/RecordingModal.tsx ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { Button } from "@/components/ui/button";
4
+ import { Input } from "@/components/ui/input";
5
+ import { Label } from "@/components/ui/label";
6
+ import {
7
+ Select,
8
+ SelectContent,
9
+ SelectItem,
10
+ SelectTrigger,
11
+ SelectValue,
12
+ } from "@/components/ui/select";
13
+ import {
14
+ Dialog,
15
+ DialogContent,
16
+ DialogHeader,
17
+ DialogTitle,
18
+ DialogDescription,
19
+ } from "@/components/ui/dialog";
20
+
21
+ interface RecordingModalProps {
22
+ open: boolean;
23
+ onOpenChange: (open: boolean) => void;
24
+ leaderPort: string;
25
+ setLeaderPort: (value: string) => void;
26
+ followerPort: string;
27
+ setFollowerPort: (value: string) => void;
28
+ leaderConfig: string;
29
+ setLeaderConfig: (value: string) => void;
30
+ followerConfig: string;
31
+ setFollowerConfig: (value: string) => void;
32
+ leaderConfigs: string[];
33
+ followerConfigs: string[];
34
+ datasetRepoId: string;
35
+ setDatasetRepoId: (value: string) => void;
36
+ singleTask: string;
37
+ setSingleTask: (value: string) => void;
38
+ numEpisodes: number;
39
+ setNumEpisodes: (value: number) => void;
40
+ isLoadingConfigs: boolean;
41
+ onStart: () => void;
42
+ }
43
+
44
+ const RecordingModal: React.FC<RecordingModalProps> = ({
45
+ open,
46
+ onOpenChange,
47
+ leaderPort,
48
+ setLeaderPort,
49
+ followerPort,
50
+ setFollowerPort,
51
+ leaderConfig,
52
+ setLeaderConfig,
53
+ followerConfig,
54
+ setFollowerConfig,
55
+ leaderConfigs,
56
+ followerConfigs,
57
+ datasetRepoId,
58
+ setDatasetRepoId,
59
+ singleTask,
60
+ setSingleTask,
61
+ numEpisodes,
62
+ setNumEpisodes,
63
+ isLoadingConfigs,
64
+ onStart,
65
+ }) => {
66
+ return (
67
+ <Dialog open={open} onOpenChange={onOpenChange}>
68
+ <DialogContent className="bg-gray-900 border-gray-800 text-white sm:max-w-[600px] p-8 max-h-[90vh] overflow-y-auto">
69
+ <DialogHeader>
70
+ <div className="flex justify-center items-center gap-4 mb-4">
71
+ <div className="w-8 h-8 bg-red-500 rounded-full flex items-center justify-center">
72
+ <span className="text-white font-bold text-sm">REC</span>
73
+ </div>
74
+ </div>
75
+ <DialogTitle className="text-white text-center text-2xl font-bold">
76
+ Configure Recording
77
+ </DialogTitle>
78
+ </DialogHeader>
79
+ <div className="space-y-6 py-4">
80
+ <DialogDescription className="text-gray-400 text-base leading-relaxed text-center">
81
+ Configure the robot arm settings and dataset parameters for
82
+ recording.
83
+ </DialogDescription>
84
+
85
+ <div className="grid grid-cols-1 gap-6">
86
+ <div className="space-y-4">
87
+ <h3 className="text-lg font-semibold text-white border-b border-gray-700 pb-2">
88
+ Robot Configuration
89
+ </h3>
90
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
91
+ <div className="space-y-2">
92
+ <Label htmlFor="recordLeaderPort" className="text-sm font-medium text-gray-300">
93
+ Leader Port
94
+ </Label>
95
+ <Input
96
+ id="recordLeaderPort"
97
+ value={leaderPort}
98
+ onChange={(e) => setLeaderPort(e.target.value)}
99
+ placeholder="/dev/tty.usbmodem5A460816421"
100
+ className="bg-gray-800 border-gray-700 text-white"
101
+ />
102
+ </div>
103
+ <div className="space-y-2">
104
+ <Label htmlFor="recordLeaderConfig" className="text-sm font-medium text-gray-300">
105
+ Leader Calibration Config
106
+ </Label>
107
+ <Select value={leaderConfig} onValueChange={setLeaderConfig}>
108
+ <SelectTrigger className="bg-gray-800 border-gray-700 text-white">
109
+ <SelectValue placeholder={isLoadingConfigs ? "Loading configs..." : "Select leader config"} />
110
+ </SelectTrigger>
111
+ <SelectContent className="bg-gray-800 border-gray-700">
112
+ {leaderConfigs.map((config) => (
113
+ <SelectItem key={config} value={config} className="text-white hover:bg-gray-700">
114
+ {config}
115
+ </SelectItem>
116
+ ))}
117
+ </SelectContent>
118
+ </Select>
119
+ </div>
120
+ <div className="space-y-2">
121
+ <Label htmlFor="recordFollowerPort" className="text-sm font-medium text-gray-300">
122
+ Follower Port
123
+ </Label>
124
+ <Input
125
+ id="recordFollowerPort"
126
+ value={followerPort}
127
+ onChange={(e) => setFollowerPort(e.target.value)}
128
+ placeholder="/dev/tty.usbmodem5A460816621"
129
+ className="bg-gray-800 border-gray-700 text-white"
130
+ />
131
+ </div>
132
+ <div className="space-y-2">
133
+ <Label htmlFor="recordFollowerConfig" className="text-sm font-medium text-gray-300">
134
+ Follower Calibration Config
135
+ </Label>
136
+ <Select value={followerConfig} onValueChange={setFollowerConfig}>
137
+ <SelectTrigger className="bg-gray-800 border-gray-700 text-white">
138
+ <SelectValue placeholder={isLoadingConfigs ? "Loading configs..." : "Select follower config"} />
139
+ </SelectTrigger>
140
+ <SelectContent className="bg-gray-800 border-gray-700">
141
+ {followerConfigs.map((config) => (
142
+ <SelectItem key={config} value={config} className="text-white hover:bg-gray-700">
143
+ {config}
144
+ </SelectItem>
145
+ ))}
146
+ </SelectContent>
147
+ </Select>
148
+ </div>
149
+ </div>
150
+ </div>
151
+
152
+ <div className="space-y-4">
153
+ <h3 className="text-lg font-semibold text-white border-b border-gray-700 pb-2">
154
+ Dataset Configuration
155
+ </h3>
156
+ <div className="grid grid-cols-1 gap-4">
157
+ <div className="space-y-2">
158
+ <Label htmlFor="datasetRepoId" className="text-sm font-medium text-gray-300">
159
+ Dataset Repository ID *
160
+ </Label>
161
+ <Input
162
+ id="datasetRepoId"
163
+ value={datasetRepoId}
164
+ onChange={(e) => setDatasetRepoId(e.target.value)}
165
+ placeholder="username/dataset_name"
166
+ className="bg-gray-800 border-gray-700 text-white"
167
+ />
168
+ </div>
169
+ <div className="space-y-2">
170
+ <Label htmlFor="singleTask" className="text-sm font-medium text-gray-300">
171
+ Task Name *
172
+ </Label>
173
+ <Input
174
+ id="singleTask"
175
+ value={singleTask}
176
+ onChange={(e) => setSingleTask(e.target.value)}
177
+ placeholder="e.g., pick_and_place"
178
+ className="bg-gray-800 border-gray-700 text-white"
179
+ />
180
+ </div>
181
+ <div className="space-y-2">
182
+ <Label htmlFor="numEpisodes" className="text-sm font-medium text-gray-300">
183
+ Number of Episodes
184
+ </Label>
185
+ <Input
186
+ id="numEpisodes"
187
+ type="number"
188
+ min="1"
189
+ max="100"
190
+ value={numEpisodes}
191
+ onChange={(e) => setNumEpisodes(parseInt(e.target.value))}
192
+ className="bg-gray-800 border-gray-700 text-white"
193
+ />
194
+ </div>
195
+ </div>
196
+ </div>
197
+ </div>
198
+
199
+ <div className="flex flex-col sm:flex-row gap-4 justify-center pt-4">
200
+ <Button
201
+ onClick={onStart}
202
+ className="w-full sm:w-auto bg-red-500 hover:bg-red-600 text-white px-10 py-6 text-lg transition-all shadow-md shadow-red-500/30 hover:shadow-lg hover:shadow-red-500/40"
203
+ disabled={isLoadingConfigs}
204
+ >
205
+ Start Recording
206
+ </Button>
207
+ <Button
208
+ onClick={() => onOpenChange(false)}
209
+ variant="outline"
210
+ className="w-full sm:w-auto border-gray-500 hover:border-gray-200 px-10 py-6 text-lg text-zinc-500 bg-zinc-900 hover:bg-zinc-800"
211
+ >
212
+ Cancel
213
+ </Button>
214
+ </div>
215
+ </div>
216
+ </DialogContent>
217
+ </Dialog>
218
+ );
219
+ };
220
+
221
+ export default RecordingModal;
src/components/landing/RobotModelSelector.tsx ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
3
+ import { Label } from "@/components/ui/label";
4
+ interface RobotModelSelectorProps {
5
+ robotModel: string;
6
+ onValueChange: (model: string) => void;
7
+ }
8
+ const RobotModelSelector: React.FC<RobotModelSelectorProps> = ({
9
+ robotModel,
10
+ onValueChange
11
+ }) => {
12
+ return <>
13
+ <h2 className="text-2xl font-semibold text-center text-white">
14
+ Select Robot Model
15
+ </h2>
16
+ <RadioGroup value={robotModel} onValueChange={onValueChange} className="space-y-2">
17
+ <div>
18
+ <RadioGroupItem value="SO100" id="so100" className="sr-only" />
19
+ <Label htmlFor="so100" className="flex items-center space-x-4 p-4 rounded-lg bg-gray-800 border border-gray-700 cursor-pointer transition-all">
20
+ <span className="w-6 h-6 rounded-full border-2 border-gray-500 flex items-center justify-center">
21
+ {robotModel === "SO100" && <span className="w-3 h-3 rounded-full bg-orange-500" />}
22
+ </span>
23
+ <span className="text-lg flex-1">SO100/SO101
24
+ </span>
25
+ </Label>
26
+ </div>
27
+ <div>
28
+ <RadioGroupItem value="LeKiwi" id="lekiwi" className="sr-only" />
29
+ <Label htmlFor="lekiwi" className="flex items-center space-x-4 p-4 rounded-lg bg-gray-800 border border-gray-700 cursor-pointer transition-all">
30
+ <span className="w-6 h-6 rounded-full border-2 border-gray-500 flex items-center justify-center">
31
+ {robotModel === "LeKiwi" && <span className="w-3 h-3 rounded-full bg-orange-500" />}
32
+ </span>
33
+ <span className="text-lg flex-1">LeKiwi</span>
34
+ </Label>
35
+ </div>
36
+ </RadioGroup>
37
+ </>;
38
+ };
39
+ export default RobotModelSelector;
src/components/landing/TeleoperationModal.tsx ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { Button } from "@/components/ui/button";
4
+ import { Input } from "@/components/ui/input";
5
+ import { Label } from "@/components/ui/label";
6
+ import {
7
+ Select,
8
+ SelectContent,
9
+ SelectItem,
10
+ SelectTrigger,
11
+ SelectValue,
12
+ } from "@/components/ui/select";
13
+ import {
14
+ Dialog,
15
+ DialogContent,
16
+ DialogHeader,
17
+ DialogTitle,
18
+ DialogDescription,
19
+ } from "@/components/ui/dialog";
20
+ import { Settings } from "lucide-react";
21
+
22
+ interface TeleoperationModalProps {
23
+ open: boolean;
24
+ onOpenChange: (open: boolean) => void;
25
+ leaderPort: string;
26
+ setLeaderPort: (value: string) => void;
27
+ followerPort: string;
28
+ setFollowerPort: (value: string) => void;
29
+ leaderConfig: string;
30
+ setLeaderConfig: (value: string) => void;
31
+ followerConfig: string;
32
+ setFollowerConfig: (value: string) => void;
33
+ leaderConfigs: string[];
34
+ followerConfigs: string[];
35
+ isLoadingConfigs: boolean;
36
+ onStart: () => void;
37
+ }
38
+
39
+ const TeleoperationModal: React.FC<TeleoperationModalProps> = ({
40
+ open,
41
+ onOpenChange,
42
+ leaderPort,
43
+ setLeaderPort,
44
+ followerPort,
45
+ setFollowerPort,
46
+ leaderConfig,
47
+ setLeaderConfig,
48
+ followerConfig,
49
+ setFollowerConfig,
50
+ leaderConfigs,
51
+ followerConfigs,
52
+ isLoadingConfigs,
53
+ onStart,
54
+ }) => {
55
+ return (
56
+ <Dialog open={open} onOpenChange={onOpenChange}>
57
+ <DialogContent className="bg-gray-900 border-gray-800 text-white sm:max-w-[600px] p-8">
58
+ <DialogHeader>
59
+ <div className="flex justify-center items-center gap-4 mb-4">
60
+ <Settings className="w-8 h-8 text-yellow-500" />
61
+ </div>
62
+ <DialogTitle className="text-white text-center text-2xl font-bold">
63
+ Configure Teleoperation
64
+ </DialogTitle>
65
+ </DialogHeader>
66
+ <div className="space-y-6 py-4">
67
+ <DialogDescription className="text-gray-400 text-base leading-relaxed text-center">
68
+ Configure the robot arm ports and calibration settings for
69
+ teleoperation.
70
+ </DialogDescription>
71
+
72
+ <div className="grid grid-cols-1 gap-6">
73
+ <div className="space-y-2">
74
+ <Label htmlFor="leaderPort" className="text-sm font-medium text-gray-300">
75
+ Leader Port
76
+ </Label>
77
+ <Input
78
+ id="leaderPort"
79
+ value={leaderPort}
80
+ onChange={(e) => setLeaderPort(e.target.value)}
81
+ placeholder="/dev/tty.usbmodem5A460816421"
82
+ className="bg-gray-800 border-gray-700 text-white"
83
+ />
84
+ </div>
85
+
86
+ <div className="space-y-2">
87
+ <Label htmlFor="leaderConfig" className="text-sm font-medium text-gray-300">
88
+ Leader Calibration Config
89
+ </Label>
90
+ <Select value={leaderConfig} onValueChange={setLeaderConfig}>
91
+ <SelectTrigger className="bg-gray-800 border-gray-700 text-white">
92
+ <SelectValue
93
+ placeholder={
94
+ isLoadingConfigs ? "Loading configs..." : "Select leader config"
95
+ }
96
+ />
97
+ </SelectTrigger>
98
+ <SelectContent className="bg-gray-800 border-gray-700">
99
+ {leaderConfigs.map((config) => (
100
+ <SelectItem key={config} value={config} className="text-white hover:bg-gray-700">
101
+ {config}
102
+ </SelectItem>
103
+ ))}
104
+ </SelectContent>
105
+ </Select>
106
+ </div>
107
+
108
+ <div className="space-y-2">
109
+ <Label htmlFor="followerPort" className="text-sm font-medium text-gray-300">
110
+ Follower Port
111
+ </Label>
112
+ <Input
113
+ id="followerPort"
114
+ value={followerPort}
115
+ onChange={(e) => setFollowerPort(e.target.value)}
116
+ placeholder="/dev/tty.usbmodem5A460816621"
117
+ className="bg-gray-800 border-gray-700 text-white"
118
+ />
119
+ </div>
120
+
121
+ <div className="space-y-2">
122
+ <Label htmlFor="followerConfig" className="text-sm font-medium text-gray-300">
123
+ Follower Calibration Config
124
+ </Label>
125
+ <Select value={followerConfig} onValueChange={setFollowerConfig}>
126
+ <SelectTrigger className="bg-gray-800 border-gray-700 text-white">
127
+ <SelectValue
128
+ placeholder={
129
+ isLoadingConfigs ? "Loading configs..." : "Select follower config"
130
+ }
131
+ />
132
+ </SelectTrigger>
133
+ <SelectContent className="bg-gray-800 border-gray-700">
134
+ {followerConfigs.map((config) => (
135
+ <SelectItem key={config} value={config} className="text-white hover:bg-gray-700">
136
+ {config}
137
+ </SelectItem>
138
+ ))}
139
+ </SelectContent>
140
+ </Select>
141
+ </div>
142
+ </div>
143
+
144
+ <div className="flex flex-col sm:flex-row gap-4 justify-center pt-4">
145
+ <Button
146
+ onClick={onStart}
147
+ className="w-full sm:w-auto bg-yellow-500 hover:bg-yellow-600 text-white px-10 py-6 text-lg transition-all shadow-md shadow-yellow-500/30 hover:shadow-lg hover:shadow-yellow-500/40"
148
+ disabled={isLoadingConfigs}
149
+ >
150
+ Start Teleoperation
151
+ </Button>
152
+ <Button
153
+ onClick={() => onOpenChange(false)}
154
+ variant="outline"
155
+ className="w-full sm:w-auto border-gray-500 hover:border-gray-200 px-10 py-6 text-lg text-zinc-500 bg-zinc-900 hover:bg-zinc-800"
156
+ >
157
+ Cancel
158
+ </Button>
159
+ </div>
160
+ </div>
161
+ </DialogContent>
162
+ </Dialog>
163
+ );
164
+ };
165
+
166
+ export default TeleoperationModal;
src/components/landing/types.ts ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+
2
+ export interface Action {
3
+ title: string;
4
+ description: string;
5
+ handler: () => void;
6
+ color: string;
7
+ }
src/components/training/ConfigurationTab.tsx ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import DatasetConfig from './config/DatasetConfig';
4
+ import PolicyConfig from './config/PolicyConfig';
5
+ import TrainingParams from './config/TrainingParams';
6
+ import OptimizerConfig from './config/OptimizerConfig';
7
+ import LoggingConfig from './config/LoggingConfig';
8
+ import WandbConfig from './config/WandbConfig';
9
+ import EnvEvalConfig from './config/EnvEvalConfig';
10
+ import AdvancedConfig from './config/AdvancedConfig';
11
+ import { ConfigComponentProps } from './types';
12
+
13
+ const ConfigurationTab: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
14
+ return (
15
+ <div className="grid grid-cols-1 xl:grid-cols-2 gap-6">
16
+ <DatasetConfig config={config} updateConfig={updateConfig} />
17
+ <PolicyConfig config={config} updateConfig={updateConfig} />
18
+ <TrainingParams config={config} updateConfig={updateConfig} />
19
+ <OptimizerConfig config={config} updateConfig={updateConfig} />
20
+ <LoggingConfig config={config} updateConfig={updateConfig} />
21
+ <WandbConfig config={config} updateConfig={updateConfig} />
22
+ <EnvEvalConfig config={config} updateConfig={updateConfig} />
23
+ <AdvancedConfig config={config} updateConfig={updateConfig} />
24
+ </div>
25
+ );
26
+ };
27
+
28
+ export default ConfigurationTab;
src/components/training/MonitoringTab.tsx ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import MonitoringStats from './monitoring/MonitoringStats';
4
+ import TrainingLogs from './monitoring/TrainingLogs';
5
+ import { TrainingStatus, LogEntry } from './types';
6
+
7
+ interface MonitoringTabProps {
8
+ trainingStatus: TrainingStatus;
9
+ logs: LogEntry[];
10
+ logContainerRef: React.RefObject<HTMLDivElement>;
11
+ getProgressPercentage: () => number;
12
+ formatTime: (seconds: number) => string;
13
+ }
14
+
15
+ const MonitoringTab: React.FC<MonitoringTabProps> = ({
16
+ trainingStatus,
17
+ logs,
18
+ logContainerRef,
19
+ getProgressPercentage,
20
+ formatTime
21
+ }) => {
22
+ return (
23
+ <div className="space-y-6">
24
+ <MonitoringStats
25
+ trainingStatus={trainingStatus}
26
+ getProgressPercentage={getProgressPercentage}
27
+ formatTime={formatTime}
28
+ />
29
+ <TrainingLogs logs={logs} logContainerRef={logContainerRef} />
30
+ </div>
31
+ );
32
+ };
33
+
34
+ export default MonitoringTab;
src/components/training/TrainingControls.tsx ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { Button } from '@/components/ui/button';
4
+ import { Loader2, Play, Square } from 'lucide-react';
5
+ import { TrainingConfig, TrainingStatus } from './types';
6
+
7
+ interface TrainingControlsProps {
8
+ trainingStatus: TrainingStatus;
9
+ isStartingTraining: boolean;
10
+ trainingConfig: TrainingConfig;
11
+ handleStartTraining: () => void;
12
+ handleStopTraining: () => void;
13
+ }
14
+
15
+ const TrainingControls: React.FC<TrainingControlsProps> = ({
16
+ trainingStatus,
17
+ isStartingTraining,
18
+ trainingConfig,
19
+ handleStartTraining,
20
+ handleStopTraining,
21
+ }) => {
22
+ return (
23
+ <div className="fixed bottom-6 right-6 flex gap-3">
24
+ {!trainingStatus.training_active ? (
25
+ <Button
26
+ onClick={handleStartTraining}
27
+ disabled={
28
+ isStartingTraining || !trainingConfig.dataset_repo_id.trim()
29
+ }
30
+ size="lg"
31
+ className="bg-green-500 hover:bg-green-600 text-white font-semibold px-6 py-3 text-base rounded-full shadow-lg transition-all hover:scale-105"
32
+ >
33
+ {isStartingTraining ? (
34
+ <>
35
+ <Loader2 className="w-5 h-5 mr-2 animate-spin" />
36
+ Starting...
37
+ </>
38
+ ) : (
39
+ <>
40
+ <Play className="w-5 h-5 mr-2" />
41
+ Start Training
42
+ </>
43
+ )}
44
+ </Button>
45
+ ) : (
46
+ <Button
47
+ onClick={handleStopTraining}
48
+ disabled={!trainingStatus.available_controls.stop_training}
49
+ size="lg"
50
+ className="bg-red-500 hover:bg-red-600 text-white font-semibold px-6 py-3 text-base rounded-full shadow-lg transition-all hover:scale-105"
51
+ >
52
+ <Square className="w-5 h-5 mr-2" />
53
+ Stop Training
54
+ </Button>
55
+ )}
56
+ </div>
57
+ );
58
+ };
59
+
60
+ export default TrainingControls;
src/components/training/TrainingHeader.tsx ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { useNavigate } from 'react-router-dom';
4
+ import { Button } from '@/components/ui/button';
5
+ import { ArrowLeft } from 'lucide-react';
6
+ import Logo from '@/components/Logo';
7
+ import { TrainingStatus } from './types';
8
+
9
+ interface TrainingHeaderProps {
10
+ trainingStatus: TrainingStatus;
11
+ }
12
+
13
+ const TrainingHeader: React.FC<TrainingHeaderProps> = ({ trainingStatus }) => {
14
+ const navigate = useNavigate();
15
+
16
+ const getStatusColor = () => {
17
+ if (trainingStatus.training_active) return "text-green-400";
18
+ return "text-gray-400";
19
+ };
20
+
21
+ const getStatusText = () => {
22
+ if (trainingStatus.training_active) return "Training Active";
23
+ return "Ready to Train";
24
+ };
25
+
26
+ return (
27
+ <div className="flex items-center justify-between mb-8">
28
+ <div className="flex items-center gap-4">
29
+ <Button
30
+ variant="ghost"
31
+ size="icon"
32
+ onClick={() => navigate("/")}
33
+ className="text-slate-400 hover:bg-slate-800 hover:text-white rounded-lg"
34
+ >
35
+ <ArrowLeft className="w-5 h-5" />
36
+ </Button>
37
+ <Logo />
38
+ <h1 className="text-3xl font-bold text-white">
39
+ Training Dashboard
40
+ </h1>
41
+ </div>
42
+
43
+ <div className="flex items-center gap-3">
44
+ <div
45
+ className={`w-3 h-3 rounded-full ${
46
+ trainingStatus.training_active
47
+ ? "bg-green-400 animate-pulse"
48
+ : "bg-slate-500"
49
+ }`}
50
+ ></div>
51
+ <span className={`font-semibold ${getStatusColor()}`}>
52
+ {getStatusText()}
53
+ </span>
54
+ </div>
55
+ </div>
56
+ );
57
+ };
58
+
59
+ export default TrainingHeader;
src/components/training/TrainingTabs.tsx ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { Button } from '@/components/ui/button';
4
+ import { Settings, Activity } from 'lucide-react';
5
+
6
+ type Tab = 'config' | 'monitoring';
7
+
8
+ interface TrainingTabsProps {
9
+ activeTab: Tab;
10
+ setActiveTab: (tab: Tab) => void;
11
+ }
12
+
13
+ const TrainingTabs: React.FC<TrainingTabsProps> = ({ activeTab, setActiveTab }) => {
14
+ return (
15
+ <div className="flex gap-2 mb-6 border-b border-slate-700">
16
+ <Button
17
+ variant="ghost"
18
+ onClick={() => setActiveTab("config")}
19
+ className={`flex items-center gap-2 rounded-none px-4 py-3 text-sm font-semibold transition-colors ${
20
+ activeTab === "config"
21
+ ? "text-white border-b-2 border-white"
22
+ : "text-slate-400 hover:bg-slate-800 hover:text-white"
23
+ }`}
24
+ >
25
+ <Settings className="w-4 h-4" />
26
+ Configuration
27
+ </Button>
28
+ <Button
29
+ variant="ghost"
30
+ onClick={() => setActiveTab("monitoring")}
31
+ className={`flex items-center gap-2 rounded-none px-4 py-3 text-sm font-semibold transition-colors ${
32
+ activeTab === "monitoring"
33
+ ? "text-white border-b-2 border-white"
34
+ : "text-slate-400 hover:bg-slate-800 hover:text-white"
35
+ }`}
36
+ >
37
+ <Activity className="w-4 h-4" />
38
+ Monitoring
39
+ </Button>
40
+ </div>
41
+ );
42
+ };
43
+
44
+ export default TrainingTabs;
src/components/training/config/AdvancedConfig.tsx ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
4
+ import { Input } from '@/components/ui/input';
5
+ import { Label } from '@/components/ui/label';
6
+ import { Switch } from '@/components/ui/switch';
7
+ import { Settings } from 'lucide-react';
8
+ import { ConfigComponentProps } from '../types';
9
+
10
+ const AdvancedConfig: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
11
+ return (
12
+ <Card className="bg-slate-800/50 border-slate-700 rounded-xl">
13
+ <CardHeader>
14
+ <CardTitle className="flex items-center gap-3 text-white">
15
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
16
+ <Settings className="w-5 h-5 text-sky-400" />
17
+ </div>
18
+ Advanced Options
19
+ </CardTitle>
20
+ </CardHeader>
21
+ <CardContent className="space-y-4">
22
+ <div>
23
+ <Label htmlFor="config_path" className="text-slate-300">
24
+ Config Path (optional)
25
+ </Label>
26
+ <Input
27
+ id="config_path"
28
+ value={config.config_path || ""}
29
+ onChange={(e) =>
30
+ updateConfig("config_path", e.target.value || undefined)
31
+ }
32
+ placeholder="path/to/config.yaml"
33
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
34
+ />
35
+ </div>
36
+ <div className="flex items-center space-x-3 pt-2">
37
+ <Switch
38
+ id="use_policy_training_preset"
39
+ checked={config.use_policy_training_preset}
40
+ onCheckedChange={(checked) =>
41
+ updateConfig("use_policy_training_preset", checked)
42
+ }
43
+ />
44
+ <Label
45
+ htmlFor="use_policy_training_preset"
46
+ className="text-slate-300"
47
+ >
48
+ Use Policy Training Preset
49
+ </Label>
50
+ </div>
51
+ </CardContent>
52
+ </Card>
53
+ );
54
+ };
55
+
56
+ export default AdvancedConfig;
src/components/training/config/DatasetConfig.tsx ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
4
+ import { Input } from '@/components/ui/input';
5
+ import { Label } from '@/components/ui/label';
6
+ import { Database } from 'lucide-react';
7
+ import { ConfigComponentProps } from '../types';
8
+
9
+ const DatasetConfig: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
10
+ return (
11
+ <Card className="bg-slate-800/50 border-slate-700 rounded-xl">
12
+ <CardHeader>
13
+ <CardTitle className="flex items-center gap-3 text-white">
14
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
15
+ <Database className="w-5 h-5 text-sky-400" />
16
+ </div>
17
+ Dataset Configuration
18
+ </CardTitle>
19
+ </CardHeader>
20
+ <CardContent className="space-y-4">
21
+ <div>
22
+ <Label htmlFor="dataset_repo_id" className="text-slate-300">
23
+ Dataset Repository ID *
24
+ </Label>
25
+ <Input
26
+ id="dataset_repo_id"
27
+ value={config.dataset_repo_id}
28
+ onChange={(e) =>
29
+ updateConfig("dataset_repo_id", e.target.value)
30
+ }
31
+ placeholder="e.g., your-username/your-dataset"
32
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
33
+ />
34
+ <p className="text-xs text-slate-500 mt-1">
35
+ HuggingFace Hub dataset repository ID
36
+ </p>
37
+ </div>
38
+
39
+ <div>
40
+ <Label htmlFor="dataset_revision" className="text-slate-300">
41
+ Dataset Revision (optional)
42
+ </Label>
43
+ <Input
44
+ id="dataset_revision"
45
+ value={config.dataset_revision || ""}
46
+ onChange={(e) =>
47
+ updateConfig(
48
+ "dataset_revision",
49
+ e.target.value || undefined
50
+ )
51
+ }
52
+ placeholder="main"
53
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
54
+ />
55
+ <p className="text-xs text-slate-500 mt-1">
56
+ Git revision (branch, tag, or commit hash)
57
+ </p>
58
+ </div>
59
+
60
+ <div>
61
+ <Label htmlFor="dataset_root" className="text-slate-300">
62
+ Dataset Root Directory (optional)
63
+ </Label>
64
+ <Input
65
+ id="dataset_root"
66
+ value={config.dataset_root || ""}
67
+ onChange={(e) =>
68
+ updateConfig("dataset_root", e.target.value || undefined)
69
+ }
70
+ placeholder="./data"
71
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
72
+ />
73
+ </div>
74
+ </CardContent>
75
+ </Card>
76
+ );
77
+ };
78
+
79
+ export default DatasetConfig;
src/components/training/config/EnvEvalConfig.tsx ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
4
+ import { Input } from '@/components/ui/input';
5
+ import { Label } from '@/components/ui/label';
6
+ import { Switch } from '@/components/ui/switch';
7
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
8
+ import { Activity } from 'lucide-react';
9
+ import { ConfigComponentProps } from '../types';
10
+
11
+ const EnvEvalConfig: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
12
+ return (
13
+ <Card className="bg-slate-800/50 border-slate-700 rounded-xl">
14
+ <CardHeader>
15
+ <CardTitle className="flex items-center gap-3 text-white">
16
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
17
+ <Activity className="w-5 h-5 text-sky-400" />
18
+ </div>
19
+ Environment & Evaluation
20
+ </CardTitle>
21
+ </CardHeader>
22
+ <CardContent className="space-y-4">
23
+ <div>
24
+ <Label htmlFor="env_type" className="text-slate-300">
25
+ Environment Type (optional)
26
+ </Label>
27
+ <Select
28
+ value={config.env_type || "none"}
29
+ onValueChange={(value) =>
30
+ updateConfig(
31
+ "env_type",
32
+ value === "none" ? undefined : value
33
+ )
34
+ }
35
+ >
36
+ <SelectTrigger className="bg-slate-900 border-slate-600 text-white rounded-lg">
37
+ <SelectValue placeholder="Select environment type" />
38
+ </SelectTrigger>
39
+ <SelectContent className="bg-slate-800 border-slate-600">
40
+ <SelectItem value="none">None</SelectItem>
41
+ <SelectItem value="aloha">Aloha</SelectItem>
42
+ <SelectItem value="pusht">PushT</SelectItem>
43
+ <SelectItem value="xarm">XArm</SelectItem>
44
+ <SelectItem value="gym_manipulator">
45
+ Gym Manipulator
46
+ </SelectItem>
47
+ <SelectItem value="hil">HIL</SelectItem>
48
+ </SelectContent>
49
+ </Select>
50
+ </div>
51
+ <div>
52
+ <Label htmlFor="env_task" className="text-slate-300">
53
+ Environment Task (optional)
54
+ </Label>
55
+ <Input
56
+ id="env_task"
57
+ value={config.env_task || ""}
58
+ onChange={(e) =>
59
+ updateConfig("env_task", e.target.value || undefined)
60
+ }
61
+ placeholder="e.g., insertion_human"
62
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
63
+ />
64
+ </div>
65
+ <div className="grid grid-cols-2 gap-4">
66
+ <div>
67
+ <Label htmlFor="eval_n_episodes" className="text-slate-300">
68
+ Eval Episodes
69
+ </Label>
70
+ <Input
71
+ id="eval_n_episodes"
72
+ type="number"
73
+ value={config.eval_n_episodes}
74
+ onChange={(e) =>
75
+ updateConfig(
76
+ "eval_n_episodes",
77
+ parseInt(e.target.value)
78
+ )
79
+ }
80
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
81
+ />
82
+ </div>
83
+ <div>
84
+ <Label htmlFor="eval_batch_size" className="text-slate-300">
85
+ Eval Batch Size
86
+ </Label>
87
+ <Input
88
+ id="eval_batch_size"
89
+ type="number"
90
+ value={config.eval_batch_size}
91
+ onChange={(e) =>
92
+ updateConfig(
93
+ "eval_batch_size",
94
+ parseInt(e.target.value)
95
+ )
96
+ }
97
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
98
+ />
99
+ </div>
100
+ </div>
101
+ <div className="flex items-center space-x-3 pt-2">
102
+ <Switch
103
+ id="eval_use_async_envs"
104
+ checked={config.eval_use_async_envs}
105
+ onCheckedChange={(checked) =>
106
+ updateConfig("eval_use_async_envs", checked)
107
+ }
108
+ />
109
+ <Label
110
+ htmlFor="eval_use_async_envs"
111
+ className="text-slate-300"
112
+ >
113
+ Use Asynchronous Environments
114
+ </Label>
115
+ </div>
116
+ </CardContent>
117
+ </Card>
118
+ );
119
+ };
120
+
121
+ export default EnvEvalConfig;
src/components/training/config/LoggingConfig.tsx ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
4
+ import { Input } from '@/components/ui/input';
5
+ import { Label } from '@/components/ui/label';
6
+ import { Switch } from '@/components/ui/switch';
7
+ import { FileText } from 'lucide-react';
8
+ import { ConfigComponentProps } from '../types';
9
+
10
+ const LoggingConfig: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
11
+ return (
12
+ <Card className="bg-slate-800/50 border-slate-700 rounded-xl">
13
+ <CardHeader>
14
+ <CardTitle className="flex items-center gap-3 text-white">
15
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
16
+ <FileText className="w-5 h-5 text-sky-400" />
17
+ </div>
18
+ Logging & Checkpointing
19
+ </CardTitle>
20
+ </CardHeader>
21
+ <CardContent className="space-y-4">
22
+ <div className="grid grid-cols-3 gap-4">
23
+ <div>
24
+ <Label htmlFor="log_freq" className="text-slate-300">
25
+ Log Frequency
26
+ </Label>
27
+ <Input
28
+ id="log_freq"
29
+ type="number"
30
+ value={config.log_freq}
31
+ onChange={(e) =>
32
+ updateConfig("log_freq", parseInt(e.target.value))
33
+ }
34
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
35
+ />
36
+ </div>
37
+ <div>
38
+ <Label htmlFor="save_freq" className="text-slate-300">
39
+ Save Frequency
40
+ </Label>
41
+ <Input
42
+ id="save_freq"
43
+ type="number"
44
+ value={config.save_freq}
45
+ onChange={(e) =>
46
+ updateConfig("save_freq", parseInt(e.target.value))
47
+ }
48
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
49
+ />
50
+ </div>
51
+ <div>
52
+ <Label htmlFor="eval_freq" className="text-slate-300">
53
+ Eval Frequency
54
+ </Label>
55
+ <Input
56
+ id="eval_freq"
57
+ type="number"
58
+ value={config.eval_freq}
59
+ onChange={(e) =>
60
+ updateConfig("eval_freq", parseInt(e.target.value))
61
+ }
62
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
63
+ />
64
+ </div>
65
+ </div>
66
+ <div>
67
+ <Label htmlFor="output_dir" className="text-slate-300">
68
+ Output Directory
69
+ </Label>
70
+ <Input
71
+ id="output_dir"
72
+ value={config.output_dir}
73
+ onChange={(e) => updateConfig("output_dir", e.target.value)}
74
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
75
+ />
76
+ </div>
77
+ <div>
78
+ <Label htmlFor="job_name" className="text-slate-300">
79
+ Job Name (optional)
80
+ </Label>
81
+ <Input
82
+ id="job_name"
83
+ value={config.job_name || ""}
84
+ onChange={(e) =>
85
+ updateConfig("job_name", e.target.value || undefined)
86
+ }
87
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
88
+ />
89
+ </div>
90
+ <div className="flex items-center space-x-3 pt-2">
91
+ <Switch
92
+ id="save_checkpoint"
93
+ checked={config.save_checkpoint}
94
+ onCheckedChange={(checked) =>
95
+ updateConfig("save_checkpoint", checked)
96
+ }
97
+ />
98
+ <Label htmlFor="save_checkpoint" className="text-slate-300">
99
+ Save Checkpoints
100
+ </Label>
101
+ </div>
102
+ <div className="flex items-center space-x-3">
103
+ <Switch
104
+ id="resume"
105
+ checked={config.resume}
106
+ onCheckedChange={(checked) =>
107
+ updateConfig("resume", checked)
108
+ }
109
+ />
110
+ <Label htmlFor="resume" className="text-slate-300">
111
+ Resume from Checkpoint
112
+ </Label>
113
+ </div>
114
+ </CardContent>
115
+ </Card>
116
+ );
117
+ };
118
+
119
+ export default LoggingConfig;
src/components/training/config/OptimizerConfig.tsx ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
4
+ import { Input } from '@/components/ui/input';
5
+ import { Label } from '@/components/ui/label';
6
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
7
+ import { Settings } from 'lucide-react';
8
+ import { ConfigComponentProps } from '../types';
9
+
10
+ const OptimizerConfig: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
11
+ return (
12
+ <Card className="bg-slate-800/50 border-slate-700 rounded-xl">
13
+ <CardHeader>
14
+ <CardTitle className="flex items-center gap-3 text-white">
15
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
16
+ <Settings className="w-5 h-5 text-sky-400" />
17
+ </div>
18
+ Optimizer Configuration
19
+ </CardTitle>
20
+ </CardHeader>
21
+ <CardContent className="space-y-4">
22
+ <div>
23
+ <Label htmlFor="optimizer_type" className="text-slate-300">
24
+ Optimizer Type
25
+ </Label>
26
+ <Select
27
+ value={config.optimizer_type || "adam"}
28
+ onValueChange={(value) =>
29
+ updateConfig("optimizer_type", value)
30
+ }
31
+ >
32
+ <SelectTrigger className="bg-slate-900 border-slate-600 text-white rounded-lg">
33
+ <SelectValue />
34
+ </SelectTrigger>
35
+ <SelectContent className="bg-slate-800 border-slate-600">
36
+ <SelectItem value="adam">Adam</SelectItem>
37
+ <SelectItem value="adamw">AdamW</SelectItem>
38
+ <SelectItem value="sgd">SGD</SelectItem>
39
+ <SelectItem value="multi_adam">Multi Adam</SelectItem>
40
+ </SelectContent>
41
+ </Select>
42
+ </div>
43
+ <div className="grid grid-cols-3 gap-4">
44
+ <div>
45
+ <Label htmlFor="optimizer_lr" className="text-slate-300">
46
+ Learning Rate
47
+ </Label>
48
+ <Input
49
+ id="optimizer_lr"
50
+ type="number"
51
+ step="0.0001"
52
+ value={config.optimizer_lr || ""}
53
+ onChange={(e) =>
54
+ updateConfig(
55
+ "optimizer_lr",
56
+ e.target.value
57
+ ? parseFloat(e.target.value)
58
+ : undefined
59
+ )
60
+ }
61
+ placeholder="Use policy default"
62
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
63
+ />
64
+ </div>
65
+ <div>
66
+ <Label
67
+ htmlFor="optimizer_weight_decay"
68
+ className="text-slate-300"
69
+ >
70
+ Weight Decay
71
+ </Label>
72
+ <Input
73
+ id="optimizer_weight_decay"
74
+ type="number"
75
+ step="0.0001"
76
+ value={config.optimizer_weight_decay || ""}
77
+ onChange={(e) =>
78
+ updateConfig(
79
+ "optimizer_weight_decay",
80
+ e.target.value
81
+ ? parseFloat(e.target.value)
82
+ : undefined
83
+ )
84
+ }
85
+ placeholder="Use policy default"
86
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
87
+ />
88
+ </div>
89
+ <div>
90
+ <Label
91
+ htmlFor="optimizer_grad_clip_norm"
92
+ className="text-slate-300"
93
+ >
94
+ Gradient Clipping
95
+ </Label>
96
+ <Input
97
+ id="optimizer_grad_clip_norm"
98
+ type="number"
99
+ value={config.optimizer_grad_clip_norm || ""}
100
+ onChange={(e) =>
101
+ updateConfig(
102
+ "optimizer_grad_clip_norm",
103
+ e.target.value
104
+ ? parseFloat(e.target.value)
105
+ : undefined
106
+ )
107
+ }
108
+ placeholder="Use policy default"
109
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
110
+ />
111
+ </div>
112
+ </div>
113
+ </CardContent>
114
+ </Card>
115
+ );
116
+ };
117
+
118
+ export default OptimizerConfig;
src/components/training/config/PolicyConfig.tsx ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
4
+ import { Label } from '@/components/ui/label';
5
+ import { Switch } from '@/components/ui/switch';
6
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
7
+ import { Cpu } from 'lucide-react';
8
+ import { ConfigComponentProps } from '../types';
9
+
10
+ const PolicyConfig: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
11
+ return (
12
+ <Card className="bg-slate-800/50 border-slate-700 rounded-xl">
13
+ <CardHeader>
14
+ <CardTitle className="flex items-center gap-3 text-white">
15
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
16
+ <Cpu className="w-5 h-5 text-sky-400" />
17
+ </div>
18
+ Policy Configuration
19
+ </CardTitle>
20
+ </CardHeader>
21
+ <CardContent className="space-y-4">
22
+ <div>
23
+ <Label htmlFor="policy_type" className="text-slate-300">
24
+ Policy Type
25
+ </Label>
26
+ <Select
27
+ value={config.policy_type}
28
+ onValueChange={(value) =>
29
+ updateConfig("policy_type", value)
30
+ }
31
+ >
32
+ <SelectTrigger className="bg-slate-900 border-slate-600 text-white rounded-lg">
33
+ <SelectValue />
34
+ </SelectTrigger>
35
+ <SelectContent className="bg-slate-800 border-slate-600">
36
+ <SelectItem value="act">ACT (Action Chunking Transformer)</SelectItem>
37
+ <SelectItem value="diffusion">Diffusion Policy</SelectItem>
38
+ <SelectItem value="pi0">PI0</SelectItem>
39
+ <SelectItem value="smolvla">SmolVLA</SelectItem>
40
+ <SelectItem value="tdmpc">TD-MPC</SelectItem>
41
+ <SelectItem value="vqbet">VQ-BeT</SelectItem>
42
+ <SelectItem value="pi0fast">PI0 Fast</SelectItem>
43
+ <SelectItem value="sac">SAC</SelectItem>
44
+ <SelectItem value="reward_classifier">Reward Classifier</SelectItem>
45
+ </SelectContent>
46
+ </Select>
47
+ </div>
48
+ <div>
49
+ <Label htmlFor="policy_device" className="text-slate-300">
50
+ Device
51
+ </Label>
52
+ <Select
53
+ value={config.policy_device || "cuda"}
54
+ onValueChange={(value) =>
55
+ updateConfig("policy_device", value)
56
+ }
57
+ >
58
+ <SelectTrigger className="bg-slate-900 border-slate-600 text-white rounded-lg">
59
+ <SelectValue />
60
+ </SelectTrigger>
61
+ <SelectContent className="bg-slate-800 border-slate-600">
62
+ <SelectItem value="cuda">CUDA (GPU)</SelectItem>
63
+ <SelectItem value="cpu">CPU</SelectItem>
64
+ <SelectItem value="mps">MPS (Apple Silicon)</SelectItem>
65
+ </SelectContent>
66
+ </Select>
67
+ </div>
68
+ <div className="flex items-center space-x-3 pt-2">
69
+ <Switch
70
+ id="policy_use_amp"
71
+ checked={config.policy_use_amp}
72
+ onCheckedChange={(checked) =>
73
+ updateConfig("policy_use_amp", checked)
74
+ }
75
+ />
76
+ <Label htmlFor="policy_use_amp" className="text-slate-300">
77
+ Use Automatic Mixed Precision (AMP)
78
+ </Label>
79
+ </div>
80
+ </CardContent>
81
+ </Card>
82
+ );
83
+ };
84
+
85
+ export default PolicyConfig;
src/components/training/config/TrainingParams.tsx ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
4
+ import { Input } from '@/components/ui/input';
5
+ import { Label } from '@/components/ui/label';
6
+ import { TrendingUp } from 'lucide-react';
7
+ import { ConfigComponentProps } from '../types';
8
+
9
+ const TrainingParams: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
10
+ return (
11
+ <Card className="bg-slate-800/50 border-slate-700 rounded-xl">
12
+ <CardHeader>
13
+ <CardTitle className="flex items-center gap-3 text-white">
14
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
15
+ <TrendingUp className="w-5 h-5 text-sky-400" />
16
+ </div>
17
+ Training Parameters
18
+ </CardTitle>
19
+ </CardHeader>
20
+ <CardContent className="space-y-4">
21
+ <div className="grid grid-cols-2 gap-4">
22
+ <div>
23
+ <Label htmlFor="steps" className="text-slate-300">
24
+ Training Steps
25
+ </Label>
26
+ <Input
27
+ id="steps"
28
+ type="number"
29
+ value={config.steps}
30
+ onChange={(e) =>
31
+ updateConfig("steps", parseInt(e.target.value))
32
+ }
33
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
34
+ />
35
+ </div>
36
+ <div>
37
+ <Label htmlFor="batch_size" className="text-slate-300">
38
+ Batch Size
39
+ </Label>
40
+ <Input
41
+ id="batch_size"
42
+ type="number"
43
+ value={config.batch_size}
44
+ onChange={(e) =>
45
+ updateConfig("batch_size", parseInt(e.target.value))
46
+ }
47
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
48
+ />
49
+ </div>
50
+ </div>
51
+ <div className="grid grid-cols-2 gap-4">
52
+ <div>
53
+ <Label htmlFor="seed" className="text-slate-300">
54
+ Random Seed
55
+ </Label>
56
+ <Input
57
+ id="seed"
58
+ type="number"
59
+ value={config.seed || ""}
60
+ onChange={(e) =>
61
+ updateConfig(
62
+ "seed",
63
+ e.target.value ? parseInt(e.target.value) : undefined
64
+ )
65
+ }
66
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
67
+ />
68
+ </div>
69
+ <div>
70
+ <Label htmlFor="num_workers" className="text-slate-300">
71
+ Number of Workers
72
+ </Label>
73
+ <Input
74
+ id="num_workers"
75
+ type="number"
76
+ value={config.num_workers}
77
+ onChange={(e) =>
78
+ updateConfig("num_workers", parseInt(e.target.value))
79
+ }
80
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
81
+ />
82
+ </div>
83
+ </div>
84
+ </CardContent>
85
+ </Card>
86
+ );
87
+ };
88
+
89
+ export default TrainingParams;
src/components/training/config/WandbConfig.tsx ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
4
+ import { Input } from '@/components/ui/input';
5
+ import { Label } from '@/components/ui/label';
6
+ import { Switch } from '@/components/ui/switch';
7
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
8
+ import { TrendingUp } from 'lucide-react';
9
+ import { ConfigComponentProps } from '../types';
10
+
11
+ const WandbConfig: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
12
+ return (
13
+ <Card className="bg-slate-800/50 border-slate-700 rounded-xl">
14
+ <CardHeader>
15
+ <CardTitle className="flex items-center gap-3 text-white">
16
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
17
+ <TrendingUp className="w-5 h-5 text-sky-400" />
18
+ </div>
19
+ Weights & Biases
20
+ </CardTitle>
21
+ </CardHeader>
22
+ <CardContent className="space-y-4">
23
+ <div className="flex items-center space-x-3 pt-2">
24
+ <Switch
25
+ id="wandb_enable"
26
+ checked={config.wandb_enable}
27
+ onCheckedChange={(checked) =>
28
+ updateConfig("wandb_enable", checked)
29
+ }
30
+ />
31
+ <Label htmlFor="wandb_enable" className="text-slate-300">
32
+ Enable Weights & Biases Logging
33
+ </Label>
34
+ </div>
35
+ {config.wandb_enable && (
36
+ <>
37
+ <div>
38
+ <Label htmlFor="wandb_project" className="text-slate-300">
39
+ W&B Project Name
40
+ </Label>
41
+ <Input
42
+ id="wandb_project"
43
+ value={config.wandb_project || ""}
44
+ onChange={(e) =>
45
+ updateConfig(
46
+ "wandb_project",
47
+ e.target.value || undefined
48
+ )
49
+ }
50
+ placeholder="my-robotics-project"
51
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
52
+ />
53
+ </div>
54
+ <div>
55
+ <Label htmlFor="wandb_entity" className="text-slate-300">
56
+ W&B Entity (optional)
57
+ </Label>
58
+ <Input
59
+ id="wandb_entity"
60
+ value={config.wandb_entity || ""}
61
+ onChange={(e) =>
62
+ updateConfig(
63
+ "wandb_entity",
64
+ e.target.value || undefined
65
+ )
66
+ }
67
+ placeholder="your-username"
68
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
69
+ />
70
+ </div>
71
+ <div>
72
+ <Label htmlFor="wandb_notes" className="text-slate-300">
73
+ W&B Notes (optional)
74
+ </Label>
75
+ <Input
76
+ id="wandb_notes"
77
+ value={config.wandb_notes || ""}
78
+ onChange={(e) =>
79
+ updateConfig(
80
+ "wandb_notes",
81
+ e.target.value || undefined
82
+ )
83
+ }
84
+ placeholder="Training run notes..."
85
+ className="bg-slate-900 border-slate-600 text-white rounded-lg"
86
+ />
87
+ </div>
88
+ <div>
89
+ <Label htmlFor="wandb_mode" className="text-slate-300">
90
+ W&B Mode
91
+ </Label>
92
+ <Select
93
+ value={config.wandb_mode || "online"}
94
+ onValueChange={(value) =>
95
+ updateConfig("wandb_mode", value)
96
+ }
97
+ >
98
+ <SelectTrigger className="bg-slate-900 border-slate-600 text-white rounded-lg">
99
+ <SelectValue />
100
+ </SelectTrigger>
101
+ <SelectContent className="bg-slate-800 border-slate-600">
102
+ <SelectItem value="online">Online</SelectItem>
103
+ <SelectItem value="offline">Offline</SelectItem>
104
+ <SelectItem value="disabled">Disabled</SelectItem>
105
+ </SelectContent>
106
+ </Select>
107
+ </div>
108
+ <div className="flex items-center space-x-3 pt-2">
109
+ <Switch
110
+ id="wandb_disable_artifact"
111
+ checked={config.wandb_disable_artifact}
112
+ onCheckedChange={(checked) =>
113
+ updateConfig("wandb_disable_artifact", checked)
114
+ }
115
+ />
116
+ <Label
117
+ htmlFor="wandb_disable_artifact"
118
+ className="text-slate-300"
119
+ >
120
+ Disable Artifacts
121
+ </Label>
122
+ </div>
123
+ </>
124
+ )}
125
+ </CardContent>
126
+ </Card>
127
+ );
128
+ };
129
+
130
+ export default WandbConfig;
src/components/training/monitoring/MonitoringStats.tsx ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { Card, CardContent } from '@/components/ui/card';
4
+ import { Progress } from '@/components/ui/progress';
5
+ import { TrainingStatus } from '../types';
6
+ import { TrendingUp, CheckCircle, Activity, Clock } from 'lucide-react';
7
+
8
+ interface MonitoringStatsProps {
9
+ trainingStatus: TrainingStatus;
10
+ getProgressPercentage: () => number;
11
+ formatTime: (seconds: number) => string;
12
+ }
13
+
14
+ const MonitoringStats: React.FC<MonitoringStatsProps> = ({ trainingStatus, getProgressPercentage, formatTime }) => {
15
+ return (
16
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
17
+ <Card className="bg-slate-800/50 border-slate-700 rounded-xl">
18
+ <CardContent className="p-6">
19
+ <div className="flex items-center gap-4">
20
+ <div className="flex h-12 w-12 items-center justify-center rounded-lg bg-blue-500/20 text-blue-400">
21
+ <TrendingUp className="w-6 h-6" />
22
+ </div>
23
+ <div>
24
+ <h3 className="text-sm text-slate-400 mb-1">Progress</h3>
25
+ <div className="text-2xl font-bold text-white">
26
+ {trainingStatus.current_step} / {trainingStatus.total_steps}
27
+ </div>
28
+ </div>
29
+ </div>
30
+ <Progress value={getProgressPercentage()} className="mt-4" />
31
+ </CardContent>
32
+ </Card>
33
+
34
+ <Card className="bg-slate-800/50 border-slate-700 rounded-xl">
35
+ <CardContent className="p-6">
36
+ <div className="flex items-center gap-4">
37
+ <div className="flex h-12 w-12 items-center justify-center rounded-lg bg-green-500/20 text-green-400">
38
+ <CheckCircle className="w-6 h-6" />
39
+ </div>
40
+ <div>
41
+ <h3 className="text-sm text-slate-400 mb-1">Current Loss</h3>
42
+ <div className="text-2xl font-bold text-white">
43
+ {trainingStatus.current_loss?.toFixed(4) || "N/A"}
44
+ </div>
45
+ </div>
46
+ </div>
47
+ </CardContent>
48
+ </Card>
49
+
50
+ <Card className="bg-slate-800/50 border-slate-700 rounded-xl">
51
+ <CardContent className="p-6">
52
+ <div className="flex items-center gap-4">
53
+ <div className="flex h-12 w-12 items-center justify-center rounded-lg bg-orange-500/20 text-orange-400">
54
+ <Activity className="w-6 h-6" />
55
+ </div>
56
+ <div>
57
+ <h3 className="text-sm text-slate-400 mb-1">Learning Rate</h3>
58
+ <div className="text-2xl font-bold text-white">
59
+ {trainingStatus.current_lr?.toExponential(2) || "N/A"}
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </CardContent>
64
+ </Card>
65
+
66
+ <Card className="bg-slate-800/50 border-slate-700 rounded-xl">
67
+ <CardContent className="p-6">
68
+ <div className="flex items-center gap-4">
69
+ <div className="flex h-12 w-12 items-center justify-center rounded-lg bg-purple-500/20 text-purple-400">
70
+ <Clock className="w-6 h-6" />
71
+ </div>
72
+ <div>
73
+ <h3 className="text-sm text-slate-400 mb-1">ETA</h3>
74
+ <div className="text-2xl font-bold text-white">
75
+ {trainingStatus.eta_seconds ? formatTime(trainingStatus.eta_seconds) : "N/A"}
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </CardContent>
80
+ </Card>
81
+ </div>
82
+ );
83
+ };
84
+
85
+ export default MonitoringStats;
src/components/training/monitoring/TrainingLogs.tsx ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
4
+ import { FileText } from 'lucide-react';
5
+ import { LogEntry } from '../types';
6
+
7
+ interface TrainingLogsProps {
8
+ logs: LogEntry[];
9
+ logContainerRef: React.RefObject<HTMLDivElement>;
10
+ }
11
+
12
+ const TrainingLogs: React.FC<TrainingLogsProps> = ({ logs, logContainerRef }) => {
13
+ return (
14
+ <Card className="bg-slate-800/50 border-slate-700 rounded-xl">
15
+ <CardHeader>
16
+ <CardTitle className="flex items-center gap-3 text-white">
17
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
18
+ <FileText className="w-5 h-5 text-sky-400" />
19
+ </div>
20
+ Training Logs
21
+ </CardTitle>
22
+ </CardHeader>
23
+ <CardContent>
24
+ <div
25
+ ref={logContainerRef}
26
+ className="bg-slate-900 rounded-lg p-4 h-96 overflow-y-auto font-mono text-sm border border-slate-700"
27
+ >
28
+ {logs.length === 0 ? (
29
+ <div className="text-slate-500 text-center py-8">
30
+ No training logs yet. Start training to see output.
31
+ </div>
32
+ ) : (
33
+ logs.map((log, index) => (
34
+ <div key={index} className="flex items-start mb-1">
35
+ <span className="text-slate-500 mr-4">
36
+ {new Date(log.timestamp * 1000).toLocaleTimeString()}
37
+ </span>
38
+ <span className="flex-1 text-slate-300 break-words whitespace-pre-wrap">
39
+ {log.message}
40
+ </span>
41
+ </div>
42
+ ))
43
+ )}
44
+ </div>
45
+ </CardContent>
46
+ </Card>
47
+ );
48
+ };
49
+
50
+ export default TrainingLogs;
src/components/training/types.ts ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ export interface TrainingConfig {
3
+ // Dataset configuration
4
+ dataset_repo_id: string;
5
+ dataset_revision?: string;
6
+ dataset_root?: string;
7
+ dataset_episodes?: number[];
8
+
9
+ // Policy configuration
10
+ policy_type: string;
11
+
12
+ // Core training parameters
13
+ steps: number;
14
+ batch_size: number;
15
+ seed?: number;
16
+ num_workers: number;
17
+
18
+ // Logging and checkpointing
19
+ log_freq: number;
20
+ save_freq: number;
21
+ eval_freq: number;
22
+ save_checkpoint: boolean;
23
+
24
+ // Output configuration
25
+ output_dir: string;
26
+ resume: boolean;
27
+ job_name?: string;
28
+
29
+ // Weights & Biases
30
+ wandb_enable: boolean;
31
+ wandb_project?: string;
32
+ wandb_entity?: string;
33
+ wandb_notes?: string;
34
+ wandb_run_id?: string;
35
+ wandb_mode?: string;
36
+ wandb_disable_artifact: boolean;
37
+
38
+ // Environment and evaluation
39
+ env_type?: string;
40
+ env_task?: string;
41
+ eval_n_episodes: number;
42
+ eval_batch_size: number;
43
+ eval_use_async_envs: boolean;
44
+
45
+ // Policy-specific parameters
46
+ policy_device?: string;
47
+ policy_use_amp: boolean;
48
+
49
+ // Optimizer parameters
50
+ optimizer_type?: string;
51
+ optimizer_lr?: number;
52
+ optimizer_weight_decay?: number;
53
+ optimizer_grad_clip_norm?: number;
54
+
55
+ // Advanced configuration
56
+ use_policy_training_preset: boolean;
57
+ config_path?: string;
58
+ }
59
+
60
+ export interface TrainingStatus {
61
+ training_active: boolean;
62
+ current_step: number;
63
+ total_steps: number;
64
+ current_loss?: number;
65
+ current_lr?: number;
66
+ grad_norm?: number;
67
+ epoch_time?: number;
68
+ eta_seconds?: number;
69
+ available_controls: {
70
+ stop_training: boolean;
71
+ pause_training: boolean;
72
+ resume_training: boolean;
73
+ };
74
+ }
75
+
76
+ export interface LogEntry {
77
+ timestamp: number;
78
+ message: string;
79
+ }
80
+
81
+ export interface ConfigComponentProps {
82
+ config: TrainingConfig;
83
+ updateConfig: <T extends keyof TrainingConfig>(
84
+ key: T,
85
+ value: TrainingConfig[T]
86
+ ) => void;
87
+ }
src/contexts/ThemeContext.tsx ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import { createContext, ReactNode, useState, useEffect } from "react";
3
+
4
+ export type Theme = "dark" | "light" | "system";
5
+
6
+ export interface ThemeProviderState {
7
+ theme: Theme;
8
+ setTheme: (theme: Theme) => void;
9
+ }
10
+
11
+ const initialState: ThemeProviderState = {
12
+ theme: "system",
13
+ setTheme: () => null,
14
+ };
15
+
16
+ export const ThemeProviderContext =
17
+ createContext<ThemeProviderState>(initialState);
18
+
19
+ interface ThemeProviderProps {
20
+ children: ReactNode;
21
+ defaultTheme?: Theme;
22
+ storageKey?: string;
23
+ }
24
+
25
+ export function ThemeProvider({
26
+ children,
27
+ defaultTheme = "system",
28
+ storageKey = "vite-ui-theme",
29
+ ...props
30
+ }: ThemeProviderProps) {
31
+ const [theme, setTheme] = useState<Theme>(
32
+ () => (localStorage.getItem(storageKey) as Theme) || defaultTheme
33
+ );
34
+
35
+ useEffect(() => {
36
+ const root = window.document.documentElement;
37
+ root.classList.remove("light", "dark");
38
+
39
+ if (theme === "system") {
40
+ const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
41
+ .matches
42
+ ? "dark"
43
+ : "light";
44
+ root.classList.add(systemTheme);
45
+ return;
46
+ }
47
+
48
+ root.classList.add(theme);
49
+ }, [theme]);
50
+
51
+ const value = {
52
+ theme,
53
+ setTheme: (newTheme: Theme) => {
54
+ localStorage.setItem(storageKey, newTheme);
55
+ setTheme(newTheme);
56
+ },
57
+ };
58
+
59
+ return (
60
+ <ThemeProviderContext.Provider {...props} value={value}>
61
+ {children}
62
+ </ThemeProviderContext.Provider>
63
+ );
64
+ }
src/pages/EditDataset.tsx ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+
4
+ const EditDataset = () => {
5
+ return (
6
+ <div className="min-h-screen bg-black text-white flex flex-col items-center justify-center p-4">
7
+ <h1 className="text-5xl font-bold tracking-tight">Edit Dataset</h1>
8
+ <p className="mt-4 text-xl text-gray-400">
9
+ This page is under construction.
10
+ </p>
11
+ </div>
12
+ );
13
+ };
14
+
15
+ export default EditDataset;