Skip to content

Commit 3f66cc3

Browse files
committed
liniting and build fixes
1 parent f4a7a1b commit 3f66cc3

16 files changed

Lines changed: 493 additions & 113 deletions

File tree

package-lock.json

Lines changed: 361 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"lucide-react": "^0.561.0",
2929
"nitro": "^3.0.260429-beta",
3030
"nitro-nightly": "^3.0.1-20260501-164602-aee73f19",
31+
"node-datachannel": "^0.32.3",
3132
"qrcode": "^1.5.4",
3233
"react": "^19.2.5",
3334
"react-dom": "^19.2.5",
@@ -37,6 +38,7 @@
3738
},
3839
"devDependencies": {
3940
"@biomejs/biome": "^2.4.14",
41+
"@rolldown/plugin-babel": "^0.2.3",
4042
"@tailwindcss/postcss": "^4.1.18",
4143
"@tailwindcss/typography": "^0.5.19",
4244
"@tanstack/devtools-vite": "^0.6.0",
@@ -51,6 +53,7 @@
5153
"@types/ws": "^8.5.14",
5254
"@vitejs/plugin-react": "^5.2.0",
5355
"autoprefixer": "^10.4.23",
56+
"babel-plugin-react-compiler": "^1.0.0",
5457
"concurrently": "^9.2.1",
5558
"daisyui": "^5.5.14",
5659
"electron": "^40.6.1",

src/components/Trackpad/ScreenMirror.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ interface ScreenMirrorProps {
1313

1414
const TEXTS = {
1515
WAITING: "Connecting to host desktop...",
16-
AUTOMATIC: "Establishing secure low-latency WebRTC connection",
16+
AUTOMATIC: "Establishing secure connection",
1717
}
1818

1919
export const ScreenMirror = ({
@@ -24,11 +24,15 @@ export const ScreenMirror = ({
2424
trackActive,
2525
}: ScreenMirrorProps) => {
2626
const videoElementRef = useRef<HTMLVideoElement | null>(null)
27-
2827
useEffect(() => {
29-
if (videoElementRef.current && videoStream) {
30-
videoElementRef.current.srcObject = videoStream
31-
videoElementRef.current.play().catch(() => {})
28+
const video = videoElementRef.current
29+
if (!video) return
30+
video.srcObject = videoStream
31+
if (videoStream) {
32+
video.play().catch(() => {})
33+
}
34+
return () => {
35+
video.srcObject = null
3236
}
3337
}, [videoStream])
3438

@@ -37,6 +41,7 @@ export const ScreenMirror = ({
3741
{/* Hardware Accelerated Video Renderer */}
3842
<video
3943
ref={videoElementRef}
44+
aria-label="Remote desktop screen share"
4045
autoPlay
4146
playsInline
4247
muted

src/contexts/ConnectionProvider.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ export function ConnectionProvider({
4747
: null
4848

4949
// High frequency mouse/touch inputs go to unordered; keyboard/clicks and others go to ordered.
50-
const isUnordered = type === "move" || type === "scroll" || type === "touch"
50+
const isUnordered =
51+
type === "move" ||
52+
type === "scroll" ||
53+
type === "touch" ||
54+
type === "zoom"
5155
const targetDc = isUnordered ? unorderedDcRef.current : orderedDcRef.current
5256

5357
if (targetDc?.readyState === "open") {

src/hooks/useRemoteConnection.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
"use client"
12
import { useConnection } from "../contexts/ConnectionProvider"
23

34
export const useRemoteConnection = () => {

src/hooks/useWebRtcStream.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -106,24 +106,10 @@ export function useWebRtcStream({ token }: UseWebRtcStreamOptions) {
106106
}).catch(console.error)
107107
}
108108

109-
// Send input PC offer to server immediately
110-
const sendInputOffer = async () => {
111-
const offer = await inputPc.createOffer()
112-
await inputPc.setLocalDescription(offer)
113-
await fetch("/api/webrtc/input-offer", {
114-
method: "POST",
115-
headers: {
116-
"Content-Type": "application/json",
117-
...(token ? { Authorization: `Bearer ${token}` } : {}),
118-
},
119-
body: JSON.stringify({ sessionId: activeSessionId, sdp: offer.sdp }),
120-
})
121-
}
122-
123-
sendInputOffer().catch(console.error)
124-
125109
// ── SSE bridge: handles both video offer and input-answer ────────────
126-
const sseUrl = `/api/webrtc/events?sessionId=${activeSessionId}${token ? `&token=${token}` : ""}`
110+
const sseParams = new URLSearchParams({ sessionId: activeSessionId })
111+
if (token) sseParams.set("token", token)
112+
const sseUrl = `/api/webrtc/events?${sseParams.toString()}`
127113
const sse = new EventSource(sseUrl)
128114
sseSourceRef.current = sse
129115

@@ -226,6 +212,20 @@ export function useWebRtcStream({ token }: UseWebRtcStreamOptions) {
226212
inputIceQueue.push(candidateInit)
227213
}
228214
})
215+
const sendInputOffer = async () => {
216+
const offer = await inputPc.createOffer()
217+
await inputPc.setLocalDescription(offer)
218+
await fetch("/api/webrtc/input-offer", {
219+
method: "POST",
220+
headers: {
221+
"Content-Type": "application/json",
222+
...(token ? { Authorization: `Bearer ${token}` } : {}),
223+
},
224+
body: JSON.stringify({ sessionId: activeSessionId, sdp: offer.sdp }),
225+
})
226+
}
227+
228+
sendInputOffer().catch(console.error)
229229

230230
return () => {
231231
sse.close()

src/routes/__root.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
Scripts,
66
createRootRoute,
77
} from "@tanstack/react-router"
8-
import { useEffect, useRef } from "react"
8+
import { useEffect } from "react"
99
import { APP_CONFIG, THEMES } from "../config"
1010
import "../styles.css"
1111
import {

src/server/api/apiHandlers.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,10 @@ export async function readBody(req: IncomingMessage): Promise<string> {
8989
req.setEncoding("utf-8")
9090
req.on("data", (chunk: string) => {
9191
raw += chunk
92-
if (raw.length > 64 * 1024) reject(new Error("Request body too large"))
92+
if (raw.length > 64 * 1024) {
93+
req.destroy()
94+
reject(new Error("Request body too large"))
95+
}
9396
})
9497
req.on("end", () => resolve(raw))
9598
req.on("error", reject)
@@ -706,4 +709,5 @@ export async function handleWhipSignalingExchange(
706709
res.end(JSON.stringify({ error: "WHIP signaling handshake timeout" }))
707710
}
708711
}, 100)
712+
req.on("close", () => clearInterval(answerCheckInterval))
709713
}

src/server/api/apiState.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import type { ServerResponse } from "node:http"
66
import { getActiveToken, generateToken, storeToken } from "../tokenStore"
77
import { HostRunner } from "../gstreamer/hostRunner"
8-
import type { InputPeerConnection } from "../InputPeerConnection"
8+
import type { InputPeerConnection } from "./InputPeerConnection"
99
import logger from "../../utils/logger"
1010

1111
export type SessionState =

src/server/drivers/linux/index.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,22 @@ class UinputDevice {
7272
}
7373

7474
open(): boolean {
75-
const fd = openUinput(UINPUT_PATH)
76-
if (fd < 0) {
77-
console.error(`[${this.name}] Failed to open ${UINPUT_PATH} (fd=${fd})`)
75+
try {
76+
const fd = openUinput(UINPUT_PATH)
77+
this.fd = fd
78+
return true
79+
} catch (err) {
80+
console.error(`[${this.name}] Failed to open ${UINPUT_PATH}:`, err)
7881
console.error(
7982
`[${this.name}] Ensure /dev/uinput exists and the process has write permission.`,
8083
)
8184
console.error(
82-
`[${this.name}] Run: sudo chmod 0660 /dev/uinput or add udev rule.`,
85+
`[${this.name}] Run: sudo chown root:input /dev/uinput && sudo chmod 0660 /dev/uinput or add udev rule.`,
86+
)
87+
throw new Error(
88+
`Failed to open /dev/uinput. Ensure your user has write permissions (run: sudo chown root:input /dev/uinput && sudo chmod 0660 /dev/uinput)`,
8389
)
84-
return false
8590
}
86-
this.fd = fd
87-
return true
8891
}
8992

9093
create(deviceName: string): boolean {

0 commit comments

Comments
 (0)