import { createSignal, For, onCleanup, onMount, Show } from "solid-js"; import { sendMessage } from "../App"; import type { DiscoveredPeer, PairedDevice, PairingStep } from "../../shared/sync-types"; interface SyncPairingProps { mode: "initiate" | "accept"; onClose: () => void; onPaired: (device: PairedDevice) => void; } function Spinner() { return ; } export function SyncPairingView(props: SyncPairingProps) { const [step, setStep] = createSignal({ stage: "idle" }); const [codeInput, setCodeInput] = createSignal(""); const [codeError, setCodeError] = createSignal(null); // Discover-and-tap state for the initiate flow. The background broadcasts // SYNC_PEER_DISCOVERED / SYNC_PEER_LOST as peers come and go on mDNS; // tapping a peer routes to INITIATE_PAIR_WITH_PEER. const [discoveredPeers, setDiscoveredPeers] = createSignal([]); const [discoveryActive, setDiscoveryActive] = createSignal(false); const [discoveryError, setDiscoveryError] = createSignal(null); const [pairingPeerId, setPairingPeerId] = createSignal(null); onMount(() => { if (props.mode !== "initiate") return; void startDiscovery(); }); onCleanup(() => { if (discoveryActive()) void sendMessage({ type: "STOP_PEER_DISCOVERY" }); }); function handleRuntimeMessage(msg: unknown) { const m = msg as { type?: string; peer?: DiscoveredPeer; deviceId?: string }; if (m?.type === "SYNC_PEER_DISCOVERED" && m.peer) { const peer = m.peer; setDiscoveredPeers((prev) => { if (prev.some((p) => p.deviceId === peer.deviceId)) return prev; return [...prev, peer]; }); } if (m?.type === "SYNC_PEER_LOST" && m.deviceId) { const id = m.deviceId; setDiscoveredPeers((prev) => prev.filter((p) => p.deviceId !== id)); } } async function startDiscovery() { chrome.runtime.onMessage.addListener(handleRuntimeMessage); onCleanup(() => chrome.runtime.onMessage.removeListener(handleRuntimeMessage)); const res = await sendMessage({ type: "START_PEER_DISCOVERY" }); const response = res as { ok: boolean; error?: string }; if (response.ok) { setDiscoveryActive(true); setDiscoveryError(null); } else { setDiscoveryError(response.error ?? "discovery_failed"); } } async function handlePairWithPeer(peer: DiscoveredPeer) { setPairingPeerId(peer.deviceId); setStep({ stage: "connecting", peerName: peer.deviceName }); const res = await sendMessage({ type: "INITIATE_PAIR_WITH_PEER", payload: { peer }, }); const response = res as { ok: boolean; error?: string }; setPairingPeerId(null); if (!response.ok) { setStep({ stage: "error", message: response.error ?? "pair_failed" }); return; } const paired: PairedDevice = { deviceId: peer.deviceId, displayName: peer.deviceName, x25519PublicKey: peer.x25519PublicKey, pairedAt: Date.now(), }; setStep({ stage: "done", device: paired }); props.onPaired(paired); } async function handleConnect() { const code = codeInput().replace(/\D/g, "").slice(0, 6); if (code.length !== 6) { setCodeError("Enter a 6-digit code"); return; } setCodeError(null); setStep({ stage: "connecting", peerName: "" }); const res = await sendMessage({ type: "ACCEPT_PAIRING", payload: { code }, }); const response = res as { ok: boolean; data?: PairedDevice; error?: string }; if (!response.ok) { setStep({ stage: "error", message: response.error ?? "Connection failed" }); return; } setStep({ stage: "verifying" }); if (response.data) { setStep({ stage: "done", device: response.data }); props.onPaired(response.data); } } function handleCodeInput(e: InputEvent) { const target = e.currentTarget as HTMLInputElement; const value = target.value.replace(/\D/g, "").slice(0, 6); setCodeInput(value); setCodeError(null); } return (
pair devices

Pair a device

{discoveryError() === "pro_required" ? "Sync requires Peach Pro." : "Native sync host is not installed yet."}

Searching for Peach on your local network...

{(peer) => (
{peer.deviceName} {peer.alreadyPaired ? "Already paired ✓" : "Found on network"}
)}

Make sure both devices are on the same Wi-Fi network.

Pair with another device

Enter the 6-digit code shown on the other device.

{Array.from({ length: 6 }, (_, i) => ( { const current = codeInput(); const digit = (e.currentTarget as HTMLInputElement).value.replace(/\D/g, "").slice(-1); const newValue = current.slice(0, i) + digit + current.slice(i + 1); setCodeInput(newValue.slice(0, 6)); setCodeError(null); if (digit && i < 5) { const inputs = document.querySelectorAll(".peach-pairing-code-box"); (inputs[i + 1] as HTMLInputElement)?.focus(); } }} onKeyDown={(e) => { if (e.key === "Backspace" && !codeInput()[i] && i > 0) { const inputs = document.querySelectorAll(".peach-pairing-code-box"); (inputs[i - 1] as HTMLInputElement)?.focus(); } }} /> ))}

{codeError()}

{step().stage === "connecting" ? "Connecting..." : "Verifying..."}

Paired!

{(step() as { stage: "done"; device: PairedDevice }).device.displayName}

Pairing failed

{(step() as { stage: "error"; message: string }).message}

); }