308 lines
16 KiB
HTML
308 lines
16 KiB
HTML
<!doctype html>
|
||
<html lang="tr">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>tunapro — gece bahçesi</title>
|
||
<meta name="description" content="Tuna Gül'ün dijital bahçesi, gece modu: projeler bir graf, notlar birer düğüm. tmux'ta yaşayan biri için doğal habitat.">
|
||
<style>
|
||
:root{
|
||
--bg:#0b0f0d; --panel:#10161200; --panel-solid:#101612; --line:#1e2b23;
|
||
--fg:#c9d6cc; --dim:#6d7f73; --green:#5dd39e; --amber:#e8b45a; --red:#e06c75;
|
||
--mono:ui-monospace,"JetBrains Mono","Fira Code",Menlo,Consolas,monospace;
|
||
}
|
||
*{box-sizing:border-box;margin:0;padding:0}
|
||
html,body{height:100%}
|
||
body{background:var(--bg);color:var(--fg);font-family:var(--mono);font-size:15px;line-height:1.6;overflow:hidden}
|
||
a{color:var(--green)}
|
||
#sky{position:fixed;inset:0;display:block;cursor:grab}
|
||
#sky.dragging{cursor:grabbing}
|
||
.hud{position:fixed;left:0;right:0;pointer-events:none;z-index:10}
|
||
.hud *{pointer-events:auto}
|
||
header.hud{top:0;padding:22px 26px;display:flex;justify-content:space-between;align-items:flex-start;gap:16px;flex-wrap:wrap}
|
||
.brand h1{font-size:18px;font-weight:600;color:var(--fg)}
|
||
.brand h1 .p{color:var(--green)}
|
||
.brand .sub{font-size:12.5px;color:var(--dim);max-width:52ch;margin-top:6px}
|
||
.legend{font-size:12px;color:var(--dim);text-align:right;line-height:1.9}
|
||
.legend .dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:6px;vertical-align:1px}
|
||
.legend .d1{background:var(--green)} .legend .d2{background:var(--amber)} .legend .d3{background:#7aa2f7}
|
||
footer.hud{bottom:0;background:var(--panel-solid);border-top:1px solid var(--line);
|
||
display:flex;align-items:center;gap:0;font-size:12.5px;height:34px;overflow:hidden}
|
||
footer .seg{padding:0 14px;display:flex;align-items:center;height:100%;white-space:nowrap}
|
||
footer .seg.g{background:var(--green);color:#08130d;font-weight:600}
|
||
footer .seg.dim{color:var(--dim)}
|
||
footer .grow{flex:1}
|
||
footer a{color:var(--fg);text-decoration:none;padding:0 10px;height:100%;display:flex;align-items:center}
|
||
footer a:hover{background:var(--line);color:var(--green)}
|
||
#card{
|
||
position:fixed;top:0;right:0;bottom:34px;width:min(460px,100vw);z-index:20;
|
||
background:rgba(13,19,15,.97);border-left:1px solid var(--line);
|
||
transform:translateX(105%);transition:transform .25s ease;
|
||
padding:28px;overflow-y:auto;backdrop-filter:blur(8px);
|
||
}
|
||
#card.open{transform:translateX(0)}
|
||
#card .close{position:absolute;top:16px;right:18px;background:none;border:1px solid var(--line);
|
||
color:var(--dim);font-family:var(--mono);font-size:13px;padding:3px 10px;cursor:pointer;border-radius:4px}
|
||
#card .close:hover{color:var(--red);border-color:var(--red)}
|
||
#card .stage{font-size:12px;color:var(--amber);letter-spacing:.08em;text-transform:uppercase}
|
||
#card h2{margin:10px 0 4px;font-size:22px;color:#fff}
|
||
#card .meta{font-size:12px;color:var(--dim);margin-bottom:16px}
|
||
#card .body{font-size:14px;color:var(--fg)}
|
||
#card .body p{margin-bottom:12px}
|
||
#card .body .cmt{color:var(--dim)}
|
||
#card .links{margin-top:18px;border-top:1px dashed var(--line);padding-top:14px;display:flex;flex-direction:column;gap:8px;font-size:13px}
|
||
#card .links a{text-decoration:none}
|
||
#card .links a:hover{text-decoration:underline}
|
||
#card .links a::before{content:"$ open ";color:var(--dim)}
|
||
#card .also{margin-top:18px;font-size:12.5px;color:var(--dim)}
|
||
#card .also button{background:var(--line);border:none;color:var(--green);font-family:var(--mono);
|
||
font-size:12px;padding:2px 8px;margin:2px 4px 2px 0;border-radius:3px;cursor:pointer}
|
||
#card .also button:hover{background:var(--green);color:#08130d}
|
||
#hint{position:fixed;left:50%;bottom:52px;transform:translateX(-50%);z-index:9;
|
||
font-size:12px;color:var(--dim);background:var(--panel-solid);border:1px solid var(--line);
|
||
padding:6px 14px;border-radius:20px;transition:opacity .5s}
|
||
#hint.hide{opacity:0}
|
||
@media(max-width:700px){.legend{display:none}}
|
||
@media (prefers-reduced-motion: reduce){ /* JS de yavaşlatır */ }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<canvas id="sky" aria-label="proje grafı: her yıldız bir not"></canvas>
|
||
|
||
<header class="hud">
|
||
<div class="brand">
|
||
<h1><span class="p">tuna@tunapro</span>:~/bahce $ <span style="color:var(--amber)">ls --graph</span></h1>
|
||
<div class="sub">Gece bahçesi. Her yıldız bir proje-notu, çizgiler fikirler arası kök bağlantıları.
|
||
Bir yıldıza tıkla; sürükleyip kurcalayabilirsin — bahçeler kurcalanmak içindir.</div>
|
||
</div>
|
||
<div class="legend">
|
||
<div><span class="dot d1"></span>🌳 köklendi — çalışıyor, yaşıyor</div>
|
||
<div><span class="dot d2"></span>🌿 büyüyor — düzenli commit alıyor</div>
|
||
<div><span class="dot d3"></span>🌱 fide — yarım fikir, taze toprak</div>
|
||
</div>
|
||
</header>
|
||
|
||
<div id="hint">bir yıldıza tıkla ✦</div>
|
||
|
||
<aside id="card" aria-live="polite">
|
||
<button class="close" onclick="closeCard()">esc</button>
|
||
<div class="stage" id="c-stage"></div>
|
||
<h2 id="c-title"></h2>
|
||
<div class="meta" id="c-meta"></div>
|
||
<div class="body" id="c-body"></div>
|
||
<div class="links" id="c-links"></div>
|
||
<div class="also" id="c-also"></div>
|
||
</aside>
|
||
|
||
<footer class="hud">
|
||
<span class="seg g">[bahce]</span>
|
||
<span class="seg">0:graf*</span>
|
||
<span class="seg dim">1:hakkımda</span>
|
||
<span class="grow"></span>
|
||
<a href="https://github.com/tunapro1234">github</a>
|
||
<a href="https://git.tunapro.xyz">gitea</a>
|
||
<a href="https://probotstudio.com">probot</a>
|
||
<a href="/yillik/">yıllık→</a>
|
||
<span class="seg dim" id="clock"></span>
|
||
<span class="seg dim">tunapro.xyz</span>
|
||
</footer>
|
||
|
||
<script>
|
||
const NOTES = {
|
||
ben: {
|
||
title:"tuna gül", stage:"çekirdek", tier:0, meta:"bahçıvan · mühendislik öğrencisi · TR",
|
||
body:`<p>Robotik, AI agent'lar, sistem mimarisi. İlkem: bir sistem <b>kurulabiliyorsa</b>, kurulacak — o yüzden izleyen değil kuran taraftayım.</p>
|
||
<p class="cmt"># bu graf benim kafamın içi; düğümler proje, kenarlar kök.</p>
|
||
<p>Her yerde <b>@tunapro1234</b>. Bu site dahil her şey kendi demirimde dönüyor.</p>`,
|
||
links:[["github.com/tunapro1234","https://github.com/tunapro1234"],["git.tunapro.xyz","https://git.tunapro.xyz"]],
|
||
edges:["blueprint","probot","homelab","frc","sims"]
|
||
},
|
||
blueprint: {
|
||
title:"blueprint (bp)", stage:"🌳 köklendi", tier:1, meta:"Go · tmux · dikim 2025, hâlâ sulanıyor",
|
||
body:`<p>tmux içinde bir AI agent <b>filosunu</b> yöneten orkestrasyon aracı. Paketler <code>BLUEPRINT.yaml</code> ile tanımlanır; snapshot tabanlı versiyonlama, symlink'le sabitlenen bağımlılıklar.</p>
|
||
<p class="cmt"># agent'lar tmux pane'lerinde yaşar. ben sadece bahçıvanım.</p>`,
|
||
links:[["bp.tunapro.xyz","https://bp.tunapro.xyz"],["github.com/tunapro1234/blueprint","https://github.com/tunapro1234/blueprint"]],
|
||
edges:["replicant","outpost","homelab"]
|
||
},
|
||
outpost: {
|
||
title:"outpost", stage:"🌿 büyüyor", tier:2, meta:"JavaScript · son push: bugün",
|
||
body:`<p>Graf-merkezli outreach aracı: insanlar, bağlantılar ve konuşmalar düğüm olarak tutulur. (Evet, bu sayfayla aynı fikir — grafları seviyorum.)</p>
|
||
<p>Canlı hali kilitli sera 🔒 — içinde gerçek insanlar var.</p>`,
|
||
links:[["github.com/tunapro1234/outpost","https://github.com/tunapro1234/outpost"],["outpost.tunapro.xyz (🔒)","https://outpost.tunapro.xyz"]],
|
||
edges:["metron"]
|
||
},
|
||
probot: {
|
||
title:"probot studio", stage:"🌳 köklendi", tier:1, meta:"C++ / Arduino · MIT · 9★",
|
||
body:`<p><i>«Bir kit, sonsuz robot.»</i> Arduino tabanlı robot yarışmaları için kütüphane + kit ekosistemi. Çekirdeği açık kaynak: <code>probot-core</code>, sahada denenmiş embedded control middleware.</p>`,
|
||
links:[["probotstudio.com","https://probotstudio.com"],["github.com/probot-studio/probot-core","https://github.com/probot-studio/probot-core"]],
|
||
edges:["frc"]
|
||
},
|
||
homelab: {
|
||
title:"homelab: *.tunapro.xyz", stage:"🌿 büyüyor", tier:2, meta:"7/24 vızıldıyor · self-host her şey",
|
||
body:`<p>Kod <a href="https://git.tunapro.xyz">Gitea</a>'da, medya kendi video sunucumda, Minecraft paneli <code>mcdash</code>'te, mail bile burada. Okuduğun sayfa da bu makinelerde.</p>
|
||
<p class="cmt"># bulut diye bir şey yok, başkasının bilgisayarı var.</p>`,
|
||
links:[["git.tunapro.xyz","https://git.tunapro.xyz"],["video.tunapro.xyz","https://video.tunapro.xyz"],["mcdash.tunapro.xyz","https://mcdash.tunapro.xyz"]],
|
||
edges:["blueprint"]
|
||
},
|
||
replicant: {
|
||
title:"replicant", stage:"🌱 fide", tier:3, meta:"Python · dikim may 2026",
|
||
body:`<p>Agent'ların kendini ve iş arkadaşlarını kopyalayıp çoğaltması üzerine deneyler. Adı tesadüf değil. Ne olacağını ben de bilmiyorum; fidelerin güzelliği bu.</p>`,
|
||
links:[["github.com/tunapro1234/replicant","https://github.com/tunapro1234/replicant"]],
|
||
edges:["metron","sims"]
|
||
},
|
||
metron: {
|
||
title:"metron", stage:"🌱 fide", tier:3, meta:"Python · 1★",
|
||
body:`<p>Ölçmediğin şeyi yönetemezsin: agent'lar ve sistemler için metrik/ölçüm katmanı. replicant koşar, metron sayar.</p>`,
|
||
links:[["github.com/tunapro1234/metron","https://github.com/tunapro1234/metron"]],
|
||
edges:[]
|
||
},
|
||
sims: {
|
||
title:"llm toplum simülasyonları", stage:"🌱 fide", tier:3, meta:"okuma listesi + deneyler",
|
||
body:`<p>AI agent'larla toplum kurma merakı: <b>hallucinating-splines</b>'ta agent'lar headless SimCity'de şehir kuruyor; <b>EDSL</b> ile AI destekli anket/deney tasarımı.</p>`,
|
||
links:[["awesome-llm-social-simulation","https://github.com/tunapro1234/awesome-llm-social-simulation"],["hallucinating-splines","https://github.com/tunapro1234/hallucinating-splines"]],
|
||
edges:["replicant"]
|
||
},
|
||
frc: {
|
||
title:"frc 7839 & 9029 → nfr", stage:"🌳 kökler", tier:1, meta:"robotiğe giriş kapım",
|
||
body:`<p>Robotikle FRC sahasında tanıştım (7839 ve 9029). O sahadan bir dönem donanım işine de girdik; benim payıma düşen kökler bugün Probot Studio'da yaşıyor.</p>`,
|
||
links:[["github.com/team-nf","https://github.com/team-nf"]],
|
||
edges:["probot"]
|
||
},
|
||
elevator: {
|
||
title:"elevator_optimization", stage:"🌱 fide", tier:3, meta:"Rust · 1★",
|
||
body:`<p>Asansör çizelgelemesi ne kadar optimize edilebilir? Rust'la yazılmış küçük bir saplantı. Beklerken düşünülen her şey proje olabilir.</p>`,
|
||
links:[["github.com/tunapro1234/elevator_optimization","https://github.com/tunapro1234/elevator_optimization"]],
|
||
edges:["ben"]
|
||
},
|
||
canbitirici: {
|
||
title:"can neden iş bitirici değil?", stage:"🍄 mantar", tier:4, meta:"hakemli değil ama kesin doğru",
|
||
body:`<p>Bahçenin gölgesinde kendiliğinden biten tür: titiz metodolojiyle(!) hazırlanmış araştırma raporu. Bilim kişisel, kindar ve grafikli olmalı.</p>`,
|
||
links:[["canbitirici.tunapro.xyz","https://canbitirici.tunapro.xyz"]],
|
||
edges:["ben"]
|
||
},
|
||
};
|
||
|
||
const COLORS = {0:"#ffffff",1:"#5dd39e",2:"#e8b45a",3:"#7aa2f7",4:"#e06c75"};
|
||
const RADII = {0:9,1:7,2:6,3:5,4:5};
|
||
|
||
const cv = document.getElementById("sky"), ctx = cv.getContext("2d");
|
||
let W,H,DPR; const nodes=[], edges=[];
|
||
const reduced = matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||
|
||
function resize(){DPR=devicePixelRatio||1;W=innerWidth;H=innerHeight;cv.width=W*DPR;cv.height=H*DPR;cv.style.width=W+"px";cv.style.height=H+"px";ctx.setTransform(DPR,0,0,DPR,0,0);}
|
||
resize(); addEventListener("resize",resize);
|
||
|
||
const ids = Object.keys(NOTES);
|
||
ids.forEach((id,i)=>{
|
||
const a = (i/ids.length)*Math.PI*2;
|
||
const r = NOTES[id].tier===0?0:(120+NOTES[id].tier*60);
|
||
nodes.push({id,x:W/2+Math.cos(a)*r+(Math.random()*40-20),y:H/2+Math.sin(a)*r*0.7+(Math.random()*40-20),vx:0,vy:0,fixed:false});
|
||
});
|
||
ids.forEach(id=>NOTES[id].edges.forEach(t=>{ if(NOTES[t]) edges.push([id,t]); }));
|
||
const byId = id=>nodes.find(n=>n.id===id);
|
||
|
||
let selected=null, hover=null, drag=null, t=0;
|
||
|
||
function step(){
|
||
// hafif force layout
|
||
for(const n of nodes){
|
||
if(n.fixed||n===drag) continue;
|
||
let fx=0, fy=0;
|
||
for(const m of nodes){ if(m===n) continue;
|
||
const dx=n.x-m.x, dy=n.y-m.y, d2=dx*dx+dy*dy+0.01, d=Math.sqrt(d2);
|
||
const rep=Math.min(9000/d2,3); fx+=dx/d*rep; fy+=dy/d*rep;
|
||
}
|
||
for(const [a,b] of edges){
|
||
let o=null; if(a===n.id) o=byId(b); else if(b===n.id) o=byId(a);
|
||
if(!o) continue;
|
||
const dx=o.x-n.x, dy=o.y-n.y, d=Math.hypot(dx,dy)||1, pull=(d-140)*0.004;
|
||
fx+=dx/d*pull*d*0.02; fy+=dy/d*pull*d*0.02;
|
||
}
|
||
fx+=(W/2-n.x)*0.0012; fy+=(H/2-n.y)*0.0012;
|
||
n.vx=(n.vx+fx)*0.86; n.vy=(n.vy+fy)*0.86;
|
||
n.x+=n.vx; n.y+=n.vy;
|
||
n.x=Math.max(50,Math.min(W-50,n.x)); n.y=Math.max(90,Math.min(H-70,n.y));
|
||
}
|
||
}
|
||
|
||
function draw(){
|
||
t+=reduced?0:0.008;
|
||
ctx.clearRect(0,0,W,H);
|
||
// arka plan yıldız tozu
|
||
ctx.fillStyle="rgba(120,150,130,.25)";
|
||
for(let i=0;i<70;i++){
|
||
const x=(i*137.5)%W, y=(i*89.7)%H;
|
||
const tw=reduced?0.5:(Math.sin(t*2+i)*0.5+0.5);
|
||
ctx.globalAlpha=0.08+tw*0.12; ctx.fillRect(x,y,1.5,1.5);
|
||
}
|
||
ctx.globalAlpha=1;
|
||
// kenarlar
|
||
for(const [a,b] of edges){
|
||
const n1=byId(a), n2=byId(b);
|
||
const active = selected && (a===selected||b===selected);
|
||
ctx.strokeStyle = active? "rgba(93,211,158,.65)":"rgba(93,211,158,.14)";
|
||
ctx.lineWidth = active?1.6:1;
|
||
ctx.beginPath(); ctx.moveTo(n1.x,n1.y); ctx.lineTo(n2.x,n2.y); ctx.stroke();
|
||
}
|
||
// düğümler
|
||
for(const n of nodes){
|
||
const note=NOTES[n.id], r=RADII[note.tier], c=COLORS[note.tier];
|
||
const isSel=n.id===selected, isHov=n.id===hover;
|
||
const pulse = reduced?0:Math.sin(t*3+n.x)*1.2;
|
||
ctx.beginPath(); ctx.arc(n.x,n.y,r+(isSel?4:isHov?2:0)+ (note.tier===2?pulse*0.5:0),0,7);
|
||
ctx.fillStyle=c; ctx.shadowColor=c; ctx.shadowBlur=isSel||isHov?22:10; ctx.fill(); ctx.shadowBlur=0;
|
||
ctx.fillStyle = isSel||isHov ? "#e9f5ec" : "rgba(201,214,204,.75)";
|
||
ctx.font="12.5px ui-monospace,Menlo,monospace"; ctx.textAlign="center";
|
||
ctx.fillText(note.title, n.x, n.y+r+18);
|
||
}
|
||
step();
|
||
requestAnimationFrame(draw);
|
||
}
|
||
requestAnimationFrame(draw);
|
||
|
||
function pick(x,y){
|
||
for(const n of nodes){ if(Math.hypot(n.x-x,n.y-y)<22) return n; }
|
||
return null;
|
||
}
|
||
cv.addEventListener("pointermove",e=>{
|
||
if(drag){drag.x=e.clientX;drag.y=e.clientY;drag.vx=drag.vy=0;return;}
|
||
hover=pick(e.clientX,e.clientY)?.id||null;
|
||
cv.style.cursor=hover?"pointer":"grab";
|
||
});
|
||
let downAt=null;
|
||
cv.addEventListener("pointerdown",e=>{
|
||
const n=pick(e.clientX,e.clientY);
|
||
if(n){drag=n;cv.classList.add("dragging");downAt=[e.clientX,e.clientY];cv.setPointerCapture(e.pointerId);}
|
||
});
|
||
cv.addEventListener("pointerup",e=>{
|
||
cv.classList.remove("dragging");
|
||
if(drag&&downAt&&Math.hypot(e.clientX-downAt[0],e.clientY-downAt[1])<6) openCard(drag.id);
|
||
drag=null;downAt=null;
|
||
});
|
||
|
||
function openCard(id){
|
||
const n=NOTES[id]; selected=id;
|
||
document.getElementById("hint").classList.add("hide");
|
||
document.getElementById("c-stage").textContent=n.stage;
|
||
document.getElementById("c-title").textContent=n.title;
|
||
document.getElementById("c-meta").textContent=n.meta;
|
||
document.getElementById("c-body").innerHTML=n.body;
|
||
document.getElementById("c-links").innerHTML=n.links.map(([t,u])=>`<a href="${u}" target="_blank" rel="noopener">${t}</a>`).join("");
|
||
const also=n.edges.filter(e=>NOTES[e]);
|
||
document.getElementById("c-also").innerHTML = also.length?
|
||
"kökleri şuraya uzanıyor: "+also.map(e=>`<button onclick="openCard('${e}')">${NOTES[e].title}</button>`).join(""):"";
|
||
document.getElementById("card").classList.add("open");
|
||
}
|
||
function closeCard(){document.getElementById("card").classList.remove("open");selected=null;}
|
||
addEventListener("keydown",e=>{if(e.key==="Escape")closeCard();});
|
||
|
||
// tmux saati
|
||
const clock=document.getElementById("clock");
|
||
setInterval(()=>{const d=new Date();clock.textContent=d.toLocaleTimeString("tr-TR",{hour:"2-digit",minute:"2-digit"});},1000);
|
||
// açılışta merkezi ipucu
|
||
setTimeout(()=>{ if(!selected) openCard("ben"); }, 1400);
|
||
</script>
|
||
</body>
|
||
</html>
|