|
1 | 1 | import { StrictMode, useState } from "react"; |
| 2 | +import type React from "react"; |
2 | 3 | import { createRoot } from "react-dom/client"; |
3 | 4 | import { Avatar, Box, Text, Link, Heading, Spinner } from "@primer/react"; |
4 | 5 | import { |
@@ -62,8 +63,20 @@ function AvatarWithFallback({ src, login, size }: { src?: string; login: string; |
62 | 63 | ); |
63 | 64 | } |
64 | 65 |
|
65 | | -function UserCard({ user }: { user: UserData }) { |
| 66 | +function UserCard({ |
| 67 | + user, |
| 68 | + onOpenLink, |
| 69 | +}: { |
| 70 | + user: UserData; |
| 71 | + onOpenLink?: (url: string) => void; |
| 72 | +}) { |
66 | 73 | const d = user.details || {}; |
| 74 | + const handleClick = |
| 75 | + onOpenLink && |
| 76 | + ((url: string) => (e: React.MouseEvent) => { |
| 77 | + e.preventDefault(); |
| 78 | + onOpenLink(url); |
| 79 | + }); |
67 | 80 |
|
68 | 81 | return ( |
69 | 82 | <Box |
@@ -103,7 +116,13 @@ function UserCard({ user }: { user: UserData }) { |
103 | 116 | {d.blog && ( |
104 | 117 | <> |
105 | 118 | <Box sx={{ color: "fg.muted" }}><LinkIcon size={16} /></Box> |
106 | | - <Link href={d.blog} target="_blank">{d.blog}</Link> |
| 119 | + <Link |
| 120 | + href={d.blog} |
| 121 | + target="_blank" |
| 122 | + onClick={handleClick?.(d.blog)} |
| 123 | + > |
| 124 | + {d.blog} |
| 125 | + </Link> |
107 | 126 | </> |
108 | 127 | )} |
109 | 128 | {d.email && ( |
@@ -140,41 +159,39 @@ function UserCard({ user }: { user: UserData }) { |
140 | 159 | } |
141 | 160 |
|
142 | 161 | function GetMeApp() { |
143 | | - const { error, toolResult } = useMcpApp({ |
| 162 | + const { error, toolResult, hostContext, openLink } = useMcpApp({ |
144 | 163 | appName: "github-mcp-server-get-me", |
145 | 164 | }); |
146 | 165 |
|
147 | | - if (error) { |
148 | | - return <Text sx={{ color: "danger.fg" }}>Error: {error.message}</Text>; |
149 | | - } |
150 | | - |
151 | | - if (!toolResult) { |
152 | | - return ( |
153 | | - <Box display="flex" alignItems="center" gap={2}> |
154 | | - <Spinner size="small" /> |
155 | | - <Text sx={{ color: "fg.muted" }}>Loading user data...</Text> |
156 | | - </Box> |
157 | | - ); |
158 | | - } |
159 | | - |
160 | | - // Parse user data from tool result |
161 | | - const textContent = toolResult.content?.find((c: { type: string }) => c.type === "text"); |
162 | | - if (!textContent || !("text" in textContent)) { |
163 | | - return <Text sx={{ color: "danger.fg" }}>No user data in response</Text>; |
164 | | - } |
| 166 | + const content = (() => { |
| 167 | + if (error) { |
| 168 | + return <Text sx={{ color: "danger.fg" }}>Error: {error.message}</Text>; |
| 169 | + } |
| 170 | + if (!toolResult) { |
| 171 | + return ( |
| 172 | + <Box display="flex" alignItems="center" gap={2}> |
| 173 | + <Spinner size="small" /> |
| 174 | + <Text sx={{ color: "fg.muted" }}>Loading user data...</Text> |
| 175 | + </Box> |
| 176 | + ); |
| 177 | + } |
| 178 | + const textContent = toolResult.content?.find((c: { type: string }) => c.type === "text"); |
| 179 | + if (!textContent || !("text" in textContent)) { |
| 180 | + return <Text sx={{ color: "danger.fg" }}>No user data in response</Text>; |
| 181 | + } |
| 182 | + try { |
| 183 | + const userData = JSON.parse(textContent.text as string) as UserData; |
| 184 | + return <UserCard user={userData} onOpenLink={(url) => void openLink(url)} />; |
| 185 | + } catch { |
| 186 | + return <Text sx={{ color: "danger.fg" }}>Failed to parse user data</Text>; |
| 187 | + } |
| 188 | + })(); |
165 | 189 |
|
166 | | - try { |
167 | | - const userData = JSON.parse(textContent.text as string) as UserData; |
168 | | - return <UserCard user={userData} />; |
169 | | - } catch { |
170 | | - return <Text sx={{ color: "danger.fg" }}>Failed to parse user data</Text>; |
171 | | - } |
| 190 | + return <AppProvider hostContext={hostContext}>{content}</AppProvider>; |
172 | 191 | } |
173 | 192 |
|
174 | 193 | createRoot(document.getElementById("root")!).render( |
175 | 194 | <StrictMode> |
176 | | - <AppProvider> |
177 | | - <GetMeApp /> |
178 | | - </AppProvider> |
| 195 | + <GetMeApp /> |
179 | 196 | </StrictMode> |
180 | 197 | ); |
0 commit comments