blanchon commited on
Commit
3165745
·
1 Parent(s): 6ce4ca6
Files changed (30) hide show
  1. .dockerignore +45 -20
  2. .gitmodules +2 -0
  3. Dockerfile +50 -29
  4. bun.lock +0 -0
  5. docker-build.sh +30 -0
  6. package.json +2 -2
  7. packages/feetech.js/package.json +3 -3
  8. src/lib/components/3d/elements/compute/GPU.svelte +2 -2
  9. src/lib/components/3d/elements/compute/modal/VideoInputConnectionModal.svelte +5 -4
  10. src/lib/components/3d/elements/video/Video.svelte +2 -2
  11. src/lib/components/3d/elements/video/VideoGridItem.svelte +1 -1
  12. src/lib/components/3d/elements/video/Videos.svelte +2 -2
  13. src/lib/components/3d/elements/video/modal/VideoInputConnectionModal.svelte +2 -2
  14. src/lib/components/3d/elements/video/modal/VideoOutputConnectionModal.svelte +2 -2
  15. src/lib/components/3d/elements/video/status/InputVideoBoxUIKit.svelte +1 -1
  16. src/lib/components/3d/elements/video/status/OutputVideoBoxUIKit.svelte +1 -1
  17. src/lib/components/3d/elements/video/status/VideoBoxUIKit.svelte +1 -1
  18. src/lib/components/3d/elements/video/status/VideoConnectionFlowBoxUIKit.svelte +1 -1
  19. src/lib/components/3d/elements/video/status/VideoStatusBillboard.svelte +1 -1
  20. src/lib/components/interface/overlay/AddSensorButton.svelte +1 -1
  21. src/lib/elements/compute/RemoteComputeManager.svelte.ts +2 -2
  22. src/lib/elements/robot/RobotManager.svelte.ts +2 -2
  23. src/lib/elements/robot/calibration/CalibrationState.svelte.ts +1 -1
  24. src/lib/elements/robot/drivers/RemoteConsumer.ts +1 -1
  25. src/lib/elements/robot/drivers/RemoteProducer.ts +1 -1
  26. src/lib/elements/video/VideoManager.svelte.ts +2 -2
  27. src/lib/elements/video/videoConnection.svelte.ts +2 -2
  28. src/lib/elements/video/videoStreaming.svelte.ts +2 -2
  29. src/lib/sensors/consumers/RemoteServerConsumer.ts +1 -1
  30. static-server.js +106 -0
.dockerignore CHANGED
@@ -1,56 +1,81 @@
1
- # Dependencies
2
  node_modules/
 
 
 
 
 
3
 
4
- # Build outputs (will be built in container)
5
  build/
6
  .svelte-kit/
7
  dist/
 
8
 
9
  # Development files
10
- .env*
11
- !.env.example
 
 
 
12
 
13
  # IDE files
14
  .vscode/
15
  .idea/
16
  *.swp
17
  *.swo
 
18
 
19
  # OS files
20
  .DS_Store
 
 
 
 
 
21
  Thumbs.db
22
 
23
  # Git
24
  .git/
25
  .gitignore
 
 
 
 
 
 
 
26
 
27
  # Logs
28
  *.log
29
- npm-debug.log*
30
- pnpm-debug.log*
31
- bun-debug.log*
32
- lerna-debug.log*
33
-
34
- # Cache directories
35
- .cache/
36
- .temp/
37
- .tmp/
38
 
39
- # Test files
40
  coverage/
41
  .nyc_output/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
  # Other build artifacts
44
  *.tgz
45
  *.tar.gz
46
 
47
- # Docker files
48
- Dockerfile*
49
- docker-compose*
50
- .dockerignore
51
-
52
  # Documentation that's not needed in container
53
  README.md
54
  CHANGELOG.md
55
- *.md
56
  !LICENSE
 
1
+ # Node.js
2
  node_modules/
3
+ npm-debug.log*
4
+ yarn-debug.log*
5
+ yarn-error.log*
6
+ .npm
7
+ .yarn
8
 
9
+ # Build outputs
10
  build/
11
  .svelte-kit/
12
  dist/
13
+ .output/
14
 
15
  # Development files
16
+ .env
17
+ .env.local
18
+ .env.development.local
19
+ .env.test.local
20
+ .env.production.local
21
 
22
  # IDE files
23
  .vscode/
24
  .idea/
25
  *.swp
26
  *.swo
27
+ *~
28
 
29
  # OS files
30
  .DS_Store
31
+ .DS_Store?
32
+ ._*
33
+ .Spotlight-V100
34
+ .Trashes
35
+ ehthumbs.db
36
  Thumbs.db
37
 
38
  # Git
39
  .git/
40
  .gitignore
41
+ .gitattributes
42
+ .gitmodules
43
+
44
+ # Docker
45
+ Dockerfile*
46
+ docker-compose*
47
+ .dockerignore
48
 
49
  # Logs
50
  *.log
51
+ logs/
52
+ log.txt
 
 
 
 
 
 
 
53
 
54
+ # Testing
55
  coverage/
56
  .nyc_output/
57
+ .pytest_cache/
58
+
59
+ # Temporary files
60
+ *.tmp
61
+ *.temp
62
+ .cache/
63
+ .parcel-cache/
64
+
65
+ # Documentation
66
+ *.md
67
+ docs/
68
+ README*
69
+
70
+ # Misc
71
+ .eslintcache
72
+ .prettierignore
73
 
74
  # Other build artifacts
75
  *.tgz
76
  *.tar.gz
77
 
 
 
 
 
 
78
  # Documentation that's not needed in container
79
  README.md
80
  CHANGELOG.md
 
81
  !LICENSE
.gitmodules CHANGED
@@ -1,6 +1,8 @@
1
  [submodule "external/RobotHub-TransportServer"]
2
  path = external/RobotHub-TransportServer
3
  url = https://github.com/julien-blanchon/RobotHub-TransportServer
 
4
  [submodule "external/RobotHub-InferenceServer"]
5
  path = external/RobotHub-InferenceServer
6
  url = https://github.com/julien-blanchon/RobotHub-InferenceServer
 
 
1
  [submodule "external/RobotHub-TransportServer"]
2
  path = external/RobotHub-TransportServer
3
  url = https://github.com/julien-blanchon/RobotHub-TransportServer
4
+ branch = main
5
  [submodule "external/RobotHub-InferenceServer"]
6
  path = external/RobotHub-InferenceServer
7
  url = https://github.com/julien-blanchon/RobotHub-InferenceServer
8
+ branch = main
Dockerfile CHANGED
@@ -1,48 +1,69 @@
1
- # Multi-stage Dockerfile for LeRobot Arena Frontend
2
- # Stage 1: Build the Svelte application with Bun
3
- FROM oven/bun:1-alpine AS builder
4
 
 
 
 
 
5
  WORKDIR /app
6
 
7
- # Install git for dependencies that might need it
8
- RUN apk add --no-cache git
 
 
 
 
 
 
9
 
10
- # Copy package files for dependency resolution (better caching)
11
  COPY package.json bun.lock* ./
12
 
13
- # Copy local packages that are linked in package.json
14
- COPY packages/ ./packages/
 
15
 
16
- # Install dependencies
17
  RUN bun install --frozen-lockfile
18
 
19
- # Copy source code
 
 
 
 
 
 
 
 
 
 
20
  COPY . .
21
 
22
- # Build the static application
23
  RUN bun run build
24
 
25
- # Stage 2: Serve with Bun's simple static server
26
- FROM oven/bun:1-alpine AS production
27
-
28
- # Set up a new user named "user" with user ID 1000 (required for HF Spaces)
29
- RUN adduser -D -u 1000 user
30
 
31
- # Switch to the "user" user
32
- USER user
 
33
 
34
- # Set home to the user's home directory
35
- ENV HOME=/home/user \
36
- PATH=/home/user/.local/bin:$PATH
37
 
38
- # Set the working directory to the user's home directory
39
- WORKDIR $HOME/app
40
 
41
- # Copy built application from previous stage with proper ownership
42
- COPY --chown=user --from=builder /app/build ./
43
 
44
- # Expose port 7860 (HF Spaces default)
45
- EXPOSE 7860
 
46
 
47
- # Start simple static server using Bun
48
- CMD ["bun", "--bun", "serve", ".", "--port", "7860"]
 
1
+ # Multi-stage build for optimal image size and security
2
+ FROM oven/bun:1.2.17-alpine AS base
 
3
 
4
+ # Install curl for healthcheck
5
+ RUN apk --no-cache add curl
6
+
7
+ # Set working directory
8
  WORKDIR /app
9
 
10
+ # Create non-root user for security
11
+ RUN addgroup -g 1001 -S svelte && \
12
+ adduser -S svelteuser -u 1001
13
+
14
+ # ===============================
15
+ # Dependencies stage
16
+ # ===============================
17
+ FROM base AS deps
18
 
19
+ # Copy package files for dependency installation
20
  COPY package.json bun.lock* ./
21
 
22
+ # Copy local packages and external dependencies
23
+ COPY packages/ packages/
24
+ COPY external/ external/
25
 
26
+ # Install dependencies with frozen lockfile (including devDependencies)
27
  RUN bun install --frozen-lockfile
28
 
29
+ # ===============================
30
+ # Build stage
31
+ # ===============================
32
+ FROM base AS builder
33
+
34
+ # Copy installed dependencies from deps stage
35
+ COPY --from=deps /app/node_modules ./node_modules
36
+ COPY --from=deps /app/packages ./packages
37
+ COPY --from=deps /app/external ./external
38
+
39
+ # Copy source code and configuration files
40
  COPY . .
41
 
42
+ # Build the static site
43
  RUN bun run build
44
 
45
+ # ===============================
46
+ # Production stage
47
+ # ===============================
48
+ FROM base AS runner
 
49
 
50
+ # Set environment to production
51
+ ENV NODE_ENV=production
52
+ ENV PORT=3000
53
 
54
+ # Copy built application and static server
55
+ COPY --from=builder --chown=svelteuser:svelte /app/build ./build
56
+ COPY --chown=svelteuser:svelte static-server.js ./
57
 
58
+ # Switch to non-root user
59
+ USER svelteuser
60
 
61
+ # Expose port
62
+ EXPOSE 3000
63
 
64
+ # Health check
65
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
66
+ CMD curl -f http://localhost:3000/ || exit 1
67
 
68
+ # Start custom static file server
69
+ CMD ["bun", "static-server.js"]
bun.lock DELETED
The diff for this file is too large to render. See raw diff
 
docker-build.sh ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Build and run the LeRobot Arena Frontend Docker container
4
+
5
+ set -e
6
+
7
+ echo "🏗️ Building LeRobot Arena Frontend Docker image..."
8
+
9
+ # Build the image
10
+ docker build -t lerobot-arena-svelte-frontend .
11
+
12
+ echo "✅ Build completed successfully!"
13
+
14
+ echo "🚀 Starting the container..."
15
+
16
+ # Run the container
17
+ docker run -d \
18
+ --name lerobot-arena-svelte-frontend \
19
+ -p 3000:3000 \
20
+ --restart unless-stopped \
21
+ lerobot-arena-svelte-frontend
22
+
23
+ echo "✅ Container started successfully!"
24
+ echo "🌐 Frontend is available at: http://localhost:3000"
25
+ echo ""
26
+ echo "📋 Useful commands:"
27
+ echo " • View logs: docker logs -f lerobot-arena-svelte-frontend"
28
+ echo " • Stop: docker stop lerobot-arena-svelte-frontend"
29
+ echo " • Remove: docker rm lerobot-arena-svelte-frontend"
30
+ echo " • Health check: docker inspect --format='{{.State.Health.Status}}' lerobot-arena-svelte-frontend"
package.json CHANGED
@@ -53,8 +53,8 @@
53
  "@types/three": "0.177.0",
54
  "clsx": "^2.1.1",
55
  "feetech.js": "file:./packages/feetech.js",
56
- "@robohub/transport-server-client": "file:../backend/transport-server/client/js",
57
- "@robohub/inference-server-client": "file:../backend/inference-server/client",
58
  "tailwind-merge": "^3.3.0",
59
  "three": "^0.177.0",
60
  "threlte-uikit": "^1.1.0",
 
53
  "@types/three": "0.177.0",
54
  "clsx": "^2.1.1",
55
  "feetech.js": "file:./packages/feetech.js",
56
+ "@robothub/transport-server-client": "file:./external/RobotHub-TransportServer/client/js",
57
+ "@robothub/inference-server-client": "file:./external/RobotHub-InferenceServer/client",
58
  "tailwind-merge": "^3.3.0",
59
  "three": "^0.177.0",
60
  "threlte-uikit": "^1.1.0",
packages/feetech.js/package.json CHANGED
@@ -16,7 +16,7 @@
16
  },
17
  "repository": {
18
  "type": "git",
19
- "url": "git+https://github.com/julien-blanchon/RoboHub.git#main:frontend/packages/feetech.js"
20
  },
21
  "keywords": [
22
  "feetech",
@@ -32,7 +32,7 @@
32
  "author": "timqian",
33
  "license": "MIT",
34
  "bugs": {
35
- "url": "https://github.com/julien-blanchon/RoboHub.git#main:frontend/packages/feetech.js"
36
  },
37
- "homepage": "https://github.com/julien-blanchon/RoboHub.git#main:frontend/packages/feetech.js"
38
  }
 
16
  },
17
  "repository": {
18
  "type": "git",
19
+ "url": "git+https://github.com/julien-blanchon/robothub.git#main:frontend/packages/feetech.js"
20
  },
21
  "keywords": [
22
  "feetech",
 
32
  "author": "timqian",
33
  "license": "MIT",
34
  "bugs": {
35
+ "url": "https://github.com/julien-blanchon/robothub.git#main:frontend/packages/feetech.js"
36
  },
37
+ "homepage": "https://github.com/julien-blanchon/robothub.git#main:frontend/packages/feetech.js"
38
  }
src/lib/components/3d/elements/compute/GPU.svelte CHANGED
@@ -6,8 +6,8 @@
6
  import Model from "./GPUModel.svelte";
7
  import { Shape, Path, ExtrudeGeometry, BoxGeometry } from "three";
8
  import { onMount } from "svelte";
9
- import type { VideoInstance } from "$lib/elements/video//VideoManager.svelte";
10
- import { videoManager } from "$lib/elements/video//VideoManager.svelte";
11
 
12
  // Props interface
13
  interface Props {
 
6
  import Model from "./GPUModel.svelte";
7
  import { Shape, Path, ExtrudeGeometry, BoxGeometry } from "three";
8
  import { onMount } from "svelte";
9
+ import type { VideoInstance } from "$lib/elements/video/VideoManager.svelte";
10
+ import { videoManager } from "$lib/elements/video/VideoManager.svelte";
11
 
12
  // Props interface
13
  interface Props {
src/lib/components/3d/elements/compute/modal/VideoInputConnectionModal.svelte CHANGED
@@ -1,12 +1,14 @@
1
  <script lang="ts">
2
  import * as Dialog from "@/components/ui/dialog";
 
 
3
  import { Button } from "@/components/ui/button";
4
  import * as Card from "@/components/ui/card";
5
  import { Badge } from "@/components/ui/badge";
6
  import { toast } from "svelte-sonner";
7
  import { settings } from "$lib/runes/settings.svelte";
8
- import { videoManager } from "$lib/elements/video//VideoManager.svelte";
9
- import type { RemoteCompute } from "$lib/elements/compute//RemoteCompute.svelte";
10
 
11
  interface Props {
12
  workspaceId: string;
@@ -50,8 +52,7 @@
50
  }
51
 
52
  // Create video producer and connect to the camera room
53
- const { VideoProducer } = await import("@robohub/transport-server-client/video");
54
- videoProducer = new VideoProducer(settings.transportServerUrl);
55
 
56
  // Connect to the EXISTING camera room (don't create new one)
57
  const participantId = `frontend-camera-${selectedCameraName}-${Date.now()}`;
 
1
  <script lang="ts">
2
  import * as Dialog from "@/components/ui/dialog";
3
+ import { video } from '@robothub/transport-server-client';
4
+ import type { video as videoTypes } from '@robothub/transport-server-client';
5
  import { Button } from "@/components/ui/button";
6
  import * as Card from "@/components/ui/card";
7
  import { Badge } from "@/components/ui/badge";
8
  import { toast } from "svelte-sonner";
9
  import { settings } from "$lib/runes/settings.svelte";
10
+ import { videoManager } from "$lib/elements/video/VideoManager.svelte";
11
+ import type { RemoteCompute } from "$lib/elements/compute/RemoteCompute.svelte";
12
 
13
  interface Props {
14
  workspaceId: string;
 
52
  }
53
 
54
  // Create video producer and connect to the camera room
55
+ videoProducer = new video.VideoProducer(settings.transportServerUrl);
 
56
 
57
  // Connect to the EXISTING camera room (don't create new one)
58
  const participantId = `frontend-camera-${selectedCameraName}-${Date.now()}`;
src/lib/components/3d/elements/video/Video.svelte CHANGED
@@ -3,8 +3,8 @@
3
  import { interactivity } from "@threlte/extras";
4
  import { VideoTexture, CanvasTexture, LinearFilter, RGBAFormat, Shape, Path, ExtrudeGeometry, BoxGeometry } from "three";
5
  import { onMount } from "svelte";
6
- import type { VideoInstance } from "$lib/elements/video//VideoManager.svelte";
7
- import { videoManager } from "$lib/elements/video//VideoManager.svelte";
8
 
9
  // Props interface
10
  interface Props {
 
3
  import { interactivity } from "@threlte/extras";
4
  import { VideoTexture, CanvasTexture, LinearFilter, RGBAFormat, Shape, Path, ExtrudeGeometry, BoxGeometry } from "three";
5
  import { onMount } from "svelte";
6
+ import type { VideoInstance } from "$lib/elements/video/VideoManager.svelte";
7
+ import { videoManager } from "$lib/elements/video/VideoManager.svelte";
8
 
9
  // Props interface
10
  interface Props {
src/lib/components/3d/elements/video/VideoGridItem.svelte CHANGED
@@ -2,7 +2,7 @@
2
  import { T } from "@threlte/core";
3
  import Video from "./Video.svelte";
4
  import VideoStatusBillboard from "./status/VideoStatusBillboard.svelte";
5
- import type { VideoInstance, VideoStatus } from "$lib/elements/video//VideoManager.svelte";
6
  import { interactivity, type IntersectionEvent, useCursor } from "@threlte/extras";
7
 
8
  interface Props {
 
2
  import { T } from "@threlte/core";
3
  import Video from "./Video.svelte";
4
  import VideoStatusBillboard from "./status/VideoStatusBillboard.svelte";
5
+ import type { VideoInstance, VideoStatus } from "$lib/elements/video/VideoManager.svelte";
6
  import { interactivity, type IntersectionEvent, useCursor } from "@threlte/extras";
7
 
8
  interface Props {
src/lib/components/3d/elements/video/Videos.svelte CHANGED
@@ -1,10 +1,10 @@
1
  <script lang="ts">
2
  import { useThrelte } from "@threlte/core";
3
- import { videoManager } from "$lib/elements/video//VideoManager.svelte";
4
  import { onMount } from "svelte";
5
  import VideoInputConnectionModal from "@/components/3d/elements/video/modal/VideoInputConnectionModal.svelte";
6
  import VideoOutputConnectionModal from "@/components/3d/elements/video/modal/VideoOutputConnectionModal.svelte";
7
- import type { VideoInstance } from "$lib/elements/video//VideoManager.svelte";
8
  import { generateName } from "$lib/utils/generateName";
9
  import VideoGridItem from "@/components/3d/elements/video/VideoGridItem.svelte";
10
 
 
1
  <script lang="ts">
2
  import { useThrelte } from "@threlte/core";
3
+ import { videoManager } from "$lib/elements/video/VideoManager.svelte";
4
  import { onMount } from "svelte";
5
  import VideoInputConnectionModal from "@/components/3d/elements/video/modal/VideoInputConnectionModal.svelte";
6
  import VideoOutputConnectionModal from "@/components/3d/elements/video/modal/VideoOutputConnectionModal.svelte";
7
+ import type { VideoInstance } from "$lib/elements/video/VideoManager.svelte";
8
  import { generateName } from "$lib/utils/generateName";
9
  import VideoGridItem from "@/components/3d/elements/video/VideoGridItem.svelte";
10
 
src/lib/components/3d/elements/video/modal/VideoInputConnectionModal.svelte CHANGED
@@ -5,8 +5,8 @@
5
  import * as Alert from "@/components/ui/alert";
6
  import { Badge } from "@/components/ui/badge";
7
  import { toast } from "svelte-sonner";
8
- import { videoManager } from "$lib/elements/video//VideoManager.svelte";
9
- import type { VideoInstance } from "$lib/elements/video//VideoManager.svelte";
10
 
11
  interface Props {
12
  workspaceId: string;
 
5
  import * as Alert from "@/components/ui/alert";
6
  import { Badge } from "@/components/ui/badge";
7
  import { toast } from "svelte-sonner";
8
+ import { videoManager } from "$lib/elements/video/VideoManager.svelte";
9
+ import type { VideoInstance } from "$lib/elements/video/VideoManager.svelte";
10
 
11
  interface Props {
12
  workspaceId: string;
src/lib/components/3d/elements/video/modal/VideoOutputConnectionModal.svelte CHANGED
@@ -5,8 +5,8 @@
5
  import * as Alert from "@/components/ui/alert";
6
  import { Badge } from "@/components/ui/badge";
7
  import { toast } from "svelte-sonner";
8
- import { videoManager } from "$lib/elements/video//VideoManager.svelte";
9
- import type { VideoInstance } from "$lib/elements/video//VideoManager.svelte";
10
 
11
  interface Props {
12
  workspaceId: string;
 
5
  import * as Alert from "@/components/ui/alert";
6
  import { Badge } from "@/components/ui/badge";
7
  import { toast } from "svelte-sonner";
8
+ import { videoManager } from "$lib/elements/video/VideoManager.svelte";
9
+ import type { VideoInstance } from "$lib/elements/video/VideoManager.svelte";
10
 
11
  interface Props {
12
  workspaceId: string;
src/lib/components/3d/elements/video/status/InputVideoBoxUIKit.svelte CHANGED
@@ -1,6 +1,6 @@
1
  <script lang="ts">
2
  import { ICON } from "$lib/utils/icon";
3
- import type { VideoInstance } from "$lib/elements/video//VideoManager.svelte";
4
  import { BaseStatusBox, StatusHeader, StatusContent, StatusIndicator, StatusButton } from "$lib/components/3d/ui";
5
 
6
  interface Props {
 
1
  <script lang="ts">
2
  import { ICON } from "$lib/utils/icon";
3
+ import type { VideoInstance } from "$lib/elements/video/VideoManager.svelte";
4
  import { BaseStatusBox, StatusHeader, StatusContent, StatusIndicator, StatusButton } from "$lib/components/3d/ui";
5
 
6
  interface Props {
src/lib/components/3d/elements/video/status/OutputVideoBoxUIKit.svelte CHANGED
@@ -1,6 +1,6 @@
1
  <script lang="ts">
2
  import { ICON } from "$lib/utils/icon";
3
- import type { VideoInstance } from "$lib/elements/video//VideoManager.svelte";
4
  import { BaseStatusBox, StatusHeader, StatusContent, StatusIndicator, StatusButton } from "$lib/components/3d/ui";
5
 
6
  interface Props {
 
1
  <script lang="ts">
2
  import { ICON } from "$lib/utils/icon";
3
+ import type { VideoInstance } from "$lib/elements/video/VideoManager.svelte";
4
  import { BaseStatusBox, StatusHeader, StatusContent, StatusIndicator, StatusButton } from "$lib/components/3d/ui";
5
 
6
  interface Props {
src/lib/components/3d/elements/video/status/VideoBoxUIKit.svelte CHANGED
@@ -1,6 +1,6 @@
1
  <script lang="ts">
2
  import { ICON } from "$lib/utils/icon";
3
- import type { VideoInstance } from "$lib/elements/video//VideoManager.svelte";
4
  import {
5
  BaseStatusBox,
6
  StatusHeader,
 
1
  <script lang="ts">
2
  import { ICON } from "$lib/utils/icon";
3
+ import type { VideoInstance } from "$lib/elements/video/VideoManager.svelte";
4
  import {
5
  BaseStatusBox,
6
  StatusHeader,
src/lib/components/3d/elements/video/status/VideoConnectionFlowBoxUIKit.svelte CHANGED
@@ -2,7 +2,7 @@
2
  import InputVideoBoxUIKit from "./InputVideoBoxUIKit.svelte";
3
  import OutputVideoBoxUIKit from "./OutputVideoBoxUIKit.svelte";
4
  import VideoBoxUIKit from "./VideoBoxUIKit.svelte";
5
- import type { VideoInstance } from "$lib/elements/video//VideoManager.svelte";
6
  import { Container } from "threlte-uikit";
7
  import { StatusArrow } from "$lib/components/3d/ui";
8
 
 
2
  import InputVideoBoxUIKit from "./InputVideoBoxUIKit.svelte";
3
  import OutputVideoBoxUIKit from "./OutputVideoBoxUIKit.svelte";
4
  import VideoBoxUIKit from "./VideoBoxUIKit.svelte";
5
+ import type { VideoInstance } from "$lib/elements/video/VideoManager.svelte";
6
  import { Container } from "threlte-uikit";
7
  import { StatusArrow } from "$lib/components/3d/ui";
8
 
src/lib/components/3d/elements/video/status/VideoStatusBillboard.svelte CHANGED
@@ -3,7 +3,7 @@
3
  import { Billboard, interactivity } from "@threlte/extras";
4
  import { Root, Container } from "threlte-uikit";
5
  import VideoConnectionFlowBoxUIKit from "./VideoConnectionFlowBoxUIKit.svelte";
6
- import type { VideoInstance } from "$lib/elements/video//VideoManager.svelte";
7
 
8
  interface Props {
9
  video: VideoInstance;
 
3
  import { Billboard, interactivity } from "@threlte/extras";
4
  import { Root, Container } from "threlte-uikit";
5
  import VideoConnectionFlowBoxUIKit from "./VideoConnectionFlowBoxUIKit.svelte";
6
+ import type { VideoInstance } from "$lib/elements/video/VideoManager.svelte";
7
 
8
  interface Props {
9
  video: VideoInstance;
src/lib/components/interface/overlay/AddSensorButton.svelte CHANGED
@@ -5,7 +5,7 @@
5
  import { toast } from "svelte-sonner";
6
  import { cn } from "$lib/utils";
7
  import { generateName } from "@/utils/generateName";
8
- import { videoManager } from "$lib/elements/video//VideoManager.svelte";
9
 
10
  interface Props {
11
  open: boolean;
 
5
  import { toast } from "svelte-sonner";
6
  import { cn } from "$lib/utils";
7
  import { generateName } from "@/utils/generateName";
8
+ import { videoManager } from "$lib/elements/video/VideoManager.svelte";
9
 
10
  interface Props {
11
  open: boolean;
src/lib/elements/compute/RemoteComputeManager.svelte.ts CHANGED
@@ -2,13 +2,13 @@ import { RemoteCompute } from './RemoteCompute.svelte';
2
  import type { Position3D } from '$lib/types/positionable.js';
3
  import { generateName } from '$lib/utils/generateName.js';
4
  import { positionManager } from '$lib/utils/positionManager.js';
5
- import { type LeRobotAIServerClient, createClient } from '@robohub/inference-server-client';
6
  import { settings } from '$lib/runes/settings.svelte';
7
  import type {
8
  CreateSessionRequest,
9
  CreateSessionResponse,
10
  SessionStatusResponse
11
- } from '@robohub/inference-server-client';
12
 
13
  export interface AISessionConfig {
14
  sessionId: string;
 
2
  import type { Position3D } from '$lib/types/positionable.js';
3
  import { generateName } from '$lib/utils/generateName.js';
4
  import { positionManager } from '$lib/utils/positionManager.js';
5
+ import { type LeRobotAIServerClient, createClient } from '@robothub/inference-server-client';
6
  import { settings } from '$lib/runes/settings.svelte';
7
  import type {
8
  CreateSessionRequest,
9
  CreateSessionResponse,
10
  SessionStatusResponse
11
+ } from '@robothub/inference-server-client';
12
 
13
  export interface AISessionConfig {
14
  sessionId: string;
src/lib/elements/robot/RobotManager.svelte.ts CHANGED
@@ -6,8 +6,8 @@ import type { RobotUrdfConfig } from '$lib/types/urdf.js';
6
  import { generateName } from '$lib/utils/generateName.js';
7
  import { positionManager } from '$lib/utils/positionManager.js';
8
  import { settings } from '$lib/runes/settings.svelte';
9
- import { robotics } from '@robohub/transport-server-client';
10
- import type { robotics as roboticsTypes } from '@robohub/transport-server-client';
11
 
12
  export class RobotManager {
13
  private _robots = $state<Robot[]>([]);
 
6
  import { generateName } from '$lib/utils/generateName.js';
7
  import { positionManager } from '$lib/utils/positionManager.js';
8
  import { settings } from '$lib/runes/settings.svelte';
9
+ import { robotics } from '@robothub/transport-server-client';
10
+ import type { robotics as roboticsTypes } from '@robothub/transport-server-client';
11
 
12
  export class RobotManager {
13
  private _robots = $state<Robot[]>([]);
src/lib/elements/robot/calibration/CalibrationState.svelte.ts CHANGED
@@ -54,7 +54,7 @@ export class CalibrationState {
54
  return Math.abs(calibration.maxServoValue - calibration.minServoValue);
55
  }
56
 
57
- private updateInterval: number | null = null;
58
  private managerUnsubscribe: (() => void) | null = null;
59
 
60
  private setupManagerSubscription(): void {
 
54
  return Math.abs(calibration.maxServoValue - calibration.minServoValue);
55
  }
56
 
57
+ private updateInterval: Timer | null = null;
58
  private managerUnsubscribe: (() => void) | null = null;
59
 
60
  private setupManagerSubscription(): void {
src/lib/elements/robot/drivers/RemoteConsumer.ts CHANGED
@@ -1,5 +1,5 @@
1
  import type { Consumer, ConnectionStatus, RobotCommand, RemoteDriverConfig } from '../models.js';
2
- import { robotics } from "@robohub/transport-server-client";
3
 
4
  export class RemoteConsumer implements Consumer {
5
  readonly id: string;
 
1
  import type { Consumer, ConnectionStatus, RobotCommand, RemoteDriverConfig } from '../models.js';
2
+ import { robotics } from "@robothub/transport-server-client";
3
 
4
  export class RemoteConsumer implements Consumer {
5
  readonly id: string;
src/lib/elements/robot/drivers/RemoteProducer.ts CHANGED
@@ -1,5 +1,5 @@
1
  import type { Producer, ConnectionStatus, RobotCommand, RemoteDriverConfig } from '../models.js';
2
- import { robotics } from "@robohub/transport-server-client";
3
 
4
  export class RemoteProducer implements Producer {
5
  readonly id: string;
 
1
  import type { Producer, ConnectionStatus, RobotCommand, RemoteDriverConfig } from '../models.js';
2
+ import { robotics } from "@robothub/transport-server-client";
3
 
4
  export class RemoteProducer implements Producer {
5
  readonly id: string;
src/lib/elements/video/VideoManager.svelte.ts CHANGED
@@ -4,8 +4,8 @@
4
  * Manages multiple video instances, each with their own streaming state
5
  */
6
 
7
- import { video as videoClient } from '@robohub/transport-server-client';
8
- import type { video as videoTypes } from '@robohub/transport-server-client';
9
  import { generateName } from "$lib/utils/generateName";
10
  import type { Positionable, Position3D } from '$lib/types/positionable';
11
  import { positionManager } from '$lib/utils/positionManager';
 
4
  * Manages multiple video instances, each with their own streaming state
5
  */
6
 
7
+ import { video as videoClient } from '@robothub/transport-server-client';
8
+ import type { video as videoTypes } from '@robothub/transport-server-client';
9
  import { generateName } from "$lib/utils/generateName";
10
  import type { Positionable, Position3D } from '$lib/types/positionable';
11
  import { positionManager } from '$lib/utils/positionManager';
src/lib/elements/video/videoConnection.svelte.ts CHANGED
@@ -3,8 +3,8 @@
3
  * Clean and simple video producer/consumer management
4
  */
5
 
6
- import { video } from '@robohub/transport-server-client';
7
- import type { video as videoTypes } from '@robohub/transport-server-client';
8
  import { settings } from '$lib/runes/settings.svelte';
9
 
10
  // Simple connection state using runes
 
3
  * Clean and simple video producer/consumer management
4
  */
5
 
6
+ import { video } from '@robothub/transport-server-client';
7
+ import type { video as videoTypes } from '@robothub/transport-server-client';
8
  import { settings } from '$lib/runes/settings.svelte';
9
 
10
  // Simple connection state using runes
src/lib/elements/video/videoStreaming.svelte.ts CHANGED
@@ -3,8 +3,8 @@
3
  * Clean separation between input sources and output destinations
4
  */
5
 
6
- import { video } from '@robohub/transport-server-client';
7
- import type { video as videoTypes } from '@robohub/transport-server-client';
8
  import { settings } from '$lib/runes/settings.svelte';
9
 
10
  // Input/Output state using runes
 
3
  * Clean separation between input sources and output destinations
4
  */
5
 
6
+ import { video } from '@robothub/transport-server-client';
7
+ import type { video as videoTypes } from '@robothub/transport-server-client';
8
  import { settings } from '$lib/runes/settings.svelte';
9
 
10
  // Input/Output state using runes
src/lib/sensors/consumers/RemoteServerConsumer.ts CHANGED
@@ -27,7 +27,7 @@ export class RemoteServerConsumer implements ConsumerSensorDriver {
27
  // Connection management
28
  private websocket: WebSocket | null = null;
29
  private reconnectAttempts = 0;
30
- private reconnectTimer?: number;
31
 
32
  // Stream management
33
  private activeOutputStreams = new Map<string, SensorStream>();
 
27
  // Connection management
28
  private websocket: WebSocket | null = null;
29
  private reconnectAttempts = 0;
30
+ private reconnectTimer?: Timer;
31
 
32
  // Stream management
33
  private activeOutputStreams = new Map<string, SensorStream>();
static-server.js ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bun
2
+
3
+ // Simple static file server for Svelte build output
4
+ import { resolve, join } from "path";
5
+
6
+ const PORT = process.env.PORT || 3000;
7
+ const BUILD_DIR = "./build";
8
+
9
+ // MIME types for common web assets
10
+ const MIME_TYPES = {
11
+ '.html': 'text/html',
12
+ '.css': 'text/css',
13
+ '.js': 'application/javascript',
14
+ '.json': 'application/json',
15
+ '.png': 'image/png',
16
+ '.jpg': 'image/jpeg',
17
+ '.jpeg': 'image/jpeg',
18
+ '.gif': 'image/gif',
19
+ '.svg': 'image/svg+xml',
20
+ '.ico': 'image/x-icon',
21
+ '.woff': 'font/woff',
22
+ '.woff2': 'font/woff2',
23
+ '.ttf': 'font/ttf',
24
+ '.eot': 'application/vnd.ms-fontobject',
25
+ '.webp': 'image/webp',
26
+ '.avif': 'image/avif',
27
+ '.mp4': 'video/mp4',
28
+ '.webm': 'video/webm'
29
+ };
30
+
31
+ function getMimeType(filename) {
32
+ const ext = filename.substring(filename.lastIndexOf('.'));
33
+ return MIME_TYPES[ext] || 'application/octet-stream';
34
+ }
35
+
36
+ const server = Bun.serve({
37
+ port: PORT,
38
+ hostname: "0.0.0.0",
39
+
40
+ async fetch(req) {
41
+ const url = new URL(req.url);
42
+ let pathname = url.pathname;
43
+
44
+ // Remove leading slash and default to index.html for root
45
+ if (pathname === '/') {
46
+ pathname = 'index.html';
47
+ } else {
48
+ pathname = pathname.substring(1); // Remove leading slash
49
+ }
50
+
51
+ try {
52
+ // Try to serve the requested file
53
+ const filePath = join(BUILD_DIR, pathname);
54
+ const file = Bun.file(filePath);
55
+
56
+ if (await file.exists()) {
57
+ const mimeType = getMimeType(pathname);
58
+ const headers = {
59
+ 'Content-Type': mimeType,
60
+ };
61
+
62
+ // Set cache headers
63
+ if (pathname.includes('/_app/immutable/')) {
64
+ // Long-term cache for immutable assets
65
+ headers['Cache-Control'] = 'public, max-age=31536000, immutable';
66
+ } else if (pathname.endsWith('.html')) {
67
+ // No cache for HTML files
68
+ headers['Cache-Control'] = 'public, max-age=0, must-revalidate';
69
+ } else {
70
+ // Short cache for other assets
71
+ headers['Cache-Control'] = 'public, max-age=3600';
72
+ }
73
+
74
+ return new Response(file, { headers });
75
+ }
76
+
77
+ // If file not found and no extension, serve index.html for SPA routing
78
+ if (!pathname.includes('.')) {
79
+ const indexFile = Bun.file(join(BUILD_DIR, 'index.html'));
80
+ if (await indexFile.exists()) {
81
+ return new Response(indexFile, {
82
+ headers: {
83
+ 'Content-Type': 'text/html',
84
+ 'Cache-Control': 'public, max-age=0, must-revalidate'
85
+ }
86
+ });
87
+ }
88
+ }
89
+
90
+ return new Response('Not Found', {
91
+ status: 404,
92
+ headers: { 'Content-Type': 'text/plain' }
93
+ });
94
+
95
+ } catch (error) {
96
+ console.error('Server error:', error);
97
+ return new Response('Internal Server Error', {
98
+ status: 500,
99
+ headers: { 'Content-Type': 'text/plain' }
100
+ });
101
+ }
102
+ }
103
+ });
104
+
105
+ console.log(`🚀 Static server running on http://localhost:${server.port}`);
106
+ console.log(`📁 Serving files from: ${BUILD_DIR}`);