Skip to content

Commit ab1a595

Browse files
committed
progress
1 parent d9cc03c commit ab1a595

File tree

205 files changed

+112290
-88
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

205 files changed

+112290
-88
lines changed

exercises/01.simple-ui/01.problem.raw-html/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
},
1515
"dependencies": {
1616
"@epic-web/invariant": "^1.0.0",
17-
"@mcp-ui/server": "^5.2.0",
17+
"@mcp-ui/server": "^5.10.0",
1818
"@modelcontextprotocol/sdk": "^1.17.2",
1919
"agents": "^0.0.111",
2020
"zod": "^3.25.67"

exercises/01.simple-ui/01.solution.raw-html/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
},
1515
"dependencies": {
1616
"@epic-web/invariant": "^1.0.0",
17-
"@mcp-ui/server": "^5.2.0",
17+
"@mcp-ui/server": "^5.10.0",
1818
"@modelcontextprotocol/sdk": "^1.17.2",
1919
"agents": "^0.0.111",
2020
"zod": "^3.25.67"

exercises/02.consistent-ui/01.problem.remote-dom/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
},
1515
"dependencies": {
1616
"@epic-web/invariant": "^1.0.0",
17-
"@mcp-ui/server": "^5.2.0",
17+
"@mcp-ui/server": "^5.10.0",
1818
"@modelcontextprotocol/sdk": "^1.17.2",
1919
"agents": "^0.0.111",
2020
"zod": "^3.25.67"

exercises/02.consistent-ui/01.solution.remote-dom/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
},
1515
"dependencies": {
1616
"@epic-web/invariant": "^1.0.0",
17-
"@mcp-ui/server": "^5.2.0",
17+
"@mcp-ui/server": "^5.10.0",
1818
"@modelcontextprotocol/sdk": "^1.17.2",
1919
"agents": "^0.0.111",
2020
"zod": "^3.25.67"

exercises/03.complex-ui/01.problem.iframe/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
},
1818
"dependencies": {
1919
"@epic-web/invariant": "^1.0.0",
20-
"@mcp-ui/server": "^5.2.0",
20+
"@mcp-ui/server": "^5.10.0",
2121
"@modelcontextprotocol/sdk": "^1.17.2",
2222
"agents": "^0.0.111",
2323
"isbot": "^5.1.30",

exercises/03.complex-ui/01.solution.iframe/app/routes/ui/journal-viewer.tsx

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
import { useTransition } from 'react'
2+
import {
3+
ErrorBoundary,
4+
useErrorBoundary,
5+
type FallbackProps,
6+
} from 'react-error-boundary'
7+
import { useDoubleCheck } from '#app/utils/misc.ts'
18
import { type Route } from './+types/journal-viewer.tsx'
29

310
export async function loader({ context }: Route.LoaderArgs) {
@@ -19,6 +26,7 @@ export default function JournalViewer({ loaderData }: Route.ComponentProps) {
1926
You have {entries.length} journal{' '}
2027
{entries.length === 1 ? 'entry' : 'entries'}
2128
</p>
29+
<XPostLink entryCount={entries.length} />
2230
</div>
2331

2432
{entries.length === 0 ? (
@@ -57,6 +65,12 @@ export default function JournalViewer({ loaderData }: Route.ComponentProps) {
5765
🏷️ {entry.tagCount} tag{entry.tagCount !== 1 ? 's' : ''}
5866
</span>
5967
</div>
68+
69+
<div className="mt-4 flex gap-2">
70+
<ViewEntryButton entry={entry} />
71+
<SummarizeEntryButton entry={entry} />
72+
<DeleteEntryButton entry={entry} />
73+
</div>
6074
</div>
6175
</div>
6276
</div>
@@ -67,3 +81,229 @@ export default function JournalViewer({ loaderData }: Route.ComponentProps) {
6781
</div>
6882
)
6983
}
84+
85+
function XPostLink({ entryCount }: { entryCount: number }) {
86+
return (
87+
<ErrorBoundary FallbackComponent={XPostLinkError}>
88+
<XPostLinkImpl entryCount={entryCount} />
89+
</ErrorBoundary>
90+
)
91+
}
92+
93+
function XPostLinkError({ error, resetErrorBoundary }: FallbackProps) {
94+
return (
95+
<div className="bg-destructive/10 border-destructive/20 text-destructive rounded-lg border p-3">
96+
<p className="text-sm font-medium">Failed to post on X</p>
97+
<p className="text-destructive/80 text-xs">{error.message}</p>
98+
<button
99+
onClick={resetErrorBoundary}
100+
className="text-destructive mt-2 cursor-pointer text-xs hover:underline"
101+
>
102+
Try again
103+
</button>
104+
</div>
105+
)
106+
}
107+
108+
function XPostLinkImpl({ entryCount }: { entryCount: number }) {
109+
const [isPending, startTransition] = useTransition()
110+
const { showBoundary } = useErrorBoundary()
111+
const handlePostOnX = () => {
112+
startTransition(async () => {
113+
try {
114+
const text = `I have ${entryCount} journal ${entryCount === 1 ? 'entry' : 'entries'} in my EpicMe journal! 📝✨`
115+
const url = new URL('https://x.com/intent/post')
116+
url.searchParams.set('text', text)
117+
118+
throw new Error(`Links not yet supported`)
119+
} catch (err) {
120+
showBoundary(err)
121+
}
122+
})
123+
}
124+
125+
return (
126+
<button
127+
onClick={handlePostOnX}
128+
disabled={isPending}
129+
className="flex cursor-pointer items-center gap-2 rounded-lg bg-black px-4 py-2 text-white transition-colors hover:bg-gray-800 disabled:cursor-not-allowed disabled:bg-gray-400"
130+
>
131+
<svg className="h-5 w-5" fill="currentColor" viewBox="0 0 24 24">
132+
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
133+
</svg>
134+
{isPending ? 'Posting...' : 'Post'}
135+
</button>
136+
)
137+
}
138+
139+
function DeleteEntryButton({
140+
entry,
141+
}: {
142+
entry: { id: number; title: string }
143+
}) {
144+
return (
145+
<ErrorBoundary FallbackComponent={DeleteEntryError}>
146+
<DeleteEntryButtonImpl entry={entry} />
147+
</ErrorBoundary>
148+
)
149+
}
150+
151+
function DeleteEntryError({ error, resetErrorBoundary }: FallbackProps) {
152+
return (
153+
<div className="bg-destructive/10 border-destructive/20 text-destructive rounded-lg border p-3">
154+
<p className="text-sm font-medium">Failed to delete entry</p>
155+
<p className="text-destructive/80 text-xs">{error.message}</p>
156+
<button
157+
onClick={resetErrorBoundary}
158+
className="text-destructive mt-2 cursor-pointer text-xs hover:underline"
159+
>
160+
Try again
161+
</button>
162+
</div>
163+
)
164+
}
165+
166+
function DeleteEntryButtonImpl({
167+
entry,
168+
}: {
169+
entry: { id: number; title: string }
170+
}) {
171+
const [isPending, startTransition] = useTransition()
172+
const { doubleCheck, getButtonProps } = useDoubleCheck()
173+
const { showBoundary } = useErrorBoundary()
174+
175+
const handleDelete = () => {
176+
startTransition(async () => {
177+
try {
178+
throw new Error('Calling tools is not yet supported')
179+
} catch (err) {
180+
showBoundary(err)
181+
}
182+
})
183+
}
184+
185+
return (
186+
<button
187+
{...getButtonProps({
188+
onClick: doubleCheck ? handleDelete : undefined,
189+
disabled: isPending,
190+
className: `text-sm font-medium px-3 py-1.5 rounded-md border transition-colors ${
191+
doubleCheck
192+
? 'bg-destructive text-destructive-foreground border-destructive hover:bg-destructive/90'
193+
: 'text-destructive border-destructive/20 hover:bg-destructive/10 hover:border-destructive/40'
194+
} ${isPending ? 'opacity-50 cursor-not-allowed' : ''}`,
195+
})}
196+
>
197+
{isPending ? 'Deleting...' : doubleCheck ? `Confirm?` : 'Delete'}
198+
</button>
199+
)
200+
}
201+
202+
function ViewEntryButton({ entry }: { entry: { id: number; title: string } }) {
203+
return (
204+
<ErrorBoundary FallbackComponent={ViewEntryError}>
205+
<ViewEntryButtonImpl entry={entry} />
206+
</ErrorBoundary>
207+
)
208+
}
209+
210+
function ViewEntryError({ error, resetErrorBoundary }: FallbackProps) {
211+
return (
212+
<div className="bg-destructive/10 border-destructive/20 text-destructive rounded-lg border p-3">
213+
<p className="text-sm font-medium">Failed to view entry</p>
214+
<p className="text-destructive/80 text-xs">{error.message}</p>
215+
<button
216+
onClick={resetErrorBoundary}
217+
className="text-destructive mt-2 cursor-pointer text-xs hover:underline"
218+
>
219+
Try again
220+
</button>
221+
</div>
222+
)
223+
}
224+
225+
function ViewEntryButtonImpl({
226+
entry,
227+
}: {
228+
entry: { id: number; title: string }
229+
}) {
230+
const [isPending, startTransition] = useTransition()
231+
const { showBoundary } = useErrorBoundary()
232+
233+
const handleViewEntry = () => {
234+
startTransition(async () => {
235+
try {
236+
throw new Error('Calling tools is not yet supported')
237+
} catch (err) {
238+
showBoundary(err)
239+
}
240+
})
241+
}
242+
243+
return (
244+
<button
245+
onClick={handleViewEntry}
246+
disabled={isPending}
247+
className="text-primary text-sm font-medium hover:underline disabled:cursor-not-allowed disabled:opacity-50"
248+
>
249+
{isPending ? 'Loading...' : 'View Details'}
250+
</button>
251+
)
252+
}
253+
254+
function SummarizeEntryButton({
255+
entry,
256+
}: {
257+
entry: { id: number; title: string }
258+
}) {
259+
return (
260+
<ErrorBoundary FallbackComponent={SummarizeEntryError}>
261+
<SummarizeEntryButtonImpl entry={entry} />
262+
</ErrorBoundary>
263+
)
264+
}
265+
266+
function SummarizeEntryError({ error, resetErrorBoundary }: FallbackProps) {
267+
return (
268+
<div className="bg-destructive/10 border-destructive/20 text-destructive rounded-lg border p-3">
269+
<p className="text-sm font-medium">Failed to summarize entry</p>
270+
<p className="text-destructive/80 text-xs">{error.message}</p>
271+
<button
272+
onClick={resetErrorBoundary}
273+
className="text-destructive mt-2 cursor-pointer text-xs hover:underline"
274+
>
275+
Try again
276+
</button>
277+
</div>
278+
)
279+
}
280+
281+
function SummarizeEntryButtonImpl({
282+
entry,
283+
}: {
284+
entry: { id: number; title: string }
285+
}) {
286+
const [isPending, startTransition] = useTransition()
287+
const { showBoundary } = useErrorBoundary()
288+
289+
const handleSummarize = () => {
290+
startTransition(async () => {
291+
try {
292+
// Get the full entry content first
293+
throw new Error('Sending prompts is not yet supported')
294+
} catch (err) {
295+
showBoundary(err)
296+
}
297+
})
298+
}
299+
300+
return (
301+
<button
302+
onClick={handleSummarize}
303+
disabled={isPending}
304+
className="text-primary text-sm font-medium hover:underline disabled:cursor-not-allowed disabled:opacity-50"
305+
>
306+
{isPending ? 'Summarizing...' : 'Summarize'}
307+
</button>
308+
)
309+
}

exercises/03.complex-ui/01.solution.iframe/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
},
1818
"dependencies": {
1919
"@epic-web/invariant": "^1.0.0",
20-
"@mcp-ui/server": "^5.2.0",
20+
"@mcp-ui/server": "^5.10.0",
2121
"@modelcontextprotocol/sdk": "^1.17.2",
2222
"agents": "^0.0.111",
2323
"isbot": "^5.1.30",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Ready

0 commit comments

Comments
 (0)