@@ -7,13 +7,17 @@ import { ExplainerCardsPortal } from "@/components/explainer-cards";
77import { DemoGallery , type DemoItem } from "@/components/demo-gallery" ;
88import { GridIcon } from "@/components/demo-gallery/grid-icon" ;
99import { DesktopTipModal } from "@/components/desktop-tip-modal" ;
10+ import { QrButton , QrModal } from "@/components/qr-modal" ;
1011import { CopilotChat , useAgent , useCopilotKit } from "@copilotkit/react-core/v2" ;
1112
1213export default function HomePage ( ) {
1314 useGenerativeUIExamples ( ) ;
1415 useExampleSuggestions ( ) ;
1516
1617 const [ demoDrawerOpen , setDemoDrawerOpen ] = useState ( false ) ;
18+ const [ qrOpen , setQrOpen ] = useState ( false ) ;
19+ const [ qrSessionId ] = useState ( ( ) => typeof crypto !== "undefined" ? crypto . randomUUID ( ) . slice ( 0 , 12 ) : "fallback" ) ;
20+ const [ scanStatus , setScanStatus ] = useState < "waiting" | "scanned" | "picked" > ( "waiting" ) ;
1721 const { agent } = useAgent ( ) ;
1822 const { copilotkit } = useCopilotKit ( ) ;
1923
@@ -23,6 +27,35 @@ export default function HomePage() {
2327 copilotkit . runAgent ( { agent } ) ;
2428 } ;
2529
30+ // Reset scan status when QR modal opens
31+ useEffect ( ( ) => {
32+ if ( qrOpen ) setScanStatus ( "waiting" ) ;
33+ } , [ qrOpen ] ) ;
34+
35+ // Poll for QR pick status
36+ useEffect ( ( ) => {
37+ if ( ! qrOpen ) return ;
38+ const interval = setInterval ( async ( ) => {
39+ try {
40+ const res = await fetch ( `/api/pick?sessionId=${ qrSessionId } ` ) ;
41+ const data = await res . json ( ) ;
42+ if ( data . status === "scanned" ) {
43+ setScanStatus ( "scanned" ) ;
44+ } else if ( data . status === "picked" && data . prompt ) {
45+ setScanStatus ( "picked" ) ;
46+ setTimeout ( ( ) => {
47+ setQrOpen ( false ) ;
48+ agent . addMessage ( { id : crypto . randomUUID ( ) , content : data . prompt , role : "user" } ) ;
49+ copilotkit . runAgent ( { agent } ) ;
50+ } , 800 ) ;
51+ }
52+ } catch {
53+ // ignore polling errors
54+ }
55+ } , 2000 ) ;
56+ return ( ) => clearInterval ( interval ) ;
57+ } , [ qrOpen , qrSessionId , agent , copilotkit ] ) ;
58+
2659 // Widget bridge: handle messages from widget iframes
2760 useEffect ( ( ) => {
2861 const handler = ( e : MessageEvent ) => {
@@ -68,6 +101,7 @@ export default function HomePage() {
68101 </ p >
69102 </ div >
70103 < div className = "flex items-center gap-1.5 sm:gap-2" >
104+ < QrButton onClick = { ( ) => setQrOpen ( true ) } />
71105 < button
72106 onClick = { ( ) => setDemoDrawerOpen ( true ) }
73107 className = "inline-flex items-center gap-1.5 px-2.5 sm:px-3 py-1.5 sm:py-2 rounded-full text-xs sm:text-sm font-medium no-underline whitespace-nowrap transition-all duration-150 hover:-translate-y-px cursor-pointer"
@@ -118,6 +152,13 @@ export default function HomePage() {
118152 />
119153
120154 < DesktopTipModal />
155+
156+ < QrModal
157+ isOpen = { qrOpen }
158+ onClose = { ( ) => setQrOpen ( false ) }
159+ sessionId = { qrSessionId }
160+ scanStatus = { scanStatus }
161+ />
121162 </ >
122163 ) ;
123164}
0 commit comments