Skip to content
Open
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
28 changes: 27 additions & 1 deletion src/routes/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const Route = createFileRoute('/settings')({

function SettingsPage() {
const [ip, setIp] = useState('');
const [availableIps, setAvailableIps] = useState<string[]>([]);
const [frontendPort, setFrontendPort] = useState(String(CONFIG.FRONTEND_PORT));
const [invertScroll, setInvertScroll] = useState(CONFIG.MOUSE_INVERT);
const [sensitivity, setSensitivity] = useState(CONFIG.MOUSE_SENSITIVITY);
Expand Down Expand Up @@ -61,7 +62,16 @@ function SettingsPage() {
const data = JSON.parse(event.data);
if (data.type === 'server-ip' && data.ip) {
console.log('Auto-detected IP:', data.ip);
setIp(data.ip);

// Only override if user hasn't set one, or if it's default
const stored = localStorage.getItem('rein_ip');
if (!stored || stored === 'localhost' || stored === '127.0.0.1' || stored === window.location.hostname) {
setIp(data.ip);
}

if (data.ips && Array.isArray(data.ips)) {
setAvailableIps(data.ips);
}
socket.close();
}
} catch (e) {
Expand Down Expand Up @@ -95,6 +105,22 @@ function SettingsPage() {
value={ip}
onChange={(e) => setIp(e.target.value)}
/>
{availableIps.length > 0 && (
<div className="pt-2">
<span className="text-xs opacity-50 block mb-1">Detected LAN IPs:</span>
<div className="flex flex-wrap gap-2">
{availableIps.map(addr => (
<button
key={addr}
className="btn btn-xs btn-outline"
onClick={() => setIp(addr)}
>
Comment on lines +113 to +117
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 explicit type="button" to prevent unintended form submission.

Per the Biome lint finding (useButtonType): buttons default to type="submit". While there's no enclosing <form> currently, adding the explicit type is a defensive best practice.

Proposed fix
                                   <button 
                                       key={addr} 
+                                      type="button"
                                       className="btn btn-xs btn-outline"
                                       onClick={() => setIp(addr)}
                                   >
📝 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
<button
key={addr}
className="btn btn-xs btn-outline"
onClick={() => setIp(addr)}
>
<button
key={addr}
type="button"
className="btn btn-xs btn-outline"
onClick={() => setIp(addr)}
>
🧰 Tools
🪛 Biome (2.3.13)

[error] 111-115: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset

(lint/a11y/useButtonType)

🤖 Prompt for AI Agents
In `@src/routes/settings.tsx` around lines 111 - 115, The button element that
calls setIp(addr) should include an explicit type to avoid accidental form
submissions; update the JSX button (the one with key={addr} and onClick={() =>
setIp(addr)}) to add type="button" so it doesn't default to type="submit".

{addr}
</button>
))}
</div>
</div>
)}
<label className="label">
<span className="label-text-alt opacity-50">This Computer's LAN IP</span>
</label>
Expand Down
22 changes: 14 additions & 8 deletions src/server/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,32 @@ import fs from 'fs';
import { Server, IncomingMessage } from 'http';
import { Socket } from 'net';

// Helper to find LAN IP
function getLocalIp() {
// Helper to find LAN IPs
function getLocalIps() {
const nets = os.networkInterfaces();
const results: string[] = [];
for (const name of Object.keys(nets)) {
for (const net of nets[name]!) {
if (net.family === 'IPv4' && !net.internal) {
return net.address;
results.push(net.address);
}
}
}
return 'localhost';
return results.length > 0 ? results : ['localhost'];
}

export function createWsServer(server: Server) {
const wss = new WebSocketServer({ noServer: true });
const inputHandler = new InputHandler();
const LAN_IP = getLocalIp();
const LAN_IPS = getLocalIps();
// Heuristic: Prefer 192.168.x.x, then 10.x.x.x, then first available
const BEST_IP = LAN_IPS.find(ip => ip.startsWith('192.168.'))
|| LAN_IPS.find(ip => ip.startsWith('10.'))
|| LAN_IPS[0];
Comment on lines +26 to +29
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

Missing 172.16.0.0/12 private subnet in heuristic.

The RFC 1918 private range 172.16.0.0 – 172.31.255.255 is not considered. This is common in corporate/VPN environments and Docker default bridges.

Proposed fix
     const BEST_IP = LAN_IPS.find(ip => ip.startsWith('192.168.'))
         || LAN_IPS.find(ip => ip.startsWith('10.'))
+        || LAN_IPS.find(ip => {
+            const second = parseInt(ip.split('.')[1], 10);
+            return ip.startsWith('172.') && second >= 16 && second <= 31;
+        })
         || LAN_IPS[0];
📝 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
// Heuristic: Prefer 192.168.x.x, then 10.x.x.x, then first available
const BEST_IP = LAN_IPS.find(ip => ip.startsWith('192.168.'))
|| LAN_IPS.find(ip => ip.startsWith('10.'))
|| LAN_IPS[0];
// Heuristic: Prefer 192.168.x.x, then 10.x.x.x, then first available
const BEST_IP = LAN_IPS.find(ip => ip.startsWith('192.168.'))
|| LAN_IPS.find(ip => ip.startsWith('10.'))
|| LAN_IPS.find(ip => {
const second = parseInt(ip.split('.')[1], 10);
return ip.startsWith('172.') && second >= 16 && second <= 31;
})
|| LAN_IPS[0];
🤖 Prompt for AI Agents
In `@src/server/websocket.ts` around lines 26 - 29, Update the IP selection
heuristic used to compute BEST_IP so it also prefers RFC1918 172.16.0.0/12
addresses before falling back to 10.x.x.x; specifically, change the LAN_IPS
checks (the expression that sets BEST_IP) to test for 192.168.* first, then
detect 172.16.0.0/12 addresses (e.g., by checking the first octet is "172" and
the second octet is between 16 and 31), then 10.*, and finally LAN_IPS[0]; keep
the order of preference and existing symbol names (BEST_IP, LAN_IPS).


console.log(`WebSocket Server initialized (Upgrade mode)`);
console.log(`WS LAN IP: ${LAN_IP}`);
console.log(`WS LAN IPS: ${LAN_IPS.join(', ')}`);
console.log(`Selected Best IP: ${BEST_IP}`);

server.on('upgrade', (request: IncomingMessage, socket: Socket, head: Buffer) => {
const pathname = request.url;
Expand All @@ -39,15 +45,15 @@ export function createWsServer(server: Server) {
wss.on('connection', (ws: WebSocket) => {
console.log('Client connected to /ws');

ws.send(JSON.stringify({ type: 'connected', serverIp: LAN_IP }));
ws.send(JSON.stringify({ type: 'connected', serverIp: BEST_IP, ips: LAN_IPS }));

ws.on('message', async (data: string) => {
try {
const raw = data.toString();
const msg = JSON.parse(raw);

if (msg.type === 'get-ip') {
ws.send(JSON.stringify({ type: 'server-ip', ip: LAN_IP }));
ws.send(JSON.stringify({ type: 'server-ip', ip: BEST_IP, ips: LAN_IPS }));
return;
}

Expand Down