From 858e4f3982bc129f07cff3e1c683a0ce379dd4ab Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Sat, 4 Apr 2026 15:27:45 -0500 Subject: [PATCH 1/2] Remove QR-based mobile pairing settings - Replace QR pairing UI with copyable pairing link - Update mobile companion and settings copy - Clean up related formatting changes --- apps/marketing/components/OkCodeLogo.tsx | 13 +---- apps/web/src/components/OkCodeMark.tsx | 13 +---- .../src/components/chat/HeaderPanelsMenu.tsx | 13 +---- .../merge-conflicts/MergeConflictShell.tsx | 27 +++++----- .../components/mobile/MobilePairingScreen.tsx | 4 +- .../src/components/mobile/PairingQrCode.tsx | 52 +++++++------------ apps/web/src/components/skills/SkillsPage.tsx | 7 ++- apps/web/src/lib/customTheme.ts | 4 +- apps/web/src/routes/_chat.settings.tsx | 6 +-- scripts/update-ios-version.ts | 11 ++-- 10 files changed, 51 insertions(+), 99 deletions(-) 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/MobilePairingScreen.tsx b/apps/web/src/components/mobile/MobilePairingScreen.tsx index a9522f9b2..6fbbf8ad1 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 show a pairing + link, then copy it and paste it below.

diff --git a/apps/web/src/components/mobile/PairingQrCode.tsx b/apps/web/src/components/mobile/PairingQrCode.tsx index 935060dde..aca9f6e06 100644 --- a/apps/web/src/components/mobile/PairingQrCode.tsx +++ b/apps/web/src/components/mobile/PairingQrCode.tsx @@ -1,6 +1,5 @@ -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; -import { generateQrSvg } from "../../lib/qrCode"; import { resolveServerHttpOrigin } from "../../lib/runtimeBridge"; import { Button } from "../ui/button"; @@ -11,19 +10,19 @@ 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. + * PairingLinkCard fetches a short-lived pairing link from the server's + * `/api/pairing` endpoint and exposes it through a copy button. * - * The QR code auto-refreshes when the pairing link expires. + * The link auto-refreshes when it expires so the desktop page stays usable + * without requiring a manual refresh action. */ -export function PairingQrCode() { +export function PairingLinkCard() { 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); + const refreshRequestedRef = useRef(false); const fetchPairingLink = useCallback(async () => { setLoading(true); @@ -39,15 +38,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); } @@ -62,17 +58,20 @@ export function PairingQrCode() { useEffect(() => { if (!pairing?.expiresAt) { setExpiresIn(null); + refreshRequestedRef.current = false; return; } + refreshRequestedRef.current = false; const update = () => { const remaining = Math.max( 0, Math.floor((new Date(pairing.expiresAt).getTime() - Date.now()) / 1000), ); setExpiresIn(remaining); - if (remaining <= 0) { + if (remaining <= 0 && !refreshRequestedRef.current) { // Auto-refresh when expired + refreshRequestedRef.current = true; void fetchPairingLink(); } }; @@ -89,7 +88,7 @@ export function PairingQrCode() { setCopied(true); setTimeout(() => setCopied(false), 2000); } catch { - // Fallback: select the text in the details element + // Clipboard access can fail in some browsers or shells; leave the button available. } }; @@ -101,7 +100,9 @@ export function PairingQrCode() { return (
-

Scan with OK Code mobile app

+

+ Pair with the OK Code mobile app +

{error ? (
@@ -116,14 +117,8 @@ export function PairingQrCode() { {loading ? "Generating..." : "Retry"}
- ) : svgHtml ? ( + ) : pairing ? ( <> -
{expiresIn !== null && (

{expiresIn > 0 ? <>Expires in {formatTime(expiresIn)} : <>Refreshing...} @@ -133,24 +128,13 @@ export function PairingQrCode() { -

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

) : loading ? ( -
-

Generating...

-
+

Generating pairing link...

) : null}
); 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."}