Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e8c84bf
Pull markdown from GitHub.
JackTench Oct 27, 2025
ca1fe64
Install react-markdown
JackTench Oct 27, 2025
a3cfc8a
Parse with react-markdown.
JackTench Oct 27, 2025
525340a
POC: Parsing markdown and applying styles/components manually without…
JackTench Oct 28, 2025
1249cc6
Handle headings
JackTench Oct 28, 2025
3b6ba62
Handle different header # differently.
JackTench Oct 28, 2025
b609080
Remove h1 from home page
JackTench Oct 28, 2025
6fa4c36
Refactor function name
JackTench Oct 28, 2025
91934b5
Add default case for headers to stop build error
JackTench Oct 28, 2025
4bd11db
Change text back to white
JackTench Oct 28, 2025
0a5fe65
Handle links in markdown
JackTench Oct 28, 2025
cfcc688
POC: Creating code blocks
JackTench Oct 28, 2025
a41aa95
Sync lockfile
JackTench Oct 28, 2025
b29f0cd
Use react-code-block to format code blocks
JackTench Oct 28, 2025
7821508
Handle lists (and inlineCode)
JackTench Oct 28, 2025
9b7b1f6
Helper function for rendering
JackTench Oct 28, 2025
7536e14
Handle bold and italic text
JackTench Oct 28, 2025
dfd8b63
Thematic breaks and block quotes
JackTench Oct 28, 2025
6693e10
[shadcn/ui] Add table.
JackTench Oct 28, 2025
9b1d62a
Install extensions for table mapping
JackTench Oct 28, 2025
ffd040c
Create helper component for structuring shadcn/ui tables.
JackTench Oct 28, 2025
2a71669
Handle tables.
JackTench Oct 28, 2025
e6405dc
Render images by catching html cases
JackTench Oct 29, 2025
3b0c6db
Install react-spinners
JackTench Oct 29, 2025
51173dc
Implement useState for page loading
JackTench Oct 29, 2025
5d91023
Implement loading bar
JackTench Oct 29, 2025
e048363
Change loading bar to green
JackTench Oct 29, 2025
06992d9
Center h1 tags on screen
JackTench Oct 29, 2025
4001fc8
Make hyperlinks blue
JackTench Oct 29, 2025
b532f3f
Render space around components
JackTench Oct 29, 2025
bb24f0a
Adjust font weight
JackTench Oct 29, 2025
7ca4ceb
Add left/right padding
JackTench Oct 29, 2025
6b80fa9
Refactor CodeBlock into it's own component for redesign
JackTench Oct 29, 2025
4597c2b
[shadcn/ui] Install card
JackTench Oct 29, 2025
475418c
Redesign code blocks
JackTench Oct 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
982 changes: 980 additions & 2 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.548.0",
"mdast-util-from-markdown": "^2.0.2",
"mdast-util-gfm": "^3.1.0",
"micromark-extension-gfm": "^3.0.0",
"next-themes": "^0.4.6",
"react": "^19.1.1",
"react-code-block": "^1.1.3",
"react-dom": "^19.1.1",
"react-router": "^7.9.4",
"react-spinners": "^0.17.0",
"tailwind-merge": "^3.3.1"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default function App() {
<div>
<AppNav />
</div>
<div>
<div className="px-2">
<AppRouter />
</div>
</div>
Expand Down
4 changes: 3 additions & 1 deletion src/app/home.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { AppGitHubReadme } from "@/components/libresplit/AppGitHubReadme";

export function Home() {
return (
<div>
<h1>LibreSplit</h1>
<AppGitHubReadme />
</div>
);
}
34 changes: 34 additions & 0 deletions src/components/libresplit/AppGitHubReadme.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useEffect, useState } from "react";

import { AppLoading } from "./AppLoading";
import { Markdown } from "@/lib/markdown";

export function AppGitHubReadme() {
const [readme, setReadme] = useState<string>("Loading...");
const [isLoading, setIsLoading] = useState(true);

const urlReadme =
"https://raw.githubusercontent.com/LibreSplit/LibreSplit/refs/heads/main/README.md";

// Fetch markdown from GitHub page for LibreSplit, place into the readme state.
useEffect(() => {
fetch(urlReadme)
.then((res) => {
if (!res.ok) throw new Error(`HTTP Error! Status: ${res.status}`);
return res.text();
})
.then((text) => setReadme(text))
.catch(() => setReadme("Failed to load README from GitHub."))
.finally(() => setIsLoading(false));
}, []);

if (isLoading) {
return <AppLoading />;
}

return (
<div>
<Markdown content={readme} />
</div>
);
}
9 changes: 9 additions & 0 deletions src/components/libresplit/AppLoading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { BarLoader } from "react-spinners";

export function AppLoading() {
return (
<div className="flex h-screen w-screen items-center justify-center">
<BarLoader color={"#00ff00"} />
</div>
);
}
28 changes: 28 additions & 0 deletions src/components/libresplit/AppMarkdownCodeBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Card, CardContent } from "../ui/card";
import { CodeBlock } from "react-code-block";

export type AppMarkdownCodeBlockProps = {
code: string;
language?: string;
};

export function AppMarkdownCodeBlock({
code,
language,
}: AppMarkdownCodeBlockProps) {
return (
<div className="w-fit px-2">
<Card className="w-fit">
<CardContent className="w-fit">
<CodeBlock code={code} language={language ?? "text"}>
<CodeBlock.Code>
<CodeBlock.LineContent>
<CodeBlock.Token />
</CodeBlock.LineContent>
</CodeBlock.Code>
</CodeBlock>
</CardContent>
</Card>
</div>
);
}
79 changes: 79 additions & 0 deletions src/components/libresplit/AppMarkdownTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "../ui/table";

type Align = "left" | "center" | "right" | null;

type MarkdownTableCell = {
type: "tableCell";
children?: any[];
};

type MarkdownTableRow = {
type: "tableRow";
children: MarkdownTableCell[];
};

type MarkdownTable = {
type: "table";
align?: Align[];
children: MarkdownTableRow[];
};

export function AppMarkdownTable({
node,
renderChildren,
caption,
className,
}: {
node: MarkdownTable;
renderChildren: (n: any) => React.ReactNode | null;
caption?: React.ReactNode;
className?: string;
}) {
const align = node.align || [];
const [headerRow, ...bodyRows] = node.children || [];

const alignClass = (idx: number) => {
const a = align[idx];
if (a === "center") return "text-center";
if (a === "right") return "text-right";
return "text-left";
};

return (
<Table className={className ?? "my-4"}>
{caption ? <TableCaption>{caption}</TableCaption> : null}

{headerRow ? (
<TableHeader>
<TableRow>
{headerRow.children.map((cell, i) => (
<TableHead key={i} className={alignClass(i)}>
{renderChildren(cell)}
</TableHead>
))}
</TableRow>
</TableHeader>
) : null}

<TableBody>
{bodyRows.map((row, rIdx) => (
<TableRow key={rIdx}>
{row.children.map((cell, cIdx) => (
<TableCell key={cIdx} className={alignClass(cIdx)}>
{renderChildren(cell)}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
);
}
92 changes: 92 additions & 0 deletions src/components/ui/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import * as React from "react";

import { cn } from "@/lib/utils";

function Card({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card"
className={cn(
"flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm",
className,
)}
{...props}
/>
);
}

function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-header"
className={cn(
"@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",
className,
)}
{...props}
/>
);
}

function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-title"
className={cn("leading-none font-semibold", className)}
{...props}
/>
);
}

function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-description"
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
);
}

function CardAction({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className,
)}
{...props}
/>
);
}

function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
className={cn("px-6", className)}
{...props}
/>
);
}

function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-footer"
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
{...props}
/>
);
}

export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
};
114 changes: 114 additions & 0 deletions src/components/ui/table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import * as React from "react";

import { cn } from "@/lib/utils";

function Table({ className, ...props }: React.ComponentProps<"table">) {
return (
<div
data-slot="table-container"
className="relative w-full overflow-x-auto"
>
<table
data-slot="table"
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
);
}

function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
return (
<thead
data-slot="table-header"
className={cn("[&_tr]:border-b", className)}
{...props}
/>
);
}

function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
return (
<tbody
data-slot="table-body"
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
);
}

function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
return (
<tfoot
data-slot="table-footer"
className={cn(
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
className,
)}
{...props}
/>
);
}

function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
return (
<tr
data-slot="table-row"
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className,
)}
{...props}
/>
);
}

function TableHead({ className, ...props }: React.ComponentProps<"th">) {
return (
<th
data-slot="table-head"
className={cn(
"h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className,
)}
{...props}
/>
);
}

function TableCell({ className, ...props }: React.ComponentProps<"td">) {
return (
<td
data-slot="table-cell"
className={cn(
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className,
)}
{...props}
/>
);
}

function TableCaption({
className,
...props
}: React.ComponentProps<"caption">) {
return (
<caption
data-slot="table-caption"
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
);
}

export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
};
Loading