beast-agent 1.9.0 → 2.0.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.
Files changed (52) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +130 -130
  3. package/bin/beast-agent.js +137 -137
  4. package/package.json +1 -1
  5. package/scripts/fix-electron.js +138 -138
  6. package/scripts/release.js +137 -131
  7. package/scripts/swap-electron.js +40 -40
  8. package/src/agent/agentdefs.js +122 -122
  9. package/src/agent/bots.js +580 -580
  10. package/src/agent/bus.js +389 -389
  11. package/src/agent/computeruse.js +200 -200
  12. package/src/agent/config.js +284 -284
  13. package/src/agent/discord.js +332 -332
  14. package/src/agent/engine.js +75 -10
  15. package/src/agent/kb.js +123 -123
  16. package/src/agent/llm.js +430 -430
  17. package/src/agent/logger.js +90 -90
  18. package/src/agent/mcp.js +427 -427
  19. package/src/agent/mem0.js +605 -605
  20. package/src/agent/memory.js +427 -427
  21. package/src/agent/mqueue.js +124 -124
  22. package/src/agent/pdf.js +20 -20
  23. package/src/agent/research.js +133 -133
  24. package/src/agent/scripts/news.py +113 -113
  25. package/src/agent/scripts/stealthsearch.py +30 -30
  26. package/src/agent/scripts/websearch.py +225 -225
  27. package/src/agent/searxng.js +325 -325
  28. package/src/agent/seeds/brainstorming/SKILL.md +90 -90
  29. package/src/agent/seeds/dispatching-parallel-agents/SKILL.md +120 -120
  30. package/src/agent/seeds/executing-plans/SKILL.md +60 -60
  31. package/src/agent/seeds/subagent-driven-development/SKILL.md +167 -167
  32. package/src/agent/seeds/systematic-debugging/SKILL.md +131 -131
  33. package/src/agent/seeds/test-driven-development/SKILL.md +152 -152
  34. package/src/agent/seeds/verification-before-completion/SKILL.md +63 -63
  35. package/src/agent/seeds/writing-plans/SKILL.md +162 -162
  36. package/src/agent/seeds/writing-skills/SKILL.md +229 -229
  37. package/src/agent/skills.js +652 -652
  38. package/src/agent/store.js +378 -378
  39. package/src/agent/telegram.js +155 -155
  40. package/src/agent/tokens.js +39 -39
  41. package/src/agent/usage.js +125 -125
  42. package/src/agent/watchers.js +312 -312
  43. package/src/agent/watext.js +80 -80
  44. package/src/agent/whatsapp.js +555 -555
  45. package/src/cron.js +255 -255
  46. package/src/main.js +348 -6
  47. package/src/preload.js +9 -0
  48. package/src/renderer/browserPreload.js +73 -73
  49. package/src/renderer/i18n.js +4 -0
  50. package/src/renderer/index.html +448 -409
  51. package/src/renderer/renderer.js +824 -41
  52. package/src/renderer/style.css +264 -5
@@ -80,6 +80,28 @@ const els = {
80
80
  bcAttach: $('#bcAttach'),
81
81
  bcMic: $('#bcMic'),
82
82
  bcChips: $('#bcChips'),
83
+ studioBtn: $('#studioBtn'),
84
+ stPanel: $('#stPanel'),
85
+ stTitle: $('#stTitle'),
86
+ stCwd: $('#stCwd'),
87
+ stOut: $('#stOut'),
88
+ stTodoWrap: $('#stTodoWrap'),
89
+ stStatus: $('#stStatus'),
90
+ stChips: $('#stChips'),
91
+ stInput: $('#stInput'),
92
+ stStop: $('#stStop'),
93
+ stClear: $('#stClear'),
94
+ stNew: $('#stNew'),
95
+ stAttach: $('#stAttach'),
96
+ stMic: $('#stMic'),
97
+ studioRow: $('#studioRow'),
98
+ stSplit: $('#stSplit'),
99
+ stVideoEl: $('#stVideoEl'),
100
+ stVideoName: $('#stVideoName'),
101
+ stVideoOpen: $('#stVideoOpen'),
102
+ stVideoExt: $('#stVideoExt'),
103
+ stVideoBody: $('#stVideoBody'),
104
+ stVideoEmpty: $('#stVideoEmpty'),
83
105
  codeTabs: $('#codeTabs'),
84
106
  codeTa: $('#codeTa'),
85
107
  codeGutter: $('#codeGutter'),
@@ -881,8 +903,9 @@ async function openSession(id) {
881
903
 
882
904
  const TEXT_EXT = /\.(txt|md|json|csv|log|js|ts|py|ps1|bat|cmd|html|css|yaml|yml|xml|ini)$/i;
883
905
 
884
- /* ek hedefleri: chat + Beast Code panosu ayrı kuyruklar */
906
+ /* ek hedefleri: chat + Beast Code + Beast Studio ayrı kuyruklar */
885
907
  let bcPending = [];
908
+ let stPending = [];
886
909
 
887
910
  function renderChips() {
888
911
  els.chips.innerHTML = '';
@@ -925,25 +948,49 @@ function renderBcChips() {
925
948
  });
926
949
  }
927
950
 
951
+ function renderStChips() {
952
+ if (!els.stChips) return;
953
+ els.stChips.innerHTML = '';
954
+ els.stChips.hidden = stPending.length === 0;
955
+ stPending.forEach((a, i) => {
956
+ const c = document.createElement('span');
957
+ c.className = 'chip';
958
+ const label = a.type === 'image' ? `\u25A3 ${a.name}` : `\u2261 ${a.name}`;
959
+ c.appendChild(document.createTextNode(label));
960
+ const x = document.createElement('span');
961
+ x.className = 'x';
962
+ x.textContent = '×';
963
+ x.addEventListener('click', () => {
964
+ stPending.splice(i, 1);
965
+ renderStChips();
966
+ });
967
+ c.appendChild(x);
968
+ els.stChips.appendChild(c);
969
+ });
970
+ }
971
+
928
972
  function addFiles(files, mode) {
929
973
  const isBc = mode === 'bc';
974
+ const isSt = mode === 'st';
975
+ const getList = () => (isBc ? bcPending : isSt ? stPending : pending);
976
+ const paint = () => (isBc ? renderBcChips() : isSt ? renderStChips() : renderChips());
930
977
  for (const f of files) {
931
978
  if (f.size > 8 * 1024 * 1024) { toast('Çok büyük (max 8MB): ' + f.name); continue; }
932
979
  if (f.type.startsWith('image/')) {
933
980
  const r = new FileReader();
934
981
  r.onload = () => {
935
- const list = isBc ? bcPending : pending;
982
+ const list = getList();
936
983
  if (list.length >= 6) { toast('En fazla 6 ek'); return; }
937
984
  list.push({ type: 'image', name: f.name, dataUrl: String(r.result) });
938
- isBc ? renderBcChips() : renderChips();
985
+ paint();
939
986
  };
940
987
  r.readAsDataURL(f);
941
988
  } else if (TEXT_EXT.test(f.name)) {
942
989
  f.text().then((t) => {
943
- const list = isBc ? bcPending : pending;
990
+ const list = getList();
944
991
  if (list.length >= 6) { toast('En fazla 6 ek'); return; }
945
992
  list.push({ type: 'file', name: f.name, content: t.slice(0, 200000) });
946
- isBc ? renderBcChips() : renderChips();
993
+ paint();
947
994
  });
948
995
  } else {
949
996
  toast('Desteklenmeyen tür: ' + f.name);
@@ -4245,6 +4292,12 @@ function renderAgentsPane() {
4245
4292
  function onEvent(ev) {
4246
4293
  if (ev.type === 'sessions') { refreshSessions(); return; }
4247
4294
  if (ev.type === 'approval') {
4295
+ /* Beast Studio oturumunun onayı: panelde ipucu + kart sohbete düşer */
4296
+ if (stSessionId && ev.sessionId === stSessionId) {
4297
+ showApprovalCard(ev);
4298
+ stLine('t-dim', '⏳ onay bekleniyor: ' + (ev.tool || '?') + ' — chat moduna geçip kartı onayla');
4299
+ return;
4300
+ }
4248
4301
  /* Beast Code oturumunun onayı: panelde ipucu + kart sohbete düşer */
4249
4302
  if (bcSessionId && ev.sessionId === bcSessionId) {
4250
4303
  showApprovalCard(ev);
@@ -4270,6 +4323,12 @@ function onEvent(ev) {
4270
4323
  }
4271
4324
  /* OFFLINE MESAJ KUYRUĞU: bağlantı + kuyruk olayları (sessionId filtresinden önce) */
4272
4325
  if (ev.type === 'net' || ev.type === 'netQueue') { onNetEvent(ev); return; }
4326
+ /* Beast Studio oturumu: olaylar SADECE Studio paneline akar — ana sohbeti
4327
+ ve Beast Code panelini kirletmez (dünyalar ayrı) */
4328
+ if (stSessionId && ev.sessionId === stSessionId) {
4329
+ stIngest(ev);
4330
+ return;
4331
+ }
4273
4332
  /* Beast Code oturumu (IDE modu ortasındaki panel): olayları panele akıt,
4274
4333
  ana sohbeti kirletme */
4275
4334
  if (bcSessionId && ev.sessionId === bcSessionId) {
@@ -5209,12 +5268,15 @@ async function init() {
5209
5268
  document.addEventListener('langchange', syncSttLang);
5210
5269
  }
5211
5270
 
5212
- /* + butonu iki hedefe hizmet eder: chat composer VE Beast Code paneli */
5271
+ /* + butonu üç hedefe hizmet eder: chat composer, Beast Code VE Beast Studio */
5213
5272
  let fileSink = 'chat';
5214
5273
  els.attachBtn.addEventListener('click', () => { fileSink = 'chat'; els.fileInput.click(); });
5215
5274
  if (els.bcAttach) {
5216
5275
  els.bcAttach.addEventListener('click', () => { fileSink = 'bc'; els.fileInput.click(); });
5217
5276
  }
5277
+ if (els.stAttach) {
5278
+ els.stAttach.addEventListener('click', () => { fileSink = 'st'; els.fileInput.click(); });
5279
+ }
5218
5280
  els.fileInput.addEventListener('change', () => {
5219
5281
  addFiles([...els.fileInput.files], fileSink);
5220
5282
  els.fileInput.value = '';
@@ -5276,6 +5338,12 @@ async function init() {
5276
5338
  els.bcInput.focus();
5277
5339
  bcInputResize();
5278
5340
  });
5341
+ setupMicButton(els.stMic, (t) => {
5342
+ if (!els.stInput) return;
5343
+ els.stInput.value = (els.stInput.value ? els.stInput.value.replace(/\s+$/, '') + ' ' : '') + t;
5344
+ els.stInput.focus();
5345
+ stInputResize();
5346
+ });
5279
5347
 
5280
5348
  beast.onWaEvent(onWaEvent);
5281
5349
  beast.onTgEvent(onTgEvent);
@@ -6144,13 +6212,27 @@ $('#ghAllRefresh').addEventListener('click', () => {
6144
6212
  Dosyaya tıkla → editör; klasöre tıkla → aç/kapa. */
6145
6213
 
6146
6214
  const IDE_ICONS = {
6147
- '.html': '🌐', '.htm': '🌐', '.css': '🎨', '.js': '📜', '.mjs': '📜', '.cjs': '📜',
6148
- '.ts': '📜', '.jsx': '📜', '.tsx': '📜', '.json': '🧾', '.md': '📝', '.txt': '📄',
6149
- '.py': '🐍', '.ps1': '⚙️', '.bat': '⚙️', '.cmd': '⚙️', '.sh': '⚙️', '.yaml': '🧾',
6150
- '.yml': '🧾', '.png': '🖼️', '.jpg': '🖼️', '.jpeg': '🖼️', '.gif': '🖼️', '.svg': '🖼️',
6151
- '.pdf': '📕',
6215
+ '.html': '🌐', '.htm': '🌐', '.css': '🎨', '.scss': '🎨', '.sass': '🎨', '.less': '🎨',
6216
+ '.js': '📜', '.mjs': '📜', '.cjs': '📜', '.ts': '📜', '.mts': '📜', '.cts': '📜',
6217
+ '.jsx': '📜', '.tsx': '📜', '.vue': '📜', '.svelte': '📜', '.astro': '📜',
6218
+ '.json': '🧾', '.jsonc': '🧾', '.yaml': '🧾', '.yml': '🧾', '.toml': '🧾', '.xml': '🧾',
6219
+ '.md': '📝', '.mdx': '📝', '.txt': '📄', '.log': '📄', '.csv': '📊', '.tsv': '📊',
6220
+ '.py': '🐍', '.go': '🐹', '.rs': '🦀', '.java': '☕', '.kt': '🧩', '.swift': '🕊️',
6221
+ '.rb': '💎', '.php': '🐘', '.cs': '🎯', '.c': '🔧', '.h': '🔧', '.cpp': '🔧',
6222
+ '.hpp': '🔧', '.sql': '🗄️', '.sh': '⚙️', '.ps1': '⚙️', '.bat': '⚙️', '.cmd': '⚙️',
6223
+ '.lua': '🌙', '.r': '📈', '.dart': '🎯', '.graphql': '◈', '.proto': '◈',
6224
+ '.png': '🖼️', '.jpg': '🖼️', '.jpeg': '🖼️', '.gif': '🖼️', '.svg': '🖼️', '.webp': '🖼️',
6225
+ '.pdf': '📕', '.env': '🔑',
6226
+ /* Studio medya ikonları — IDE ağacında da doğru simge */
6227
+ '.mp4': '🎬', '.mkv': '🎬', '.mov': '🎬', '.avi': '🎬', '.webm': '🎬', '.m4v': '🎬',
6228
+ '.mpg': '🎬', '.mpeg': '🎬', '.wmv': '🎬', '.flv': '🎬', '.3gp': '🎬', '.ogv': '🎬',
6229
+ '.mp3': '🎵', '.wav': '🎵', '.ogg': '🎵', '.m4a': '🎵', '.flac': '🎵', '.aac': '🎵', '.opus': '🎵',
6230
+ '.srt': '💬', '.vtt': '💬', '.ass': '💬', '.ssa': '💬',
6152
6231
  };
6153
6232
 
6233
+ /* Studio modunda ağaçta TIKLANAN medya dosyaları video bölümünde oynatılır */
6234
+ const MEDIA_FILE_RE = /\.(mp4|mkv|mov|avi|webm|m4v|mpg|mpeg|wmv|flv|3gp|ogv|mp3|wav|ogg|m4a|flac|aac|opus)$/i;
6235
+
6154
6236
  const ideState = {
6155
6237
  cache: new Map(), // rel -> entries[]
6156
6238
  open: new Set(['']),
@@ -6162,8 +6244,10 @@ function ideModeOn() {
6162
6244
  }
6163
6245
 
6164
6246
  async function setIdeMode(on) {
6247
+ if (on && studioModeOn()) await setStudioMode(false); /* Studio açıkken IDE'ye geçiş — Studio kapanır */
6165
6248
  document.body.classList.toggle('ide-mode', !!on);
6166
- /* soldaki marka: chat modunda BEAST Agent, IDE modunda BEAST Code */
6249
+ if (els.ideBtn) els.ideBtn.classList.toggle('on', !!on);
6250
+ /* soldaki marka: chat modunda BEAST Agent, IDE modunda BEAST Code, Studio modunda BEAST Studio */
6167
6251
  const brandSub = document.querySelector('#brand .brand-sub');
6168
6252
  if (brandSub) brandSub.textContent = on ? 'Code' : 'Agent';
6169
6253
  /* rail her iki modda da DEFAULT KAPALI — çalışan paralel ajan varsa
@@ -6188,6 +6272,166 @@ async function setIdeMode(on) {
6188
6272
  }
6189
6273
  }
6190
6274
 
6275
+ /* ================= BEAST STUDIO MODU =================
6276
+ Beast Code ile AYRI dünya: sol klasör konsolu (başlık: BEAST STUDIO),
6277
+ sağ → chat + video bölümü. Oturumlar main tarafında studioRoot bazlı ve
6278
+ ÖZELDİR — chat geçmişi listesine VE Beast Code paneline karışmaz. */
6279
+
6280
+ const studioState = {
6281
+ cache: new Map(),
6282
+ open: new Set(['']),
6283
+ path: '',
6284
+ };
6285
+
6286
+ function studioModeOn() {
6287
+ return document.body.classList.contains('studio-mode');
6288
+ }
6289
+
6290
+ async function setStudioMode(on) {
6291
+ if (on && ideModeOn()) await setIdeMode(false); /* IDE açıkken Studio'ya geçiş — IDE kapanır */
6292
+ document.body.classList.toggle('studio-mode', !!on);
6293
+ if (els.studioBtn) els.studioBtn.classList.toggle('on', !!on);
6294
+ const brandSub = document.querySelector('#brand .brand-sub');
6295
+ if (brandSub) brandSub.textContent = on ? 'Studio' : 'Agent';
6296
+ /* klasör konsolunun en üstü: Studio modunda "BEAST STUDIO" yazar */
6297
+ const ftp = document.getElementById('filePanelTitle');
6298
+ if (ftp) {
6299
+ if (on) {
6300
+ ftp.textContent = 'BEAST STUDIO';
6301
+ ftp.removeAttribute('data-i18n');
6302
+ } else {
6303
+ ftp.setAttribute('data-i18n', 'ide_files');
6304
+ ftp.textContent = (window.I18N && window.I18N.t('ide_files')) || 'DOSYALAR';
6305
+ }
6306
+ }
6307
+ toggleRail(true);
6308
+ railManualOpen = false;
6309
+ /* Studio'da web önizleme yok — açık tarayıcıyı her iki yönde de kapat */
6310
+ if (document.body.classList.contains('browser-open')) {
6311
+ try { beast.toggleBrowser(); } catch {}
6312
+ }
6313
+ if (on) {
6314
+ stSplitApply();
6315
+ await loadStudioTree();
6316
+ stBanner();
6317
+ } else {
6318
+ stVideoStop();
6319
+ }
6320
+ }
6321
+
6322
+ async function loadStudioTree() {
6323
+ studioState.cache.clear();
6324
+ await renderFileTree();
6325
+ }
6326
+
6327
+ /* ---------- chat/video ayırıcı (IDE split ile aynı model) ---------- */
6328
+ let stSplitFrac = 0;
6329
+
6330
+ function stSplitApply() {
6331
+ if (!stSplitFrac) {
6332
+ let saved = 0;
6333
+ try { saved = parseFloat(localStorage.getItem('beast.studioSplit')) || 0; } catch {}
6334
+ stSplitFrac = saved > 0.05 && saved < 0.95 ? saved : 0.48;
6335
+ }
6336
+ stSplitFrac = Math.max(0.2, Math.min(stSplitFrac, 0.8));
6337
+ const rowW = els.studioRow && els.studioRow.clientWidth ? els.studioRow.clientWidth : window.innerWidth - 250;
6338
+ const w = Math.max(300, Math.min(Math.round(rowW * stSplitFrac), Math.max(340, rowW - 260)));
6339
+ document.body.style.setProperty('--stSplit', w + 'px');
6340
+ }
6341
+
6342
+ if (els.stSplit) {
6343
+ els.stSplit.addEventListener('mousedown', (e) => {
6344
+ e.preventDefault();
6345
+ els.stSplit.classList.add('dragging');
6346
+ const rowLeft = els.stPanel ? els.stPanel.getBoundingClientRect().left : 250;
6347
+ const rowW = Math.max(600, els.studioRow && els.studioRow.clientWidth ? els.studioRow.clientWidth : window.innerWidth - 250);
6348
+ const move = (ev) => {
6349
+ const w = Math.max(300, Math.min(ev.clientX - rowLeft, rowW - 260));
6350
+ stSplitFrac = Math.max(0.2, Math.min(w / rowW, 0.8));
6351
+ document.body.style.setProperty('--stSplit', Math.round(w) + 'px');
6352
+ };
6353
+ const up = () => {
6354
+ document.removeEventListener('mousemove', move);
6355
+ document.removeEventListener('mouseup', up);
6356
+ els.stSplit.classList.remove('dragging');
6357
+ try { localStorage.setItem('beast.studioSplit', String(stSplitFrac)); } catch {}
6358
+ };
6359
+ document.addEventListener('mousemove', move);
6360
+ document.addEventListener('mouseup', up);
6361
+ });
6362
+ window.addEventListener('resize', () => {
6363
+ if (studioModeOn()) stSplitApply();
6364
+ });
6365
+ }
6366
+
6367
+ /* ---------- video bölümü ----------
6368
+ Sol ağaçtan video/ses seçilir veya "Aç" ile dosya seçilir; ajanın ffmpeg
6369
+ çıktıları (output/ klasörü) ağaçta belirdiği an burada oynatılabilir. */
6370
+ let stVideoPath = '';
6371
+
6372
+ function stVideoFileUrl(abs) {
6373
+ let p = String(abs || '').replace(/\\/g, '/');
6374
+ if (!p.startsWith('/')) p = '/' + p;
6375
+ return 'file://' + p.split('/').map((seg) => encodeURIComponent(seg)).join('/');
6376
+ }
6377
+
6378
+ function stVideoPlayPath(abs) {
6379
+ if (!els.stVideoEl) return;
6380
+ stVideoPath = String(abs || '');
6381
+ els.stVideoEl.src = stVideoFileUrl(stVideoPath);
6382
+ els.stVideoEl.hidden = false;
6383
+ if (els.stVideoEmpty) els.stVideoEmpty.hidden = true;
6384
+ const nm = stVideoPath.split(/[\\/]/).pop() || '—';
6385
+ if (els.stVideoName) { els.stVideoName.textContent = nm; els.stVideoName.title = stVideoPath; }
6386
+ els.stVideoEl.play().catch(() => {});
6387
+ }
6388
+
6389
+ function stVideoPlay(rel) {
6390
+ const root = studioState.path;
6391
+ if (!root || !rel) return;
6392
+ stVideoPlayPath(root + '\\' + String(rel).replace(/\//g, '\\'));
6393
+ }
6394
+
6395
+ function stVideoStop() {
6396
+ if (!els.stVideoEl) return;
6397
+ try { els.stVideoEl.pause(); } catch {}
6398
+ els.stVideoEl.removeAttribute('src');
6399
+ try { els.stVideoEl.load(); } catch {}
6400
+ els.stVideoEl.hidden = true;
6401
+ if (els.stVideoEmpty) els.stVideoEmpty.hidden = false;
6402
+ if (els.stVideoName) els.stVideoName.textContent = '—';
6403
+ stVideoPath = '';
6404
+ }
6405
+
6406
+ if (els.stVideoOpen) {
6407
+ els.stVideoOpen.addEventListener('click', async () => {
6408
+ const r = await beast.studioPickVideo().catch(() => null);
6409
+ if (r && r.ok && r.path) stVideoPlayPath(r.path);
6410
+ else if (r && !r.canceled && r.error) toast(r.error);
6411
+ });
6412
+ }
6413
+ if (els.stVideoExt) {
6414
+ els.stVideoExt.addEventListener('click', () => {
6415
+ if (!stVideoPath) { toast('Açık video yok'); return; }
6416
+ beast.openPath(stVideoPath).catch(() => {});
6417
+ });
6418
+ }
6419
+ /* sürükle-bırak: video dosyası doğrudan video bölümüne bırakılabilir */
6420
+ if (els.stVideoBody) {
6421
+ els.stVideoBody.addEventListener('dragover', (e) => {
6422
+ e.preventDefault();
6423
+ els.stVideoBody.classList.add('dragover');
6424
+ });
6425
+ els.stVideoBody.addEventListener('dragleave', () => els.stVideoBody.classList.remove('dragover'));
6426
+ els.stVideoBody.addEventListener('drop', (e) => {
6427
+ e.preventDefault();
6428
+ els.stVideoBody.classList.remove('dragover');
6429
+ const f = e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0];
6430
+ if (f && f.path && MEDIA_FILE_RE.test(f.name)) stVideoPlayPath(f.path);
6431
+ else if (f) toast('Medya dosyası değil: ' + f.name);
6432
+ });
6433
+ }
6434
+
6191
6435
  /* ---------- editör panelini kapat/göster ----------
6192
6436
  Kapatınca kod bölmesi gider, Beast Code tam genişlik alır; tercih
6193
6437
  localStorage'da kalıcıdır. Dosya ağacından dosya açınca geri açılır. */
@@ -6279,21 +6523,31 @@ async function renderFileTree() {
6279
6523
  if (!tree) return;
6280
6524
  tree.innerHTML = '';
6281
6525
  const wsLabel = $('#filePanelPath');
6526
+ /* Studio modunda ağaç studioRoot'tan, IDE modunda ideRoot'tan okunur —
6527
+ aynı panel, aynı çizim; yalnız kaynak ve davranış moduna göre değişir */
6528
+ const studio = studioModeOn();
6529
+ const st = studio ? studioState : ideState;
6530
+ const treeApi = studio ? beast.studioTree : beast.ideTree;
6282
6531
  const build = async (rel, depth, container) => {
6283
- let entries = ideState.cache.get(rel);
6532
+ let entries = st.cache.get(rel);
6284
6533
  if (!entries) {
6285
- const r = await beast.ideTree(rel).catch(() => null);
6534
+ const r = await treeApi(rel).catch(() => null);
6286
6535
  if (!r || !r.ok) {
6287
6536
  if (!rel) container.innerHTML = `<div class="file-empty">${escapeHtml((r && r.error) || 'okunamadı')}</div>`;
6288
6537
  return;
6289
6538
  }
6290
6539
  entries = r.entries;
6291
- ideState.cache.set(rel, entries);
6540
+ st.cache.set(rel, entries);
6292
6541
  if (r.workspace) {
6293
- ideState.path = r.workspace;
6542
+ st.path = r.workspace;
6294
6543
  if (wsLabel) { wsLabel.textContent = r.workspace; wsLabel.title = r.workspace; }
6295
- if (els.bcCwd) { els.bcCwd.textContent = r.workspace; els.bcCwd.title = r.workspace; }
6296
- bcOnFolderChanged(r.workspace);
6544
+ if (studio) {
6545
+ if (els.stCwd) { els.stCwd.textContent = r.workspace; els.stCwd.title = r.workspace; }
6546
+ stOnFolderChanged(r.workspace);
6547
+ } else {
6548
+ if (els.bcCwd) { els.bcCwd.textContent = r.workspace; els.bcCwd.title = r.workspace; }
6549
+ bcOnFolderChanged(r.workspace);
6550
+ }
6297
6551
  }
6298
6552
  }
6299
6553
  for (const e of entries) {
@@ -6301,16 +6555,21 @@ async function renderFileTree() {
6301
6555
  const row = document.createElement('div');
6302
6556
  row.className = 'file-row';
6303
6557
  row.style.paddingLeft = 6 + depth * 14 + 'px';
6304
- const ico = e.dir ? (ideState.open.has(child) ? '📂' : '📁') : IDE_ICONS[e.name.slice(e.name.lastIndexOf('.')).toLowerCase()] || '📄';
6558
+ const ext = e.name.slice(e.name.lastIndexOf('.')).toLowerCase();
6559
+ const ico = e.dir ? (st.open.has(child) ? '📂' : '📁') : IDE_ICONS[ext] || '📄';
6305
6560
  row.innerHTML =
6306
6561
  `<span class="f-ico">${ico}</span>` +
6307
6562
  `<span class="f-nm" title="${escapeHtml(e.name)}">${escapeHtml(e.name)}</span>` +
6308
6563
  (e.dir ? '' : `<span class="f-sz">${fmtFileSize(e.size)}</span>`);
6309
6564
  row.addEventListener('click', () => {
6310
6565
  if (e.dir) {
6311
- if (ideState.open.has(child)) ideState.open.delete(child);
6312
- else ideState.open.add(child);
6566
+ if (st.open.has(child)) st.open.delete(child);
6567
+ else st.open.add(child);
6313
6568
  renderFileTree();
6569
+ } else if (studio && MEDIA_FILE_RE.test(e.name)) {
6570
+ stVideoPlay(child); /* video/ses → sağdaki video bölümünde oynat */
6571
+ } else if (studio) {
6572
+ toast('Studio modunda medya dosyaları oynatılır — dosya düzenleme Beast Code modunda');
6314
6573
  } else {
6315
6574
  codeOpen(child);
6316
6575
  }
@@ -6318,7 +6577,7 @@ async function renderFileTree() {
6318
6577
  row.dataset.rel = child;
6319
6578
  row.dataset.dir = e.dir ? '1' : '0';
6320
6579
  container.appendChild(row);
6321
- if (e.dir && ideState.open.has(child)) {
6580
+ if (e.dir && st.open.has(child)) {
6322
6581
  const box = document.createElement('div');
6323
6582
  container.appendChild(box);
6324
6583
  await build(child, depth + 1, box);
@@ -6337,7 +6596,9 @@ function ideRefreshTree() {
6337
6596
  ideTreeTimer = setTimeout(() => {
6338
6597
  ideTreeTimer = null;
6339
6598
  try {
6340
- ideState.cache.clear();
6599
+ /* hangi moddaysak o ağacın önbelleği tazelenir */
6600
+ if (studioModeOn()) studioState.cache.clear();
6601
+ else ideState.cache.clear();
6341
6602
  renderFileTree().catch(() => {});
6342
6603
  } catch {}
6343
6604
  }, 600);
@@ -6730,16 +6991,29 @@ async function codeReloadIfOpen(p) {
6730
6991
  }
6731
6992
 
6732
6993
  $('#ideBtn').addEventListener('click', () => setIdeMode(!ideModeOn()));
6994
+ if (els.studioBtn) els.studioBtn.addEventListener('click', () => setStudioMode(!studioModeOn()));
6733
6995
  $('#filePick').addEventListener('click', async () => {
6734
- const r = await beast.ideSetRoot().catch(() => null);
6996
+ /* Studio modunda klasör seçimi Studio'nun KENDİ kökünü değiştirir —
6997
+ IDE köküne dokunmaz (dünyalar ayrıdır) */
6998
+ const r = studioModeOn()
6999
+ ? await beast.studioSetRoot().catch(() => null)
7000
+ : await beast.ideSetRoot().catch(() => null);
6735
7001
  if (r && r.ok) {
6736
- ideState.open = new Set(['']);
6737
- await loadIdeTree();
7002
+ if (studioModeOn()) {
7003
+ studioState.open = new Set(['']);
7004
+ await loadStudioTree();
7005
+ } else {
7006
+ ideState.open = new Set(['']);
7007
+ await loadIdeTree();
7008
+ }
6738
7009
  } else if (r && !r.canceled && r.error) {
6739
7010
  toast(r.error);
6740
7011
  }
6741
7012
  });
6742
- $('#fileRefresh').addEventListener('click', () => loadIdeTree());
7013
+ $('#fileRefresh').addEventListener('click', () => {
7014
+ if (studioModeOn()) loadStudioTree();
7015
+ else loadIdeTree();
7016
+ });
6743
7017
 
6744
7018
  /* ---------- görev listesi + TEK TUŞ GERİ ALMA (dosya paneli) ---------- */
6745
7019
  let tasksPanelOpen = false;
@@ -6754,11 +7028,14 @@ function toggleTasksPanel(force) {
6754
7028
  async function renderTasksPanel() {
6755
7029
  const box = els.tasksPanel;
6756
7030
  if (!box) return;
6757
- if (!bcSessionId) {
6758
- box.innerHTML = '<div class="tsk-empty">\u00D6nce Beast Code\u2019da bir mesaj yaz — g\u00F6rev listesi i\u00E7in oturum gerekli.</div>';
7031
+ const sid = studioModeOn() ? stSessionId : bcSessionId;
7032
+ if (!sid) {
7033
+ box.innerHTML = '<div class="tsk-empty">' + (studioModeOn()
7034
+ ? '\u00D6nce Beast Studio\u2019da bir mesaj yaz — g\u00F6rev listesi i\u00E7in oturum gerekli.'
7035
+ : '\u00D6nce Beast Code\u2019da bir mesaj yaz — g\u00F6rev listesi i\u00E7in oturum gerekli.') + '</div>';
6759
7036
  return;
6760
7037
  }
6761
- const r = await beast.bcTodos(bcSessionId).catch(() => null);
7038
+ const r = await beast.bcTodos(sid).catch(() => null);
6762
7039
  if (!r || !r.ok) {
6763
7040
  box.innerHTML = '<div class="tsk-empty">' + escapeHtml((r && r.error) || 'okunamad\u0131') + '</div>';
6764
7041
  return;
@@ -6794,9 +7071,10 @@ async function renderTasksPanel() {
6794
7071
  }
6795
7072
 
6796
7073
  async function undoTask(id) {
6797
- if (!bcSessionId) return;
7074
+ const sid = studioModeOn() ? stSessionId : bcSessionId;
7075
+ if (!sid) return;
6798
7076
  if (busy) { toast('Ajan \u00E7al\u0131\u015F\u0131yor — \u25A0 ile durdurup \u00F6yle geri al'); return; }
6799
- const r = await beast.bcUndo(bcSessionId, id).catch(() => null);
7077
+ const r = await beast.bcUndo(sid, id).catch(() => null);
6800
7078
  if (r && r.ok) {
6801
7079
  toast('\u21B6 Geri al\u0131nd\u0131 — ' + (r.reverted || 0) + ' dosya \u00F6nceki h\u00E2line d\u00F6nd\u00FC');
6802
7080
  ideRefreshTree();
@@ -6895,17 +7173,23 @@ function fileCtxShow(e, rel, isDir) {
6895
7173
  const menu = els.fileCtxMenu;
6896
7174
  if (!menu || !rel) return;
6897
7175
  e.preventDefault();
6898
- const isHtml = /\.html?$/i.test(rel);
7176
+ const studio = studioModeOn();
7177
+ const st = studio ? studioState : ideState;
7178
+ const isMedia = MEDIA_FILE_RE.test(rel.split('/').pop() || '');
6899
7179
  const items = [];
6900
7180
  if (isDir) {
6901
7181
  items.push({
6902
- label: ideState.open.has(rel) ? 'Kapat' : 'Aç',
7182
+ label: st.open.has(rel) ? 'Kapat' : 'Aç',
6903
7183
  fn: () => {
6904
- if (ideState.open.has(rel)) ideState.open.delete(rel);
6905
- else ideState.open.add(rel);
7184
+ if (st.open.has(rel)) st.open.delete(rel);
7185
+ else st.open.add(rel);
6906
7186
  renderFileTree();
6907
7187
  },
6908
7188
  });
7189
+ } else if (studio && isMedia) {
7190
+ items.push({ label: 'Oynat', fn: () => stVideoPlay(rel) });
7191
+ } else if (studio) {
7192
+ items.push({ label: 'Dış uygulamada aç', fn: () => beast.openPath((studioState.path || '') + '\\' + rel.replace(/\//g, '\\')).catch(() => {}) });
6909
7193
  } else {
6910
7194
  items.push({ label: 'Aç', fn: () => codeOpen(rel) });
6911
7195
  if (isHtml) {
@@ -6922,13 +7206,17 @@ function fileCtxShow(e, rel, isDir) {
6922
7206
  label: 'Sil',
6923
7207
  danger: true,
6924
7208
  fn: async () => {
6925
- const r = await beast.ideDelete(rel).catch(() => null);
7209
+ const r = studio
7210
+ ? await beast.studioDelete(rel).catch(() => null)
7211
+ : await beast.ideDelete(rel).catch(() => null);
6926
7212
  if (r && r.ok) {
6927
- /* silinen dosya/klasörün açık sekmelerini kapat */
6928
- for (let i = codeTabs.length - 1; i >= 0; i--) {
6929
- if (codeTabs[i].rel === rel || codeTabs[i].rel.startsWith(rel + '/')) codeClose(i);
7213
+ if (!studio) {
7214
+ /* silinen dosya/klasörün açık sekmelerini kapat */
7215
+ for (let i = codeTabs.length - 1; i >= 0; i--) {
7216
+ if (codeTabs[i].rel === rel || codeTabs[i].rel.startsWith(rel + '/')) codeClose(i);
7217
+ }
6930
7218
  }
6931
- await loadIdeTree();
7219
+ await renderFileTree();
6932
7220
  } else if (r && !r.canceled && r.error) {
6933
7221
  toast(r.error);
6934
7222
  }
@@ -6962,12 +7250,113 @@ if (els.fileCtxMenu) {
6962
7250
  $('#fileTree').addEventListener('contextmenu', (e) => {
6963
7251
  const row = e.target.closest('.file-row');
6964
7252
  if (!row || !row.dataset.rel) {
7253
+ /* boş ağaç alanı: hızlı işlemler (mod-farkında) */
7254
+ e.preventDefault();
6965
7255
  fileCtxHide();
7256
+ const menu = els.fileCtxMenu;
7257
+ if (!menu) return;
7258
+ const items = [{ label: 'Yenile', fn: () => (studioModeOn() ? loadStudioTree() : loadIdeTree()) }];
7259
+ const pick = document.getElementById('filePick');
7260
+ if (pick) items.push({ label: 'Klasör seç…', fn: () => pick.click() });
7261
+ menu.innerHTML = '';
7262
+ for (const it of items) ctxMenuAppend(menu, it);
7263
+ menu.hidden = false;
7264
+ const mh = items.length * 33 + 8;
7265
+ menu.style.left = Math.max(4, Math.min(e.clientX, window.innerWidth - 170)) + 'px';
7266
+ menu.style.top = Math.max(4, Math.min(e.clientY, window.innerHeight - mh)) + 'px';
6966
7267
  return;
6967
7268
  }
6968
7269
  fileCtxShow(e, row.dataset.rel, row.dataset.dir === '1');
6969
7270
  });
6970
7271
 
7272
+ /* ---------- UYGULAMA GENELİ SAĞ TIK ----------
7273
+ Metin alanlarında (input/textarea): Kes / Kopyala / Yapıştır / Tümünü seç;
7274
+ seçili metinde (sohbet çıktısı vs.): Kopyala. Pano işlemleri pano
7275
+ köprüsüyle (clip:read / clip:write) MANUEL yapılır — Chromium'un
7276
+ execCommand('paste') kısıtı Electron'da da güvenilir değil; imleç
7277
+ konumuna elle eklenir, 'input' olayı tetiklenir (oto-büyüme vs. çalışır).
7278
+ Dosya ağacı kendi menüsünü gösterir (yukarıda). */
7279
+ function isEditableEl(t) {
7280
+ if (!t || !t.tagName) return false;
7281
+ const tag = String(t.tagName).toLowerCase();
7282
+ if (tag === 'input' || tag === 'textarea') return !(t.disabled || t.readOnly);
7283
+ return false;
7284
+ }
7285
+
7286
+ function ctxMenuAppend(menu, it) {
7287
+ const el = document.createElement('div');
7288
+ el.className = 'ctx-item' + (it.danger ? ' danger' : '') + (it.disabled ? ' disabled' : '');
7289
+ el.textContent = it.label;
7290
+ if (!it.disabled) el.addEventListener('click', () => { fileCtxHide(); it.fn(); });
7291
+ menu.appendChild(el);
7292
+ }
7293
+
7294
+ function ctxFieldInputEvent(el) {
7295
+ try { el.dispatchEvent(new Event('input', { bubbles: true })); } catch {}
7296
+ }
7297
+
7298
+ async function ctxFieldOp(el, op) {
7299
+ if (!el) return;
7300
+ el.focus();
7301
+ if (op === 'all') {
7302
+ if (typeof el.select === 'function') el.select();
7303
+ return;
7304
+ }
7305
+ const v = typeof el.value === 'string' ? el.value : '';
7306
+ const start = typeof el.selectionStart === 'number' ? el.selectionStart : v.length;
7307
+ const end = typeof el.selectionEnd === 'number' ? el.selectionEnd : v.length;
7308
+ if (op === 'paste') {
7309
+ const text = String((await beast.clipRead().catch(() => '')) || '');
7310
+ if (!text) return;
7311
+ el.value = v.slice(0, start) + text + v.slice(end);
7312
+ el.setSelectionRange(start + text.length, start + text.length);
7313
+ ctxFieldInputEvent(el);
7314
+ return;
7315
+ }
7316
+ if (op === 'copy' || op === 'cut') {
7317
+ const sel = v.slice(start, end);
7318
+ if (!sel) return;
7319
+ await beast.clipWrite(sel).catch(() => {});
7320
+ if (op === 'cut') {
7321
+ el.value = v.slice(0, start) + v.slice(end);
7322
+ el.setSelectionRange(start, start);
7323
+ ctxFieldInputEvent(el);
7324
+ }
7325
+ }
7326
+ }
7327
+
7328
+ document.addEventListener('contextmenu', (e) => {
7329
+ /* dosya ağacı kendi menüsünü kendisi açar */
7330
+ if (e.target && e.target.closest && e.target.closest('#fileTree')) return;
7331
+ fileCtxHide();
7332
+ const field = isEditableEl(e.target) ? e.target : (isEditableEl(document.activeElement) ? document.activeElement : null);
7333
+ const selText = String((window.getSelection && window.getSelection()) || '');
7334
+ const hasSel = field && typeof field.selectionStart === 'number'
7335
+ ? field.selectionEnd > field.selectionStart
7336
+ : !!selText.trim();
7337
+ if (!field && !hasSel) return; /* boş alan — menü yok */
7338
+ e.preventDefault();
7339
+ const menu = els.fileCtxMenu;
7340
+ if (!menu) return;
7341
+ const items = [];
7342
+ if (field) items.push({ label: 'Kes', disabled: !hasSel, fn: () => ctxFieldOp(field, 'cut') });
7343
+ items.push({
7344
+ label: 'Kopyala',
7345
+ disabled: !hasSel,
7346
+ fn: () => (field ? ctxFieldOp(field, 'copy') : beast.clipWrite(selText).catch(() => {})),
7347
+ });
7348
+ if (field) {
7349
+ items.push({ label: 'Yapıştır', fn: () => ctxFieldOp(field, 'paste') });
7350
+ items.push({ label: 'Tümünü seç', fn: () => ctxFieldOp(field, 'all') });
7351
+ }
7352
+ menu.innerHTML = '';
7353
+ for (const it of items) ctxMenuAppend(menu, it);
7354
+ menu.hidden = false;
7355
+ const mh = items.length * 33 + 8;
7356
+ menu.style.left = Math.max(4, Math.min(e.clientX, window.innerWidth - 170)) + 'px';
7357
+ menu.style.top = Math.max(4, Math.min(e.clientY, window.innerHeight - mh)) + 'px';
7358
+ });
7359
+
6971
7360
  /* ---------- IDE modu ortası: BEAST CODE paneli ----------
6972
7361
  Sohbet yerine Beast'in kendi ajanı (varsayılan model) çalışır;
6973
7362
  soldaki dosya panelindeki klasörü workspace olarak kullanır.
@@ -7457,5 +7846,399 @@ if (els.bcNew) els.bcNew.addEventListener('click', async () => {
7457
7846
  bcLine('t-sys', 'yeni oturum — sonraki mesaj taze başlar');
7458
7847
  }
7459
7848
  });
7849
+
7850
+ /* ---------- BEAST STUDIO paneli ----------
7851
+ Beast Code panelinin birebir karşılığı — AMA ayrı oturum, ayrı kuyruk,
7852
+ ayrı çıktı. Olaylar stSessionId ile eşlenir; Beast Code sohbetine ve ana
7853
+ chate ASLA sızmaz. Ajan buildStudioSystem ile açılır: video yapma/düzenleme. */
7854
+
7855
+ const ST_MAX_LINES = 1200;
7856
+ let stSessionId = null;
7857
+ let stRunning = false;
7858
+ let stBannerDone = false;
7859
+ let stStreamEl = null;
7860
+ let stStreamRaw = '';
7861
+ let stWsPath = '';
7862
+ const stTools = new Map(); /* callId → { box, sec } */
7863
+ let stCurBox = null;
7864
+ let stNoteEl = null;
7865
+ let stNoteRaw = '';
7866
+
7867
+ function stScroll() {
7868
+ const b = els.stOut;
7869
+ if (!b) return;
7870
+ b.scrollTop = b.scrollHeight;
7871
+ }
7872
+
7873
+ function stBodyScroll(el) {
7874
+ const b = el && el.closest ? el.closest('.bc-toolbody') : null;
7875
+ if (b) b.scrollTop = b.scrollHeight;
7876
+ }
7877
+
7878
+ function stLine(cls, text, time = false) {
7879
+ if (!els.stOut) return;
7880
+ const el = document.createElement('div');
7881
+ el.className = 't-line ' + (cls || '');
7882
+ const ts = time ? '<span class="t-time">' + new Date().toTimeString().slice(0, 8) + '</span> ' : '';
7883
+ el.innerHTML = ts + escapeHtml(String(text ?? ''));
7884
+ els.stOut.appendChild(el);
7885
+ while (els.stOut.childElementCount > ST_MAX_LINES) els.stOut.removeChild(els.stOut.firstChild);
7886
+ stScroll();
7887
+ }
7888
+
7889
+ function stBanner() {
7890
+ if (stBannerDone) return;
7891
+ stBannerDone = true;
7892
+ const cwd = studioState.path || '';
7893
+ if (els.stCwd && cwd) { els.stCwd.textContent = cwd; els.stCwd.title = cwd; }
7894
+ stLine('t-sys', 'BEAST STUDIO — stüdyo klasörü: ' + (cwd || '?'));
7895
+ stLine('t-dim', 'Video yapma/düzenleme ajanı: montaj, kesme, altyazı, ses, format — ffmpeg ile üretir, çıktılar "output" klasörüne düşer. + yeni oturum · Temizle çıktıyı siler.', false);
7896
+ }
7897
+
7898
+ /* Klasör değişimi → panel taze başlar (oturum main'de klasör bazlı saklanır) */
7899
+ function stOnFolderChanged(p) {
7900
+ if (!p || stWsPath === p) return;
7901
+ const first = !stWsPath;
7902
+ stWsPath = p;
7903
+ if (first) return; /* ilk yükleme — panel zaten boş, banner setStudioMode'da basılır */
7904
+ if (els.stOut) els.stOut.innerHTML = '';
7905
+ stTools.clear();
7906
+ stCloseToolGroup();
7907
+ stHideTodos();
7908
+ stStreamEl = null;
7909
+ stStreamRaw = '';
7910
+ stBannerDone = false;
7911
+ stBanner();
7912
+ }
7913
+
7914
+ function stSetBusy(v) {
7915
+ stRunning = !!v;
7916
+ if (els.stStop) els.stStop.hidden = !v;
7917
+ }
7918
+
7919
+ function stFlushStream() {
7920
+ stStreamEl = null;
7921
+ stStreamRaw = '';
7922
+ }
7923
+
7924
+ function stStreamDelta(delta) {
7925
+ if (!els.stOut) return;
7926
+ if (!stStreamEl) {
7927
+ stStreamEl = document.createElement('div');
7928
+ stStreamEl.className = 't-line t-out';
7929
+ els.stOut.appendChild(stStreamEl);
7930
+ }
7931
+ stStreamRaw += String(delta || '');
7932
+ stStreamEl.textContent = stStreamRaw.slice(-4000);
7933
+ stScroll(true);
7934
+ }
7935
+
7936
+ function stStatusShow(text) {
7937
+ if (!els.stStatus) return;
7938
+ const t = String(text || '').trim();
7939
+ if (!t) return;
7940
+ els.stStatus.textContent = t;
7941
+ els.stStatus.hidden = false;
7942
+ }
7943
+
7944
+ function stStatusHide() {
7945
+ if (els.stStatus) els.stStatus.hidden = true;
7946
+ }
7947
+
7948
+ function stNoteDetach() {
7949
+ stNoteEl = null;
7950
+ stNoteRaw = '';
7951
+ }
7952
+
7953
+ function stCloseToolGroup() {
7954
+ if (stCurBox) stCurBox.classList.remove('running');
7955
+ stCurBox = null;
7956
+ stNoteDetach();
7957
+ }
7958
+
7959
+ function stToolGroupNew() {
7960
+ const box = document.createElement('div');
7961
+ box.className = 'bc-toolbox';
7962
+ const body = document.createElement('div');
7963
+ body.className = 'bc-toolbody';
7964
+ box.appendChild(body);
7965
+ els.stOut.appendChild(box);
7966
+ while (els.stOut.childElementCount > ST_MAX_LINES) els.stOut.removeChild(els.stOut.firstChild);
7967
+ return box;
7968
+ }
7969
+
7970
+ function stToolBoxStart(callId, name, args) {
7971
+ stFlushStream();
7972
+ if (!stCurBox) stCurBox = stToolGroupNew();
7973
+ const sec = document.createElement('div');
7974
+ sec.className = 'bc-toolsection';
7975
+ const short = termShortTool(name, args);
7976
+ sec.innerHTML =
7977
+ '<div class="bc-toolhead">' +
7978
+ '<span class="bc-toolspin"></span>' +
7979
+ '<span class="bc-toolname">▸ ' + escapeHtml(String(name || 'araç')) + '</span>' +
7980
+ (short ? '<span class="bc-toolar" title="' + escapeHtml(short) + '">' + escapeHtml(short) + '</span>' : '') +
7981
+ '</div>' +
7982
+ '<div class="bc-toolout" hidden></div>';
7983
+ stCurBox.classList.add('running');
7984
+ const body = stCurBox.querySelector('.bc-toolbody') || stCurBox;
7985
+ body.appendChild(sec);
7986
+ if (callId) stTools.set(callId, { box: stCurBox, sec });
7987
+ stScroll();
7988
+ stBodyScroll(body);
7989
+ }
7990
+
7991
+ function stToolBoxEnd(callId, ok, result, diff) {
7992
+ stFlushStream();
7993
+ const t = (callId && stTools.get(callId)) || null;
7994
+ if (callId) stTools.delete(callId);
7995
+ const out = String(result || '').replace(/\s+$/, '');
7996
+ if (t) {
7997
+ const { sec } = t;
7998
+ sec.classList.add('done');
7999
+ if (!ok) sec.classList.add('failed');
8000
+ if (!t.box.querySelector('.bc-toolsection:not(.done)')) {
8001
+ t.box.classList.remove('running');
8002
+ }
8003
+ const pre = sec.querySelector('.bc-toolout');
8004
+ if (diff && diff.path) {
8005
+ const headEl = sec.querySelector('.bc-toolhead');
8006
+ if (headEl && !headEl.querySelector('.diff-badge')) {
8007
+ const badge = document.createElement('span');
8008
+ badge.className = 'diff-badge';
8009
+ const a = document.createElement('span');
8010
+ a.className = 'diff-badge-add';
8011
+ a.textContent = '+' + (diff.additions || 0);
8012
+ const d = document.createElement('span');
8013
+ d.className = 'diff-badge-del';
8014
+ d.textContent = '−' + (diff.deletions || 0);
8015
+ badge.appendChild(a);
8016
+ badge.appendChild(d);
8017
+ headEl.appendChild(badge);
8018
+ }
8019
+ pre.hidden = false;
8020
+ pre.textContent = '';
8021
+ pre.classList.add('bc-diffview');
8022
+ pre.appendChild(buildDiffEl(diff, pre.clientWidth || 480));
8023
+ } else if (out) {
8024
+ pre.hidden = false;
8025
+ pre.textContent = out.length > 4000 ? out.slice(0, 4000) + '\n… (kesildi)' : out;
8026
+ } else {
8027
+ pre.remove();
8028
+ }
8029
+ stBodyScroll(sec);
8030
+ } else if (out || !ok) {
8031
+ stLine(ok ? 't-out' : 't-err', out ? (out.length > 400 ? out.slice(0, 400) + ' …(kesildi)' : out) : '(hata)');
8032
+ }
8033
+ stScroll();
8034
+ }
8035
+
8036
+ /* Ara yazı → açık grup varsa kutu İÇİNE not olarak akar */
8037
+ function stNoteStream(delta) {
8038
+ if (!stCurBox) return false;
8039
+ if (!stNoteEl) {
8040
+ stNoteEl = document.createElement('div');
8041
+ stNoteEl.className = 'bc-toolnote';
8042
+ const body = stCurBox.querySelector('.bc-toolbody') || stCurBox;
8043
+ body.appendChild(stNoteEl);
8044
+ }
8045
+ stNoteRaw += String(delta || '');
8046
+ stNoteEl.textContent = stNoteRaw.slice(-2000);
8047
+ stScroll(true);
8048
+ stBodyScroll(stNoteEl);
8049
+ return true;
8050
+ }
8051
+
8052
+ function stRenderTodos(todos) {
8053
+ const list = Array.isArray(todos) ? todos : [];
8054
+ if (!els.stTodoWrap) return;
8055
+ stFlushStream();
8056
+ if (!list.length) {
8057
+ stHideTodos();
8058
+ return;
8059
+ }
8060
+ els.stTodoWrap.hidden = false;
8061
+ els.stTodoWrap.innerHTML =
8062
+ '<div class="bc-todobox">' +
8063
+ '<div class="bc-todotitle"><span>GÖREVLER</span><span class="bc-todocount"></span></div>' +
8064
+ '<div class="bc-todoitems"></div>';
8065
+ const done = list.filter((t) => t.status === 'done').length;
8066
+ els.stTodoWrap.querySelector('.bc-todocount').textContent = done + '/' + list.length;
8067
+ const wrap = els.stTodoWrap.querySelector('.bc-todoitems');
8068
+ wrap.innerHTML = '';
8069
+ for (const t of list) {
8070
+ const st = TODO_GLYPH[t.status] ? t.status : 'pending';
8071
+ const row = document.createElement('div');
8072
+ row.className = 'bc-todoitem ' + st;
8073
+ row.innerHTML =
8074
+ '<span class="bc-todocheck">' + TODO_GLYPH[st] + '</span>' +
8075
+ '<span class="bc-todotext"></span>';
8076
+ row.querySelector('.bc-todotext').textContent = String(t.title || '');
8077
+ wrap.appendChild(row);
8078
+ }
8079
+ }
8080
+
8081
+ function stHideTodos() {
8082
+ if (els.stTodoWrap) {
8083
+ els.stTodoWrap.hidden = true;
8084
+ els.stTodoWrap.innerHTML = '';
8085
+ }
8086
+ }
8087
+
8088
+ /* Studio oturumunun engine olayları → panel çıktısı (bcIngest'in karşılığı) */
8089
+ function stIngest(ev) {
8090
+ switch (ev.type) {
8091
+ case 'token':
8092
+ if (stNoteStream(ev.delta)) break;
8093
+ stStreamDelta(ev.delta);
8094
+ break;
8095
+ case 'message':
8096
+ if (ev.message && ev.message.role === 'assistant') {
8097
+ const content = typeof ev.message.content === 'string' ? ev.message.content : '';
8098
+ const hasTools = Array.isArray(ev.message.tool_calls) && ev.message.tool_calls.length > 0;
8099
+ if (hasTools) {
8100
+ stNoteDetach();
8101
+ stFlushStream();
8102
+ break;
8103
+ }
8104
+ if (stNoteEl) { stNoteEl.remove(); stNoteDetach(); }
8105
+ if (content && !stStreamRaw.trim()) stLine('t-out', content);
8106
+ stCloseToolGroup();
8107
+ stFlushStream();
8108
+ }
8109
+ break;
8110
+ case 'todos':
8111
+ stRenderTodos(ev.todos);
8112
+ break;
8113
+ case 'tool-start':
8114
+ stSetBusy(true);
8115
+ stToolBoxStart(ev.callId, ev.name, ev.args);
8116
+ break;
8117
+ case 'tool-end':
8118
+ stToolBoxEnd(ev.callId, ev.ok, ev.result, ev.diff);
8119
+ if (ev.name === 'write_file' || ev.name === 'edit_file' || ev.name === 'run_command' || ev.name === 'python_run') {
8120
+ ideRefreshTree(); /* ffmpeg çıktısı anında ağaca düşer */
8121
+ }
8122
+ break;
8123
+ case 'st-mode':
8124
+ if (els.stTitle) {
8125
+ els.stTitle.textContent =
8126
+ ev.mode === 'plan' ? 'BEAST STUDIO · PLAN' :
8127
+ ev.mode === 'build' ? 'BEAST STUDIO · BUILD' : 'BEAST STUDIO';
8128
+ }
8129
+ stLine('t-dim', '[' + (ev.body || ev.mode || 'mod değişti') + ']');
8130
+ break;
8131
+ case 'done':
8132
+ stFlushStream();
8133
+ stCloseToolGroup();
8134
+ stSetBusy(false);
8135
+ stStatusHide();
8136
+ stLine(ev.aborted ? 't-err' : 't-dim', ev.aborted ? '[durduruldu — sebep: ' + (ev.reason || 'sebep belirtilmedi') + ']' : '(tamamlandı)');
8137
+ ideRefreshTree();
8138
+ if (studioModeOn() && els.stInput) els.stInput.focus();
8139
+ break;
8140
+ case 'error':
8141
+ stFlushStream();
8142
+ stCloseToolGroup();
8143
+ stSetBusy(false);
8144
+ stStatusHide();
8145
+ stLine('t-err', '[hata] ' + (ev.error || ''));
8146
+ break;
8147
+ case 'status':
8148
+ if (ev.status === 'idle') {
8149
+ stCloseToolGroup();
8150
+ stSetBusy(false);
8151
+ stStatusHide();
8152
+ } else {
8153
+ stSetBusy(true);
8154
+ stStatusShow(ev.status === 'thinking' ? 'düşünüyor…' : String(ev.status || ''));
8155
+ }
8156
+ break;
8157
+ }
8158
+ }
8159
+
8160
+ function stInputResize() {
8161
+ const ta = els.stInput;
8162
+ if (!ta) return;
8163
+ ta.style.height = 'auto';
8164
+ const max = 140;
8165
+ const h = Math.min(ta.scrollHeight, max);
8166
+ ta.style.height = h + 'px';
8167
+ ta.classList.toggle('expand', ta.scrollHeight > max);
8168
+ }
8169
+
8170
+ function stRunCurrent() {
8171
+ const msg = els.stInput.value.trim();
8172
+ if (!msg && !stPending.length) return;
8173
+ const atts = stPending.slice();
8174
+ stPending = [];
8175
+ renderStChips();
8176
+ els.stInput.value = '';
8177
+ stInputResize();
8178
+ els.stInput.focus();
8179
+ stLine('t-cmd', 'studio> ' + (msg || (atts.length ? '[' + atts.length + ' ek]' : '')));
8180
+ stHideTodos();
8181
+ stSetBusy(true);
8182
+ beast.studioSend(msg || '[dosya ekleri]', atts).then((r) => {
8183
+ if (r && r.ok && r.mode) {
8184
+ if (r.sessionId) stSessionId = r.sessionId;
8185
+ stSetBusy(false);
8186
+ } else if (r && r.ok && r.queued) {
8187
+ if (r.sessionId) stSessionId = r.sessionId;
8188
+ stLine('t-dim', '⏳ kuyrukta (' + r.count + ') — iş bitince gönderilir');
8189
+ } else if (r && r.ok && r.pending) {
8190
+ if (r.sessionId) stSessionId = r.sessionId;
8191
+ } else if (r && r.ok) {
8192
+ if (r.sessionId) stSessionId = r.sessionId;
8193
+ } else if (r && r.busy) {
8194
+ stLine('t-dim', (r && r.error) || 'mesaj sürüyor');
8195
+ } else {
8196
+ stSetBusy(false);
8197
+ stLine('t-err', (r && r.error) || 'gönderilemedi');
8198
+ }
8199
+ }).catch((e) => {
8200
+ stSetBusy(false);
8201
+ stLine('t-err', String((e && e.message) || e));
8202
+ });
8203
+ }
8204
+
8205
+ if (els.stInput) {
8206
+ els.stInput.addEventListener('input', stInputResize);
8207
+ els.stInput.addEventListener('keydown', (e) => {
8208
+ if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); stRunCurrent(); }
8209
+ });
8210
+ }
8211
+ if (els.stStop) els.stStop.addEventListener('click', () => {
8212
+ stPending = [];
8213
+ renderStChips();
8214
+ beast.studioStop().then((r) => {
8215
+ if (r && r.ok && r.wasBusy === false) {
8216
+ stSetBusy(false);
8217
+ stStatusHide();
8218
+ }
8219
+ }).catch(() => {});
8220
+ });
8221
+ if (els.stClear) els.stClear.addEventListener('click', () => {
8222
+ els.stOut.innerHTML = '';
8223
+ stTools.clear();
8224
+ stCloseToolGroup();
8225
+ stHideTodos();
8226
+ stFlushStream();
8227
+ });
8228
+ if (els.stNew) els.stNew.addEventListener('click', async () => {
8229
+ if (stRunning) { toast('Mesaj sürüyor — önce ■ ile durdur'); return; }
8230
+ const r = await beast.studioNew().catch(() => null);
8231
+ if (r && r.ok) {
8232
+ stSessionId = null;
8233
+ stPending = [];
8234
+ renderStChips();
8235
+ stTools.clear();
8236
+ stCloseToolGroup();
8237
+ stHideTodos();
8238
+ stFlushStream();
8239
+ if (els.stTitle) els.stTitle.textContent = 'BEAST STUDIO';
8240
+ stLine('t-sys', 'yeni oturum — sonraki mesaj taze başlar');
8241
+ }
8242
+ });
7460
8243
  /* Uygulama her açılışta agent (chat) modunda başlar — IDE modu yalnız
7461
8244
  kullanıcı butona basınca girilir (oturumluk). */