import React, { useState, useEffect } from 'react';
import { Terminal, Github, Heart, Share2, Globe, Code, Clock, ShieldAlert, Cpu } from 'lucide-react';
// --- Components ---
const Intro = ({ onStart }) => (
DIGITAL GRAVE
你的 GitHub,是你的数字墓碑。
"在比特的海洋里,我们将如同法老一般,将我们的思维固化在提交记录的金字塔中。
当肉体消逝,git log 将是我们唯一的呼吸。"
);
const ConfigForm = ({ onSubmit, loading }) => {
const [username, setUsername] = useState('');
const [message, setMessage] = useState('');
const [step, setStep] = useState(1);
const handleNext = () => {
if (username.trim()) setStep(2);
};
const handleSubmit = (e) => {
e.preventDefault();
if (message.trim()) onSubmit({ username, message });
};
return (
{/* Progress Bar */}
{step === 1 ? (
Step 1: 身份锚定
setUsername(e.target.value)}
placeholder="torvalds"
className="w-full bg-black border-b border-gray-700 p-4 text-2xl focus:outline-none focus:border-white transition-colors placeholder-gray-800"
autoFocus
/>
) : (
)}
);
};
const Tombstone = ({ data }) => {
const [ipfsHash, setIpfsHash] = useState(null);
const [isMinting, setIsMinting] = useState(false);
// Simulate generating a unique hash for the tombstone
const generateHash = () => {
setIsMinting(true);
setTimeout(() => {
setIpfsHash('QmXyZ' + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15));
setIsMinting(false);
}, 2500);
};
const formatDate = (dateString) => {
if (!dateString) return 'UNKNOWN';
const date = new Date(dateString);
return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }).toUpperCase();
};
// Mock data fallback if API fails or for demo
const user = data.user || {
name: "Unknown Soul",
login: "ghost",
avatar_url: "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png",
created_at: "2010-01-01T00:00:00Z",
bio: "The user has left the simulation."
};
const lastPush = data.lastPush || new Date().toISOString();
const topRepo = data.topRepo || { name: "legacy-code", description: "A final contribution to the void.", stargazers_count: 404, language: "C++" };
return (
{/* The Monument Card */}
{/* Ornamental Border */}
{/* Header: Photo & Identity */}
{user.name || user.login}
@{user.login}
{/* The Timeline */}
Initialized
{formatDate(user.created_at)}
Terminated
{formatDate(lastPush)}
{/* The Epitaph (User Message) */}
{/* The Magnum Opus (Top Repo) */}
Magnum Opus
{topRepo.name}
{topRepo.language || 'Code'}
{topRepo.description || "No description provided."}
{topRepo.stargazers_count} stars
{/* Footer: Last Hash */}
Final Commit Hash
{data.lastHash || '7b3f1d2a9c4e8f0b5d6a7c8e9f0b1a2c3d4e5f6'}
{/* Action Area */}
{!ipfsHash ? (
) : (
IPFS CID GENERATED:
ipfs://{ipfsHash}
)}
);
};
// --- Main App & Logic ---
export default function DigitalGrave() {
const [view, setView] = useState('intro'); // intro, config, tombstone
const [graveData, setGraveData] = useState(null);
const [loading, setLoading] = useState(false);
// Mock fetching logic - in a real app this would hit GitHub API
const fetchGitHubData = async (username, message) => {
setLoading(true);
try {
// Trying to fetch real public data
const userRes = await fetch(`https://api.github.com/users/${username}`);
let userData = null;
let topRepo = null;
let lastPush = new Date().toISOString();
if (userRes.ok) {
userData = await userRes.json();
// Fetch repos to find top one
const reposRes = await fetch(`https://api.github.com/users/${username}/repos?sort=pushed&per_page=100`);
if (reposRes.ok) {
const repos = await reposRes.json();
// Find repo with most stars
if (repos.length > 0) {
topRepo = repos.reduce((prev, current) => (prev.stargazers_count > current.stargazers_count) ? prev : current);
// Use most recent push
const latest = repos.sort((a, b) => new Date(b.pushed_at) - new Date(a.pushed_at))[0];
lastPush = latest ? latest.pushed_at : lastPush;
}
}
} else {
// Fallback to mock data if rate limited or user not found (for demo purposes)
console.warn("GitHub API failed or user not found, using mock data");
userData = { login: username, name: username, avatar_url: `https://ui-avatars.com/api/?name=${username}&background=random&color=fff` };
}
setGraveData({
user: userData,
message: message,
topRepo: topRepo,
lastPush: lastPush,
lastHash: Array.from({length: 40}, () => Math.floor(Math.random() * 16).toString(16)).join('') // Simulated hash
});
// Artificial delay for dramatic effect
setTimeout(() => {
setLoading(false);
setView('tombstone');
}, 1500);
} catch (error) {
console.error(error);
setLoading(false);
}
};
return (
{view === 'intro' && setView('config')} />}
{view === 'config' && fetchGitHubData(data.username, data.message)} loading={loading} />}
{view === 'tombstone' && graveData && }
);
}