beast-agent 0.24.5 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -58,8 +58,6 @@ const els = {
58
58
  bbShot: $('#bbShot'),
59
59
  bbClose: $('#bbClose'),
60
60
  bbResize: $('#bbResize'),
61
- termBtn: $('#termBtn'),
62
- termGBtn: $('#termGBtn'),
63
61
  termCBtn: $('#termCBtn'),
64
62
  termPanel: $('#termPanel'),
65
63
  termCwd: $('#termCwd'),
@@ -114,8 +112,8 @@ let streamEl = null;
114
112
  let streamRaw = '';
115
113
  let renderQueued = false;
116
114
 
117
- /* #19 sağ panel tercih durumu (tarayıcı açılmadan önceki) */
118
- let railPrefBeforeBrowser = true;
115
+ /* #19 sağ panel tercih durumu (tarayıcı açılmadan önceki) — panel varsayılan KAPALI */
116
+ let railPrefBeforeBrowser = false;
119
117
 
120
118
  function toggleRail(hide) {
121
119
  document.body.classList.toggle('rail-hidden', !!hide);
@@ -285,11 +283,18 @@ function setBusy(b) {
285
283
  }
286
284
 
287
285
  let toastTimer = null;
286
+ let toastHideTimer = null;
288
287
  function toast(msg) {
288
+ /* akış içi balon: göster → fade-in, süre dolunca fade-out → tamamen gizle (yer kaplamasın) */
289
289
  els.toast.textContent = msg;
290
- els.toast.classList.add('show');
290
+ els.toast.hidden = false;
291
+ requestAnimationFrame(() => els.toast.classList.add('show'));
291
292
  clearTimeout(toastTimer);
292
- toastTimer = setTimeout(() => els.toast.classList.remove('show'), 2200);
293
+ clearTimeout(toastHideTimer);
294
+ toastTimer = setTimeout(() => {
295
+ els.toast.classList.remove('show');
296
+ toastHideTimer = setTimeout(() => { els.toast.hidden = true; }, 260);
297
+ }, 2200);
293
298
  }
294
299
 
295
300
  /* ---------------- messages ---------------- */
@@ -497,15 +502,12 @@ async function renderSessions(list) {
497
502
  async function refreshSessions() {
498
503
  await renderSessions(await beast.listSessions());
499
504
  renderBotCards(); // bot kartlarındaki numara/sayı etiketleri de tazelensin
500
- renderGuestBotMenu(); // davet menüsü (aktif bot/bot listesi değişmiş olabilir)
501
505
  }
502
506
 
503
507
  async function openSession(id) {
504
508
  activeId = id;
505
509
  streamEl = null;
506
510
  const s = await beast.openSession(id);
507
- activeGuests = Array.isArray(s.guests) ? s.guests : [];
508
- renderGuestBotMenu();
509
511
  els.msgs.innerHTML = '';
510
512
  showEmpty(s.messages.length === 0);
511
513
  renderTodos(s.todos || []);
@@ -2462,47 +2464,6 @@ let botsCache = [];
2462
2464
  let botPageId = null; // null = genel bakış (Tüm Botlar)
2463
2465
  let botPageStats = [];
2464
2466
  let activeBotId = 'beast'; // masaüstü UI'ının şu an hangi botta olduğu (varsayılan: ilk bot/Beast)
2465
- let activeGuests = []; // aktif ana sohbete davetli botlar [{id, code, name}]
2466
-
2467
- /* ANA SOHBETE BOT DAVETİ: yalnız admin bottayken görünür (composer'daki 🤖 ikonu);
2468
- davetli botlar session'a kaydedilir, ajan bot_dm aracıyla onlara danışır */
2469
- function renderGuestBotMenu() {
2470
- const btn = $('#guestBotBtn');
2471
- const menu = $('#guestBotMenu');
2472
- if (!btn || !menu) return;
2473
- const isAdmin = activeBotId === 'beast';
2474
- const show = isAdmin && !!activeId;
2475
- /* DİKKAT: hidden ATTRIBUTUE'sünü kaldırmak şart — style.display ile açılmaz */
2476
- btn.hidden = !show;
2477
- if (!show) { menu.hidden = true; return; }
2478
- const candidates = botsCache.filter((b) => !b.admin && b.vis !== false);
2479
- menu.innerHTML = '';
2480
- if (!candidates.length) {
2481
- /* bot hiç yoksa yönlendir + tek tıkla oluştur; bot varsa da davet edilebilir değilse bilgi ver */
2482
- const nonAdmin = botsCache.filter((b) => !b.admin).length;
2483
- menu.innerHTML = nonAdmin
2484
- ? '<div class="dd-empty">Davet edilebilir görünür bot yok</div>'
2485
- : '<div class="dd-empty">Henüz bot yok — <a href="#" id="gbCreate" style="color:var(--accent);font-weight:700">bot oluştur</a></div>';
2486
- const cr = menu.querySelector('#gbCreate');
2487
- if (cr) cr.addEventListener('click', (e) => { e.preventDefault(); menu.hidden = true; botAddClick(); });
2488
- return;
2489
- }
2490
- for (const b of candidates) {
2491
- const invited = activeGuests.some((g) => g.id === b.id);
2492
- const item = document.createElement('div');
2493
- item.className = 'dd-item';
2494
- item.innerHTML = `${b.icon} ${escapeHtml(b.name)} <span style="float:right;color:var(--muted)">${invited ? '✓ davetli' : '+ davet et'}</span>`;
2495
- item.addEventListener('click', async () => {
2496
- const next = invited ? activeGuests.filter((g) => g.id !== b.id) : [...activeGuests, { id: b.id, code: b.code || '', name: b.name }];
2497
- activeGuests = next;
2498
- await beast.sessionSetGuests(activeId, next).catch(() => {});
2499
- renderGuestBotMenu();
2500
- toast(invited ? `${b.name} davetten çıkarıldı` : `${b.name} sohbete davet edildi — ajan gerektiğinde ona danışacak`);
2501
- menu.hidden = true;
2502
- });
2503
- menu.appendChild(item);
2504
- }
2505
- }
2506
2467
 
2507
2468
  async function refreshBots() {
2508
2469
  try {
@@ -2833,9 +2794,6 @@ function renderBotSettings(pane, b) {
2833
2794
  <div class="bot-checks" style="margin-bottom:6px">
2834
2795
  <label><input type="checkbox" id="bExtBrowser" ${b.extBrowser ? 'checked' : ''}/> ${_t('bot_ext_browser')}</label>
2835
2796
  </div>
2836
- <div class="bot-checks" style="margin-bottom:6px">
2837
- <label><input type="checkbox" id="bVis" ${b.vis !== false ? 'checked' : ''}/> ${_t('bot_vis')}</label>
2838
- </div>
2839
2797
  <div class="form-grid">
2840
2798
  <div>
2841
2799
  <label class="mem-label" style="margin-top:0">${_t('bot_def_browser')}</label>
@@ -2896,7 +2854,6 @@ function renderBotSettings(pane, b) {
2896
2854
  prompt: $('#bPrompt').value,
2897
2855
  seeBots: [...f.querySelectorAll('#bSee input:checked')].map((c) => c.dataset.see),
2898
2856
  extBrowser: $('#bExtBrowser').checked,
2899
- vis: f.querySelector('#bVis') ? f.querySelector('#bVis').checked : true,
2900
2857
  browserDefault: $('#bBrDef').value,
2901
2858
  extCommand: $('#bBrCmd').value.trim(),
2902
2859
  numbers: (b.numbers || []).map((n) => n.num),
@@ -3055,6 +3012,7 @@ function renderBotAdd(pane) {
3055
3012
 
3056
3013
  const agentState = {
3057
3014
  jobs: [],
3015
+ autoOpened: false, // konsol bu turda otomatik açıldı mı (boştan-çalışıyor geçişi)
3058
3016
  last: new Map(), // bgSessionId -> son aktivite metni
3059
3017
  runningIds: new Set(),
3060
3018
  expanded: new Set(), // detayı açık kartlar
@@ -3082,11 +3040,26 @@ function scheduleAgentsRender() {
3082
3040
 
3083
3041
  async function refreshAgentsPane() {
3084
3042
  try { agentState.jobs = (await beast.agentsList()) || []; } catch {}
3043
+ /* açılışta ajanlar zaten çalışıyorsa konsolu otomatik aç */
3044
+ maybeAutoOpenRail();
3085
3045
  updateAgentIds();
3086
3046
  renderAgentsPane();
3087
3047
  renderAgentRail();
3088
3048
  }
3089
3049
 
3050
+ /* ajanlar çalışmaya başladığında sağdaki Paralel Ajan konsolu otomatik açılır
3051
+ (yalnız boştan-çalışıyor geçişinde; kullanıcı çalışırken elle kapatırsa ezilmez,
3052
+ dahili tarayıcı açıkken de karışılmaz) */
3053
+ function maybeAutoOpenRail() {
3054
+ const hasRunning = agentState.jobs.some((j) => j.status === 'running');
3055
+ if (hasRunning && !agentState.autoOpened && document.body.classList.contains('rail-hidden') &&
3056
+ !document.body.classList.contains('browser-open')) {
3057
+ toggleRail(false);
3058
+ railPrefBeforeBrowser = true;
3059
+ }
3060
+ agentState.autoOpened = hasRunning;
3061
+ }
3062
+
3090
3063
  function ingestAgentActivity(ev) {
3091
3064
  const id = ev.sessionId;
3092
3065
  let label = agentState.last.get(id) || '';
@@ -3347,6 +3320,7 @@ function onEvent(ev) {
3347
3320
  }
3348
3321
  if (ev.type === 'agents') {
3349
3322
  agentState.jobs = ev.jobs || [];
3323
+ maybeAutoOpenRail();
3350
3324
  updateAgentIds();
3351
3325
  scheduleAgentsRender();
3352
3326
  return;
@@ -3512,23 +3486,19 @@ let termBannerDone = false;
3512
3486
  let termRunning = false;
3513
3487
  let termHist = [];
3514
3488
  let termHistIdx = -1;
3515
- let termShell = 'powershell';
3489
+ let termShell = 'cmd';
3516
3490
  const TERM_MAX_LINES = 800;
3517
3491
 
3518
- /* terminal kabukları: > PowerShell · G Git Bash · C CMD */
3492
+ /* terminal kabuğu: yalnız CMD (PowerShell/Git Bash kaldırıldı) */
3519
3493
  const TERM_SHELLS = {
3520
- powershell: { label: 'PowerShell', prompt: 'PS>' },
3521
- bash: { label: 'Git Bash', prompt: 'bash $' },
3522
- cmd: { label: 'CMD', prompt: 'C>' },
3494
+ cmd: { label: 'CMD', prompt: 'CMD>' },
3523
3495
  };
3524
3496
 
3525
3497
  function termSetShell(s) {
3526
- if (!TERM_SHELLS[s]) s = 'powershell';
3498
+ if (!TERM_SHELLS[s]) s = 'cmd';
3527
3499
  const changed = termShell !== s;
3528
3500
  termShell = s;
3529
3501
  if (els.termPrompt) els.termPrompt.textContent = TERM_SHELLS[s].prompt;
3530
- if (els.termBtn) els.termBtn.classList.toggle('on', termOpen && s === 'powershell');
3531
- if (els.termGBtn) els.termGBtn.classList.toggle('on', termOpen && s === 'bash');
3532
3502
  if (els.termCBtn) els.termCBtn.classList.toggle('on', termOpen && s === 'cmd');
3533
3503
  if (changed && termOpen && termBannerDone) termLine('t-sys', 'Kabuk: ' + TERM_SHELLS[s].label);
3534
3504
  }
@@ -3573,7 +3543,7 @@ function termBanner(cwd) {
3573
3543
  els.termCwd.title = cwd || '';
3574
3544
  termLine('t-sys', 'Beast Terminal — çalışma klasörü: ' + (cwd || '?'));
3575
3545
  termLine('t-sys', 'Kabuk: ' + TERM_SHELLS[termShell].label + ' · Ajanlar çalışırken araç çağrıları ve çıktıları burada canlı akar.', false);
3576
- termLine('t-dim', 'Komut yaz, Enter\'a bas — her komut workspace\'te ayrı süreçle çalışır. Üst bar: > PowerShell · G Git Bash · C CMD.', false);
3546
+ termLine('t-dim', 'Komut yaz, Enter\'a bas — her komut workspace\'te ayrı süreçle çalışır. Kabuk: CMD.', false);
3577
3547
  }
3578
3548
 
3579
3549
  function termShortTool(name, args) {
@@ -3608,7 +3578,7 @@ function termCmdDone(ev) {
3608
3578
  }
3609
3579
 
3610
3580
  async function termToggle(shell) {
3611
- const want = TERM_SHELLS[shell] ? shell : 'powershell';
3581
+ const want = TERM_SHELLS[shell] ? shell : 'cmd';
3612
3582
  /* aynı kabuk butonuna tekrar basıldıysa paneli kapat */
3613
3583
  if (termOpen && termShell === want) {
3614
3584
  termSetOpen(false);
@@ -3635,7 +3605,7 @@ function termRunCurrent() {
3635
3605
  termHist.push(cmd);
3636
3606
  if (termHist.length > 100) termHist.shift();
3637
3607
  termHistIdx = termHist.length;
3638
- termLine('t-cmd', 'PS> ' + cmd);
3608
+ termLine('t-cmd', TERM_SHELLS[termShell].prompt + ' ' + cmd);
3639
3609
  termRunning = true;
3640
3610
  els.termStop.hidden = false;
3641
3611
  beast.terminalRun(cmd, termShell).then((r) => {
@@ -4075,6 +4045,7 @@ function openCron() { refreshCron(); }
4075
4045
 
4076
4046
  async function init() {
4077
4047
  beast.onEvent(onEvent);
4048
+ toggleRail(true); /* Paralel Ajanlar paneli varsayılan KAPALI — railBtn ile açılır */
4078
4049
 
4079
4050
  state = await beast.getState();
4080
4051
  try {
@@ -4411,8 +4382,6 @@ async function init() {
4411
4382
 
4412
4383
  /* terminal paneli — olay bağlama */
4413
4384
  try { termSetWidth(parseInt(localStorage.getItem('beast.termW')) || 520); } catch {}
4414
- if (els.termBtn) els.termBtn.addEventListener('click', () => termToggle('powershell'));
4415
- if (els.termGBtn) els.termGBtn.addEventListener('click', () => termToggle('bash'));
4416
4385
  if (els.termCBtn) els.termCBtn.addEventListener('click', () => termToggle('cmd'));
4417
4386
  if (els.termClose) els.termClose.addEventListener('click', () => termSetOpen(false));
4418
4387
  if (els.termClear) els.termClear.addEventListener('click', () => { els.termOut.innerHTML = ''; });
@@ -4485,18 +4454,6 @@ async function init() {
4485
4454
  if ($('#botClose')) $('#botClose').addEventListener('click', () => botOverlaySetOpen(false));
4486
4455
  if ($('#botOverviewBack')) $('#botOverviewBack').addEventListener('click', () => openBotPage(null));
4487
4456
  if ($('#botChip')) $('#botChip').addEventListener('click', () => openBotPage(activeBotId));
4488
-
4489
- /* BOT DAVET MENÜSÜ: butona tıkla → aç/kapa; dışarı tık → kapa */
4490
- if ($('#guestBotBtn') && $('#guestBotMenu')) {
4491
- $('#guestBotBtn').addEventListener('click', (e) => {
4492
- e.stopPropagation();
4493
- renderGuestBotMenu();
4494
- $('#guestBotMenu').hidden = !$('#guestBotMenu').hidden;
4495
- });
4496
- document.addEventListener('click', (e) => {
4497
- if (!$('#guestBotMenu').hidden && !$('#guestBotMenu').contains(e.target)) $('#guestBotMenu').hidden = true;
4498
- });
4499
- }
4500
4457
  /* kalıcı aktif bot: restart sonrası aynı botun UI'ı açılır */
4501
4458
  beast.botsActiveGet().then((r) => { activeBotId = (r && r.id) || 'beast'; updateBotChip(); renderBotCards(); }).catch(() => {});
4502
4459
  document.querySelectorAll('.btab').forEach((b) =>
@@ -4899,31 +4856,276 @@ document.querySelectorAll('.stab').forEach((b) =>
4899
4856
  renderStorePane();
4900
4857
  })
4901
4858
  );
4902
-
4903
-
4904
- /* ================= IDE MODU =================
4905
- Tek tuş (#ideBtn): sol → dosya gezgini, orta → chat, sağ → dahili tarayıcı (preview).
4906
- Preview: workspace kökündeki index.html (yoksa ilk *.html) sağdaki tarayıcıda açılır.
4907
- Dosyaya tıkla → editör; klasöre tıkla → aç/kapa. */
4908
-
4909
- const IDE_ICONS = {
4910
- '.html': '🌐', '.htm': '🌐', '.css': '🎨', '.js': '📜', '.mjs': '📜', '.cjs': '📜',
4911
- '.ts': '📜', '.jsx': '📜', '.tsx': '📜', '.json': '🧾', '.md': '📝', '.txt': '📄',
4912
- '.py': '🐍', '.ps1': '⚙️', '.bat': '⚙️', '.cmd': '⚙️', '.sh': '⚙️', '.yaml': '🧾',
4913
- '.yml': '🧾', '.png': '🖼️', '.jpg': '🖼️', '.jpeg': '🖼️', '.gif': '🖼️', '.svg': '🖼️',
4914
- '.pdf': '📕',
4915
- };
4916
-
4859
+
4860
+
4861
+ /* ================= GITHUB TRENDING =================
4862
+ Skills Store'un sağındaki buton → %70 modal (store ile aynı tasarım).
4863
+ SOL: son 14 günün yıldız yükselenleri (GitHub Search API).
4864
+ SAĞ: favoriler (localStorage'da kalıcı). Her kartta:
4865
+ ★ favori · Chate at (ajana araştırma mesajı) · Kopyala · repoya git. */
4866
+
4867
+ const ghState = { items: [], err: '', range: '2w', sort: 'desc', q: '', ghTab: 'trend', allQ: '', allSort: 'best' };
4868
+ const GH_FAV_KEY = 'beast.github.favs';
4869
+ const GH_RANGE_LABEL = {
4870
+ all: '— tüm zamanların en yıldızlıları',
4871
+ '6m': '— son 6 ayın yıldız yükselenleri',
4872
+ '1m': '— son 1 ayın yıldız yükselenleri',
4873
+ '2w': '— son 2 haftanın yıldız yükselenleri',
4874
+ '1w': '— son 1 haftanın yıldız yükselenleri',
4875
+ };
4876
+ const GH_ALL_SORT_LABEL = { best: '⭐ En İyi', desc: '↓ Yıldız', asc: '↑ Yıldız' };
4877
+
4878
+ function ghFavs() {
4879
+ try { return JSON.parse(localStorage.getItem(GH_FAV_KEY) || '[]'); } catch { return []; }
4880
+ }
4881
+ function ghSaveFavs(list) {
4882
+ try { localStorage.setItem(GH_FAV_KEY, JSON.stringify(list.slice(0, 100))); } catch {}
4883
+ }
4884
+ function ghIsFav(fullName) {
4885
+ return ghFavs().some((f) => f.full_name === fullName);
4886
+ }
4887
+
4888
+ async function openGit() {
4889
+ $('#ghOverlay').hidden = false;
4890
+ renderGhFavs();
4891
+ updateGhSortBtn();
4892
+ updateGhAllSortBtn();
4893
+ updateGhSub();
4894
+ loadGhTrend();
4895
+ }
4896
+
4897
+ async function loadGhTrend() {
4898
+ const box = $('#ghTrendList');
4899
+ /* tüm repolar sekmesinde boş arama = API'yi boşa yorma, ipucu göster */
4900
+ if (ghState.ghTab === 'all' && !ghState.allQ) {
4901
+ box.innerHTML = '<div class="gh-empty">Depo adı, konu veya anahtar kelime yazıp Enter\u2019a bas — yıldız sayısına bakılmaksızın tüm GitHub aranır.</div>';
4902
+ return;
4903
+ }
4904
+ box.innerHTML = '<div class="gh-empty">Yükleniyor…</div>';
4905
+ const params = ghState.ghTab === 'all'
4906
+ ? { mode: 'all', q: ghState.allQ, allSort: ghState.allSort, order: ghState.allSort === 'desc' ? 'desc' : 'asc' }
4907
+ : { mode: 'trend', range: ghState.range, sort: ghState.sort, q: ghState.q };
4908
+ const r = await beast.githubTrending(params).catch(() => ({ ok: false, error: 'ipc' }));
4909
+ if (r && r.ok) {
4910
+ ghState.items = r.items || [];
4911
+ ghState.err = '';
4912
+ } else {
4913
+ ghState.items = [];
4914
+ ghState.err = (r && r.error) || 'GitHub\u2019a ulaşılamadı';
4915
+ }
4916
+ renderGhTrend();
4917
+ }
4918
+
4919
+ function updateGhSortBtn() {
4920
+ const btn = $('#ghSortBtn');
4921
+ if (btn) {
4922
+ btn.textContent = ghState.sort === 'desc' ? '↓ Yıldız' : '↑ Yıldız';
4923
+ btn.title = ghState.sort === 'desc' ? 'Yıldız sırası: azalan — artana çevir' : 'Yıldız sırası: artan — azalana çevir';
4924
+ }
4925
+ }
4926
+
4927
+ function updateGhSub() {
4928
+ const sub = $('#ghTrendSub');
4929
+ if (!sub) return;
4930
+ if (ghState.ghTab === 'all') {
4931
+ const sortTxt = ghState.allSort === 'best' ? 'en iyi eşleşme' : ghState.allSort === 'desc' ? 'yıldız azalan' : 'yıldız artan';
4932
+ sub.textContent = ghState.allQ ? `— "${ghState.allQ}" araması · ${sortTxt}` : '— tüm GitHub: ada, konuya, açıklamaya göre arama';
4933
+ } else {
4934
+ sub.textContent = ghState.q ? `— "${ghState.q}" araması · tüm zamanlar` : GH_RANGE_LABEL[ghState.range] || '';
4935
+ }
4936
+ }
4937
+
4938
+ function updateGhAllSortBtn() {
4939
+ const btn = $('#ghAllSortBtn');
4940
+ if (btn) btn.textContent = GH_ALL_SORT_LABEL[ghState.allSort] || '⭐ En İyi';
4941
+ }
4942
+
4943
+ function closeGit() {
4944
+ $('#ghOverlay').hidden = true;
4945
+ }
4946
+
4947
+ /* chate at: ajana araştırma komutu gider (oturum yoksa input'a bırakılır) */
4948
+ function ghSendToChat(url, name) {
4949
+ const msg = `GitHub\u2019da şu repoyu araştır: ${url}${name ? ' (' + name + ')' : ''} — ne işe yarıyor, öne çıkan özellikleri neler, aktif mi? Kısa özet ver.`;
4950
+ if (!activeId) {
4951
+ els.input.value = msg;
4952
+ toast('Aktif oturum yok — mesaj kutusuna bırakıldı');
4953
+ return;
4954
+ }
4955
+ els.input.value = msg;
4956
+ sendCurrent();
4957
+ }
4958
+
4959
+ function ghCard(repo, isFav) {
4960
+ const stars = repo.stars >= 1000 ? (repo.stars / 1000).toFixed(1).replace('.0', '') + 'k' : String(repo.stars);
4961
+ const lang = repo.language ? `<span class="gh-lang">${escapeHtml(repo.language)}</span>` : '';
4962
+ return `
4963
+ <div class="gh-card" data-repo="${escapeHtml(repo.full_name)}">
4964
+ <div class="gh-card-top">
4965
+ <span class="gh-name" data-url="${escapeHtml(repo.html_url)}" title="${escapeHtml(repo.html_url)}">${escapeHtml(repo.full_name)}</span>
4966
+ <span class="gh-stars">★ ${stars}</span>
4967
+ ${lang}
4968
+ </div>
4969
+ ${repo.description ? `<div class="gh-desc" title="${escapeHtml(repo.description)}">${escapeHtml(repo.description)}</div>` : ''}
4970
+ <div class="gh-actions">
4971
+ <button class="gh-fav ${isFav ? 'on' : ''}" data-act="fav">${isFav ? '★ Favoride' : '☆ Favorile'}</button>
4972
+ <button data-act="chat"><svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-1px;margin-right:4px"><path d="M22 2 11 13"/><path d="M22 2 15 22l-4-9-9-4z"/></svg>Chate at</button>
4973
+ <button data-act="copy">⧉ Kopyala</button>
4974
+ </div>
4975
+ </div>`;
4976
+ }
4977
+
4978
+ function wireGhCards(container, list, isFavPane) {
4979
+ container.querySelectorAll('.gh-card').forEach((card) => {
4980
+ const repo = list.find((x) => x.full_name === card.dataset.repo);
4981
+ if (!repo) return;
4982
+ card.querySelector('.gh-name').addEventListener('click', () => beast.openExternal(repo.html_url).catch(() => {}));
4983
+ card.querySelector('[data-act="chat"]').addEventListener('click', () => {
4984
+ ghSendToChat(repo.html_url, repo.full_name);
4985
+ /* mesaj chate gitti — cevap chatten geleceği için modal kapanır */
4986
+ closeGit();
4987
+ });
4988
+ card.querySelector('[data-act="copy"]').addEventListener('click', async () => {
4989
+ try { await navigator.clipboard.writeText(repo.html_url); toast('Link kopyalandı'); } catch {}
4990
+ });
4991
+ card.querySelector('[data-act="fav"]').addEventListener('click', () => {
4992
+ const favs = ghFavs();
4993
+ if (ghIsFav(repo.full_name)) {
4994
+ ghSaveFavs(favs.filter((f) => f.full_name !== repo.full_name));
4995
+ toast('Favoriden çıkarıldı: ' + repo.full_name);
4996
+ } else {
4997
+ favs.unshift({ ...repo, addedAt: new Date().toISOString() });
4998
+ ghSaveFavs(favs);
4999
+ toast('Favorilere eklendi: ' + repo.full_name);
5000
+ }
5001
+ renderGhTrend();
5002
+ renderGhFavs();
5003
+ });
5004
+ });
5005
+ }
5006
+
5007
+ function renderGhTrend() {
5008
+ const box = $('#ghTrendList');
5009
+ if (!box) return;
5010
+ if (ghState.err) {
5011
+ box.innerHTML = `<div class="gh-err">⚠ ${escapeHtml(ghState.err)}</div>`;
5012
+ return;
5013
+ }
5014
+ if (!ghState.items.length) {
5015
+ box.innerHTML = '<div class="gh-empty">Son 14 günde öne çıkan repo bulunamadı.</div>';
5016
+ return;
5017
+ }
5018
+ box.innerHTML = ghState.items.map((r) => ghCard(r, ghIsFav(r.full_name))).join('');
5019
+ wireGhCards(box, ghState.items, false);
5020
+ }
5021
+
5022
+ function renderGhFavs() {
5023
+ const box = $('#ghFavList');
5024
+ if (!box) return;
5025
+ const favs = ghFavs();
5026
+ if (!favs.length) {
5027
+ box.innerHTML = '<div class="gh-empty">Henüz favori yok — trendden ☆ ile ekle.</div>';
5028
+ return;
5029
+ }
5030
+ box.innerHTML = favs.map((r) => ghCard(r, true)).join('');
5031
+ wireGhCards(box, favs, true);
5032
+ }
5033
+
5034
+ $('#gitBtn').addEventListener('click', openGit);
5035
+ $('#ghClose').addEventListener('click', closeGit);
5036
+ $('#ghOverlay').addEventListener('click', (e) => {
5037
+ if (e.target === $('#ghOverlay')) closeGit();
5038
+ });
5039
+ /* aralık filtreleri: Tümü / 6 Ay / 1 Ay / 2 Hafta / 1 Hafta */
5040
+ document.querySelectorAll('#ghRanges button').forEach((b) =>
5041
+ b.addEventListener('click', () => {
5042
+ ghState.range = b.dataset.range;
5043
+ document.querySelectorAll('#ghRanges button').forEach((x) => x.classList.toggle('on', x === b));
5044
+ loadGhTrend();
5045
+ updateGhSub();
5046
+ })
5047
+ );
5048
+ /* yıldız sırası: azalan ⇄ artan */
5049
+ $('#ghSortBtn').addEventListener('click', () => {
5050
+ ghState.sort = ghState.sort === 'desc' ? 'asc' : 'desc';
5051
+ updateGhSortBtn();
5052
+ loadGhTrend();
5053
+ });
5054
+ /* repo arama: Enter ile; boş Enter → trend moduna döner */
5055
+ $('#ghSearch').addEventListener('keydown', (e) => {
5056
+ if (e.key !== 'Enter') return;
5057
+ ghState.q = $('#ghSearch').value.trim();
5058
+ updateGhSub();
5059
+ loadGhTrend();
5060
+ });
5061
+ /* sol sekme: Trend Depolar ⇄ Tüm Repolar (eşit bölünmüş, ortalanmış) */
5062
+ document.querySelectorAll('.gh-tab').forEach((b) =>
5063
+ b.addEventListener('click', () => {
5064
+ ghState.ghTab = b.dataset.ghtab;
5065
+ document.querySelectorAll('.gh-tab').forEach((x) => x.classList.toggle('on', x === b));
5066
+ $('#ghTrendTools').hidden = ghState.ghTab !== 'trend';
5067
+ $('#ghAllTools').hidden = ghState.ghTab !== 'all';
5068
+ updateGhSub();
5069
+ loadGhTrend();
5070
+ })
5071
+ );
5072
+ /* tüm repolar arama + üçlü sıralama: en iyi eşleşme → azalan → artan */
5073
+ $('#ghAllSearch').addEventListener('keydown', (e) => {
5074
+ if (e.key !== 'Enter') return;
5075
+ ghState.allQ = $('#ghAllSearch').value.trim();
5076
+ updateGhSub();
5077
+ loadGhTrend();
5078
+ });
5079
+ $('#ghAllSortBtn').addEventListener('click', () => {
5080
+ ghState.allSort = ghState.allSort === 'best' ? 'desc' : ghState.allSort === 'desc' ? 'asc' : 'best';
5081
+ updateGhAllSortBtn();
5082
+ updateGhSub();
5083
+ loadGhTrend();
5084
+ });
5085
+ /* ⟳ sıfırla + yenile: ilgili sekmenin filtrelerini başa alır, modal ilk açıldığı gibi yüklenir */
5086
+ $('#ghTrendRefresh').addEventListener('click', () => {
5087
+ ghState.range = '2w';
5088
+ ghState.sort = 'desc';
5089
+ ghState.q = '';
5090
+ $('#ghSearch').value = '';
5091
+ document.querySelectorAll('#ghRanges button').forEach((x) => x.classList.toggle('on', x.dataset.range === '2w'));
5092
+ updateGhSortBtn();
5093
+ updateGhSub();
5094
+ loadGhTrend();
5095
+ });
5096
+ $('#ghAllRefresh').addEventListener('click', () => {
5097
+ ghState.allQ = '';
5098
+ ghState.allSort = 'best';
5099
+ $('#ghAllSearch').value = '';
5100
+ updateGhAllSortBtn();
5101
+ updateGhSub();
5102
+ loadGhTrend();
5103
+ });
5104
+
5105
+
5106
+ /* ================= IDE MODU =================
5107
+ Tek tuş (#ideBtn): sol → dosya gezgini, orta → chat, sağ → dahili tarayıcı (preview).
5108
+ Preview: workspace kökündeki index.html (yoksa ilk *.html) sağdaki tarayıcıda açılır.
5109
+ Dosyaya tıkla → editör; klasöre tıkla → aç/kapa. */
5110
+
5111
+ const IDE_ICONS = {
5112
+ '.html': '🌐', '.htm': '🌐', '.css': '🎨', '.js': '📜', '.mjs': '📜', '.cjs': '📜',
5113
+ '.ts': '📜', '.jsx': '📜', '.tsx': '📜', '.json': '🧾', '.md': '📝', '.txt': '📄',
5114
+ '.py': '🐍', '.ps1': '⚙️', '.bat': '⚙️', '.cmd': '⚙️', '.sh': '⚙️', '.yaml': '🧾',
5115
+ '.yml': '🧾', '.png': '🖼️', '.jpg': '🖼️', '.jpeg': '🖼️', '.gif': '🖼️', '.svg': '🖼️',
5116
+ '.pdf': '📕',
5117
+ };
5118
+
4917
5119
  const ideState = {
4918
5120
  cache: new Map(), // rel -> entries[]
4919
5121
  open: new Set(['']),
4920
5122
  path: '',
4921
- };
4922
-
4923
- function ideModeOn() {
4924
- return document.body.classList.contains('ide-mode');
4925
- }
4926
-
5123
+ };
5124
+
5125
+ function ideModeOn() {
5126
+ return document.body.classList.contains('ide-mode');
5127
+ }
5128
+
4927
5129
  async function setIdeMode(on) {
4928
5130
  document.body.classList.toggle('ide-mode', !!on);
4929
5131
  /* IDE modu oturumluk: uygulama HER AÇILIŞTA agent (chat) modunda başlar */
@@ -4995,52 +5197,52 @@ if (els.ideSplit) {
4995
5197
  window.addEventListener('resize', () => {
4996
5198
  if (ideModeOn()) ideSplitApplyFrac();
4997
5199
  });
4998
- }
4999
-
5000
- async function loadIdeTree() {
5001
- ideState.cache.clear();
5002
- await renderFileTree();
5003
- }
5004
-
5005
- function fmtFileSize(n) {
5006
- n = Number(n) || 0;
5007
- if (n >= 1048576) return (n / 1048576).toFixed(1) + 'M';
5008
- if (n >= 1024) return Math.round(n / 1024) + 'K';
5009
- return n ? n + 'B' : '';
5010
- }
5011
-
5012
- async function renderFileTree() {
5013
- const tree = $('#fileTree');
5014
- if (!tree) return;
5015
- tree.innerHTML = '';
5016
- const wsLabel = $('#filePanelPath');
5017
- const build = async (rel, depth, container) => {
5018
- let entries = ideState.cache.get(rel);
5019
- if (!entries) {
5020
- const r = await beast.ideTree(rel).catch(() => null);
5021
- if (!r || !r.ok) {
5022
- if (!rel) container.innerHTML = `<div class="file-empty">${escapeHtml((r && r.error) || 'okunamadı')}</div>`;
5023
- return;
5024
- }
5025
- entries = r.entries;
5026
- ideState.cache.set(rel, entries);
5200
+ }
5201
+
5202
+ async function loadIdeTree() {
5203
+ ideState.cache.clear();
5204
+ await renderFileTree();
5205
+ }
5206
+
5207
+ function fmtFileSize(n) {
5208
+ n = Number(n) || 0;
5209
+ if (n >= 1048576) return (n / 1048576).toFixed(1) + 'M';
5210
+ if (n >= 1024) return Math.round(n / 1024) + 'K';
5211
+ return n ? n + 'B' : '';
5212
+ }
5213
+
5214
+ async function renderFileTree() {
5215
+ const tree = $('#fileTree');
5216
+ if (!tree) return;
5217
+ tree.innerHTML = '';
5218
+ const wsLabel = $('#filePanelPath');
5219
+ const build = async (rel, depth, container) => {
5220
+ let entries = ideState.cache.get(rel);
5221
+ if (!entries) {
5222
+ const r = await beast.ideTree(rel).catch(() => null);
5223
+ if (!r || !r.ok) {
5224
+ if (!rel) container.innerHTML = `<div class="file-empty">${escapeHtml((r && r.error) || 'okunamadı')}</div>`;
5225
+ return;
5226
+ }
5227
+ entries = r.entries;
5228
+ ideState.cache.set(rel, entries);
5027
5229
  if (r.workspace) {
5028
5230
  ideState.path = r.workspace;
5029
5231
  if (wsLabel) { wsLabel.textContent = r.workspace; wsLabel.title = r.workspace; }
5030
5232
  if (els.bcCwd) { els.bcCwd.textContent = r.workspace; els.bcCwd.title = r.workspace; }
5031
5233
  bcOnFolderChanged(r.workspace);
5032
- }
5033
- }
5034
- for (const e of entries) {
5035
- const child = rel ? rel + '/' + e.name : e.name;
5036
- const row = document.createElement('div');
5037
- row.className = 'file-row';
5038
- row.style.paddingLeft = 6 + depth * 14 + 'px';
5039
- const ico = e.dir ? (ideState.open.has(child) ? '📂' : '📁') : IDE_ICONS[e.name.slice(e.name.lastIndexOf('.')).toLowerCase()] || '📄';
5040
- row.innerHTML =
5041
- `<span class="f-ico">${ico}</span>` +
5042
- `<span class="f-nm" title="${escapeHtml(e.name)}">${escapeHtml(e.name)}</span>` +
5043
- (e.dir ? '' : `<span class="f-sz">${fmtFileSize(e.size)}</span>`);
5234
+ }
5235
+ }
5236
+ for (const e of entries) {
5237
+ const child = rel ? rel + '/' + e.name : e.name;
5238
+ const row = document.createElement('div');
5239
+ row.className = 'file-row';
5240
+ row.style.paddingLeft = 6 + depth * 14 + 'px';
5241
+ const ico = e.dir ? (ideState.open.has(child) ? '📂' : '📁') : IDE_ICONS[e.name.slice(e.name.lastIndexOf('.')).toLowerCase()] || '📄';
5242
+ row.innerHTML =
5243
+ `<span class="f-ico">${ico}</span>` +
5244
+ `<span class="f-nm" title="${escapeHtml(e.name)}">${escapeHtml(e.name)}</span>` +
5245
+ (e.dir ? '' : `<span class="f-sz">${fmtFileSize(e.size)}</span>`);
5044
5246
  row.addEventListener('click', () => {
5045
5247
  if (e.dir) {
5046
5248
  if (ideState.open.has(child)) ideState.open.delete(child);
@@ -5280,7 +5482,7 @@ $('#fileTree').addEventListener('contextmenu', (e) => {
5280
5482
  return;
5281
5483
  }
5282
5484
  fileCtxShow(e, row.dataset.rel, row.dataset.dir === '1');
5283
- });
5485
+ });
5284
5486
 
5285
5487
  /* ---------- IDE modu ortası: BEAST CODE paneli ----------
5286
5488
  Sohbet yerine Beast'in kendi ajanı (varsayılan model) çalışır;
@@ -5627,4 +5829,4 @@ if (els.bcNew) els.bcNew.addEventListener('click', async () => {
5627
5829
  }
5628
5830
  });
5629
5831
  /* Uygulama her açılışta agent (chat) modunda başlar — IDE modu yalnız
5630
- kullanıcı butona basınca girilir (oturumluk). */
5832
+ kullanıcı butona basınca girilir (oturumluk). */