diff --git a/app/connection/[id].tsx b/app/connection/[id].tsx index 9467ea3..db2d802 100644 --- a/app/connection/[id].tsx +++ b/app/connection/[id].tsx @@ -68,7 +68,7 @@ export default function EditConnectionScreen() { } setIsTesting(true) - const success = await testConnection( + const result = await testConnection( { id: connection.id, name: name || "Test", @@ -82,8 +82,10 @@ export default function EditConnectionScreen() { setIsTesting(false) Alert.alert( - success ? "Success" : "Failed", - success ? "Connection successful!" : "Could not connect to the server. Check the URL and credentials.", + result.ok ? "Success" : "Failed", + result.ok + ? "Connection successful!" + : `Could not connect to ${url.trim()}\n\n${result.error || "Check the URL and credentials."}`, ) } diff --git a/app/connection/add.tsx b/app/connection/add.tsx index e30bbea..19c612a 100644 --- a/app/connection/add.tsx +++ b/app/connection/add.tsx @@ -34,8 +34,25 @@ export default function AddConnectionScreen() { const buildUrl = () => { if (mode === "advanced") return url.trim() - if (!ip.trim()) return "" - return `http://${ip.trim()}:${port || "4096"}` + const raw = ip.trim() + if (!raw) return "" + // Be forgiving about pasted values: a full URL, a host:port, or a + // host with a trailing path. Extract scheme, host, and port so we + // never produce "http://http://host:4096:4096". + const schemeMatch = raw.match(/^(https?):\/\//i) + const scheme = schemeMatch ? schemeMatch[1].toLowerCase() : "http" + let rest = raw.replace(/^https?:\/\//i, "") + rest = rest.split("/")[0] // drop any path/query + let host = rest + let pastedPort = "" + const lastColon = rest.lastIndexOf(":") + // Only treat trailing ":NNNN" as a port (ignore IPv6 colons / bare host) + if (lastColon > -1 && /^\d+$/.test(rest.slice(lastColon + 1))) { + host = rest.slice(0, lastColon) + pastedPort = rest.slice(lastColon + 1) + } + const finalPort = pastedPort || port.trim() || "4096" + return `${scheme}://${host}:${finalPort}` } const handleQuickConnect = async () => { @@ -48,7 +65,7 @@ export default function AddConnectionScreen() { setIsConnecting(true) // Test connection first - const success = await testConnection( + const result = await testConnection( { id: "", name: name || "My Server", @@ -59,7 +76,7 @@ export default function AddConnectionScreen() { password || undefined, ) - if (success) { + if (result.ok) { // Save and go back await addConnection( { @@ -76,7 +93,7 @@ export default function AddConnectionScreen() { setIsConnecting(false) Alert.alert( "Connection Failed", - "Could not connect to the server.\n\nMake sure:\n1. OpenCode is running: opencode serve --hostname 0.0.0.0\n2. You're on the same WiFi network\n3. The IP address is correct", + `Could not connect to ${serverUrl}\n\n${result.error || "Unknown error"}\n\nMake sure:\n1. OpenCode is running: opencode serve --hostname 0.0.0.0\n2. You're on the same network (or Tailscale is connected)\n3. The address is correct (use the IP for tailnet if MagicDNS isn't enabled)`, [{ text: "OK" }], ) } @@ -128,13 +145,13 @@ export default function AddConnectionScreen() { : , password?: string) => Promise removeConnection: (id: string) => Promise setActiveConnection: (id: string) => Promise - testConnection: (connection: ServerConnection, password?: string) => Promise + testConnection: (connection: ServerConnection, password?: string) => Promise<{ ok: boolean; error?: string }> updateConnection: (id: string, updates: Partial) => Promise refreshProject: () => Promise // Create a one-off client pointing at a specific directory (for cross-project operations) @@ -227,9 +227,9 @@ export const useConnections = create((set, get) => ({ }) await client.global.health() - return true - } catch { - return false + return { ok: true } + } catch (error) { + return { ok: false, error: error instanceof Error ? error.message : String(error) } } },