NERDDISCO commited on
Commit
a8d792f
·
1 Parent(s): 009de70

feat: added cyberpunk example

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. examples/cyberpunk-standalone/.gitignore +24 -0
  2. examples/cyberpunk-standalone/README.md +69 -0
  3. examples/cyberpunk-standalone/eslint.config.js +23 -0
  4. examples/cyberpunk-standalone/index.html +36 -0
  5. examples/cyberpunk-standalone/package.json +78 -0
  6. examples/cyberpunk-standalone/pnpm-lock.yaml +0 -0
  7. examples/cyberpunk-standalone/postcss.config.mjs +8 -0
  8. examples/cyberpunk-standalone/public/fonts/Geist-Bold.woff2 +0 -0
  9. examples/cyberpunk-standalone/public/fonts/Geist-Regular.woff2 +0 -0
  10. examples/cyberpunk-standalone/public/fonts/Geist-SemiBold.woff2 +0 -0
  11. examples/cyberpunk-standalone/public/fonts/GeistMono-Bold.woff2 +0 -0
  12. examples/cyberpunk-standalone/public/fonts/GeistMono-Regular.woff2 +0 -0
  13. examples/cyberpunk-standalone/public/fonts/GeistMono-SemiBold.woff2 +0 -0
  14. examples/cyberpunk-standalone/public/partabot-logo.png +0 -0
  15. examples/cyberpunk-standalone/src/App.css +42 -0
  16. examples/cyberpunk-standalone/src/App.tsx +222 -0
  17. examples/cyberpunk-standalone/src/components/VirtualKey.tsx +27 -0
  18. examples/cyberpunk-standalone/src/components/boot-sequence.tsx +61 -0
  19. examples/cyberpunk-standalone/src/components/calibration-view.tsx +108 -0
  20. examples/cyberpunk-standalone/src/components/device-dashboard.tsx +174 -0
  21. examples/cyberpunk-standalone/src/components/docs-section.tsx +150 -0
  22. examples/cyberpunk-standalone/src/components/edit-robot-dialog.tsx +78 -0
  23. examples/cyberpunk-standalone/src/components/footer.tsx +41 -0
  24. examples/cyberpunk-standalone/src/components/grid-background.tsx +22 -0
  25. examples/cyberpunk-standalone/src/components/hardware-support-section.tsx +140 -0
  26. examples/cyberpunk-standalone/src/components/header.tsx +27 -0
  27. examples/cyberpunk-standalone/src/components/hud-corners.tsx +46 -0
  28. examples/cyberpunk-standalone/src/components/motor-calibration-visual.tsx +32 -0
  29. examples/cyberpunk-standalone/src/components/roadmap-section.tsx +179 -0
  30. examples/cyberpunk-standalone/src/components/setup-cards.tsx +160 -0
  31. examples/cyberpunk-standalone/src/components/teleoperation-view.tsx +240 -0
  32. examples/cyberpunk-standalone/src/components/theme-provider.tsx +8 -0
  33. examples/cyberpunk-standalone/src/components/theme-toggle.tsx +39 -0
  34. examples/cyberpunk-standalone/src/components/ui/accordion.tsx +58 -0
  35. examples/cyberpunk-standalone/src/components/ui/alert-dialog.tsx +141 -0
  36. examples/cyberpunk-standalone/src/components/ui/alert.tsx +59 -0
  37. examples/cyberpunk-standalone/src/components/ui/aspect-ratio.tsx +7 -0
  38. examples/cyberpunk-standalone/src/components/ui/avatar.tsx +50 -0
  39. examples/cyberpunk-standalone/src/components/ui/badge.tsx +36 -0
  40. examples/cyberpunk-standalone/src/components/ui/breadcrumb.tsx +115 -0
  41. examples/cyberpunk-standalone/src/components/ui/button.tsx +56 -0
  42. examples/cyberpunk-standalone/src/components/ui/calendar.tsx +66 -0
  43. examples/cyberpunk-standalone/src/components/ui/card.tsx +48 -0
  44. examples/cyberpunk-standalone/src/components/ui/carousel.tsx +262 -0
  45. examples/cyberpunk-standalone/src/components/ui/chart.tsx +365 -0
  46. examples/cyberpunk-standalone/src/components/ui/checkbox.tsx +30 -0
  47. examples/cyberpunk-standalone/src/components/ui/collapsible.tsx +11 -0
  48. examples/cyberpunk-standalone/src/components/ui/command.tsx +153 -0
  49. examples/cyberpunk-standalone/src/components/ui/context-menu.tsx +200 -0
  50. examples/cyberpunk-standalone/src/components/ui/dialog.tsx +122 -0
examples/cyberpunk-standalone/.gitignore ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/extensions.json
18
+ .idea
19
+ .DS_Store
20
+ *.suo
21
+ *.ntvs*
22
+ *.njsproj
23
+ *.sln
24
+ *.sw?
examples/cyberpunk-standalone/README.md ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # React + TypeScript + Vite
2
+
3
+ This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4
+
5
+ Currently, two official plugins are available:
6
+
7
+ - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
8
+ - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9
+
10
+ ## Expanding the ESLint configuration
11
+
12
+ If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
13
+
14
+ ```js
15
+ export default tseslint.config([
16
+ globalIgnores(['dist']),
17
+ {
18
+ files: ['**/*.{ts,tsx}'],
19
+ extends: [
20
+ // Other configs...
21
+
22
+ // Remove tseslint.configs.recommended and replace with this
23
+ ...tseslint.configs.recommendedTypeChecked,
24
+ // Alternatively, use this for stricter rules
25
+ ...tseslint.configs.strictTypeChecked,
26
+ // Optionally, add this for stylistic rules
27
+ ...tseslint.configs.stylisticTypeChecked,
28
+
29
+ // Other configs...
30
+ ],
31
+ languageOptions: {
32
+ parserOptions: {
33
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
34
+ tsconfigRootDir: import.meta.dirname,
35
+ },
36
+ // other options...
37
+ },
38
+ },
39
+ ])
40
+ ```
41
+
42
+ You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
43
+
44
+ ```js
45
+ // eslint.config.js
46
+ import reactX from 'eslint-plugin-react-x'
47
+ import reactDom from 'eslint-plugin-react-dom'
48
+
49
+ export default tseslint.config([
50
+ globalIgnores(['dist']),
51
+ {
52
+ files: ['**/*.{ts,tsx}'],
53
+ extends: [
54
+ // Other configs...
55
+ // Enable lint rules for React
56
+ reactX.configs['recommended-typescript'],
57
+ // Enable lint rules for React DOM
58
+ reactDom.configs.recommended,
59
+ ],
60
+ languageOptions: {
61
+ parserOptions: {
62
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
63
+ tsconfigRootDir: import.meta.dirname,
64
+ },
65
+ // other options...
66
+ },
67
+ },
68
+ ])
69
+ ```
examples/cyberpunk-standalone/eslint.config.js ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import js from '@eslint/js'
2
+ import globals from 'globals'
3
+ import reactHooks from 'eslint-plugin-react-hooks'
4
+ import reactRefresh from 'eslint-plugin-react-refresh'
5
+ import tseslint from 'typescript-eslint'
6
+ import { globalIgnores } from 'eslint/config'
7
+
8
+ export default tseslint.config([
9
+ globalIgnores(['dist']),
10
+ {
11
+ files: ['**/*.{ts,tsx}'],
12
+ extends: [
13
+ js.configs.recommended,
14
+ tseslint.configs.recommended,
15
+ reactHooks.configs['recommended-latest'],
16
+ reactRefresh.configs.vite,
17
+ ],
18
+ languageOptions: {
19
+ ecmaVersion: 2020,
20
+ globals: globals.browser,
21
+ },
22
+ },
23
+ ])
examples/cyberpunk-standalone/index.html ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Cyberpunk Standalone</title>
8
+
9
+ <!-- Preload critical fonts -->
10
+ <link
11
+ rel="preload"
12
+ href="/fonts/Geist-Regular.woff2"
13
+ as="font"
14
+ type="font/woff2"
15
+ crossorigin
16
+ />
17
+ <link
18
+ rel="preload"
19
+ href="/fonts/Geist-SemiBold.woff2"
20
+ as="font"
21
+ type="font/woff2"
22
+ crossorigin
23
+ />
24
+ <link
25
+ rel="preload"
26
+ href="/fonts/GeistMono-Regular.woff2"
27
+ as="font"
28
+ type="font/woff2"
29
+ crossorigin
30
+ />
31
+ </head>
32
+ <body>
33
+ <div id="root"></div>
34
+ <script type="module" src="/src/main.tsx"></script>
35
+ </body>
36
+ </html>
examples/cyberpunk-standalone/package.json ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "cyberpunk-standalone",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc -b && vite build",
9
+ "lint": "eslint .",
10
+ "preview": "vite preview"
11
+ },
12
+ "dependencies": {
13
+ "@hookform/resolvers": "^3.9.1",
14
+ "@radix-ui/react-accordion": "1.2.2",
15
+ "@radix-ui/react-alert-dialog": "1.1.4",
16
+ "@radix-ui/react-aspect-ratio": "1.1.1",
17
+ "@radix-ui/react-avatar": "1.1.2",
18
+ "@radix-ui/react-checkbox": "1.1.3",
19
+ "@radix-ui/react-collapsible": "1.1.2",
20
+ "@radix-ui/react-context-menu": "2.2.4",
21
+ "@radix-ui/react-dialog": "1.1.4",
22
+ "@radix-ui/react-dropdown-menu": "2.1.4",
23
+ "@radix-ui/react-hover-card": "1.1.4",
24
+ "@radix-ui/react-label": "2.1.1",
25
+ "@radix-ui/react-menubar": "1.1.4",
26
+ "@radix-ui/react-navigation-menu": "1.2.3",
27
+ "@radix-ui/react-popover": "1.1.4",
28
+ "@radix-ui/react-progress": "1.1.1",
29
+ "@radix-ui/react-radio-group": "1.2.2",
30
+ "@radix-ui/react-scroll-area": "1.2.2",
31
+ "@radix-ui/react-select": "2.1.4",
32
+ "@radix-ui/react-separator": "1.1.1",
33
+ "@radix-ui/react-slider": "latest",
34
+ "@radix-ui/react-slot": "1.1.1",
35
+ "@radix-ui/react-switch": "1.1.2",
36
+ "@radix-ui/react-tabs": "1.1.2",
37
+ "@radix-ui/react-toast": "1.2.4",
38
+ "@radix-ui/react-toggle": "1.1.1",
39
+ "@radix-ui/react-toggle-group": "1.1.1",
40
+ "@radix-ui/react-tooltip": "1.1.6",
41
+ "autoprefixer": "^10.4.20",
42
+ "class-variance-authority": "^0.7.1",
43
+ "clsx": "^2.1.1",
44
+ "cmdk": "1.0.4",
45
+ "date-fns": "4.1.0",
46
+ "embla-carousel-react": "8.5.1",
47
+ "input-otp": "1.4.1",
48
+ "lucide-react": "^0.454.0",
49
+ "next-themes": "^0.4.6",
50
+ "react": "^19",
51
+ "react-day-picker": "8.10.1",
52
+ "react-dom": "^19",
53
+ "react-hook-form": "^7.54.1",
54
+ "react-resizable-panels": "^2.1.7",
55
+ "recharts": "2.15.0",
56
+ "sonner": "^1.7.1",
57
+ "tailwind-merge": "^2.5.5",
58
+ "tailwindcss-animate": "^1.0.7",
59
+ "vaul": "^0.9.6",
60
+ "zod": "^3.24.1"
61
+ },
62
+ "devDependencies": {
63
+ "@eslint/js": "^9.29.0",
64
+ "@types/node": "^22",
65
+ "@types/react": "^19.1.8",
66
+ "@types/react-dom": "^19.1.6",
67
+ "@vitejs/plugin-react": "^4.5.2",
68
+ "eslint": "^9.29.0",
69
+ "eslint-plugin-react-hooks": "^5.2.0",
70
+ "eslint-plugin-react-refresh": "^0.4.20",
71
+ "globals": "^16.2.0",
72
+ "postcss": "^8.5",
73
+ "tailwindcss": "^3.4.17",
74
+ "typescript": "^5",
75
+ "typescript-eslint": "^8.34.1",
76
+ "vite": "^7.0.0"
77
+ }
78
+ }
examples/cyberpunk-standalone/pnpm-lock.yaml ADDED
The diff for this file is too large to render. See raw diff
 
examples/cyberpunk-standalone/postcss.config.mjs ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ tailwindcss: {},
5
+ },
6
+ };
7
+
8
+ export default config;
examples/cyberpunk-standalone/public/fonts/Geist-Bold.woff2 ADDED
Binary file (42.3 kB). View file
 
examples/cyberpunk-standalone/public/fonts/Geist-Regular.woff2 ADDED
Binary file (41.2 kB). View file
 
examples/cyberpunk-standalone/public/fonts/Geist-SemiBold.woff2 ADDED
Binary file (42 kB). View file
 
examples/cyberpunk-standalone/public/fonts/GeistMono-Bold.woff2 ADDED
Binary file (48 kB). View file
 
examples/cyberpunk-standalone/public/fonts/GeistMono-Regular.woff2 ADDED
Binary file (47 kB). View file
 
examples/cyberpunk-standalone/public/fonts/GeistMono-SemiBold.woff2 ADDED
Binary file (48 kB). View file
 
examples/cyberpunk-standalone/public/partabot-logo.png ADDED
examples/cyberpunk-standalone/src/App.css ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #root {
2
+ max-width: 1280px;
3
+ margin: 0 auto;
4
+ padding: 2rem;
5
+ text-align: center;
6
+ }
7
+
8
+ .logo {
9
+ height: 6em;
10
+ padding: 1.5em;
11
+ will-change: filter;
12
+ transition: filter 300ms;
13
+ }
14
+ .logo:hover {
15
+ filter: drop-shadow(0 0 2em #646cffaa);
16
+ }
17
+ .logo.react:hover {
18
+ filter: drop-shadow(0 0 2em #61dafbaa);
19
+ }
20
+
21
+ @keyframes logo-spin {
22
+ from {
23
+ transform: rotate(0deg);
24
+ }
25
+ to {
26
+ transform: rotate(360deg);
27
+ }
28
+ }
29
+
30
+ @media (prefers-reduced-motion: no-preference) {
31
+ a:nth-of-type(2) .logo {
32
+ animation: logo-spin infinite 20s linear;
33
+ }
34
+ }
35
+
36
+ .card {
37
+ padding: 2em;
38
+ }
39
+
40
+ .read-the-docs {
41
+ color: #888;
42
+ }
examples/cyberpunk-standalone/src/App.tsx ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, useEffect, useRef } from "react";
2
+ import { ChevronLeft } from "lucide-react";
3
+ import { Header } from "@/components/header";
4
+ import { Footer } from "@/components/footer";
5
+ import { EditRobotDialog } from "@/components/edit-robot-dialog";
6
+ import { DeviceDashboard } from "@/components/device-dashboard";
7
+ import { CalibrationView } from "@/components/calibration-view";
8
+ import { TeleoperationView } from "@/components/teleoperation-view";
9
+ import { SetupCards } from "@/components/setup-cards";
10
+ import { DocsSection } from "@/components/docs-section";
11
+ import { RoadmapSection } from "@/components/roadmap-section";
12
+ import { HardwareSupportSection } from "@/components/hardware-support-section";
13
+ import { lerobot } from "@/lib/mock-api";
14
+ import { useLocalStorage } from "@/hooks/use-local-storage";
15
+ import type { RobotConnection } from "@/types/robot";
16
+
17
+ function App() {
18
+ const [view, setView] = useState<
19
+ "dashboard" | "calibrating" | "teleoperating"
20
+ >("dashboard");
21
+ const [robots, setRobots] = useLocalStorage<RobotConnection[]>(
22
+ "connected-robots",
23
+ []
24
+ );
25
+ const [selectedRobot, setSelectedRobot] = useState<RobotConnection | null>(
26
+ null
27
+ );
28
+ const [editingRobot, setEditingRobot] = useState<RobotConnection | null>(
29
+ null
30
+ );
31
+ const [logs, setLogs] = useState<string[]>([]);
32
+ const [isConnecting, setIsConnecting] = useState(false);
33
+ const hardwareSectionRef = useRef<HTMLDivElement>(null);
34
+
35
+ useEffect(() => {
36
+ const loadSavedRobots = async () => {
37
+ const robotConfigs = robots.map(({ port, ...config }) => config);
38
+ if (robotConfigs.length > 0) {
39
+ const findPortProcess = await lerobot.findPort({
40
+ robotConfigs,
41
+ onMessage: (msg) => setLogs((prev) => [...prev, msg]),
42
+ });
43
+ const reconnectedRobots = await findPortProcess.result;
44
+ setRobots(reconnectedRobots);
45
+ }
46
+ setIsConnecting(false);
47
+ };
48
+ loadSavedRobots();
49
+ }, []);
50
+
51
+ const handleFindNewRobots = async () => {
52
+ setIsConnecting(true);
53
+ const findPortProcess = await lerobot.findPort({
54
+ onMessage: (msg) => setLogs((prev) => [...prev, msg]),
55
+ });
56
+ const newRobots = await findPortProcess.result;
57
+ setRobots((prev) => {
58
+ const existingIds = new Set(prev.map((r) => r.robotId));
59
+ const uniqueNewRobots = newRobots.filter(
60
+ (r) => !existingIds.has(r.robotId)
61
+ );
62
+ return [...prev, ...uniqueNewRobots];
63
+ });
64
+ if (newRobots.length > 0) {
65
+ setEditingRobot(newRobots[0]);
66
+ }
67
+ setIsConnecting(false);
68
+ };
69
+
70
+ const handleUpdateRobot = (updatedRobot: RobotConnection) => {
71
+ setRobots((prev) =>
72
+ prev.map((r) => (r.robotId === updatedRobot.robotId ? updatedRobot : r))
73
+ );
74
+ setEditingRobot(null);
75
+ };
76
+
77
+ const handleRemoveRobot = (robotId: string) => {
78
+ setRobots((prev) => prev.filter((r) => r.robotId !== robotId));
79
+ };
80
+
81
+ const handleCalibrate = (robot: RobotConnection) => {
82
+ setSelectedRobot(robot);
83
+ setView("calibrating");
84
+ };
85
+
86
+ const handleTeleoperate = (robot: RobotConnection) => {
87
+ setSelectedRobot(robot);
88
+ setView("teleoperating");
89
+ };
90
+
91
+ const handleCloseSubView = () => {
92
+ setSelectedRobot(null);
93
+ setView("dashboard");
94
+ };
95
+
96
+ const scrollToHardware = () => {
97
+ hardwareSectionRef.current?.scrollIntoView({ behavior: "smooth" });
98
+ };
99
+
100
+ const renderView = () => {
101
+ switch (view) {
102
+ case "calibrating":
103
+ return selectedRobot && <CalibrationView robot={selectedRobot} />;
104
+ case "teleoperating":
105
+ return selectedRobot && <TeleoperationView robot={selectedRobot} />;
106
+ case "dashboard":
107
+ default:
108
+ return (
109
+ <div className="space-y-12">
110
+ <DeviceDashboard
111
+ robots={robots}
112
+ onCalibrate={handleCalibrate}
113
+ onTeleoperate={handleTeleoperate}
114
+ onRemove={handleRemoveRobot}
115
+ onEdit={setEditingRobot}
116
+ onFindNew={handleFindNewRobots}
117
+ isConnecting={isConnecting}
118
+ onScrollToHardware={scrollToHardware}
119
+ />
120
+ <div>
121
+ <div className="mb-6">
122
+ <h2 className="text-2xl font-bold text-primary font-mono tracking-wider mb-2 uppercase">
123
+ install
124
+ </h2>
125
+ <p className="text-sm text-muted-foreground font-mono">
126
+ Choose your preferred development environment
127
+ </p>
128
+ </div>
129
+ <SetupCards />
130
+ </div>
131
+ <DocsSection />
132
+ <RoadmapSection />
133
+ <div ref={hardwareSectionRef}>
134
+ <HardwareSupportSection />
135
+ </div>
136
+ </div>
137
+ );
138
+ }
139
+ };
140
+
141
+ const PageHeader = () => {
142
+ let title = "DASHBOARD";
143
+ if (view === "calibrating" && selectedRobot) {
144
+ title = `CALIBRATE: ${selectedRobot.name.toUpperCase()}`;
145
+ } else if (view === "teleoperating" && selectedRobot) {
146
+ title = `TELEOPERATE: ${selectedRobot.name.toUpperCase()}`;
147
+ }
148
+
149
+ return (
150
+ <div className="flex items-center justify-between mb-12">
151
+ <div className="flex items-center gap-4">
152
+ <div>
153
+ {view === "calibrating" && selectedRobot ? (
154
+ <h1 className="font-mono text-4xl font-bold tracking-wider">
155
+ <span className="text-muted-foreground uppercase">
156
+ calibrate:
157
+ </span>{" "}
158
+ <span
159
+ className="text-primary text-glitch uppercase"
160
+ data-text={selectedRobot.name}
161
+ >
162
+ {selectedRobot.name.toUpperCase()}
163
+ </span>
164
+ </h1>
165
+ ) : view === "teleoperating" && selectedRobot ? (
166
+ <h1 className="font-mono text-4xl font-bold tracking-wider">
167
+ <span className="text-muted-foreground uppercase">
168
+ teleoperate:
169
+ </span>{" "}
170
+ <span
171
+ className="text-primary text-glitch uppercase"
172
+ data-text={selectedRobot.name}
173
+ >
174
+ {selectedRobot.name.toUpperCase()}
175
+ </span>
176
+ </h1>
177
+ ) : (
178
+ <h1
179
+ className="font-mono text-4xl font-bold text-primary tracking-wider text-glitch uppercase"
180
+ data-text="dashboard"
181
+ >
182
+ DASHBOARD
183
+ </h1>
184
+ )}
185
+ <div className="h-6 flex items-center">
186
+ {view !== "dashboard" ? (
187
+ <button
188
+ onClick={handleCloseSubView}
189
+ className="flex items-center gap-2 text-sm text-muted-foreground font-mono hover:text-primary transition-colors"
190
+ >
191
+ <ChevronLeft className="w-4 h-4" />
192
+ <span className="uppercase">back to dashboard</span>
193
+ </button>
194
+ ) : (
195
+ <p className="text-sm text-muted-foreground font-mono">{""} </p>
196
+ )}
197
+ </div>
198
+ </div>
199
+ </div>
200
+ </div>
201
+ );
202
+ };
203
+
204
+ return (
205
+ <div className="flex flex-col min-h-screen font-sans scanline-overlay">
206
+ <Header />
207
+ <main className="flex-grow container mx-auto py-12 px-4 md:px-6">
208
+ <PageHeader />
209
+ {renderView()}
210
+ <EditRobotDialog
211
+ robot={editingRobot}
212
+ isOpen={!!editingRobot}
213
+ onOpenChange={(open) => !open && setEditingRobot(null)}
214
+ onSave={handleUpdateRobot}
215
+ />
216
+ </main>
217
+ <Footer />
218
+ </div>
219
+ );
220
+ }
221
+
222
+ export default App;
examples/cyberpunk-standalone/src/components/VirtualKey.tsx ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { cn } from "@/lib/utils"
2
+
3
+ interface VirtualKeyProps {
4
+ label: string
5
+ subLabel?: string
6
+ isPressed?: boolean
7
+ }
8
+
9
+ const VirtualKey = ({ label, subLabel, isPressed }: VirtualKeyProps) => {
10
+ return (
11
+ <div className="flex flex-col items-center">
12
+ <div
13
+ className={cn(
14
+ "w-12 h-12 border rounded-md flex items-center justify-center font-bold transition-all duration-100",
15
+ isPressed
16
+ ? "bg-primary text-primary-foreground scale-110 border-primary"
17
+ : "bg-black/30 text-muted-foreground border-white/10",
18
+ )}
19
+ >
20
+ {label}
21
+ </div>
22
+ {subLabel && <span className="text-xs text-muted-foreground mt-1 font-mono">{subLabel}</span>}
23
+ </div>
24
+ )
25
+ }
26
+
27
+ export default VirtualKey
examples/cyberpunk-standalone/src/components/boot-sequence.tsx ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import { useState, useEffect } from "react"
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const bootLines = [
7
+ "INITIALIZING BIOS...",
8
+ "CHECKING MEMORY: 1024MB OK",
9
+ "DETECTING PRIMARY BUS...",
10
+ "LEROBOT.JS CORE v1.0.2",
11
+ "LOADING OPERATOR CONSOLE...",
12
+ "CONNECTION PROTOCOL: WEBSERIAL",
13
+ "ENCRYPTION: AES-256",
14
+ "SYSTEM STATUS: NOMINAL",
15
+ "BOOT SEQUENCE COMPLETE.",
16
+ ]
17
+
18
+ export function BootSequence({ onComplete }: { onComplete: () => void }) {
19
+ const [visibleLines, setVisibleLines] = useState<string[]>([])
20
+ const [show, setShow] = useState(true)
21
+
22
+ useEffect(() => {
23
+ const timeouts: NodeJS.Timeout[] = []
24
+ bootLines.forEach((line, index) => {
25
+ timeouts.push(
26
+ setTimeout(() => {
27
+ setVisibleLines((prev) => [...prev, line])
28
+ }, index * 150),
29
+ )
30
+ })
31
+
32
+ timeouts.push(
33
+ setTimeout(
34
+ () => {
35
+ setShow(false)
36
+ setTimeout(onComplete, 500) // Wait for fade out
37
+ },
38
+ bootLines.length * 150 + 500,
39
+ ),
40
+ )
41
+
42
+ return () => timeouts.forEach(clearTimeout)
43
+ }, [onComplete])
44
+
45
+ return (
46
+ <div
47
+ className={cn(
48
+ "fixed inset-0 z-50 flex items-center justify-center bg-black transition-opacity duration-500",
49
+ show ? "opacity-100" : "opacity-0 pointer-events-none",
50
+ )}
51
+ >
52
+ <div className="font-mono text-primary text-sm md:text-base p-4">
53
+ {visibleLines.map((line, index) => (
54
+ <p key={index} className="text-glitch" data-text={line}>
55
+ &gt; {line}
56
+ </p>
57
+ ))}
58
+ </div>
59
+ </div>
60
+ )
61
+ }
examples/cyberpunk-standalone/src/components/calibration-view.tsx ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+ import { useState, useMemo } from "react"
3
+ import { Download } from "lucide-react"
4
+ import { Button } from "@/components/ui/button"
5
+ import { Card } from "@/components/ui/card"
6
+ import { lerobot } from "@/lib/mock-api"
7
+ import { useLocalStorage } from "@/hooks/use-local-storage"
8
+ import { MotorCalibrationVisual } from "@/components/motor-calibration-visual"
9
+ import type { RobotConnection, LiveCalibrationData, WebCalibrationResults } from "@/types/robot"
10
+
11
+ interface CalibrationViewProps {
12
+ robot: RobotConnection
13
+ }
14
+
15
+ export function CalibrationView({ robot }: CalibrationViewProps) {
16
+ const [status, setStatus] = useState("Ready to calibrate.")
17
+ const [liveData, setLiveData] = useState<LiveCalibrationData | null>(null)
18
+ const [isCalibrating, setIsCalibrating] = useState(false)
19
+ const [calibrationProcess, setCalibrationProcess] = useState<{
20
+ stop: () => void
21
+ result: Promise<WebCalibrationResults>
22
+ } | null>(null)
23
+ const [calibrationResults, setCalibrationResults] = useLocalStorage<WebCalibrationResults | null>(
24
+ `calibration-${robot.robotId}`,
25
+ null,
26
+ )
27
+
28
+ const handleStart = async () => {
29
+ setIsCalibrating(true)
30
+ const process = await lerobot.calibrate(robot, {
31
+ onLiveUpdate: setLiveData,
32
+ onProgress: setStatus,
33
+ })
34
+ setCalibrationProcess(process)
35
+ }
36
+
37
+ const handleFinish = async () => {
38
+ if (calibrationProcess) {
39
+ calibrationProcess.stop()
40
+ const results = await calibrationProcess.result
41
+ setCalibrationResults(results)
42
+ setIsCalibrating(false)
43
+ setCalibrationProcess(null)
44
+ }
45
+ }
46
+
47
+ const downloadJson = () => {
48
+ if (!calibrationResults) return
49
+ const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(calibrationResults, null, 2))
50
+ const downloadAnchorNode = document.createElement("a")
51
+ downloadAnchorNode.setAttribute("href", dataStr)
52
+ downloadAnchorNode.setAttribute("download", `${robot.robotId}_calibration.json`)
53
+ document.body.appendChild(downloadAnchorNode)
54
+ downloadAnchorNode.click()
55
+ downloadAnchorNode.remove()
56
+ }
57
+
58
+ const motorData = useMemo(
59
+ () =>
60
+ liveData
61
+ ? Object.entries(liveData)
62
+ : lerobot.MOCK_MOTOR_NAMES.map((name) => [name, { current: 0, min: 4095, max: 0, range: 0 }]),
63
+ [liveData],
64
+ )
65
+
66
+ return (
67
+ <Card className="border-0 rounded-none">
68
+ <div className="p-4 border-b border-white/10 flex items-center justify-between">
69
+ <div className="flex items-center gap-4">
70
+ <div className="w-1 h-8 bg-primary"></div>
71
+ <div>
72
+ <h3 className="text-xl font-bold text-foreground font-mono tracking-wider uppercase">motor calibration</h3>
73
+ <p className="text-sm text-muted-foreground font-mono">move all joints to their limits</p>
74
+ </div>
75
+ </div>
76
+ <div className="flex gap-4">
77
+ {!isCalibrating ? (
78
+ <Button onClick={handleStart} size="lg">
79
+ Start Calibration
80
+ </Button>
81
+ ) : (
82
+ <Button onClick={handleFinish} variant="destructive" size="lg">
83
+ Finish Recording
84
+ </Button>
85
+ )}
86
+ <Button onClick={downloadJson} variant="outline" size="lg" disabled={!calibrationResults}>
87
+ <Download className="w-4 h-4 mr-2" /> Download JSON
88
+ </Button>
89
+ </div>
90
+ </div>
91
+ <div className="pt-6 p-6">
92
+ <div className="flex items-center gap-4 py-2 px-4 text-sm font-sans text-muted-foreground">
93
+ <div className="w-40">Motor Name</div>
94
+ <div className="flex-1">Visual Range</div>
95
+ <div className="w-16 text-right">Current</div>
96
+ <div className="w-16 text-right">Min</div>
97
+ <div className="w-16 text-right">Max</div>
98
+ <div className="w-16 text-right">Range</div>
99
+ </div>
100
+ <div className="border-t border-white/10">
101
+ {motorData.map(([name, data]) => (
102
+ <MotorCalibrationVisual key={name} name={name} data={data} />
103
+ ))}
104
+ </div>
105
+ </div>
106
+ </Card>
107
+ )
108
+ }
examples/cyberpunk-standalone/src/components/device-dashboard.tsx ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import { Settings, Gamepad2, Trash2, Pencil, Plus, ExternalLink } from "lucide-react"
4
+ import { Button } from "@/components/ui/button"
5
+ import { Card, CardFooter } from "@/components/ui/card"
6
+ import { Badge } from "@/components/ui/badge"
7
+ import { cn } from "@/lib/utils"
8
+ import HudCorners from "@/components/hud-corners"
9
+ import type { RobotConnection } from "@/types/robot"
10
+
11
+ interface DeviceDashboardProps {
12
+ robots: RobotConnection[]
13
+ onCalibrate: (robot: RobotConnection) => void
14
+ onTeleoperate: (robot: RobotConnection) => void
15
+ onRemove: (robotId: string) => void
16
+ onEdit: (robot: RobotConnection) => void
17
+ onFindNew: () => void
18
+ isConnecting: boolean
19
+ onScrollToHardware: () => void
20
+ }
21
+
22
+ export function DeviceDashboard({
23
+ robots,
24
+ onCalibrate,
25
+ onTeleoperate,
26
+ onRemove,
27
+ onEdit,
28
+ onFindNew,
29
+ isConnecting,
30
+ onScrollToHardware,
31
+ }: DeviceDashboardProps) {
32
+ return (
33
+ <Card className="border-0 rounded-none">
34
+ <div className="p-4 border-b border-white/10 flex items-center justify-between">
35
+ <div className="flex items-center gap-4">
36
+ <div className="w-1 h-8 bg-primary"></div>
37
+ <div>
38
+ <h3 className="text-xl font-bold text-foreground font-mono tracking-wider uppercase">device registry</h3>
39
+ <div className="flex items-center gap-2 mt-1">
40
+ <span className="text-xs text-muted-foreground font-mono">currently supports SO-100 </span>
41
+ <button
42
+ onClick={onScrollToHardware}
43
+ className="text-xs text-primary hover:text-accent transition-colors underline font-mono flex items-center gap-1"
44
+ >
45
+ <ExternalLink className="w-3 h-3" />
46
+ add more devices
47
+ </button>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ {robots.length > 0 && (
52
+ <Button onClick={onFindNew} disabled={isConnecting} size="lg" className="font-mono uppercase">
53
+ <Plus className="w-4 h-4 mr-2" />
54
+ add unit
55
+ </Button>
56
+ )}
57
+ </div>
58
+
59
+ <div className="pt-6 p-6">
60
+ {robots.length === 0 ? (
61
+ <div className="relative">
62
+ <HudCorners className="p-16">
63
+ <div className="text-center font-mono">
64
+ <div className="mb-6">
65
+ {isConnecting ? (
66
+ <>
67
+ <div className="w-16 h-16 mx-auto mb-4 border-2 border-primary/50 rounded-lg flex items-center justify-center animate-pulse">
68
+ <Plus className="w-8 h-8 text-primary animate-spin" />
69
+ </div>
70
+ <h4 className="text-xl text-primary mb-2 tracking-wider uppercase">scanning for units</h4>
71
+ <p className="text-sm text-muted-foreground mb-8">searching for available devices...</p>
72
+ </>
73
+ ) : (
74
+ <>
75
+ <div className="w-16 h-16 mx-auto mb-4 border-2 border-dashed border-primary/50 rounded-lg flex items-center justify-center">
76
+ <Plus className="w-8 h-8 text-primary/50" />
77
+ </div>
78
+ <h4 className="text-xl text-primary mb-2 tracking-wider uppercase">no units detected</h4>
79
+
80
+ <Button onClick={onFindNew} size="lg" className="font-mono uppercase">
81
+ <Plus className="w-4 h-4 mr-2" />
82
+ add unit
83
+ </Button>
84
+ </>
85
+ )}
86
+ </div>
87
+ </div>
88
+ </HudCorners>
89
+ </div>
90
+ ) : (
91
+ <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
92
+ {robots.map((robot) => (
93
+ <HudCorners key={robot.robotId}>
94
+ <Card className="flex flex-col h-full">
95
+ <div className="p-4 border-b border-white/10">
96
+ <div className="flex items-start justify-between mb-3">
97
+ <div className="flex-1">
98
+ <h4 className="text-xl font-bold text-primary font-mono tracking-wider">{robot.name}</h4>
99
+ <div className="flex items-center gap-2 mt-1">
100
+ <span className="text-xs text-muted-foreground font-mono">{robot.serialNumber}</span>
101
+ <span className="text-xs text-muted-foreground">•</span>
102
+ <span className="text-xs font-mono uppercase text-muted-foreground">{robot.robotType}</span>
103
+ </div>
104
+ </div>
105
+ <Badge
106
+ variant="outline"
107
+ className={cn(
108
+ "border-primary/50 bg-primary/20 text-primary font-mono text-xs",
109
+ robot.isConnected && "animate-pulse-slow",
110
+ )}
111
+ >
112
+ {robot.isConnected ? "ONLINE" : "OFFLINE"}
113
+ </Badge>
114
+ </div>
115
+ </div>
116
+
117
+ <div className="flex-grow p-4">
118
+ <div className="grid grid-cols-2 gap-4 text-xs font-mono">
119
+ <div>
120
+ <span className="text-muted-foreground uppercase">status:</span>
121
+ <div className="text-primary uppercase">
122
+ {robot.isConnected ? "operational" : "disconnected"}
123
+ </div>
124
+ </div>
125
+ <div>
126
+ <span className="text-muted-foreground uppercase">type:</span>
127
+ <div className="text-accent uppercase">{robot.robotType}</div>
128
+ </div>
129
+ </div>
130
+ </div>
131
+
132
+ <CardFooter className="p-4 border-t border-white/10 flex flex-wrap gap-2">
133
+ <Button
134
+ variant="outline"
135
+ size="sm"
136
+ onClick={() => onEdit(robot)}
137
+ className="font-mono text-xs uppercase"
138
+ >
139
+ <Pencil className="w-3 h-3 mr-1" /> edit
140
+ </Button>
141
+ <Button
142
+ variant="outline"
143
+ size="sm"
144
+ onClick={() => onCalibrate(robot)}
145
+ className="font-mono text-xs uppercase"
146
+ >
147
+ <Settings className="w-3 h-3 mr-1" /> calibrate
148
+ </Button>
149
+ <Button
150
+ variant="outline"
151
+ size="sm"
152
+ onClick={() => onTeleoperate(robot)}
153
+ className="font-mono text-xs uppercase"
154
+ >
155
+ <Gamepad2 className="w-3 h-3 mr-1" /> control
156
+ </Button>
157
+ <Button
158
+ variant="destructive"
159
+ size="sm"
160
+ onClick={() => onRemove(robot.robotId)}
161
+ className="font-mono text-xs uppercase"
162
+ >
163
+ <Trash2 className="w-3 h-3 mr-1" /> remove
164
+ </Button>
165
+ </CardFooter>
166
+ </Card>
167
+ </HudCorners>
168
+ ))}
169
+ </div>
170
+ )}
171
+ </div>
172
+ </Card>
173
+ )
174
+ }
examples/cyberpunk-standalone/src/components/docs-section.tsx ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import type React from "react"
4
+ import { Book, Code2, Terminal, Copy, Check } from "lucide-react"
5
+ import { Button } from "@/components/ui/button"
6
+ import { useState } from "react"
7
+
8
+ const CodeBlock = ({ children }: { children: React.ReactNode }) => {
9
+ const [copied, setCopied] = useState(false)
10
+
11
+ const copyToClipboard = async (text: string) => {
12
+ try {
13
+ await navigator.clipboard.writeText(text)
14
+ setCopied(true)
15
+ setTimeout(() => setCopied(false), 2000)
16
+ } catch (err) {
17
+ console.error("Failed to copy:", err)
18
+ }
19
+ }
20
+
21
+ const codeText = typeof children === "string" ? children : children?.toString() || ""
22
+
23
+ return (
24
+ <div className="bg-muted/50 dark:bg-black/40 border border-border dark:border-white/10 rounded-md overflow-hidden my-4 relative">
25
+ <pre className="p-4 text-sm overflow-x-auto">
26
+ <code>{children}</code>
27
+ </pre>
28
+ <Button
29
+ variant="outline"
30
+ size="sm"
31
+ onClick={() => copyToClipboard(codeText)}
32
+ className="absolute top-2 right-2 h-7 w-7 p-0 transition-all"
33
+ >
34
+ {copied ? <Check className="w-3 h-3" /> : <Copy className="w-3 h-3" />}
35
+ </Button>
36
+ </div>
37
+ )
38
+ }
39
+
40
+ export function DocsSection() {
41
+ return (
42
+ <div className="font-mono">
43
+ <div className="mb-6">
44
+ <h2 className="text-2xl font-bold text-primary tracking-wider mb-2 uppercase flex items-center gap-3">
45
+ <Book className="w-6 h-6" />
46
+ Docs
47
+ </h2>
48
+ <p className="text-sm text-muted-foreground">A quick guide to get started with the @lerobot/web API.</p>
49
+ </div>
50
+
51
+ <div className="bg-muted/40 dark:bg-black/30 border-l-4 border-cyan-500 dark:border-accent-cyan p-6 md:p-8 rounded-r-lg space-y-8">
52
+ {/* Getting Started */}
53
+ <div>
54
+ <h3 className="text-xl font-bold text-cyan-600 dark:text-accent-cyan tracking-wider uppercase flex items-center gap-2">
55
+ <Terminal className="w-5 h-5" />
56
+ Getting Started
57
+ </h3>
58
+ <p className="text-muted-foreground text-sm mt-2 mb-4">
59
+ First, import the API from the library. Then you can call its methods to find and control your robot.
60
+ </p>
61
+ <CodeBlock>
62
+ {`import { lerobot } from '@lerobot/web';
63
+
64
+ async function connectToRobot() {
65
+ const { result } = await lerobot.findPort({
66
+ onMessage: (message) => console.log('Status:', message),
67
+ });
68
+
69
+ const robots = await result;
70
+ console.log('Found robots:', robots);
71
+ }`}
72
+ </CodeBlock>
73
+ </div>
74
+
75
+ {/* API Reference */}
76
+ <div>
77
+ <h3 className="text-xl font-bold text-cyan-600 dark:text-accent-cyan tracking-wider uppercase flex items-center gap-2">
78
+ <Code2 className="w-5 h-5" />
79
+ API Reference
80
+ </h3>
81
+ <div className="space-y-6 mt-4">
82
+ {/* findPort */}
83
+ <div>
84
+ <h4 className="font-bold text-primary">lerobot.findPort(options)</h4>
85
+ <p className="text-muted-foreground text-sm mt-1">Scans for available robot connections via WebSerial.</p>
86
+ <CodeBlock>
87
+ {`// Options
88
+ {
89
+ robotConfigs?: RobotConfig[], // Optional: Saved configs to try reconnecting
90
+ onMessage?: (msg: string) => void // Callback for status updates
91
+ }
92
+
93
+ // Returns
94
+ {
95
+ result: Promise<RobotConnection[]> // A promise that resolves with found robots
96
+ }`}
97
+ </CodeBlock>
98
+ </div>
99
+
100
+ {/* calibrate */}
101
+ <div>
102
+ <h4 className="font-bold text-primary">lerobot.calibrate(robot, options)</h4>
103
+ <p className="text-muted-foreground text-sm mt-1">Starts the calibration process for a specific robot.</p>
104
+ <CodeBlock>
105
+ {`// Parameters
106
+ robot: RobotConnection // The robot instance to calibrate
107
+
108
+ // Options
109
+ {
110
+ onLiveUpdate: (data: LiveCalibrationData) => void, // Callback for real-time joint data
111
+ onProgress: (msg: string) => void // Callback for status messages
112
+ }
113
+
114
+ // Returns
115
+ {
116
+ result: Promise<WebCalibrationResults>, // A promise that resolves with final calibration
117
+ stop: () => void // Function to stop the calibration recording
118
+ }`}
119
+ </CodeBlock>
120
+ </div>
121
+
122
+ {/* teleoperate */}
123
+ <div>
124
+ <h4 className="font-bold text-primary">lerobot.teleoperate(robot, options)</h4>
125
+ <p className="text-muted-foreground text-sm mt-1">Initializes the teleoperation interface for a robot.</p>
126
+ <CodeBlock>
127
+ {`// Parameters
128
+ robot: RobotConnection,
129
+ calibrationData: WebCalibrationResults
130
+
131
+ // Options
132
+ {
133
+ onStateUpdate: (state: TeleoperationState) => void // Callback for real-time state updates
134
+ }
135
+
136
+ // Returns
137
+ {
138
+ start: () => void,
139
+ stop: () => void,
140
+ updateKeyState: (key: string, pressed: boolean) => void,
141
+ moveMotor: (motorName: string, position: number) => void
142
+ }`}
143
+ </CodeBlock>
144
+ </div>
145
+ </div>
146
+ </div>
147
+ </div>
148
+ </div>
149
+ )
150
+ }
examples/cyberpunk-standalone/src/components/edit-robot-dialog.tsx ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+ import { useState, useEffect } from "react"
3
+ import { Button } from "@/components/ui/button"
4
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogClose } from "@/components/ui/dialog"
5
+ import { Input } from "@/components/ui/input"
6
+ import { Label } from "@/components/ui/label"
7
+ import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
8
+ import type { RobotConnection } from "@/types/robot"
9
+
10
+ interface EditRobotDialogProps {
11
+ robot: RobotConnection | null
12
+ isOpen: boolean
13
+ onOpenChange: (open: boolean) => void
14
+ onSave: (updatedRobot: RobotConnection) => void
15
+ }
16
+
17
+ export function EditRobotDialog({ robot, isOpen, onOpenChange, onSave }: EditRobotDialogProps) {
18
+ const [name, setName] = useState("")
19
+ const [type, setType] = useState<"so100_follower" | "so100_leader">("so100_follower")
20
+
21
+ useEffect(() => {
22
+ if (robot) {
23
+ setName(robot.name)
24
+ setType(robot.robotType || "so100_follower")
25
+ }
26
+ }, [robot])
27
+
28
+ const handleSave = () => {
29
+ if (robot) {
30
+ onSave({ ...robot, name, robotType: type })
31
+ }
32
+ }
33
+
34
+ if (!robot) return null
35
+
36
+ return (
37
+ <Dialog open={isOpen} onOpenChange={onOpenChange}>
38
+ <DialogContent className="font-sans bg-background/80 backdrop-blur-sm border-primary/20">
39
+ <DialogHeader>
40
+ <DialogTitle>Configure Robot</DialogTitle>
41
+ </DialogHeader>
42
+ <div className="grid gap-4 py-4">
43
+ <div className="grid grid-cols-4 items-center gap-4">
44
+ <Label htmlFor="name" className="text-right">
45
+ Name
46
+ </Label>
47
+ <Input id="name" value={name} onChange={(e) => setName(e.target.value)} className="col-span-3 font-mono" />
48
+ </div>
49
+ <div className="grid grid-cols-4 items-center gap-4">
50
+ <Label className="text-right">Type</Label>
51
+ <RadioGroup value={type} onValueChange={(value) => setType(value as any)} className="col-span-3">
52
+ <div className="flex items-center space-x-2">
53
+ <RadioGroupItem value="so100_follower" id="r1" />
54
+ <Label htmlFor="r1">SO-100 Follower</Label>
55
+ </div>
56
+ <div className="flex items-center space-x-2">
57
+ <RadioGroupItem value="so100_leader" id="r2" />
58
+ <Label htmlFor="r2">SO-100 Leader</Label>
59
+ </div>
60
+ </RadioGroup>
61
+ </div>
62
+ </div>
63
+ <DialogFooter>
64
+ <DialogClose asChild>
65
+ <Button type="button" variant="secondary">
66
+ Cancel
67
+ </Button>
68
+ </DialogClose>
69
+ <DialogClose asChild>
70
+ <Button type="submit" onClick={handleSave}>
71
+ Save Changes
72
+ </Button>
73
+ </DialogClose>
74
+ </DialogFooter>
75
+ </DialogContent>
76
+ </Dialog>
77
+ )
78
+ }
examples/cyberpunk-standalone/src/components/footer.tsx ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Github } from "lucide-react"
2
+
3
+ export function Footer() {
4
+ return (
5
+ <footer className="w-full border-t">
6
+ <div className="container mx-auto flex h-16 items-center justify-center px-4 md:px-6">
7
+ <div className="flex items-center gap-4 text-sm text-muted-foreground">
8
+ <span>
9
+ Created with ❤️ by{" "}
10
+ <a
11
+ href="https://github.com/timpietrusky"
12
+ target="_blank"
13
+ rel="noopener noreferrer"
14
+ className="text-primary hover:text-accent transition-colors underline"
15
+ >
16
+ Tim Pietrusky
17
+ </a>{" "}
18
+ under{" "}
19
+ <a
20
+ href="https://github.com/timpietrusky/lerobot.js/blob/main/LICENSE"
21
+ target="_blank"
22
+ rel="noopener noreferrer"
23
+ className="text-primary hover:text-accent transition-colors underline"
24
+ >
25
+ Apache 2.0
26
+ </a>
27
+ </span>
28
+ <a
29
+ href="https://github.com/timpietrusky/lerobot.js"
30
+ target="_blank"
31
+ rel="noopener noreferrer"
32
+ className="flex items-center gap-1 hover:text-foreground transition-colors"
33
+ >
34
+ <Github className="h-4 w-4" />
35
+ GitHub
36
+ </a>
37
+ </div>
38
+ </div>
39
+ </footer>
40
+ )
41
+ }
examples/cyberpunk-standalone/src/components/grid-background.tsx ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import type React from "react"
4
+
5
+ import { useEffect } from "react"
6
+
7
+ export function GridBackground({ children }: { children: React.ReactNode }) {
8
+ useEffect(() => {
9
+ const handleMouseMove = (e: MouseEvent) => {
10
+ document.body.style.setProperty("--mouse-x", `${e.clientX}px`)
11
+ document.body.style.setProperty("--mouse-y", `${e.clientY}px`)
12
+ }
13
+
14
+ window.addEventListener("mousemove", handleMouseMove)
15
+
16
+ return () => {
17
+ window.removeEventListener("mousemove", handleMouseMove)
18
+ }
19
+ }, [])
20
+
21
+ return <>{children}</>
22
+ }
examples/cyberpunk-standalone/src/components/hardware-support-section.tsx ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import { Cpu, Mail, Plus, ExternalLink } from "lucide-react";
4
+ import { Button } from "@/components/ui/button";
5
+ import HudCorners from "@/components/hud-corners";
6
+
7
+ const supportedHardware = [
8
+ {
9
+ name: "SO-100",
10
+ status: "supported",
11
+ description:
12
+ "6-DOF robotic arm with gripper, leader/follower configuration",
13
+ },
14
+ ];
15
+
16
+ export function HardwareSupportSection() {
17
+ return (
18
+ <div className="font-mono">
19
+ <div className="mb-6">
20
+ <h2 className="text-2xl font-bold text-primary tracking-wider mb-2 uppercase flex items-center gap-3">
21
+ <Cpu className="w-6 h-6" />
22
+ Hardware Support
23
+ </h2>
24
+ <p className="text-sm text-muted-foreground">
25
+ {"making sure that robotic ai is accessible to everyone"}
26
+ </p>
27
+ </div>
28
+
29
+ <div className="space-y-8">
30
+ {/* Currently Supported */}
31
+ <HudCorners color="primary" size="sm" layer="front">
32
+ <div className="bg-muted/40 dark:bg-black/30 border border-primary/30 p-6 rounded-lg">
33
+ <h3 className="text-xl font-bold text-cyan-600 dark:text-accent-cyan tracking-wider uppercase mb-4 flex items-center gap-2">
34
+ <Cpu className="w-5 h-5" />
35
+ Currently Supported
36
+ </h3>
37
+
38
+ <div className="space-y-4">
39
+ {supportedHardware.map((robot, index) => (
40
+ <div
41
+ key={index}
42
+ className="flex items-center justify-between p-4 bg-muted/50 dark:bg-black/20 rounded-lg border border-border dark:border-white/10"
43
+ >
44
+ <div className="flex-1">
45
+ <div className="flex items-center gap-3 mb-2">
46
+ <h4 className="font-bold text-primary">{robot.name}</h4>
47
+ </div>
48
+ <p className="text-muted-foreground text-xs">
49
+ {robot.description}
50
+ </p>
51
+ </div>
52
+
53
+ {/* Sponsored By Section */}
54
+ <div className="flex flex-col items-end gap-2 ml-6">
55
+ <span className="text-xs text-muted-foreground uppercase tracking-wider">
56
+ Sponsored by
57
+ </span>
58
+ <a
59
+ href="https://partabot.com"
60
+ target="_blank"
61
+ rel="noopener noreferrer"
62
+ className="inline-block hover:opacity-80 transition-opacity"
63
+ >
64
+ <img
65
+ src="partabot-logo.png"
66
+ alt="Partabot.com"
67
+ width="100"
68
+ height="32"
69
+ className="h-6 w-auto invert dark:invert-0"
70
+ />
71
+ </a>
72
+ </div>
73
+ </div>
74
+ ))}
75
+ </div>
76
+ </div>
77
+ </HudCorners>
78
+
79
+ {/* Add New Hardware */}
80
+ <HudCorners color="whiteMuted" size="sm" layer="front">
81
+ <div className="bg-muted/40 dark:bg-black/30 border border-muted/30 p-6 rounded-lg">
82
+ <h3 className="text-xl font-bold text-cyan-600 dark:text-accent-cyan tracking-wider uppercase mb-4 flex items-center gap-2">
83
+ <Plus className="w-5 h-5" />
84
+ Add New Hardware Support
85
+ </h3>
86
+
87
+ <div className="space-y-6">
88
+ <div>
89
+ <p className="text-muted-foreground mb-4">
90
+ please provide us with access to different robot hardware, so
91
+ we can add them to lerobot.js
92
+ </p>
93
+
94
+ <div className="bg-muted/60 dark:bg-black/40 border border-border dark:border-white/10 rounded-lg p-4 mb-4">
95
+ <h4 className="font-bold text-primary mb-2">
96
+ How to contribute hardware support:
97
+ </h4>
98
+ <ul className="text-foreground/80 dark:text-muted-foreground text-sm space-y-1 list-disc list-inside">
99
+ <li>Loan or donate hardware for development</li>
100
+ <li>Provide technical documentation and APIs</li>
101
+ <li>Contribute code for new robot integrations</li>
102
+ <li>Help with testing and validation</li>
103
+ </ul>
104
+ </div>
105
+
106
+ <div className="flex flex-col sm:flex-row gap-4">
107
+ <Button
108
+ className="font-mono uppercase flex items-center gap-2"
109
+ onClick={() =>
110
+ window.open(
111
+ "mailto:[email protected]?subject=LeRobot.js Hardware Support",
112
+ "_blank"
113
+ )
114
+ }
115
+ >
116
+ <Mail className="w-4 h-4" />
117
+ Contact Tim
118
+ </Button>
119
+ <Button
120
+ variant="outline"
121
+ className="font-mono uppercase flex items-center gap-2 bg-transparent"
122
+ onClick={() =>
123
+ window.open(
124
+ "https://github.com/timpietrusky/lerobot.js",
125
+ "_blank"
126
+ )
127
+ }
128
+ >
129
+ <ExternalLink className="w-4 h-4" />
130
+ GitHub
131
+ </Button>
132
+ </div>
133
+ </div>
134
+ </div>
135
+ </div>
136
+ </HudCorners>
137
+ </div>
138
+ </div>
139
+ );
140
+ }
examples/cyberpunk-standalone/src/components/header.tsx ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ThemeToggle } from "./theme-toggle"
2
+
3
+ export function Header() {
4
+ return (
5
+ <header className="w-full border-b">
6
+ <div className="container mx-auto flex h-16 items-center justify-between px-4 md:px-6">
7
+ <div className="flex items-center gap-6">
8
+ <h1 className="text-2xl font-bold">LeRobot.js</h1>
9
+ <div className="flex items-center gap-2 text-primary font-mono text-sm">
10
+ <span className="text-xs text-muted-foreground font-mono">
11
+ interact with your robot in JS, inspired by{" "}
12
+ <a
13
+ href="https://huggingface.co/docs/lerobot"
14
+ target="_blank"
15
+ rel="noopener noreferrer"
16
+ className="text-primary hover:text-accent transition-colors underline"
17
+ >
18
+ LeRobot
19
+ </a>
20
+ </span>
21
+ </div>
22
+ </div>
23
+ <ThemeToggle />
24
+ </div>
25
+ </header>
26
+ )
27
+ }
examples/cyberpunk-standalone/src/components/hud-corners.tsx ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type React from "react"
2
+ import { cn } from "@/lib/utils"
3
+
4
+ const HudCorners = ({
5
+ children,
6
+ className,
7
+ color = "primary",
8
+ size = "md",
9
+ layer = "behind",
10
+ }: {
11
+ children: React.ReactNode
12
+ className?: string
13
+ color?: "primary" | "whiteMuted"
14
+ size?: "sm" | "md" | "lg" | "xl"
15
+ layer?: "behind" | "front" | "top"
16
+ }) => {
17
+ // Define size variants for corner dimensions
18
+ const sizeClasses = {
19
+ sm: "w-4 h-4", // 16px × 16px - small corners
20
+ md: "w-6 h-6", // 24px × 24px - default
21
+ lg: "w-8 h-8", // 32px × 32px - large corners
22
+ xl: "w-12 h-12", // 48px × 48px - extra large corners
23
+ }
24
+
25
+ // Define layer/z-index variants
26
+ const layerClasses = {
27
+ behind: "z-0", // Behind content (default behavior)
28
+ front: "z-10", // Above most content
29
+ top: "z-50", // Above almost everything (modals, dropdowns, etc.)
30
+ }
31
+
32
+ const cornerClasses = `absolute ${sizeClasses[size]} ${layerClasses[layer]}`
33
+ const colorClasses = color === "primary" ? "border-primary/80" : "border-muted-foreground/30"
34
+
35
+ return (
36
+ <div className={cn("relative", className)}>
37
+ <div className={`${cornerClasses} ${colorClasses} top-0 left-0 border-t border-l`}></div>
38
+ <div className={`${cornerClasses} ${colorClasses} top-0 right-0 border-t border-r`}></div>
39
+ <div className={`${cornerClasses} ${colorClasses} bottom-0 left-0 border-b border-l`}></div>
40
+ <div className={`${cornerClasses} ${colorClasses} bottom-0 right-0 border-b border-r`}></div>
41
+ {children}
42
+ </div>
43
+ )
44
+ }
45
+
46
+ export default HudCorners
examples/cyberpunk-standalone/src/components/motor-calibration-visual.tsx ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ interface MotorCalibrationVisualProps {
2
+ name: string
3
+ data: { current: number; min: number; max: number; range: number }
4
+ }
5
+
6
+ export function MotorCalibrationVisual({ name, data }: MotorCalibrationVisualProps) {
7
+ const totalRange = 4095 // Standard range for many servos
8
+ const toPercent = (val: number) => (val / totalRange) * 100
9
+
10
+ const currentPos = toPercent(data.current)
11
+ const minPos = toPercent(data.min)
12
+ const maxPos = toPercent(data.max)
13
+ const rangeWidth = maxPos - minPos
14
+
15
+ return (
16
+ <div className="flex items-center gap-4 py-2">
17
+ <div className="w-40 font-mono text-muted-foreground">{name}</div>
18
+ <div className="flex-1">
19
+ <div className="h-6 bg-black/30 rounded-sm relative overflow-hidden">
20
+ {data.range > 0 && (
21
+ <div className="absolute h-full bg-primary/20" style={{ left: `${minPos}%`, width: `${rangeWidth}%` }} />
22
+ )}
23
+ <div className="absolute top-0 h-full w-1 bg-accent" style={{ left: `calc(${currentPos}% - 2px)` }} />
24
+ </div>
25
+ </div>
26
+ <div className="w-16 text-right font-mono text-lg">{data.current}</div>
27
+ <div className="w-16 text-right font-mono text-lg text-primary/80">{data.min === 4095 ? "N/A" : data.min}</div>
28
+ <div className="w-16 text-right font-mono text-lg text-primary/80">{data.max === 0 ? "N/A" : data.max}</div>
29
+ <div className="w-16 text-right font-mono text-lg text-accent">{data.range}</div>
30
+ </div>
31
+ )
32
+ }
examples/cyberpunk-standalone/src/components/roadmap-section.tsx ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+ import { CheckCircle, Clock, Target } from "lucide-react"
3
+ import { Badge } from "@/components/ui/badge"
4
+ import { cn } from "@/lib/utils"
5
+
6
+ interface RoadmapItem {
7
+ title: string
8
+ description: string
9
+ status: "completed" | "planned"
10
+ }
11
+
12
+ const roadmapItems: RoadmapItem[] = [
13
+ {
14
+ title: "findPort",
15
+ description: "WebSerial-based robot detection and connection management",
16
+ status: "completed",
17
+ },
18
+ {
19
+ title: "calibrate",
20
+ description: "Real-time joint calibration with visual feedback and data export",
21
+ status: "completed",
22
+ },
23
+ {
24
+ title: "teleoperate",
25
+ description: "Manual robot control with keyboard and slider inputs",
26
+ status: "completed",
27
+ },
28
+ {
29
+ title: "record",
30
+ description: "Record robot trajectories and sensor data to create datasets",
31
+ status: "planned",
32
+ },
33
+ {
34
+ title: "replay",
35
+ description: "Replay any recorded episode or episodes from existing datasets",
36
+ status: "planned",
37
+ },
38
+ {
39
+ title: "train",
40
+ description: "Run training based on a given dataset to create a policy",
41
+ status: "planned",
42
+ },
43
+ {
44
+ title: "eval",
45
+ description: "Run inference using trained policies for autonomous operation",
46
+ status: "planned",
47
+ },
48
+ ]
49
+
50
+ const statusConfig = {
51
+ completed: {
52
+ icon: CheckCircle,
53
+ label: "COMPLETED",
54
+ dotColor: "bg-green-500 dark:bg-green-400",
55
+ textColor: "text-green-600 dark:text-green-400",
56
+ bgColor: "bg-green-500/10 dark:bg-green-400/5",
57
+ borderColor: "border-green-500/30 dark:border-green-400/20",
58
+ },
59
+ planned: {
60
+ icon: Clock,
61
+ label: "PLANNED",
62
+ dotColor: "bg-slate-500 dark:bg-muted-foreground",
63
+ textColor: "text-slate-600 dark:text-muted-foreground",
64
+ bgColor: "bg-slate-500/10 dark:bg-muted-foreground/5",
65
+ borderColor: "border-slate-500/30 dark:border-muted-foreground/20",
66
+ },
67
+ }
68
+
69
+ export function RoadmapSection() {
70
+ const completedCount = roadmapItems.filter((item) => item.status === "completed").length
71
+ const totalCount = roadmapItems.length
72
+
73
+ return (
74
+ <div className="font-mono">
75
+ <div className="mb-6">
76
+ <h2 className="text-2xl font-bold text-primary tracking-wider mb-2 uppercase flex items-center gap-3">
77
+ <Target className="w-6 h-6" />
78
+ Roadmap
79
+ </h2>
80
+ <p className="text-sm text-muted-foreground">lfg o7</p>
81
+ </div>
82
+
83
+ <div className="bg-gradient-to-br from-muted/60 to-muted/40 dark:from-black/40 dark:to-black/20 border border-primary/20 rounded-lg overflow-hidden">
84
+ {/* Header */}
85
+ <div className="bg-primary/30 dark:bg-primary/10 border-b border-primary/20 p-4">
86
+ <div className="flex items-center justify-between">
87
+ <div className="flex items-center gap-4">
88
+ <div className="text-primary font-bold text-lg tracking-wider">SYSTEM OBJECTIVES</div>
89
+ <div className="flex items-center gap-2">
90
+ <div className="w-2 h-2 bg-green-500 dark:bg-green-400 rounded-full animate-pulse"></div>
91
+ <span className="text-green-600 dark:text-green-400 text-sm">{completedCount} ACTIVE</span>
92
+ <div className="w-2 h-2 bg-slate-500 dark:bg-muted-foreground rounded-full"></div>
93
+ <span className="text-slate-600 dark:text-muted-foreground text-sm">
94
+ {totalCount - completedCount} QUEUED
95
+ </span>
96
+ </div>
97
+ </div>
98
+ <div className="text-xs text-muted-foreground">
99
+ PROGRESS: {Math.round((completedCount / totalCount) * 100)}%
100
+ </div>
101
+ </div>
102
+ </div>
103
+
104
+ {/* Progress Bar */}
105
+ <div className="bg-muted/50 dark:bg-black/30 p-4 border-b border-border dark:border-white/10">
106
+ <div className="w-full bg-muted/80 dark:bg-black/40 rounded-full h-2 overflow-hidden">
107
+ <div
108
+ className="h-full bg-gradient-to-r from-green-500 to-primary dark:from-green-400 dark:to-primary transition-all duration-1000 ease-out"
109
+ style={{ width: `${(completedCount / totalCount) * 100}%` }}
110
+ ></div>
111
+ </div>
112
+ </div>
113
+
114
+ {/* Items List */}
115
+ <div className="p-6">
116
+ <div className="space-y-3">
117
+ {roadmapItems.map((item, index) => {
118
+ const config = statusConfig[item.status]
119
+ const StatusIcon = config.icon
120
+
121
+ return (
122
+ <div
123
+ key={index}
124
+ className={cn(
125
+ "flex items-center gap-4 p-4 rounded border transition-all hover:bg-muted/30 dark:hover:bg-white/5",
126
+ config.bgColor,
127
+ config.borderColor,
128
+ )}
129
+ >
130
+ {/* Number */}
131
+ <div className="text-muted-foreground/50 text-xs font-mono min-w-[2rem] text-right">
132
+ {String(index + 1).padStart(2, "0")}
133
+ </div>
134
+
135
+ {/* Content */}
136
+ <div className="flex-1 min-w-0">
137
+ <h4 className={cn("font-bold text-lg", config.textColor)}>{item.title}()</h4>
138
+ <p className="text-muted-foreground text-sm mt-1">{item.description}</p>
139
+ </div>
140
+
141
+ {/* Status Badge */}
142
+ <Badge
143
+ variant="outline"
144
+ className={cn(
145
+ "text-xs font-bold tracking-wider border-0 px-3 py-1 flex-shrink-0",
146
+ config.textColor,
147
+ config.bgColor,
148
+ )}
149
+ >
150
+ {config.label}
151
+ </Badge>
152
+
153
+ {/* Status Icon */}
154
+ <StatusIcon className={cn("w-4 h-4 flex-shrink-0 ml-3", config.textColor)} />
155
+ </div>
156
+ )
157
+ })}
158
+ </div>
159
+ </div>
160
+
161
+ {/* Footer */}
162
+ <div className="bg-muted/50 dark:bg-black/30 border-t border-border dark:border-white/10 p-4">
163
+ <p className="text-muted-foreground text-sm">
164
+ <span className="text-cyan-600 dark:text-accent-cyan">REFERENCE:</span> functions based on the original{" "}
165
+ <a
166
+ href="https://huggingface.co/docs/lerobot"
167
+ target="_blank"
168
+ rel="noopener noreferrer"
169
+ className="text-primary hover:text-accent transition-colors underline"
170
+ >
171
+ LeRobot
172
+ </a>{" "}
173
+ (python) and adapted for web robotic ai
174
+ </p>
175
+ </div>
176
+ </div>
177
+ </div>
178
+ )
179
+ }
examples/cyberpunk-standalone/src/components/setup-cards.tsx ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+ import { useState } from "react"
3
+ import { Copy, Package, Clock, Check, Terminal } from "lucide-react"
4
+ import { Button } from "@/components/ui/button"
5
+ import { Card } from "@/components/ui/card"
6
+ import { Badge } from "@/components/ui/badge"
7
+ import HudCorners from "@/components/hud-corners"
8
+ import { cn } from "@/lib/utils"
9
+
10
+ type PackageManager = "npm" | "yarn" | "pnpm" | "bun"
11
+
12
+ interface PackageInstallerProps {
13
+ packageName: string
14
+ disabled?: boolean
15
+ }
16
+
17
+ function PackageInstaller({ packageName, disabled = false }: PackageInstallerProps) {
18
+ const [selectedPM, setSelectedPM] = useState<PackageManager>("pnpm")
19
+ const [copied, setCopied] = useState(false)
20
+
21
+ const packageManagers: { value: PackageManager; label: string; command: string }[] = [
22
+ { value: "pnpm", label: "pnpm", command: `pnpm add ${packageName}` },
23
+ { value: "npm", label: "npm", command: `npm i ${packageName}` },
24
+ { value: "yarn", label: "yarn", command: `yarn add ${packageName}` },
25
+ ]
26
+
27
+ const copyToClipboard = async (text: string) => {
28
+ try {
29
+ await navigator.clipboard.writeText(text)
30
+ setCopied(true)
31
+ setTimeout(() => setCopied(false), 2000)
32
+ } catch (err) {
33
+ console.error("Failed to copy:", err)
34
+ }
35
+ }
36
+
37
+ const currentCommand = packageManagers.find((pm) => pm.value === selectedPM)?.command || ""
38
+
39
+ return (
40
+ <div className="max-w-md">
41
+ <div className="space-y-3">
42
+ <div className="flex gap-1 w-fit">
43
+ {packageManagers.map((pm) => (
44
+ <Button
45
+ key={pm.value}
46
+ variant="ghost"
47
+ size="sm"
48
+ onClick={() => setSelectedPM(pm.value)}
49
+ disabled={disabled}
50
+ className={cn(
51
+ "font-mono text-xs px-3 min-w-[60px] h-8 border transition-colors",
52
+ selectedPM === pm.value
53
+ ? "bg-primary text-primary-foreground border-primary"
54
+ : "bg-transparent border-input hover:bg-accent hover:text-accent-foreground",
55
+ )}
56
+ >
57
+ {pm.label}
58
+ </Button>
59
+ ))}
60
+ </div>
61
+
62
+ <div className="relative">
63
+ <div
64
+ className={cn(
65
+ "border rounded-md p-3 font-mono text-sm transition-colors",
66
+ disabled
67
+ ? "bg-muted/60 dark:bg-black/20 border-dashed border-muted/40 dark:border-muted/20 text-muted-foreground/70 dark:text-muted-foreground/50"
68
+ : "bg-muted/60 dark:bg-black/40 border-border dark:border-white/10 text-foreground dark:text-primary",
69
+ )}
70
+ >
71
+ {currentCommand}
72
+ </div>
73
+ <Button
74
+ variant="outline"
75
+ size="sm"
76
+ onClick={() => copyToClipboard(currentCommand)}
77
+ disabled={disabled}
78
+ className="absolute right-2 top-2 h-7 w-7 p-0 transition-all"
79
+ >
80
+ {disabled ? (
81
+ <Clock className="w-3 h-3" />
82
+ ) : copied ? (
83
+ <Check className="w-3 h-3" />
84
+ ) : (
85
+ <Copy className="w-3 h-3" />
86
+ )}
87
+ </Button>
88
+ </div>
89
+ </div>
90
+ </div>
91
+ )
92
+ }
93
+
94
+ export function SetupCards() {
95
+ return (
96
+ <div className="grid md:grid-cols-2 gap-6 mb-8">
97
+ {/* Web Installation Card */}
98
+ {/* HudCorners creates 4 absolutely positioned corner elements with primary color borders */}
99
+ <HudCorners color="primary" size="sm" layer="front">
100
+ <Card className="h-full border-dashed border border-muted/30">
101
+ {/* This div contains the card content and sits above the corner elements due to relative positioning */}
102
+ <div className="p-6">
103
+ <div className="flex items-center gap-3 mb-4">
104
+ <div className="w-12 h-12 bg-primary/20 rounded-lg flex items-center justify-center">
105
+ <Package className="w-6 h-6 text-primary" />
106
+ </div>
107
+ <div>
108
+ <h3 className="text-xl font-bold text-primary font-mono tracking-wider uppercase">web</h3>
109
+ <p className="text-sm text-muted-foreground font-mono">run lerobot in the browser</p>
110
+ </div>
111
+ </div>
112
+
113
+ <PackageInstaller packageName="@lerobot/web" />
114
+ </div>
115
+ </Card>
116
+ </HudCorners>
117
+
118
+ {/* Node.js Card - Coming Soon */}
119
+ <HudCorners color="whiteMuted" size="sm" layer="front">
120
+ <Card className="h-full opacity-60 relative overflow-hidden border-dashed border border-muted/30">
121
+ {/* Disabled overlay effect */}
122
+ <div className="absolute inset-0 bg-gradient-to-br from-transparent via-muted/20 to-muted/40 dark:via-black/20 dark:to-black/40 pointer-events-none" />
123
+
124
+ <div className="p-6 relative h-full">
125
+ <div className="flex items-center justify-between mb-4">
126
+ <div className="flex items-center gap-3">
127
+ <div className="w-12 h-12 bg-muted/20 rounded-lg flex items-center justify-center border border-dashed border-muted/30">
128
+ <Terminal className="w-6 h-6 text-muted-foreground" />
129
+ </div>
130
+ <div>
131
+ <h3 className="text-xl font-bold text-muted-foreground font-mono tracking-wider uppercase">node</h3>
132
+ <p className="text-sm text-muted-foreground/70 font-mono">run lerobot on the server</p>
133
+ </div>
134
+ </div>
135
+ <Badge
136
+ variant="outline"
137
+ className="border-dashed border-muted/30 bg-muted/10 text-muted-foreground font-mono text-xs animate-pulse"
138
+ >
139
+ coming soon
140
+ </Badge>
141
+ </div>
142
+
143
+ <PackageInstaller packageName="@lerobot/node" disabled />
144
+
145
+ {/* Cyberpunk scan lines effect */}
146
+ <div className="absolute inset-0 pointer-events-none">
147
+ <div
148
+ className="absolute inset-0 bg-gradient-to-b from-transparent via-primary/5 to-transparent"
149
+ style={{
150
+ backgroundImage:
151
+ "repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(255,255,255,0.03) 2px, rgba(255,255,255,0.03) 4px)",
152
+ }}
153
+ />
154
+ </div>
155
+ </div>
156
+ </Card>
157
+ </HudCorners>
158
+ </div>
159
+ )
160
+ }
examples/cyberpunk-standalone/src/components/teleoperation-view.tsx ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+ import { useState, useEffect, useMemo } from "react"
3
+ import { Power, PowerOff, Keyboard } from "lucide-react"
4
+ import { Button } from "@/components/ui/button"
5
+ import { Card } from "@/components/ui/card"
6
+ import { Badge } from "@/components/ui/badge"
7
+ import { Slider } from "@/components/ui/slider"
8
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
9
+ import { cn } from "@/lib/utils"
10
+ import { lerobot } from "@/lib/mock-api"
11
+ import { useLocalStorage } from "@/hooks/use-local-storage"
12
+ import VirtualKey from "@/components/VirtualKey"
13
+ import type { RobotConnection, WebCalibrationResults, TeleoperationState } from "@/types/robot"
14
+
15
+ interface TeleoperationViewProps {
16
+ robot: RobotConnection
17
+ }
18
+
19
+ export function TeleoperationView({ robot }: TeleoperationViewProps) {
20
+ const [localCalibrationData] = useLocalStorage<WebCalibrationResults | null>(`calibration-${robot.robotId}`, null)
21
+ const [teleopState, setTeleopState] = useState<TeleoperationState | null>(null)
22
+ const [teleopProcess, setTeleopProcess] = useState<{
23
+ start: () => void
24
+ stop: () => void
25
+ updateKeyState: (key: string, pressed: boolean) => void
26
+ moveMotor: (motorName: string, position: number) => void
27
+ } | null>(null)
28
+
29
+ const calibrationData = useMemo(() => {
30
+ if (localCalibrationData) return localCalibrationData
31
+ return lerobot.MOCK_MOTOR_NAMES.reduce((acc, name) => {
32
+ acc[name] = { min: 1000, max: 3000 }
33
+ return acc
34
+ }, {} as WebCalibrationResults)
35
+ }, [localCalibrationData])
36
+
37
+ useEffect(() => {
38
+ let process: Awaited<ReturnType<typeof lerobot.teleoperate>>
39
+ const setup = async () => {
40
+ process = await lerobot.teleoperate(robot, {
41
+ calibrationData,
42
+ onStateUpdate: setTeleopState,
43
+ })
44
+ setTeleopProcess(process)
45
+ }
46
+ setup()
47
+
48
+ const handleKeyDown = (e: KeyboardEvent) => process?.updateKeyState(e.key, true)
49
+ const handleKeyUp = (e: KeyboardEvent) => process?.updateKeyState(e.key, false)
50
+
51
+ window.addEventListener("keydown", handleKeyDown)
52
+ window.addEventListener("keyup", handleKeyUp)
53
+
54
+ return () => {
55
+ process?.stop()
56
+ window.removeEventListener("keydown", handleKeyDown)
57
+ window.removeEventListener("keyup", handleKeyUp)
58
+ }
59
+ }, [robot, calibrationData])
60
+
61
+ const motorConfigs =
62
+ teleopState?.motorConfigs ??
63
+ lerobot.MOCK_MOTOR_NAMES.map((name) => ({
64
+ name,
65
+ currentPosition: 2048,
66
+ minPosition: calibrationData[name]?.min ?? 0,
67
+ maxPosition: calibrationData[name]?.max ?? 4095,
68
+ }))
69
+ const keyStates = teleopState?.keyStates ?? {}
70
+ const controls = lerobot.SO100_KEYBOARD_CONTROLS
71
+
72
+ return (
73
+ <Card className="border-0 rounded-none">
74
+ <div className="p-4 border-b border-white/10">
75
+ <div className="flex items-center justify-between">
76
+ <div className="flex items-center gap-4">
77
+ <div className="w-1 h-8 bg-primary"></div>
78
+ <div>
79
+ <h3 className="text-xl font-bold text-foreground font-mono tracking-wider uppercase">robot control</h3>
80
+ <p className="text-sm text-muted-foreground font-mono">
81
+ manual <span className="text-muted-foreground">teleoperate</span> interface
82
+ </p>
83
+ </div>
84
+ </div>
85
+ <div className="flex items-center gap-6">
86
+ <div className="border-l border-white/10 pl-6 flex items-center gap-4">
87
+ {teleopState?.isActive ? (
88
+ <Button onClick={() => teleopProcess?.stop()} variant="destructive" size="lg">
89
+ <PowerOff className="w-5 h-5 mr-2" /> Stop Control
90
+ </Button>
91
+ ) : (
92
+ <Button onClick={() => teleopProcess?.start()} size="lg">
93
+ <Power className="w-5 h-5 mr-2" /> Control Robot
94
+ </Button>
95
+ )}
96
+ <div className="flex items-center gap-2">
97
+ <span className="text-sm font-mono text-muted-foreground uppercase">status:</span>
98
+ <Badge
99
+ variant="outline"
100
+ className={cn(
101
+ "border-primary/50 bg-primary/20 text-primary font-mono text-xs",
102
+ teleopState?.isActive && "animate-pulse-slow",
103
+ )}
104
+ >
105
+ {teleopState?.isActive ? "ACTIVE" : "STOPPED"}
106
+ </Badge>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ </div>
112
+ <div className="pt-6 p-6 grid md:grid-cols-2 gap-8">
113
+ <div>
114
+ <h3 className="font-sans font-semibold mb-4 text-xl">Motor Control</h3>
115
+ <div className="space-y-6">
116
+ {motorConfigs.map((motor) => (
117
+ <div key={motor.name}>
118
+ <label className="text-sm font-mono text-muted-foreground">{motor.name}</label>
119
+ <div className="flex items-center gap-4">
120
+ <Slider
121
+ value={[motor.currentPosition]}
122
+ min={motor.minPosition}
123
+ max={motor.maxPosition}
124
+ step={1}
125
+ onValueChange={(val) => teleopProcess?.moveMotor(motor.name, val[0])}
126
+ disabled={!teleopState?.isActive}
127
+ />
128
+ <span className="text-lg font-mono w-16 text-right text-accent">
129
+ {Math.round(motor.currentPosition)}
130
+ </span>
131
+ </div>
132
+ </div>
133
+ ))}
134
+ </div>
135
+ </div>
136
+ <div>
137
+ <h3 className="font-sans font-semibold mb-4 text-xl">Keyboard Layout & Status</h3>
138
+ <div className="p-4 bg-black/30 rounded-lg space-y-4">
139
+ <div className="flex justify-around items-end">
140
+ <div className="flex flex-col items-center gap-2">
141
+ <VirtualKey
142
+ label="↑"
143
+ subLabel="Lift+"
144
+ isPressed={!!keyStates[controls.shoulder_lift.positive]?.pressed}
145
+ />
146
+ <div className="flex gap-2">
147
+ <VirtualKey
148
+ label="←"
149
+ subLabel="Pan-"
150
+ isPressed={!!keyStates[controls.shoulder_pan.negative]?.pressed}
151
+ />
152
+ <VirtualKey
153
+ label="↓"
154
+ subLabel="Lift-"
155
+ isPressed={!!keyStates[controls.shoulder_lift.negative]?.pressed}
156
+ />
157
+ <VirtualKey
158
+ label="→"
159
+ subLabel="Pan+"
160
+ isPressed={!!keyStates[controls.shoulder_pan.positive]?.pressed}
161
+ />
162
+ </div>
163
+ <span className="font-bold text-sm font-sans">Shoulder</span>
164
+ </div>
165
+ <div className="flex flex-col items-center gap-2">
166
+ <VirtualKey
167
+ label="W"
168
+ subLabel="Elbow+"
169
+ isPressed={!!keyStates[controls.elbow_flex.positive]?.pressed}
170
+ />
171
+ <div className="flex gap-2">
172
+ <VirtualKey
173
+ label="A"
174
+ subLabel="Wrist+"
175
+ isPressed={!!keyStates[controls.wrist_flex.positive]?.pressed}
176
+ />
177
+ <VirtualKey
178
+ label="S"
179
+ subLabel="Elbow-"
180
+ isPressed={!!keyStates[controls.elbow_flex.negative]?.pressed}
181
+ />
182
+ <VirtualKey
183
+ label="D"
184
+ subLabel="Wrist-"
185
+ isPressed={!!keyStates[controls.wrist_flex.negative]?.pressed}
186
+ />
187
+ </div>
188
+ <span className="font-bold text-sm font-sans">Elbow/Wrist</span>
189
+ </div>
190
+ <div className="flex flex-col items-center gap-2">
191
+ <div className="flex gap-2">
192
+ <VirtualKey
193
+ label="Q"
194
+ subLabel="Roll+"
195
+ isPressed={!!keyStates[controls.wrist_roll.positive]?.pressed}
196
+ />
197
+ <VirtualKey
198
+ label="E"
199
+ subLabel="Roll-"
200
+ isPressed={!!keyStates[controls.wrist_roll.negative]?.pressed}
201
+ />
202
+ </div>
203
+ <div className="flex gap-2">
204
+ <VirtualKey label="O" subLabel="Grip+" isPressed={!!keyStates[controls.gripper.positive]?.pressed} />
205
+ <VirtualKey label="C" subLabel="Grip-" isPressed={!!keyStates[controls.gripper.negative]?.pressed} />
206
+ </div>
207
+ <span className="font-bold text-sm font-sans">Roll/Grip</span>
208
+ </div>
209
+ </div>
210
+ <div className="pt-4 border-t border-white/10">
211
+ <div className="flex justify-between items-center font-mono text-sm">
212
+ <div className="flex items-center gap-2 text-muted-foreground">
213
+ <Keyboard className="w-4 h-4" />
214
+ <span>Active Keys: {Object.values(keyStates).filter((k) => k.pressed).length}</span>
215
+ </div>
216
+ <TooltipProvider>
217
+ <Tooltip>
218
+ <TooltipTrigger asChild>
219
+ <div
220
+ className={cn(
221
+ "w-10 h-6 border rounded-md flex items-center justify-center font-mono text-xs transition-all",
222
+ !!keyStates[controls.stop]?.pressed
223
+ ? "bg-destructive text-destructive-foreground border-destructive"
224
+ : "bg-background",
225
+ )}
226
+ >
227
+ ESC
228
+ </div>
229
+ </TooltipTrigger>
230
+ <TooltipContent>Emergency Stop</TooltipContent>
231
+ </Tooltip>
232
+ </TooltipProvider>
233
+ </div>
234
+ </div>
235
+ </div>
236
+ </div>
237
+ </div>
238
+ </Card>
239
+ )
240
+ }
examples/cyberpunk-standalone/src/components/theme-provider.tsx ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ ThemeProvider as NextThemesProvider,
3
+ type ThemeProviderProps,
4
+ } from "next-themes";
5
+
6
+ export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
7
+ return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
8
+ }
examples/cyberpunk-standalone/src/components/theme-toggle.tsx ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+ import { Moon, Sun } from "lucide-react"
3
+ import { useTheme } from "next-themes"
4
+ import { useEffect, useState } from "react"
5
+
6
+ import { Button } from "@/components/ui/button"
7
+
8
+ export function ThemeToggle() {
9
+ const { setTheme, resolvedTheme } = useTheme()
10
+ const [mounted, setMounted] = useState(false)
11
+
12
+ useEffect(() => {
13
+ setMounted(true)
14
+ }, [])
15
+
16
+ if (!mounted) {
17
+ return (
18
+ <Button variant="outline" size="icon">
19
+ <Sun className="h-[1.2rem] w-[1.2rem]" />
20
+ <span className="sr-only">Toggle theme</span>
21
+ </Button>
22
+ )
23
+ }
24
+
25
+ const handleToggle = () => {
26
+ setTheme(resolvedTheme === "dark" ? "light" : "dark")
27
+ }
28
+
29
+ return (
30
+ <Button variant="outline" size="icon" onClick={handleToggle}>
31
+ {resolvedTheme === "dark" ? (
32
+ <Sun className="h-[1.2rem] w-[1.2rem]" />
33
+ ) : (
34
+ <Moon className="h-[1.2rem] w-[1.2rem]" />
35
+ )}
36
+ <span className="sr-only">Toggle theme</span>
37
+ </Button>
38
+ )
39
+ }
examples/cyberpunk-standalone/src/components/ui/accordion.tsx ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as AccordionPrimitive from "@radix-ui/react-accordion"
5
+ import { ChevronDown } from "lucide-react"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ const Accordion = AccordionPrimitive.Root
10
+
11
+ const AccordionItem = React.forwardRef<
12
+ React.ElementRef<typeof AccordionPrimitive.Item>,
13
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
14
+ >(({ className, ...props }, ref) => (
15
+ <AccordionPrimitive.Item
16
+ ref={ref}
17
+ className={cn("border-b", className)}
18
+ {...props}
19
+ />
20
+ ))
21
+ AccordionItem.displayName = "AccordionItem"
22
+
23
+ const AccordionTrigger = React.forwardRef<
24
+ React.ElementRef<typeof AccordionPrimitive.Trigger>,
25
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
26
+ >(({ className, children, ...props }, ref) => (
27
+ <AccordionPrimitive.Header className="flex">
28
+ <AccordionPrimitive.Trigger
29
+ ref={ref}
30
+ className={cn(
31
+ "flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
32
+ className
33
+ )}
34
+ {...props}
35
+ >
36
+ {children}
37
+ <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
38
+ </AccordionPrimitive.Trigger>
39
+ </AccordionPrimitive.Header>
40
+ ))
41
+ AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
42
+
43
+ const AccordionContent = React.forwardRef<
44
+ React.ElementRef<typeof AccordionPrimitive.Content>,
45
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
46
+ >(({ className, children, ...props }, ref) => (
47
+ <AccordionPrimitive.Content
48
+ ref={ref}
49
+ className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
50
+ {...props}
51
+ >
52
+ <div className={cn("pb-4 pt-0", className)}>{children}</div>
53
+ </AccordionPrimitive.Content>
54
+ ))
55
+
56
+ AccordionContent.displayName = AccordionPrimitive.Content.displayName
57
+
58
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
examples/cyberpunk-standalone/src/components/ui/alert-dialog.tsx ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
5
+
6
+ import { cn } from "@/lib/utils"
7
+ import { buttonVariants } from "@/components/ui/button"
8
+
9
+ const AlertDialog = AlertDialogPrimitive.Root
10
+
11
+ const AlertDialogTrigger = AlertDialogPrimitive.Trigger
12
+
13
+ const AlertDialogPortal = AlertDialogPrimitive.Portal
14
+
15
+ const AlertDialogOverlay = React.forwardRef<
16
+ React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
17
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
18
+ >(({ className, ...props }, ref) => (
19
+ <AlertDialogPrimitive.Overlay
20
+ className={cn(
21
+ "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
22
+ className
23
+ )}
24
+ {...props}
25
+ ref={ref}
26
+ />
27
+ ))
28
+ AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
29
+
30
+ const AlertDialogContent = React.forwardRef<
31
+ React.ElementRef<typeof AlertDialogPrimitive.Content>,
32
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
33
+ >(({ className, ...props }, ref) => (
34
+ <AlertDialogPortal>
35
+ <AlertDialogOverlay />
36
+ <AlertDialogPrimitive.Content
37
+ ref={ref}
38
+ className={cn(
39
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
40
+ className
41
+ )}
42
+ {...props}
43
+ />
44
+ </AlertDialogPortal>
45
+ ))
46
+ AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
47
+
48
+ const AlertDialogHeader = ({
49
+ className,
50
+ ...props
51
+ }: React.HTMLAttributes<HTMLDivElement>) => (
52
+ <div
53
+ className={cn(
54
+ "flex flex-col space-y-2 text-center sm:text-left",
55
+ className
56
+ )}
57
+ {...props}
58
+ />
59
+ )
60
+ AlertDialogHeader.displayName = "AlertDialogHeader"
61
+
62
+ const AlertDialogFooter = ({
63
+ className,
64
+ ...props
65
+ }: React.HTMLAttributes<HTMLDivElement>) => (
66
+ <div
67
+ className={cn(
68
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
69
+ className
70
+ )}
71
+ {...props}
72
+ />
73
+ )
74
+ AlertDialogFooter.displayName = "AlertDialogFooter"
75
+
76
+ const AlertDialogTitle = React.forwardRef<
77
+ React.ElementRef<typeof AlertDialogPrimitive.Title>,
78
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
79
+ >(({ className, ...props }, ref) => (
80
+ <AlertDialogPrimitive.Title
81
+ ref={ref}
82
+ className={cn("text-lg font-semibold", className)}
83
+ {...props}
84
+ />
85
+ ))
86
+ AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
87
+
88
+ const AlertDialogDescription = React.forwardRef<
89
+ React.ElementRef<typeof AlertDialogPrimitive.Description>,
90
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
91
+ >(({ className, ...props }, ref) => (
92
+ <AlertDialogPrimitive.Description
93
+ ref={ref}
94
+ className={cn("text-sm text-muted-foreground", className)}
95
+ {...props}
96
+ />
97
+ ))
98
+ AlertDialogDescription.displayName =
99
+ AlertDialogPrimitive.Description.displayName
100
+
101
+ const AlertDialogAction = React.forwardRef<
102
+ React.ElementRef<typeof AlertDialogPrimitive.Action>,
103
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
104
+ >(({ className, ...props }, ref) => (
105
+ <AlertDialogPrimitive.Action
106
+ ref={ref}
107
+ className={cn(buttonVariants(), className)}
108
+ {...props}
109
+ />
110
+ ))
111
+ AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
112
+
113
+ const AlertDialogCancel = React.forwardRef<
114
+ React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
115
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
116
+ >(({ className, ...props }, ref) => (
117
+ <AlertDialogPrimitive.Cancel
118
+ ref={ref}
119
+ className={cn(
120
+ buttonVariants({ variant: "outline" }),
121
+ "mt-2 sm:mt-0",
122
+ className
123
+ )}
124
+ {...props}
125
+ />
126
+ ))
127
+ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
128
+
129
+ export {
130
+ AlertDialog,
131
+ AlertDialogPortal,
132
+ AlertDialogOverlay,
133
+ AlertDialogTrigger,
134
+ AlertDialogContent,
135
+ AlertDialogHeader,
136
+ AlertDialogFooter,
137
+ AlertDialogTitle,
138
+ AlertDialogDescription,
139
+ AlertDialogAction,
140
+ AlertDialogCancel,
141
+ }
examples/cyberpunk-standalone/src/components/ui/alert.tsx ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const alertVariants = cva(
7
+ "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-background text-foreground",
12
+ destructive:
13
+ "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ variant: "default",
18
+ },
19
+ }
20
+ )
21
+
22
+ const Alert = React.forwardRef<
23
+ HTMLDivElement,
24
+ React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
25
+ >(({ className, variant, ...props }, ref) => (
26
+ <div
27
+ ref={ref}
28
+ role="alert"
29
+ className={cn(alertVariants({ variant }), className)}
30
+ {...props}
31
+ />
32
+ ))
33
+ Alert.displayName = "Alert"
34
+
35
+ const AlertTitle = React.forwardRef<
36
+ HTMLParagraphElement,
37
+ React.HTMLAttributes<HTMLHeadingElement>
38
+ >(({ className, ...props }, ref) => (
39
+ <h5
40
+ ref={ref}
41
+ className={cn("mb-1 font-medium leading-none tracking-tight", className)}
42
+ {...props}
43
+ />
44
+ ))
45
+ AlertTitle.displayName = "AlertTitle"
46
+
47
+ const AlertDescription = React.forwardRef<
48
+ HTMLParagraphElement,
49
+ React.HTMLAttributes<HTMLParagraphElement>
50
+ >(({ className, ...props }, ref) => (
51
+ <div
52
+ ref={ref}
53
+ className={cn("text-sm [&_p]:leading-relaxed", className)}
54
+ {...props}
55
+ />
56
+ ))
57
+ AlertDescription.displayName = "AlertDescription"
58
+
59
+ export { Alert, AlertTitle, AlertDescription }
examples/cyberpunk-standalone/src/components/ui/aspect-ratio.tsx ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"
4
+
5
+ const AspectRatio = AspectRatioPrimitive.Root
6
+
7
+ export { AspectRatio }
examples/cyberpunk-standalone/src/components/ui/avatar.tsx ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as AvatarPrimitive from "@radix-ui/react-avatar"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const Avatar = React.forwardRef<
9
+ React.ElementRef<typeof AvatarPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
11
+ >(({ className, ...props }, ref) => (
12
+ <AvatarPrimitive.Root
13
+ ref={ref}
14
+ className={cn(
15
+ "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
16
+ className
17
+ )}
18
+ {...props}
19
+ />
20
+ ))
21
+ Avatar.displayName = AvatarPrimitive.Root.displayName
22
+
23
+ const AvatarImage = React.forwardRef<
24
+ React.ElementRef<typeof AvatarPrimitive.Image>,
25
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
26
+ >(({ className, ...props }, ref) => (
27
+ <AvatarPrimitive.Image
28
+ ref={ref}
29
+ className={cn("aspect-square h-full w-full", className)}
30
+ {...props}
31
+ />
32
+ ))
33
+ AvatarImage.displayName = AvatarPrimitive.Image.displayName
34
+
35
+ const AvatarFallback = React.forwardRef<
36
+ React.ElementRef<typeof AvatarPrimitive.Fallback>,
37
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
38
+ >(({ className, ...props }, ref) => (
39
+ <AvatarPrimitive.Fallback
40
+ ref={ref}
41
+ className={cn(
42
+ "flex h-full w-full items-center justify-center rounded-full bg-muted",
43
+ className
44
+ )}
45
+ {...props}
46
+ />
47
+ ))
48
+ AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
49
+
50
+ export { Avatar, AvatarImage, AvatarFallback }
examples/cyberpunk-standalone/src/components/ui/badge.tsx ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const badgeVariants = cva(
7
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default:
12
+ "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
13
+ secondary:
14
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15
+ destructive:
16
+ "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
17
+ outline: "text-foreground",
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ variant: "default",
22
+ },
23
+ }
24
+ )
25
+
26
+ export interface BadgeProps
27
+ extends React.HTMLAttributes<HTMLDivElement>,
28
+ VariantProps<typeof badgeVariants> {}
29
+
30
+ function Badge({ className, variant, ...props }: BadgeProps) {
31
+ return (
32
+ <div className={cn(badgeVariants({ variant }), className)} {...props} />
33
+ )
34
+ }
35
+
36
+ export { Badge, badgeVariants }
examples/cyberpunk-standalone/src/components/ui/breadcrumb.tsx ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { ChevronRight, MoreHorizontal } from "lucide-react"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const Breadcrumb = React.forwardRef<
8
+ HTMLElement,
9
+ React.ComponentPropsWithoutRef<"nav"> & {
10
+ separator?: React.ReactNode
11
+ }
12
+ >(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />)
13
+ Breadcrumb.displayName = "Breadcrumb"
14
+
15
+ const BreadcrumbList = React.forwardRef<
16
+ HTMLOListElement,
17
+ React.ComponentPropsWithoutRef<"ol">
18
+ >(({ className, ...props }, ref) => (
19
+ <ol
20
+ ref={ref}
21
+ className={cn(
22
+ "flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5",
23
+ className
24
+ )}
25
+ {...props}
26
+ />
27
+ ))
28
+ BreadcrumbList.displayName = "BreadcrumbList"
29
+
30
+ const BreadcrumbItem = React.forwardRef<
31
+ HTMLLIElement,
32
+ React.ComponentPropsWithoutRef<"li">
33
+ >(({ className, ...props }, ref) => (
34
+ <li
35
+ ref={ref}
36
+ className={cn("inline-flex items-center gap-1.5", className)}
37
+ {...props}
38
+ />
39
+ ))
40
+ BreadcrumbItem.displayName = "BreadcrumbItem"
41
+
42
+ const BreadcrumbLink = React.forwardRef<
43
+ HTMLAnchorElement,
44
+ React.ComponentPropsWithoutRef<"a"> & {
45
+ asChild?: boolean
46
+ }
47
+ >(({ asChild, className, ...props }, ref) => {
48
+ const Comp = asChild ? Slot : "a"
49
+
50
+ return (
51
+ <Comp
52
+ ref={ref}
53
+ className={cn("transition-colors hover:text-foreground", className)}
54
+ {...props}
55
+ />
56
+ )
57
+ })
58
+ BreadcrumbLink.displayName = "BreadcrumbLink"
59
+
60
+ const BreadcrumbPage = React.forwardRef<
61
+ HTMLSpanElement,
62
+ React.ComponentPropsWithoutRef<"span">
63
+ >(({ className, ...props }, ref) => (
64
+ <span
65
+ ref={ref}
66
+ role="link"
67
+ aria-disabled="true"
68
+ aria-current="page"
69
+ className={cn("font-normal text-foreground", className)}
70
+ {...props}
71
+ />
72
+ ))
73
+ BreadcrumbPage.displayName = "BreadcrumbPage"
74
+
75
+ const BreadcrumbSeparator = ({
76
+ children,
77
+ className,
78
+ ...props
79
+ }: React.ComponentProps<"li">) => (
80
+ <li
81
+ role="presentation"
82
+ aria-hidden="true"
83
+ className={cn("[&>svg]:w-3.5 [&>svg]:h-3.5", className)}
84
+ {...props}
85
+ >
86
+ {children ?? <ChevronRight />}
87
+ </li>
88
+ )
89
+ BreadcrumbSeparator.displayName = "BreadcrumbSeparator"
90
+
91
+ const BreadcrumbEllipsis = ({
92
+ className,
93
+ ...props
94
+ }: React.ComponentProps<"span">) => (
95
+ <span
96
+ role="presentation"
97
+ aria-hidden="true"
98
+ className={cn("flex h-9 w-9 items-center justify-center", className)}
99
+ {...props}
100
+ >
101
+ <MoreHorizontal className="h-4 w-4" />
102
+ <span className="sr-only">More</span>
103
+ </span>
104
+ )
105
+ BreadcrumbEllipsis.displayName = "BreadcrumbElipssis"
106
+
107
+ export {
108
+ Breadcrumb,
109
+ BreadcrumbList,
110
+ BreadcrumbItem,
111
+ BreadcrumbLink,
112
+ BreadcrumbPage,
113
+ BreadcrumbSeparator,
114
+ BreadcrumbEllipsis,
115
+ }
examples/cyberpunk-standalone/src/components/ui/button.tsx ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const buttonVariants = cva(
8
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
13
+ destructive:
14
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90",
15
+ outline:
16
+ "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
17
+ secondary:
18
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19
+ ghost: "hover:bg-accent hover:text-accent-foreground",
20
+ link: "text-primary underline-offset-4 hover:underline",
21
+ },
22
+ size: {
23
+ default: "h-10 px-4 py-2",
24
+ sm: "h-9 rounded-md px-3",
25
+ lg: "h-11 rounded-md px-8",
26
+ icon: "h-10 w-10",
27
+ },
28
+ },
29
+ defaultVariants: {
30
+ variant: "default",
31
+ size: "default",
32
+ },
33
+ }
34
+ )
35
+
36
+ export interface ButtonProps
37
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
38
+ VariantProps<typeof buttonVariants> {
39
+ asChild?: boolean
40
+ }
41
+
42
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
43
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
44
+ const Comp = asChild ? Slot : "button"
45
+ return (
46
+ <Comp
47
+ className={cn(buttonVariants({ variant, size, className }))}
48
+ ref={ref}
49
+ {...props}
50
+ />
51
+ )
52
+ }
53
+ )
54
+ Button.displayName = "Button"
55
+
56
+ export { Button, buttonVariants }
examples/cyberpunk-standalone/src/components/ui/calendar.tsx ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { ChevronLeft, ChevronRight } from "lucide-react"
5
+ import { DayPicker } from "react-day-picker"
6
+
7
+ import { cn } from "@/lib/utils"
8
+ import { buttonVariants } from "@/components/ui/button"
9
+
10
+ export type CalendarProps = React.ComponentProps<typeof DayPicker>
11
+
12
+ function Calendar({
13
+ className,
14
+ classNames,
15
+ showOutsideDays = true,
16
+ ...props
17
+ }: CalendarProps) {
18
+ return (
19
+ <DayPicker
20
+ showOutsideDays={showOutsideDays}
21
+ className={cn("p-3", className)}
22
+ classNames={{
23
+ months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
24
+ month: "space-y-4",
25
+ caption: "flex justify-center pt-1 relative items-center",
26
+ caption_label: "text-sm font-medium",
27
+ nav: "space-x-1 flex items-center",
28
+ nav_button: cn(
29
+ buttonVariants({ variant: "outline" }),
30
+ "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
31
+ ),
32
+ nav_button_previous: "absolute left-1",
33
+ nav_button_next: "absolute right-1",
34
+ table: "w-full border-collapse space-y-1",
35
+ head_row: "flex",
36
+ head_cell:
37
+ "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]",
38
+ row: "flex w-full mt-2",
39
+ cell: "h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20",
40
+ day: cn(
41
+ buttonVariants({ variant: "ghost" }),
42
+ "h-9 w-9 p-0 font-normal aria-selected:opacity-100"
43
+ ),
44
+ day_range_end: "day-range-end",
45
+ day_selected:
46
+ "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
47
+ day_today: "bg-accent text-accent-foreground",
48
+ day_outside:
49
+ "day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground",
50
+ day_disabled: "text-muted-foreground opacity-50",
51
+ day_range_middle:
52
+ "aria-selected:bg-accent aria-selected:text-accent-foreground",
53
+ day_hidden: "invisible",
54
+ ...classNames,
55
+ }}
56
+ components={{
57
+ IconLeft: ({ ...props }) => <ChevronLeft className="h-4 w-4" />,
58
+ IconRight: ({ ...props }) => <ChevronRight className="h-4 w-4" />,
59
+ }}
60
+ {...props}
61
+ />
62
+ )
63
+ }
64
+ Calendar.displayName = "Calendar"
65
+
66
+ export { Calendar }
examples/cyberpunk-standalone/src/components/ui/card.tsx ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
6
+ <div
7
+ ref={ref}
8
+ className={cn(
9
+ "rounded-lg border bg-card text-card-foreground shadow-sm",
10
+ "relative bg-card/60 border-white/10",
11
+ className,
12
+ )}
13
+ >
14
+ <div className="absolute inset-0 bg-hex-pattern opacity-50"></div>
15
+ <div className="relative z-10 h-full">{props.children}</div>
16
+ </div>
17
+ ))
18
+ Card.displayName = "Card"
19
+
20
+ const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
21
+ ({ className, ...props }, ref) => (
22
+ <div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
23
+ ),
24
+ )
25
+ CardHeader.displayName = "CardHeader"
26
+
27
+ const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
28
+ ({ className, ...props }, ref) => (
29
+ <h3 ref={ref} className={cn("text-2xl font-semibold leading-none tracking-tight", className)} {...props} />
30
+ ),
31
+ )
32
+ CardTitle.displayName = "CardTitle"
33
+
34
+ const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
35
+ ({ className, ...props }, ref) => (
36
+ <p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
37
+ ),
38
+ )
39
+ CardDescription.displayName = "CardDescription"
40
+
41
+ const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
42
+ ({ className, ...props }, ref) => (
43
+ <div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
44
+ ),
45
+ )
46
+ CardFooter.displayName = "CardFooter"
47
+
48
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription }
examples/cyberpunk-standalone/src/components/ui/carousel.tsx ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import useEmblaCarousel, {
5
+ type UseEmblaCarouselType,
6
+ } from "embla-carousel-react"
7
+ import { ArrowLeft, ArrowRight } from "lucide-react"
8
+
9
+ import { cn } from "@/lib/utils"
10
+ import { Button } from "@/components/ui/button"
11
+
12
+ type CarouselApi = UseEmblaCarouselType[1]
13
+ type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
14
+ type CarouselOptions = UseCarouselParameters[0]
15
+ type CarouselPlugin = UseCarouselParameters[1]
16
+
17
+ type CarouselProps = {
18
+ opts?: CarouselOptions
19
+ plugins?: CarouselPlugin
20
+ orientation?: "horizontal" | "vertical"
21
+ setApi?: (api: CarouselApi) => void
22
+ }
23
+
24
+ type CarouselContextProps = {
25
+ carouselRef: ReturnType<typeof useEmblaCarousel>[0]
26
+ api: ReturnType<typeof useEmblaCarousel>[1]
27
+ scrollPrev: () => void
28
+ scrollNext: () => void
29
+ canScrollPrev: boolean
30
+ canScrollNext: boolean
31
+ } & CarouselProps
32
+
33
+ const CarouselContext = React.createContext<CarouselContextProps | null>(null)
34
+
35
+ function useCarousel() {
36
+ const context = React.useContext(CarouselContext)
37
+
38
+ if (!context) {
39
+ throw new Error("useCarousel must be used within a <Carousel />")
40
+ }
41
+
42
+ return context
43
+ }
44
+
45
+ const Carousel = React.forwardRef<
46
+ HTMLDivElement,
47
+ React.HTMLAttributes<HTMLDivElement> & CarouselProps
48
+ >(
49
+ (
50
+ {
51
+ orientation = "horizontal",
52
+ opts,
53
+ setApi,
54
+ plugins,
55
+ className,
56
+ children,
57
+ ...props
58
+ },
59
+ ref
60
+ ) => {
61
+ const [carouselRef, api] = useEmblaCarousel(
62
+ {
63
+ ...opts,
64
+ axis: orientation === "horizontal" ? "x" : "y",
65
+ },
66
+ plugins
67
+ )
68
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false)
69
+ const [canScrollNext, setCanScrollNext] = React.useState(false)
70
+
71
+ const onSelect = React.useCallback((api: CarouselApi) => {
72
+ if (!api) {
73
+ return
74
+ }
75
+
76
+ setCanScrollPrev(api.canScrollPrev())
77
+ setCanScrollNext(api.canScrollNext())
78
+ }, [])
79
+
80
+ const scrollPrev = React.useCallback(() => {
81
+ api?.scrollPrev()
82
+ }, [api])
83
+
84
+ const scrollNext = React.useCallback(() => {
85
+ api?.scrollNext()
86
+ }, [api])
87
+
88
+ const handleKeyDown = React.useCallback(
89
+ (event: React.KeyboardEvent<HTMLDivElement>) => {
90
+ if (event.key === "ArrowLeft") {
91
+ event.preventDefault()
92
+ scrollPrev()
93
+ } else if (event.key === "ArrowRight") {
94
+ event.preventDefault()
95
+ scrollNext()
96
+ }
97
+ },
98
+ [scrollPrev, scrollNext]
99
+ )
100
+
101
+ React.useEffect(() => {
102
+ if (!api || !setApi) {
103
+ return
104
+ }
105
+
106
+ setApi(api)
107
+ }, [api, setApi])
108
+
109
+ React.useEffect(() => {
110
+ if (!api) {
111
+ return
112
+ }
113
+
114
+ onSelect(api)
115
+ api.on("reInit", onSelect)
116
+ api.on("select", onSelect)
117
+
118
+ return () => {
119
+ api?.off("select", onSelect)
120
+ }
121
+ }, [api, onSelect])
122
+
123
+ return (
124
+ <CarouselContext.Provider
125
+ value={{
126
+ carouselRef,
127
+ api: api,
128
+ opts,
129
+ orientation:
130
+ orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
131
+ scrollPrev,
132
+ scrollNext,
133
+ canScrollPrev,
134
+ canScrollNext,
135
+ }}
136
+ >
137
+ <div
138
+ ref={ref}
139
+ onKeyDownCapture={handleKeyDown}
140
+ className={cn("relative", className)}
141
+ role="region"
142
+ aria-roledescription="carousel"
143
+ {...props}
144
+ >
145
+ {children}
146
+ </div>
147
+ </CarouselContext.Provider>
148
+ )
149
+ }
150
+ )
151
+ Carousel.displayName = "Carousel"
152
+
153
+ const CarouselContent = React.forwardRef<
154
+ HTMLDivElement,
155
+ React.HTMLAttributes<HTMLDivElement>
156
+ >(({ className, ...props }, ref) => {
157
+ const { carouselRef, orientation } = useCarousel()
158
+
159
+ return (
160
+ <div ref={carouselRef} className="overflow-hidden">
161
+ <div
162
+ ref={ref}
163
+ className={cn(
164
+ "flex",
165
+ orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
166
+ className
167
+ )}
168
+ {...props}
169
+ />
170
+ </div>
171
+ )
172
+ })
173
+ CarouselContent.displayName = "CarouselContent"
174
+
175
+ const CarouselItem = React.forwardRef<
176
+ HTMLDivElement,
177
+ React.HTMLAttributes<HTMLDivElement>
178
+ >(({ className, ...props }, ref) => {
179
+ const { orientation } = useCarousel()
180
+
181
+ return (
182
+ <div
183
+ ref={ref}
184
+ role="group"
185
+ aria-roledescription="slide"
186
+ className={cn(
187
+ "min-w-0 shrink-0 grow-0 basis-full",
188
+ orientation === "horizontal" ? "pl-4" : "pt-4",
189
+ className
190
+ )}
191
+ {...props}
192
+ />
193
+ )
194
+ })
195
+ CarouselItem.displayName = "CarouselItem"
196
+
197
+ const CarouselPrevious = React.forwardRef<
198
+ HTMLButtonElement,
199
+ React.ComponentProps<typeof Button>
200
+ >(({ className, variant = "outline", size = "icon", ...props }, ref) => {
201
+ const { orientation, scrollPrev, canScrollPrev } = useCarousel()
202
+
203
+ return (
204
+ <Button
205
+ ref={ref}
206
+ variant={variant}
207
+ size={size}
208
+ className={cn(
209
+ "absolute h-8 w-8 rounded-full",
210
+ orientation === "horizontal"
211
+ ? "-left-12 top-1/2 -translate-y-1/2"
212
+ : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
213
+ className
214
+ )}
215
+ disabled={!canScrollPrev}
216
+ onClick={scrollPrev}
217
+ {...props}
218
+ >
219
+ <ArrowLeft className="h-4 w-4" />
220
+ <span className="sr-only">Previous slide</span>
221
+ </Button>
222
+ )
223
+ })
224
+ CarouselPrevious.displayName = "CarouselPrevious"
225
+
226
+ const CarouselNext = React.forwardRef<
227
+ HTMLButtonElement,
228
+ React.ComponentProps<typeof Button>
229
+ >(({ className, variant = "outline", size = "icon", ...props }, ref) => {
230
+ const { orientation, scrollNext, canScrollNext } = useCarousel()
231
+
232
+ return (
233
+ <Button
234
+ ref={ref}
235
+ variant={variant}
236
+ size={size}
237
+ className={cn(
238
+ "absolute h-8 w-8 rounded-full",
239
+ orientation === "horizontal"
240
+ ? "-right-12 top-1/2 -translate-y-1/2"
241
+ : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
242
+ className
243
+ )}
244
+ disabled={!canScrollNext}
245
+ onClick={scrollNext}
246
+ {...props}
247
+ >
248
+ <ArrowRight className="h-4 w-4" />
249
+ <span className="sr-only">Next slide</span>
250
+ </Button>
251
+ )
252
+ })
253
+ CarouselNext.displayName = "CarouselNext"
254
+
255
+ export {
256
+ type CarouselApi,
257
+ Carousel,
258
+ CarouselContent,
259
+ CarouselItem,
260
+ CarouselPrevious,
261
+ CarouselNext,
262
+ }
examples/cyberpunk-standalone/src/components/ui/chart.tsx ADDED
@@ -0,0 +1,365 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as RechartsPrimitive from "recharts"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ // Format: { THEME_NAME: CSS_SELECTOR }
9
+ const THEMES = { light: "", dark: ".dark" } as const
10
+
11
+ export type ChartConfig = {
12
+ [k in string]: {
13
+ label?: React.ReactNode
14
+ icon?: React.ComponentType
15
+ } & (
16
+ | { color?: string; theme?: never }
17
+ | { color?: never; theme: Record<keyof typeof THEMES, string> }
18
+ )
19
+ }
20
+
21
+ type ChartContextProps = {
22
+ config: ChartConfig
23
+ }
24
+
25
+ const ChartContext = React.createContext<ChartContextProps | null>(null)
26
+
27
+ function useChart() {
28
+ const context = React.useContext(ChartContext)
29
+
30
+ if (!context) {
31
+ throw new Error("useChart must be used within a <ChartContainer />")
32
+ }
33
+
34
+ return context
35
+ }
36
+
37
+ const ChartContainer = React.forwardRef<
38
+ HTMLDivElement,
39
+ React.ComponentProps<"div"> & {
40
+ config: ChartConfig
41
+ children: React.ComponentProps<
42
+ typeof RechartsPrimitive.ResponsiveContainer
43
+ >["children"]
44
+ }
45
+ >(({ id, className, children, config, ...props }, ref) => {
46
+ const uniqueId = React.useId()
47
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
48
+
49
+ return (
50
+ <ChartContext.Provider value={{ config }}>
51
+ <div
52
+ data-chart={chartId}
53
+ ref={ref}
54
+ className={cn(
55
+ "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
56
+ className
57
+ )}
58
+ {...props}
59
+ >
60
+ <ChartStyle id={chartId} config={config} />
61
+ <RechartsPrimitive.ResponsiveContainer>
62
+ {children}
63
+ </RechartsPrimitive.ResponsiveContainer>
64
+ </div>
65
+ </ChartContext.Provider>
66
+ )
67
+ })
68
+ ChartContainer.displayName = "Chart"
69
+
70
+ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
71
+ const colorConfig = Object.entries(config).filter(
72
+ ([_, config]) => config.theme || config.color
73
+ )
74
+
75
+ if (!colorConfig.length) {
76
+ return null
77
+ }
78
+
79
+ return (
80
+ <style
81
+ dangerouslySetInnerHTML={{
82
+ __html: Object.entries(THEMES)
83
+ .map(
84
+ ([theme, prefix]) => `
85
+ ${prefix} [data-chart=${id}] {
86
+ ${colorConfig
87
+ .map(([key, itemConfig]) => {
88
+ const color =
89
+ itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
90
+ itemConfig.color
91
+ return color ? ` --color-${key}: ${color};` : null
92
+ })
93
+ .join("\n")}
94
+ }
95
+ `
96
+ )
97
+ .join("\n"),
98
+ }}
99
+ />
100
+ )
101
+ }
102
+
103
+ const ChartTooltip = RechartsPrimitive.Tooltip
104
+
105
+ const ChartTooltipContent = React.forwardRef<
106
+ HTMLDivElement,
107
+ React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
108
+ React.ComponentProps<"div"> & {
109
+ hideLabel?: boolean
110
+ hideIndicator?: boolean
111
+ indicator?: "line" | "dot" | "dashed"
112
+ nameKey?: string
113
+ labelKey?: string
114
+ }
115
+ >(
116
+ (
117
+ {
118
+ active,
119
+ payload,
120
+ className,
121
+ indicator = "dot",
122
+ hideLabel = false,
123
+ hideIndicator = false,
124
+ label,
125
+ labelFormatter,
126
+ labelClassName,
127
+ formatter,
128
+ color,
129
+ nameKey,
130
+ labelKey,
131
+ },
132
+ ref
133
+ ) => {
134
+ const { config } = useChart()
135
+
136
+ const tooltipLabel = React.useMemo(() => {
137
+ if (hideLabel || !payload?.length) {
138
+ return null
139
+ }
140
+
141
+ const [item] = payload
142
+ const key = `${labelKey || item.dataKey || item.name || "value"}`
143
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
144
+ const value =
145
+ !labelKey && typeof label === "string"
146
+ ? config[label as keyof typeof config]?.label || label
147
+ : itemConfig?.label
148
+
149
+ if (labelFormatter) {
150
+ return (
151
+ <div className={cn("font-medium", labelClassName)}>
152
+ {labelFormatter(value, payload)}
153
+ </div>
154
+ )
155
+ }
156
+
157
+ if (!value) {
158
+ return null
159
+ }
160
+
161
+ return <div className={cn("font-medium", labelClassName)}>{value}</div>
162
+ }, [
163
+ label,
164
+ labelFormatter,
165
+ payload,
166
+ hideLabel,
167
+ labelClassName,
168
+ config,
169
+ labelKey,
170
+ ])
171
+
172
+ if (!active || !payload?.length) {
173
+ return null
174
+ }
175
+
176
+ const nestLabel = payload.length === 1 && indicator !== "dot"
177
+
178
+ return (
179
+ <div
180
+ ref={ref}
181
+ className={cn(
182
+ "grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
183
+ className
184
+ )}
185
+ >
186
+ {!nestLabel ? tooltipLabel : null}
187
+ <div className="grid gap-1.5">
188
+ {payload.map((item, index) => {
189
+ const key = `${nameKey || item.name || item.dataKey || "value"}`
190
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
191
+ const indicatorColor = color || item.payload.fill || item.color
192
+
193
+ return (
194
+ <div
195
+ key={item.dataKey}
196
+ className={cn(
197
+ "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
198
+ indicator === "dot" && "items-center"
199
+ )}
200
+ >
201
+ {formatter && item?.value !== undefined && item.name ? (
202
+ formatter(item.value, item.name, item, index, item.payload)
203
+ ) : (
204
+ <>
205
+ {itemConfig?.icon ? (
206
+ <itemConfig.icon />
207
+ ) : (
208
+ !hideIndicator && (
209
+ <div
210
+ className={cn(
211
+ "shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
212
+ {
213
+ "h-2.5 w-2.5": indicator === "dot",
214
+ "w-1": indicator === "line",
215
+ "w-0 border-[1.5px] border-dashed bg-transparent":
216
+ indicator === "dashed",
217
+ "my-0.5": nestLabel && indicator === "dashed",
218
+ }
219
+ )}
220
+ style={
221
+ {
222
+ "--color-bg": indicatorColor,
223
+ "--color-border": indicatorColor,
224
+ } as React.CSSProperties
225
+ }
226
+ />
227
+ )
228
+ )}
229
+ <div
230
+ className={cn(
231
+ "flex flex-1 justify-between leading-none",
232
+ nestLabel ? "items-end" : "items-center"
233
+ )}
234
+ >
235
+ <div className="grid gap-1.5">
236
+ {nestLabel ? tooltipLabel : null}
237
+ <span className="text-muted-foreground">
238
+ {itemConfig?.label || item.name}
239
+ </span>
240
+ </div>
241
+ {item.value && (
242
+ <span className="font-mono font-medium tabular-nums text-foreground">
243
+ {item.value.toLocaleString()}
244
+ </span>
245
+ )}
246
+ </div>
247
+ </>
248
+ )}
249
+ </div>
250
+ )
251
+ })}
252
+ </div>
253
+ </div>
254
+ )
255
+ }
256
+ )
257
+ ChartTooltipContent.displayName = "ChartTooltip"
258
+
259
+ const ChartLegend = RechartsPrimitive.Legend
260
+
261
+ const ChartLegendContent = React.forwardRef<
262
+ HTMLDivElement,
263
+ React.ComponentProps<"div"> &
264
+ Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
265
+ hideIcon?: boolean
266
+ nameKey?: string
267
+ }
268
+ >(
269
+ (
270
+ { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
271
+ ref
272
+ ) => {
273
+ const { config } = useChart()
274
+
275
+ if (!payload?.length) {
276
+ return null
277
+ }
278
+
279
+ return (
280
+ <div
281
+ ref={ref}
282
+ className={cn(
283
+ "flex items-center justify-center gap-4",
284
+ verticalAlign === "top" ? "pb-3" : "pt-3",
285
+ className
286
+ )}
287
+ >
288
+ {payload.map((item) => {
289
+ const key = `${nameKey || item.dataKey || "value"}`
290
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
291
+
292
+ return (
293
+ <div
294
+ key={item.value}
295
+ className={cn(
296
+ "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
297
+ )}
298
+ >
299
+ {itemConfig?.icon && !hideIcon ? (
300
+ <itemConfig.icon />
301
+ ) : (
302
+ <div
303
+ className="h-2 w-2 shrink-0 rounded-[2px]"
304
+ style={{
305
+ backgroundColor: item.color,
306
+ }}
307
+ />
308
+ )}
309
+ {itemConfig?.label}
310
+ </div>
311
+ )
312
+ })}
313
+ </div>
314
+ )
315
+ }
316
+ )
317
+ ChartLegendContent.displayName = "ChartLegend"
318
+
319
+ // Helper to extract item config from a payload.
320
+ function getPayloadConfigFromPayload(
321
+ config: ChartConfig,
322
+ payload: unknown,
323
+ key: string
324
+ ) {
325
+ if (typeof payload !== "object" || payload === null) {
326
+ return undefined
327
+ }
328
+
329
+ const payloadPayload =
330
+ "payload" in payload &&
331
+ typeof payload.payload === "object" &&
332
+ payload.payload !== null
333
+ ? payload.payload
334
+ : undefined
335
+
336
+ let configLabelKey: string = key
337
+
338
+ if (
339
+ key in payload &&
340
+ typeof payload[key as keyof typeof payload] === "string"
341
+ ) {
342
+ configLabelKey = payload[key as keyof typeof payload] as string
343
+ } else if (
344
+ payloadPayload &&
345
+ key in payloadPayload &&
346
+ typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
347
+ ) {
348
+ configLabelKey = payloadPayload[
349
+ key as keyof typeof payloadPayload
350
+ ] as string
351
+ }
352
+
353
+ return configLabelKey in config
354
+ ? config[configLabelKey]
355
+ : config[key as keyof typeof config]
356
+ }
357
+
358
+ export {
359
+ ChartContainer,
360
+ ChartTooltip,
361
+ ChartTooltipContent,
362
+ ChartLegend,
363
+ ChartLegendContent,
364
+ ChartStyle,
365
+ }
examples/cyberpunk-standalone/src/components/ui/checkbox.tsx ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
5
+ import { Check } from "lucide-react"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ const Checkbox = React.forwardRef<
10
+ React.ElementRef<typeof CheckboxPrimitive.Root>,
11
+ React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
12
+ >(({ className, ...props }, ref) => (
13
+ <CheckboxPrimitive.Root
14
+ ref={ref}
15
+ className={cn(
16
+ "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
17
+ className
18
+ )}
19
+ {...props}
20
+ >
21
+ <CheckboxPrimitive.Indicator
22
+ className={cn("flex items-center justify-center text-current")}
23
+ >
24
+ <Check className="h-4 w-4" />
25
+ </CheckboxPrimitive.Indicator>
26
+ </CheckboxPrimitive.Root>
27
+ ))
28
+ Checkbox.displayName = CheckboxPrimitive.Root.displayName
29
+
30
+ export { Checkbox }
examples/cyberpunk-standalone/src/components/ui/collapsible.tsx ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
4
+
5
+ const Collapsible = CollapsiblePrimitive.Root
6
+
7
+ const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
8
+
9
+ const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
10
+
11
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent }
examples/cyberpunk-standalone/src/components/ui/command.tsx ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { type DialogProps } from "@radix-ui/react-dialog"
5
+ import { Command as CommandPrimitive } from "cmdk"
6
+ import { Search } from "lucide-react"
7
+
8
+ import { cn } from "@/lib/utils"
9
+ import { Dialog, DialogContent } from "@/components/ui/dialog"
10
+
11
+ const Command = React.forwardRef<
12
+ React.ElementRef<typeof CommandPrimitive>,
13
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive>
14
+ >(({ className, ...props }, ref) => (
15
+ <CommandPrimitive
16
+ ref={ref}
17
+ className={cn(
18
+ "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
19
+ className
20
+ )}
21
+ {...props}
22
+ />
23
+ ))
24
+ Command.displayName = CommandPrimitive.displayName
25
+
26
+ const CommandDialog = ({ children, ...props }: DialogProps) => {
27
+ return (
28
+ <Dialog {...props}>
29
+ <DialogContent className="overflow-hidden p-0 shadow-lg">
30
+ <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
31
+ {children}
32
+ </Command>
33
+ </DialogContent>
34
+ </Dialog>
35
+ )
36
+ }
37
+
38
+ const CommandInput = React.forwardRef<
39
+ React.ElementRef<typeof CommandPrimitive.Input>,
40
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
41
+ >(({ className, ...props }, ref) => (
42
+ <div className="flex items-center border-b px-3" cmdk-input-wrapper="">
43
+ <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
44
+ <CommandPrimitive.Input
45
+ ref={ref}
46
+ className={cn(
47
+ "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
48
+ className
49
+ )}
50
+ {...props}
51
+ />
52
+ </div>
53
+ ))
54
+
55
+ CommandInput.displayName = CommandPrimitive.Input.displayName
56
+
57
+ const CommandList = React.forwardRef<
58
+ React.ElementRef<typeof CommandPrimitive.List>,
59
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
60
+ >(({ className, ...props }, ref) => (
61
+ <CommandPrimitive.List
62
+ ref={ref}
63
+ className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
64
+ {...props}
65
+ />
66
+ ))
67
+
68
+ CommandList.displayName = CommandPrimitive.List.displayName
69
+
70
+ const CommandEmpty = React.forwardRef<
71
+ React.ElementRef<typeof CommandPrimitive.Empty>,
72
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
73
+ >((props, ref) => (
74
+ <CommandPrimitive.Empty
75
+ ref={ref}
76
+ className="py-6 text-center text-sm"
77
+ {...props}
78
+ />
79
+ ))
80
+
81
+ CommandEmpty.displayName = CommandPrimitive.Empty.displayName
82
+
83
+ const CommandGroup = React.forwardRef<
84
+ React.ElementRef<typeof CommandPrimitive.Group>,
85
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
86
+ >(({ className, ...props }, ref) => (
87
+ <CommandPrimitive.Group
88
+ ref={ref}
89
+ className={cn(
90
+ "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
91
+ className
92
+ )}
93
+ {...props}
94
+ />
95
+ ))
96
+
97
+ CommandGroup.displayName = CommandPrimitive.Group.displayName
98
+
99
+ const CommandSeparator = React.forwardRef<
100
+ React.ElementRef<typeof CommandPrimitive.Separator>,
101
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
102
+ >(({ className, ...props }, ref) => (
103
+ <CommandPrimitive.Separator
104
+ ref={ref}
105
+ className={cn("-mx-1 h-px bg-border", className)}
106
+ {...props}
107
+ />
108
+ ))
109
+ CommandSeparator.displayName = CommandPrimitive.Separator.displayName
110
+
111
+ const CommandItem = React.forwardRef<
112
+ React.ElementRef<typeof CommandPrimitive.Item>,
113
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
114
+ >(({ className, ...props }, ref) => (
115
+ <CommandPrimitive.Item
116
+ ref={ref}
117
+ className={cn(
118
+ "relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected='true']:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
119
+ className
120
+ )}
121
+ {...props}
122
+ />
123
+ ))
124
+
125
+ CommandItem.displayName = CommandPrimitive.Item.displayName
126
+
127
+ const CommandShortcut = ({
128
+ className,
129
+ ...props
130
+ }: React.HTMLAttributes<HTMLSpanElement>) => {
131
+ return (
132
+ <span
133
+ className={cn(
134
+ "ml-auto text-xs tracking-widest text-muted-foreground",
135
+ className
136
+ )}
137
+ {...props}
138
+ />
139
+ )
140
+ }
141
+ CommandShortcut.displayName = "CommandShortcut"
142
+
143
+ export {
144
+ Command,
145
+ CommandDialog,
146
+ CommandInput,
147
+ CommandList,
148
+ CommandEmpty,
149
+ CommandGroup,
150
+ CommandItem,
151
+ CommandShortcut,
152
+ CommandSeparator,
153
+ }
examples/cyberpunk-standalone/src/components/ui/context-menu.tsx ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"
5
+ import { Check, ChevronRight, Circle } from "lucide-react"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ const ContextMenu = ContextMenuPrimitive.Root
10
+
11
+ const ContextMenuTrigger = ContextMenuPrimitive.Trigger
12
+
13
+ const ContextMenuGroup = ContextMenuPrimitive.Group
14
+
15
+ const ContextMenuPortal = ContextMenuPrimitive.Portal
16
+
17
+ const ContextMenuSub = ContextMenuPrimitive.Sub
18
+
19
+ const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup
20
+
21
+ const ContextMenuSubTrigger = React.forwardRef<
22
+ React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
23
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
24
+ inset?: boolean
25
+ }
26
+ >(({ className, inset, children, ...props }, ref) => (
27
+ <ContextMenuPrimitive.SubTrigger
28
+ ref={ref}
29
+ className={cn(
30
+ "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
31
+ inset && "pl-8",
32
+ className
33
+ )}
34
+ {...props}
35
+ >
36
+ {children}
37
+ <ChevronRight className="ml-auto h-4 w-4" />
38
+ </ContextMenuPrimitive.SubTrigger>
39
+ ))
40
+ ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName
41
+
42
+ const ContextMenuSubContent = React.forwardRef<
43
+ React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
44
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>
45
+ >(({ className, ...props }, ref) => (
46
+ <ContextMenuPrimitive.SubContent
47
+ ref={ref}
48
+ className={cn(
49
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
50
+ className
51
+ )}
52
+ {...props}
53
+ />
54
+ ))
55
+ ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName
56
+
57
+ const ContextMenuContent = React.forwardRef<
58
+ React.ElementRef<typeof ContextMenuPrimitive.Content>,
59
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content>
60
+ >(({ className, ...props }, ref) => (
61
+ <ContextMenuPrimitive.Portal>
62
+ <ContextMenuPrimitive.Content
63
+ ref={ref}
64
+ className={cn(
65
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
66
+ className
67
+ )}
68
+ {...props}
69
+ />
70
+ </ContextMenuPrimitive.Portal>
71
+ ))
72
+ ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName
73
+
74
+ const ContextMenuItem = React.forwardRef<
75
+ React.ElementRef<typeof ContextMenuPrimitive.Item>,
76
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
77
+ inset?: boolean
78
+ }
79
+ >(({ className, inset, ...props }, ref) => (
80
+ <ContextMenuPrimitive.Item
81
+ ref={ref}
82
+ className={cn(
83
+ "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
84
+ inset && "pl-8",
85
+ className
86
+ )}
87
+ {...props}
88
+ />
89
+ ))
90
+ ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName
91
+
92
+ const ContextMenuCheckboxItem = React.forwardRef<
93
+ React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,
94
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem>
95
+ >(({ className, children, checked, ...props }, ref) => (
96
+ <ContextMenuPrimitive.CheckboxItem
97
+ ref={ref}
98
+ className={cn(
99
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
100
+ className
101
+ )}
102
+ checked={checked}
103
+ {...props}
104
+ >
105
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
106
+ <ContextMenuPrimitive.ItemIndicator>
107
+ <Check className="h-4 w-4" />
108
+ </ContextMenuPrimitive.ItemIndicator>
109
+ </span>
110
+ {children}
111
+ </ContextMenuPrimitive.CheckboxItem>
112
+ ))
113
+ ContextMenuCheckboxItem.displayName =
114
+ ContextMenuPrimitive.CheckboxItem.displayName
115
+
116
+ const ContextMenuRadioItem = React.forwardRef<
117
+ React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,
118
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem>
119
+ >(({ className, children, ...props }, ref) => (
120
+ <ContextMenuPrimitive.RadioItem
121
+ ref={ref}
122
+ className={cn(
123
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
124
+ className
125
+ )}
126
+ {...props}
127
+ >
128
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
129
+ <ContextMenuPrimitive.ItemIndicator>
130
+ <Circle className="h-2 w-2 fill-current" />
131
+ </ContextMenuPrimitive.ItemIndicator>
132
+ </span>
133
+ {children}
134
+ </ContextMenuPrimitive.RadioItem>
135
+ ))
136
+ ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName
137
+
138
+ const ContextMenuLabel = React.forwardRef<
139
+ React.ElementRef<typeof ContextMenuPrimitive.Label>,
140
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
141
+ inset?: boolean
142
+ }
143
+ >(({ className, inset, ...props }, ref) => (
144
+ <ContextMenuPrimitive.Label
145
+ ref={ref}
146
+ className={cn(
147
+ "px-2 py-1.5 text-sm font-semibold text-foreground",
148
+ inset && "pl-8",
149
+ className
150
+ )}
151
+ {...props}
152
+ />
153
+ ))
154
+ ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName
155
+
156
+ const ContextMenuSeparator = React.forwardRef<
157
+ React.ElementRef<typeof ContextMenuPrimitive.Separator>,
158
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator>
159
+ >(({ className, ...props }, ref) => (
160
+ <ContextMenuPrimitive.Separator
161
+ ref={ref}
162
+ className={cn("-mx-1 my-1 h-px bg-border", className)}
163
+ {...props}
164
+ />
165
+ ))
166
+ ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName
167
+
168
+ const ContextMenuShortcut = ({
169
+ className,
170
+ ...props
171
+ }: React.HTMLAttributes<HTMLSpanElement>) => {
172
+ return (
173
+ <span
174
+ className={cn(
175
+ "ml-auto text-xs tracking-widest text-muted-foreground",
176
+ className
177
+ )}
178
+ {...props}
179
+ />
180
+ )
181
+ }
182
+ ContextMenuShortcut.displayName = "ContextMenuShortcut"
183
+
184
+ export {
185
+ ContextMenu,
186
+ ContextMenuTrigger,
187
+ ContextMenuContent,
188
+ ContextMenuItem,
189
+ ContextMenuCheckboxItem,
190
+ ContextMenuRadioItem,
191
+ ContextMenuLabel,
192
+ ContextMenuSeparator,
193
+ ContextMenuShortcut,
194
+ ContextMenuGroup,
195
+ ContextMenuPortal,
196
+ ContextMenuSub,
197
+ ContextMenuSubContent,
198
+ ContextMenuSubTrigger,
199
+ ContextMenuRadioGroup,
200
+ }
examples/cyberpunk-standalone/src/components/ui/dialog.tsx ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as DialogPrimitive from "@radix-ui/react-dialog"
5
+ import { X } from "lucide-react"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ const Dialog = DialogPrimitive.Root
10
+
11
+ const DialogTrigger = DialogPrimitive.Trigger
12
+
13
+ const DialogPortal = DialogPrimitive.Portal
14
+
15
+ const DialogClose = DialogPrimitive.Close
16
+
17
+ const DialogOverlay = React.forwardRef<
18
+ React.ElementRef<typeof DialogPrimitive.Overlay>,
19
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
20
+ >(({ className, ...props }, ref) => (
21
+ <DialogPrimitive.Overlay
22
+ ref={ref}
23
+ className={cn(
24
+ "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
25
+ className
26
+ )}
27
+ {...props}
28
+ />
29
+ ))
30
+ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
31
+
32
+ const DialogContent = React.forwardRef<
33
+ React.ElementRef<typeof DialogPrimitive.Content>,
34
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
35
+ >(({ className, children, ...props }, ref) => (
36
+ <DialogPortal>
37
+ <DialogOverlay />
38
+ <DialogPrimitive.Content
39
+ ref={ref}
40
+ className={cn(
41
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
42
+ className
43
+ )}
44
+ {...props}
45
+ >
46
+ {children}
47
+ <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
48
+ <X className="h-4 w-4" />
49
+ <span className="sr-only">Close</span>
50
+ </DialogPrimitive.Close>
51
+ </DialogPrimitive.Content>
52
+ </DialogPortal>
53
+ ))
54
+ DialogContent.displayName = DialogPrimitive.Content.displayName
55
+
56
+ const DialogHeader = ({
57
+ className,
58
+ ...props
59
+ }: React.HTMLAttributes<HTMLDivElement>) => (
60
+ <div
61
+ className={cn(
62
+ "flex flex-col space-y-1.5 text-center sm:text-left",
63
+ className
64
+ )}
65
+ {...props}
66
+ />
67
+ )
68
+ DialogHeader.displayName = "DialogHeader"
69
+
70
+ const DialogFooter = ({
71
+ className,
72
+ ...props
73
+ }: React.HTMLAttributes<HTMLDivElement>) => (
74
+ <div
75
+ className={cn(
76
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
77
+ className
78
+ )}
79
+ {...props}
80
+ />
81
+ )
82
+ DialogFooter.displayName = "DialogFooter"
83
+
84
+ const DialogTitle = React.forwardRef<
85
+ React.ElementRef<typeof DialogPrimitive.Title>,
86
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
87
+ >(({ className, ...props }, ref) => (
88
+ <DialogPrimitive.Title
89
+ ref={ref}
90
+ className={cn(
91
+ "text-lg font-semibold leading-none tracking-tight",
92
+ className
93
+ )}
94
+ {...props}
95
+ />
96
+ ))
97
+ DialogTitle.displayName = DialogPrimitive.Title.displayName
98
+
99
+ const DialogDescription = React.forwardRef<
100
+ React.ElementRef<typeof DialogPrimitive.Description>,
101
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
102
+ >(({ className, ...props }, ref) => (
103
+ <DialogPrimitive.Description
104
+ ref={ref}
105
+ className={cn("text-sm text-muted-foreground", className)}
106
+ {...props}
107
+ />
108
+ ))
109
+ DialogDescription.displayName = DialogPrimitive.Description.displayName
110
+
111
+ export {
112
+ Dialog,
113
+ DialogPortal,
114
+ DialogOverlay,
115
+ DialogClose,
116
+ DialogTrigger,
117
+ DialogContent,
118
+ DialogHeader,
119
+ DialogFooter,
120
+ DialogTitle,
121
+ DialogDescription,
122
+ }