diff --git a/.env.sample b/.env.sample
new file mode 100644
index 0000000..b33e72d
--- /dev/null
+++ b/.env.sample
@@ -0,0 +1 @@
+GITHUB_TOKEN=github_pat_xxxxxxxxxxxxxx
diff --git a/.gitignore b/.gitignore
index 0b304ce..21065ee 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,4 +45,10 @@ next-env.d.ts
.source
# package管理
-.package-lock.json
\ No newline at end of file
+.package-lock.json
+
+# Agents.md
+Agents.md
+
+# Environment variables
+.env
\ No newline at end of file
diff --git a/app/components/Contributors.tsx b/app/components/Contributors.tsx
new file mode 100644
index 0000000..f41aebd
--- /dev/null
+++ b/app/components/Contributors.tsx
@@ -0,0 +1,47 @@
+import Image from "next/image";
+import Link from "next/link";
+
+interface Contributor {
+ login: string;
+ avatar_url: string;
+ html_url: string;
+}
+
+export function Contributors({
+ contributors,
+}: {
+ contributors: Contributor[];
+}) {
+ if (contributors.length === 0) {
+ return null;
+ }
+
+ return (
+
+
+ 贡献者
+
+ {contributors.map((contributor) => (
+ -
+
+
+ {contributor.login}
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/app/docs/[...slug]/page.tsx b/app/docs/[...slug]/page.tsx
index f243b8a..a71654f 100644
--- a/app/docs/[...slug]/page.tsx
+++ b/app/docs/[...slug]/page.tsx
@@ -4,6 +4,8 @@ import { notFound } from "next/navigation";
import type { Metadata } from "next";
import { getMDXComponents } from "@/mdx-components";
import { GiscusComments } from "@/app/components/GiscusComments";
+import { getContributors } from "@/lib/github";
+import { Contributors } from "@/app/components/Contributors";
interface Param {
params: Promise<{
@@ -19,6 +21,12 @@ export default async function DocPage({ params }: Param) {
notFound();
}
+ // Get file path for contributors
+ const filePath = "app/docs/" + page.file.path;
+
+ // Fetch contributors data on server side
+ const contributors = await getContributors(filePath);
+
const Mdx = page.data.body;
return (
@@ -28,6 +36,7 @@ export default async function DocPage({ params }: Param) {
{page.data.title}
+
diff --git a/app/globals.css b/app/globals.css
index 405c963..1aa5565 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -203,25 +203,6 @@
--tracking-normal: 0em;
}
-/* Force remove rounded corners globally for research style */
-@layer base {
- .rounded,
- .rounded-sm,
- .rounded-md,
- .rounded-lg,
- .rounded-xl,
- .rounded-2xl,
- .rounded-3xl,
- .rounded-full,
- .rounded-none {
- border-radius: 0 !important;
- }
-}
-
-/*
- ---break---
-*/
-
.dark {
--background: oklch(0.2077 0.0398 265.7549);
--foreground: oklch(0.9288 0.0126 255.5078);
diff --git a/lib/github.ts b/lib/github.ts
new file mode 100644
index 0000000..bb248ec
--- /dev/null
+++ b/lib/github.ts
@@ -0,0 +1,57 @@
+import { cache } from "react";
+
+// Define contributor data structure
+interface Contributor {
+ login: string;
+ avatar_url: string;
+ html_url: string;
+}
+
+// Use React's cache function for request caching and deduplication
+export const getContributors = cache(
+ async (filePath: string): Promise => {
+ try {
+ // Use GITHUB_TOKEN environment variable for authorization to increase API rate limit
+ const headers: HeadersInit = {};
+ if (process.env.GITHUB_TOKEN) {
+ headers.Authorization = `token ${process.env.GITHUB_TOKEN}`;
+ }
+
+ const response = await fetch(
+ `https://api.github.com/repos/InvolutionHell/involutionhell.github.io/commits?path=${filePath}`,
+ {
+ headers,
+ // Use next.revalidate to configure cache strategy (e.g., revalidate every hour)
+ next: { revalidate: 3600 },
+ },
+ );
+
+ if (!response.ok) {
+ // If request fails, return empty array
+ console.error(
+ `Failed to fetch contributors for ${filePath}: ${response.statusText}`,
+ );
+ return [];
+ }
+
+ const commits = await response.json();
+
+ // Use Map to deduplicate contributors
+ const uniqueContributors = new Map();
+ commits.forEach((commit: { author?: Contributor }) => {
+ if (commit.author) {
+ uniqueContributors.set(commit.author.login, {
+ login: commit.author.login,
+ avatar_url: commit.author.avatar_url,
+ html_url: commit.author.html_url,
+ });
+ }
+ });
+
+ return Array.from(uniqueContributors.values());
+ } catch (error) {
+ console.error(`Error fetching contributors for ${filePath}:`, error);
+ return [];
+ }
+ },
+);