Spaces:
Running
Running
feat: added react / tailwind ui
Browse files- index.html +65 -16
- package.json +17 -2
- pnpm-lock.yaml +1273 -2
- postcss.config.js +6 -0
- postcss.config.mjs +6 -0
- src/demo/App.tsx +20 -0
- src/demo/components/CalibrationPanel.tsx +526 -0
- src/demo/components/CalibrationWizard.tsx +217 -0
- src/demo/components/ErrorBoundary.tsx +65 -0
- src/demo/components/PortManager.tsx +1050 -0
- src/demo/components/ui/alert.tsx +58 -0
- src/demo/components/ui/badge.tsx +35 -0
- src/demo/components/ui/button.tsx +53 -0
- src/demo/components/ui/card.tsx +85 -0
- src/demo/index.css +12 -0
- src/demo/lib/utils.ts +6 -0
- src/demo/main.tsx +10 -0
- src/demo/pages/Calibrate.tsx +121 -0
- src/demo/pages/Home.tsx +98 -0
- src/demo/pages/Setup.tsx +99 -0
- src/demo/types.ts +20 -0
- src/vite-env.d.ts +32 -0
- tailwind.config.js +56 -0
- vanilla.html +32 -0
- vite.config.ts +75 -0
index.html
CHANGED
|
@@ -3,30 +3,79 @@
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8" />
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
-
<title>🤖 lerobot.js -
|
| 7 |
<meta
|
| 8 |
name="description"
|
| 9 |
-
content="State-of-the-art AI for real-world robotics in JavaScript/TypeScript"
|
| 10 |
/>
|
| 11 |
<style>
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
}
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
}
|
| 20 |
</style>
|
| 21 |
</head>
|
| 22 |
<body>
|
| 23 |
-
<div id="
|
| 24 |
-
<script type="module" src="/src/main.
|
| 25 |
-
<script>
|
| 26 |
-
// Add loaded class when page is ready
|
| 27 |
-
window.addEventListener("load", () => {
|
| 28 |
-
document.body.classList.add("loaded");
|
| 29 |
-
});
|
| 30 |
-
</script>
|
| 31 |
</body>
|
| 32 |
</html>
|
|
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8" />
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<title>🤖 lerobot.js - React Demo</title>
|
| 7 |
<meta
|
| 8 |
name="description"
|
| 9 |
+
content="State-of-the-art AI for real-world robotics in JavaScript/TypeScript - Interactive React Demo"
|
| 10 |
/>
|
| 11 |
<style>
|
| 12 |
+
:root {
|
| 13 |
+
--background: 0 0% 100%;
|
| 14 |
+
--foreground: 240 10% 3.9%;
|
| 15 |
+
--card: 0 0% 100%;
|
| 16 |
+
--card-foreground: 240 10% 3.9%;
|
| 17 |
+
--popover: 0 0% 100%;
|
| 18 |
+
--popover-foreground: 240 10% 3.9%;
|
| 19 |
+
--primary: 240 9% 17%;
|
| 20 |
+
--primary-foreground: 0 0% 98%;
|
| 21 |
+
--secondary: 240 4.8% 95.9%;
|
| 22 |
+
--secondary-foreground: 240 5.9% 10%;
|
| 23 |
+
--muted: 240 4.8% 95.9%;
|
| 24 |
+
--muted-foreground: 240 3.8% 46.1%;
|
| 25 |
+
--accent: 240 4.8% 95.9%;
|
| 26 |
+
--accent-foreground: 240 5.9% 10%;
|
| 27 |
+
--destructive: 0 84.2% 60.2%;
|
| 28 |
+
--destructive-foreground: 0 0% 98%;
|
| 29 |
+
--border: 240 5.9% 90%;
|
| 30 |
+
--input: 240 5.9% 90%;
|
| 31 |
+
--ring: 240 10% 3.9%;
|
| 32 |
+
--chart-1: 12 76% 61%;
|
| 33 |
+
--chart-2: 173 58% 39%;
|
| 34 |
+
--chart-3: 197 37% 24%;
|
| 35 |
+
--chart-4: 43 74% 66%;
|
| 36 |
+
--chart-5: 27 87% 67%;
|
| 37 |
+
--radius: 0.5rem;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
.dark {
|
| 41 |
+
--background: 240 10% 3.9%;
|
| 42 |
+
--foreground: 0 0% 98%;
|
| 43 |
+
--card: 240 10% 3.9%;
|
| 44 |
+
--card-foreground: 0 0% 98%;
|
| 45 |
+
--popover: 240 10% 3.9%;
|
| 46 |
+
--popover-foreground: 0 0% 98%;
|
| 47 |
+
--primary: 0 0% 98%;
|
| 48 |
+
--primary-foreground: 240 5.9% 10%;
|
| 49 |
+
--secondary: 240 3.7% 15.9%;
|
| 50 |
+
--secondary-foreground: 0 0% 98%;
|
| 51 |
+
--muted: 240 3.7% 15.9%;
|
| 52 |
+
--muted-foreground: 240 5% 64.9%;
|
| 53 |
+
--accent: 240 3.7% 15.9%;
|
| 54 |
+
--accent-foreground: 0 0% 98%;
|
| 55 |
+
--destructive: 0 62.8% 30.6%;
|
| 56 |
+
--destructive-foreground: 0 0% 98%;
|
| 57 |
+
--border: 240 3.7% 15.9%;
|
| 58 |
+
--input: 240 3.7% 15.9%;
|
| 59 |
+
--ring: 240 4.9% 83.9%;
|
| 60 |
+
--chart-1: 220 70% 50%;
|
| 61 |
+
--chart-2: 160 60% 45%;
|
| 62 |
+
--chart-3: 30 80% 55%;
|
| 63 |
+
--chart-4: 280 65% 60%;
|
| 64 |
+
--chart-5: 340 75% 55%;
|
| 65 |
}
|
| 66 |
+
|
| 67 |
+
* {
|
| 68 |
+
border-color: hsl(var(--border));
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
body {
|
| 72 |
+
background-color: hsl(var(--background));
|
| 73 |
+
color: hsl(var(--foreground));
|
| 74 |
}
|
| 75 |
</style>
|
| 76 |
</head>
|
| 77 |
<body>
|
| 78 |
+
<div id="root"></div>
|
| 79 |
+
<script type="module" src="/src/demo/main.tsx"></script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
</body>
|
| 81 |
</html>
|
package.json
CHANGED
|
@@ -19,10 +19,13 @@
|
|
| 19 |
"lerobot"
|
| 20 |
],
|
| 21 |
"scripts": {
|
| 22 |
-
"dev": "vite",
|
|
|
|
|
|
|
| 23 |
"build": "pnpm run build:cli",
|
| 24 |
"build:cli": "tsc --project tsconfig.cli.json",
|
| 25 |
-
"build:web": "tsc && vite build",
|
|
|
|
| 26 |
"preview": "vite preview",
|
| 27 |
"cli:find-port": "tsx src/cli/index.ts find-port",
|
| 28 |
"cli:calibrate": "tsx src/cli/index.ts calibrate",
|
|
@@ -35,6 +38,18 @@
|
|
| 35 |
},
|
| 36 |
"devDependencies": {
|
| 37 |
"@types/node": "^22.10.5",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
"tsx": "^4.19.2",
|
| 39 |
"typescript": "~5.8.3",
|
| 40 |
"vite": "^6.3.5"
|
|
|
|
| 19 |
"lerobot"
|
| 20 |
],
|
| 21 |
"scripts": {
|
| 22 |
+
"dev": "vite --mode demo",
|
| 23 |
+
"dev:vanilla": "vite --mode vanilla",
|
| 24 |
+
"dev:lib": "vite --mode lib",
|
| 25 |
"build": "pnpm run build:cli",
|
| 26 |
"build:cli": "tsc --project tsconfig.cli.json",
|
| 27 |
+
"build:web": "tsc && vite build --mode lib",
|
| 28 |
+
"build:demo": "tsc && vite build --mode demo",
|
| 29 |
"preview": "vite preview",
|
| 30 |
"cli:find-port": "tsx src/cli/index.ts find-port",
|
| 31 |
"cli:calibrate": "tsx src/cli/index.ts calibrate",
|
|
|
|
| 38 |
},
|
| 39 |
"devDependencies": {
|
| 40 |
"@types/node": "^22.10.5",
|
| 41 |
+
"@types/react": "^18.2.79",
|
| 42 |
+
"@types/react-dom": "^18.2.25",
|
| 43 |
+
"@vitejs/plugin-react": "^4.2.1",
|
| 44 |
+
"autoprefixer": "^10.4.19",
|
| 45 |
+
"class-variance-authority": "^0.7.0",
|
| 46 |
+
"clsx": "^2.1.1",
|
| 47 |
+
"lucide-react": "^0.400.0",
|
| 48 |
+
"postcss": "^8.4.38",
|
| 49 |
+
"react": "^18.2.0",
|
| 50 |
+
"react-dom": "^18.2.0",
|
| 51 |
+
"tailwind-merge": "^2.3.0",
|
| 52 |
+
"tailwindcss": "^3.4.0",
|
| 53 |
"tsx": "^4.19.2",
|
| 54 |
"typescript": "~5.8.3",
|
| 55 |
"vite": "^6.3.5"
|
pnpm-lock.yaml
CHANGED
|
@@ -18,6 +18,42 @@ importers:
|
|
| 18 |
'@types/node':
|
| 19 |
specifier: ^22.10.5
|
| 20 |
version: 22.15.31
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
tsx:
|
| 22 |
specifier: ^4.19.2
|
| 23 |
version: 4.20.3
|
|
@@ -26,10 +62,97 @@ importers:
|
|
| 26 |
version: 5.8.3
|
| 27 |
vite:
|
| 28 |
specifier: ^6.3.5
|
| 29 |
-
version: 6.3.5(@types/[email protected])([email protected])
|
| 30 |
|
| 31 |
packages:
|
| 32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
'@esbuild/[email protected]':
|
| 34 |
resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==}
|
| 35 |
engines: {node: '>=18'}
|
|
@@ -180,6 +303,47 @@ packages:
|
|
| 180 |
cpu: [x64]
|
| 181 |
os: [win32]
|
| 182 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
'@rollup/[email protected]':
|
| 184 |
resolution: {integrity: sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==}
|
| 185 |
cpu: [arm]
|
|
@@ -344,28 +508,145 @@ packages:
|
|
| 344 |
resolution: {integrity: sha512-9On64rhzuqKdOQyiYLYv2lQOh3TZU/D3+IWCR5gk0alPel2nwpp4YwDEGiUBfrQZEdQ6xww0PWkzqth4wqwX3Q==}
|
| 345 |
engines: {node: '>=12.0.0'}
|
| 346 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 347 |
'@types/[email protected]':
|
| 348 |
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
|
| 349 |
|
| 350 |
'@types/[email protected]':
|
| 351 |
resolution: {integrity: sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==}
|
| 352 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 353 | |
| 354 |
resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==}
|
| 355 |
engines: {node: '>=18'}
|
| 356 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 357 | |
| 358 |
resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
|
| 359 |
engines: {node: '>=12'}
|
| 360 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 361 | |
| 362 |
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
|
| 363 |
engines: {node: '>=12'}
|
| 364 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 | |
| 366 |
resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
|
| 367 |
engines: {node: '>=18'}
|
| 368 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 369 | |
| 370 |
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
| 371 |
engines: {node: '>=6.0'}
|
|
@@ -375,9 +656,27 @@ packages:
|
|
| 375 |
supports-color:
|
| 376 |
optional: true
|
| 377 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 378 | |
| 379 |
resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
|
| 380 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 381 | |
| 382 |
resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
|
| 383 |
engines: {node: '>=18'}
|
|
@@ -387,6 +686,17 @@ packages:
|
|
| 387 |
engines: {node: '>=18'}
|
| 388 |
hasBin: true
|
| 389 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 | |
| 391 |
resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
|
| 392 |
peerDependencies:
|
|
@@ -395,11 +705,29 @@ packages:
|
|
| 395 |
picomatch:
|
| 396 |
optional: true
|
| 397 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 398 | |
| 399 |
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
| 400 |
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
| 401 |
os: [darwin]
|
| 402 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 | |
| 404 |
resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==}
|
| 405 |
engines: {node: '>=18'}
|
|
@@ -407,21 +735,129 @@ packages:
|
|
| 407 | |
| 408 |
resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
|
| 409 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 410 | |
| 411 |
resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==}
|
| 412 |
engines: {node: '>=18'}
|
| 413 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 414 | |
| 415 |
resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
|
| 416 |
engines: {node: '>=18'}
|
| 417 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 418 | |
| 419 |
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
|
| 420 |
engines: {node: '>=18'}
|
| 421 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 | |
| 423 |
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
| 424 |
|
|
|
|
|
|
|
|
|
|
| 425 | |
| 426 |
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
| 427 |
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
|
@@ -434,37 +870,169 @@ packages:
|
|
| 434 |
resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
|
| 435 |
hasBin: true
|
| 436 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 437 | |
| 438 |
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
|
| 439 |
engines: {node: '>=18'}
|
| 440 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 441 | |
| 442 |
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
| 443 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 444 | |
| 445 |
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
|
| 446 |
engines: {node: '>=12'}
|
| 447 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 | |
| 449 |
resolution: {integrity: sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==}
|
| 450 |
engines: {node: ^10 || ^12 || >=14}
|
| 451 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 452 | |
| 453 |
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
|
| 454 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 455 | |
| 456 |
resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
|
| 457 |
engines: {node: '>=18'}
|
| 458 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 459 | |
| 460 |
resolution: {integrity: sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==}
|
| 461 |
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
| 462 |
hasBin: true
|
| 463 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 464 | |
| 465 |
resolution: {integrity: sha512-AmH3D9hHPFmnF/oq/rvigfiAouAKyK/TjnrkwZRYSFZxNggJxwvbAbfYrLeuvq7ktUdhuHdVdSjj852Z55R+uA==}
|
| 466 |
engines: {node: '>=16.0.0'}
|
| 467 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 468 | |
| 469 |
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
| 470 |
engines: {node: '>=14'}
|
|
@@ -477,18 +1045,61 @@ packages:
|
|
| 477 |
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
| 478 |
engines: {node: '>=0.10.0'}
|
| 479 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 480 | |
| 481 |
resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
|
| 482 |
engines: {node: '>=18'}
|
| 483 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 484 | |
| 485 |
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
|
| 486 |
engines: {node: '>=12'}
|
| 487 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 488 | |
| 489 |
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
|
| 490 |
engines: {node: '>=12.0.0'}
|
| 491 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 492 | |
| 493 |
resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==}
|
| 494 |
engines: {node: '>=18.0.0'}
|
|
@@ -502,6 +1113,15 @@ packages:
|
|
| 502 | |
| 503 |
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
| 504 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 505 | |
| 506 |
resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==}
|
| 507 |
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
|
@@ -542,12 +1162,150 @@ packages:
|
|
| 542 |
yaml:
|
| 543 |
optional: true
|
| 544 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 545 | |
| 546 |
resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==}
|
| 547 |
engines: {node: '>=18'}
|
| 548 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 549 |
snapshots:
|
| 550 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 551 |
'@esbuild/[email protected]':
|
| 552 |
optional: true
|
| 553 |
|
|
@@ -623,6 +1381,49 @@ snapshots:
|
|
| 623 |
'@esbuild/[email protected]':
|
| 624 |
optional: true
|
| 625 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 626 |
'@rollup/[email protected]':
|
| 627 |
optional: true
|
| 628 |
|
|
@@ -737,30 +1538,172 @@ snapshots:
|
|
| 737 |
transitivePeerDependencies:
|
| 738 |
- supports-color
|
| 739 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 740 |
'@types/[email protected]': {}
|
| 741 |
|
| 742 |
'@types/[email protected]':
|
| 743 |
dependencies:
|
| 744 |
undici-types: 6.21.0
|
| 745 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 746 | |
| 747 |
dependencies:
|
| 748 |
environment: 1.1.0
|
| 749 |
|
|
|
|
|
|
|
| 750 | |
| 751 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 752 | |
| 753 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 754 | |
| 755 |
dependencies:
|
| 756 |
restore-cursor: 5.1.0
|
| 757 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 758 | |
| 759 |
dependencies:
|
| 760 |
ms: 2.1.2
|
| 761 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 762 | |
| 763 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 764 | |
| 765 |
|
| 766 | |
|
@@ -791,23 +1734,113 @@ snapshots:
|
|
| 791 |
'@esbuild/win32-ia32': 0.25.5
|
| 792 |
'@esbuild/win32-x64': 0.25.5
|
| 793 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 794 | |
| 795 |
optionalDependencies:
|
| 796 |
picomatch: 4.0.2
|
| 797 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 798 | |
| 799 |
optional: true
|
| 800 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 801 | |
| 802 |
|
| 803 | |
| 804 |
dependencies:
|
| 805 |
resolve-pkg-maps: 1.0.0
|
| 806 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 807 | |
| 808 |
dependencies:
|
| 809 |
get-east-asian-width: 1.3.0
|
| 810 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 811 | |
| 812 |
dependencies:
|
| 813 |
ansi-escapes: 7.0.0
|
|
@@ -816,37 +1849,158 @@ snapshots:
|
|
| 816 |
strip-ansi: 7.1.0
|
| 817 |
wrap-ansi: 9.0.0
|
| 818 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 819 | |
| 820 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 821 | |
| 822 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 823 | |
| 824 |
|
| 825 | |
| 826 |
|
| 827 | |
| 828 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 829 | |
| 830 |
dependencies:
|
| 831 |
mimic-function: 5.0.1
|
| 832 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 833 | |
| 834 |
|
|
|
|
|
|
|
| 835 | |
| 836 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 837 | |
| 838 |
dependencies:
|
| 839 |
nanoid: 3.3.11
|
| 840 |
picocolors: 1.1.1
|
| 841 |
source-map-js: 1.2.1
|
| 842 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 843 | |
| 844 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 845 | |
| 846 |
dependencies:
|
| 847 |
onetime: 7.0.0
|
| 848 |
signal-exit: 4.1.0
|
| 849 |
|
|
|
|
|
|
|
| 850 | |
| 851 |
dependencies:
|
| 852 |
'@types/estree': 1.0.7
|
|
@@ -873,6 +2027,16 @@ snapshots:
|
|
| 873 |
'@rollup/rollup-win32-x64-msvc': 4.43.0
|
| 874 |
fsevents: 2.3.3
|
| 875 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 876 | |
| 877 |
dependencies:
|
| 878 |
'@serialport/binding-mock': 10.2.2
|
|
@@ -892,6 +2056,12 @@ snapshots:
|
|
| 892 |
transitivePeerDependencies:
|
| 893 |
- supports-color
|
| 894 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 895 | |
| 896 |
|
| 897 | |
|
@@ -901,21 +2071,92 @@ snapshots:
|
|
| 901 |
|
| 902 | |
| 903 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 904 | |
| 905 |
dependencies:
|
| 906 |
emoji-regex: 10.4.0
|
| 907 |
get-east-asian-width: 1.3.0
|
| 908 |
strip-ansi: 7.1.0
|
| 909 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 910 | |
| 911 |
dependencies:
|
| 912 |
ansi-regex: 6.1.0
|
| 913 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 914 | |
| 915 |
dependencies:
|
| 916 |
fdir: 6.4.6([email protected])
|
| 917 |
picomatch: 4.0.2
|
| 918 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 919 | |
| 920 |
dependencies:
|
| 921 |
esbuild: 0.25.5
|
|
@@ -927,7 +2168,15 @@ snapshots:
|
|
| 927 |
|
| 928 | |
| 929 |
|
| 930 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 931 |
dependencies:
|
| 932 |
esbuild: 0.25.5
|
| 933 |
fdir: 6.4.6([email protected])
|
|
@@ -938,10 +2187,32 @@ snapshots:
|
|
| 938 |
optionalDependencies:
|
| 939 |
'@types/node': 22.15.31
|
| 940 |
fsevents: 2.3.3
|
|
|
|
| 941 |
tsx: 4.20.3
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 942 |
|
| 943 | |
| 944 |
dependencies:
|
| 945 |
ansi-styles: 6.2.1
|
| 946 |
string-width: 7.2.0
|
| 947 |
strip-ansi: 7.1.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
'@types/node':
|
| 19 |
specifier: ^22.10.5
|
| 20 |
version: 22.15.31
|
| 21 |
+
'@types/react':
|
| 22 |
+
specifier: ^18.2.79
|
| 23 |
+
version: 18.3.23
|
| 24 |
+
'@types/react-dom':
|
| 25 |
+
specifier: ^18.2.25
|
| 26 |
+
version: 18.3.7(@types/[email protected])
|
| 27 |
+
'@vitejs/plugin-react':
|
| 28 |
+
specifier: ^4.2.1
|
| 29 |
+
version: 4.5.2([email protected](@types/[email protected])([email protected])([email protected])([email protected]))
|
| 30 |
+
autoprefixer:
|
| 31 |
+
specifier: ^10.4.19
|
| 32 |
+
version: 10.4.21([email protected])
|
| 33 |
+
class-variance-authority:
|
| 34 |
+
specifier: ^0.7.0
|
| 35 |
+
version: 0.7.1
|
| 36 |
+
clsx:
|
| 37 |
+
specifier: ^2.1.1
|
| 38 |
+
version: 2.1.1
|
| 39 |
+
lucide-react:
|
| 40 |
+
specifier: ^0.400.0
|
| 41 |
+
version: 0.400.0([email protected])
|
| 42 |
+
postcss:
|
| 43 |
+
specifier: ^8.4.38
|
| 44 |
+
version: 8.5.5
|
| 45 |
+
react:
|
| 46 |
+
specifier: ^18.2.0
|
| 47 |
+
version: 18.3.1
|
| 48 |
+
react-dom:
|
| 49 |
+
specifier: ^18.2.0
|
| 50 |
+
version: 18.3.1([email protected])
|
| 51 |
+
tailwind-merge:
|
| 52 |
+
specifier: ^2.3.0
|
| 53 |
+
version: 2.6.0
|
| 54 |
+
tailwindcss:
|
| 55 |
+
specifier: ^3.4.0
|
| 56 |
+
version: 3.4.17
|
| 57 |
tsx:
|
| 58 |
specifier: ^4.19.2
|
| 59 |
version: 4.20.3
|
|
|
|
| 62 |
version: 5.8.3
|
| 63 |
vite:
|
| 64 |
specifier: ^6.3.5
|
| 65 |
+
version: 6.3.5(@types/[email protected])([email protected])([email protected])([email protected])
|
| 66 |
|
| 67 |
packages:
|
| 68 |
|
| 69 |
+
'@alloc/[email protected]':
|
| 70 |
+
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
|
| 71 |
+
engines: {node: '>=10'}
|
| 72 |
+
|
| 73 |
+
'@ampproject/[email protected]':
|
| 74 |
+
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
|
| 75 |
+
engines: {node: '>=6.0.0'}
|
| 76 |
+
|
| 77 |
+
'@babel/[email protected]':
|
| 78 |
+
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
|
| 79 |
+
engines: {node: '>=6.9.0'}
|
| 80 |
+
|
| 81 |
+
'@babel/[email protected]':
|
| 82 |
+
resolution: {integrity: sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==}
|
| 83 |
+
engines: {node: '>=6.9.0'}
|
| 84 |
+
|
| 85 |
+
'@babel/[email protected]':
|
| 86 |
+
resolution: {integrity: sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==}
|
| 87 |
+
engines: {node: '>=6.9.0'}
|
| 88 |
+
|
| 89 |
+
'@babel/[email protected]':
|
| 90 |
+
resolution: {integrity: sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==}
|
| 91 |
+
engines: {node: '>=6.9.0'}
|
| 92 |
+
|
| 93 |
+
'@babel/[email protected]':
|
| 94 |
+
resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==}
|
| 95 |
+
engines: {node: '>=6.9.0'}
|
| 96 |
+
|
| 97 |
+
'@babel/[email protected]':
|
| 98 |
+
resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
|
| 99 |
+
engines: {node: '>=6.9.0'}
|
| 100 |
+
|
| 101 |
+
'@babel/[email protected]':
|
| 102 |
+
resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==}
|
| 103 |
+
engines: {node: '>=6.9.0'}
|
| 104 |
+
peerDependencies:
|
| 105 |
+
'@babel/core': ^7.0.0
|
| 106 |
+
|
| 107 |
+
'@babel/[email protected]':
|
| 108 |
+
resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
|
| 109 |
+
engines: {node: '>=6.9.0'}
|
| 110 |
+
|
| 111 |
+
'@babel/[email protected]':
|
| 112 |
+
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
|
| 113 |
+
engines: {node: '>=6.9.0'}
|
| 114 |
+
|
| 115 |
+
'@babel/[email protected]':
|
| 116 |
+
resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
|
| 117 |
+
engines: {node: '>=6.9.0'}
|
| 118 |
+
|
| 119 |
+
'@babel/[email protected]':
|
| 120 |
+
resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
|
| 121 |
+
engines: {node: '>=6.9.0'}
|
| 122 |
+
|
| 123 |
+
'@babel/[email protected]':
|
| 124 |
+
resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==}
|
| 125 |
+
engines: {node: '>=6.9.0'}
|
| 126 |
+
|
| 127 |
+
'@babel/[email protected]':
|
| 128 |
+
resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==}
|
| 129 |
+
engines: {node: '>=6.0.0'}
|
| 130 |
+
hasBin: true
|
| 131 |
+
|
| 132 |
+
'@babel/[email protected]':
|
| 133 |
+
resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==}
|
| 134 |
+
engines: {node: '>=6.9.0'}
|
| 135 |
+
peerDependencies:
|
| 136 |
+
'@babel/core': ^7.0.0-0
|
| 137 |
+
|
| 138 |
+
'@babel/[email protected]':
|
| 139 |
+
resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==}
|
| 140 |
+
engines: {node: '>=6.9.0'}
|
| 141 |
+
peerDependencies:
|
| 142 |
+
'@babel/core': ^7.0.0-0
|
| 143 |
+
|
| 144 |
+
'@babel/[email protected]':
|
| 145 |
+
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
|
| 146 |
+
engines: {node: '>=6.9.0'}
|
| 147 |
+
|
| 148 |
+
'@babel/[email protected]':
|
| 149 |
+
resolution: {integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==}
|
| 150 |
+
engines: {node: '>=6.9.0'}
|
| 151 |
+
|
| 152 |
+
'@babel/[email protected]':
|
| 153 |
+
resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==}
|
| 154 |
+
engines: {node: '>=6.9.0'}
|
| 155 |
+
|
| 156 |
'@esbuild/[email protected]':
|
| 157 |
resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==}
|
| 158 |
engines: {node: '>=18'}
|
|
|
|
| 303 |
cpu: [x64]
|
| 304 |
os: [win32]
|
| 305 |
|
| 306 |
+
'@isaacs/[email protected]':
|
| 307 |
+
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
|
| 308 |
+
engines: {node: '>=12'}
|
| 309 |
+
|
| 310 |
+
'@jridgewell/[email protected]':
|
| 311 |
+
resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
|
| 312 |
+
engines: {node: '>=6.0.0'}
|
| 313 |
+
|
| 314 |
+
'@jridgewell/[email protected]':
|
| 315 |
+
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
|
| 316 |
+
engines: {node: '>=6.0.0'}
|
| 317 |
+
|
| 318 |
+
'@jridgewell/[email protected]':
|
| 319 |
+
resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
|
| 320 |
+
engines: {node: '>=6.0.0'}
|
| 321 |
+
|
| 322 |
+
'@jridgewell/[email protected]':
|
| 323 |
+
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
|
| 324 |
+
|
| 325 |
+
'@jridgewell/[email protected]':
|
| 326 |
+
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
|
| 327 |
+
|
| 328 |
+
'@nodelib/[email protected]':
|
| 329 |
+
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
| 330 |
+
engines: {node: '>= 8'}
|
| 331 |
+
|
| 332 |
+
'@nodelib/[email protected]':
|
| 333 |
+
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
|
| 334 |
+
engines: {node: '>= 8'}
|
| 335 |
+
|
| 336 |
+
'@nodelib/[email protected]':
|
| 337 |
+
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
| 338 |
+
engines: {node: '>= 8'}
|
| 339 |
+
|
| 340 |
+
'@pkgjs/[email protected]':
|
| 341 |
+
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
| 342 |
+
engines: {node: '>=14'}
|
| 343 |
+
|
| 344 |
+
'@rolldown/[email protected]':
|
| 345 |
+
resolution: {integrity: sha512-L/gAA/hyCSuzTF1ftlzUSI/IKr2POHsv1Dd78GfqkR83KMNuswWD61JxGV2L7nRwBBBSDr6R1gCkdTmoN7W4ag==}
|
| 346 |
+
|
| 347 |
'@rollup/[email protected]':
|
| 348 |
resolution: {integrity: sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==}
|
| 349 |
cpu: [arm]
|
|
|
|
| 508 |
resolution: {integrity: sha512-9On64rhzuqKdOQyiYLYv2lQOh3TZU/D3+IWCR5gk0alPel2nwpp4YwDEGiUBfrQZEdQ6xww0PWkzqth4wqwX3Q==}
|
| 509 |
engines: {node: '>=12.0.0'}
|
| 510 |
|
| 511 |
+
'@types/[email protected]':
|
| 512 |
+
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
|
| 513 |
+
|
| 514 |
+
'@types/[email protected]':
|
| 515 |
+
resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
|
| 516 |
+
|
| 517 |
+
'@types/[email protected]':
|
| 518 |
+
resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
|
| 519 |
+
|
| 520 |
+
'@types/[email protected]':
|
| 521 |
+
resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==}
|
| 522 |
+
|
| 523 |
'@types/[email protected]':
|
| 524 |
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
|
| 525 |
|
| 526 |
'@types/[email protected]':
|
| 527 |
resolution: {integrity: sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==}
|
| 528 |
|
| 529 |
+
'@types/[email protected]':
|
| 530 |
+
resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==}
|
| 531 |
+
|
| 532 |
+
'@types/[email protected]':
|
| 533 |
+
resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==}
|
| 534 |
+
peerDependencies:
|
| 535 |
+
'@types/react': ^18.0.0
|
| 536 |
+
|
| 537 |
+
'@types/[email protected]':
|
| 538 |
+
resolution: {integrity: sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==}
|
| 539 |
+
|
| 540 |
+
'@vitejs/[email protected]':
|
| 541 |
+
resolution: {integrity: sha512-QNVT3/Lxx99nMQWJWF7K4N6apUEuT0KlZA3mx/mVaoGj3smm/8rc8ezz15J1pcbcjDK0V15rpHetVfya08r76Q==}
|
| 542 |
+
engines: {node: ^14.18.0 || >=16.0.0}
|
| 543 |
+
peerDependencies:
|
| 544 |
+
vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0
|
| 545 |
+
|
| 546 | |
| 547 |
resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==}
|
| 548 |
engines: {node: '>=18'}
|
| 549 |
|
| 550 | |
| 551 |
+
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
| 552 |
+
engines: {node: '>=8'}
|
| 553 |
+
|
| 554 | |
| 555 |
resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
|
| 556 |
engines: {node: '>=12'}
|
| 557 |
|
| 558 | |
| 559 |
+
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
| 560 |
+
engines: {node: '>=8'}
|
| 561 |
+
|
| 562 | |
| 563 |
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
|
| 564 |
engines: {node: '>=12'}
|
| 565 |
|
| 566 | |
| 567 |
+
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
|
| 568 |
+
|
| 569 | |
| 570 |
+
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
| 571 |
+
engines: {node: '>= 8'}
|
| 572 |
+
|
| 573 | |
| 574 |
+
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
|
| 575 |
+
|
| 576 | |
| 577 |
+
resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==}
|
| 578 |
+
engines: {node: ^10 || ^12 || >=14}
|
| 579 |
+
hasBin: true
|
| 580 |
+
peerDependencies:
|
| 581 |
+
postcss: ^8.1.0
|
| 582 |
+
|
| 583 | |
| 584 |
+
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
| 585 |
+
|
| 586 | |
| 587 |
+
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
| 588 |
+
engines: {node: '>=8'}
|
| 589 |
+
|
| 590 | |
| 591 |
+
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
|
| 592 |
+
|
| 593 | |
| 594 |
+
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
| 595 |
+
engines: {node: '>=8'}
|
| 596 |
+
|
| 597 | |
| 598 |
+
resolution: {integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==}
|
| 599 |
+
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
| 600 |
+
hasBin: true
|
| 601 |
+
|
| 602 | |
| 603 |
+
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
|
| 604 |
+
engines: {node: '>= 6'}
|
| 605 |
+
|
| 606 | |
| 607 |
+
resolution: {integrity: sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==}
|
| 608 |
+
|
| 609 | |
| 610 |
+
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
| 611 |
+
engines: {node: '>= 8.10.0'}
|
| 612 |
+
|
| 613 | |
| 614 |
+
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
|
| 615 |
+
|
| 616 | |
| 617 |
resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
|
| 618 |
engines: {node: '>=18'}
|
| 619 |
|
| 620 | |
| 621 |
+
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
| 622 |
+
engines: {node: '>=6'}
|
| 623 |
+
|
| 624 | |
| 625 |
+
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
| 626 |
+
engines: {node: '>=7.0.0'}
|
| 627 |
+
|
| 628 | |
| 629 |
+
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
| 630 |
+
|
| 631 | |
| 632 |
+
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
|
| 633 |
+
engines: {node: '>= 6'}
|
| 634 |
+
|
| 635 | |
| 636 |
+
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
| 637 |
+
|
| 638 | |
| 639 |
+
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
| 640 |
+
engines: {node: '>= 8'}
|
| 641 |
+
|
| 642 | |
| 643 |
+
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
|
| 644 |
+
engines: {node: '>=4'}
|
| 645 |
+
hasBin: true
|
| 646 |
+
|
| 647 | |
| 648 |
+
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
| 649 |
+
|
| 650 | |
| 651 |
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
| 652 |
engines: {node: '>=6.0'}
|
|
|
|
| 656 |
supports-color:
|
| 657 |
optional: true
|
| 658 |
|
| 659 | |
| 660 |
+
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
| 661 |
+
|
| 662 | |
| 663 |
+
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
|
| 664 |
+
|
| 665 | |
| 666 |
+
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
| 667 |
+
|
| 668 | |
| 669 |
+
resolution: {integrity: sha512-RUNQmFLNIWVW6+z32EJQ5+qx8ci6RGvdtDC0Ls+F89wz6I2AthpXF0w0DIrn2jpLX0/PU9ZCo+Qp7bg/EckJmA==}
|
| 670 |
+
|
| 671 | |
| 672 |
resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
|
| 673 |
|
| 674 | |
| 675 |
+
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
| 676 |
+
|
| 677 | |
| 678 |
+
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
| 679 |
+
|
| 680 | |
| 681 |
resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
|
| 682 |
engines: {node: '>=18'}
|
|
|
|
| 686 |
engines: {node: '>=18'}
|
| 687 |
hasBin: true
|
| 688 |
|
| 689 | |
| 690 |
+
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
| 691 |
+
engines: {node: '>=6'}
|
| 692 |
+
|
| 693 | |
| 694 |
+
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
|
| 695 |
+
engines: {node: '>=8.6.0'}
|
| 696 |
+
|
| 697 | |
| 698 |
+
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
|
| 699 |
+
|
| 700 | |
| 701 |
resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
|
| 702 |
peerDependencies:
|
|
|
|
| 705 |
picomatch:
|
| 706 |
optional: true
|
| 707 |
|
| 708 | |
| 709 |
+
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
| 710 |
+
engines: {node: '>=8'}
|
| 711 |
+
|
| 712 | |
| 713 |
+
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
| 714 |
+
engines: {node: '>=14'}
|
| 715 |
+
|
| 716 | |
| 717 |
+
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
|
| 718 |
+
|
| 719 | |
| 720 |
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
| 721 |
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
| 722 |
os: [darwin]
|
| 723 |
|
| 724 | |
| 725 |
+
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
| 726 |
+
|
| 727 | |
| 728 |
+
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
| 729 |
+
engines: {node: '>=6.9.0'}
|
| 730 |
+
|
| 731 | |
| 732 |
resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==}
|
| 733 |
engines: {node: '>=18'}
|
|
|
|
| 735 | |
| 736 |
resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
|
| 737 |
|
| 738 | |
| 739 |
+
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
| 740 |
+
engines: {node: '>= 6'}
|
| 741 |
+
|
| 742 | |
| 743 |
+
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
| 744 |
+
engines: {node: '>=10.13.0'}
|
| 745 |
+
|
| 746 | |
| 747 |
+
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
|
| 748 |
+
hasBin: true
|
| 749 |
+
|
| 750 | |
| 751 |
+
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
|
| 752 |
+
engines: {node: '>=4'}
|
| 753 |
+
|
| 754 | |
| 755 |
+
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
| 756 |
+
engines: {node: '>= 0.4'}
|
| 757 |
+
|
| 758 | |
| 759 |
+
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
| 760 |
+
engines: {node: '>=8'}
|
| 761 |
+
|
| 762 | |
| 763 |
+
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
|
| 764 |
+
engines: {node: '>= 0.4'}
|
| 765 |
+
|
| 766 | |
| 767 |
+
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
| 768 |
+
engines: {node: '>=0.10.0'}
|
| 769 |
+
|
| 770 | |
| 771 |
+
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
| 772 |
+
engines: {node: '>=8'}
|
| 773 |
+
|
| 774 | |
| 775 |
resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==}
|
| 776 |
engines: {node: '>=18'}
|
| 777 |
|
| 778 | |
| 779 |
+
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
| 780 |
+
engines: {node: '>=0.10.0'}
|
| 781 |
+
|
| 782 | |
| 783 |
+
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
| 784 |
+
engines: {node: '>=0.12.0'}
|
| 785 |
+
|
| 786 | |
| 787 |
+
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
| 788 |
+
|
| 789 | |
| 790 |
+
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
|
| 791 |
+
|
| 792 | |
| 793 |
+
resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
|
| 794 |
+
hasBin: true
|
| 795 |
+
|
| 796 | |
| 797 |
+
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
| 798 |
+
|
| 799 | |
| 800 |
+
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
|
| 801 |
+
engines: {node: '>=6'}
|
| 802 |
+
hasBin: true
|
| 803 |
+
|
| 804 | |
| 805 |
+
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
|
| 806 |
+
engines: {node: '>=6'}
|
| 807 |
+
hasBin: true
|
| 808 |
+
|
| 809 | |
| 810 |
+
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
|
| 811 |
+
engines: {node: '>=14'}
|
| 812 |
+
|
| 813 | |
| 814 |
+
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
| 815 |
+
|
| 816 | |
| 817 |
resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
|
| 818 |
engines: {node: '>=18'}
|
| 819 |
|
| 820 | |
| 821 |
+
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
| 822 |
+
hasBin: true
|
| 823 |
+
|
| 824 | |
| 825 |
+
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
|
| 826 |
+
|
| 827 | |
| 828 |
+
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
| 829 |
+
|
| 830 | |
| 831 |
+
resolution: {integrity: sha512-rpp7pFHh3Xd93KHixNgB0SqThMHpYNzsGUu69UaQbSZ75Q/J3m5t6EhKyMT3m4w2WOxmJ2mY0tD3vebnXqQryQ==}
|
| 832 |
+
peerDependencies:
|
| 833 |
+
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
| 834 |
+
|
| 835 | |
| 836 |
+
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
| 837 |
+
engines: {node: '>= 8'}
|
| 838 |
+
|
| 839 | |
| 840 |
+
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
| 841 |
+
engines: {node: '>=8.6'}
|
| 842 |
+
|
| 843 | |
| 844 |
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
|
| 845 |
engines: {node: '>=18'}
|
| 846 |
|
| 847 | |
| 848 |
+
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
|
| 849 |
+
engines: {node: '>=16 || 14 >=14.17'}
|
| 850 |
+
|
| 851 | |
| 852 |
+
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
|
| 853 |
+
engines: {node: '>=16 || 14 >=14.17'}
|
| 854 |
+
|
| 855 | |
| 856 |
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
| 857 |
|
| 858 | |
| 859 |
+
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
|
| 860 |
+
|
| 861 | |
| 862 |
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
| 863 |
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
|
|
|
| 870 |
resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
|
| 871 |
hasBin: true
|
| 872 |
|
| 873 | |
| 874 |
+
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
|
| 875 |
+
|
| 876 | |
| 877 |
+
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
| 878 |
+
engines: {node: '>=0.10.0'}
|
| 879 |
+
|
| 880 | |
| 881 |
+
resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
|
| 882 |
+
engines: {node: '>=0.10.0'}
|
| 883 |
+
|
| 884 | |
| 885 |
+
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
| 886 |
+
engines: {node: '>=0.10.0'}
|
| 887 |
+
|
| 888 | |
| 889 |
+
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
|
| 890 |
+
engines: {node: '>= 6'}
|
| 891 |
+
|
| 892 | |
| 893 |
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
|
| 894 |
engines: {node: '>=18'}
|
| 895 |
|
| 896 | |
| 897 |
+
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
|
| 898 |
+
|
| 899 | |
| 900 |
+
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
| 901 |
+
engines: {node: '>=8'}
|
| 902 |
+
|
| 903 | |
| 904 |
+
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
| 905 |
+
|
| 906 | |
| 907 |
+
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
|
| 908 |
+
engines: {node: '>=16 || 14 >=14.18'}
|
| 909 |
+
|
| 910 | |
| 911 |
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
| 912 |
|
| 913 | |
| 914 |
+
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
| 915 |
+
engines: {node: '>=8.6'}
|
| 916 |
+
|
| 917 | |
| 918 |
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
|
| 919 |
engines: {node: '>=12'}
|
| 920 |
|
| 921 | |
| 922 |
+
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
|
| 923 |
+
engines: {node: '>=0.10.0'}
|
| 924 |
+
|
| 925 | |
| 926 |
+
resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
|
| 927 |
+
engines: {node: '>= 6'}
|
| 928 |
+
|
| 929 | |
| 930 |
+
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
|
| 931 |
+
engines: {node: '>=14.0.0'}
|
| 932 |
+
peerDependencies:
|
| 933 |
+
postcss: ^8.0.0
|
| 934 |
+
|
| 935 | |
| 936 |
+
resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
|
| 937 |
+
engines: {node: ^12 || ^14 || >= 16}
|
| 938 |
+
peerDependencies:
|
| 939 |
+
postcss: ^8.4.21
|
| 940 |
+
|
| 941 | |
| 942 |
+
resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
|
| 943 |
+
engines: {node: '>= 14'}
|
| 944 |
+
peerDependencies:
|
| 945 |
+
postcss: '>=8.0.9'
|
| 946 |
+
ts-node: '>=9.0.0'
|
| 947 |
+
peerDependenciesMeta:
|
| 948 |
+
postcss:
|
| 949 |
+
optional: true
|
| 950 |
+
ts-node:
|
| 951 |
+
optional: true
|
| 952 |
+
|
| 953 | |
| 954 |
+
resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
|
| 955 |
+
engines: {node: '>=12.0'}
|
| 956 |
+
peerDependencies:
|
| 957 |
+
postcss: ^8.2.14
|
| 958 |
+
|
| 959 | |
| 960 |
+
resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
|
| 961 |
+
engines: {node: '>=4'}
|
| 962 |
+
|
| 963 | |
| 964 |
+
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
|
| 965 |
+
|
| 966 | |
| 967 |
resolution: {integrity: sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==}
|
| 968 |
engines: {node: ^10 || ^12 || >=14}
|
| 969 |
|
| 970 | |
| 971 |
+
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
| 972 |
+
|
| 973 | |
| 974 |
+
resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==}
|
| 975 |
+
peerDependencies:
|
| 976 |
+
react: ^18.3.1
|
| 977 |
+
|
| 978 | |
| 979 |
+
resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
|
| 980 |
+
engines: {node: '>=0.10.0'}
|
| 981 |
+
|
| 982 | |
| 983 |
+
resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
|
| 984 |
+
engines: {node: '>=0.10.0'}
|
| 985 |
+
|
| 986 | |
| 987 |
+
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
|
| 988 |
+
|
| 989 | |
| 990 |
+
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
| 991 |
+
engines: {node: '>=8.10.0'}
|
| 992 |
+
|
| 993 | |
| 994 |
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
|
| 995 |
|
| 996 | |
| 997 |
+
resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
|
| 998 |
+
engines: {node: '>= 0.4'}
|
| 999 |
+
hasBin: true
|
| 1000 |
+
|
| 1001 | |
| 1002 |
resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
|
| 1003 |
engines: {node: '>=18'}
|
| 1004 |
|
| 1005 | |
| 1006 |
+
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
| 1007 |
+
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
| 1008 |
+
|
| 1009 | |
| 1010 |
resolution: {integrity: sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==}
|
| 1011 |
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
| 1012 |
hasBin: true
|
| 1013 |
|
| 1014 | |
| 1015 |
+
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
| 1016 |
+
|
| 1017 | |
| 1018 |
+
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
|
| 1019 |
+
|
| 1020 | |
| 1021 |
+
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
| 1022 |
+
hasBin: true
|
| 1023 |
+
|
| 1024 | |
| 1025 |
resolution: {integrity: sha512-AmH3D9hHPFmnF/oq/rvigfiAouAKyK/TjnrkwZRYSFZxNggJxwvbAbfYrLeuvq7ktUdhuHdVdSjj852Z55R+uA==}
|
| 1026 |
engines: {node: '>=16.0.0'}
|
| 1027 |
|
| 1028 | |
| 1029 |
+
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
| 1030 |
+
engines: {node: '>=8'}
|
| 1031 |
+
|
| 1032 | |
| 1033 |
+
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
| 1034 |
+
engines: {node: '>=8'}
|
| 1035 |
+
|
| 1036 | |
| 1037 |
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
| 1038 |
engines: {node: '>=14'}
|
|
|
|
| 1045 |
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
| 1046 |
engines: {node: '>=0.10.0'}
|
| 1047 |
|
| 1048 | |
| 1049 |
+
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
| 1050 |
+
engines: {node: '>=8'}
|
| 1051 |
+
|
| 1052 | |
| 1053 |
+
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
|
| 1054 |
+
engines: {node: '>=12'}
|
| 1055 |
+
|
| 1056 | |
| 1057 |
resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
|
| 1058 |
engines: {node: '>=18'}
|
| 1059 |
|
| 1060 | |
| 1061 |
+
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
| 1062 |
+
engines: {node: '>=8'}
|
| 1063 |
+
|
| 1064 | |
| 1065 |
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
|
| 1066 |
engines: {node: '>=12'}
|
| 1067 |
|
| 1068 | |
| 1069 |
+
resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
|
| 1070 |
+
engines: {node: '>=16 || 14 >=14.17'}
|
| 1071 |
+
hasBin: true
|
| 1072 |
+
|
| 1073 | |
| 1074 |
+
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
| 1075 |
+
engines: {node: '>= 0.4'}
|
| 1076 |
+
|
| 1077 | |
| 1078 |
+
resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==}
|
| 1079 |
+
|
| 1080 | |
| 1081 |
+
resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==}
|
| 1082 |
+
engines: {node: '>=14.0.0'}
|
| 1083 |
+
hasBin: true
|
| 1084 |
+
|
| 1085 | |
| 1086 |
+
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
|
| 1087 |
+
engines: {node: '>=0.8'}
|
| 1088 |
+
|
| 1089 | |
| 1090 |
+
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
|
| 1091 |
+
|
| 1092 | |
| 1093 |
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
|
| 1094 |
engines: {node: '>=12.0.0'}
|
| 1095 |
|
| 1096 | |
| 1097 |
+
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
| 1098 |
+
engines: {node: '>=8.0'}
|
| 1099 |
+
|
| 1100 | |
| 1101 |
+
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
| 1102 |
+
|
| 1103 | |
| 1104 |
resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==}
|
| 1105 |
engines: {node: '>=18.0.0'}
|
|
|
|
| 1113 | |
| 1114 |
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
| 1115 |
|
| 1116 | |
| 1117 |
+
resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
|
| 1118 |
+
hasBin: true
|
| 1119 |
+
peerDependencies:
|
| 1120 |
+
browserslist: '>= 4.21.0'
|
| 1121 |
+
|
| 1122 | |
| 1123 |
+
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
| 1124 |
+
|
| 1125 | |
| 1126 |
resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==}
|
| 1127 |
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
|
|
|
| 1162 |
yaml:
|
| 1163 |
optional: true
|
| 1164 |
|
| 1165 | |
| 1166 |
+
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
| 1167 |
+
engines: {node: '>= 8'}
|
| 1168 |
+
hasBin: true
|
| 1169 |
+
|
| 1170 | |
| 1171 |
+
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
| 1172 |
+
engines: {node: '>=10'}
|
| 1173 |
+
|
| 1174 | |
| 1175 |
+
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
|
| 1176 |
+
engines: {node: '>=12'}
|
| 1177 |
+
|
| 1178 | |
| 1179 |
resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==}
|
| 1180 |
engines: {node: '>=18'}
|
| 1181 |
|
| 1182 | |
| 1183 |
+
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
| 1184 |
+
|
| 1185 | |
| 1186 |
+
resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==}
|
| 1187 |
+
engines: {node: '>= 14.6'}
|
| 1188 |
+
hasBin: true
|
| 1189 |
+
|
| 1190 |
snapshots:
|
| 1191 |
|
| 1192 |
+
'@alloc/[email protected]': {}
|
| 1193 |
+
|
| 1194 |
+
'@ampproject/[email protected]':
|
| 1195 |
+
dependencies:
|
| 1196 |
+
'@jridgewell/gen-mapping': 0.3.8
|
| 1197 |
+
'@jridgewell/trace-mapping': 0.3.25
|
| 1198 |
+
|
| 1199 |
+
'@babel/[email protected]':
|
| 1200 |
+
dependencies:
|
| 1201 |
+
'@babel/helper-validator-identifier': 7.27.1
|
| 1202 |
+
js-tokens: 4.0.0
|
| 1203 |
+
picocolors: 1.1.1
|
| 1204 |
+
|
| 1205 |
+
'@babel/[email protected]': {}
|
| 1206 |
+
|
| 1207 |
+
'@babel/[email protected]':
|
| 1208 |
+
dependencies:
|
| 1209 |
+
'@ampproject/remapping': 2.3.0
|
| 1210 |
+
'@babel/code-frame': 7.27.1
|
| 1211 |
+
'@babel/generator': 7.27.5
|
| 1212 |
+
'@babel/helper-compilation-targets': 7.27.2
|
| 1213 |
+
'@babel/helper-module-transforms': 7.27.3(@babel/[email protected])
|
| 1214 |
+
'@babel/helpers': 7.27.6
|
| 1215 |
+
'@babel/parser': 7.27.5
|
| 1216 |
+
'@babel/template': 7.27.2
|
| 1217 |
+
'@babel/traverse': 7.27.4
|
| 1218 |
+
'@babel/types': 7.27.6
|
| 1219 |
+
convert-source-map: 2.0.0
|
| 1220 |
+
debug: 4.3.4
|
| 1221 |
+
gensync: 1.0.0-beta.2
|
| 1222 |
+
json5: 2.2.3
|
| 1223 |
+
semver: 6.3.1
|
| 1224 |
+
transitivePeerDependencies:
|
| 1225 |
+
- supports-color
|
| 1226 |
+
|
| 1227 |
+
'@babel/[email protected]':
|
| 1228 |
+
dependencies:
|
| 1229 |
+
'@babel/parser': 7.27.5
|
| 1230 |
+
'@babel/types': 7.27.6
|
| 1231 |
+
'@jridgewell/gen-mapping': 0.3.8
|
| 1232 |
+
'@jridgewell/trace-mapping': 0.3.25
|
| 1233 |
+
jsesc: 3.1.0
|
| 1234 |
+
|
| 1235 |
+
'@babel/[email protected]':
|
| 1236 |
+
dependencies:
|
| 1237 |
+
'@babel/compat-data': 7.27.5
|
| 1238 |
+
'@babel/helper-validator-option': 7.27.1
|
| 1239 |
+
browserslist: 4.25.0
|
| 1240 |
+
lru-cache: 5.1.1
|
| 1241 |
+
semver: 6.3.1
|
| 1242 |
+
|
| 1243 |
+
'@babel/[email protected]':
|
| 1244 |
+
dependencies:
|
| 1245 |
+
'@babel/traverse': 7.27.4
|
| 1246 |
+
'@babel/types': 7.27.6
|
| 1247 |
+
transitivePeerDependencies:
|
| 1248 |
+
- supports-color
|
| 1249 |
+
|
| 1250 |
+
'@babel/[email protected](@babel/[email protected])':
|
| 1251 |
+
dependencies:
|
| 1252 |
+
'@babel/core': 7.27.4
|
| 1253 |
+
'@babel/helper-module-imports': 7.27.1
|
| 1254 |
+
'@babel/helper-validator-identifier': 7.27.1
|
| 1255 |
+
'@babel/traverse': 7.27.4
|
| 1256 |
+
transitivePeerDependencies:
|
| 1257 |
+
- supports-color
|
| 1258 |
+
|
| 1259 |
+
'@babel/[email protected]': {}
|
| 1260 |
+
|
| 1261 |
+
'@babel/[email protected]': {}
|
| 1262 |
+
|
| 1263 |
+
'@babel/[email protected]': {}
|
| 1264 |
+
|
| 1265 |
+
'@babel/[email protected]': {}
|
| 1266 |
+
|
| 1267 |
+
'@babel/[email protected]':
|
| 1268 |
+
dependencies:
|
| 1269 |
+
'@babel/template': 7.27.2
|
| 1270 |
+
'@babel/types': 7.27.6
|
| 1271 |
+
|
| 1272 |
+
'@babel/[email protected]':
|
| 1273 |
+
dependencies:
|
| 1274 |
+
'@babel/types': 7.27.6
|
| 1275 |
+
|
| 1276 |
+
'@babel/[email protected](@babel/[email protected])':
|
| 1277 |
+
dependencies:
|
| 1278 |
+
'@babel/core': 7.27.4
|
| 1279 |
+
'@babel/helper-plugin-utils': 7.27.1
|
| 1280 |
+
|
| 1281 |
+
'@babel/[email protected](@babel/[email protected])':
|
| 1282 |
+
dependencies:
|
| 1283 |
+
'@babel/core': 7.27.4
|
| 1284 |
+
'@babel/helper-plugin-utils': 7.27.1
|
| 1285 |
+
|
| 1286 |
+
'@babel/[email protected]':
|
| 1287 |
+
dependencies:
|
| 1288 |
+
'@babel/code-frame': 7.27.1
|
| 1289 |
+
'@babel/parser': 7.27.5
|
| 1290 |
+
'@babel/types': 7.27.6
|
| 1291 |
+
|
| 1292 |
+
'@babel/[email protected]':
|
| 1293 |
+
dependencies:
|
| 1294 |
+
'@babel/code-frame': 7.27.1
|
| 1295 |
+
'@babel/generator': 7.27.5
|
| 1296 |
+
'@babel/parser': 7.27.5
|
| 1297 |
+
'@babel/template': 7.27.2
|
| 1298 |
+
'@babel/types': 7.27.6
|
| 1299 |
+
debug: 4.3.4
|
| 1300 |
+
globals: 11.12.0
|
| 1301 |
+
transitivePeerDependencies:
|
| 1302 |
+
- supports-color
|
| 1303 |
+
|
| 1304 |
+
'@babel/[email protected]':
|
| 1305 |
+
dependencies:
|
| 1306 |
+
'@babel/helper-string-parser': 7.27.1
|
| 1307 |
+
'@babel/helper-validator-identifier': 7.27.1
|
| 1308 |
+
|
| 1309 |
'@esbuild/[email protected]':
|
| 1310 |
optional: true
|
| 1311 |
|
|
|
|
| 1381 |
'@esbuild/[email protected]':
|
| 1382 |
optional: true
|
| 1383 |
|
| 1384 |
+
'@isaacs/[email protected]':
|
| 1385 |
+
dependencies:
|
| 1386 |
+
string-width: 5.1.2
|
| 1387 |
+
string-width-cjs: [email protected]
|
| 1388 |
+
strip-ansi: 7.1.0
|
| 1389 |
+
strip-ansi-cjs: [email protected]
|
| 1390 |
+
wrap-ansi: 8.1.0
|
| 1391 |
+
wrap-ansi-cjs: [email protected]
|
| 1392 |
+
|
| 1393 |
+
'@jridgewell/[email protected]':
|
| 1394 |
+
dependencies:
|
| 1395 |
+
'@jridgewell/set-array': 1.2.1
|
| 1396 |
+
'@jridgewell/sourcemap-codec': 1.5.0
|
| 1397 |
+
'@jridgewell/trace-mapping': 0.3.25
|
| 1398 |
+
|
| 1399 |
+
'@jridgewell/[email protected]': {}
|
| 1400 |
+
|
| 1401 |
+
'@jridgewell/[email protected]': {}
|
| 1402 |
+
|
| 1403 |
+
'@jridgewell/[email protected]': {}
|
| 1404 |
+
|
| 1405 |
+
'@jridgewell/[email protected]':
|
| 1406 |
+
dependencies:
|
| 1407 |
+
'@jridgewell/resolve-uri': 3.1.2
|
| 1408 |
+
'@jridgewell/sourcemap-codec': 1.5.0
|
| 1409 |
+
|
| 1410 |
+
'@nodelib/[email protected]':
|
| 1411 |
+
dependencies:
|
| 1412 |
+
'@nodelib/fs.stat': 2.0.5
|
| 1413 |
+
run-parallel: 1.2.0
|
| 1414 |
+
|
| 1415 |
+
'@nodelib/[email protected]': {}
|
| 1416 |
+
|
| 1417 |
+
'@nodelib/[email protected]':
|
| 1418 |
+
dependencies:
|
| 1419 |
+
'@nodelib/fs.scandir': 2.1.5
|
| 1420 |
+
fastq: 1.19.1
|
| 1421 |
+
|
| 1422 |
+
'@pkgjs/[email protected]':
|
| 1423 |
+
optional: true
|
| 1424 |
+
|
| 1425 |
+
'@rolldown/[email protected]': {}
|
| 1426 |
+
|
| 1427 |
'@rollup/[email protected]':
|
| 1428 |
optional: true
|
| 1429 |
|
|
|
|
| 1538 |
transitivePeerDependencies:
|
| 1539 |
- supports-color
|
| 1540 |
|
| 1541 |
+
'@types/[email protected]':
|
| 1542 |
+
dependencies:
|
| 1543 |
+
'@babel/parser': 7.27.5
|
| 1544 |
+
'@babel/types': 7.27.6
|
| 1545 |
+
'@types/babel__generator': 7.27.0
|
| 1546 |
+
'@types/babel__template': 7.4.4
|
| 1547 |
+
'@types/babel__traverse': 7.20.7
|
| 1548 |
+
|
| 1549 |
+
'@types/[email protected]':
|
| 1550 |
+
dependencies:
|
| 1551 |
+
'@babel/types': 7.27.6
|
| 1552 |
+
|
| 1553 |
+
'@types/[email protected]':
|
| 1554 |
+
dependencies:
|
| 1555 |
+
'@babel/parser': 7.27.5
|
| 1556 |
+
'@babel/types': 7.27.6
|
| 1557 |
+
|
| 1558 |
+
'@types/[email protected]':
|
| 1559 |
+
dependencies:
|
| 1560 |
+
'@babel/types': 7.27.6
|
| 1561 |
+
|
| 1562 |
'@types/[email protected]': {}
|
| 1563 |
|
| 1564 |
'@types/[email protected]':
|
| 1565 |
dependencies:
|
| 1566 |
undici-types: 6.21.0
|
| 1567 |
|
| 1568 |
+
'@types/[email protected]': {}
|
| 1569 |
+
|
| 1570 |
+
'@types/[email protected](@types/[email protected])':
|
| 1571 |
+
dependencies:
|
| 1572 |
+
'@types/react': 18.3.23
|
| 1573 |
+
|
| 1574 |
+
'@types/[email protected]':
|
| 1575 |
+
dependencies:
|
| 1576 |
+
'@types/prop-types': 15.7.15
|
| 1577 |
+
csstype: 3.1.3
|
| 1578 |
+
|
| 1579 |
+
'@vitejs/[email protected]([email protected](@types/[email protected])([email protected])([email protected])([email protected]))':
|
| 1580 |
+
dependencies:
|
| 1581 |
+
'@babel/core': 7.27.4
|
| 1582 |
+
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/[email protected])
|
| 1583 |
+
'@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/[email protected])
|
| 1584 |
+
'@rolldown/pluginutils': 1.0.0-beta.11
|
| 1585 |
+
'@types/babel__core': 7.20.5
|
| 1586 |
+
react-refresh: 0.17.0
|
| 1587 |
+
vite: 6.3.5(@types/[email protected])([email protected])([email protected])([email protected])
|
| 1588 |
+
transitivePeerDependencies:
|
| 1589 |
+
- supports-color
|
| 1590 |
+
|
| 1591 | |
| 1592 |
dependencies:
|
| 1593 |
environment: 1.1.0
|
| 1594 |
|
| 1595 |
+
[email protected]: {}
|
| 1596 |
+
|
| 1597 | |
| 1598 |
|
| 1599 | |
| 1600 |
+
dependencies:
|
| 1601 |
+
color-convert: 2.0.1
|
| 1602 |
+
|
| 1603 | |
| 1604 |
|
| 1605 |
+
[email protected]: {}
|
| 1606 |
+
|
| 1607 | |
| 1608 |
+
dependencies:
|
| 1609 |
+
normalize-path: 3.0.0
|
| 1610 |
+
picomatch: 2.3.1
|
| 1611 |
+
|
| 1612 |
+
[email protected]: {}
|
| 1613 |
+
|
| 1614 | |
| 1615 |
+
dependencies:
|
| 1616 |
+
browserslist: 4.25.0
|
| 1617 |
+
caniuse-lite: 1.0.30001723
|
| 1618 |
+
fraction.js: 4.3.7
|
| 1619 |
+
normalize-range: 0.1.2
|
| 1620 |
+
picocolors: 1.1.1
|
| 1621 |
+
postcss: 8.5.5
|
| 1622 |
+
postcss-value-parser: 4.2.0
|
| 1623 |
+
|
| 1624 |
+
[email protected]: {}
|
| 1625 |
+
|
| 1626 |
+
[email protected]: {}
|
| 1627 |
+
|
| 1628 | |
| 1629 |
+
dependencies:
|
| 1630 |
+
balanced-match: 1.0.2
|
| 1631 |
+
|
| 1632 | |
| 1633 |
+
dependencies:
|
| 1634 |
+
fill-range: 7.1.1
|
| 1635 |
+
|
| 1636 | |
| 1637 |
+
dependencies:
|
| 1638 |
+
caniuse-lite: 1.0.30001723
|
| 1639 |
+
electron-to-chromium: 1.5.168
|
| 1640 |
+
node-releases: 2.0.19
|
| 1641 |
+
update-browserslist-db: 1.1.3([email protected])
|
| 1642 |
+
|
| 1643 |
+
[email protected]: {}
|
| 1644 |
+
|
| 1645 |
+
[email protected]: {}
|
| 1646 |
+
|
| 1647 | |
| 1648 |
+
dependencies:
|
| 1649 |
+
anymatch: 3.1.3
|
| 1650 |
+
braces: 3.0.3
|
| 1651 |
+
glob-parent: 5.1.2
|
| 1652 |
+
is-binary-path: 2.1.0
|
| 1653 |
+
is-glob: 4.0.3
|
| 1654 |
+
normalize-path: 3.0.0
|
| 1655 |
+
readdirp: 3.6.0
|
| 1656 |
+
optionalDependencies:
|
| 1657 |
+
fsevents: 2.3.3
|
| 1658 |
+
|
| 1659 | |
| 1660 |
+
dependencies:
|
| 1661 |
+
clsx: 2.1.1
|
| 1662 |
+
|
| 1663 | |
| 1664 |
dependencies:
|
| 1665 |
restore-cursor: 5.1.0
|
| 1666 |
|
| 1667 |
+
[email protected]: {}
|
| 1668 |
+
|
| 1669 | |
| 1670 |
+
dependencies:
|
| 1671 |
+
color-name: 1.1.4
|
| 1672 |
+
|
| 1673 |
+
[email protected]: {}
|
| 1674 |
+
|
| 1675 |
+
[email protected]: {}
|
| 1676 |
+
|
| 1677 |
+
[email protected]: {}
|
| 1678 |
+
|
| 1679 | |
| 1680 |
+
dependencies:
|
| 1681 |
+
path-key: 3.1.1
|
| 1682 |
+
shebang-command: 2.0.0
|
| 1683 |
+
which: 2.0.2
|
| 1684 |
+
|
| 1685 |
+
[email protected]: {}
|
| 1686 |
+
|
| 1687 |
+
[email protected]: {}
|
| 1688 |
+
|
| 1689 | |
| 1690 |
dependencies:
|
| 1691 |
ms: 2.1.2
|
| 1692 |
|
| 1693 |
+
[email protected]: {}
|
| 1694 |
+
|
| 1695 |
+
[email protected]: {}
|
| 1696 |
+
|
| 1697 |
+
[email protected]: {}
|
| 1698 |
+
|
| 1699 |
+
[email protected]: {}
|
| 1700 |
+
|
| 1701 | |
| 1702 |
|
| 1703 |
+
[email protected]: {}
|
| 1704 |
+
|
| 1705 |
+
[email protected]: {}
|
| 1706 |
+
|
| 1707 | |
| 1708 |
|
| 1709 | |
|
|
|
| 1734 |
'@esbuild/win32-ia32': 0.25.5
|
| 1735 |
'@esbuild/win32-x64': 0.25.5
|
| 1736 |
|
| 1737 |
+
[email protected]: {}
|
| 1738 |
+
|
| 1739 | |
| 1740 |
+
dependencies:
|
| 1741 |
+
'@nodelib/fs.stat': 2.0.5
|
| 1742 |
+
'@nodelib/fs.walk': 1.2.8
|
| 1743 |
+
glob-parent: 5.1.2
|
| 1744 |
+
merge2: 1.4.1
|
| 1745 |
+
micromatch: 4.0.8
|
| 1746 |
+
|
| 1747 | |
| 1748 |
+
dependencies:
|
| 1749 |
+
reusify: 1.1.0
|
| 1750 |
+
|
| 1751 | |
| 1752 |
optionalDependencies:
|
| 1753 |
picomatch: 4.0.2
|
| 1754 |
|
| 1755 | |
| 1756 |
+
dependencies:
|
| 1757 |
+
to-regex-range: 5.0.1
|
| 1758 |
+
|
| 1759 | |
| 1760 |
+
dependencies:
|
| 1761 |
+
cross-spawn: 7.0.6
|
| 1762 |
+
signal-exit: 4.1.0
|
| 1763 |
+
|
| 1764 |
+
[email protected]: {}
|
| 1765 |
+
|
| 1766 | |
| 1767 |
optional: true
|
| 1768 |
|
| 1769 |
+
[email protected]: {}
|
| 1770 |
+
|
| 1771 |
+
[email protected]: {}
|
| 1772 |
+
|
| 1773 | |
| 1774 |
|
| 1775 | |
| 1776 |
dependencies:
|
| 1777 |
resolve-pkg-maps: 1.0.0
|
| 1778 |
|
| 1779 | |
| 1780 |
+
dependencies:
|
| 1781 |
+
is-glob: 4.0.3
|
| 1782 |
+
|
| 1783 | |
| 1784 |
+
dependencies:
|
| 1785 |
+
is-glob: 4.0.3
|
| 1786 |
+
|
| 1787 | |
| 1788 |
+
dependencies:
|
| 1789 |
+
foreground-child: 3.3.1
|
| 1790 |
+
jackspeak: 3.4.3
|
| 1791 |
+
minimatch: 9.0.5
|
| 1792 |
+
minipass: 7.1.2
|
| 1793 |
+
package-json-from-dist: 1.0.1
|
| 1794 |
+
path-scurry: 1.11.1
|
| 1795 |
+
|
| 1796 |
+
[email protected]: {}
|
| 1797 |
+
|
| 1798 | |
| 1799 |
+
dependencies:
|
| 1800 |
+
function-bind: 1.1.2
|
| 1801 |
+
|
| 1802 | |
| 1803 |
+
dependencies:
|
| 1804 |
+
binary-extensions: 2.3.0
|
| 1805 |
+
|
| 1806 | |
| 1807 |
+
dependencies:
|
| 1808 |
+
hasown: 2.0.2
|
| 1809 |
+
|
| 1810 |
+
[email protected]: {}
|
| 1811 |
+
|
| 1812 |
+
[email protected]: {}
|
| 1813 |
+
|
| 1814 | |
| 1815 |
dependencies:
|
| 1816 |
get-east-asian-width: 1.3.0
|
| 1817 |
|
| 1818 | |
| 1819 |
+
dependencies:
|
| 1820 |
+
is-extglob: 2.1.1
|
| 1821 |
+
|
| 1822 |
+
[email protected]: {}
|
| 1823 |
+
|
| 1824 |
+
[email protected]: {}
|
| 1825 |
+
|
| 1826 | |
| 1827 |
+
dependencies:
|
| 1828 |
+
'@isaacs/cliui': 8.0.2
|
| 1829 |
+
optionalDependencies:
|
| 1830 |
+
'@pkgjs/parseargs': 0.11.0
|
| 1831 |
+
|
| 1832 |
+
[email protected]: {}
|
| 1833 |
+
|
| 1834 |
+
[email protected]: {}
|
| 1835 |
+
|
| 1836 |
+
[email protected]: {}
|
| 1837 |
+
|
| 1838 |
+
[email protected]: {}
|
| 1839 |
+
|
| 1840 |
+
[email protected]: {}
|
| 1841 |
+
|
| 1842 |
+
[email protected]: {}
|
| 1843 |
+
|
| 1844 | |
| 1845 |
dependencies:
|
| 1846 |
ansi-escapes: 7.0.0
|
|
|
|
| 1849 |
strip-ansi: 7.1.0
|
| 1850 |
wrap-ansi: 9.0.0
|
| 1851 |
|
| 1852 | |
| 1853 |
+
dependencies:
|
| 1854 |
+
js-tokens: 4.0.0
|
| 1855 |
+
|
| 1856 |
+
[email protected]: {}
|
| 1857 |
+
|
| 1858 | |
| 1859 |
+
dependencies:
|
| 1860 |
+
yallist: 3.1.1
|
| 1861 |
+
|
| 1862 | |
| 1863 |
+
dependencies:
|
| 1864 |
+
react: 18.3.1
|
| 1865 |
+
|
| 1866 |
+
[email protected]: {}
|
| 1867 |
+
|
| 1868 | |
| 1869 |
+
dependencies:
|
| 1870 |
+
braces: 3.0.3
|
| 1871 |
+
picomatch: 2.3.1
|
| 1872 |
+
|
| 1873 | |
| 1874 |
|
| 1875 | |
| 1876 |
+
dependencies:
|
| 1877 |
+
brace-expansion: 2.0.2
|
| 1878 |
+
|
| 1879 |
+
[email protected]: {}
|
| 1880 |
+
|
| 1881 | |
| 1882 |
|
| 1883 | |
| 1884 |
+
dependencies:
|
| 1885 |
+
any-promise: 1.3.0
|
| 1886 |
+
object-assign: 4.1.1
|
| 1887 |
+
thenify-all: 1.6.0
|
| 1888 |
+
|
| 1889 | |
| 1890 |
|
| 1891 | |
| 1892 |
|
| 1893 | |
| 1894 |
|
| 1895 |
+
[email protected]: {}
|
| 1896 |
+
|
| 1897 |
+
[email protected]: {}
|
| 1898 |
+
|
| 1899 |
+
[email protected]: {}
|
| 1900 |
+
|
| 1901 |
+
[email protected]: {}
|
| 1902 |
+
|
| 1903 |
+
[email protected]: {}
|
| 1904 |
+
|
| 1905 | |
| 1906 |
dependencies:
|
| 1907 |
mimic-function: 5.0.1
|
| 1908 |
|
| 1909 |
+
[email protected]: {}
|
| 1910 |
+
|
| 1911 |
+
[email protected]: {}
|
| 1912 |
+
|
| 1913 |
+
[email protected]: {}
|
| 1914 |
+
|
| 1915 | |
| 1916 |
+
dependencies:
|
| 1917 |
+
lru-cache: 10.4.3
|
| 1918 |
+
minipass: 7.1.2
|
| 1919 |
+
|
| 1920 | |
| 1921 |
|
| 1922 |
+
[email protected]: {}
|
| 1923 |
+
|
| 1924 | |
| 1925 |
|
| 1926 |
+
[email protected]: {}
|
| 1927 |
+
|
| 1928 |
+
[email protected]: {}
|
| 1929 |
+
|
| 1930 | |
| 1931 |
+
dependencies:
|
| 1932 |
+
postcss: 8.5.5
|
| 1933 |
+
postcss-value-parser: 4.2.0
|
| 1934 |
+
read-cache: 1.0.0
|
| 1935 |
+
resolve: 1.22.10
|
| 1936 |
+
|
| 1937 | |
| 1938 |
+
dependencies:
|
| 1939 |
+
camelcase-css: 2.0.1
|
| 1940 |
+
postcss: 8.5.5
|
| 1941 |
+
|
| 1942 | |
| 1943 |
+
dependencies:
|
| 1944 |
+
lilconfig: 3.1.3
|
| 1945 |
+
yaml: 2.8.0
|
| 1946 |
+
optionalDependencies:
|
| 1947 |
+
postcss: 8.5.5
|
| 1948 |
+
|
| 1949 | |
| 1950 |
+
dependencies:
|
| 1951 |
+
postcss: 8.5.5
|
| 1952 |
+
postcss-selector-parser: 6.1.2
|
| 1953 |
+
|
| 1954 | |
| 1955 |
+
dependencies:
|
| 1956 |
+
cssesc: 3.0.0
|
| 1957 |
+
util-deprecate: 1.0.2
|
| 1958 |
+
|
| 1959 |
+
[email protected]: {}
|
| 1960 |
+
|
| 1961 | |
| 1962 |
dependencies:
|
| 1963 |
nanoid: 3.3.11
|
| 1964 |
picocolors: 1.1.1
|
| 1965 |
source-map-js: 1.2.1
|
| 1966 |
|
| 1967 |
+
[email protected]: {}
|
| 1968 |
+
|
| 1969 | |
| 1970 |
+
dependencies:
|
| 1971 |
+
loose-envify: 1.4.0
|
| 1972 |
+
react: 18.3.1
|
| 1973 |
+
scheduler: 0.23.2
|
| 1974 |
+
|
| 1975 |
+
[email protected]: {}
|
| 1976 |
+
|
| 1977 | |
| 1978 |
+
dependencies:
|
| 1979 |
+
loose-envify: 1.4.0
|
| 1980 |
+
|
| 1981 | |
| 1982 |
+
dependencies:
|
| 1983 |
+
pify: 2.3.0
|
| 1984 |
+
|
| 1985 | |
| 1986 |
+
dependencies:
|
| 1987 |
+
picomatch: 2.3.1
|
| 1988 |
+
|
| 1989 | |
| 1990 |
|
| 1991 | |
| 1992 |
+
dependencies:
|
| 1993 |
+
is-core-module: 2.16.1
|
| 1994 |
+
path-parse: 1.0.7
|
| 1995 |
+
supports-preserve-symlinks-flag: 1.0.0
|
| 1996 |
+
|
| 1997 | |
| 1998 |
dependencies:
|
| 1999 |
onetime: 7.0.0
|
| 2000 |
signal-exit: 4.1.0
|
| 2001 |
|
| 2002 |
+
[email protected]: {}
|
| 2003 |
+
|
| 2004 | |
| 2005 |
dependencies:
|
| 2006 |
'@types/estree': 1.0.7
|
|
|
|
| 2027 |
'@rollup/rollup-win32-x64-msvc': 4.43.0
|
| 2028 |
fsevents: 2.3.3
|
| 2029 |
|
| 2030 | |
| 2031 |
+
dependencies:
|
| 2032 |
+
queue-microtask: 1.2.3
|
| 2033 |
+
|
| 2034 | |
| 2035 |
+
dependencies:
|
| 2036 |
+
loose-envify: 1.4.0
|
| 2037 |
+
|
| 2038 |
+
[email protected]: {}
|
| 2039 |
+
|
| 2040 | |
| 2041 |
dependencies:
|
| 2042 |
'@serialport/binding-mock': 10.2.2
|
|
|
|
| 2056 |
transitivePeerDependencies:
|
| 2057 |
- supports-color
|
| 2058 |
|
| 2059 | |
| 2060 |
+
dependencies:
|
| 2061 |
+
shebang-regex: 3.0.0
|
| 2062 |
+
|
| 2063 |
+
[email protected]: {}
|
| 2064 |
+
|
| 2065 | |
| 2066 |
|
| 2067 | |
|
|
|
| 2071 |
|
| 2072 | |
| 2073 |
|
| 2074 | |
| 2075 |
+
dependencies:
|
| 2076 |
+
emoji-regex: 8.0.0
|
| 2077 |
+
is-fullwidth-code-point: 3.0.0
|
| 2078 |
+
strip-ansi: 6.0.1
|
| 2079 |
+
|
| 2080 | |
| 2081 |
+
dependencies:
|
| 2082 |
+
eastasianwidth: 0.2.0
|
| 2083 |
+
emoji-regex: 9.2.2
|
| 2084 |
+
strip-ansi: 7.1.0
|
| 2085 |
+
|
| 2086 | |
| 2087 |
dependencies:
|
| 2088 |
emoji-regex: 10.4.0
|
| 2089 |
get-east-asian-width: 1.3.0
|
| 2090 |
strip-ansi: 7.1.0
|
| 2091 |
|
| 2092 | |
| 2093 |
+
dependencies:
|
| 2094 |
+
ansi-regex: 5.0.1
|
| 2095 |
+
|
| 2096 | |
| 2097 |
dependencies:
|
| 2098 |
ansi-regex: 6.1.0
|
| 2099 |
|
| 2100 | |
| 2101 |
+
dependencies:
|
| 2102 |
+
'@jridgewell/gen-mapping': 0.3.8
|
| 2103 |
+
commander: 4.1.1
|
| 2104 |
+
glob: 10.4.5
|
| 2105 |
+
lines-and-columns: 1.2.4
|
| 2106 |
+
mz: 2.7.0
|
| 2107 |
+
pirates: 4.0.7
|
| 2108 |
+
ts-interface-checker: 0.1.13
|
| 2109 |
+
|
| 2110 |
+
[email protected]: {}
|
| 2111 |
+
|
| 2112 |
+
[email protected]: {}
|
| 2113 |
+
|
| 2114 | |
| 2115 |
+
dependencies:
|
| 2116 |
+
'@alloc/quick-lru': 5.2.0
|
| 2117 |
+
arg: 5.0.2
|
| 2118 |
+
chokidar: 3.6.0
|
| 2119 |
+
didyoumean: 1.2.2
|
| 2120 |
+
dlv: 1.1.3
|
| 2121 |
+
fast-glob: 3.3.3
|
| 2122 |
+
glob-parent: 6.0.2
|
| 2123 |
+
is-glob: 4.0.3
|
| 2124 |
+
jiti: 1.21.7
|
| 2125 |
+
lilconfig: 3.1.3
|
| 2126 |
+
micromatch: 4.0.8
|
| 2127 |
+
normalize-path: 3.0.0
|
| 2128 |
+
object-hash: 3.0.0
|
| 2129 |
+
picocolors: 1.1.1
|
| 2130 |
+
postcss: 8.5.5
|
| 2131 |
+
postcss-import: 15.1.0([email protected])
|
| 2132 |
+
postcss-js: 4.0.1([email protected])
|
| 2133 |
+
postcss-load-config: 4.0.2([email protected])
|
| 2134 |
+
postcss-nested: 6.2.0([email protected])
|
| 2135 |
+
postcss-selector-parser: 6.1.2
|
| 2136 |
+
resolve: 1.22.10
|
| 2137 |
+
sucrase: 3.35.0
|
| 2138 |
+
transitivePeerDependencies:
|
| 2139 |
+
- ts-node
|
| 2140 |
+
|
| 2141 | |
| 2142 |
+
dependencies:
|
| 2143 |
+
thenify: 3.3.1
|
| 2144 |
+
|
| 2145 | |
| 2146 |
+
dependencies:
|
| 2147 |
+
any-promise: 1.3.0
|
| 2148 |
+
|
| 2149 | |
| 2150 |
dependencies:
|
| 2151 |
fdir: 6.4.6([email protected])
|
| 2152 |
picomatch: 4.0.2
|
| 2153 |
|
| 2154 | |
| 2155 |
+
dependencies:
|
| 2156 |
+
is-number: 7.0.0
|
| 2157 |
+
|
| 2158 |
+
[email protected]: {}
|
| 2159 |
+
|
| 2160 | |
| 2161 |
dependencies:
|
| 2162 |
esbuild: 0.25.5
|
|
|
|
| 2168 |
|
| 2169 | |
| 2170 |
|
| 2171 |
+
update-browserslist-db@1.1.3(browserslist@4.25.0):
|
| 2172 |
+
dependencies:
|
| 2173 |
+
browserslist: 4.25.0
|
| 2174 |
+
escalade: 3.2.0
|
| 2175 |
+
picocolors: 1.1.1
|
| 2176 |
+
|
| 2177 |
+
[email protected]: {}
|
| 2178 |
+
|
| 2179 | |
| 2180 |
dependencies:
|
| 2181 |
esbuild: 0.25.5
|
| 2182 |
fdir: 6.4.6([email protected])
|
|
|
|
| 2187 |
optionalDependencies:
|
| 2188 |
'@types/node': 22.15.31
|
| 2189 |
fsevents: 2.3.3
|
| 2190 |
+
jiti: 1.21.7
|
| 2191 |
tsx: 4.20.3
|
| 2192 |
+
yaml: 2.8.0
|
| 2193 |
+
|
| 2194 | |
| 2195 |
+
dependencies:
|
| 2196 |
+
isexe: 2.0.0
|
| 2197 |
+
|
| 2198 | |
| 2199 |
+
dependencies:
|
| 2200 |
+
ansi-styles: 4.3.0
|
| 2201 |
+
string-width: 4.2.3
|
| 2202 |
+
strip-ansi: 6.0.1
|
| 2203 |
+
|
| 2204 | |
| 2205 |
+
dependencies:
|
| 2206 |
+
ansi-styles: 6.2.1
|
| 2207 |
+
string-width: 5.1.2
|
| 2208 |
+
strip-ansi: 7.1.0
|
| 2209 |
|
| 2210 | |
| 2211 |
dependencies:
|
| 2212 |
ansi-styles: 6.2.1
|
| 2213 |
string-width: 7.2.0
|
| 2214 |
strip-ansi: 7.1.0
|
| 2215 |
+
|
| 2216 |
+
[email protected]: {}
|
| 2217 |
+
|
| 2218 |
+
[email protected]: {}
|
postcss.config.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import tailwindcss from "tailwindcss";
|
| 2 |
+
import autoprefixer from "autoprefixer";
|
| 3 |
+
|
| 4 |
+
export default {
|
| 5 |
+
plugins: [tailwindcss, autoprefixer],
|
| 6 |
+
};
|
postcss.config.mjs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import tailwindcss from "tailwindcss";
|
| 2 |
+
import autoprefixer from "autoprefixer";
|
| 3 |
+
|
| 4 |
+
export default {
|
| 5 |
+
plugins: [tailwindcss, autoprefixer],
|
| 6 |
+
};
|
src/demo/App.tsx
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState } from "react";
|
| 2 |
+
import { Home } from "./pages/Home";
|
| 3 |
+
import { ErrorBoundary } from "./components/ErrorBoundary";
|
| 4 |
+
import type { ConnectedRobot } from "./types";
|
| 5 |
+
|
| 6 |
+
export function App() {
|
| 7 |
+
const [connectedRobots, setConnectedRobots] = useState<ConnectedRobot[]>([]);
|
| 8 |
+
|
| 9 |
+
return (
|
| 10 |
+
<ErrorBoundary>
|
| 11 |
+
<div className="min-h-screen bg-background">
|
| 12 |
+
<Home
|
| 13 |
+
onGetStarted={() => {}} // No longer needed
|
| 14 |
+
connectedRobots={connectedRobots}
|
| 15 |
+
onConnectedRobotsChange={setConnectedRobots}
|
| 16 |
+
/>
|
| 17 |
+
</div>
|
| 18 |
+
</ErrorBoundary>
|
| 19 |
+
);
|
| 20 |
+
}
|
src/demo/components/CalibrationPanel.tsx
ADDED
|
@@ -0,0 +1,526 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState, useEffect, useCallback, useRef } from "react";
|
| 2 |
+
import { Button } from "./ui/button";
|
| 3 |
+
import {
|
| 4 |
+
Card,
|
| 5 |
+
CardContent,
|
| 6 |
+
CardDescription,
|
| 7 |
+
CardHeader,
|
| 8 |
+
CardTitle,
|
| 9 |
+
} from "./ui/card";
|
| 10 |
+
import { Badge } from "./ui/badge";
|
| 11 |
+
import { calibrateWithPort } from "../../lerobot/web/calibrate";
|
| 12 |
+
import type { ConnectedRobot } from "../types";
|
| 13 |
+
|
| 14 |
+
interface CalibrationPanelProps {
|
| 15 |
+
robot: ConnectedRobot;
|
| 16 |
+
onFinish: () => void;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
interface MotorCalibrationData {
|
| 20 |
+
name: string;
|
| 21 |
+
current: number;
|
| 22 |
+
min: number;
|
| 23 |
+
max: number;
|
| 24 |
+
range: number;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
export function CalibrationPanel({ robot, onFinish }: CalibrationPanelProps) {
|
| 28 |
+
const [isCalibrating, setIsCalibrating] = useState(false);
|
| 29 |
+
const [motorData, setMotorData] = useState<MotorCalibrationData[]>([]);
|
| 30 |
+
const [calibrationStatus, setCalibrationStatus] =
|
| 31 |
+
useState<string>("Ready to calibrate");
|
| 32 |
+
const [calibrationComplete, setCalibrationComplete] = useState(false);
|
| 33 |
+
const [readCount, setReadCount] = useState(0);
|
| 34 |
+
|
| 35 |
+
const animationFrameRef = useRef<number>();
|
| 36 |
+
const lastReadTime = useRef<number>(0);
|
| 37 |
+
const isReading = useRef<boolean>(false);
|
| 38 |
+
|
| 39 |
+
// Motor names matching Node CLI exactly
|
| 40 |
+
const motorNames = [
|
| 41 |
+
"waist",
|
| 42 |
+
"shoulder",
|
| 43 |
+
"elbow",
|
| 44 |
+
"forearm_roll",
|
| 45 |
+
"wrist_angle",
|
| 46 |
+
"wrist_rotate",
|
| 47 |
+
];
|
| 48 |
+
|
| 49 |
+
// Initialize motor data with center positions
|
| 50 |
+
const initializeMotorData = useCallback(() => {
|
| 51 |
+
const initialData = motorNames.map((name) => ({
|
| 52 |
+
name,
|
| 53 |
+
current: 2047, // Center position for STS3215 (4095/2)
|
| 54 |
+
min: 2047,
|
| 55 |
+
max: 2047,
|
| 56 |
+
range: 0,
|
| 57 |
+
}));
|
| 58 |
+
setMotorData(initialData);
|
| 59 |
+
setReadCount(0);
|
| 60 |
+
}, []);
|
| 61 |
+
|
| 62 |
+
// Keep track of last known good positions to avoid glitches
|
| 63 |
+
const lastKnownPositions = useRef<number[]>([
|
| 64 |
+
2047, 2047, 2047, 2047, 2047, 2047,
|
| 65 |
+
]);
|
| 66 |
+
|
| 67 |
+
// Read actual motor positions with robust error handling
|
| 68 |
+
const readMotorPositions = useCallback(async (): Promise<number[]> => {
|
| 69 |
+
if (!robot.port || !robot.port.readable || !robot.port.writable) {
|
| 70 |
+
throw new Error("Robot port not available for communication");
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
const positions: number[] = [];
|
| 74 |
+
const motorIds = [1, 2, 3, 4, 5, 6];
|
| 75 |
+
|
| 76 |
+
// Get persistent reader/writer for this session
|
| 77 |
+
const reader = robot.port.readable.getReader();
|
| 78 |
+
const writer = robot.port.writable.getWriter();
|
| 79 |
+
|
| 80 |
+
try {
|
| 81 |
+
for (let index = 0; index < motorIds.length; index++) {
|
| 82 |
+
const motorId = motorIds[index];
|
| 83 |
+
let success = false;
|
| 84 |
+
let retries = 2; // Allow 2 retries per motor
|
| 85 |
+
|
| 86 |
+
while (!success && retries > 0) {
|
| 87 |
+
try {
|
| 88 |
+
// Create STS3215 Read Position packet
|
| 89 |
+
const packet = new Uint8Array([
|
| 90 |
+
0xff,
|
| 91 |
+
0xff,
|
| 92 |
+
motorId,
|
| 93 |
+
0x04,
|
| 94 |
+
0x02,
|
| 95 |
+
0x38,
|
| 96 |
+
0x02,
|
| 97 |
+
0x00,
|
| 98 |
+
]);
|
| 99 |
+
const checksum = ~(motorId + 0x04 + 0x02 + 0x38 + 0x02) & 0xff;
|
| 100 |
+
packet[7] = checksum;
|
| 101 |
+
|
| 102 |
+
// Write packet
|
| 103 |
+
await writer.write(packet);
|
| 104 |
+
|
| 105 |
+
// Wait for response
|
| 106 |
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
| 107 |
+
|
| 108 |
+
// Read with timeout
|
| 109 |
+
const timeoutPromise = new Promise((_, reject) =>
|
| 110 |
+
setTimeout(() => reject(new Error("Timeout")), 100)
|
| 111 |
+
);
|
| 112 |
+
|
| 113 |
+
const result = (await Promise.race([
|
| 114 |
+
reader.read(),
|
| 115 |
+
timeoutPromise,
|
| 116 |
+
])) as ReadableStreamReadResult<Uint8Array>;
|
| 117 |
+
|
| 118 |
+
if (
|
| 119 |
+
result &&
|
| 120 |
+
!result.done &&
|
| 121 |
+
result.value &&
|
| 122 |
+
result.value.length >= 7
|
| 123 |
+
) {
|
| 124 |
+
const response = result.value;
|
| 125 |
+
const responseId = response[2];
|
| 126 |
+
const error = response[4];
|
| 127 |
+
|
| 128 |
+
// Check if this is the response we're looking for
|
| 129 |
+
if (responseId === motorId && error === 0) {
|
| 130 |
+
const position = response[5] | (response[6] << 8);
|
| 131 |
+
positions.push(position);
|
| 132 |
+
lastKnownPositions.current[index] = position; // Update last known good position
|
| 133 |
+
success = true;
|
| 134 |
+
} else {
|
| 135 |
+
// Wrong motor ID or error - might be out of sync, try again
|
| 136 |
+
retries--;
|
| 137 |
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
| 138 |
+
}
|
| 139 |
+
} else {
|
| 140 |
+
retries--;
|
| 141 |
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
| 142 |
+
}
|
| 143 |
+
} catch (error) {
|
| 144 |
+
retries--;
|
| 145 |
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
| 146 |
+
}
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
if (!success) {
|
| 150 |
+
// Use last known good position instead of fallback center position
|
| 151 |
+
positions.push(lastKnownPositions.current[index]);
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
// Small delay between motors
|
| 155 |
+
await new Promise((resolve) => setTimeout(resolve, 2));
|
| 156 |
+
}
|
| 157 |
+
} finally {
|
| 158 |
+
reader.releaseLock();
|
| 159 |
+
writer.releaseLock();
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
return positions;
|
| 163 |
+
}, [robot.port]);
|
| 164 |
+
|
| 165 |
+
// Update motor data with new readings - NO SIMULATION, REAL VALUES ONLY
|
| 166 |
+
const updateMotorData = useCallback(async () => {
|
| 167 |
+
if (!isCalibrating || isReading.current) return;
|
| 168 |
+
|
| 169 |
+
const now = performance.now();
|
| 170 |
+
// Read at ~15Hz to reduce serial communication load (66ms intervals)
|
| 171 |
+
if (now - lastReadTime.current < 66) return;
|
| 172 |
+
|
| 173 |
+
lastReadTime.current = now;
|
| 174 |
+
isReading.current = true;
|
| 175 |
+
|
| 176 |
+
try {
|
| 177 |
+
const positions = await readMotorPositions();
|
| 178 |
+
|
| 179 |
+
// Always update since we're now keeping last known good positions
|
| 180 |
+
// Only show warning if all motors are still at center position (no successful reads yet)
|
| 181 |
+
const allAtCenter = positions.every((pos) => pos === 2047);
|
| 182 |
+
if (allAtCenter && readCount === 0) {
|
| 183 |
+
console.log("No motor data received yet - still trying to connect");
|
| 184 |
+
setCalibrationStatus("Connecting to motors - please wait...");
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
setMotorData((prev) =>
|
| 188 |
+
prev.map((motor, index) => {
|
| 189 |
+
const current = positions[index];
|
| 190 |
+
const min = Math.min(motor.min, current);
|
| 191 |
+
const max = Math.max(motor.max, current);
|
| 192 |
+
const range = max - min;
|
| 193 |
+
|
| 194 |
+
return {
|
| 195 |
+
...motor,
|
| 196 |
+
current,
|
| 197 |
+
min,
|
| 198 |
+
max,
|
| 199 |
+
range,
|
| 200 |
+
};
|
| 201 |
+
})
|
| 202 |
+
);
|
| 203 |
+
|
| 204 |
+
setReadCount((prev) => prev + 1);
|
| 205 |
+
console.log(`Real motor positions:`, positions);
|
| 206 |
+
} catch (error) {
|
| 207 |
+
console.warn("Failed to read motor positions:", error);
|
| 208 |
+
setCalibrationStatus(
|
| 209 |
+
`Error reading motors: ${
|
| 210 |
+
error instanceof Error ? error.message : error
|
| 211 |
+
}`
|
| 212 |
+
);
|
| 213 |
+
} finally {
|
| 214 |
+
isReading.current = false;
|
| 215 |
+
}
|
| 216 |
+
}, [isCalibrating, readMotorPositions]);
|
| 217 |
+
|
| 218 |
+
// Animation loop using RAF (requestAnimationFrame)
|
| 219 |
+
const animationLoop = useCallback(() => {
|
| 220 |
+
updateMotorData();
|
| 221 |
+
|
| 222 |
+
if (isCalibrating) {
|
| 223 |
+
animationFrameRef.current = requestAnimationFrame(animationLoop);
|
| 224 |
+
}
|
| 225 |
+
}, [isCalibrating, updateMotorData]);
|
| 226 |
+
|
| 227 |
+
useEffect(() => {
|
| 228 |
+
initializeMotorData();
|
| 229 |
+
}, [initializeMotorData]);
|
| 230 |
+
|
| 231 |
+
useEffect(() => {
|
| 232 |
+
if (isCalibrating) {
|
| 233 |
+
animationFrameRef.current = requestAnimationFrame(animationLoop);
|
| 234 |
+
} else {
|
| 235 |
+
if (animationFrameRef.current) {
|
| 236 |
+
cancelAnimationFrame(animationFrameRef.current);
|
| 237 |
+
}
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
return () => {
|
| 241 |
+
if (animationFrameRef.current) {
|
| 242 |
+
cancelAnimationFrame(animationFrameRef.current);
|
| 243 |
+
}
|
| 244 |
+
};
|
| 245 |
+
}, [isCalibrating, animationLoop]);
|
| 246 |
+
|
| 247 |
+
const startCalibration = async () => {
|
| 248 |
+
if (!robot.port || !robot.robotType) {
|
| 249 |
+
setCalibrationStatus("Error: Invalid robot configuration");
|
| 250 |
+
return;
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
setCalibrationStatus(
|
| 254 |
+
"Initializing calibration - reading current positions..."
|
| 255 |
+
);
|
| 256 |
+
|
| 257 |
+
try {
|
| 258 |
+
// Get current positions to use as starting point for min/max
|
| 259 |
+
const currentPositions = await readMotorPositions();
|
| 260 |
+
|
| 261 |
+
// Reset calibration data with current positions as both min and max
|
| 262 |
+
const freshData = motorNames.map((name, index) => ({
|
| 263 |
+
name,
|
| 264 |
+
current: currentPositions[index],
|
| 265 |
+
min: currentPositions[index], // Start with current position
|
| 266 |
+
max: currentPositions[index], // Start with current position
|
| 267 |
+
range: 0, // No range yet
|
| 268 |
+
}));
|
| 269 |
+
|
| 270 |
+
setMotorData(freshData);
|
| 271 |
+
setReadCount(0);
|
| 272 |
+
setIsCalibrating(true);
|
| 273 |
+
setCalibrationComplete(false);
|
| 274 |
+
setCalibrationStatus(
|
| 275 |
+
"Recording ranges of motion - move all joints through their full range..."
|
| 276 |
+
);
|
| 277 |
+
} catch (error) {
|
| 278 |
+
setCalibrationStatus(
|
| 279 |
+
`Error starting calibration: ${
|
| 280 |
+
error instanceof Error ? error.message : error
|
| 281 |
+
}`
|
| 282 |
+
);
|
| 283 |
+
}
|
| 284 |
+
};
|
| 285 |
+
|
| 286 |
+
// Generate calibration config JSON matching Node CLI format
|
| 287 |
+
const generateConfigJSON = () => {
|
| 288 |
+
const calibrationData = {
|
| 289 |
+
homing_offset: motorData.map((motor) => motor.current - 2047), // Center offset
|
| 290 |
+
drive_mode: [3, 3, 3, 3, 3, 3], // SO-100 standard drive mode
|
| 291 |
+
start_pos: motorData.map((motor) => motor.min),
|
| 292 |
+
end_pos: motorData.map((motor) => motor.max),
|
| 293 |
+
calib_mode: ["middle", "middle", "middle", "middle", "middle", "middle"], // SO-100 standard
|
| 294 |
+
motor_names: motorNames,
|
| 295 |
+
};
|
| 296 |
+
|
| 297 |
+
return calibrationData;
|
| 298 |
+
};
|
| 299 |
+
|
| 300 |
+
// Download calibration config as JSON file
|
| 301 |
+
const downloadConfigJSON = () => {
|
| 302 |
+
const configData = generateConfigJSON();
|
| 303 |
+
const jsonString = JSON.stringify(configData, null, 2);
|
| 304 |
+
const blob = new Blob([jsonString], { type: "application/json" });
|
| 305 |
+
const url = URL.createObjectURL(blob);
|
| 306 |
+
|
| 307 |
+
const link = document.createElement("a");
|
| 308 |
+
link.href = url;
|
| 309 |
+
link.download = `${robot.robotId || robot.robotType}_calibration.json`;
|
| 310 |
+
document.body.appendChild(link);
|
| 311 |
+
link.click();
|
| 312 |
+
document.body.removeChild(link);
|
| 313 |
+
URL.revokeObjectURL(url);
|
| 314 |
+
};
|
| 315 |
+
|
| 316 |
+
const finishCalibration = () => {
|
| 317 |
+
setIsCalibrating(false);
|
| 318 |
+
setCalibrationComplete(true);
|
| 319 |
+
setCalibrationStatus(
|
| 320 |
+
`✅ Calibration completed! Recorded ${readCount} position readings.`
|
| 321 |
+
);
|
| 322 |
+
|
| 323 |
+
// Save calibration config to localStorage using serial number
|
| 324 |
+
const configData = generateConfigJSON();
|
| 325 |
+
const serialNumber = (robot as any).serialNumber;
|
| 326 |
+
|
| 327 |
+
if (!serialNumber) {
|
| 328 |
+
console.warn("⚠️ No serial number available for calibration storage");
|
| 329 |
+
setCalibrationStatus(
|
| 330 |
+
`⚠️ Calibration completed but cannot save - no robot serial number`
|
| 331 |
+
);
|
| 332 |
+
return;
|
| 333 |
+
}
|
| 334 |
+
|
| 335 |
+
const calibrationKey = `lerobot-calibration-${serialNumber}`;
|
| 336 |
+
try {
|
| 337 |
+
localStorage.setItem(
|
| 338 |
+
calibrationKey,
|
| 339 |
+
JSON.stringify({
|
| 340 |
+
config: configData,
|
| 341 |
+
timestamp: new Date().toISOString(),
|
| 342 |
+
serialNumber: serialNumber,
|
| 343 |
+
robotId: robot.robotId,
|
| 344 |
+
robotType: robot.robotType,
|
| 345 |
+
readCount: readCount,
|
| 346 |
+
})
|
| 347 |
+
);
|
| 348 |
+
console.log(`💾 Calibration saved for robot serial: ${serialNumber}`);
|
| 349 |
+
} catch (error) {
|
| 350 |
+
console.warn("Failed to save calibration to localStorage:", error);
|
| 351 |
+
setCalibrationStatus(
|
| 352 |
+
`⚠️ Calibration completed but save failed: ${error}`
|
| 353 |
+
);
|
| 354 |
+
}
|
| 355 |
+
};
|
| 356 |
+
|
| 357 |
+
return (
|
| 358 |
+
<div className="space-y-4">
|
| 359 |
+
{/* Calibration Status Card */}
|
| 360 |
+
<Card>
|
| 361 |
+
<CardHeader>
|
| 362 |
+
<div className="flex items-center justify-between">
|
| 363 |
+
<div>
|
| 364 |
+
<CardTitle className="text-lg">
|
| 365 |
+
🛠️ Calibrating: {robot.robotId}
|
| 366 |
+
</CardTitle>
|
| 367 |
+
<CardDescription>
|
| 368 |
+
{robot.robotType?.replace("_", " ")} • {robot.name}
|
| 369 |
+
</CardDescription>
|
| 370 |
+
</div>
|
| 371 |
+
<Badge
|
| 372 |
+
variant={
|
| 373 |
+
isCalibrating
|
| 374 |
+
? "default"
|
| 375 |
+
: calibrationComplete
|
| 376 |
+
? "default"
|
| 377 |
+
: "outline"
|
| 378 |
+
}
|
| 379 |
+
>
|
| 380 |
+
{isCalibrating
|
| 381 |
+
? "Recording"
|
| 382 |
+
: calibrationComplete
|
| 383 |
+
? "Complete"
|
| 384 |
+
: "Ready"}
|
| 385 |
+
</Badge>
|
| 386 |
+
</div>
|
| 387 |
+
</CardHeader>
|
| 388 |
+
<CardContent>
|
| 389 |
+
<div className="space-y-4">
|
| 390 |
+
<div className="p-3 bg-blue-50 rounded-lg">
|
| 391 |
+
<p className="text-sm font-medium text-blue-900">Status:</p>
|
| 392 |
+
<p className="text-sm text-blue-800">{calibrationStatus}</p>
|
| 393 |
+
{isCalibrating && (
|
| 394 |
+
<p className="text-xs text-blue-600 mt-1">
|
| 395 |
+
Readings: {readCount} | Press "Finish Calibration" when done
|
| 396 |
+
</p>
|
| 397 |
+
)}
|
| 398 |
+
</div>
|
| 399 |
+
|
| 400 |
+
<div className="flex gap-2">
|
| 401 |
+
{!isCalibrating && !calibrationComplete && (
|
| 402 |
+
<Button onClick={startCalibration}>Start Calibration</Button>
|
| 403 |
+
)}
|
| 404 |
+
|
| 405 |
+
{isCalibrating && (
|
| 406 |
+
<Button onClick={finishCalibration} variant="outline">
|
| 407 |
+
Finish Calibration
|
| 408 |
+
</Button>
|
| 409 |
+
)}
|
| 410 |
+
|
| 411 |
+
{calibrationComplete && (
|
| 412 |
+
<>
|
| 413 |
+
<Button onClick={downloadConfigJSON} variant="outline">
|
| 414 |
+
Download Config JSON
|
| 415 |
+
</Button>
|
| 416 |
+
<Button onClick={onFinish}>Done</Button>
|
| 417 |
+
</>
|
| 418 |
+
)}
|
| 419 |
+
</div>
|
| 420 |
+
</div>
|
| 421 |
+
</CardContent>
|
| 422 |
+
</Card>
|
| 423 |
+
|
| 424 |
+
{/* Configuration JSON Display */}
|
| 425 |
+
{calibrationComplete && (
|
| 426 |
+
<Card>
|
| 427 |
+
<CardHeader>
|
| 428 |
+
<CardTitle className="text-lg">
|
| 429 |
+
🎯 Calibration Configuration
|
| 430 |
+
</CardTitle>
|
| 431 |
+
<CardDescription>
|
| 432 |
+
Copy this JSON or download it for your robot setup
|
| 433 |
+
</CardDescription>
|
| 434 |
+
</CardHeader>
|
| 435 |
+
<CardContent>
|
| 436 |
+
<div className="space-y-3">
|
| 437 |
+
<pre className="bg-gray-100 p-4 rounded-lg text-sm overflow-x-auto border">
|
| 438 |
+
<code>{JSON.stringify(generateConfigJSON(), null, 2)}</code>
|
| 439 |
+
</pre>
|
| 440 |
+
<div className="flex gap-2">
|
| 441 |
+
<Button onClick={downloadConfigJSON} variant="outline">
|
| 442 |
+
📄 Download JSON File
|
| 443 |
+
</Button>
|
| 444 |
+
<Button
|
| 445 |
+
onClick={() => {
|
| 446 |
+
navigator.clipboard.writeText(
|
| 447 |
+
JSON.stringify(generateConfigJSON(), null, 2)
|
| 448 |
+
);
|
| 449 |
+
}}
|
| 450 |
+
variant="outline"
|
| 451 |
+
>
|
| 452 |
+
📋 Copy to Clipboard
|
| 453 |
+
</Button>
|
| 454 |
+
</div>
|
| 455 |
+
</div>
|
| 456 |
+
</CardContent>
|
| 457 |
+
</Card>
|
| 458 |
+
)}
|
| 459 |
+
|
| 460 |
+
{/* Live Position Recording Table (matching Node CLI exactly) */}
|
| 461 |
+
<Card>
|
| 462 |
+
<CardHeader>
|
| 463 |
+
<CardTitle className="text-lg">Live Position Recording</CardTitle>
|
| 464 |
+
<CardDescription>
|
| 465 |
+
Real-time motor position feedback - exactly like Node CLI
|
| 466 |
+
</CardDescription>
|
| 467 |
+
</CardHeader>
|
| 468 |
+
<CardContent>
|
| 469 |
+
<div className="overflow-hidden rounded-lg border">
|
| 470 |
+
<table className="w-full font-mono text-sm">
|
| 471 |
+
<thead className="bg-gray-50">
|
| 472 |
+
<tr>
|
| 473 |
+
<th className="px-4 py-2 text-left font-medium text-gray-900">
|
| 474 |
+
Motor Name
|
| 475 |
+
</th>
|
| 476 |
+
<th className="px-4 py-2 text-right font-medium text-gray-900">
|
| 477 |
+
Current
|
| 478 |
+
</th>
|
| 479 |
+
<th className="px-4 py-2 text-right font-medium text-gray-900">
|
| 480 |
+
Min
|
| 481 |
+
</th>
|
| 482 |
+
<th className="px-4 py-2 text-right font-medium text-gray-900">
|
| 483 |
+
Max
|
| 484 |
+
</th>
|
| 485 |
+
<th className="px-4 py-2 text-right font-medium text-gray-900">
|
| 486 |
+
Range
|
| 487 |
+
</th>
|
| 488 |
+
</tr>
|
| 489 |
+
</thead>
|
| 490 |
+
<tbody className="divide-y divide-gray-200">
|
| 491 |
+
{motorData.map((motor, index) => (
|
| 492 |
+
<tr key={index} className="hover:bg-gray-50">
|
| 493 |
+
<td className="px-4 py-2 font-medium flex items-center gap-2">
|
| 494 |
+
{motor.name}
|
| 495 |
+
{motor.range > 100 && (
|
| 496 |
+
<span className="text-green-600 text-xs">✓</span>
|
| 497 |
+
)}
|
| 498 |
+
</td>
|
| 499 |
+
<td className="px-4 py-2 text-right">{motor.current}</td>
|
| 500 |
+
<td className="px-4 py-2 text-right">{motor.min}</td>
|
| 501 |
+
<td className="px-4 py-2 text-right">{motor.max}</td>
|
| 502 |
+
<td className="px-4 py-2 text-right font-medium">
|
| 503 |
+
<span
|
| 504 |
+
className={
|
| 505 |
+
motor.range > 100 ? "text-green-600" : "text-gray-500"
|
| 506 |
+
}
|
| 507 |
+
>
|
| 508 |
+
{motor.range}
|
| 509 |
+
</span>
|
| 510 |
+
</td>
|
| 511 |
+
</tr>
|
| 512 |
+
))}
|
| 513 |
+
</tbody>
|
| 514 |
+
</table>
|
| 515 |
+
</div>
|
| 516 |
+
|
| 517 |
+
{isCalibrating && (
|
| 518 |
+
<div className="mt-3 text-center text-sm text-gray-600">
|
| 519 |
+
Move joints through their full range of motion...
|
| 520 |
+
</div>
|
| 521 |
+
)}
|
| 522 |
+
</CardContent>
|
| 523 |
+
</Card>
|
| 524 |
+
</div>
|
| 525 |
+
);
|
| 526 |
+
}
|
src/demo/components/CalibrationWizard.tsx
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState, useEffect } from "react";
|
| 2 |
+
import { Button } from "./ui/button";
|
| 3 |
+
import {
|
| 4 |
+
Card,
|
| 5 |
+
CardContent,
|
| 6 |
+
CardDescription,
|
| 7 |
+
CardHeader,
|
| 8 |
+
CardTitle,
|
| 9 |
+
} from "./ui/card";
|
| 10 |
+
import { Alert, AlertDescription, AlertTitle } from "./ui/alert";
|
| 11 |
+
import { Badge } from "./ui/badge";
|
| 12 |
+
import { calibrateWithPort } from "../../lerobot/web/calibrate";
|
| 13 |
+
import type { ConnectedRobot } from "../types";
|
| 14 |
+
|
| 15 |
+
interface CalibrationWizardProps {
|
| 16 |
+
robot: ConnectedRobot;
|
| 17 |
+
onComplete: () => void;
|
| 18 |
+
onCancel: () => void;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
interface CalibrationStep {
|
| 22 |
+
id: string;
|
| 23 |
+
title: string;
|
| 24 |
+
description: string;
|
| 25 |
+
status: "pending" | "running" | "complete" | "error";
|
| 26 |
+
message?: string;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
export function CalibrationWizard({
|
| 30 |
+
robot,
|
| 31 |
+
onComplete,
|
| 32 |
+
onCancel,
|
| 33 |
+
}: CalibrationWizardProps) {
|
| 34 |
+
const [currentStepIndex, setCurrentStepIndex] = useState(0);
|
| 35 |
+
const [steps, setSteps] = useState<CalibrationStep[]>([
|
| 36 |
+
{
|
| 37 |
+
id: "init",
|
| 38 |
+
title: "Initialize Robot",
|
| 39 |
+
description: "Connecting to robot and checking status",
|
| 40 |
+
status: "pending",
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
id: "calibrate",
|
| 44 |
+
title: "Calibrate Motors",
|
| 45 |
+
description: "Running calibration sequence",
|
| 46 |
+
status: "pending",
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
id: "verify",
|
| 50 |
+
title: "Verify Calibration",
|
| 51 |
+
description: "Testing calibrated positions",
|
| 52 |
+
status: "pending",
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
id: "complete",
|
| 56 |
+
title: "Complete",
|
| 57 |
+
description: "Calibration finished successfully",
|
| 58 |
+
status: "pending",
|
| 59 |
+
},
|
| 60 |
+
]);
|
| 61 |
+
|
| 62 |
+
const [isRunning, setIsRunning] = useState(false);
|
| 63 |
+
const [error, setError] = useState<string | null>(null);
|
| 64 |
+
|
| 65 |
+
useEffect(() => {
|
| 66 |
+
startCalibration();
|
| 67 |
+
}, []);
|
| 68 |
+
|
| 69 |
+
const updateStep = (
|
| 70 |
+
stepId: string,
|
| 71 |
+
status: CalibrationStep["status"],
|
| 72 |
+
message?: string
|
| 73 |
+
) => {
|
| 74 |
+
setSteps((prev) =>
|
| 75 |
+
prev.map((step) =>
|
| 76 |
+
step.id === stepId ? { ...step, status, message } : step
|
| 77 |
+
)
|
| 78 |
+
);
|
| 79 |
+
};
|
| 80 |
+
|
| 81 |
+
const startCalibration = async () => {
|
| 82 |
+
if (!robot.port || !robot.robotType) {
|
| 83 |
+
setError("Invalid robot configuration");
|
| 84 |
+
return;
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
setIsRunning(true);
|
| 88 |
+
setError(null);
|
| 89 |
+
|
| 90 |
+
try {
|
| 91 |
+
// Step 1: Initialize
|
| 92 |
+
setCurrentStepIndex(0);
|
| 93 |
+
updateStep("init", "running");
|
| 94 |
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
| 95 |
+
updateStep("init", "complete", "Robot initialized successfully");
|
| 96 |
+
|
| 97 |
+
// Step 2: Calibrate
|
| 98 |
+
setCurrentStepIndex(1);
|
| 99 |
+
updateStep("calibrate", "running");
|
| 100 |
+
|
| 101 |
+
try {
|
| 102 |
+
await calibrateWithPort(robot.port, robot.robotType);
|
| 103 |
+
updateStep("calibrate", "complete", "Motor calibration completed");
|
| 104 |
+
} catch (error) {
|
| 105 |
+
updateStep(
|
| 106 |
+
"calibrate",
|
| 107 |
+
"error",
|
| 108 |
+
error instanceof Error ? error.message : "Calibration failed"
|
| 109 |
+
);
|
| 110 |
+
throw error;
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
// Step 3: Verify
|
| 114 |
+
setCurrentStepIndex(2);
|
| 115 |
+
updateStep("verify", "running");
|
| 116 |
+
await new Promise((resolve) => setTimeout(resolve, 1500));
|
| 117 |
+
updateStep("verify", "complete", "Calibration verified");
|
| 118 |
+
|
| 119 |
+
// Step 4: Complete
|
| 120 |
+
setCurrentStepIndex(3);
|
| 121 |
+
updateStep("complete", "complete", "Robot is ready for use");
|
| 122 |
+
|
| 123 |
+
setTimeout(() => {
|
| 124 |
+
onComplete();
|
| 125 |
+
}, 2000);
|
| 126 |
+
} catch (error) {
|
| 127 |
+
setError(error instanceof Error ? error.message : "Calibration failed");
|
| 128 |
+
} finally {
|
| 129 |
+
setIsRunning(false);
|
| 130 |
+
}
|
| 131 |
+
};
|
| 132 |
+
|
| 133 |
+
const getStepIcon = (status: CalibrationStep["status"]) => {
|
| 134 |
+
switch (status) {
|
| 135 |
+
case "pending":
|
| 136 |
+
return "⏳";
|
| 137 |
+
case "running":
|
| 138 |
+
return "🔄";
|
| 139 |
+
case "complete":
|
| 140 |
+
return "✅";
|
| 141 |
+
case "error":
|
| 142 |
+
return "❌";
|
| 143 |
+
default:
|
| 144 |
+
return "⏳";
|
| 145 |
+
}
|
| 146 |
+
};
|
| 147 |
+
|
| 148 |
+
const getStepBadgeVariant = (status: CalibrationStep["status"]) => {
|
| 149 |
+
switch (status) {
|
| 150 |
+
case "pending":
|
| 151 |
+
return "secondary" as const;
|
| 152 |
+
case "running":
|
| 153 |
+
return "default" as const;
|
| 154 |
+
case "complete":
|
| 155 |
+
return "default" as const;
|
| 156 |
+
case "error":
|
| 157 |
+
return "destructive" as const;
|
| 158 |
+
default:
|
| 159 |
+
return "secondary" as const;
|
| 160 |
+
}
|
| 161 |
+
};
|
| 162 |
+
|
| 163 |
+
return (
|
| 164 |
+
<div className="space-y-6">
|
| 165 |
+
<div className="text-center">
|
| 166 |
+
<h3 className="text-lg font-semibold mb-2">Calibration in Progress</h3>
|
| 167 |
+
<p className="text-muted-foreground">
|
| 168 |
+
Calibrating {robot.robotId} ({robot.robotType?.replace("_", " ")})
|
| 169 |
+
</p>
|
| 170 |
+
</div>
|
| 171 |
+
|
| 172 |
+
{error && (
|
| 173 |
+
<Alert variant="destructive">
|
| 174 |
+
<AlertDescription>{error}</AlertDescription>
|
| 175 |
+
</Alert>
|
| 176 |
+
)}
|
| 177 |
+
|
| 178 |
+
<div className="space-y-4">
|
| 179 |
+
{steps.map((step, index) => (
|
| 180 |
+
<Card
|
| 181 |
+
key={step.id}
|
| 182 |
+
className={index === currentStepIndex ? "ring-2 ring-blue-500" : ""}
|
| 183 |
+
>
|
| 184 |
+
<CardHeader className="pb-3">
|
| 185 |
+
<div className="flex items-center justify-between">
|
| 186 |
+
<div className="flex items-center space-x-3">
|
| 187 |
+
<span className="text-2xl">{getStepIcon(step.status)}</span>
|
| 188 |
+
<div>
|
| 189 |
+
<CardTitle className="text-base">{step.title}</CardTitle>
|
| 190 |
+
<CardDescription className="text-sm">
|
| 191 |
+
{step.description}
|
| 192 |
+
</CardDescription>
|
| 193 |
+
</div>
|
| 194 |
+
</div>
|
| 195 |
+
<Badge variant={getStepBadgeVariant(step.status)}>
|
| 196 |
+
{step.status.charAt(0).toUpperCase() + step.status.slice(1)}
|
| 197 |
+
</Badge>
|
| 198 |
+
</div>
|
| 199 |
+
</CardHeader>
|
| 200 |
+
{step.message && (
|
| 201 |
+
<CardContent className="pt-0">
|
| 202 |
+
<p className="text-sm text-muted-foreground">{step.message}</p>
|
| 203 |
+
</CardContent>
|
| 204 |
+
)}
|
| 205 |
+
</Card>
|
| 206 |
+
))}
|
| 207 |
+
</div>
|
| 208 |
+
|
| 209 |
+
<div className="flex justify-center space-x-4">
|
| 210 |
+
<Button variant="outline" onClick={onCancel} disabled={isRunning}>
|
| 211 |
+
Cancel
|
| 212 |
+
</Button>
|
| 213 |
+
{error && <Button onClick={startCalibration}>Retry Calibration</Button>}
|
| 214 |
+
</div>
|
| 215 |
+
</div>
|
| 216 |
+
);
|
| 217 |
+
}
|
src/demo/components/ErrorBoundary.tsx
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { Component, ErrorInfo, ReactNode } from "react";
|
| 2 |
+
import { Alert, AlertDescription, AlertTitle } from "./ui/alert";
|
| 3 |
+
import { Button } from "./ui/button";
|
| 4 |
+
|
| 5 |
+
interface Props {
|
| 6 |
+
children: ReactNode;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
interface State {
|
| 10 |
+
hasError: boolean;
|
| 11 |
+
error?: Error;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
export class ErrorBoundary extends Component<Props, State> {
|
| 15 |
+
constructor(props: Props) {
|
| 16 |
+
super(props);
|
| 17 |
+
this.state = { hasError: false };
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
static getDerivedStateFromError(error: Error): State {
|
| 21 |
+
return { hasError: true, error };
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
| 25 |
+
console.error("ErrorBoundary caught an error:", error, errorInfo);
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
render() {
|
| 29 |
+
if (this.state.hasError) {
|
| 30 |
+
return (
|
| 31 |
+
<div className="min-h-screen flex items-center justify-center p-8">
|
| 32 |
+
<div className="max-w-md w-full">
|
| 33 |
+
<Alert variant="destructive">
|
| 34 |
+
<AlertTitle>Something went wrong</AlertTitle>
|
| 35 |
+
<AlertDescription>
|
| 36 |
+
The application encountered an error. Please try refreshing the
|
| 37 |
+
page or contact support if the problem persists.
|
| 38 |
+
</AlertDescription>
|
| 39 |
+
</Alert>
|
| 40 |
+
<div className="mt-4 flex gap-2">
|
| 41 |
+
<Button onClick={() => window.location.reload()}>
|
| 42 |
+
Refresh Page
|
| 43 |
+
</Button>
|
| 44 |
+
<Button
|
| 45 |
+
variant="outline"
|
| 46 |
+
onClick={() =>
|
| 47 |
+
this.setState({ hasError: false, error: undefined })
|
| 48 |
+
}
|
| 49 |
+
>
|
| 50 |
+
Try Again
|
| 51 |
+
</Button>
|
| 52 |
+
</div>
|
| 53 |
+
{process.env.NODE_ENV === "development" && this.state.error && (
|
| 54 |
+
<div className="mt-4 p-4 bg-gray-100 rounded-md text-xs">
|
| 55 |
+
<pre>{this.state.error.stack}</pre>
|
| 56 |
+
</div>
|
| 57 |
+
)}
|
| 58 |
+
</div>
|
| 59 |
+
</div>
|
| 60 |
+
);
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
return this.props.children;
|
| 64 |
+
}
|
| 65 |
+
}
|
src/demo/components/PortManager.tsx
ADDED
|
@@ -0,0 +1,1050 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState, useEffect } from "react";
|
| 2 |
+
import { Button } from "./ui/button";
|
| 3 |
+
import {
|
| 4 |
+
Card,
|
| 5 |
+
CardContent,
|
| 6 |
+
CardDescription,
|
| 7 |
+
CardHeader,
|
| 8 |
+
CardTitle,
|
| 9 |
+
} from "./ui/card";
|
| 10 |
+
import { Alert, AlertDescription } from "./ui/alert";
|
| 11 |
+
import { Badge } from "./ui/badge";
|
| 12 |
+
import { isWebSerialSupported } from "../../lerobot/web/calibrate";
|
| 13 |
+
import type { ConnectedRobot } from "../types";
|
| 14 |
+
|
| 15 |
+
interface PortManagerProps {
|
| 16 |
+
connectedRobots: ConnectedRobot[];
|
| 17 |
+
onConnectedRobotsChange: (robots: ConnectedRobot[]) => void;
|
| 18 |
+
onCalibrate?: (
|
| 19 |
+
port: SerialPort,
|
| 20 |
+
robotType: "so100_follower" | "so100_leader",
|
| 21 |
+
robotId: string
|
| 22 |
+
) => void;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
export function PortManager({
|
| 26 |
+
connectedRobots,
|
| 27 |
+
onConnectedRobotsChange,
|
| 28 |
+
onCalibrate,
|
| 29 |
+
}: PortManagerProps) {
|
| 30 |
+
const [isConnecting, setIsConnecting] = useState(false);
|
| 31 |
+
const [isFindingPorts, setIsFindingPorts] = useState(false);
|
| 32 |
+
const [findPortsLog, setFindPortsLog] = useState<string[]>([]);
|
| 33 |
+
const [error, setError] = useState<string | null>(null);
|
| 34 |
+
|
| 35 |
+
// Load saved port data from localStorage on mount
|
| 36 |
+
useEffect(() => {
|
| 37 |
+
loadSavedPorts();
|
| 38 |
+
}, []);
|
| 39 |
+
|
| 40 |
+
// Save port data to localStorage whenever connectedPorts changes
|
| 41 |
+
useEffect(() => {
|
| 42 |
+
savePortsToStorage();
|
| 43 |
+
}, [connectedRobots]);
|
| 44 |
+
|
| 45 |
+
const loadSavedPorts = async () => {
|
| 46 |
+
try {
|
| 47 |
+
const saved = localStorage.getItem("lerobot-ports");
|
| 48 |
+
if (!saved) return;
|
| 49 |
+
|
| 50 |
+
const savedData = JSON.parse(saved);
|
| 51 |
+
const existingPorts = await navigator.serial.getPorts();
|
| 52 |
+
|
| 53 |
+
const restoredPorts: ConnectedRobot[] = [];
|
| 54 |
+
|
| 55 |
+
for (const port of existingPorts) {
|
| 56 |
+
// Find saved data by matching port info instead of display name
|
| 57 |
+
const portInfo = port.getInfo();
|
| 58 |
+
const savedPort = savedData.find((p: any) => {
|
| 59 |
+
// Try to match by USB vendor/product ID if available
|
| 60 |
+
if (portInfo.usbVendorId && portInfo.usbProductId) {
|
| 61 |
+
return (
|
| 62 |
+
p.usbVendorId === portInfo.usbVendorId &&
|
| 63 |
+
p.usbProductId === portInfo.usbProductId
|
| 64 |
+
);
|
| 65 |
+
}
|
| 66 |
+
// Fallback to name matching
|
| 67 |
+
return p.name === getPortDisplayName(port);
|
| 68 |
+
});
|
| 69 |
+
|
| 70 |
+
// Auto-connect to paired robots
|
| 71 |
+
let isConnected = false;
|
| 72 |
+
try {
|
| 73 |
+
// Check if already open
|
| 74 |
+
if (port.readable !== null && port.writable !== null) {
|
| 75 |
+
isConnected = true;
|
| 76 |
+
} else {
|
| 77 |
+
// Auto-open paired robots
|
| 78 |
+
await port.open({ baudRate: 1000000 });
|
| 79 |
+
isConnected = true;
|
| 80 |
+
}
|
| 81 |
+
} catch (error) {
|
| 82 |
+
console.log("Could not auto-connect to paired robot:", error);
|
| 83 |
+
isConnected = false;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
restoredPorts.push({
|
| 87 |
+
port,
|
| 88 |
+
name: getPortDisplayName(port),
|
| 89 |
+
isConnected,
|
| 90 |
+
robotType: savedPort?.robotType,
|
| 91 |
+
robotId: savedPort?.robotId,
|
| 92 |
+
});
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
onConnectedRobotsChange(restoredPorts);
|
| 96 |
+
} catch (error) {
|
| 97 |
+
console.error("Failed to load saved ports:", error);
|
| 98 |
+
}
|
| 99 |
+
};
|
| 100 |
+
|
| 101 |
+
const savePortsToStorage = () => {
|
| 102 |
+
try {
|
| 103 |
+
const dataToSave = connectedRobots.map((p) => {
|
| 104 |
+
const portInfo = p.port.getInfo();
|
| 105 |
+
return {
|
| 106 |
+
name: p.name,
|
| 107 |
+
robotType: p.robotType,
|
| 108 |
+
robotId: p.robotId,
|
| 109 |
+
usbVendorId: portInfo.usbVendorId,
|
| 110 |
+
usbProductId: portInfo.usbProductId,
|
| 111 |
+
};
|
| 112 |
+
});
|
| 113 |
+
localStorage.setItem("lerobot-ports", JSON.stringify(dataToSave));
|
| 114 |
+
} catch (error) {
|
| 115 |
+
console.error("Failed to save ports to storage:", error);
|
| 116 |
+
}
|
| 117 |
+
};
|
| 118 |
+
|
| 119 |
+
const getPortDisplayName = (port: SerialPort): string => {
|
| 120 |
+
try {
|
| 121 |
+
const info = port.getInfo();
|
| 122 |
+
if (info.usbVendorId && info.usbProductId) {
|
| 123 |
+
return `USB Port (${info.usbVendorId}:${info.usbProductId})`;
|
| 124 |
+
}
|
| 125 |
+
if (info.usbVendorId) {
|
| 126 |
+
return `Serial Port (VID:${info.usbVendorId
|
| 127 |
+
.toString(16)
|
| 128 |
+
.toUpperCase()})`;
|
| 129 |
+
}
|
| 130 |
+
} catch (error) {
|
| 131 |
+
// getInfo() might not be available
|
| 132 |
+
}
|
| 133 |
+
return `Serial Port ${Date.now()}`;
|
| 134 |
+
};
|
| 135 |
+
|
| 136 |
+
const handleConnect = async () => {
|
| 137 |
+
if (!isWebSerialSupported()) {
|
| 138 |
+
setError("Web Serial API is not supported in this browser");
|
| 139 |
+
return;
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
try {
|
| 143 |
+
setIsConnecting(true);
|
| 144 |
+
setError(null);
|
| 145 |
+
|
| 146 |
+
// Step 1: Request Web Serial port
|
| 147 |
+
console.log("Step 1: Requesting Web Serial port...");
|
| 148 |
+
const port = await navigator.serial.requestPort();
|
| 149 |
+
await port.open({ baudRate: 1000000 });
|
| 150 |
+
|
| 151 |
+
// Step 2: Request WebUSB device for metadata
|
| 152 |
+
console.log(
|
| 153 |
+
"Step 2: Requesting WebUSB device for unique identification..."
|
| 154 |
+
);
|
| 155 |
+
let serialNumber = null;
|
| 156 |
+
let usbMetadata = null;
|
| 157 |
+
|
| 158 |
+
try {
|
| 159 |
+
// Request USB device access for metadata
|
| 160 |
+
const usbDevice = await navigator.usb.requestDevice({
|
| 161 |
+
filters: [
|
| 162 |
+
{ vendorId: 0x0403 }, // FTDI
|
| 163 |
+
{ vendorId: 0x067b }, // Prolific
|
| 164 |
+
{ vendorId: 0x10c4 }, // Silicon Labs
|
| 165 |
+
{ vendorId: 0x1a86 }, // QinHeng Electronics (CH340)
|
| 166 |
+
{ vendorId: 0x239a }, // Adafruit
|
| 167 |
+
{ vendorId: 0x2341 }, // Arduino
|
| 168 |
+
{ vendorId: 0x2e8a }, // Raspberry Pi Foundation
|
| 169 |
+
{ vendorId: 0x1b4f }, // SparkFun
|
| 170 |
+
],
|
| 171 |
+
});
|
| 172 |
+
|
| 173 |
+
if (usbDevice) {
|
| 174 |
+
serialNumber =
|
| 175 |
+
usbDevice.serialNumber ||
|
| 176 |
+
`${usbDevice.vendorId}-${usbDevice.productId}-${Date.now()}`;
|
| 177 |
+
usbMetadata = {
|
| 178 |
+
vendorId: `0x${usbDevice.vendorId.toString(16).padStart(4, "0")}`,
|
| 179 |
+
productId: `0x${usbDevice.productId.toString(16).padStart(4, "0")}`,
|
| 180 |
+
serialNumber: usbDevice.serialNumber || "Generated ID",
|
| 181 |
+
manufacturerName: usbDevice.manufacturerName || "Unknown",
|
| 182 |
+
productName: usbDevice.productName || "Unknown",
|
| 183 |
+
usbVersionMajor: usbDevice.usbVersionMajor,
|
| 184 |
+
usbVersionMinor: usbDevice.usbVersionMinor,
|
| 185 |
+
deviceClass: usbDevice.deviceClass,
|
| 186 |
+
deviceSubclass: usbDevice.deviceSubclass,
|
| 187 |
+
deviceProtocol: usbDevice.deviceProtocol,
|
| 188 |
+
};
|
| 189 |
+
console.log("✅ USB device metadata acquired:", usbMetadata);
|
| 190 |
+
}
|
| 191 |
+
} catch (usbError) {
|
| 192 |
+
console.log(
|
| 193 |
+
"⚠️ WebUSB request failed, generating fallback ID:",
|
| 194 |
+
usbError
|
| 195 |
+
);
|
| 196 |
+
// Generate a fallback unique ID if WebUSB fails
|
| 197 |
+
serialNumber = `fallback-${Date.now()}-${Math.random()
|
| 198 |
+
.toString(36)
|
| 199 |
+
.substr(2, 9)}`;
|
| 200 |
+
usbMetadata = {
|
| 201 |
+
vendorId: "Unknown",
|
| 202 |
+
productId: "Unknown",
|
| 203 |
+
serialNumber: serialNumber,
|
| 204 |
+
manufacturerName: "USB Metadata Not Available",
|
| 205 |
+
productName: "Check browser WebUSB support",
|
| 206 |
+
};
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
const portName = getPortDisplayName(port);
|
| 210 |
+
|
| 211 |
+
// Step 3: Check if this robot (by serial number) is already connected
|
| 212 |
+
const existingIndex = connectedRobots.findIndex(
|
| 213 |
+
(robot) => robot.serialNumber === serialNumber
|
| 214 |
+
);
|
| 215 |
+
|
| 216 |
+
if (existingIndex === -1) {
|
| 217 |
+
// New robot - add to list
|
| 218 |
+
const newRobot: ConnectedRobot = {
|
| 219 |
+
port,
|
| 220 |
+
name: portName,
|
| 221 |
+
isConnected: true,
|
| 222 |
+
serialNumber: serialNumber!,
|
| 223 |
+
usbMetadata: usbMetadata || undefined,
|
| 224 |
+
};
|
| 225 |
+
|
| 226 |
+
// Try to load saved robot info by serial number
|
| 227 |
+
try {
|
| 228 |
+
const savedRobotKey = `lerobot-robot-${serialNumber}`;
|
| 229 |
+
const savedData = localStorage.getItem(savedRobotKey);
|
| 230 |
+
if (savedData) {
|
| 231 |
+
const parsed = JSON.parse(savedData);
|
| 232 |
+
newRobot.robotType = parsed.robotType;
|
| 233 |
+
newRobot.robotId = parsed.robotId;
|
| 234 |
+
console.log("📋 Loaded saved robot configuration:", parsed);
|
| 235 |
+
}
|
| 236 |
+
} catch (error) {
|
| 237 |
+
console.warn("Failed to load saved robot data:", error);
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
onConnectedRobotsChange([...connectedRobots, newRobot]);
|
| 241 |
+
console.log("🤖 New robot connected with ID:", serialNumber);
|
| 242 |
+
} else {
|
| 243 |
+
// Existing robot - update port and connection status
|
| 244 |
+
const updatedRobots = connectedRobots.map((robot, index) =>
|
| 245 |
+
index === existingIndex
|
| 246 |
+
? { ...robot, port, isConnected: true, name: portName }
|
| 247 |
+
: robot
|
| 248 |
+
);
|
| 249 |
+
onConnectedRobotsChange(updatedRobots);
|
| 250 |
+
console.log("🔄 Existing robot reconnected:", serialNumber);
|
| 251 |
+
}
|
| 252 |
+
} catch (error) {
|
| 253 |
+
if (
|
| 254 |
+
error instanceof Error &&
|
| 255 |
+
(error.message.includes("cancelled") ||
|
| 256 |
+
error.message.includes("No port selected by the user") ||
|
| 257 |
+
error.name === "NotAllowedError")
|
| 258 |
+
) {
|
| 259 |
+
// User cancelled - no error message needed, just log to console
|
| 260 |
+
console.log("Connection cancelled by user");
|
| 261 |
+
return;
|
| 262 |
+
}
|
| 263 |
+
setError(
|
| 264 |
+
error instanceof Error ? error.message : "Failed to connect to robot"
|
| 265 |
+
);
|
| 266 |
+
} finally {
|
| 267 |
+
setIsConnecting(false);
|
| 268 |
+
}
|
| 269 |
+
};
|
| 270 |
+
|
| 271 |
+
const handleDisconnect = async (index: number) => {
|
| 272 |
+
try {
|
| 273 |
+
const portInfo = connectedRobots[index];
|
| 274 |
+
if (portInfo.isConnected) {
|
| 275 |
+
await portInfo.port.close();
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
const updatedRobots = connectedRobots.filter((_, i) => i !== index);
|
| 279 |
+
onConnectedRobotsChange(updatedRobots);
|
| 280 |
+
} catch (error) {
|
| 281 |
+
setError(
|
| 282 |
+
error instanceof Error ? error.message : "Failed to disconnect port"
|
| 283 |
+
);
|
| 284 |
+
}
|
| 285 |
+
};
|
| 286 |
+
|
| 287 |
+
const handleUpdatePortInfo = (
|
| 288 |
+
index: number,
|
| 289 |
+
robotType: "so100_follower" | "so100_leader",
|
| 290 |
+
robotId: string
|
| 291 |
+
) => {
|
| 292 |
+
const updatedRobots = connectedRobots.map((robot, i) => {
|
| 293 |
+
if (i === index) {
|
| 294 |
+
const updatedRobot = { ...robot, robotType, robotId };
|
| 295 |
+
|
| 296 |
+
// Save robot configuration to localStorage using serial number
|
| 297 |
+
if (updatedRobot.serialNumber) {
|
| 298 |
+
try {
|
| 299 |
+
const robotKey = `lerobot-robot-${updatedRobot.serialNumber}`;
|
| 300 |
+
const robotData = {
|
| 301 |
+
robotType,
|
| 302 |
+
robotId,
|
| 303 |
+
serialNumber: updatedRobot.serialNumber,
|
| 304 |
+
lastUpdated: new Date().toISOString(),
|
| 305 |
+
};
|
| 306 |
+
localStorage.setItem(robotKey, JSON.stringify(robotData));
|
| 307 |
+
console.log(
|
| 308 |
+
"💾 Saved robot configuration for:",
|
| 309 |
+
updatedRobot.serialNumber
|
| 310 |
+
);
|
| 311 |
+
} catch (error) {
|
| 312 |
+
console.warn("Failed to save robot configuration:", error);
|
| 313 |
+
}
|
| 314 |
+
}
|
| 315 |
+
|
| 316 |
+
return updatedRobot;
|
| 317 |
+
}
|
| 318 |
+
return robot;
|
| 319 |
+
});
|
| 320 |
+
onConnectedRobotsChange(updatedRobots);
|
| 321 |
+
};
|
| 322 |
+
|
| 323 |
+
const handleFindPorts = async () => {
|
| 324 |
+
if (!isWebSerialSupported()) {
|
| 325 |
+
setError("Web Serial API is not supported in this browser");
|
| 326 |
+
return;
|
| 327 |
+
}
|
| 328 |
+
|
| 329 |
+
try {
|
| 330 |
+
setIsFindingPorts(true);
|
| 331 |
+
setFindPortsLog([]);
|
| 332 |
+
setError(null);
|
| 333 |
+
|
| 334 |
+
// Get initial ports
|
| 335 |
+
const initialPorts = await navigator.serial.getPorts();
|
| 336 |
+
setFindPortsLog((prev) => [
|
| 337 |
+
...prev,
|
| 338 |
+
`Found ${initialPorts.length} existing paired port(s)`,
|
| 339 |
+
]);
|
| 340 |
+
|
| 341 |
+
// Ask user to disconnect
|
| 342 |
+
setFindPortsLog((prev) => [
|
| 343 |
+
...prev,
|
| 344 |
+
"Please disconnect the USB cable from your robot and click OK",
|
| 345 |
+
]);
|
| 346 |
+
|
| 347 |
+
// Simple implementation - just show the instruction
|
| 348 |
+
// In a real implementation, we'd monitor port changes
|
| 349 |
+
const confirmed = confirm(
|
| 350 |
+
"Disconnect the USB cable from your robot and click OK when done"
|
| 351 |
+
);
|
| 352 |
+
|
| 353 |
+
if (confirmed) {
|
| 354 |
+
setFindPortsLog((prev) => [...prev, "Reconnect the USB cable now"]);
|
| 355 |
+
|
| 356 |
+
// Request port selection
|
| 357 |
+
const port = await navigator.serial.requestPort();
|
| 358 |
+
await port.open({ baudRate: 1000000 });
|
| 359 |
+
|
| 360 |
+
const portName = getPortDisplayName(port);
|
| 361 |
+
setFindPortsLog((prev) => [...prev, `Identified port: ${portName}`]);
|
| 362 |
+
|
| 363 |
+
// Add to connected ports if not already there
|
| 364 |
+
const existingIndex = connectedRobots.findIndex(
|
| 365 |
+
(p) => p.name === portName
|
| 366 |
+
);
|
| 367 |
+
if (existingIndex === -1) {
|
| 368 |
+
const newPort: ConnectedRobot = {
|
| 369 |
+
port,
|
| 370 |
+
name: portName,
|
| 371 |
+
isConnected: true,
|
| 372 |
+
};
|
| 373 |
+
onConnectedRobotsChange([...connectedRobots, newPort]);
|
| 374 |
+
}
|
| 375 |
+
}
|
| 376 |
+
} catch (error) {
|
| 377 |
+
if (
|
| 378 |
+
error instanceof Error &&
|
| 379 |
+
(error.message.includes("cancelled") ||
|
| 380 |
+
error.name === "NotAllowedError")
|
| 381 |
+
) {
|
| 382 |
+
// User cancelled - no message needed, just log to console
|
| 383 |
+
console.log("Port identification cancelled by user");
|
| 384 |
+
return;
|
| 385 |
+
}
|
| 386 |
+
setError(error instanceof Error ? error.message : "Failed to find ports");
|
| 387 |
+
} finally {
|
| 388 |
+
setIsFindingPorts(false);
|
| 389 |
+
}
|
| 390 |
+
};
|
| 391 |
+
|
| 392 |
+
const ensurePortIsOpen = async (robotIndex: number) => {
|
| 393 |
+
const robot = connectedRobots[robotIndex];
|
| 394 |
+
if (!robot) return false;
|
| 395 |
+
|
| 396 |
+
try {
|
| 397 |
+
// If port is already open, we're good
|
| 398 |
+
if (robot.port.readable !== null && robot.port.writable !== null) {
|
| 399 |
+
return true;
|
| 400 |
+
}
|
| 401 |
+
|
| 402 |
+
// Try to open the port
|
| 403 |
+
await robot.port.open({ baudRate: 1000000 });
|
| 404 |
+
|
| 405 |
+
// Update the robot's connection status
|
| 406 |
+
const updatedRobots = connectedRobots.map((r, i) =>
|
| 407 |
+
i === robotIndex ? { ...r, isConnected: true } : r
|
| 408 |
+
);
|
| 409 |
+
onConnectedRobotsChange(updatedRobots);
|
| 410 |
+
|
| 411 |
+
return true;
|
| 412 |
+
} catch (error) {
|
| 413 |
+
console.error("Failed to open port for calibration:", error);
|
| 414 |
+
setError(error instanceof Error ? error.message : "Failed to open port");
|
| 415 |
+
return false;
|
| 416 |
+
}
|
| 417 |
+
};
|
| 418 |
+
|
| 419 |
+
const handleCalibrate = async (port: ConnectedRobot) => {
|
| 420 |
+
if (!port.robotType || !port.robotId) {
|
| 421 |
+
setError("Please set robot type and ID before calibrating");
|
| 422 |
+
return;
|
| 423 |
+
}
|
| 424 |
+
|
| 425 |
+
// Find the robot index
|
| 426 |
+
const robotIndex = connectedRobots.findIndex((r) => r.port === port.port);
|
| 427 |
+
if (robotIndex === -1) {
|
| 428 |
+
setError("Robot not found in connected robots list");
|
| 429 |
+
return;
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
// Ensure port is open before calibrating
|
| 433 |
+
const isOpen = await ensurePortIsOpen(robotIndex);
|
| 434 |
+
if (!isOpen) {
|
| 435 |
+
return; // Error already set in ensurePortIsOpen
|
| 436 |
+
}
|
| 437 |
+
|
| 438 |
+
if (onCalibrate) {
|
| 439 |
+
onCalibrate(port.port, port.robotType, port.robotId);
|
| 440 |
+
}
|
| 441 |
+
};
|
| 442 |
+
|
| 443 |
+
return (
|
| 444 |
+
<Card>
|
| 445 |
+
<CardHeader>
|
| 446 |
+
<CardTitle>🔌 Robot Connection Manager</CardTitle>
|
| 447 |
+
<CardDescription>
|
| 448 |
+
Connect, identify, and manage your robot arms
|
| 449 |
+
</CardDescription>
|
| 450 |
+
</CardHeader>
|
| 451 |
+
<CardContent>
|
| 452 |
+
<div className="space-y-6">
|
| 453 |
+
{/* Error Display */}
|
| 454 |
+
{error && (
|
| 455 |
+
<Alert variant="destructive">
|
| 456 |
+
<AlertDescription>{error}</AlertDescription>
|
| 457 |
+
</Alert>
|
| 458 |
+
)}
|
| 459 |
+
|
| 460 |
+
{/* Connection Controls */}
|
| 461 |
+
<div className="flex gap-2">
|
| 462 |
+
<Button
|
| 463 |
+
onClick={handleConnect}
|
| 464 |
+
disabled={isConnecting || !isWebSerialSupported()}
|
| 465 |
+
className="flex-1"
|
| 466 |
+
>
|
| 467 |
+
{isConnecting ? "Connecting..." : "Connect Robot"}
|
| 468 |
+
</Button>
|
| 469 |
+
<Button
|
| 470 |
+
variant="outline"
|
| 471 |
+
onClick={handleFindPorts}
|
| 472 |
+
disabled={isFindingPorts || !isWebSerialSupported()}
|
| 473 |
+
className="flex-1"
|
| 474 |
+
>
|
| 475 |
+
{isFindingPorts ? "Finding..." : "Find Port"}
|
| 476 |
+
</Button>
|
| 477 |
+
</div>
|
| 478 |
+
|
| 479 |
+
{/* Find Ports Log */}
|
| 480 |
+
{findPortsLog.length > 0 && (
|
| 481 |
+
<div className="bg-gray-50 p-3 rounded-md text-sm space-y-1">
|
| 482 |
+
{findPortsLog.map((log, index) => (
|
| 483 |
+
<div key={index} className="text-gray-700">
|
| 484 |
+
{log}
|
| 485 |
+
</div>
|
| 486 |
+
))}
|
| 487 |
+
</div>
|
| 488 |
+
)}
|
| 489 |
+
|
| 490 |
+
{/* Connected Ports */}
|
| 491 |
+
<div>
|
| 492 |
+
<h4 className="font-semibold mb-3">
|
| 493 |
+
Connected Robots ({connectedRobots.length})
|
| 494 |
+
</h4>
|
| 495 |
+
|
| 496 |
+
{connectedRobots.length === 0 ? (
|
| 497 |
+
<div className="text-center py-8 text-gray-500">
|
| 498 |
+
<div className="text-2xl mb-2">🤖</div>
|
| 499 |
+
<p>No robots connected</p>
|
| 500 |
+
<p className="text-xs">
|
| 501 |
+
Use "Connect Robot" or "Find Port" to add robots
|
| 502 |
+
</p>
|
| 503 |
+
</div>
|
| 504 |
+
) : (
|
| 505 |
+
<div className="space-y-4">
|
| 506 |
+
{connectedRobots.map((portInfo, index) => (
|
| 507 |
+
<PortCard
|
| 508 |
+
key={index}
|
| 509 |
+
portInfo={portInfo}
|
| 510 |
+
onDisconnect={() => handleDisconnect(index)}
|
| 511 |
+
onUpdateInfo={(robotType, robotId) =>
|
| 512 |
+
handleUpdatePortInfo(index, robotType, robotId)
|
| 513 |
+
}
|
| 514 |
+
onCalibrate={() => handleCalibrate(portInfo)}
|
| 515 |
+
/>
|
| 516 |
+
))}
|
| 517 |
+
</div>
|
| 518 |
+
)}
|
| 519 |
+
</div>
|
| 520 |
+
</div>
|
| 521 |
+
</CardContent>
|
| 522 |
+
</Card>
|
| 523 |
+
);
|
| 524 |
+
}
|
| 525 |
+
|
| 526 |
+
interface PortCardProps {
|
| 527 |
+
portInfo: ConnectedRobot;
|
| 528 |
+
onDisconnect: () => void;
|
| 529 |
+
onUpdateInfo: (
|
| 530 |
+
robotType: "so100_follower" | "so100_leader",
|
| 531 |
+
robotId: string
|
| 532 |
+
) => void;
|
| 533 |
+
onCalibrate: () => void;
|
| 534 |
+
}
|
| 535 |
+
|
| 536 |
+
function PortCard({
|
| 537 |
+
portInfo,
|
| 538 |
+
onDisconnect,
|
| 539 |
+
onUpdateInfo,
|
| 540 |
+
onCalibrate,
|
| 541 |
+
}: PortCardProps) {
|
| 542 |
+
const [robotType, setRobotType] = useState<"so100_follower" | "so100_leader">(
|
| 543 |
+
portInfo.robotType || "so100_follower"
|
| 544 |
+
);
|
| 545 |
+
const [robotId, setRobotId] = useState(portInfo.robotId || "");
|
| 546 |
+
const [isEditing, setIsEditing] = useState(false);
|
| 547 |
+
const [isScanning, setIsScanning] = useState(false);
|
| 548 |
+
const [motorIDs, setMotorIDs] = useState<number[]>([]);
|
| 549 |
+
const [portMetadata, setPortMetadata] = useState<any>(null);
|
| 550 |
+
const [showDeviceInfo, setShowDeviceInfo] = useState(false);
|
| 551 |
+
|
| 552 |
+
// Check for calibration in localStorage using serial number
|
| 553 |
+
const getCalibrationStatus = () => {
|
| 554 |
+
if (!portInfo.serialNumber) return null;
|
| 555 |
+
|
| 556 |
+
const calibrationKey = `lerobot-calibration-${portInfo.serialNumber}`;
|
| 557 |
+
try {
|
| 558 |
+
const saved = localStorage.getItem(calibrationKey);
|
| 559 |
+
if (saved) {
|
| 560 |
+
const calibrationData = JSON.parse(saved);
|
| 561 |
+
return {
|
| 562 |
+
timestamp: calibrationData.timestamp,
|
| 563 |
+
readCount: calibrationData.readCount,
|
| 564 |
+
};
|
| 565 |
+
}
|
| 566 |
+
} catch (error) {
|
| 567 |
+
console.warn("Failed to read calibration from localStorage:", error);
|
| 568 |
+
}
|
| 569 |
+
return null;
|
| 570 |
+
};
|
| 571 |
+
|
| 572 |
+
const calibrationStatus = getCalibrationStatus();
|
| 573 |
+
|
| 574 |
+
const handleSave = () => {
|
| 575 |
+
if (robotId.trim()) {
|
| 576 |
+
onUpdateInfo(robotType, robotId.trim());
|
| 577 |
+
setIsEditing(false);
|
| 578 |
+
}
|
| 579 |
+
};
|
| 580 |
+
|
| 581 |
+
// Use current values (either from props or local state)
|
| 582 |
+
const currentRobotType = portInfo.robotType || robotType;
|
| 583 |
+
const currentRobotId = portInfo.robotId || robotId;
|
| 584 |
+
|
| 585 |
+
const handleCancel = () => {
|
| 586 |
+
setRobotType(portInfo.robotType || "so100_follower");
|
| 587 |
+
setRobotId(portInfo.robotId || "");
|
| 588 |
+
setIsEditing(false);
|
| 589 |
+
};
|
| 590 |
+
|
| 591 |
+
// Scan for motor IDs and gather USB device metadata
|
| 592 |
+
const scanDeviceInfo = async () => {
|
| 593 |
+
if (!portInfo.port || !portInfo.isConnected) {
|
| 594 |
+
console.warn("Port not connected");
|
| 595 |
+
return;
|
| 596 |
+
}
|
| 597 |
+
|
| 598 |
+
setIsScanning(true);
|
| 599 |
+
setMotorIDs([]);
|
| 600 |
+
setPortMetadata(null);
|
| 601 |
+
const foundIDs: number[] = [];
|
| 602 |
+
|
| 603 |
+
try {
|
| 604 |
+
// Try to get USB device info using WebUSB for better metadata
|
| 605 |
+
let usbDeviceInfo = null;
|
| 606 |
+
|
| 607 |
+
try {
|
| 608 |
+
// First, check if we already have USB device permissions
|
| 609 |
+
let usbDevices = await navigator.usb.getDevices();
|
| 610 |
+
console.log("Already permitted USB devices:", usbDevices);
|
| 611 |
+
|
| 612 |
+
// If no devices found, request permission for USB-to-serial devices
|
| 613 |
+
if (usbDevices.length === 0) {
|
| 614 |
+
console.log(
|
| 615 |
+
"No USB permissions yet, requesting access to USB-to-serial devices..."
|
| 616 |
+
);
|
| 617 |
+
|
| 618 |
+
// Request access to common USB-to-serial chips
|
| 619 |
+
try {
|
| 620 |
+
const device = await navigator.usb.requestDevice({
|
| 621 |
+
filters: [
|
| 622 |
+
{ vendorId: 0x0403 }, // FTDI
|
| 623 |
+
{ vendorId: 0x067b }, // Prolific
|
| 624 |
+
{ vendorId: 0x10c4 }, // Silicon Labs
|
| 625 |
+
{ vendorId: 0x1a86 }, // QinHeng Electronics (CH340)
|
| 626 |
+
{ vendorId: 0x239a }, // Adafruit
|
| 627 |
+
{ vendorId: 0x2341 }, // Arduino
|
| 628 |
+
{ vendorId: 0x2e8a }, // Raspberry Pi Foundation
|
| 629 |
+
{ vendorId: 0x1b4f }, // SparkFun
|
| 630 |
+
],
|
| 631 |
+
});
|
| 632 |
+
|
| 633 |
+
if (device) {
|
| 634 |
+
usbDevices = [device];
|
| 635 |
+
console.log("USB device access granted:", device);
|
| 636 |
+
}
|
| 637 |
+
} catch (requestError) {
|
| 638 |
+
console.log(
|
| 639 |
+
"User cancelled USB device selection or no devices found"
|
| 640 |
+
);
|
| 641 |
+
// Try requesting any device as fallback
|
| 642 |
+
try {
|
| 643 |
+
const anyDevice = await navigator.usb.requestDevice({
|
| 644 |
+
filters: [], // Allow any USB device
|
| 645 |
+
});
|
| 646 |
+
if (anyDevice) {
|
| 647 |
+
usbDevices = [anyDevice];
|
| 648 |
+
console.log("Fallback USB device selected:", anyDevice);
|
| 649 |
+
}
|
| 650 |
+
} catch (fallbackError) {
|
| 651 |
+
console.log("No USB device selected");
|
| 652 |
+
}
|
| 653 |
+
}
|
| 654 |
+
}
|
| 655 |
+
|
| 656 |
+
// Try to match with Web Serial port (this is tricky, so we'll take the first available)
|
| 657 |
+
if (usbDevices.length > 0) {
|
| 658 |
+
// Look for common USB-to-serial chip vendor IDs
|
| 659 |
+
const serialChipVendors = [
|
| 660 |
+
0x0403, // FTDI
|
| 661 |
+
0x067b, // Prolific
|
| 662 |
+
0x10c4, // Silicon Labs
|
| 663 |
+
0x1a86, // QinHeng Electronics (CH340)
|
| 664 |
+
0x239a, // Adafruit
|
| 665 |
+
0x2341, // Arduino
|
| 666 |
+
0x2e8a, // Raspberry Pi Foundation
|
| 667 |
+
0x1b4f, // SparkFun
|
| 668 |
+
];
|
| 669 |
+
|
| 670 |
+
const serialDevice =
|
| 671 |
+
usbDevices.find((device) =>
|
| 672 |
+
serialChipVendors.includes(device.vendorId)
|
| 673 |
+
) || usbDevices[0]; // Fallback to first device
|
| 674 |
+
|
| 675 |
+
if (serialDevice) {
|
| 676 |
+
usbDeviceInfo = {
|
| 677 |
+
vendorId: `0x${serialDevice.vendorId
|
| 678 |
+
.toString(16)
|
| 679 |
+
.padStart(4, "0")}`,
|
| 680 |
+
productId: `0x${serialDevice.productId
|
| 681 |
+
.toString(16)
|
| 682 |
+
.padStart(4, "0")}`,
|
| 683 |
+
serialNumber: serialDevice.serialNumber || "Not available",
|
| 684 |
+
manufacturerName: serialDevice.manufacturerName || "Unknown",
|
| 685 |
+
productName: serialDevice.productName || "Unknown",
|
| 686 |
+
usbVersionMajor: serialDevice.usbVersionMajor,
|
| 687 |
+
usbVersionMinor: serialDevice.usbVersionMinor,
|
| 688 |
+
deviceClass: serialDevice.deviceClass,
|
| 689 |
+
deviceSubclass: serialDevice.deviceSubclass,
|
| 690 |
+
deviceProtocol: serialDevice.deviceProtocol,
|
| 691 |
+
};
|
| 692 |
+
console.log("USB device info:", usbDeviceInfo);
|
| 693 |
+
}
|
| 694 |
+
}
|
| 695 |
+
} catch (usbError) {
|
| 696 |
+
console.log("WebUSB not available or no permissions:", usbError);
|
| 697 |
+
// Fallback to Web Serial API info
|
| 698 |
+
const portInfo_metadata = portInfo.port.getInfo();
|
| 699 |
+
console.log("Serial port metadata fallback:", portInfo_metadata);
|
| 700 |
+
if (Object.keys(portInfo_metadata).length > 0) {
|
| 701 |
+
usbDeviceInfo = {
|
| 702 |
+
vendorId: portInfo_metadata.usbVendorId
|
| 703 |
+
? `0x${portInfo_metadata.usbVendorId
|
| 704 |
+
.toString(16)
|
| 705 |
+
.padStart(4, "0")}`
|
| 706 |
+
: "Not available",
|
| 707 |
+
productId: portInfo_metadata.usbProductId
|
| 708 |
+
? `0x${portInfo_metadata.usbProductId
|
| 709 |
+
.toString(16)
|
| 710 |
+
.padStart(4, "0")}`
|
| 711 |
+
: "Not available",
|
| 712 |
+
serialNumber: "Not available via Web Serial",
|
| 713 |
+
manufacturerName: "Not available via Web Serial",
|
| 714 |
+
productName: "Not available via Web Serial",
|
| 715 |
+
};
|
| 716 |
+
}
|
| 717 |
+
}
|
| 718 |
+
|
| 719 |
+
setPortMetadata(usbDeviceInfo);
|
| 720 |
+
|
| 721 |
+
// Get reader/writer for the port
|
| 722 |
+
const reader = portInfo.port.readable?.getReader();
|
| 723 |
+
const writer = portInfo.port.writable?.getWriter();
|
| 724 |
+
|
| 725 |
+
if (!reader || !writer) {
|
| 726 |
+
console.warn("Cannot access port reader/writer");
|
| 727 |
+
setShowDeviceInfo(true);
|
| 728 |
+
return;
|
| 729 |
+
}
|
| 730 |
+
|
| 731 |
+
// Test motor IDs 1-10 (common range for servos)
|
| 732 |
+
for (let motorId = 1; motorId <= 10; motorId++) {
|
| 733 |
+
try {
|
| 734 |
+
// Create STS3215 ping packet
|
| 735 |
+
const packet = new Uint8Array([
|
| 736 |
+
0xff,
|
| 737 |
+
0xff,
|
| 738 |
+
motorId,
|
| 739 |
+
0x02,
|
| 740 |
+
0x01,
|
| 741 |
+
0x00,
|
| 742 |
+
]);
|
| 743 |
+
const checksum = ~(motorId + 0x02 + 0x01) & 0xff;
|
| 744 |
+
packet[5] = checksum;
|
| 745 |
+
|
| 746 |
+
// Send ping
|
| 747 |
+
await writer.write(packet);
|
| 748 |
+
|
| 749 |
+
// Wait a bit for response
|
| 750 |
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
| 751 |
+
|
| 752 |
+
// Try to read response with timeout
|
| 753 |
+
const timeoutPromise = new Promise((_, reject) =>
|
| 754 |
+
setTimeout(() => reject(new Error("Timeout")), 50)
|
| 755 |
+
);
|
| 756 |
+
|
| 757 |
+
try {
|
| 758 |
+
const result = (await Promise.race([
|
| 759 |
+
reader.read(),
|
| 760 |
+
timeoutPromise,
|
| 761 |
+
])) as ReadableStreamReadResult<Uint8Array>;
|
| 762 |
+
|
| 763 |
+
if (
|
| 764 |
+
result &&
|
| 765 |
+
!result.done &&
|
| 766 |
+
result.value &&
|
| 767 |
+
result.value.length >= 6
|
| 768 |
+
) {
|
| 769 |
+
const response = result.value;
|
| 770 |
+
const responseId = response[2];
|
| 771 |
+
|
| 772 |
+
// If we got a response with matching ID, motor exists
|
| 773 |
+
if (responseId === motorId) {
|
| 774 |
+
foundIDs.push(motorId);
|
| 775 |
+
}
|
| 776 |
+
}
|
| 777 |
+
} catch (readError) {
|
| 778 |
+
// No response from this motor ID - that's normal
|
| 779 |
+
}
|
| 780 |
+
} catch (error) {
|
| 781 |
+
console.warn(`Error testing motor ID ${motorId}:`, error);
|
| 782 |
+
}
|
| 783 |
+
|
| 784 |
+
// Small delay between tests
|
| 785 |
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
| 786 |
+
}
|
| 787 |
+
|
| 788 |
+
reader.releaseLock();
|
| 789 |
+
writer.releaseLock();
|
| 790 |
+
|
| 791 |
+
setMotorIDs(foundIDs);
|
| 792 |
+
setShowDeviceInfo(true);
|
| 793 |
+
} catch (error) {
|
| 794 |
+
console.error("Device info scan failed:", error);
|
| 795 |
+
} finally {
|
| 796 |
+
setIsScanning(false);
|
| 797 |
+
}
|
| 798 |
+
};
|
| 799 |
+
|
| 800 |
+
return (
|
| 801 |
+
<div className="border rounded-lg p-4 space-y-3">
|
| 802 |
+
{/* Header with port name and status */}
|
| 803 |
+
<div className="flex items-center justify-between">
|
| 804 |
+
<div className="flex items-center space-x-2">
|
| 805 |
+
<div className="flex flex-col">
|
| 806 |
+
<span className="font-medium">{portInfo.name}</span>
|
| 807 |
+
{portInfo.serialNumber && (
|
| 808 |
+
<span className="text-xs text-gray-500 font-mono">
|
| 809 |
+
ID:{" "}
|
| 810 |
+
{portInfo.serialNumber.length > 20
|
| 811 |
+
? portInfo.serialNumber.substring(0, 20) + "..."
|
| 812 |
+
: portInfo.serialNumber}
|
| 813 |
+
</span>
|
| 814 |
+
)}
|
| 815 |
+
</div>
|
| 816 |
+
<Badge variant={portInfo.isConnected ? "default" : "outline"}>
|
| 817 |
+
{portInfo.isConnected ? "Connected" : "Available"}
|
| 818 |
+
</Badge>
|
| 819 |
+
{portInfo.usbMetadata && (
|
| 820 |
+
<Badge variant="outline" className="text-xs">
|
| 821 |
+
{portInfo.usbMetadata.manufacturerName}
|
| 822 |
+
</Badge>
|
| 823 |
+
)}
|
| 824 |
+
</div>
|
| 825 |
+
<Button variant="destructive" size="sm" onClick={onDisconnect}>
|
| 826 |
+
Remove
|
| 827 |
+
</Button>
|
| 828 |
+
</div>
|
| 829 |
+
|
| 830 |
+
{/* Robot Info Display (when not editing) */}
|
| 831 |
+
{!isEditing && currentRobotType && currentRobotId && (
|
| 832 |
+
<div className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
| 833 |
+
<div className="flex items-center space-x-3">
|
| 834 |
+
<div>
|
| 835 |
+
<div className="font-medium text-sm">{currentRobotId}</div>
|
| 836 |
+
<div className="text-xs text-gray-600">
|
| 837 |
+
{currentRobotType.replace("_", " ")}
|
| 838 |
+
</div>
|
| 839 |
+
</div>
|
| 840 |
+
{calibrationStatus && (
|
| 841 |
+
<Badge variant="default" className="bg-green-100 text-green-800">
|
| 842 |
+
✅ Calibrated
|
| 843 |
+
</Badge>
|
| 844 |
+
)}
|
| 845 |
+
</div>
|
| 846 |
+
<Button
|
| 847 |
+
variant="outline"
|
| 848 |
+
size="sm"
|
| 849 |
+
onClick={() => setIsEditing(true)}
|
| 850 |
+
>
|
| 851 |
+
Edit
|
| 852 |
+
</Button>
|
| 853 |
+
</div>
|
| 854 |
+
)}
|
| 855 |
+
|
| 856 |
+
{/* Setup prompt for unconfigured robots */}
|
| 857 |
+
{!isEditing && (!currentRobotType || !currentRobotId) && (
|
| 858 |
+
<div className="flex items-center justify-between p-3 bg-blue-50 rounded-lg">
|
| 859 |
+
<div className="text-sm text-blue-800">
|
| 860 |
+
Robot needs configuration before use
|
| 861 |
+
</div>
|
| 862 |
+
<Button
|
| 863 |
+
variant="outline"
|
| 864 |
+
size="sm"
|
| 865 |
+
onClick={() => setIsEditing(true)}
|
| 866 |
+
>
|
| 867 |
+
Configure
|
| 868 |
+
</Button>
|
| 869 |
+
</div>
|
| 870 |
+
)}
|
| 871 |
+
|
| 872 |
+
{/* Robot Configuration Form (when editing) */}
|
| 873 |
+
{isEditing && (
|
| 874 |
+
<div className="space-y-3 p-3 bg-gray-50 rounded-lg">
|
| 875 |
+
<div className="grid grid-cols-2 gap-3">
|
| 876 |
+
<div>
|
| 877 |
+
<label className="text-sm font-medium block mb-1">
|
| 878 |
+
Robot Type
|
| 879 |
+
</label>
|
| 880 |
+
<select
|
| 881 |
+
value={robotType}
|
| 882 |
+
onChange={(e) =>
|
| 883 |
+
setRobotType(
|
| 884 |
+
e.target.value as "so100_follower" | "so100_leader"
|
| 885 |
+
)
|
| 886 |
+
}
|
| 887 |
+
className="w-full px-2 py-1 border rounded text-sm"
|
| 888 |
+
>
|
| 889 |
+
<option value="so100_follower">SO-100 Follower</option>
|
| 890 |
+
<option value="so100_leader">SO-100 Leader</option>
|
| 891 |
+
</select>
|
| 892 |
+
</div>
|
| 893 |
+
<div>
|
| 894 |
+
<label className="text-sm font-medium block mb-1">Robot ID</label>
|
| 895 |
+
<input
|
| 896 |
+
type="text"
|
| 897 |
+
value={robotId}
|
| 898 |
+
onChange={(e) => setRobotId(e.target.value)}
|
| 899 |
+
placeholder="e.g., my_robot"
|
| 900 |
+
className="w-full px-2 py-1 border rounded text-sm"
|
| 901 |
+
/>
|
| 902 |
+
</div>
|
| 903 |
+
</div>
|
| 904 |
+
|
| 905 |
+
<div className="flex gap-2">
|
| 906 |
+
<Button size="sm" onClick={handleSave} disabled={!robotId.trim()}>
|
| 907 |
+
Save
|
| 908 |
+
</Button>
|
| 909 |
+
<Button size="sm" variant="outline" onClick={handleCancel}>
|
| 910 |
+
Cancel
|
| 911 |
+
</Button>
|
| 912 |
+
</div>
|
| 913 |
+
</div>
|
| 914 |
+
)}
|
| 915 |
+
|
| 916 |
+
{/* Calibration Status and Action */}
|
| 917 |
+
{currentRobotType && currentRobotId && (
|
| 918 |
+
<div className="space-y-3">
|
| 919 |
+
<div className="flex items-center justify-between">
|
| 920 |
+
<div className="text-sm text-gray-600">
|
| 921 |
+
{calibrationStatus ? (
|
| 922 |
+
<span>
|
| 923 |
+
Last calibrated:{" "}
|
| 924 |
+
{new Date(calibrationStatus.timestamp).toLocaleDateString()}
|
| 925 |
+
<span className="text-xs ml-1">
|
| 926 |
+
({calibrationStatus.readCount} readings)
|
| 927 |
+
</span>
|
| 928 |
+
</span>
|
| 929 |
+
) : (
|
| 930 |
+
<span>Not calibrated yet</span>
|
| 931 |
+
)}
|
| 932 |
+
</div>
|
| 933 |
+
<Button
|
| 934 |
+
size="sm"
|
| 935 |
+
variant={calibrationStatus ? "outline" : "default"}
|
| 936 |
+
onClick={onCalibrate}
|
| 937 |
+
disabled={!currentRobotType || !currentRobotId}
|
| 938 |
+
>
|
| 939 |
+
{calibrationStatus ? "Re-calibrate" : "Calibrate"}
|
| 940 |
+
</Button>
|
| 941 |
+
</div>
|
| 942 |
+
|
| 943 |
+
{/* Device Info Scanner */}
|
| 944 |
+
<div className="flex items-center justify-between">
|
| 945 |
+
<div className="text-sm text-gray-600">
|
| 946 |
+
Scan device info and motor IDs
|
| 947 |
+
</div>
|
| 948 |
+
<Button
|
| 949 |
+
size="sm"
|
| 950 |
+
variant="outline"
|
| 951 |
+
onClick={scanDeviceInfo}
|
| 952 |
+
disabled={!portInfo.isConnected || isScanning}
|
| 953 |
+
>
|
| 954 |
+
{isScanning ? "Scanning..." : "Show Device Info"}
|
| 955 |
+
</Button>
|
| 956 |
+
</div>
|
| 957 |
+
|
| 958 |
+
{/* Device Info Results */}
|
| 959 |
+
{showDeviceInfo && (
|
| 960 |
+
<div className="p-3 bg-gray-50 rounded-lg space-y-3">
|
| 961 |
+
{/* USB Device Information */}
|
| 962 |
+
{portMetadata && (
|
| 963 |
+
<div>
|
| 964 |
+
<div className="text-sm font-medium mb-2">
|
| 965 |
+
📱 USB Device Info:
|
| 966 |
+
</div>
|
| 967 |
+
<div className="space-y-1 text-xs">
|
| 968 |
+
<div className="flex justify-between">
|
| 969 |
+
<span className="text-gray-600">Vendor ID:</span>
|
| 970 |
+
<span className="font-mono">{portMetadata.vendorId}</span>
|
| 971 |
+
</div>
|
| 972 |
+
<div className="flex justify-between">
|
| 973 |
+
<span className="text-gray-600">Product ID:</span>
|
| 974 |
+
<span className="font-mono">
|
| 975 |
+
{portMetadata.productId}
|
| 976 |
+
</span>
|
| 977 |
+
</div>
|
| 978 |
+
<div className="flex justify-between">
|
| 979 |
+
<span className="text-gray-600">Serial Number:</span>
|
| 980 |
+
<span className="font-mono text-green-600 font-semibold">
|
| 981 |
+
{portMetadata.serialNumber}
|
| 982 |
+
</span>
|
| 983 |
+
</div>
|
| 984 |
+
<div className="flex justify-between">
|
| 985 |
+
<span className="text-gray-600">Manufacturer:</span>
|
| 986 |
+
<span>{portMetadata.manufacturerName}</span>
|
| 987 |
+
</div>
|
| 988 |
+
<div className="flex justify-between">
|
| 989 |
+
<span className="text-gray-600">Product:</span>
|
| 990 |
+
<span>{portMetadata.productName}</span>
|
| 991 |
+
</div>
|
| 992 |
+
{portMetadata.usbVersionMajor && (
|
| 993 |
+
<div className="flex justify-between">
|
| 994 |
+
<span className="text-gray-600">USB Version:</span>
|
| 995 |
+
<span>
|
| 996 |
+
{portMetadata.usbVersionMajor}.
|
| 997 |
+
{portMetadata.usbVersionMinor}
|
| 998 |
+
</span>
|
| 999 |
+
</div>
|
| 1000 |
+
)}
|
| 1001 |
+
{portMetadata.deviceClass !== undefined && (
|
| 1002 |
+
<div className="flex justify-between">
|
| 1003 |
+
<span className="text-gray-600">Device Class:</span>
|
| 1004 |
+
<span>
|
| 1005 |
+
0x
|
| 1006 |
+
{portMetadata.deviceClass
|
| 1007 |
+
.toString(16)
|
| 1008 |
+
.padStart(2, "0")}
|
| 1009 |
+
</span>
|
| 1010 |
+
</div>
|
| 1011 |
+
)}
|
| 1012 |
+
</div>
|
| 1013 |
+
</div>
|
| 1014 |
+
)}
|
| 1015 |
+
|
| 1016 |
+
{/* Motor IDs */}
|
| 1017 |
+
<div>
|
| 1018 |
+
<div className="text-sm font-medium mb-2">
|
| 1019 |
+
🤖 Found Motor IDs:
|
| 1020 |
+
</div>
|
| 1021 |
+
{motorIDs.length > 0 ? (
|
| 1022 |
+
<div className="flex flex-wrap gap-2">
|
| 1023 |
+
{motorIDs.map((id) => (
|
| 1024 |
+
<Badge key={id} variant="outline" className="text-xs">
|
| 1025 |
+
Motor {id}
|
| 1026 |
+
</Badge>
|
| 1027 |
+
))}
|
| 1028 |
+
</div>
|
| 1029 |
+
) : (
|
| 1030 |
+
<div className="text-sm text-gray-500">
|
| 1031 |
+
No motor IDs found. Check connection and power.
|
| 1032 |
+
</div>
|
| 1033 |
+
)}
|
| 1034 |
+
</div>
|
| 1035 |
+
|
| 1036 |
+
<Button
|
| 1037 |
+
size="sm"
|
| 1038 |
+
variant="outline"
|
| 1039 |
+
onClick={() => setShowDeviceInfo(false)}
|
| 1040 |
+
className="mt-2 text-xs"
|
| 1041 |
+
>
|
| 1042 |
+
Hide
|
| 1043 |
+
</Button>
|
| 1044 |
+
</div>
|
| 1045 |
+
)}
|
| 1046 |
+
</div>
|
| 1047 |
+
)}
|
| 1048 |
+
</div>
|
| 1049 |
+
);
|
| 1050 |
+
}
|
src/demo/components/ui/alert.tsx
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as React from "react";
|
| 2 |
+
import { cva, type VariantProps } from "class-variance-authority";
|
| 3 |
+
import { cn } from "../../lib/utils";
|
| 4 |
+
|
| 5 |
+
const alertVariants = cva(
|
| 6 |
+
"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",
|
| 7 |
+
{
|
| 8 |
+
variants: {
|
| 9 |
+
variant: {
|
| 10 |
+
default: "bg-background text-foreground",
|
| 11 |
+
destructive:
|
| 12 |
+
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
| 13 |
+
},
|
| 14 |
+
},
|
| 15 |
+
defaultVariants: {
|
| 16 |
+
variant: "default",
|
| 17 |
+
},
|
| 18 |
+
}
|
| 19 |
+
);
|
| 20 |
+
|
| 21 |
+
const Alert = React.forwardRef<
|
| 22 |
+
HTMLDivElement,
|
| 23 |
+
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
| 24 |
+
>(({ className, variant, ...props }, ref) => (
|
| 25 |
+
<div
|
| 26 |
+
ref={ref}
|
| 27 |
+
role="alert"
|
| 28 |
+
className={cn(alertVariants({ variant }), className)}
|
| 29 |
+
{...props}
|
| 30 |
+
/>
|
| 31 |
+
));
|
| 32 |
+
Alert.displayName = "Alert";
|
| 33 |
+
|
| 34 |
+
const AlertTitle = React.forwardRef<
|
| 35 |
+
HTMLParagraphElement,
|
| 36 |
+
React.HTMLAttributes<HTMLHeadingElement>
|
| 37 |
+
>(({ className, ...props }, ref) => (
|
| 38 |
+
<h5
|
| 39 |
+
ref={ref}
|
| 40 |
+
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
| 41 |
+
{...props}
|
| 42 |
+
/>
|
| 43 |
+
));
|
| 44 |
+
AlertTitle.displayName = "AlertTitle";
|
| 45 |
+
|
| 46 |
+
const AlertDescription = React.forwardRef<
|
| 47 |
+
HTMLParagraphElement,
|
| 48 |
+
React.HTMLAttributes<HTMLParagraphElement>
|
| 49 |
+
>(({ className, ...props }, ref) => (
|
| 50 |
+
<div
|
| 51 |
+
ref={ref}
|
| 52 |
+
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
| 53 |
+
{...props}
|
| 54 |
+
/>
|
| 55 |
+
));
|
| 56 |
+
AlertDescription.displayName = "AlertDescription";
|
| 57 |
+
|
| 58 |
+
export { Alert, AlertTitle, AlertDescription };
|
src/demo/components/ui/badge.tsx
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as React from "react";
|
| 2 |
+
import { cva, type VariantProps } from "class-variance-authority";
|
| 3 |
+
import { cn } from "../../lib/utils";
|
| 4 |
+
|
| 5 |
+
const badgeVariants = cva(
|
| 6 |
+
"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",
|
| 7 |
+
{
|
| 8 |
+
variants: {
|
| 9 |
+
variant: {
|
| 10 |
+
default:
|
| 11 |
+
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
| 12 |
+
secondary:
|
| 13 |
+
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
| 14 |
+
destructive:
|
| 15 |
+
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
| 16 |
+
outline: "text-foreground",
|
| 17 |
+
},
|
| 18 |
+
},
|
| 19 |
+
defaultVariants: {
|
| 20 |
+
variant: "default",
|
| 21 |
+
},
|
| 22 |
+
}
|
| 23 |
+
);
|
| 24 |
+
|
| 25 |
+
export interface BadgeProps
|
| 26 |
+
extends React.HTMLAttributes<HTMLDivElement>,
|
| 27 |
+
VariantProps<typeof badgeVariants> {}
|
| 28 |
+
|
| 29 |
+
function Badge({ className, variant, ...props }: BadgeProps) {
|
| 30 |
+
return (
|
| 31 |
+
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
| 32 |
+
);
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
export { Badge, badgeVariants };
|
src/demo/components/ui/button.tsx
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as React from "react";
|
| 2 |
+
import { cva, type VariantProps } from "class-variance-authority";
|
| 3 |
+
import { cn } from "../../lib/utils";
|
| 4 |
+
|
| 5 |
+
const buttonVariants = cva(
|
| 6 |
+
"inline-flex items-center justify-center 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",
|
| 7 |
+
{
|
| 8 |
+
variants: {
|
| 9 |
+
variant: {
|
| 10 |
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
| 11 |
+
destructive:
|
| 12 |
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
| 13 |
+
outline:
|
| 14 |
+
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
| 15 |
+
secondary:
|
| 16 |
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
| 17 |
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
| 18 |
+
link: "text-primary underline-offset-4 hover:underline",
|
| 19 |
+
},
|
| 20 |
+
size: {
|
| 21 |
+
default: "h-10 px-4 py-2",
|
| 22 |
+
sm: "h-9 rounded-md px-3",
|
| 23 |
+
lg: "h-11 rounded-md px-8",
|
| 24 |
+
icon: "h-10 w-10",
|
| 25 |
+
},
|
| 26 |
+
},
|
| 27 |
+
defaultVariants: {
|
| 28 |
+
variant: "default",
|
| 29 |
+
size: "default",
|
| 30 |
+
},
|
| 31 |
+
}
|
| 32 |
+
);
|
| 33 |
+
|
| 34 |
+
export interface ButtonProps
|
| 35 |
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
| 36 |
+
VariantProps<typeof buttonVariants> {
|
| 37 |
+
asChild?: boolean;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
| 41 |
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
| 42 |
+
return (
|
| 43 |
+
<button
|
| 44 |
+
className={cn(buttonVariants({ variant, size, className }))}
|
| 45 |
+
ref={ref}
|
| 46 |
+
{...props}
|
| 47 |
+
/>
|
| 48 |
+
);
|
| 49 |
+
}
|
| 50 |
+
);
|
| 51 |
+
Button.displayName = "Button";
|
| 52 |
+
|
| 53 |
+
export { Button, buttonVariants };
|
src/demo/components/ui/card.tsx
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as React from "react";
|
| 2 |
+
import { cn } from "../../lib/utils";
|
| 3 |
+
|
| 4 |
+
const Card = React.forwardRef<
|
| 5 |
+
HTMLDivElement,
|
| 6 |
+
React.HTMLAttributes<HTMLDivElement>
|
| 7 |
+
>(({ className, ...props }, ref) => (
|
| 8 |
+
<div
|
| 9 |
+
ref={ref}
|
| 10 |
+
className={cn(
|
| 11 |
+
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
| 12 |
+
className
|
| 13 |
+
)}
|
| 14 |
+
{...props}
|
| 15 |
+
/>
|
| 16 |
+
));
|
| 17 |
+
Card.displayName = "Card";
|
| 18 |
+
|
| 19 |
+
const CardHeader = React.forwardRef<
|
| 20 |
+
HTMLDivElement,
|
| 21 |
+
React.HTMLAttributes<HTMLDivElement>
|
| 22 |
+
>(({ className, ...props }, ref) => (
|
| 23 |
+
<div
|
| 24 |
+
ref={ref}
|
| 25 |
+
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
| 26 |
+
{...props}
|
| 27 |
+
/>
|
| 28 |
+
));
|
| 29 |
+
CardHeader.displayName = "CardHeader";
|
| 30 |
+
|
| 31 |
+
const CardTitle = React.forwardRef<
|
| 32 |
+
HTMLParagraphElement,
|
| 33 |
+
React.HTMLAttributes<HTMLHeadingElement>
|
| 34 |
+
>(({ className, ...props }, ref) => (
|
| 35 |
+
<h3
|
| 36 |
+
ref={ref}
|
| 37 |
+
className={cn(
|
| 38 |
+
"text-2xl font-semibold leading-none tracking-tight",
|
| 39 |
+
className
|
| 40 |
+
)}
|
| 41 |
+
{...props}
|
| 42 |
+
/>
|
| 43 |
+
));
|
| 44 |
+
CardTitle.displayName = "CardTitle";
|
| 45 |
+
|
| 46 |
+
const CardDescription = React.forwardRef<
|
| 47 |
+
HTMLParagraphElement,
|
| 48 |
+
React.HTMLAttributes<HTMLParagraphElement>
|
| 49 |
+
>(({ className, ...props }, ref) => (
|
| 50 |
+
<p
|
| 51 |
+
ref={ref}
|
| 52 |
+
className={cn("text-sm text-muted-foreground", className)}
|
| 53 |
+
{...props}
|
| 54 |
+
/>
|
| 55 |
+
));
|
| 56 |
+
CardDescription.displayName = "CardDescription";
|
| 57 |
+
|
| 58 |
+
const CardContent = React.forwardRef<
|
| 59 |
+
HTMLDivElement,
|
| 60 |
+
React.HTMLAttributes<HTMLDivElement>
|
| 61 |
+
>(({ className, ...props }, ref) => (
|
| 62 |
+
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
| 63 |
+
));
|
| 64 |
+
CardContent.displayName = "CardContent";
|
| 65 |
+
|
| 66 |
+
const CardFooter = React.forwardRef<
|
| 67 |
+
HTMLDivElement,
|
| 68 |
+
React.HTMLAttributes<HTMLDivElement>
|
| 69 |
+
>(({ className, ...props }, ref) => (
|
| 70 |
+
<div
|
| 71 |
+
ref={ref}
|
| 72 |
+
className={cn("flex items-center p-6 pt-0", className)}
|
| 73 |
+
{...props}
|
| 74 |
+
/>
|
| 75 |
+
));
|
| 76 |
+
CardFooter.displayName = "CardFooter";
|
| 77 |
+
|
| 78 |
+
export {
|
| 79 |
+
Card,
|
| 80 |
+
CardHeader,
|
| 81 |
+
CardFooter,
|
| 82 |
+
CardTitle,
|
| 83 |
+
CardDescription,
|
| 84 |
+
CardContent,
|
| 85 |
+
};
|
src/demo/index.css
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@tailwind base;
|
| 2 |
+
@tailwind components;
|
| 3 |
+
@tailwind utilities;
|
| 4 |
+
|
| 5 |
+
@layer base {
|
| 6 |
+
* {
|
| 7 |
+
@apply border-border;
|
| 8 |
+
}
|
| 9 |
+
body {
|
| 10 |
+
@apply bg-background text-foreground;
|
| 11 |
+
}
|
| 12 |
+
}
|
src/demo/lib/utils.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { type ClassValue, clsx } from "clsx";
|
| 2 |
+
import { twMerge } from "tailwind-merge";
|
| 3 |
+
|
| 4 |
+
export function cn(...inputs: ClassValue[]) {
|
| 5 |
+
return twMerge(clsx(inputs));
|
| 6 |
+
}
|
src/demo/main.tsx
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from "react";
|
| 2 |
+
import ReactDOM from "react-dom/client";
|
| 3 |
+
import { App } from "./App";
|
| 4 |
+
import "./index.css";
|
| 5 |
+
|
| 6 |
+
ReactDOM.createRoot(document.getElementById("root")!).render(
|
| 7 |
+
<React.StrictMode>
|
| 8 |
+
<App />
|
| 9 |
+
</React.StrictMode>
|
| 10 |
+
);
|
src/demo/pages/Calibrate.tsx
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState, useEffect } from "react";
|
| 2 |
+
import { Button } from "../components/ui/button";
|
| 3 |
+
import {
|
| 4 |
+
Card,
|
| 5 |
+
CardContent,
|
| 6 |
+
CardDescription,
|
| 7 |
+
CardHeader,
|
| 8 |
+
CardTitle,
|
| 9 |
+
} from "../components/ui/card";
|
| 10 |
+
import { Alert, AlertDescription } from "../components/ui/alert";
|
| 11 |
+
import { Badge } from "../components/ui/badge";
|
| 12 |
+
import { CalibrationWizard } from "../components/CalibrationWizard";
|
| 13 |
+
import type { ConnectedRobot } from "../types";
|
| 14 |
+
|
| 15 |
+
interface CalibrateProps {
|
| 16 |
+
selectedRobot: ConnectedRobot;
|
| 17 |
+
onBack: () => void;
|
| 18 |
+
onHome: () => void;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
export function Calibrate({ selectedRobot, onBack, onHome }: CalibrateProps) {
|
| 22 |
+
const [calibrationStarted, setCalibrationStarted] = useState(false);
|
| 23 |
+
|
| 24 |
+
// Auto-start calibration when component mounts
|
| 25 |
+
useEffect(() => {
|
| 26 |
+
if (selectedRobot && selectedRobot.isConnected) {
|
| 27 |
+
setCalibrationStarted(true);
|
| 28 |
+
}
|
| 29 |
+
}, [selectedRobot]);
|
| 30 |
+
|
| 31 |
+
if (!selectedRobot) {
|
| 32 |
+
return (
|
| 33 |
+
<div className="container mx-auto px-4 py-8">
|
| 34 |
+
<Alert variant="destructive">
|
| 35 |
+
<AlertDescription>
|
| 36 |
+
No robot selected. Please go back to setup.
|
| 37 |
+
</AlertDescription>
|
| 38 |
+
</Alert>
|
| 39 |
+
<div className="mt-4">
|
| 40 |
+
<Button onClick={onBack}>Back to Setup</Button>
|
| 41 |
+
</div>
|
| 42 |
+
</div>
|
| 43 |
+
);
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
return (
|
| 47 |
+
<div className="container mx-auto px-4 py-8 max-w-4xl">
|
| 48 |
+
<div className="space-y-6">
|
| 49 |
+
<div className="text-center space-y-2">
|
| 50 |
+
<h1 className="text-3xl font-bold">Robot Calibration</h1>
|
| 51 |
+
<p className="text-muted-foreground">
|
| 52 |
+
Calibrating: {selectedRobot.robotId}
|
| 53 |
+
</p>
|
| 54 |
+
</div>
|
| 55 |
+
|
| 56 |
+
<Card>
|
| 57 |
+
<CardHeader>
|
| 58 |
+
<div className="flex items-center justify-between">
|
| 59 |
+
<div>
|
| 60 |
+
<CardTitle className="text-xl">
|
| 61 |
+
{selectedRobot.robotId}
|
| 62 |
+
</CardTitle>
|
| 63 |
+
<CardDescription>{selectedRobot.name}</CardDescription>
|
| 64 |
+
</div>
|
| 65 |
+
<div className="flex items-center space-x-2">
|
| 66 |
+
<Badge
|
| 67 |
+
variant={selectedRobot.isConnected ? "default" : "secondary"}
|
| 68 |
+
>
|
| 69 |
+
{selectedRobot.isConnected ? "Connected" : "Disconnected"}
|
| 70 |
+
</Badge>
|
| 71 |
+
<Badge variant="outline">
|
| 72 |
+
{selectedRobot.robotType?.replace("_", " ")}
|
| 73 |
+
</Badge>
|
| 74 |
+
</div>
|
| 75 |
+
</div>
|
| 76 |
+
</CardHeader>
|
| 77 |
+
<CardContent>
|
| 78 |
+
{!selectedRobot.isConnected ? (
|
| 79 |
+
<Alert variant="destructive">
|
| 80 |
+
<AlertDescription>
|
| 81 |
+
Robot is not connected. Please check connection and try again.
|
| 82 |
+
</AlertDescription>
|
| 83 |
+
</Alert>
|
| 84 |
+
) : calibrationStarted ? (
|
| 85 |
+
<CalibrationWizard
|
| 86 |
+
robot={selectedRobot}
|
| 87 |
+
onComplete={onHome}
|
| 88 |
+
onCancel={onBack}
|
| 89 |
+
/>
|
| 90 |
+
) : (
|
| 91 |
+
<div className="space-y-4">
|
| 92 |
+
<div className="text-center py-8">
|
| 93 |
+
<div className="text-2xl mb-4">🛠️</div>
|
| 94 |
+
<h3 className="text-lg font-semibold mb-2">
|
| 95 |
+
Ready to Calibrate
|
| 96 |
+
</h3>
|
| 97 |
+
<p className="text-muted-foreground mb-4">
|
| 98 |
+
Make sure your robot arm is in a safe position and you have
|
| 99 |
+
a clear workspace.
|
| 100 |
+
</p>
|
| 101 |
+
<Button onClick={() => setCalibrationStarted(true)} size="lg">
|
| 102 |
+
Start Calibration
|
| 103 |
+
</Button>
|
| 104 |
+
</div>
|
| 105 |
+
</div>
|
| 106 |
+
)}
|
| 107 |
+
</CardContent>
|
| 108 |
+
</Card>
|
| 109 |
+
|
| 110 |
+
<div className="flex justify-center space-x-4">
|
| 111 |
+
<Button variant="outline" onClick={onBack}>
|
| 112 |
+
Back to Setup
|
| 113 |
+
</Button>
|
| 114 |
+
<Button variant="outline" onClick={onHome}>
|
| 115 |
+
Back to Home
|
| 116 |
+
</Button>
|
| 117 |
+
</div>
|
| 118 |
+
</div>
|
| 119 |
+
</div>
|
| 120 |
+
);
|
| 121 |
+
}
|
src/demo/pages/Home.tsx
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState } from "react";
|
| 2 |
+
import { Button } from "../components/ui/button";
|
| 3 |
+
import {
|
| 4 |
+
Card,
|
| 5 |
+
CardContent,
|
| 6 |
+
CardDescription,
|
| 7 |
+
CardHeader,
|
| 8 |
+
CardTitle,
|
| 9 |
+
} from "../components/ui/card";
|
| 10 |
+
import { Alert, AlertDescription } from "../components/ui/alert";
|
| 11 |
+
import { PortManager } from "../components/PortManager";
|
| 12 |
+
import { CalibrationPanel } from "../components/CalibrationPanel";
|
| 13 |
+
import { isWebSerialSupported } from "../../lerobot/web/calibrate";
|
| 14 |
+
import type { ConnectedRobot } from "../types";
|
| 15 |
+
|
| 16 |
+
interface HomeProps {
|
| 17 |
+
onGetStarted: () => void;
|
| 18 |
+
connectedRobots: ConnectedRobot[];
|
| 19 |
+
onConnectedRobotsChange: (robots: ConnectedRobot[]) => void;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
export function Home({
|
| 23 |
+
onGetStarted,
|
| 24 |
+
connectedRobots,
|
| 25 |
+
onConnectedRobotsChange,
|
| 26 |
+
}: HomeProps) {
|
| 27 |
+
const [calibratingRobot, setCalibratingRobot] =
|
| 28 |
+
useState<ConnectedRobot | null>(null);
|
| 29 |
+
const isSupported = isWebSerialSupported();
|
| 30 |
+
|
| 31 |
+
const handleCalibrate = (
|
| 32 |
+
port: SerialPort,
|
| 33 |
+
robotType: "so100_follower" | "so100_leader",
|
| 34 |
+
robotId: string
|
| 35 |
+
) => {
|
| 36 |
+
// Find the robot from connectedRobots
|
| 37 |
+
const robot = connectedRobots.find((r) => r.port === port);
|
| 38 |
+
if (robot) {
|
| 39 |
+
setCalibratingRobot(robot);
|
| 40 |
+
}
|
| 41 |
+
};
|
| 42 |
+
|
| 43 |
+
const handleFinishCalibration = () => {
|
| 44 |
+
setCalibratingRobot(null);
|
| 45 |
+
};
|
| 46 |
+
|
| 47 |
+
return (
|
| 48 |
+
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
|
| 49 |
+
<div className="container mx-auto px-6 py-12">
|
| 50 |
+
{/* Header */}
|
| 51 |
+
<div className="text-center mb-12">
|
| 52 |
+
<h1 className="text-4xl font-bold text-gray-900 mb-4">
|
| 53 |
+
🤖 LeRobot.js
|
| 54 |
+
</h1>
|
| 55 |
+
<p className="text-xl text-gray-600 mb-8">
|
| 56 |
+
State-of-the-art AI for real-world robotics in JavaScript
|
| 57 |
+
</p>
|
| 58 |
+
|
| 59 |
+
{!isSupported && (
|
| 60 |
+
<Alert variant="destructive" className="max-w-2xl mx-auto mb-8">
|
| 61 |
+
<AlertDescription>
|
| 62 |
+
Web Serial API is not supported in this browser. Please use
|
| 63 |
+
Chrome, Edge, or another Chromium-based browser to use this
|
| 64 |
+
demo.
|
| 65 |
+
</AlertDescription>
|
| 66 |
+
</Alert>
|
| 67 |
+
)}
|
| 68 |
+
</div>
|
| 69 |
+
|
| 70 |
+
{/* Main Content */}
|
| 71 |
+
{calibratingRobot ? (
|
| 72 |
+
<div className="max-w-6xl mx-auto">
|
| 73 |
+
<div className="mb-4">
|
| 74 |
+
<Button
|
| 75 |
+
variant="outline"
|
| 76 |
+
onClick={() => setCalibratingRobot(null)}
|
| 77 |
+
>
|
| 78 |
+
← Back to Dashboard
|
| 79 |
+
</Button>
|
| 80 |
+
</div>
|
| 81 |
+
<CalibrationPanel
|
| 82 |
+
robot={calibratingRobot}
|
| 83 |
+
onFinish={handleFinishCalibration}
|
| 84 |
+
/>
|
| 85 |
+
</div>
|
| 86 |
+
) : (
|
| 87 |
+
<div className="max-w-6xl mx-auto">
|
| 88 |
+
<PortManager
|
| 89 |
+
onCalibrate={handleCalibrate}
|
| 90 |
+
connectedRobots={connectedRobots}
|
| 91 |
+
onConnectedRobotsChange={onConnectedRobotsChange}
|
| 92 |
+
/>
|
| 93 |
+
</div>
|
| 94 |
+
)}
|
| 95 |
+
</div>
|
| 96 |
+
</div>
|
| 97 |
+
);
|
| 98 |
+
}
|
src/demo/pages/Setup.tsx
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from "react";
|
| 2 |
+
import { Button } from "../components/ui/button";
|
| 3 |
+
import {
|
| 4 |
+
Card,
|
| 5 |
+
CardContent,
|
| 6 |
+
CardDescription,
|
| 7 |
+
CardHeader,
|
| 8 |
+
CardTitle,
|
| 9 |
+
} from "../components/ui/card";
|
| 10 |
+
import { Alert, AlertDescription, AlertTitle } from "../components/ui/alert";
|
| 11 |
+
import { Badge } from "../components/ui/badge";
|
| 12 |
+
import type { ConnectedRobot } from "../types";
|
| 13 |
+
|
| 14 |
+
interface SetupProps {
|
| 15 |
+
connectedRobots: ConnectedRobot[];
|
| 16 |
+
onBack: () => void;
|
| 17 |
+
onNext: (robot: ConnectedRobot) => void;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
export function Setup({ connectedRobots, onBack, onNext }: SetupProps) {
|
| 21 |
+
const configuredRobots = connectedRobots.filter(
|
| 22 |
+
(r) => r.robotType && r.robotId
|
| 23 |
+
);
|
| 24 |
+
|
| 25 |
+
return (
|
| 26 |
+
<div className="container mx-auto px-4 py-8 max-w-4xl">
|
| 27 |
+
<div className="space-y-6">
|
| 28 |
+
<div className="text-center space-y-2">
|
| 29 |
+
<h1 className="text-3xl font-bold">Robot Setup</h1>
|
| 30 |
+
<p className="text-muted-foreground">
|
| 31 |
+
Select a connected robot to calibrate
|
| 32 |
+
</p>
|
| 33 |
+
</div>
|
| 34 |
+
|
| 35 |
+
<div className="space-y-4">
|
| 36 |
+
<div className="flex items-center justify-between">
|
| 37 |
+
<h2 className="text-xl font-semibold">Connected Robots</h2>
|
| 38 |
+
<Badge variant="outline">{configuredRobots.length} ready</Badge>
|
| 39 |
+
</div>
|
| 40 |
+
|
| 41 |
+
{configuredRobots.length === 0 ? (
|
| 42 |
+
<Card>
|
| 43 |
+
<CardContent className="text-center py-8">
|
| 44 |
+
<div className="text-muted-foreground space-y-2">
|
| 45 |
+
<p>No configured robots found.</p>
|
| 46 |
+
<p className="text-sm">
|
| 47 |
+
Go back to the home page to connect and configure your
|
| 48 |
+
robots.
|
| 49 |
+
</p>
|
| 50 |
+
</div>
|
| 51 |
+
</CardContent>
|
| 52 |
+
</Card>
|
| 53 |
+
) : (
|
| 54 |
+
<div className="grid gap-4">
|
| 55 |
+
{configuredRobots.map((robot, index) => (
|
| 56 |
+
<Card
|
| 57 |
+
key={index}
|
| 58 |
+
className="cursor-pointer hover:shadow-md transition-shadow"
|
| 59 |
+
>
|
| 60 |
+
<CardHeader>
|
| 61 |
+
<div className="flex items-center justify-between">
|
| 62 |
+
<div>
|
| 63 |
+
<CardTitle className="text-lg">
|
| 64 |
+
{robot.robotId}
|
| 65 |
+
</CardTitle>
|
| 66 |
+
<CardDescription>{robot.name}</CardDescription>
|
| 67 |
+
</div>
|
| 68 |
+
<div className="flex items-center space-x-2">
|
| 69 |
+
<Badge
|
| 70 |
+
variant={robot.isConnected ? "default" : "outline"}
|
| 71 |
+
>
|
| 72 |
+
{robot.isConnected ? "Connected" : "Available"}
|
| 73 |
+
</Badge>
|
| 74 |
+
<Badge variant="outline">
|
| 75 |
+
{robot.robotType?.replace("_", " ")}
|
| 76 |
+
</Badge>
|
| 77 |
+
</div>
|
| 78 |
+
</div>
|
| 79 |
+
</CardHeader>
|
| 80 |
+
<CardContent>
|
| 81 |
+
<Button onClick={() => onNext(robot)} className="w-full">
|
| 82 |
+
Calibrate This Robot
|
| 83 |
+
</Button>
|
| 84 |
+
</CardContent>
|
| 85 |
+
</Card>
|
| 86 |
+
))}
|
| 87 |
+
</div>
|
| 88 |
+
)}
|
| 89 |
+
</div>
|
| 90 |
+
|
| 91 |
+
<div className="flex justify-center">
|
| 92 |
+
<Button variant="outline" onClick={onBack}>
|
| 93 |
+
Back to Home
|
| 94 |
+
</Button>
|
| 95 |
+
</div>
|
| 96 |
+
</div>
|
| 97 |
+
</div>
|
| 98 |
+
);
|
| 99 |
+
}
|
src/demo/types.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export interface ConnectedRobot {
|
| 2 |
+
port: SerialPort;
|
| 3 |
+
name: string;
|
| 4 |
+
isConnected: boolean;
|
| 5 |
+
robotType?: "so100_follower" | "so100_leader";
|
| 6 |
+
robotId?: string;
|
| 7 |
+
serialNumber?: string; // Unique identifier from USB device
|
| 8 |
+
usbMetadata?: {
|
| 9 |
+
vendorId: string;
|
| 10 |
+
productId: string;
|
| 11 |
+
serialNumber: string;
|
| 12 |
+
manufacturerName: string;
|
| 13 |
+
productName: string;
|
| 14 |
+
usbVersionMajor?: number;
|
| 15 |
+
usbVersionMinor?: number;
|
| 16 |
+
deviceClass?: number;
|
| 17 |
+
deviceSubclass?: number;
|
| 18 |
+
deviceProtocol?: number;
|
| 19 |
+
};
|
| 20 |
+
}
|
src/vite-env.d.ts
CHANGED
|
@@ -1 +1,33 @@
|
|
| 1 |
/// <reference types="vite/client" />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
/// <reference types="vite/client" />
|
| 2 |
+
|
| 3 |
+
// WebUSB API type declarations
|
| 4 |
+
interface USBDevice {
|
| 5 |
+
vendorId: number;
|
| 6 |
+
productId: number;
|
| 7 |
+
serialNumber?: string;
|
| 8 |
+
manufacturerName?: string;
|
| 9 |
+
productName?: string;
|
| 10 |
+
usbVersionMajor: number;
|
| 11 |
+
usbVersionMinor: number;
|
| 12 |
+
deviceClass: number;
|
| 13 |
+
deviceSubclass: number;
|
| 14 |
+
deviceProtocol: number;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
interface USBDeviceFilter {
|
| 18 |
+
vendorId?: number;
|
| 19 |
+
productId?: number;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
interface USBDeviceRequestOptions {
|
| 23 |
+
filters: USBDeviceFilter[];
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
interface USB {
|
| 27 |
+
getDevices(): Promise<USBDevice[]>;
|
| 28 |
+
requestDevice(options: USBDeviceRequestOptions): Promise<USBDevice>;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
interface Navigator {
|
| 32 |
+
usb: USB;
|
| 33 |
+
}
|
tailwind.config.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/** @type {import('tailwindcss').Config} */
|
| 2 |
+
export default {
|
| 3 |
+
content: ["./index.html", "./src/demo/**/*.{js,ts,jsx,tsx}"],
|
| 4 |
+
theme: {
|
| 5 |
+
extend: {
|
| 6 |
+
borderRadius: {
|
| 7 |
+
lg: "var(--radius)",
|
| 8 |
+
md: "calc(var(--radius) - 2px)",
|
| 9 |
+
sm: "calc(var(--radius) - 4px)",
|
| 10 |
+
},
|
| 11 |
+
colors: {
|
| 12 |
+
background: "hsl(var(--background))",
|
| 13 |
+
foreground: "hsl(var(--foreground))",
|
| 14 |
+
card: {
|
| 15 |
+
DEFAULT: "hsl(var(--card))",
|
| 16 |
+
foreground: "hsl(var(--foreground))",
|
| 17 |
+
},
|
| 18 |
+
popover: {
|
| 19 |
+
DEFAULT: "hsl(var(--popover))",
|
| 20 |
+
foreground: "hsl(var(--popover-foreground))",
|
| 21 |
+
},
|
| 22 |
+
primary: {
|
| 23 |
+
DEFAULT: "hsl(var(--primary))",
|
| 24 |
+
foreground: "hsl(var(--primary-foreground))",
|
| 25 |
+
},
|
| 26 |
+
secondary: {
|
| 27 |
+
DEFAULT: "hsl(var(--secondary))",
|
| 28 |
+
foreground: "hsl(var(--secondary-foreground))",
|
| 29 |
+
},
|
| 30 |
+
muted: {
|
| 31 |
+
DEFAULT: "hsl(var(--muted))",
|
| 32 |
+
foreground: "hsl(var(--muted-foreground))",
|
| 33 |
+
},
|
| 34 |
+
accent: {
|
| 35 |
+
DEFAULT: "hsl(var(--accent))",
|
| 36 |
+
foreground: "hsl(var(--accent-foreground))",
|
| 37 |
+
},
|
| 38 |
+
destructive: {
|
| 39 |
+
DEFAULT: "hsl(var(--destructive))",
|
| 40 |
+
foreground: "hsl(var(--destructive-foreground))",
|
| 41 |
+
},
|
| 42 |
+
border: "hsl(var(--border))",
|
| 43 |
+
input: "hsl(var(--input))",
|
| 44 |
+
ring: "hsl(var(--ring))",
|
| 45 |
+
chart: {
|
| 46 |
+
1: "hsl(var(--chart-1))",
|
| 47 |
+
2: "hsl(var(--chart-2))",
|
| 48 |
+
3: "hsl(var(--chart-3))",
|
| 49 |
+
4: "hsl(var(--chart-4))",
|
| 50 |
+
5: "hsl(var(--chart-5))",
|
| 51 |
+
},
|
| 52 |
+
},
|
| 53 |
+
},
|
| 54 |
+
},
|
| 55 |
+
plugins: [],
|
| 56 |
+
};
|
vanilla.html
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<title>🤖 lerobot.js - Vanilla Demo</title>
|
| 7 |
+
<meta
|
| 8 |
+
name="description"
|
| 9 |
+
content="State-of-the-art AI for real-world robotics in JavaScript/TypeScript"
|
| 10 |
+
/>
|
| 11 |
+
<style>
|
| 12 |
+
/* Prevent flash of unstyled content */
|
| 13 |
+
body {
|
| 14 |
+
opacity: 0;
|
| 15 |
+
}
|
| 16 |
+
body.loaded {
|
| 17 |
+
opacity: 1;
|
| 18 |
+
transition: opacity 0.3s ease;
|
| 19 |
+
}
|
| 20 |
+
</style>
|
| 21 |
+
</head>
|
| 22 |
+
<body>
|
| 23 |
+
<div id="app"></div>
|
| 24 |
+
<script type="module" src="/src/main.ts"></script>
|
| 25 |
+
<script>
|
| 26 |
+
// Add loaded class when page is ready
|
| 27 |
+
window.addEventListener("load", () => {
|
| 28 |
+
document.body.classList.add("loaded");
|
| 29 |
+
});
|
| 30 |
+
</script>
|
| 31 |
+
</body>
|
| 32 |
+
</html>
|
vite.config.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { defineConfig } from "vite";
|
| 2 |
+
import react from "@vitejs/plugin-react";
|
| 3 |
+
import { resolve } from "path";
|
| 4 |
+
|
| 5 |
+
export default defineConfig(({ mode }) => {
|
| 6 |
+
const baseConfig = {
|
| 7 |
+
plugins: [],
|
| 8 |
+
resolve: {
|
| 9 |
+
alias: {
|
| 10 |
+
"@": resolve(__dirname, "./src"),
|
| 11 |
+
},
|
| 12 |
+
},
|
| 13 |
+
};
|
| 14 |
+
|
| 15 |
+
if (mode === "demo") {
|
| 16 |
+
// React demo mode - includes React, Tailwind, shadcn/ui
|
| 17 |
+
return {
|
| 18 |
+
...baseConfig,
|
| 19 |
+
plugins: [react()],
|
| 20 |
+
css: {
|
| 21 |
+
postcss: "./postcss.config.mjs",
|
| 22 |
+
},
|
| 23 |
+
build: {
|
| 24 |
+
outDir: "dist/demo",
|
| 25 |
+
rollupOptions: {
|
| 26 |
+
input: {
|
| 27 |
+
main: resolve(__dirname, "index.html"),
|
| 28 |
+
},
|
| 29 |
+
},
|
| 30 |
+
},
|
| 31 |
+
};
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
if (mode === "vanilla") {
|
| 35 |
+
// Vanilla mode - current implementation without React
|
| 36 |
+
return {
|
| 37 |
+
...baseConfig,
|
| 38 |
+
build: {
|
| 39 |
+
outDir: "dist/vanilla",
|
| 40 |
+
rollupOptions: {
|
| 41 |
+
input: {
|
| 42 |
+
main: resolve(__dirname, "vanilla.html"),
|
| 43 |
+
},
|
| 44 |
+
},
|
| 45 |
+
},
|
| 46 |
+
};
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
if (mode === "lib") {
|
| 50 |
+
// Library mode - core library without any demo UI
|
| 51 |
+
return {
|
| 52 |
+
...baseConfig,
|
| 53 |
+
build: {
|
| 54 |
+
lib: {
|
| 55 |
+
entry: resolve(__dirname, "src/main.ts"),
|
| 56 |
+
name: "LeRobot",
|
| 57 |
+
fileName: "lerobot",
|
| 58 |
+
},
|
| 59 |
+
rollupOptions: {
|
| 60 |
+
external: ["serialport", "react", "react-dom"],
|
| 61 |
+
output: {
|
| 62 |
+
globals: {
|
| 63 |
+
serialport: "SerialPort",
|
| 64 |
+
react: "React",
|
| 65 |
+
"react-dom": "ReactDOM",
|
| 66 |
+
},
|
| 67 |
+
},
|
| 68 |
+
},
|
| 69 |
+
},
|
| 70 |
+
};
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
// Default mode (fallback to demo)
|
| 74 |
+
return defineConfig({ mode: "demo" });
|
| 75 |
+
});
|