Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 3 additions & 48 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 40 additions & 2 deletions src/hooks/useRemoteConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { useState, useEffect, useCallback } from 'react';
export const useRemoteConnection = () => {
const [ws, setWs] = useState<WebSocket | null>(null);
const [status, setStatus] = useState<'connecting' | 'connected' | 'disconnected'>('disconnected');
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [authRequired, setAuthRequired] = useState(false);
const [authError, setAuthError] = useState(false);

useEffect(() => {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
Expand All @@ -16,9 +19,38 @@ export const useRemoteConnection = () => {
setStatus('connecting');
const socket = new WebSocket(wsUrl);

socket.onopen = () => setStatus('connected');
socket.onopen = () => {
setStatus('connected');
// Auth state is determined by the 'connected' message
};

socket.onmessage = (event) => {
try {
const msg = JSON.parse(event.data);
if (msg.type === 'connected') {
if (msg.authRequired) {
setAuthRequired(true);
setIsAuthenticated(false);
} else {
setIsAuthenticated(true);
setAuthRequired(false);
}
} else if (msg.type === 'auth-success') {
setIsAuthenticated(true);
setAuthError(false);
} else if (msg.type === 'auth-failed') {
setAuthError(true);
setIsAuthenticated(false);
}
} catch (e) {
console.error('WS Message Error', e);
}
};

socket.onclose = () => {
setStatus('disconnected');
setIsAuthenticated(false);
setAuthRequired(false);
reconnectTimer = setTimeout(connect, 3000);
};
Comment on lines 50 to 55
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

authError is not reset on reconnection.

When the WebSocket closes and reconnects, isAuthenticated and authRequired are cleared but authError remains true. After reconnection, the PIN entry screen will immediately show the "Incorrect PIN" error from the previous session.

Proposed fix
 socket.onclose = () => {
     setStatus('disconnected');
     setIsAuthenticated(false);
     setAuthRequired(false);
+    setAuthError(false);
     reconnectTimer = setTimeout(connect, 3000);
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
socket.onclose = () => {
setStatus('disconnected');
setIsAuthenticated(false);
setAuthRequired(false);
reconnectTimer = setTimeout(connect, 3000);
};
socket.onclose = () => {
setStatus('disconnected');
setIsAuthenticated(false);
setAuthRequired(false);
setAuthError(false);
reconnectTimer = setTimeout(connect, 3000);
};
🤖 Prompt for AI Agents
In `@src/hooks/useRemoteConnection.ts` around lines 50 - 55, The socket.onclose
handler currently clears status, isAuthenticated and authRequired but leaves
authError true; update the onclose block in useRemoteConnection (socket.onclose)
to also reset authError by calling setAuthError(false) so the PIN screen doesn’t
show a stale "Incorrect PIN" after reconnect; optionally also ensure
socket.onopen/handleConnect clears authError as well to be safe during
successful reconnects.

socket.onerror = (e) => {
Expand All @@ -42,5 +74,11 @@ export const useRemoteConnection = () => {
}
}, [ws]);

return { status, send };
const authenticate = useCallback((pin: string) => {
if (ws?.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'authenticate', pin }));
}
}, [ws]);

return { status, send, isAuthenticated, authRequired, authenticate, authError };
};
28 changes: 27 additions & 1 deletion src/routes/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ function SettingsPage() {
const [invertScroll, setInvertScroll] = useState(CONFIG.MOUSE_INVERT);
const [sensitivity, setSensitivity] = useState(CONFIG.MOUSE_SENSITIVITY);
const [qrData, setQrData] = useState('');
const [pin, setPin] = useState<string | null>(null);

// Load initial state
useEffect(() => {
Expand Down Expand Up @@ -53,12 +54,24 @@ function SettingsPage() {

socket.onopen = () => {
console.log('Connected to local server for IP detection');
socket.send(JSON.stringify({ type: 'get-ip' }));
// socket.send(JSON.stringify({ type: 'get-ip' })); // Not strictly needed as connected sends logic
};

socket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);

if (data.type === 'connected') {
if (data.serverIp) {
console.log('Auto-detected IP:', data.serverIp);
setIp(data.serverIp);
}
if (data.pin) {
console.log('Received session PIN');
setPin(data.pin);
}
}

if (data.type === 'server-ip' && data.ip) {
console.log('Auto-detected IP:', data.ip);
setIp(data.ip);
Expand All @@ -84,6 +97,19 @@ function SettingsPage() {
<div className="p-6 pb-safe max-w-md mx-auto space-y-8 min-h-full">
<h1 className="text-3xl font-bold pt-4">Settings</h1>

{pin && (
<div className="alert alert-info shadow-lg">
<div>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" className="stroke-current flex-shrink-0 w-6 h-6"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
<div>
<h3 className="font-bold">Session PIN</h3>
<div className="text-xs">Enter this PIN on your mobile device</div>
</div>
</div>
<div className="font-mono text-4xl font-black">{pin}</div>
</div>
)}
Comment on lines +100 to +111
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add accessible title to the SVG icon.

The SVG on line 101 lacks an accessible title element, flagged by Biome's noSvgWithoutTitle rule.

Proposed fix
-<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" className="stroke-current flex-shrink-0 w-6 h-6"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" className="stroke-current flex-shrink-0 w-6 h-6" aria-label="Info"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{pin && (
<div className="alert alert-info shadow-lg">
<div>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" className="stroke-current flex-shrink-0 w-6 h-6"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
<div>
<h3 className="font-bold">Session PIN</h3>
<div className="text-xs">Enter this PIN on your mobile device</div>
</div>
</div>
<div className="font-mono text-4xl font-black">{pin}</div>
</div>
)}
{pin && (
<div className="alert alert-info shadow-lg">
<div>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" className="stroke-current flex-shrink-0 w-6 h-6" aria-label="Info"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
<div>
<h3 className="font-bold">Session PIN</h3>
<div className="text-xs">Enter this PIN on your mobile device</div>
</div>
</div>
<div className="font-mono text-4xl font-black">{pin}</div>
</div>
)}
🧰 Tools
🪛 Biome (2.3.13)

[error] 101-101: Alternative text title element cannot be empty

For accessibility purposes, SVGs should have an alternative text, provided via title element. If the svg element has role="img", you should add the aria-label or aria-labelledby attribute.

(lint/a11y/noSvgWithoutTitle)

🤖 Prompt for AI Agents
In `@src/routes/settings.tsx` around lines 98 - 109, The SVG used inside the PIN
alert (the JSX block that renders when the variable `pin` is truthy) is missing
an accessible title; add a unique <title> element and reference it from the SVG
via aria-labelledby (or aria-label) so screen readers can announce the
icon—e.g., insert a descriptive title like "Info" (with an id such as
"session-pin-icon-title") and set aria-labelledby="session-pin-icon-title" (and
keep role="img" if needed) on the same <svg> element that currently renders the
info icon in the Session PIN alert.


<div className="form-control w-full">
<label className="label">
<span className="label-text">Server IP (for Remote)</span>
Expand Down
Loading