Skip to content

Commit 8c3e52b

Browse files
minihellboyclaude
andcommitted
Configure GitHub Pages deployment for pnpm monorepo
Replace default Next.js workflow with custom pnpm-aware deploy workflow. Add static export config, basePath, and generateStaticParams for all dynamic routes to support GitHub Pages hosting. Co-Authored-By: Claude Opus 4.6 <[email protected]>
1 parent a2b9f6e commit 8c3e52b

7 files changed

Lines changed: 208 additions & 240 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Deploy to GitHub Pages
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: read
10+
pages: write
11+
id-token: write
12+
13+
concurrency:
14+
group: "pages"
15+
cancel-in-progress: false
16+
17+
jobs:
18+
build:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- uses: pnpm/action-setup@v4
24+
with:
25+
version: 9
26+
27+
- uses: actions/setup-node@v4
28+
with:
29+
node-version: 20
30+
cache: pnpm
31+
32+
- name: Install dependencies
33+
run: pnpm install --frozen-lockfile
34+
35+
- name: Setup Pages
36+
uses: actions/configure-pages@v5
37+
38+
- name: Build
39+
env:
40+
NEXT_PUBLIC_BASE_PATH: /browserforge
41+
run: pnpm --filter @browserforge/web build
42+
43+
- name: Add .nojekyll
44+
run: touch apps/web/out/.nojekyll
45+
46+
- name: Upload artifact
47+
uses: actions/upload-pages-artifact@v3
48+
with:
49+
path: apps/web/out
50+
51+
deploy:
52+
environment:
53+
name: github-pages
54+
url: ${{ steps.deployment.outputs.page_url }}
55+
runs-on: ubuntu-latest
56+
needs: build
57+
steps:
58+
- name: Deploy to GitHub Pages
59+
id: deployment
60+
uses: actions/deploy-pages@v4

.github/workflows/nextjs.yml

Lines changed: 0 additions & 93 deletions
This file was deleted.
Lines changed: 10 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,13 @@
1-
"use client";
2-
3-
import React, { Suspense } from "react";
4-
import { useParams } from "next/navigation";
5-
import { ToolLayout } from "@/components/shared/tool-layout";
6-
import { getToolById } from "@/lib/registry/tools";
7-
import { Wrench, Loader2 } from "lucide-react";
8-
9-
function ToolLoading() {
10-
return (
11-
<div className="flex items-center justify-center py-20">
12-
<Loader2 className="h-8 w-8 animate-spin text-green-500" />
13-
</div>
14-
);
1+
import { allTools } from "@/lib/registry/tools";
2+
import ToolContent from "./tool-content";
3+
4+
export function generateStaticParams() {
5+
return allTools.map((tool) => ({
6+
category: tool.category,
7+
tool: tool.id,
8+
}));
159
}
1610

17-
export default function ToolPage() {
18-
const params = useParams();
19-
const toolId = params.tool as string;
20-
const tool = getToolById(toolId);
21-
22-
if (!tool) {
23-
return (
24-
<div className="text-center py-20 space-y-4">
25-
<div className="flex justify-center">
26-
<div className="flex h-16 w-16 items-center justify-center rounded-lg bg-[hsl(var(--surface-2))] border border-border">
27-
<Wrench className="h-8 w-8 text-muted-foreground" />
28-
</div>
29-
</div>
30-
<h1 className="text-2xl font-mono font-bold">Tool not found</h1>
31-
<p className="text-muted-foreground font-mono text-sm">
32-
$ error: tool not found or coming soon
33-
</p>
34-
</div>
35-
);
36-
}
37-
38-
const ToolComponent = tool.component;
39-
40-
return (
41-
<ToolLayout tool={tool}>
42-
{ToolComponent ? (
43-
<Suspense fallback={<ToolLoading />}>
44-
<ToolComponent />
45-
</Suspense>
46-
) : (
47-
<div className="text-center py-12 space-y-3">
48-
<p className="text-muted-foreground font-mono text-sm">
49-
$ status: building... check back soon
50-
</p>
51-
</div>
52-
)}
53-
</ToolLayout>
54-
);
11+
export default function ToolPage({ params }: { params: { category: string; tool: string } }) {
12+
return <ToolContent toolId={params.tool} />;
5513
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"use client";
2+
3+
import React, { Suspense } from "react";
4+
import { ToolLayout } from "@/components/shared/tool-layout";
5+
import { getToolById } from "@/lib/registry/tools";
6+
import { Wrench, Loader2 } from "lucide-react";
7+
8+
function ToolLoading() {
9+
return (
10+
<div className="flex items-center justify-center py-20">
11+
<Loader2 className="h-8 w-8 animate-spin text-green-500" />
12+
</div>
13+
);
14+
}
15+
16+
export default function ToolContent({ toolId }: { toolId: string }) {
17+
const tool = getToolById(toolId);
18+
19+
if (!tool) {
20+
return (
21+
<div className="text-center py-20 space-y-4">
22+
<div className="flex justify-center">
23+
<div className="flex h-16 w-16 items-center justify-center rounded-lg bg-[hsl(var(--surface-2))] border border-border">
24+
<Wrench className="h-8 w-8 text-muted-foreground" />
25+
</div>
26+
</div>
27+
<h1 className="text-2xl font-mono font-bold">Tool not found</h1>
28+
<p className="text-muted-foreground font-mono text-sm">
29+
$ error: tool not found or coming soon
30+
</p>
31+
</div>
32+
);
33+
}
34+
35+
const ToolComponent = tool.component;
36+
37+
return (
38+
<ToolLayout tool={tool}>
39+
{ToolComponent ? (
40+
<Suspense fallback={<ToolLoading />}>
41+
<ToolComponent />
42+
</Suspense>
43+
) : (
44+
<div className="text-center py-12 space-y-3">
45+
<p className="text-muted-foreground font-mono text-sm">
46+
$ status: building... check back soon
47+
</p>
48+
</div>
49+
)}
50+
</ToolLayout>
51+
);
52+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"use client";
2+
3+
import React from "react";
4+
import {
5+
ShieldCheck,
6+
Globe,
7+
Fingerprint,
8+
FileText,
9+
ImageIcon,
10+
ArrowLeftRight,
11+
Type,
12+
Code2,
13+
Sparkles,
14+
Calculator,
15+
TrendingUp,
16+
BarChart4,
17+
} from "lucide-react";
18+
import { ToolCard } from "@/components/shared/tool-card";
19+
import { ToolCategory, CATEGORY_META } from "@/lib/registry/types";
20+
import { getToolsByCategory } from "@/lib/registry/tools";
21+
22+
const CATEGORY_ICONS: Record<ToolCategory, React.ElementType> = {
23+
[ToolCategory.HASH]: ShieldCheck,
24+
[ToolCategory.NETWORK]: Globe,
25+
[ToolCategory.UUID]: Fingerprint,
26+
[ToolCategory.PDF]: FileText,
27+
[ToolCategory.IMAGE]: ImageIcon,
28+
[ToolCategory.DATA]: ArrowLeftRight,
29+
[ToolCategory.TEXT]: Type,
30+
[ToolCategory.DEV]: Code2,
31+
[ToolCategory.AI]: Sparkles,
32+
[ToolCategory.MATH]: Calculator,
33+
[ToolCategory.STATS]: TrendingUp,
34+
[ToolCategory.PLOT]: BarChart4,
35+
};
36+
37+
export default function CategoryContent({ category }: { category: string }) {
38+
const cat = category as ToolCategory;
39+
const meta = CATEGORY_META[cat];
40+
const tools = getToolsByCategory(cat);
41+
const Icon = CATEGORY_ICONS[cat];
42+
43+
if (!meta) {
44+
return (
45+
<div className="text-center py-20">
46+
<h1 className="text-2xl font-mono font-bold">Category not found</h1>
47+
<p className="text-muted-foreground mt-2 font-mono text-sm">
48+
The category you are looking for does not exist.
49+
</p>
50+
</div>
51+
);
52+
}
53+
54+
return (
55+
<div className="space-y-8">
56+
<div className="flex items-center gap-4">
57+
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-[hsl(var(--surface-2))] border border-border">
58+
{Icon && <Icon className={`h-6 w-6 ${meta.color}`} />}
59+
</div>
60+
<div>
61+
<h1 className="text-2xl font-mono font-bold tracking-tight">{meta.label}</h1>
62+
<p className="text-sm text-muted-foreground font-sans">{meta.description}</p>
63+
<p className="text-xs text-muted-foreground font-mono mt-0.5">
64+
{tools.length} tools available
65+
</p>
66+
</div>
67+
</div>
68+
69+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3">
70+
{tools.map((tool) => (
71+
<ToolCard key={tool.id} tool={tool} />
72+
))}
73+
</div>
74+
</div>
75+
);
76+
}

0 commit comments

Comments
 (0)