From ddf76139d96165d0c5a2f80dfc732465b1afc5df Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Sat, 4 Apr 2026 14:09:30 -0500 Subject: [PATCH 1/2] Switch mobile pairing to link-based flow - Replace QR display with a copyable pairing link - Update mobile pairing copy in settings and onboarding --- .../components/mobile/MobilePairingScreen.tsx | 4 +- .../src/components/mobile/PairingQrCode.tsx | 57 +++++++++---------- apps/web/src/routes/_chat.settings.tsx | 6 +- 3 files changed, 32 insertions(+), 35 deletions(-) diff --git a/apps/web/src/components/mobile/MobilePairingScreen.tsx b/apps/web/src/components/mobile/MobilePairingScreen.tsx index a9522f9b2..d62748f7d 100644 --- a/apps/web/src/components/mobile/MobilePairingScreen.tsx +++ b/apps/web/src/components/mobile/MobilePairingScreen.tsx @@ -97,8 +97,8 @@ export function MobilePairingScreen() {

Pair this device

- Open Settings → Mobile Companion on your desktop to show a QR - pairing code, then copy the link and paste it below. + Open Settings → Mobile Companion on your desktop to generate a + pairing link, then copy the link and paste it below.

diff --git a/apps/web/src/components/mobile/PairingQrCode.tsx b/apps/web/src/components/mobile/PairingQrCode.tsx index 935060dde..c54197183 100644 --- a/apps/web/src/components/mobile/PairingQrCode.tsx +++ b/apps/web/src/components/mobile/PairingQrCode.tsx @@ -1,8 +1,8 @@ import { useCallback, useEffect, useState } from "react"; -import { generateQrSvg } from "../../lib/qrCode"; import { resolveServerHttpOrigin } from "../../lib/runtimeBridge"; import { Button } from "../ui/button"; +import { Input } from "../ui/input"; interface PairingInfo { pairingUrl: string; @@ -11,17 +11,14 @@ interface PairingInfo { } /** - * PairingQrCode renders a QR code on the desktop web app that mobile devices - * can scan to pair. It fetches a short-lived pairing link from the server's - * `/api/pairing` endpoint and displays it as a scannable QR code. - * - * The QR code auto-refreshes when the pairing link expires. + * PairingLink renders the desktop pairing link used to connect a mobile + * device. It fetches a short-lived pairing link from the server's + * `/api/pairing` endpoint and lets the user copy it directly. */ -export function PairingQrCode() { +export function PairingLink() { const [pairing, setPairing] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); - const [svgHtml, setSvgHtml] = useState(null); const [expiresIn, setExpiresIn] = useState(null); const [copied, setCopied] = useState(false); @@ -39,15 +36,12 @@ export function PairingQrCode() { if ("error" in data) { setError(data.error as unknown as string); setPairing(null); - setSvgHtml(null); return; } setPairing(data); - setSvgHtml(generateQrSvg(data.pairingUrl)); } catch (err) { setError(err instanceof Error ? err.message : "Failed to generate pairing link."); setPairing(null); - setSvgHtml(null); } finally { setLoading(false); } @@ -100,11 +94,11 @@ export function PairingQrCode() { }; return ( -
-

Scan with OK Code mobile app

+
+

Pair with a mobile device

{error ? ( -
+

{error}

- ) : svgHtml ? ( + ) : pairing?.pairingUrl ? ( <> -
- {expiresIn !== null && ( +

- {expiresIn > 0 ? <>Expires in {formatTime(expiresIn)} : <>Refreshing...} + Copy this link and open it on the mobile app or device you want to pair.

- )} -
+ event.currentTarget.select()} + className="font-mono text-xs" + /> +
+
@@ -142,16 +136,19 @@ export function PairingQrCode() { {loading ? "Generating..." : "Refresh"}
-

- Scan the QR code with your phone camera, or copy the link and paste it in the mobile - app. -

+ {expiresIn !== null ? ( +

+ {expiresIn > 0 ? <>Expires in {formatTime(expiresIn)} : <>Refreshing...} +

+ ) : null} ) : loading ? ( -
+

Generating...

) : null}
); } + +export { PairingLink as PairingQrCode }; diff --git a/apps/web/src/routes/_chat.settings.tsx b/apps/web/src/routes/_chat.settings.tsx index aab482fd9..5089e35d6 100644 --- a/apps/web/src/routes/_chat.settings.tsx +++ b/apps/web/src/routes/_chat.settings.tsx @@ -70,7 +70,7 @@ import { serverConfigQueryOptions } from "../lib/serverReactQuery"; import { cn } from "../lib/utils"; import { ensureNativeApi, readNativeApi } from "../nativeApi"; import { useStore } from "../store"; -import { PairingQrCode } from "../components/mobile/PairingQrCode"; +import { PairingLink } from "../components/mobile/PairingQrCode"; const THEME_OPTIONS = [ { @@ -1650,10 +1650,10 @@ function SettingsRouteView() {
- +
From 4a80406eddb5898b6a0142edc936954189357acc Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Sat, 4 Apr 2026 14:10:04 -0500 Subject: [PATCH 2/2] Rename mobile pairing QR flow to link-only - Rename the pairing component and update its import path - Remove the QR-code-specific export alias - Carry the new mobile-only pairing flow through related UI and theme cleanup --- apps/marketing/components/OkCodeLogo.tsx | 13 ++------- apps/web/src/components/OkCodeMark.tsx | 13 ++------- .../src/components/chat/HeaderPanelsMenu.tsx | 13 ++------- .../merge-conflicts/MergeConflictShell.tsx | 27 +++++++++---------- .../{PairingQrCode.tsx => PairingLink.tsx} | 7 +---- apps/web/src/components/skills/SkillsPage.tsx | 7 +++-- apps/web/src/lib/customTheme.ts | 4 +-- apps/web/src/routes/_chat.settings.tsx | 2 +- scripts/update-ios-version.ts | 11 ++++---- 9 files changed, 30 insertions(+), 67 deletions(-) rename apps/web/src/components/mobile/{PairingQrCode.tsx => PairingLink.tsx} (96%) diff --git a/apps/marketing/components/OkCodeLogo.tsx b/apps/marketing/components/OkCodeLogo.tsx index a90024c81..2e1c5e699 100644 --- a/apps/marketing/components/OkCodeLogo.tsx +++ b/apps/marketing/components/OkCodeLogo.tsx @@ -9,17 +9,8 @@ type OkCodeLogoProps = { export function OkCodeMark({ className = "w-6 h-6" }: OkCodeMarkProps) { return ( -
diff --git a/apps/web/src/components/mobile/PairingQrCode.tsx b/apps/web/src/components/mobile/PairingLink.tsx similarity index 96% rename from apps/web/src/components/mobile/PairingQrCode.tsx rename to apps/web/src/components/mobile/PairingLink.tsx index c54197183..118d9b783 100644 --- a/apps/web/src/components/mobile/PairingQrCode.tsx +++ b/apps/web/src/components/mobile/PairingLink.tsx @@ -47,12 +47,10 @@ export function PairingLink() { } }, []); - // Fetch on mount useEffect(() => { void fetchPairingLink(); }, [fetchPairingLink]); - // Countdown timer useEffect(() => { if (!pairing?.expiresAt) { setExpiresIn(null); @@ -66,7 +64,6 @@ export function PairingLink() { ); setExpiresIn(remaining); if (remaining <= 0) { - // Auto-refresh when expired void fetchPairingLink(); } }; @@ -83,7 +80,7 @@ export function PairingLink() { setCopied(true); setTimeout(() => setCopied(false), 2000); } catch { - // Fallback: select the text in the details element + // Fallback: select the text in the input below. } }; @@ -150,5 +147,3 @@ export function PairingLink() {
); } - -export { PairingLink as PairingQrCode }; diff --git a/apps/web/src/components/skills/SkillsPage.tsx b/apps/web/src/components/skills/SkillsPage.tsx index cc70f1389..7a11481db 100644 --- a/apps/web/src/components/skills/SkillsPage.tsx +++ b/apps/web/src/components/skills/SkillsPage.tsx @@ -510,11 +510,10 @@ export function SkillsPage(props: {
-

- Unable to load skills -

+

Unable to load skills

- {queryErrorMessage ?? "The skills service is unavailable. Check that the server is running."} + {queryErrorMessage ?? + "The skills service is unavailable. Check that the server is running."}