mirror of
https://github.com/kennethreitz/kennethreitz.org.git
synced 2026-06-05 22:50:17 +00:00
1265 lines
42 KiB
HTML
1265 lines
42 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en" class="antialiased">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Kenneth Reitz - Digital Mind Map</title>
|
||
|
||
<!-- Load Google Fonts -->
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Crimson+Text:ital,wght@0,400;0,600;1,400;1,600&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||
|
||
<!-- Load Tailwind CSS -->
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<script>
|
||
tailwind.config = {
|
||
theme: {
|
||
extend: {
|
||
colors: {
|
||
primary: {
|
||
DEFAULT: '#4e3979',
|
||
50: '#f8f7fd',
|
||
100: '#f0eef9',
|
||
200: '#e3e0f4',
|
||
300: '#d0c8ec',
|
||
400: '#b5a7e0',
|
||
500: '#9b86d3',
|
||
600: '#8265c4',
|
||
700: '#6f52b0',
|
||
800: '#5c4394',
|
||
900: '#4e3979',
|
||
950: '#3a2b5c',
|
||
}
|
||
},
|
||
fontFamily: {
|
||
'sans': ['Inter', 'system-ui', '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'sans-serif'],
|
||
'serif': ['Crimson Text', 'Charter', 'Georgia', 'Times New Roman', 'serif'],
|
||
'mono': ['JetBrains Mono', 'Fira Code', 'SF Mono', 'Consolas', 'monospace'],
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
</head>
|
||
<body class="bg-gray-900 font-serif text-gray-100 selection:bg-primary-700 selection:text-white">
|
||
<!-- Header -->
|
||
<header class="border-b border-gray-700 bg-gray-900/90 backdrop-blur-xl sticky top-0 z-50 shadow-lg">
|
||
<div class="max-w-6xl mx-auto px-6 py-8">
|
||
<div class="flex flex-col space-y-4">
|
||
<!-- Main brand -->
|
||
<div class="flex items-center justify-between">
|
||
<div>
|
||
<h1 class="text-5xl font-bold tracking-tight">
|
||
<a href="/" class="text-gray-100 hover:text-primary-400 transition-colors duration-200 no-underline font-serif bg-gradient-to-r from-primary-400 via-purple-400 to-pink-400 bg-clip-text text-transparent">
|
||
Kenneth Reitz
|
||
</a>
|
||
</h1>
|
||
<p class="text-xl text-gray-400 font-serif italic mt-2 tracking-wide">
|
||
Creator of <span class="text-primary-400 font-semibold">Requests</span> • Python Advocate • Making Complex Things Simple
|
||
</p>
|
||
</div>
|
||
|
||
<!-- Mobile-friendly navigation -->
|
||
<nav class="hidden md:flex space-x-6">
|
||
<a href="/directory" class="text-sm font-medium text-gray-300 hover:text-primary-400 transition-all duration-300 px-4 py-2 rounded-lg hover:bg-primary-900/30 border border-transparent hover:border-primary-500/30">
|
||
📁 Explore Files
|
||
</a>
|
||
<a href="https://github.com/psf/requests" target="_blank" class="text-sm font-medium text-gray-300 hover:text-primary-400 transition-all duration-300 px-4 py-2 rounded-lg hover:bg-primary-900/30 border border-transparent hover:border-primary-500/30">
|
||
🌐 Requests
|
||
</a>
|
||
</nav>
|
||
</div>
|
||
|
||
<!-- Mobile navigation -->
|
||
<div class="flex md:hidden">
|
||
<!-- Mobile nav links -->
|
||
<nav class="flex space-x-4">
|
||
<a href="/directory" class="text-sm font-medium text-gray-300 hover:text-primary-400 transition-all duration-300 px-4 py-2 rounded-lg hover:bg-primary-900/30">
|
||
📁 Files
|
||
</a>
|
||
<a href="https://github.com/psf/requests" target="_blank" class="text-sm font-medium text-gray-300 hover:text-primary-400 transition-all duration-300 px-4 py-2 rounded-lg hover:bg-primary-900/30">
|
||
🌐 Requests
|
||
</a>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<style>
|
||
/* Kenneth Reitz Brand Colors & Variables */
|
||
:root {
|
||
--kr-primary: #6366f1;
|
||
--kr-primary-dark: #4338ca;
|
||
--kr-secondary: #f59e0b;
|
||
--kr-accent: #10b981;
|
||
--kr-purple: #8b5cf6;
|
||
--kr-pink: #ec4899;
|
||
--kr-bg: #0f0f0f;
|
||
--kr-surface: #1a1a1a;
|
||
--kr-card: #262626;
|
||
--kr-text: #f8fafc;
|
||
--kr-text-muted: #94a3b8;
|
||
--kr-border: #374151;
|
||
--kr-glow: rgba(99, 102, 241, 0.3);
|
||
}
|
||
|
||
body {
|
||
background: linear-gradient(135deg, var(--kr-bg) 0%, #111827 50%, var(--kr-bg) 100%);
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* Hero Section */
|
||
.hero-section {
|
||
padding: 80px 0 60px;
|
||
text-align: center;
|
||
position: relative;
|
||
background: radial-gradient(circle at 50% 20%, rgba(99, 102, 241, 0.1) 0%, transparent 50%);
|
||
}
|
||
|
||
.hero-title {
|
||
font-size: clamp(3rem, 8vw, 6rem);
|
||
font-weight: 800;
|
||
background: linear-gradient(135deg, var(--kr-primary) 0%, var(--kr-purple) 50%, var(--kr-pink) 100%);
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
margin-bottom: 1rem;
|
||
line-height: 1.1;
|
||
letter-spacing: -0.02em;
|
||
}
|
||
|
||
.hero-subtitle {
|
||
font-size: 1.5rem;
|
||
color: var(--kr-text-muted);
|
||
margin-bottom: 2rem;
|
||
max-width: 600px;
|
||
margin-left: auto;
|
||
margin-right: auto;
|
||
}
|
||
|
||
.hero-stats {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 3rem;
|
||
margin-bottom: 3rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.stat-item {
|
||
text-align: center;
|
||
}
|
||
|
||
.stat-number {
|
||
font-size: 2.5rem;
|
||
font-weight: 700;
|
||
color: var(--kr-primary);
|
||
display: block;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
#live-downloads {
|
||
background: linear-gradient(135deg, var(--kr-primary), var(--kr-purple));
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
position: relative;
|
||
}
|
||
|
||
#live-downloads::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: linear-gradient(135deg, rgba(99, 102, 241, 0.1), rgba(139, 92, 246, 0.1));
|
||
border-radius: 8px;
|
||
animation: pulse-bg 3s ease-in-out infinite;
|
||
z-index: -1;
|
||
}
|
||
|
||
@keyframes pulse-bg {
|
||
0%, 100% { opacity: 0.3; transform: scale(1); }
|
||
50% { opacity: 0.7; transform: scale(1.05); }
|
||
}
|
||
|
||
.live-indicator {
|
||
display: inline-block;
|
||
width: 8px;
|
||
height: 8px;
|
||
background: #10b981;
|
||
border-radius: 50%;
|
||
margin-left: 8px;
|
||
animation: live-pulse 2s ease-in-out infinite;
|
||
}
|
||
|
||
@keyframes live-pulse {
|
||
0%, 100% { opacity: 1; transform: scale(1); }
|
||
50% { opacity: 0.6; transform: scale(1.2); }
|
||
}
|
||
|
||
.stat-label {
|
||
color: var(--kr-text-muted);
|
||
font-size: 0.9rem;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
}
|
||
|
||
/* Featured Projects */
|
||
.featured-section {
|
||
padding: 60px 0;
|
||
background: rgba(26, 26, 26, 0.8);
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.featured-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||
gap: 2rem;
|
||
margin-top: 3rem;
|
||
}
|
||
|
||
.project-card {
|
||
background: var(--kr-card);
|
||
border: 1px solid var(--kr-border);
|
||
border-radius: 16px;
|
||
padding: 2rem;
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.project-card::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 3px;
|
||
background: linear-gradient(90deg, var(--kr-primary), var(--kr-purple));
|
||
transform: scaleX(0);
|
||
transition: transform 0.3s ease;
|
||
}
|
||
|
||
.project-card:hover::before {
|
||
transform: scaleX(1);
|
||
}
|
||
|
||
.project-card:hover {
|
||
transform: translateY(-8px);
|
||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(99, 102, 241, 0.2);
|
||
border-color: var(--kr-primary);
|
||
}
|
||
|
||
.project-icon {
|
||
width: 60px;
|
||
height: 60px;
|
||
background: linear-gradient(135deg, var(--kr-primary), var(--kr-purple));
|
||
border-radius: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 24px;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.project-title {
|
||
font-size: 1.5rem;
|
||
font-weight: 700;
|
||
color: var(--kr-text);
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.project-desc {
|
||
color: var(--kr-text-muted);
|
||
line-height: 1.6;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.project-stats {
|
||
display: flex;
|
||
gap: 1rem;
|
||
font-size: 0.9rem;
|
||
color: var(--kr-text-muted);
|
||
}
|
||
|
||
/* Mind Map Section */
|
||
.mindmap-section {
|
||
padding: 60px 0;
|
||
}
|
||
|
||
.obsidian-mindmap {
|
||
background: linear-gradient(135deg, var(--kr-surface) 0%, #1f1f1f 100%);
|
||
border: 1px solid var(--kr-border);
|
||
border-radius: 20px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
height: 70vh;
|
||
min-height: 600px;
|
||
box-shadow:
|
||
0 25px 50px rgba(0, 0, 0, 0.5),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
.graph-container {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: relative;
|
||
cursor: grab;
|
||
}
|
||
|
||
.graph-container:active {
|
||
cursor: grabbing;
|
||
}
|
||
|
||
.mindmap-svg {
|
||
width: 100%;
|
||
height: 100%;
|
||
background: radial-gradient(circle at 50% 50%, rgba(99, 102, 241, 0.05) 0%, transparent 70%);
|
||
}
|
||
|
||
/* Enhanced Node Styles */
|
||
.node {
|
||
cursor: pointer;
|
||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
.node circle {
|
||
stroke-width: 2;
|
||
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.4));
|
||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
.node.directory circle {
|
||
fill: var(--kr-primary);
|
||
stroke: rgba(99, 102, 241, 0.8);
|
||
r: 10;
|
||
}
|
||
|
||
.node.file circle {
|
||
fill: var(--kr-accent);
|
||
stroke: rgba(16, 185, 129, 0.8);
|
||
r: 7;
|
||
}
|
||
|
||
.node.root circle {
|
||
fill: var(--kr-secondary);
|
||
stroke: rgba(245, 158, 11, 0.9);
|
||
r: 16;
|
||
stroke-width: 3;
|
||
}
|
||
|
||
.node:hover circle {
|
||
transform: scale(1.3);
|
||
filter: drop-shadow(0 8px 20px rgba(0, 0, 0, 0.6)) brightness(1.2);
|
||
}
|
||
|
||
.node.active circle {
|
||
stroke-width: 4;
|
||
filter: drop-shadow(0 0 20px currentColor) drop-shadow(0 6px 15px rgba(0, 0, 0, 0.4));
|
||
animation: pulse 2s infinite;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 1; transform: scale(1); }
|
||
50% { opacity: 0.8; transform: scale(1.05); }
|
||
}
|
||
|
||
.node text {
|
||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
fill: #000000;
|
||
text-anchor: middle;
|
||
dominant-baseline: middle;
|
||
pointer-events: none;
|
||
text-shadow: 0 2px 4px rgba(255, 255, 255, 0.9);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.node.directory text {
|
||
font-weight: 700;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.node.root text {
|
||
font-size: 15px;
|
||
font-weight: 800;
|
||
fill: #000000;
|
||
}
|
||
|
||
.node:hover text {
|
||
font-size: 13px;
|
||
fill: #000000;
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* Enhanced Links */
|
||
.link {
|
||
fill: none;
|
||
stroke: rgba(0, 0, 0, 0.4);
|
||
stroke-width: 2;
|
||
stroke-opacity: 0.7;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.link.active {
|
||
stroke: rgba(0, 0, 0, 0.9);
|
||
stroke-width: 3;
|
||
stroke-opacity: 1;
|
||
filter: drop-shadow(0 0 8px rgba(0, 0, 0, 0.5));
|
||
}
|
||
|
||
/* Enhanced Tooltip */
|
||
.obsidian-tooltip {
|
||
position: absolute;
|
||
background: rgba(26, 26, 26, 0.95);
|
||
border: 1px solid var(--kr-border);
|
||
border-radius: 12px;
|
||
padding: 16px 20px;
|
||
color: var(--kr-text);
|
||
font-size: 13px;
|
||
font-family: 'Inter', sans-serif;
|
||
max-width: 320px;
|
||
z-index: 1000;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
backdrop-filter: blur(12px);
|
||
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
transform: translateY(8px);
|
||
}
|
||
|
||
.obsidian-tooltip.visible {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
|
||
.tooltip-title {
|
||
font-weight: 700;
|
||
color: var(--kr-primary);
|
||
margin-bottom: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.tooltip-path {
|
||
font-size: 11px;
|
||
color: var(--kr-text-muted);
|
||
font-family: 'JetBrains Mono', monospace;
|
||
margin-bottom: 8px;
|
||
background: rgba(0, 0, 0, 0.3);
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.tooltip-meta {
|
||
font-size: 11px;
|
||
color: var(--kr-text-muted);
|
||
}
|
||
|
||
/* Control Panels */
|
||
.control-panel {
|
||
position: absolute;
|
||
top: 20px;
|
||
right: 20px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
z-index: 100;
|
||
}
|
||
|
||
.control-button {
|
||
width: 48px;
|
||
height: 48px;
|
||
background: rgba(26, 26, 26, 0.9);
|
||
border: 1px solid var(--kr-border);
|
||
border-radius: 12px;
|
||
color: var(--kr-text);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
backdrop-filter: blur(12px);
|
||
}
|
||
|
||
.control-button:hover {
|
||
background: var(--kr-primary);
|
||
color: white;
|
||
transform: translateY(-2px) scale(1.05);
|
||
box-shadow: 0 8px 24px rgba(99, 102, 241, 0.4);
|
||
}
|
||
|
||
.search-panel {
|
||
position: absolute;
|
||
top: 20px;
|
||
left: 20px;
|
||
width: 320px;
|
||
background: rgba(26, 26, 26, 0.95);
|
||
border: 1px solid var(--kr-border);
|
||
border-radius: 16px;
|
||
padding: 20px;
|
||
backdrop-filter: blur(12px);
|
||
z-index: 100;
|
||
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
|
||
}
|
||
|
||
.search-input {
|
||
width: 100%;
|
||
background: rgba(15, 15, 15, 0.8);
|
||
border: 1px solid var(--kr-border);
|
||
border-radius: 10px;
|
||
padding: 12px 16px;
|
||
color: var(--kr-text);
|
||
font-size: 14px;
|
||
transition: all 0.3s ease;
|
||
font-family: 'Inter', sans-serif;
|
||
}
|
||
|
||
.search-input:focus {
|
||
outline: none;
|
||
border-color: var(--kr-primary);
|
||
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
|
||
background: rgba(15, 15, 15, 0.9);
|
||
}
|
||
|
||
.search-input::placeholder {
|
||
color: var(--kr-text-muted);
|
||
}
|
||
|
||
.info-panel {
|
||
position: absolute;
|
||
bottom: 20px;
|
||
left: 20px;
|
||
background: rgba(26, 26, 26, 0.95);
|
||
border: 1px solid var(--kr-border);
|
||
border-radius: 16px;
|
||
padding: 20px;
|
||
backdrop-filter: blur(12px);
|
||
z-index: 100;
|
||
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
|
||
font-family: 'Inter', sans-serif;
|
||
font-size: 12px;
|
||
color: var(--kr-text-muted);
|
||
min-width: 220px;
|
||
}
|
||
|
||
.info-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-bottom: 8px;
|
||
padding: 6px 0;
|
||
border-bottom: 1px solid rgba(99, 102, 241, 0.1);
|
||
}
|
||
|
||
.info-item:last-child {
|
||
margin-bottom: 0;
|
||
border-bottom: none;
|
||
}
|
||
|
||
.info-label {
|
||
color: var(--kr-text-muted);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.info-value {
|
||
color: var(--kr-primary);
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* Loading Animation */
|
||
.loading-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 100%;
|
||
color: var(--kr-text);
|
||
font-family: 'Inter', sans-serif;
|
||
}
|
||
|
||
.loading-spinner {
|
||
width: 56px;
|
||
height: 56px;
|
||
border: 4px solid rgba(99, 102, 241, 0.2);
|
||
border-top: 4px solid var(--kr-primary);
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
@keyframes spin {
|
||
0% { transform: rotate(0deg); }
|
||
100% { transform: rotate(360deg); }
|
||
}
|
||
|
||
.loading-text {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: var(--kr-text-muted);
|
||
}
|
||
|
||
/* Philosophy Quote */
|
||
.philosophy-section {
|
||
text-align: center;
|
||
padding: 60px 0;
|
||
background: linear-gradient(135deg, rgba(99, 102, 241, 0.05) 0%, rgba(139, 92, 246, 0.05) 100%);
|
||
}
|
||
|
||
.philosophy-quote {
|
||
font-size: 1.8rem;
|
||
font-style: italic;
|
||
color: var(--kr-text);
|
||
margin-bottom: 1rem;
|
||
max-width: 800px;
|
||
margin-left: auto;
|
||
margin-right: auto;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.philosophy-author {
|
||
color: var(--kr-primary);
|
||
font-weight: 600;
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
/* Responsive Design */
|
||
@media (max-width: 768px) {
|
||
.hero-stats {
|
||
gap: 2rem;
|
||
}
|
||
|
||
.stat-number {
|
||
font-size: 2rem;
|
||
}
|
||
|
||
.featured-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.search-panel {
|
||
width: calc(100% - 40px);
|
||
position: relative;
|
||
top: 0;
|
||
left: 0;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.obsidian-mindmap {
|
||
height: 60vh;
|
||
min-height: 500px;
|
||
}
|
||
|
||
.info-panel {
|
||
position: relative;
|
||
bottom: 0;
|
||
left: 0;
|
||
margin-top: 20px;
|
||
width: 100%;
|
||
}
|
||
|
||
.control-panel {
|
||
top: 10px;
|
||
right: 10px;
|
||
flex-direction: row;
|
||
}
|
||
|
||
.control-button {
|
||
width: 40px;
|
||
height: 40px;
|
||
font-size: 16px;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<!-- Hero Section -->
|
||
<section class="hero-section">
|
||
<div class="max-w-6xl mx-auto px-6">
|
||
<h1 class="hero-title">Kenneth Reitz</h1>
|
||
<p class="hero-subtitle">
|
||
Creator of <strong>Requests</strong> • Python Advocate • Making Complex Things Simple & Beautiful
|
||
</p>
|
||
|
||
<div class="hero-stats">
|
||
<div class="stat-item">
|
||
<span class="stat-number" id="live-downloads">0</span>
|
||
<span class="stat-label">Live Downloads Today <span class="live-indicator"></span></span>
|
||
</div>
|
||
<div class="stat-item">
|
||
<span class="stat-number">16M</span>
|
||
<span class="stat-label">Daily Average</span>
|
||
</div>
|
||
<div class="stat-item">
|
||
<span class="stat-number">51K+</span>
|
||
<span class="stat-label">GitHub Stars</span>
|
||
</div>
|
||
<div class="stat-item">
|
||
<span class="stat-number">∞</span>
|
||
<span class="stat-label">Impact</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Featured Projects -->
|
||
<section class="featured-section">
|
||
<div class="max-w-6xl mx-auto px-6">
|
||
<h2 class="text-4xl font-bold text-center mb-4" style="color: var(--kr-text);">Legendary Contributions</h2>
|
||
<p class="text-center text-xl" style="color: var(--kr-text-muted);">Libraries that changed how the world codes</p>
|
||
|
||
<div class="featured-grid">
|
||
<div class="project-card">
|
||
<div class="project-icon">🌐</div>
|
||
<h3 class="project-title">Requests</h3>
|
||
<p class="project-desc">HTTP for Humans. The most elegant Python library ever created, downloaded by every Python developer on Earth.</p>
|
||
<div class="project-stats">
|
||
<span>⭐ 51k stars</span>
|
||
<span>📦 16M daily downloads</span>
|
||
<span>🔥 Industry standard</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="project-card">
|
||
<div class="project-icon">📦</div>
|
||
<h3 class="project-title">pipenv</h3>
|
||
<p class="project-desc">Python packaging and dependency management for humans. Bringing sanity to Python environments.</p>
|
||
<div class="project-stats">
|
||
<span>⭐ 24k stars</span>
|
||
<span>🛠️ Dev tool</span>
|
||
<span>🎯 Problem solver</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="project-card">
|
||
<div class="project-icon">⏰</div>
|
||
<h3 class="project-title">maya</h3>
|
||
<p class="project-desc">Datetimes for Humans. Making datetime manipulation intuitive and Pythonic with natural language parsing.</p>
|
||
<div class="project-stats">
|
||
<span>⭐ 3.4k stars</span>
|
||
<span>🕐 Time magic</span>
|
||
<span>🐍 Pythonic</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="project-card">
|
||
<div class="project-icon">🎨</div>
|
||
<h3 class="project-title">Philosophy</h3>
|
||
<p class="project-desc">"For Humans" design philosophy. Creating APIs that feel natural and make developers happy to write code.</p>
|
||
<div class="project-stats">
|
||
<span>💡 Human-centered</span>
|
||
<span>✨ Beautiful APIs</span>
|
||
<span>🌟 Developer joy</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Philosophy Section -->
|
||
<section class="philosophy-section">
|
||
<div class="max-w-4xl mx-auto px-6">
|
||
<blockquote class="philosophy-quote">
|
||
"Beautiful is better than ugly. Simple is better than complex. Complex is better than complicated."
|
||
</blockquote>
|
||
<div class="philosophy-author">— The Zen of Python (Tim Peters)</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Mind Map Section -->
|
||
<section class="mindmap-section">
|
||
<div class="max-w-7xl mx-auto px-6">
|
||
<h2 class="text-4xl font-bold text-center mb-4" style="color: var(--kr-text);">Explore the Digital Universe</h2>
|
||
<p class="text-center text-xl mb-8" style="color: var(--kr-text-muted);">Interactive knowledge graph of thoughts, projects, and insights</p>
|
||
|
||
<div class="obsidian-mindmap">
|
||
<!-- Loading State -->
|
||
<div id="loading" class="loading-container">
|
||
<div class="loading-spinner"></div>
|
||
<div class="loading-text">Mapping neural pathways...</div>
|
||
</div>
|
||
|
||
<!-- Main Graph Container -->
|
||
<div id="graph-container" class="graph-container" style="display: none;">
|
||
<svg class="mindmap-svg" id="mindmap-svg"></svg>
|
||
|
||
<!-- Search Panel -->
|
||
<div class="search-panel">
|
||
<input
|
||
type="text"
|
||
id="search-input"
|
||
class="search-input"
|
||
placeholder="Search the knowledge graph..."
|
||
>
|
||
</div>
|
||
|
||
<!-- Control Panel -->
|
||
<div class="control-panel">
|
||
<div class="control-button" id="zoom-in" title="Zoom In">+</div>
|
||
<div class="control-button" id="zoom-out" title="Zoom Out">−</div>
|
||
<div class="control-button" id="reset-view" title="Reset View">⌂</div>
|
||
<div class="control-button" id="toggle-physics" title="Toggle Physics">⚡</div>
|
||
</div>
|
||
|
||
<!-- Info Panel -->
|
||
<div class="info-panel">
|
||
<div class="info-item">
|
||
<span class="info-label">Nodes</span>
|
||
<span class="info-value" id="node-count">0</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">Connections</span>
|
||
<span class="info-value" id="link-count">0</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">Directories</span>
|
||
<span class="info-value" id="dir-count">0</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">Files</span>
|
||
<span class="info-value" id="file-count">0</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tooltip -->
|
||
<div class="obsidian-tooltip" id="tooltip"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<!-- Footer -->
|
||
<footer class="border-t border-gray-800/60 mt-20 bg-gradient-to-t from-gray-900 to-gray-900/60 backdrop-blur-sm">
|
||
<div class="max-w-6xl mx-auto px-6 py-16">
|
||
<div class="text-center">
|
||
<!-- Impact Statement -->
|
||
<div class="mb-8">
|
||
<p class="text-lg text-gray-300 font-medium mb-2">
|
||
Empowering developers worldwide with elegant, human-centered code
|
||
</p>
|
||
<p class="text-sm text-gray-500">
|
||
16M downloads daily • Touching every Python project on Earth
|
||
</p>
|
||
</div>
|
||
|
||
<!-- Social Links -->
|
||
<div class="flex justify-center space-x-8 mb-8">
|
||
<a href="https://github.com/kennethreitz" target="_blank" class="text-gray-400 hover:text-primary-400 transition-all duration-300 flex items-center space-x-2 group">
|
||
<svg class="h-6 w-6 group-hover:scale-110 transition-transform" fill="currentColor" viewBox="0 0 20 20">
|
||
<path fill-rule="evenodd" d="M10 0C4.477 0 0 4.484 0 10.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0110 4.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.203 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.942.359.31.678.921.678 1.856 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0020 10.017C20 4.484 15.522 0 10 0z" clip-rule="evenodd"/>
|
||
</svg>
|
||
<span class="hidden sm:inline font-medium">GitHub</span>
|
||
</a>
|
||
<a href="https://twitter.com/kennethreitz" target="_blank" class="text-gray-400 hover:text-primary-400 transition-all duration-300 flex items-center space-x-2 group">
|
||
<svg class="h-6 w-6 group-hover:scale-110 transition-transform" fill="currentColor" viewBox="0 0 20 20">
|
||
<path d="M6.29 18.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0020 3.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.073 4.073 0 01.8 7.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 010 16.407a11.616 11.616 0 006.29 1.84"/>
|
||
</svg>
|
||
<span class="hidden sm:inline font-medium">Twitter</span>
|
||
</a>
|
||
<a href="https://github.com/psf/requests" target="_blank" class="text-gray-400 hover:text-primary-400 transition-all duration-300 flex items-center space-x-2 group">
|
||
<svg class="h-6 w-6 group-hover:scale-110 transition-transform" fill="currentColor" viewBox="0 0 20 20">
|
||
<path d="M10 2L3 7v11h14V7l-7-5z"/>
|
||
</svg>
|
||
<span class="hidden sm:inline font-medium">Requests</span>
|
||
</a>
|
||
</div>
|
||
|
||
<!-- Copyright -->
|
||
<div class="border-t border-gray-800/40 pt-8">
|
||
<p class="text-sm text-gray-500">
|
||
© 2024 Kenneth Reitz. Crafted with passion for beautiful, human-centered design.
|
||
</p>
|
||
<p class="text-xs text-gray-600 mt-2">
|
||
"Beautiful is better than ugly. Simple is better than complex."
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||
<script>
|
||
class ObsidianMindmap {
|
||
constructor() {
|
||
this.data = null;
|
||
this.nodes = [];
|
||
this.links = [];
|
||
this.simulation = null;
|
||
this.svg = null;
|
||
this.g = null;
|
||
this.zoom = null;
|
||
this.physicsEnabled = true;
|
||
this.searchTerm = '';
|
||
|
||
this.width = 0;
|
||
this.height = 0;
|
||
|
||
this.init();
|
||
}
|
||
|
||
async init() {
|
||
try {
|
||
const response = await fetch('/api/mindmap');
|
||
this.data = await response.json();
|
||
|
||
this.setupSVG();
|
||
this.processData();
|
||
this.createSimulation();
|
||
this.render();
|
||
this.setupEventListeners();
|
||
this.updateStats();
|
||
|
||
document.getElementById('loading').style.display = 'none';
|
||
document.getElementById('graph-container').style.display = 'block';
|
||
} catch (error) {
|
||
console.error('Failed to load mindmap data:', error);
|
||
}
|
||
}
|
||
|
||
setupSVG() {
|
||
const container = document.getElementById('graph-container');
|
||
this.width = container.clientWidth;
|
||
this.height = container.clientHeight;
|
||
|
||
this.svg = d3.select('#mindmap-svg')
|
||
.attr('width', this.width)
|
||
.attr('height', this.height);
|
||
|
||
this.zoom = d3.zoom()
|
||
.scaleExtent([0.1, 4])
|
||
.on('zoom', (event) => {
|
||
this.g.attr('transform', event.transform);
|
||
});
|
||
|
||
this.svg.call(this.zoom);
|
||
this.g = this.svg.append('g');
|
||
}
|
||
|
||
processData(node = this.data, parent = null, level = 0) {
|
||
if (!node) return;
|
||
|
||
const nodeData = {
|
||
id: node.path || 'root',
|
||
name: node.name || 'Root',
|
||
type: node.type || 'root',
|
||
path: node.path || '',
|
||
level: level,
|
||
parent: parent,
|
||
children: node.children || []
|
||
};
|
||
|
||
this.nodes.push(nodeData);
|
||
|
||
if (parent) {
|
||
this.links.push({
|
||
source: parent.id,
|
||
target: nodeData.id
|
||
});
|
||
}
|
||
|
||
if (node.children) {
|
||
node.children.forEach(child => {
|
||
this.processData(child, nodeData, level + 1);
|
||
});
|
||
}
|
||
}
|
||
|
||
createSimulation() {
|
||
this.simulation = d3.forceSimulation(this.nodes)
|
||
.force('link', d3.forceLink(this.links)
|
||
.id(d => d.id)
|
||
.distance(d => {
|
||
const sourceLevel = this.nodes.find(n => n.id === d.source.id)?.level || 0;
|
||
const targetLevel = this.nodes.find(n => n.id === d.target.id)?.level || 0;
|
||
return 100 + (Math.max(sourceLevel, targetLevel) * 30);
|
||
})
|
||
.strength(0.7))
|
||
.force('charge', d3.forceManyBody()
|
||
.strength(d => {
|
||
if (d.type === 'root') return -2000;
|
||
if (d.type === 'directory') return -800;
|
||
return -400;
|
||
}))
|
||
.force('center', d3.forceCenter(this.width / 2, this.height / 2))
|
||
.force('collision', d3.forceCollide()
|
||
.radius(d => {
|
||
if (d.type === 'root') return 30;
|
||
if (d.type === 'directory') return 25;
|
||
return 20;
|
||
}))
|
||
.force('x', d3.forceX(this.width / 2).strength(0.1))
|
||
.force('y', d3.forceY(this.height / 2).strength(0.1));
|
||
|
||
this.simulation.on('tick', () => this.ticked());
|
||
}
|
||
|
||
render() {
|
||
// Render links first (so they appear behind nodes)
|
||
this.linkElements = this.g.selectAll('.link')
|
||
.data(this.links)
|
||
.enter()
|
||
.append('line')
|
||
.attr('class', 'link')
|
||
.attr('stroke-width', d => {
|
||
const sourceNode = this.nodes.find(n => n.id === d.source.id);
|
||
const targetNode = this.nodes.find(n => n.id === d.target.id);
|
||
if (sourceNode?.type === 'root' || targetNode?.type === 'root') return 4;
|
||
if (sourceNode?.type === 'directory' || targetNode?.type === 'directory') return 3;
|
||
return 2;
|
||
});
|
||
|
||
// Render nodes
|
||
this.nodeElements = this.g.selectAll('.node')
|
||
.data(this.nodes)
|
||
.enter()
|
||
.append('g')
|
||
.attr('class', d => `node ${d.type}`)
|
||
.call(this.drag());
|
||
|
||
// Add circles to nodes
|
||
this.nodeElements.append('circle');
|
||
|
||
// Add text labels
|
||
this.nodeElements.append('text')
|
||
.attr('dy', d => d.type === 'root' ? 30 : 25)
|
||
.text(d => {
|
||
if (d.name.length > 20) {
|
||
return d.name.substring(0, 17) + '...';
|
||
}
|
||
return d.name;
|
||
});
|
||
|
||
// Add event listeners
|
||
this.nodeElements
|
||
.on('mouseover', (event, d) => this.showTooltip(event, d))
|
||
.on('mouseout', () => this.hideTooltip())
|
||
.on('click', (event, d) => this.handleNodeClick(event, d))
|
||
.on('dblclick', (event, d) => this.handleNodeDoubleClick(event, d));
|
||
}
|
||
|
||
ticked() {
|
||
if (this.linkElements) {
|
||
this.linkElements
|
||
.attr('x1', d => d.source.x)
|
||
.attr('y1', d => d.source.y)
|
||
.attr('x2', d => d.target.x)
|
||
.attr('y2', d => d.target.y);
|
||
}
|
||
|
||
if (this.nodeElements) {
|
||
this.nodeElements
|
||
.attr('transform', d => `translate(${d.x}, ${d.y})`);
|
||
}
|
||
}
|
||
|
||
drag() {
|
||
return d3.drag()
|
||
.on('start', (event, d) => {
|
||
if (!event.active && this.physicsEnabled) {
|
||
this.simulation.alphaTarget(0.3).restart();
|
||
}
|
||
d.fx = d.x;
|
||
d.fy = d.y;
|
||
})
|
||
.on('drag', (event, d) => {
|
||
d.fx = event.x;
|
||
d.fy = event.y;
|
||
})
|
||
.on('end', (event, d) => {
|
||
if (!event.active && this.physicsEnabled) {
|
||
this.simulation.alphaTarget(0);
|
||
}
|
||
d.fx = null;
|
||
d.fy = null;
|
||
});
|
||
}
|
||
|
||
showTooltip(event, d) {
|
||
const tooltip = document.getElementById('tooltip');
|
||
const icon = d.type === 'directory' ? '📁' : d.type === 'root' ? '🌟' : '📄';
|
||
|
||
tooltip.innerHTML = `
|
||
<div class="tooltip-title">
|
||
<span>${icon}</span>
|
||
<span>${d.name}</span>
|
||
</div>
|
||
<div class="tooltip-path">${d.path || 'Root'}</div>
|
||
<div class="tooltip-meta">
|
||
Type: ${d.type} • Level: ${d.level}
|
||
${d.children ? ` • Children: ${d.children.length}` : ''}
|
||
</div>
|
||
`;
|
||
|
||
tooltip.style.left = (event.pageX + 15) + 'px';
|
||
tooltip.style.top = (event.pageY - 15) + 'px';
|
||
tooltip.classList.add('visible');
|
||
}
|
||
|
||
hideTooltip() {
|
||
document.getElementById('tooltip').classList.remove('visible');
|
||
}
|
||
|
||
handleNodeClick(event, d) {
|
||
// Highlight connected nodes
|
||
this.nodeElements.classed('active', false);
|
||
this.linkElements.classed('active', false);
|
||
|
||
d3.select(event.currentTarget).classed('active', true);
|
||
|
||
// Highlight connected links
|
||
this.linkElements
|
||
.classed('active', link =>
|
||
link.source.id === d.id || link.target.id === d.id
|
||
);
|
||
}
|
||
|
||
handleNodeDoubleClick(event, d) {
|
||
if (d.path && d.path !== '') {
|
||
window.open(`/${d.path}`, '_blank');
|
||
}
|
||
}
|
||
|
||
setupEventListeners() {
|
||
// Zoom controls
|
||
document.getElementById('zoom-in').addEventListener('click', () => {
|
||
this.svg.transition().duration(300).call(
|
||
this.zoom.scaleBy, 1.5
|
||
);
|
||
});
|
||
|
||
document.getElementById('zoom-out').addEventListener('click', () => {
|
||
this.svg.transition().duration(300).call(
|
||
this.zoom.scaleBy, 1 / 1.5
|
||
);
|
||
});
|
||
|
||
document.getElementById('reset-view').addEventListener('click', () => {
|
||
this.svg.transition().duration(750).call(
|
||
this.zoom.transform,
|
||
d3.zoomIdentity.translate(this.width / 2, this.height / 2).scale(1)
|
||
);
|
||
});
|
||
|
||
document.getElementById('toggle-physics').addEventListener('click', () => {
|
||
this.physicsEnabled = !this.physicsEnabled;
|
||
if (this.physicsEnabled) {
|
||
this.simulation.alphaTarget(0.3).restart();
|
||
} else {
|
||
this.simulation.stop();
|
||
}
|
||
});
|
||
|
||
// Search functionality
|
||
const searchInput = document.getElementById('search-input');
|
||
searchInput.addEventListener('input', (event) => {
|
||
this.searchTerm = event.target.value.toLowerCase();
|
||
this.filterNodes();
|
||
});
|
||
|
||
// Window resize
|
||
window.addEventListener('resize', () => {
|
||
this.handleResize();
|
||
});
|
||
}
|
||
|
||
filterNodes() {
|
||
this.nodeElements
|
||
.style('opacity', d => {
|
||
if (!this.searchTerm) return 1;
|
||
return d.name.toLowerCase().includes(this.searchTerm) ? 1 : 0.3;
|
||
})
|
||
.select('circle')
|
||
.style('transform', d => {
|
||
if (!this.searchTerm) return null;
|
||
const scale = d.name.toLowerCase().includes(this.searchTerm) ? 1.2 : 0.8;
|
||
return `scale(${scale})`;
|
||
});
|
||
|
||
this.linkElements
|
||
.style('opacity', d => {
|
||
if (!this.searchTerm) return 0.7;
|
||
const sourceMatch = d.source.name.toLowerCase().includes(this.searchTerm);
|
||
const targetMatch = d.target.name.toLowerCase().includes(this.searchTerm);
|
||
return (sourceMatch || targetMatch) ? 1 : 0.2;
|
||
});
|
||
}
|
||
|
||
updateStats() {
|
||
const nodeCount = this.nodes.length;
|
||
const linkCount = this.links.length;
|
||
const dirCount = this.nodes.filter(n => n.type === 'directory').length;
|
||
const fileCount = this.nodes.filter(n => n.type === 'file').length;
|
||
|
||
document.getElementById('node-count').textContent = nodeCount;
|
||
document.getElementById('link-count').textContent = linkCount;
|
||
document.getElementById('dir-count').textContent = dirCount;
|
||
document.getElementById('file-count').textContent = fileCount;
|
||
}
|
||
|
||
handleResize() {
|
||
const container = document.getElementById('graph-container');
|
||
this.width = container.clientWidth;
|
||
this.height = container.clientHeight;
|
||
|
||
this.svg
|
||
.attr('width', this.width)
|
||
.attr('height', this.height);
|
||
|
||
this.simulation
|
||
.force('center', d3.forceCenter(this.width / 2, this.height / 2))
|
||
.force('x', d3.forceX(this.width / 2).strength(0.1))
|
||
.force('y', d3.forceY(this.height / 2).strength(0.1))
|
||
.alpha(0.3)
|
||
.restart();
|
||
}
|
||
}
|
||
|
||
// Real-time download counter
|
||
function initDownloadCounter() {
|
||
const downloadsPerSecond = 16000000 / (24 * 60 * 60); // 16M per day
|
||
const startOfDay = new Date();
|
||
startOfDay.setHours(0, 0, 0, 0);
|
||
const now = new Date();
|
||
const secondsSinceStartOfDay = (now - startOfDay) / 1000;
|
||
|
||
let currentDownloads = Math.floor(secondsSinceStartOfDay * downloadsPerSecond);
|
||
|
||
const counter = document.getElementById('live-downloads');
|
||
|
||
function updateCounter() {
|
||
currentDownloads += downloadsPerSecond;
|
||
const displayNumber = Math.floor(currentDownloads);
|
||
|
||
// Format with commas
|
||
counter.textContent = displayNumber.toLocaleString();
|
||
|
||
// Add a subtle glow effect when updating
|
||
counter.style.transform = 'scale(1.05)';
|
||
counter.style.filter = 'brightness(1.2)';
|
||
setTimeout(() => {
|
||
counter.style.transform = 'scale(1)';
|
||
counter.style.filter = 'brightness(1)';
|
||
}, 200);
|
||
}
|
||
|
||
// Initial display
|
||
updateCounter();
|
||
|
||
// Update every second
|
||
setInterval(updateCounter, 1000);
|
||
}
|
||
|
||
// Initialize the mindmap when the page loads
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
new ObsidianMindmap();
|
||
initDownloadCounter();
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |