-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
206 lines (190 loc) · 34.9 KB
/
index.html
File metadata and controls
206 lines (190 loc) · 34.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jaren - AI Architect & Software Engineer</title>
<script src="https://cdn.tailwindcss.com"></script>
<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@400;500;700;900&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
/* All CSS is identical to the previous version */
:root { --electric-indigo: #6f00ff; --cyber-teal: #00e5ff; --accent-color: var(--electric-indigo); --bg-color: #0a0a0a; --text-color: #e5e7eb; --border-color: rgba(255, 255, 255, 0.1); }
html.light { --bg-color: #f0f2f5; --text-color: #1f2937; --border-color: rgba(0, 0, 0, 0.1); }
body { font-family: 'Inter', sans-serif; background-color: var(--bg-color); color: var(--text-color); transition: background-color 0.5s ease, color 0.5s ease; overflow-x: hidden; }
#hero-canvas { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; }
::-webkit-scrollbar { width: 8px; } ::-webkit-scrollbar-track { background: var(--bg-color); } ::-webkit-scrollbar-thumb { background-color: var(--accent-color); border-radius: 10px; transition: background-color 0.3s ease; }
.glass-effect { background: rgba(16, 16, 18, 0.6); -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px); border: 1px solid var(--border-color); transition: background 0.3s ease, border 0.3s ease; }
html.light .glass-effect { background: rgba(255, 255, 255, 0.6); }
.accent-text { color: var(--accent-color); transition: color 0.3s ease; }
@keyframes fadeInChar { to { opacity: 1; transform: translateY(0); } } .headline-char { opacity: 0; transform: translateY(20px); animation: fadeInChar 0.5s forwards; }
@keyframes blink { from, to { opacity: 0; } 50% { opacity: 1; } } .blinking-cursor { color: var(--accent-color); animation: blink 1s step-end infinite; transition: color 0.3s ease; }
section { opacity: 0; transform: translateY(40px); transition: opacity 0.8s ease-out, transform 0.8s ease-out; } section.visible { opacity: 1; transform: translateY(0); }
.project-card { transform-style: preserve-3d; transition: transform 0.5s cubic-bezier(0.25, 1, 0.5, 1); transform: perspective(1500px); }
.command-item { transition: all 0.2s ease-in-out; border-left: 3px solid transparent; } .command-item:hover, .command-item.active { background-color: rgba(255, 255, 255, 0.05); color: var(--accent-color); border-left-color: var(--accent-color); transform: translateX(5px); }
html.light .command-item:hover, html.light .command-item.active { background-color: rgba(0, 0, 0, 0.03); }
.command-content { max-height: 0; overflow: hidden; transition: max-height 0.5s cubic-bezier(0.25, 1, 0.5, 1), padding 0.5s ease, opacity 0.5s ease; opacity: 0; padding: 0 1.25rem; } .command-content.open { max-height: 500px; opacity: 1; padding: 1rem 1.25rem 1.5rem 1.25rem; }
.blueprint-cell { transition: all 0.4s ease; position: relative; } .blueprint-icon { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.5); opacity: 0; transition: all 0.4s ease; color: var(--accent-color); } .blueprint-cell.active { background-color: var(--accent-color) !important; opacity: 0.2 !important; } .blueprint-cell.active .blueprint-icon { transform: translate(-50%, -50%) scale(1); opacity: 1; }
.tech-icon { position: relative; transition: transform 0.2s ease-in-out; } .tech-icon:hover { transform: scale(1.1); } .tech-tooltip { visibility: hidden; opacity: 0; transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out; } .tech-icon:hover .tech-tooltip { visibility: visible; opacity: 1; transform: translateY(-0.5rem); }
.font-mono { font-family: 'Roboto Mono', monospace; } #sandbox-input { background: transparent; border: none; outline: none; caret-color: var(--accent-color); transition: caret-color 0.3s ease; } #sandbox-suggestion { position: absolute; top: 0; left: 0; color: #6b7280; z-index: 1; pointer-events: none; } #sandbox-history-log { position: absolute; bottom: calc(100% + 0.5rem); left: 0; width: 100%; max-height: 50vh; overflow-y: auto; }
.history-item { transition: opacity 0.3s ease; } .ai-response-content { white-space: pre-wrap; word-wrap: break-word; } .copy-button { position: absolute; top: 0.5rem; right: 0.5rem; background-color: rgba(255,255,255,0.1); border: 1px solid var(--border-color); color: #9ca3af; padding: 0.25rem 0.5rem; border-radius: 0.375rem; font-size: 0.75rem; cursor: pointer; transition: all 0.2s ease; opacity: 0.5; } .ai-response:hover .copy-button { opacity: 1; } .copy-button:hover { background-color: var(--accent-color); color: white; }
@keyframes spin { to { transform: rotate(360deg); } } .loader { width: 1rem; height: 1rem; border-radius: 50%; border: 2px solid var(--border-color); border-top-color: var(--accent-color); animation: spin 0.8s linear infinite; }
</style>
</head>
<body class="antialiased">
<canvas id="hero-canvas"></canvas>
<div class="relative z-10">
<header id="hero" class="h-screen w-full flex flex-col justify-center items-center text-center p-4">
<h1 id="headline" class="text-7xl md:text-9xl font-black tracking-tighter mb-4"></h1>
<p id="sub-headline" class="text-lg md:text-2xl text-gray-300 dark:text-gray-400 max-w-3xl mx-auto opacity-0 transition-opacity duration-1000 delay-1000">I bridge the gap between ambitious AI strategy and production-grade software engineering.</p>
<div id="initiate-cta" class="absolute bottom-10 right-10 text-lg md:text-xl font-mono text-gray-400 dark:text-gray-500 cursor-pointer hover:text-white dark:hover:text-gray-200 transition-colors duration-300 opacity-0 animate-pulse">/initiate<span class="blinking-cursor">_</span></div>
</header>
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<section id="philosophy" class="py-24 md:py-32">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-24 items-center">
<div>
<h2 class="text-4xl md:text-5xl font-bold mb-8 tracking-tight">Deconstructing the Blueprint.</h2>
<div id="typing-text-container" class="space-y-4 text-lg text-gray-300 dark:text-gray-400 min-h-[250px] font-mono"></div>
</div>
<div id="blueprint-grid" class="w-full h-80 md:h-96 grid grid-cols-5 grid-rows-5 gap-2 relative"></div>
</div>
</section>
<section id="proof" class="py-24 md:py-32">
<h2 class="text-4xl md:text-5xl font-bold mb-12 text-center tracking-tight">Case Study: Project C.O.D.E.R.</h2>
<div class="project-card glass-effect rounded-2xl p-8 md:p-12">
<div class="grid grid-cols-1 lg:grid-cols-5 gap-12">
<div class="lg:col-span-2">
<h3 class="text-2xl font-bold mb-2 accent-text">The Challenge</h3>
<p class="text-gray-400 dark:text-gray-500 mb-6">Massive codebases with inconsistent documentation led to slow onboarding and increased bugs. Manual documentation was a significant bottleneck.</p>
<h3 class="text-2xl font-bold mb-2 accent-text">The Solution</h3>
<p class="text-gray-400 dark:text-gray-500">Developed C.O.D.E.R. (Contextual On-Demand Documentation & Explanation Resource), an autonomous AI agent that analyzes code, generates high-quality documentation, and integrates it directly into the CI/CD pipeline.</p>
<button id="run-simulation-btn" class="mt-6 w-full text-center bg-[var(--accent-color)] text-white font-bold py-3 px-6 rounded-lg hover:opacity-90 transition-opacity disabled:opacity-50 disabled:cursor-wait">Run Simulation</button>
</div>
<div class="lg:col-span-3 flex flex-col justify-center items-center text-center">
<div id="simulation-area" class="w-full h-40 bg-gray-800/20 dark:bg-black/20 rounded-lg flex justify-around items-center p-4 border border-[var(--border-color)] relative overflow-hidden mb-8">
<div id="sim-code" class="text-4xl transition-transform duration-500">💻</div>
<div id="sim-pipe" class="w-1/3 h-1 bg-gray-700/50 rounded-full relative"><div id="sim-progress" class="absolute top-0 left-0 h-full bg-[var(--accent-color)] rounded-full" style="width: 0%;"></div></div>
<div class="flex flex-col space-y-2">
<div id="sim-doc" class="text-4xl opacity-20 transition-opacity duration-500">📄</div>
<div id="sim-check" class="text-4xl opacity-20 transition-opacity duration-500">✅</div>
</div>
</div>
<div class="relative group">
<p class="text-gray-400 dark:text-gray-500 mb-1">Code Quality Improvement</p>
<div id="main-metric" class="text-7xl md:text-9xl font-black">35%</div>
<div class="absolute bottom-full mb-2 w-max left-1/2 -translate-x-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-300 glass-effect text-sm rounded-lg py-2 px-4 shadow-lg pointer-events-none">
<p><strong>-60%</strong> time on manual documentation.</p>
<p><strong>-40%</strong> faster development cycles.</p>
</div>
</div>
<div class="mt-8">
<h4 class="font-bold text-lg mb-4">Tech Stack</h4>
<div id="tech-stack-container" class="flex justify-center items-center gap-x-6 gap-y-4 flex-wrap"></div>
</div>
</div>
</div>
</div>
</section>
<section id="services" class="py-24 md:py-32">
<h2 class="text-4xl md:text-5xl font-bold mb-12 text-center tracking-tight">The Command Palette.</h2>
<div class="glass-effect rounded-2xl max-w-4xl mx-auto">
<div class="p-4 border-b border-[var(--border-color)] text-gray-400 font-mono text-sm">> Select a service to expand</div>
<div id="command-palette" role="tablist" class="flex flex-col"></div>
</div>
</section>
<section id="contact" class="py-32 md:py-48 text-center">
<h2 class="text-5xl md:text-7xl font-black tracking-tighter mb-8">Let's architect what's next.</h2>
<div class="flex justify-center items-center space-x-6">
<a href="mailto:Jaren.Dev@proton.me" class="text-lg md:text-xl text-gray-300 dark:text-gray-400 hover:text-[var(--accent-color)] transition-colors duration-300">Jaren.Dev@proton.me</a>
<span class="text-gray-600 dark:text-gray-700">|</span>
<a href="https://www.linkedin.com" target="_blank" rel="noopener noreferrer" class="text-lg md:text-xl text-gray-300 dark:text-gray-400 hover:text-[var(--accent-color)] transition-colors duration-300">LinkedIn</a>
</div>
</section>
</main>
</div>
<div id="sandbox" class="fixed bottom-0 left-0 right-0 p-2 z-50">
<div class="max-w-7xl mx-auto relative">
<div id="sandbox-history-log" class="font-mono text-sm space-y-2 p-1"></div>
<div class="glass-effect rounded-lg px-4 py-2 flex items-center font-mono text-lg" id="sandbox-container">
<span class="text-gray-400 mr-2">/</span>
<div class="relative flex-grow h-full">
<span id="sandbox-suggestion" class="absolute inset-y-0 left-0 px-0 py-0 flex items-center"></span>
<input type="text" id="sandbox-input" placeholder="Type a command..." class="w-full z-10 relative bg-transparent" autocomplete="off">
</div>
<div class="text-gray-500 text-sm hidden md:block ml-4">/help for commands</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// --- CORE VARIABLES & STATE ---
const IS_MOBILE = window.innerWidth < 768; const root = document.documentElement;
// --- PARTICLE ENGINE ---
const canvas = document.getElementById('hero-canvas'); const ctx = canvas.getContext('2d'); let particles = []; let mouse = { x: null, y: null, radius: IS_MOBILE ? 80 : 150 }; let canvasState = { mode: 'chaotic', connections: true };
class Particle { constructor(x, y, gridX, gridY) { this.x = x; this.y = y; this.originX = x; this.originY = y; this.gridX = gridX; this.gridY = gridY; this.size = (Math.random() * 1.5) + 0.5; this.vx = 0; this.vy = 0; this.density = (Math.random() * 30) + 5; } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2, false); ctx.fillStyle = root.classList.contains('light') ? 'rgba(0,0,0,0.5)' : 'rgba(255,255,255,0.6)'; ctx.fill(); } update() { let targetX = (canvasState.mode === 'grid') ? this.gridX : this.originX; let targetY = (canvasState.mode === 'grid') ? this.gridY : this.originY; let dxMouse = mouse.x - this.x; let dyMouse = mouse.y - this.y; let distanceMouse = Math.sqrt(dxMouse * dxMouse + dyMouse * dyMouse); if (distanceMouse < mouse.radius) { let force = (mouse.radius - distanceMouse) / mouse.radius; this.vx -= (dxMouse / distanceMouse) * force * this.density * 0.1; this.vy -= (dyMouse / distanceMouse) * force * this.density * 0.1; } this.vx += (targetX - this.x) * 0.005; this.vy += (targetY - this.y) * 0.005; this.vx *= 0.92; this.vy *= 0.92; this.x += this.vx; this.y += this.vy; this.draw(); } }
function initParticles() { particles = []; let particleCount = (canvas.width * canvas.height) / (IS_MOBILE ? 15000 : 9000); let gridSize = Math.floor(Math.sqrt(particleCount)) + 2; let cellW = canvas.width / gridSize; let cellH = canvas.height / gridSize; for (let y = 0; y < gridSize; y++) { for (let x = 0; x < gridSize; x++) { if (particles.length < particleCount) { let pX = Math.random() * canvas.width; let pY = Math.random() * canvas.height; let gX = (x * cellW) + (cellW / 2) + (Math.random() - 0.5) * 20; let gY = (y * cellH) + (cellH / 2) + (Math.random() - 0.5) * 20; particles.push(new Particle(pX, pY, gX, gY)); } } } }
function connectParticles() { if (!canvasState.connections) return; const accentColorValue = getComputedStyle(root).getPropertyValue('--accent-color').trim(); const accentRGB = accentColorValue.includes('indigo') ? '111,0,255' : '0,229,255'; const baseRGB = root.classList.contains('light') ? '0,0,0' : '255,255,255'; let connectDist = IS_MOBILE ? 80 : 120; for (let a = 0; a < particles.length; a++) { for (let b = a; b < particles.length; b++) { let dist = Math.hypot(particles[a].x - particles[b].x, particles[a].y - particles[b].y); if (dist < connectDist) { let opacity = 1 - (dist / connectDist); let mouseDist = mouse.x ? Math.hypot(mouse.x - particles[a].x, mouse.y - particles[a].y) : Infinity; ctx.strokeStyle = (mouseDist < mouse.radius / 1.5) ? `rgba(${accentRGB},${opacity})` : `rgba(${baseRGB},${opacity * 0.2})`; ctx.lineWidth = 0.5; ctx.beginPath(); ctx.moveTo(particles[a].x, particles[a].y); ctx.lineTo(particles[b].x, particles[b].y); ctx.stroke(); } } } }
function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); particles.forEach(p => p.update()); connectParticles(); requestAnimationFrame(animate); }
function handleResize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; mouse.radius = window.innerWidth < 768 ? 80 : 150; initParticles(); }
window.addEventListener('resize', handleResize); window.addEventListener('mousemove', e => { mouse.x = e.clientX; mouse.y = e.clientY; }); window.addEventListener('mouseout', () => { mouse.x = null; mouse.y = null; });
handleResize(); animate();
// --- GEMINI API CALLER ---
async function callGemini(userPrompt, systemInstruction = null) {
// This now calls our own backend server
const SERVER_URL = 'http://localhost:3000/api/gemini';
try {
const response = await fetch(SERVER_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userPrompt, systemInstruction })
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error.message || `HTTP error! status: ${response.status}`);
}
return data.candidates[0].content.parts[0].text;
} catch (error) {
console.error("API call failed:", error);
throw error;
}
}
// --- PAGE SETUP & STATIC CONTENT ---
// All static content and non-AI animations are restored here.
// Headline typing
const headlineEl = document.getElementById('headline');
"Jaren.".split('').forEach((char, index) => { headlineEl.innerHTML += `<span class="headline-char" style="animation-delay: ${index * 100}ms">${char}</span>`; });
setTimeout(() => { document.getElementById('sub-headline').style.opacity = '1'; document.getElementById('initiate-cta').style.opacity = '1'; }, 800);
document.getElementById('initiate-cta').addEventListener('click', () => { document.getElementById('philosophy').scrollIntoView({ behavior: 'smooth' }); });
// Scroll observer
const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('visible'); canvasState.mode = (entry.target.id === 'philosophy' || entry.target.id === 'proof') ? 'grid' : 'chaotic'; if (entry.target.id === 'philosophy') typePhilosophyText(); } }); }, { threshold: 0.2 });
document.querySelectorAll('section').forEach(section => observer.observe(section));
// Blueprint & typing
const blueprintGrid = document.getElementById('blueprint-grid'); const blueprintIcons = { 'Cloud': { cells: [6, 7, 8, 11, 13], svg: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z"/></svg>`}, 'AI/ML': { cells: [17, 18, 21, 22, 23], svg: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 8V4H8"/><rect x="4" y="12" width="4" height="6"/><path d="M12 12h4"/><path d="M20 12h-4"/><path d="M20 18v-6h-4"/><path d="M12 20v-4h4"/></svg>`}, 'Automation': { cells: [1, 2, 3, 5, 9], svg: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20V10"/><path d="M18 20V4"/><path d="M6 20V16"/></svg>`} };
for (let i = 0; i < 25; i++) { blueprintGrid.innerHTML += `<div class="blueprint-cell bg-gray-800/10 dark:bg-black/20 border border-[var(--border-color)] rounded-sm"><div class="blueprint-icon"></div></div>`; }
const gridCells = blueprintGrid.querySelectorAll('.blueprint-cell'); let typingInitiated = false; const typingContainer = document.getElementById('typing-text-container'); const typingTexts = [ { text: "🤔 Is your AI strategy stuck in the 'proof-of-concept' phase?", keyword: null }, { text: "I architect and build the bridge from ambitious ideas to production-grade, scalable reality.", keyword: null }, { text: "My expertise lies in Scalable Cloud Architecture, Custom AI/ML Model Integration, and creating Intelligent Automation systems that deliver tangible results.", keywords: ['Cloud', 'AI/ML', 'Automation'] } ];
function activateGridCells(keyword) { gridCells.forEach(cell => cell.classList.remove('active')); if (blueprintIcons[keyword]) { blueprintIcons[keyword].cells.forEach(cellIndex => { gridCells[cellIndex].classList.add('active'); gridCells[cellIndex].querySelector('.blueprint-icon').innerHTML = blueprintIcons[keyword].svg; }); } }
function typePhilosophyText() { if (typingInitiated) return; typingInitiated = true; typingContainer.innerHTML = ''; let sentenceIndex = 0; function typeSentence() { if (sentenceIndex >= typingTexts.length) { setTimeout(() => gridCells.forEach(cell => cell.classList.remove('active')), 1000); return; } const p = document.createElement('p'); p.className = "transition-opacity duration-500 opacity-0"; typingContainer.appendChild(p); setTimeout(() => p.style.opacity = 1, 50); const { text, keywords } = typingTexts[sentenceIndex]; let charIndex = 0; let keywordIndex = 0; const interval = setInterval(() => { p.textContent += text[charIndex]; if (keywords && text.substring(0, charIndex+1).includes(keywords[keywordIndex])) { activateGridCells(keywords[keywordIndex]); keywordIndex++; } charIndex++; if (charIndex === text.length) { clearInterval(interval); sentenceIndex++; setTimeout(typeSentence, 500); } }, 25); } typeSentence(); }
// Project Card
const techStack = [ { name: 'AWS Lambda', svg: `<svg viewBox="0 0 24 24" fill="none" class="w-8 h-8"><path d="M11.97 19.175.75 12.333l3.525-2.035 7.695 4.442 7.695-4.442 3.525 2.035-11.22 6.842Zm0-14.35L.75 11.667l3.525-2.035 7.695 4.442 7.695-4.442L23.25 11.667 11.97 4.825Z" fill="#FF9900"/></svg>` }, { name: 'Python', svg: `<svg viewBox="0 0 24 24" fill="none" class="w-8 h-8"><path d="M12 21a9 9 0 0 0 9-9h-4.5A4.5 4.5 0 0 1 12 7.5V3a9 9 0 0 0-9 9 9 9 0 0 0 9 9Z" fill="#3776AB"/><path d="M12 3a9 9 0 0 0-9 9h4.5A4.5 4.5 0 0 1 12 7.5V3Z" fill="#FFD43B"/></svg>` }, { name: 'Docker', svg: `<svg viewBox="0 0 24 24" fill="#0db7ed" class="w-8 h-8"><path d="M21.933 9.475c-1.125-.334-2.229-.2-2.933.075.021-.521.042-1.042.042-1.563 0-3.334-2.083-6.25-5.25-6.25H6.208c-3.146 0-5.208 2.917-5.208 6.25v6.25c0 3.333 2.062 6.25 5.208 6.25h6.729c2.917 0 4.792-2.5 4.792-5.417 0-.146-.021-.292-.021-.458.854.084 1.833.021 2.583-.229a2.083 2.083 0 0 0 1.854-2.084v-1.125c0-1.104-.75-1.958-1.854-2.187M8.542 11.75H5.833V9.042h2.709v2.708m3.541 0h-2.708V9.042h2.708v2.708m3.542 0h-2.709V9.042h2.709v2.708m-3.542 3.542h-2.708v-2.709h2.708v2.709m-4.375-7.792h2.708V4.792h-2.708v2.709Z"/></svg>` }, { name: 'GitLab CI', svg: `<svg viewBox="0 0 24 24" fill="#FC6D26" class="w-8 h-8"><path d="m23.13 10.15-2.29-7.05a.77.77 0 0 0-.71-.5H3.87a.77.77 0 0 0-.71.5L.87 10.15a1.23 1.23 0 0 0 .28 1.34L12 21.3l10.85-9.81a1.23 1.23 0 0 0 .28-1.34"/></svg>` } ];
const techContainer = document.getElementById('tech-stack-container');
techContainer.innerHTML = techStack.map(tech => ` <div class="tech-icon"> ${tech.svg} <div class="tech-tooltip absolute bottom-full mb-2 w-max left-1/2 -translate-x-1/2 glass-effect text-xs rounded-md py-1 px-2 shadow-lg pointer-events-none">${tech.name}</div> </div> `).join('');
const projectCard = document.querySelector('.project-card'); if (!IS_MOBILE) { projectCard.addEventListener('mousemove', e => { const { left, top, width, height } = projectCard.getBoundingClientRect(); const x = (e.clientX - left - width / 2) / (width / 2); const y = (e.clientY - top - height / 2) / (height / 2); projectCard.style.transform = `perspective(1500px) rotateY(${x * 8}deg) rotateX(${-y * 8}deg) scale(1.02)`; }); projectCard.addEventListener('mouseleave', () => { projectCard.style.transform = `perspective(1500px) rotateY(0) rotateX(0) scale(1)`; }); }
document.getElementById('run-simulation-btn').addEventListener('click', (e) => { const btn = e.currentTarget; if (btn.disabled) return; btn.disabled = true; btn.textContent = 'Running...'; const progress = document.getElementById('sim-progress'); const code = document.getElementById('sim-code'); const doc = document.getElementById('sim-doc'); const check = document.getElementById('sim-check'); progress.style.transition = 'none'; progress.style.width = '0%'; code.style.transform = 'scale(1)'; doc.style.opacity = 0.2; check.style.opacity = 0.2; setTimeout(() => { progress.style.transition = 'width 1s ease-in-out'; code.style.transform = 'scale(0.75)'; progress.style.width = '100%'; }, 100); setTimeout(() => { doc.style.opacity = 1; }, 800); setTimeout(() => { check.style.opacity = 1; }, 1100); setTimeout(() => { btn.disabled = false; btn.textContent = 'Run Simulation Again'; }, 1500); });
// Command Palette
const commandPalette = document.getElementById('command-palette');
const services = [ { title: "AI Strategy Consulting", content: "From ideation to a concrete roadmap, I help you define a winning AI strategy that aligns with your business goals, ensuring feasibility, scalability, and ROI." }, { title: "Custom AI Solution Development", content: "I design, build, and deploy bespoke AI and machine learning models tailored to your unique data and challenges, going beyond off-the-shelf solutions." }, { title: "Intelligent Automation", content: "Leverage AI to automate complex workflows, reduce manual effort, and increase operational efficiency. From data processing to decision-making." }, { title: "LLM & Generative AI Integration", content: "Integrate the power of Large Language Models (like GPT) and generative AI into your products and services for enhanced user experiences and capabilities." }, { title: "Production-Grade Software Engineering", content: "Building robust, scalable, and maintainable software systems that form the backbone of your AI-powered applications." }, { title: "Cloud Architecture for AI", content: "Designing and implementing optimal cloud infrastructure on platforms like AWS, GCP, or Azure to support demanding AI workloads efficiently." }, { title: "MLOps Implementation", content: "Establishing pipelines and practices for seamless model training, deployment, monitoring, and lifecycle management, ensuring your AI stays effective." }, { title: "Expert Prompt Engineering", content: "Crafting and refining the precise instructions needed to elicit optimal performance from generative AI models for your specific use cases." }, { title: "AI Ethics & Governance Consulting", content: "Navigating the complexities of responsible AI. I help you build systems that are fair, transparent, and aligned with ethical principles." }, { title: "Technical Leadership & Team Augmentation", content: "Providing senior-level engineering leadership to guide your team, or augmenting your existing team with specialized AI expertise." } ];
services.forEach((service, index) => { const item = document.createElement('div'); item.innerHTML = ` <button id="tab-${index}" role="tab" aria-controls="panel-${index}" aria-selected="false" class="command-item w-full text-left font-medium p-4 text-lg">> <span class="ml-2">${service.title}</span></button> <div id="panel-${index}" role="tabpanel" aria-labelledby="tab-${index}" class="command-content text-gray-400"><p>${service.content}</p></div> `; commandPalette.appendChild(item); item.querySelector('button').addEventListener('click', (e) => { const btn = e.currentTarget; const isActive = btn.classList.contains('active'); commandPalette.querySelectorAll('[role="tab"]').forEach(b => { b.classList.remove('active'); b.setAttribute('aria-selected', 'false'); }); commandPalette.querySelectorAll('[role="tabpanel"]').forEach(c => c.classList.remove('open')); if (!isActive) { btn.classList.add('active'); btn.setAttribute('aria-selected', 'true'); item.querySelector('[role="tabpanel"]').classList.add('open'); } }); });
// --- SANDBOX & COMMAND HANDLING ---
const sandboxInput = document.getElementById('sandbox-input'); const sandboxSuggestion = document.getElementById('sandbox-suggestion'); const historyLog = document.getElementById('sandbox-history-log');
function escapeHtml(unsafe) { return unsafe.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'"); }
function addToHistory(userInput, aiResponse, isLoading = false, isError = false) { const item = document.createElement('div'); item.className = 'history-item glass-effect rounded-lg p-2 opacity-0'; let userHtml = userInput ? `<div class="text-gray-500">> ${escapeHtml(userInput)}</div>` : ''; let responseHtml = ''; if (isLoading) { responseHtml = '<div class="flex items-center space-x-2 accent-text"><div class="loader"></div><span>Thinking...</span></div>'; } else if (isError) { responseHtml = `<div class="text-red-400">Error: ${escapeHtml(aiResponse)}</div>`; } else { const escapedResponse = escapeHtml(aiResponse); const uniqueId = `copy-${Date.now()}-${Math.random()}`; responseHtml = ` <div class="ai-response text-gray-300 relative pt-1"> <div id="${uniqueId}" class="ai-response-content">${escapedResponse}</div> <button class="copy-button" onclick="copyToClipboard('${uniqueId}')">Copy</button> </div>`; } item.innerHTML = userHtml + responseHtml; historyLog.appendChild(item); setTimeout(() => item.style.opacity = 1, 50); historyLog.scrollTop = historyLog.scrollHeight; return item; }
window.copyToClipboard = (elementId) => { const textToCopy = document.getElementById(elementId).textContent; const tempTextArea = document.createElement('textarea'); tempTextArea.value = textToCopy; document.body.appendChild(tempTextArea); tempTextArea.select(); document.execCommand('copy'); document.body.removeChild(tempTextArea); };
const AI_COMMANDS = { '/prompt': { description: 'Crafts an expert prompt. Usage: /prompt <your goal>', async action(args) { if (!args) throw new Error("Please provide a goal for your prompt."); const systemInstruction = "You are an elite prompt engineer. A user will provide you with a goal or a rough idea for a prompt. Your task is to analyze their input and construct the most effective, detailed, and well-structured prompt possible to achieve that goal with a generative AI model. Return ONLY the engineered prompt itself, without any conversational text, introductions, or explanations."; return await callGemini(args, systemInstruction); } }, '/summarize': { description: 'Summarizes a section. Usage: /summarize [philosophy|proof|services]', async action(args) { const sectionId = args.split(' ')[0]; const element = document.getElementById(sectionId); if (!element) throw new Error(`Section '${sectionId}' not found.`); const text = element.innerText; return await callGemini(text, "Summarize the following text concisely, in one paragraph."); } }, '/explain_tech': { description: 'Explains a technology. Usage: /explain_tech <tech_name>', async action(args) { if (!args) throw new Error("Please provide a technology name."); return await callGemini(`Explain the technology "${args}" and its primary use case in a modern software development context. Keep it brief (2-3 sentences).`); } }, '/rewrite_headline': { description: 'Rewrites the main headline. Usage: /rewrite_headline <style>', async action(args) { if (!args) throw new Error("Please provide a style (e.g., professional, witty)."); const currentHeadline = document.getElementById('headline').textContent; const newHeadline = await callGemini(`Rewrite the headline "${currentHeadline}" in a ${args} style. Return only the new headline text.`); document.getElementById('headline').textContent = newHeadline.replace(/["']/g, ""); return `Headline updated to: "${newHeadline}"`; } } };
const LOCAL_COMMANDS = { '/help': { description: 'Shows this help message.', action() { const allCommands = {...LOCAL_COMMANDS, ...AI_COMMANDS }; return Object.entries(allCommands).map(([cmd, { description }]) => `${cmd}: ${description}`).join('\n'); } }, '/theme': { description: 'Usage: /theme [light|dark]', action(arg) { root.className = arg; return `Theme set to ${arg}.`; } }, '/color': { description: 'Usage: /color [indigo|teal]', action(arg) { root.style.setProperty('--accent-color', `var(--electric-${arg})`); return `Accent color set to ${arg}.`; } }, '/connections': { description: 'Usage: /connections [on|off]', action(arg) { canvasState.connections = (arg === 'on'); return `Particle connections turned ${arg}.`; } }, '/contact': { description: 'Scrolls to the contact section.', action() { document.getElementById('contact').scrollIntoView({ behavior: 'smooth' }); return "Navigating to contact."; } }, '/clear': { description: 'Clears the sandbox history.', action() { historyLog.innerHTML = ''; return 'History cleared.'; } } };
sandboxInput.addEventListener('input', () => { const value = sandboxInput.value; if (!value.startsWith('/')) { sandboxSuggestion.textContent = ''; return; } const allCommands = {...LOCAL_COMMANDS, ...AI_COMMANDS}; const [cmd, arg] = value.split(' '); let suggestionText = ''; if (allCommands[cmd]) { if (allCommands[cmd].description.includes('[') && arg) { const options = allCommands[cmd].description.match(/\[(.*?)\]/)[1].split('|'); const match = options.find(s => s.startsWith(arg)); if (match) suggestionText = cmd + ' ' + match; } else if (!arg) { suggestionText = cmd; } } else { const match = Object.keys(allCommands).find(c => c.startsWith(cmd)); if (match) suggestionText = match; } const suggestionDisplay = suggestionText.substring(value.length); sandboxSuggestion.innerHTML = `<span class="invisible">${value}</span><span class="text-gray-500">${suggestionDisplay}</span>`; sandboxSuggestion.dataset.fullCommand = suggestionText; });
sandboxInput.addEventListener('keydown', async (e) => { if (e.key === 'Tab' && sandboxSuggestion.dataset.fullCommand) { e.preventDefault(); sandboxInput.value = sandboxSuggestion.dataset.fullCommand; sandboxSuggestion.textContent = ''; sandboxSuggestion.dataset.fullCommand = ''; } if (e.key === 'Enter') { const userInput = sandboxInput.value.trim(); if (!userInput) return; const [cmd, ...argsArr] = userInput.split(' '); const args = argsArr.join(' '); sandboxInput.value = ''; sandboxSuggestion.textContent = ''; sandboxSuggestion.dataset.fullCommand = ''; if (LOCAL_COMMANDS[cmd]) { try { const result = LOCAL_COMMANDS[cmd].action(args); addToHistory(userInput, result); } catch (err) { addToHistory(userInput, err.message, false, true); } } else if (AI_COMMANDS[cmd]) { if (aiCommandCount >= AI_COMMAND_LIMIT) { addToHistory(userInput, "AI command limit (25) reached for this session. Please reload the page to start a new session.", false, true); return; } aiCommandCount++; const loadingItem = addToHistory(userInput, '', true); try { const result = await AI_COMMANDS[cmd].action(args); loadingItem.remove(); addToHistory(userInput, result); } catch (err) { loadingItem.remove(); addToHistory(userInput, err.message, false, true); } } else { addToHistory(userInput, `Unknown command. Type /help for options.`, false, true); } } });
});
</script>
</body>
</html>