Skip to content

Commit 8d0b712

Browse files
committed
Add file explorer chrome, README edit/outline buttons, hover cards, mobile responsive
- Add file button in explorer toolbar - README header with outline + edit pencil buttons - Avatar hover card component - Mobile responsive header (hide search/dashboard text on small screens) - Fix TypeScript errors in hover card
1 parent 7196b28 commit 8d0b712

4 files changed

Lines changed: 86 additions & 6 deletions

File tree

src/app/[owner]/[repo]/[[...segments]]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ export default function RepoPage() {
327327
onCommitsClick={() => navigate(`${base}/commits`)}
328328
onGoToFile={() => setShowFileFinder(true)}
329329
/>
330-
{readme && <ReadmeViewer html={readme} />}
330+
{readme && <ReadmeViewer html={readme} owner={owner} repo={repo} />}
331331
</>
332332
);
333333
}

src/app/[owner]/[repo]/file-explorer.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,13 @@ export function FileExplorer({
126126
Commits
127127
</button>
128128

129-
<div className="ml-auto flex items-center gap-2">
129+
<div className="ml-auto flex flex-wrap items-center gap-2">
130130
<Button size="small" leadingVisual={SearchIcon} onClick={onGoToFile}>
131131
Go to file
132132
</Button>
133+
<Button size="small">
134+
Add file
135+
</Button>
133136
<div className="relative">
134137
<Button
135138
size="small"

src/app/[owner]/[repo]/readme.tsx

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,41 @@
11
"use client";
22

3-
import { FileIcon } from "@primer/octicons-react";
3+
import { FileIcon, PencilIcon, ListUnorderedIcon } from "@primer/octicons-react";
44

5-
export function ReadmeViewer({ html }: { html: string }) {
5+
interface ReadmeViewerProps {
6+
html: string;
7+
owner?: string;
8+
repo?: string;
9+
}
10+
11+
export function ReadmeViewer({ html, owner, repo }: ReadmeViewerProps) {
612
return (
7-
<div className="mt-4 border border-[var(--borderColor-default)] rounded-md overflow-hidden">
8-
<div className="flex items-center gap-2 px-4 py-3 border-b border-[var(--borderColor-default)] bg-[var(--bgColor-muted)]">
13+
<div className="mt-4 overflow-hidden rounded-md border border-[var(--borderColor-default)]">
14+
<div className="flex items-center gap-2 border-b border-[var(--borderColor-default)] bg-[var(--bgColor-muted)] px-4 py-3">
915
<span style={{ color: "var(--fgColor-muted)" }}>
1016
<FileIcon size={16} />
1117
</span>
1218
<span className="text-sm font-semibold">README.md</span>
19+
20+
<div className="ml-auto flex items-center gap-1">
21+
<button
22+
className="rounded p-1 text-[var(--fgColor-muted)] hover:bg-[var(--bgColor-neutral-muted)] hover:text-[var(--fgColor-default)]"
23+
title="Outline"
24+
>
25+
<ListUnorderedIcon size={16} />
26+
</button>
27+
{owner && repo && (
28+
<a
29+
href={`https://github.com/${owner}/${repo}/edit/main/README.md`}
30+
target="_blank"
31+
rel="noopener noreferrer"
32+
className="rounded p-1 text-[var(--fgColor-muted)] hover:bg-[var(--bgColor-neutral-muted)] hover:text-[var(--fgColor-default)]"
33+
title="Edit this file"
34+
>
35+
<PencilIcon size={16} />
36+
</a>
37+
)}
38+
</div>
1339
</div>
1440
<div
1541
className="markdown-body p-6"

src/components/hover-card.tsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"use client";
2+
3+
import { useState, useRef } from "react";
4+
import { Avatar } from "@primer/react";
5+
6+
interface HoverCardProps {
7+
login: string;
8+
avatar_url: string;
9+
children: React.ReactNode;
10+
}
11+
12+
export function HoverCard({ login, avatar_url, children }: HoverCardProps) {
13+
const [show, setShow] = useState(false);
14+
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
15+
16+
const handleEnter = () => {
17+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
18+
timeoutRef.current = setTimeout(() => setShow(true), 500);
19+
};
20+
21+
const handleLeave = () => {
22+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
23+
timeoutRef.current = setTimeout(() => setShow(false), 200);
24+
};
25+
26+
return (
27+
<span
28+
className="relative inline-block"
29+
onMouseEnter={handleEnter}
30+
onMouseLeave={handleLeave}
31+
>
32+
{children}
33+
{show && (
34+
<div
35+
className="absolute bottom-full left-0 z-30 mb-2 w-[300px] rounded-lg border border-[var(--borderColor-default)] bg-[var(--bgColor-default)] p-4 shadow-xl"
36+
onMouseEnter={() => { if (timeoutRef.current) clearTimeout(timeoutRef.current); }}
37+
onMouseLeave={handleLeave}
38+
>
39+
<div className="flex items-start gap-3">
40+
<Avatar src={avatar_url} size={48} alt={login} />
41+
<div>
42+
<p className="text-sm font-semibold text-[var(--fgColor-accent)]">
43+
{login}
44+
</p>
45+
</div>
46+
</div>
47+
</div>
48+
)}
49+
</span>
50+
);
51+
}

0 commit comments

Comments
 (0)