@@ -3,6 +3,7 @@ import { Link } from '@tanstack/react-router'
33import {
44 AlertCircle ,
55 ArrowLeft ,
6+ BookOpen ,
67 Copy ,
78 Download ,
89 ExternalLink ,
@@ -14,9 +15,11 @@ import { toast } from 'sonner'
1415
1516import { CodeFileViewer } from '@/components/code-file-viewer'
1617import { FileIcon } from '@/components/file-icon'
18+ import { ProjectReadme } from '@/components/project-readme'
1719import { RepoBreadcrumb } from '@/components/repo-breadcrumb'
1820import { Alert , AlertDescription , AlertTitle } from '@/components/ui/alert'
1921import { Button } from '@/components/ui/button'
22+ import { Skeleton } from '@/components/ui/skeleton'
2023import {
2124 Tooltip ,
2225 TooltipContent ,
@@ -45,12 +48,14 @@ type GitHubRepoBrowserProps = {
4548 repo : string
4649 file ?: string
4750 path ?: string
51+ source ?: boolean
4852}
4953
5054export function GitHubRepoBrowser ( {
5155 repo,
5256 file,
5357 path,
58+ source,
5459} : GitHubRepoBrowserProps ) {
5560 const owner = GITHUB_OWNER
5661 const curated = findCuratedProject ( repo )
@@ -121,6 +126,15 @@ export function GitHubRepoBrowser({
121126 ( ) => buildRepoListing ( files , currentPath ) ,
122127 [ files , currentPath ] ,
123128 )
129+ const readmeFile = useMemo (
130+ ( ) =>
131+ files . find (
132+ ( candidate ) =>
133+ ! candidate . path . includes ( '/' ) &&
134+ / ^ r e a d m e (?: \. (?: m d | m d x | m a r k d o w n ) ) ? $ / i. test ( candidate . name ) ,
135+ ) ?? null ,
136+ [ files ] ,
137+ )
124138
125139 const rawUrl = useMemo ( ( ) => {
126140 if ( ! activeFile ) return null
@@ -270,7 +284,11 @@ export function GitHubRepoBrowser({
270284 label : repo ,
271285 href : '/repos/$repo' ,
272286 params : { repo } ,
273- search : { file : undefined , path : undefined } ,
287+ search : {
288+ file : undefined ,
289+ path : undefined ,
290+ source : source ? true : undefined ,
291+ } ,
274292 } ,
275293 ]
276294
@@ -284,7 +302,7 @@ export function GitHubRepoBrowser({
284302 label : part ,
285303 href : '/repos/$repo' ,
286304 params : { repo } ,
287- search : { file : undefined , path : runningPath } ,
305+ search : { file : undefined , path : runningPath , source : true } ,
288306 } )
289307 }
290308
@@ -297,15 +315,107 @@ export function GitHubRepoBrowser({
297315 }
298316
299317 return segments
300- } , [ currentPath , activeFile , repo ] )
318+ } , [ currentPath , activeFile , repo , source ] )
319+
320+ if ( ! source && ! selectedPath ) {
321+ return (
322+ < div className = "flex min-h-0 flex-1 flex-col" >
323+ < main className = "mx-auto w-full max-w-5xl flex-1 px-4 py-3 sm:px-6 sm:py-4 lg:px-8" >
324+ < div className = "flex flex-col gap-4" >
325+ < header className = "grid gap-3 border-b border-border/70 pb-3 sm:grid-cols-[minmax(0,1fr)_auto] sm:items-center" >
326+ < div className = "flex min-w-0 items-start gap-2" >
327+ < Button
328+ asChild
329+ variant = "ghost"
330+ size = "icon-sm"
331+ className = "mt-0.5 text-muted-foreground"
332+ >
333+ < Link to = "/" aria-label = "Back to work" >
334+ < ArrowLeft />
335+ </ Link >
336+ </ Button >
337+ < div className = "min-w-0" >
338+ < h1 className = "font-display text-2xl font-semibold tracking-tight text-balance" >
339+ { title }
340+ </ h1 >
341+ < p className = "mt-1 max-w-3xl text-sm leading-relaxed text-muted-foreground" >
342+ { description }
343+ </ p >
344+ </ div >
345+ </ div >
346+
347+ < div className = "flex shrink-0" >
348+ < Button asChild size = "sm" >
349+ < a href = { githubRepoUrl } target = "_blank" rel = "noreferrer" >
350+ < ExternalLink data-icon = "inline-start" />
351+ View source
352+ </ a >
353+ </ Button >
354+ </ div >
355+ </ header >
356+
357+ < section className = "mx-auto flex w-full max-w-3xl flex-col gap-6" >
358+ { treeError ? (
359+ < Alert >
360+ < AlertTitle > Project details unavailable</ AlertTitle >
361+ < AlertDescription > { treeError } </ AlertDescription >
362+ </ Alert >
363+ ) : isLoadingTree ? (
364+ < div
365+ className = "flex flex-col gap-4 py-4"
366+ aria-label = "Loading project details"
367+ >
368+ < Skeleton className = "h-8 w-2/3" />
369+ < Skeleton className = "h-4 w-full" />
370+ < Skeleton className = "h-4 w-5/6" />
371+ < Skeleton className = "h-24 w-full" />
372+ </ div >
373+ ) : readmeFile ? (
374+ < ProjectReadme
375+ owner = { owner }
376+ repo = { repo }
377+ branch = { branch }
378+ path = { readmeFile . path }
379+ title = { title }
380+ />
381+ ) : (
382+ < Alert >
383+ < AlertTitle > No project details found</ AlertTitle >
384+ < AlertDescription >
385+ Use View source to open this project on GitHub.
386+ </ AlertDescription >
387+ </ Alert >
388+ ) }
389+ </ section >
390+ </ div >
391+ </ main >
392+ </ div >
393+ )
394+ }
301395
302396 if ( ! selectedPath ) {
303397 return (
304398 < div className = "flex min-h-0 flex-1 flex-col" >
305399 < main className = "mx-auto w-full max-w-7xl flex-1 px-4 py-6 sm:px-6 sm:py-8 lg:px-8 lg:py-10" >
306400 < div className = "flex flex-col gap-8" >
307401 < header className = "flex flex-col gap-4" >
308- < RepoBreadcrumb segments = { breadcrumbSegments } />
402+ < div className = "flex flex-wrap items-center justify-between gap-3" >
403+ < RepoBreadcrumb segments = { breadcrumbSegments } />
404+ < Button asChild variant = "outline" size = "sm" >
405+ < Link
406+ to = "/repos/$repo"
407+ params = { { repo } }
408+ search = { {
409+ file : undefined ,
410+ path : undefined ,
411+ source : undefined ,
412+ } }
413+ >
414+ < BookOpen data-icon = "inline-start" />
415+ Project overview
416+ </ Link >
417+ </ Button >
418+ </ div >
309419 < div className = "flex flex-col gap-2" >
310420 < div className = "flex flex-wrap items-center gap-3" >
311421 < h1 className = "font-display text-2xl font-semibold tracking-tight sm:text-3xl" >
@@ -349,7 +459,11 @@ export function GitHubRepoBrowser({
349459 key = { folder . path }
350460 to = "/repos/$repo"
351461 params = { { repo } }
352- search = { { file : undefined , path : folder . path } }
462+ search = { {
463+ file : undefined ,
464+ path : folder . path ,
465+ source : true ,
466+ } }
353467 className = "catalog-row group"
354468 >
355469 < span className = "catalog-caret" aria-hidden />
@@ -369,7 +483,11 @@ export function GitHubRepoBrowser({
369483 key = { item . path }
370484 to = "/repos/$repo"
371485 params = { { repo } }
372- search = { { file : item . path , path : undefined } }
486+ search = { {
487+ file : item . path ,
488+ path : undefined ,
489+ source : true ,
490+ } }
373491 className = "catalog-row group"
374492 >
375493 < span className = "catalog-caret" aria-hidden />
@@ -429,7 +547,11 @@ export function GitHubRepoBrowser({
429547 < Link
430548 to = "/repos/$repo"
431549 params = { { repo } }
432- search = { { file : undefined , path : currentPath || undefined } }
550+ search = { {
551+ file : undefined ,
552+ path : currentPath || undefined ,
553+ source : true ,
554+ } }
433555 >
434556 < ArrowLeft />
435557 < span className = "hidden sm:inline" > Back</ span >
0 commit comments