Skip to content

Commit 3b9b4d5

Browse files
authored
Merge pull request #9 from JackTench/readme
Deploy readme to homepage
2 parents 4a94ea2 + 475418c commit 3b9b4d5

File tree

11 files changed

+1494
-4
lines changed

11 files changed

+1494
-4
lines changed

package-lock.json

Lines changed: 980 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,15 @@
1717
"class-variance-authority": "^0.7.1",
1818
"clsx": "^2.1.1",
1919
"lucide-react": "^0.548.0",
20+
"mdast-util-from-markdown": "^2.0.2",
21+
"mdast-util-gfm": "^3.1.0",
22+
"micromark-extension-gfm": "^3.0.0",
2023
"next-themes": "^0.4.6",
2124
"react": "^19.1.1",
25+
"react-code-block": "^1.1.3",
2226
"react-dom": "^19.1.1",
2327
"react-router": "^7.9.4",
28+
"react-spinners": "^0.17.0",
2429
"tailwind-merge": "^3.3.1"
2530
},
2631
"devDependencies": {

src/app.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export default function App() {
77
<div>
88
<AppNav />
99
</div>
10-
<div>
10+
<div className="px-2">
1111
<AppRouter />
1212
</div>
1313
</div>

src/app/home.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import { AppGitHubReadme } from "@/components/libresplit/AppGitHubReadme";
2+
13
export function Home() {
24
return (
35
<div>
4-
<h1>LibreSplit</h1>
6+
<AppGitHubReadme />
57
</div>
68
);
79
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { useEffect, useState } from "react";
2+
3+
import { AppLoading } from "./AppLoading";
4+
import { Markdown } from "@/lib/markdown";
5+
6+
export function AppGitHubReadme() {
7+
const [readme, setReadme] = useState<string>("Loading...");
8+
const [isLoading, setIsLoading] = useState(true);
9+
10+
const urlReadme =
11+
"https://raw.githubusercontent.com/LibreSplit/LibreSplit/refs/heads/main/README.md";
12+
13+
// Fetch markdown from GitHub page for LibreSplit, place into the readme state.
14+
useEffect(() => {
15+
fetch(urlReadme)
16+
.then((res) => {
17+
if (!res.ok) throw new Error(`HTTP Error! Status: ${res.status}`);
18+
return res.text();
19+
})
20+
.then((text) => setReadme(text))
21+
.catch(() => setReadme("Failed to load README from GitHub."))
22+
.finally(() => setIsLoading(false));
23+
}, []);
24+
25+
if (isLoading) {
26+
return <AppLoading />;
27+
}
28+
29+
return (
30+
<div>
31+
<Markdown content={readme} />
32+
</div>
33+
);
34+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { BarLoader } from "react-spinners";
2+
3+
export function AppLoading() {
4+
return (
5+
<div className="flex h-screen w-screen items-center justify-center">
6+
<BarLoader color={"#00ff00"} />
7+
</div>
8+
);
9+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Card, CardContent } from "../ui/card";
2+
import { CodeBlock } from "react-code-block";
3+
4+
export type AppMarkdownCodeBlockProps = {
5+
code: string;
6+
language?: string;
7+
};
8+
9+
export function AppMarkdownCodeBlock({
10+
code,
11+
language,
12+
}: AppMarkdownCodeBlockProps) {
13+
return (
14+
<div className="w-fit px-2">
15+
<Card className="w-fit">
16+
<CardContent className="w-fit">
17+
<CodeBlock code={code} language={language ?? "text"}>
18+
<CodeBlock.Code>
19+
<CodeBlock.LineContent>
20+
<CodeBlock.Token />
21+
</CodeBlock.LineContent>
22+
</CodeBlock.Code>
23+
</CodeBlock>
24+
</CardContent>
25+
</Card>
26+
</div>
27+
);
28+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import {
2+
Table,
3+
TableBody,
4+
TableCaption,
5+
TableCell,
6+
TableHead,
7+
TableHeader,
8+
TableRow,
9+
} from "../ui/table";
10+
11+
type Align = "left" | "center" | "right" | null;
12+
13+
type MarkdownTableCell = {
14+
type: "tableCell";
15+
children?: any[];
16+
};
17+
18+
type MarkdownTableRow = {
19+
type: "tableRow";
20+
children: MarkdownTableCell[];
21+
};
22+
23+
type MarkdownTable = {
24+
type: "table";
25+
align?: Align[];
26+
children: MarkdownTableRow[];
27+
};
28+
29+
export function AppMarkdownTable({
30+
node,
31+
renderChildren,
32+
caption,
33+
className,
34+
}: {
35+
node: MarkdownTable;
36+
renderChildren: (n: any) => React.ReactNode | null;
37+
caption?: React.ReactNode;
38+
className?: string;
39+
}) {
40+
const align = node.align || [];
41+
const [headerRow, ...bodyRows] = node.children || [];
42+
43+
const alignClass = (idx: number) => {
44+
const a = align[idx];
45+
if (a === "center") return "text-center";
46+
if (a === "right") return "text-right";
47+
return "text-left";
48+
};
49+
50+
return (
51+
<Table className={className ?? "my-4"}>
52+
{caption ? <TableCaption>{caption}</TableCaption> : null}
53+
54+
{headerRow ? (
55+
<TableHeader>
56+
<TableRow>
57+
{headerRow.children.map((cell, i) => (
58+
<TableHead key={i} className={alignClass(i)}>
59+
{renderChildren(cell)}
60+
</TableHead>
61+
))}
62+
</TableRow>
63+
</TableHeader>
64+
) : null}
65+
66+
<TableBody>
67+
{bodyRows.map((row, rIdx) => (
68+
<TableRow key={rIdx}>
69+
{row.children.map((cell, cIdx) => (
70+
<TableCell key={cIdx} className={alignClass(cIdx)}>
71+
{renderChildren(cell)}
72+
</TableCell>
73+
))}
74+
</TableRow>
75+
))}
76+
</TableBody>
77+
</Table>
78+
);
79+
}

src/components/ui/card.tsx

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import * as React from "react";
2+
3+
import { cn } from "@/lib/utils";
4+
5+
function Card({ className, ...props }: React.ComponentProps<"div">) {
6+
return (
7+
<div
8+
data-slot="card"
9+
className={cn(
10+
"flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm",
11+
className,
12+
)}
13+
{...props}
14+
/>
15+
);
16+
}
17+
18+
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
19+
return (
20+
<div
21+
data-slot="card-header"
22+
className={cn(
23+
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
24+
className,
25+
)}
26+
{...props}
27+
/>
28+
);
29+
}
30+
31+
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
32+
return (
33+
<div
34+
data-slot="card-title"
35+
className={cn("leading-none font-semibold", className)}
36+
{...props}
37+
/>
38+
);
39+
}
40+
41+
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
42+
return (
43+
<div
44+
data-slot="card-description"
45+
className={cn("text-sm text-muted-foreground", className)}
46+
{...props}
47+
/>
48+
);
49+
}
50+
51+
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
52+
return (
53+
<div
54+
data-slot="card-action"
55+
className={cn(
56+
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
57+
className,
58+
)}
59+
{...props}
60+
/>
61+
);
62+
}
63+
64+
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
65+
return (
66+
<div
67+
data-slot="card-content"
68+
className={cn("px-6", className)}
69+
{...props}
70+
/>
71+
);
72+
}
73+
74+
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
75+
return (
76+
<div
77+
data-slot="card-footer"
78+
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
79+
{...props}
80+
/>
81+
);
82+
}
83+
84+
export {
85+
Card,
86+
CardHeader,
87+
CardFooter,
88+
CardTitle,
89+
CardAction,
90+
CardDescription,
91+
CardContent,
92+
};

src/components/ui/table.tsx

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import * as React from "react";
2+
3+
import { cn } from "@/lib/utils";
4+
5+
function Table({ className, ...props }: React.ComponentProps<"table">) {
6+
return (
7+
<div
8+
data-slot="table-container"
9+
className="relative w-full overflow-x-auto"
10+
>
11+
<table
12+
data-slot="table"
13+
className={cn("w-full caption-bottom text-sm", className)}
14+
{...props}
15+
/>
16+
</div>
17+
);
18+
}
19+
20+
function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
21+
return (
22+
<thead
23+
data-slot="table-header"
24+
className={cn("[&_tr]:border-b", className)}
25+
{...props}
26+
/>
27+
);
28+
}
29+
30+
function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
31+
return (
32+
<tbody
33+
data-slot="table-body"
34+
className={cn("[&_tr:last-child]:border-0", className)}
35+
{...props}
36+
/>
37+
);
38+
}
39+
40+
function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
41+
return (
42+
<tfoot
43+
data-slot="table-footer"
44+
className={cn(
45+
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
46+
className,
47+
)}
48+
{...props}
49+
/>
50+
);
51+
}
52+
53+
function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
54+
return (
55+
<tr
56+
data-slot="table-row"
57+
className={cn(
58+
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
59+
className,
60+
)}
61+
{...props}
62+
/>
63+
);
64+
}
65+
66+
function TableHead({ className, ...props }: React.ComponentProps<"th">) {
67+
return (
68+
<th
69+
data-slot="table-head"
70+
className={cn(
71+
"h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
72+
className,
73+
)}
74+
{...props}
75+
/>
76+
);
77+
}
78+
79+
function TableCell({ className, ...props }: React.ComponentProps<"td">) {
80+
return (
81+
<td
82+
data-slot="table-cell"
83+
className={cn(
84+
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
85+
className,
86+
)}
87+
{...props}
88+
/>
89+
);
90+
}
91+
92+
function TableCaption({
93+
className,
94+
...props
95+
}: React.ComponentProps<"caption">) {
96+
return (
97+
<caption
98+
data-slot="table-caption"
99+
className={cn("mt-4 text-sm text-muted-foreground", className)}
100+
{...props}
101+
/>
102+
);
103+
}
104+
105+
export {
106+
Table,
107+
TableHeader,
108+
TableBody,
109+
TableFooter,
110+
TableHead,
111+
TableRow,
112+
TableCell,
113+
TableCaption,
114+
};

0 commit comments

Comments
 (0)