From cff9c004db04b517e1850c8d1ba727672e4bdff1 Mon Sep 17 00:00:00 2001 From: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> Date: Thu, 5 Feb 2026 09:53:09 +0800 Subject: [PATCH] feat(ui): add paste mode success/error feedback with state-based rendering Replace overlay approach with conditional rendering for paste validation states. Success shows checkmark icon with 600ms delay before connecting, error shows alert with message and auto-resets after 2s. Improves Connecting text visibility. Co-Authored-By: Claude Opus 4.5 --- packages/ui/src/components/connect-prompt.tsx | 91 ++++++++++++++----- .../ui/src/components/qr-scanner-view.tsx | 14 --- 2 files changed, 67 insertions(+), 38 deletions(-) diff --git a/packages/ui/src/components/connect-prompt.tsx b/packages/ui/src/components/connect-prompt.tsx index 1e40e7f7..7cf85022 100644 --- a/packages/ui/src/components/connect-prompt.tsx +++ b/packages/ui/src/components/connect-prompt.tsx @@ -3,7 +3,6 @@ import { useState, useCallback, useRef } from "react"; import { Button } from "@multica/ui/components/ui/button"; import { Textarea } from "@multica/ui/components/ui/textarea"; -import { toast } from "@multica/ui/components/ui/sonner"; import { useConnectionStore, parseConnectionCode, @@ -11,15 +10,23 @@ import { } from "@multica/store"; import { useIsMobile } from "@multica/ui/hooks/use-mobile"; import { HugeiconsIcon } from "@hugeicons/react"; -import { Camera01Icon, TextIcon } from "@hugeicons/core-free-icons"; +import { + Camera01Icon, + TextIcon, + CheckmarkCircle02Icon, + Alert02Icon, +} from "@hugeicons/core-free-icons"; import { QrScannerView } from "@multica/ui/components/qr-scanner-view"; type Mode = "scan" | "paste"; +type PasteState = "idle" | "success" | "error"; export function ConnectPrompt() { const gwState = useConnectionStore((s) => s.connectionState); const [mode, setMode] = useState("scan"); const [codeInput, setCodeInput] = useState(""); + const [pasteState, setPasteState] = useState("idle"); + const [pasteError, setPasteError] = useState(null); const isMobile = useIsMobile(); const validatingRef = useRef(false); @@ -29,10 +36,22 @@ export function ConnectPrompt() { validatingRef.current = true; try { const info = parseConnectionCode(trimmed); - saveConnection(info); - useConnectionStore.getState().connect(info); + setPasteState("success"); + navigator.vibrate?.(50); + // Let the user see the success state before connecting + setTimeout(() => { + saveConnection(info); + useConnectionStore.getState().connect(info); + }, 600); } catch (e) { - toast.error((e as Error).message); + setPasteState("error"); + setPasteError((e as Error).message || "Invalid code"); + navigator.vibrate?.([30, 50, 30]); + setTimeout(() => { + setPasteState("idle"); + setPasteError(null); + setCodeInput(""); + }, 2000); } finally { validatingRef.current = false; } @@ -65,10 +84,10 @@ export function ConnectPrompt() {

Scan to start

- Scan a QR code to use an Agent + Scan a Multica QR code to start chatting

{isConnecting && ( -

+

Connecting to Agent...

)} @@ -87,11 +106,11 @@ export function ConnectPrompt() {

{mode === "scan" - ? "Scan a QR code to use an Agent" - : "Paste a connection code to use an Agent"} + ? "Scan a Multica QR code to start chatting" + : "Paste a Multica connection code to start chatting"}

{isConnecting && ( -

+

Connecting to Agent...

)} @@ -124,20 +143,44 @@ export function ConnectPrompt() { {mode === "scan" ? ( ) : ( -
-