beast-agent 0.20.0 → 0.21.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.
- package/package.json +1 -1
- package/scripts/release.js +21 -34
- package/src/agent/engine.js +223 -40
- package/src/main.js +232 -3
- package/src/preload.js +10 -0
- package/src/renderer/i18n.js +20 -0
- package/src/renderer/index.html +45 -1
- package/src/renderer/renderer.js +796 -2
- package/src/renderer/style.css +524 -2
package/src/renderer/renderer.js
CHANGED
|
@@ -70,6 +70,21 @@ const els = {
|
|
|
70
70
|
termClear: $('#termClear'),
|
|
71
71
|
termClose: $('#termClose'),
|
|
72
72
|
termResize: $('#termResize'),
|
|
73
|
+
bcOut: $('#bcOut'),
|
|
74
|
+
bcCwd: $('#bcCwd'),
|
|
75
|
+
bcInput: $('#bcInput'),
|
|
76
|
+
bcStop: $('#bcStop'),
|
|
77
|
+
bcClear: $('#bcClear'),
|
|
78
|
+
bcNew: $('#bcNew'),
|
|
79
|
+
bcTodoWrap: $('#bcTodoWrap'),
|
|
80
|
+
codeTabs: $('#codeTabs'),
|
|
81
|
+
codeTa: $('#codeTa'),
|
|
82
|
+
codePath: $('#codePath'),
|
|
83
|
+
codeSave: $('#codeSave'),
|
|
84
|
+
ideRow: $('#ideRow'),
|
|
85
|
+
ideSplit: $('#ideSplit'),
|
|
86
|
+
bcPanel: $('#bcPanel'),
|
|
87
|
+
fileCtxMenu: $('#fileCtxMenu'),
|
|
73
88
|
cronName: $('#cronName'),
|
|
74
89
|
cronPreset: $('#cronPreset'),
|
|
75
90
|
cronSchedule: $('#cronSchedule'),
|
|
@@ -298,6 +313,15 @@ function addErrorBubble(text) {
|
|
|
298
313
|
}
|
|
299
314
|
|
|
300
315
|
/* ---------------- tool cards ---------------- */
|
|
316
|
+
/* Ard arda gelen araç kartları TEK kutuda toplanır (.tool-box): çalışırken
|
|
317
|
+
lacivert outline döner, gövde max 420px scroll'lu. Grup; tool_calls İÇEREN
|
|
318
|
+
iş turlarında açık kalır, saf metin cevapta (normal mesaj) kapanır. */
|
|
319
|
+
let chatToolGroup = null; /* { box, body } */
|
|
320
|
+
|
|
321
|
+
function closeChatToolGroup() {
|
|
322
|
+
if (chatToolGroup) chatToolGroup.box.classList.remove('running');
|
|
323
|
+
chatToolGroup = null;
|
|
324
|
+
}
|
|
301
325
|
|
|
302
326
|
function argSummary(name, args) {
|
|
303
327
|
try {
|
|
@@ -314,6 +338,18 @@ function argSummary(name, args) {
|
|
|
314
338
|
function addToolCard(callId, name, args) {
|
|
315
339
|
streamEl = null;
|
|
316
340
|
showEmpty(false);
|
|
341
|
+
/* grup yoksa / DOM temizlenmişse yeni kutu aç */
|
|
342
|
+
if (!chatToolGroup || !els.msgs.contains(chatToolGroup.box)) {
|
|
343
|
+
const box = document.createElement('div');
|
|
344
|
+
box.className = 'tool-box running';
|
|
345
|
+
const body = document.createElement('div');
|
|
346
|
+
body.className = 'tool-boxbody';
|
|
347
|
+
box.appendChild(body);
|
|
348
|
+
els.msgs.appendChild(box);
|
|
349
|
+
chatToolGroup = { box, body };
|
|
350
|
+
} else {
|
|
351
|
+
chatToolGroup.box.classList.add('running');
|
|
352
|
+
}
|
|
317
353
|
const card = document.createElement('div');
|
|
318
354
|
card.className = 'tool-card';
|
|
319
355
|
card.dataset.callId = callId || '';
|
|
@@ -327,7 +363,8 @@ function addToolCard(callId, name, args) {
|
|
|
327
363
|
card.querySelector('.tool-head').addEventListener('click', () => {
|
|
328
364
|
card.querySelector('.tool-body').classList.toggle('open');
|
|
329
365
|
});
|
|
330
|
-
|
|
366
|
+
chatToolGroup.body.appendChild(card);
|
|
367
|
+
chatToolGroup.body.scrollTop = chatToolGroup.body.scrollHeight;
|
|
331
368
|
scrollDown(true);
|
|
332
369
|
return card;
|
|
333
370
|
}
|
|
@@ -345,6 +382,10 @@ function finishToolCard(callId, ok, result) {
|
|
|
345
382
|
const body = card.querySelector('.tool-body');
|
|
346
383
|
body.textContent = String(result || '(çıktı yok)').slice(0, 4000);
|
|
347
384
|
if (String(result || '').length <= 300) body.classList.add('open');
|
|
385
|
+
/* ring burada söndürülmez — iş bitene kadar (closeChatToolGroup) döner */
|
|
386
|
+
if (chatToolGroup && chatToolGroup.body.contains(card)) {
|
|
387
|
+
chatToolGroup.body.scrollTop = chatToolGroup.body.scrollHeight;
|
|
388
|
+
}
|
|
348
389
|
}
|
|
349
390
|
|
|
350
391
|
/* ---------------- sessions sidebar ---------------- */
|
|
@@ -2784,6 +2825,12 @@ function renderAgentsPane() {
|
|
|
2784
2825
|
function onEvent(ev) {
|
|
2785
2826
|
if (ev.type === 'sessions') { refreshSessions(); return; }
|
|
2786
2827
|
if (ev.type === 'approval') {
|
|
2828
|
+
/* Beast Code oturumunun onayı: panelde ipucu + kart sohbete düşer */
|
|
2829
|
+
if (bcSessionId && ev.sessionId === bcSessionId) {
|
|
2830
|
+
showApprovalCard(ev);
|
|
2831
|
+
bcLine('t-dim', '⏳ onay bekleniyor: ' + (ev.tool || '?') + ' — IDE modundan çıkıp sohbetteki kartı onayla');
|
|
2832
|
+
return;
|
|
2833
|
+
}
|
|
2787
2834
|
if (ev.sessionId && ev.sessionId !== activeId) return;
|
|
2788
2835
|
showApprovalCard(ev);
|
|
2789
2836
|
return;
|
|
@@ -2800,6 +2847,13 @@ function onEvent(ev) {
|
|
|
2800
2847
|
scheduleAgentsRender();
|
|
2801
2848
|
return;
|
|
2802
2849
|
}
|
|
2850
|
+
/* Beast Code oturumu (IDE modu ortasındaki panel): olayları panele akıt,
|
|
2851
|
+
ana sohbeti kirletme */
|
|
2852
|
+
if (bcSessionId && ev.sessionId === bcSessionId) {
|
|
2853
|
+
bcIngest(ev);
|
|
2854
|
+
return;
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2803
2857
|
if (ev.sessionId && ev.sessionId !== activeId) {
|
|
2804
2858
|
/* paralel ajan canlı akışı: sohbeti kirletme, sekmede göster */
|
|
2805
2859
|
if (agentState.runningIds.has(ev.sessionId)) {
|
|
@@ -2816,6 +2870,7 @@ function onEvent(ev) {
|
|
|
2816
2870
|
els.msgs.innerHTML = '';
|
|
2817
2871
|
streamEl = null;
|
|
2818
2872
|
streamRaw = '';
|
|
2873
|
+
closeChatToolGroup();
|
|
2819
2874
|
showEmpty(true);
|
|
2820
2875
|
refreshSessions();
|
|
2821
2876
|
break;
|
|
@@ -2825,7 +2880,12 @@ function onEvent(ev) {
|
|
|
2825
2880
|
break;
|
|
2826
2881
|
case 'message':
|
|
2827
2882
|
if (ev.message.role === 'user') addUserBubble(ev.message.content);
|
|
2828
|
-
else if (ev.message.role === 'assistant')
|
|
2883
|
+
else if (ev.message.role === 'assistant') {
|
|
2884
|
+
finalizeAssistant(ev.message.content);
|
|
2885
|
+
/* tool_calls'lı iş turu grupları açık tutar; saf metin (normal cevap) kapatır */
|
|
2886
|
+
const hasTools = Array.isArray(ev.message.tool_calls) && ev.message.tool_calls.length > 0;
|
|
2887
|
+
if (!hasTools) closeChatToolGroup();
|
|
2888
|
+
}
|
|
2829
2889
|
break;
|
|
2830
2890
|
case 'token':
|
|
2831
2891
|
ensureStreamBubble();
|
|
@@ -2869,6 +2929,9 @@ function onEvent(ev) {
|
|
|
2869
2929
|
} else {
|
|
2870
2930
|
toggleRail(!railPrefBeforeBrowser);
|
|
2871
2931
|
}
|
|
2932
|
+
/* IDE modunda tarayıcı açılır/kapanır/genişlik değişirse row yeniden bölünür —
|
|
2933
|
+
editör + Beast Code ORTAK kırpılıp ORTAK açılır (kullanıcı payı korunur) */
|
|
2934
|
+
if (ideModeOn()) ideSplitApplyFrac();
|
|
2872
2935
|
if (shown) {
|
|
2873
2936
|
els.browserBtn.classList.add('on');
|
|
2874
2937
|
if (ev.url && document.activeElement !== els.bbUrl) {
|
|
@@ -2894,11 +2957,13 @@ function onEvent(ev) {
|
|
|
2894
2957
|
applyState();
|
|
2895
2958
|
break;
|
|
2896
2959
|
case 'done':
|
|
2960
|
+
closeChatToolGroup();
|
|
2897
2961
|
setBusy(false);
|
|
2898
2962
|
refreshSessions();
|
|
2899
2963
|
els.input.focus();
|
|
2900
2964
|
break;
|
|
2901
2965
|
case 'error':
|
|
2966
|
+
closeChatToolGroup();
|
|
2902
2967
|
setBusy(false);
|
|
2903
2968
|
addErrorBubble(ev.error);
|
|
2904
2969
|
refreshSessions();
|
|
@@ -4315,3 +4380,732 @@ document.querySelectorAll('.stab').forEach((b) =>
|
|
|
4315
4380
|
renderStorePane();
|
|
4316
4381
|
})
|
|
4317
4382
|
);
|
|
4383
|
+
|
|
4384
|
+
|
|
4385
|
+
/* ================= IDE MODU =================
|
|
4386
|
+
Tek tuş (#ideBtn): sol → dosya gezgini, orta → chat, sağ → dahili tarayıcı (preview).
|
|
4387
|
+
Preview: workspace kökündeki index.html (yoksa ilk *.html) sağdaki tarayıcıda açılır.
|
|
4388
|
+
Dosyaya tıkla → editör; klasöre tıkla → aç/kapa. */
|
|
4389
|
+
|
|
4390
|
+
const IDE_ICONS = {
|
|
4391
|
+
'.html': '🌐', '.htm': '🌐', '.css': '🎨', '.js': '📜', '.mjs': '📜', '.cjs': '📜',
|
|
4392
|
+
'.ts': '📜', '.jsx': '📜', '.tsx': '📜', '.json': '🧾', '.md': '📝', '.txt': '📄',
|
|
4393
|
+
'.py': '🐍', '.ps1': '⚙️', '.bat': '⚙️', '.cmd': '⚙️', '.sh': '⚙️', '.yaml': '🧾',
|
|
4394
|
+
'.yml': '🧾', '.png': '🖼️', '.jpg': '🖼️', '.jpeg': '🖼️', '.gif': '🖼️', '.svg': '🖼️',
|
|
4395
|
+
'.pdf': '📕',
|
|
4396
|
+
};
|
|
4397
|
+
|
|
4398
|
+
const ideState = {
|
|
4399
|
+
cache: new Map(), // rel -> entries[]
|
|
4400
|
+
open: new Set(['']),
|
|
4401
|
+
path: '',
|
|
4402
|
+
};
|
|
4403
|
+
|
|
4404
|
+
function ideModeOn() {
|
|
4405
|
+
return document.body.classList.contains('ide-mode');
|
|
4406
|
+
}
|
|
4407
|
+
|
|
4408
|
+
async function setIdeMode(on) {
|
|
4409
|
+
document.body.classList.toggle('ide-mode', !!on);
|
|
4410
|
+
/* IDE modu oturumluk: uygulama HER AÇILIŞTA agent (chat) modunda başlar */
|
|
4411
|
+
toggleRail(!!on);
|
|
4412
|
+
/* IDE'den çıkış: açık tarayıcı da kapansın — her şey default haline dönsün */
|
|
4413
|
+
if (!on && document.body.classList.contains('browser-open')) {
|
|
4414
|
+
try { beast.toggleBrowser(); } catch {}
|
|
4415
|
+
}
|
|
4416
|
+
if (on) {
|
|
4417
|
+
ideSplitRestore();
|
|
4418
|
+
await loadIdeTree();
|
|
4419
|
+
bcBanner();
|
|
4420
|
+
}
|
|
4421
|
+
}
|
|
4422
|
+
|
|
4423
|
+
/* ---------- editor/chat ayırıcı (VS Code gibi sürükle-bırak) ----------
|
|
4424
|
+
Orantılı model: kaydedilen, Beast Code'un ideRow içindeki PAYI'dır (0-1).
|
|
4425
|
+
Tarayıcı açılınca/kapanınca/genişlik değişince row yeniden bölünür — editör
|
|
4426
|
+
ve Beast Code ORTAK kırpılıp ORTAK açılır (oranlar korunur).
|
|
4427
|
+
Kayıt yoksa default: tarayıcı açıkken 3 eşit parça, kapalıyken 2 eşit parça. */
|
|
4428
|
+
let ideSplitFrac = 0; /* 0 = kayıt yok → 0.5 default */
|
|
4429
|
+
|
|
4430
|
+
function ideSplitApplyFrac() {
|
|
4431
|
+
if (!ideSplitFrac) ideSplitFrac = 0.5;
|
|
4432
|
+
ideSplitFrac = Math.max(0.2, Math.min(ideSplitFrac, 0.8));
|
|
4433
|
+
const rowW = els.ideRow && els.ideRow.clientWidth ? els.ideRow.clientWidth : window.innerWidth - 250;
|
|
4434
|
+
const w = Math.max(280, Math.min(Math.round(rowW * ideSplitFrac), Math.max(320, rowW - 300)));
|
|
4435
|
+
document.body.style.setProperty('--ideSplit', w + 'px');
|
|
4436
|
+
}
|
|
4437
|
+
|
|
4438
|
+
function ideSplitSave() {
|
|
4439
|
+
try { localStorage.setItem('beast.ideSplit', String(ideSplitFrac)); } catch {}
|
|
4440
|
+
}
|
|
4441
|
+
|
|
4442
|
+
function ideSplitRestore() {
|
|
4443
|
+
let saved = 0;
|
|
4444
|
+
try { saved = parseFloat(localStorage.getItem('beast.ideSplit')) || 0; } catch {}
|
|
4445
|
+
const hasUserSetting = saved > 0.05 && saved < 0.95;
|
|
4446
|
+
ideSplitFrac = hasUserSetting ? saved : 0;
|
|
4447
|
+
ideSplitApplyFrac();
|
|
4448
|
+
/* tarayıcı açıksa ve kullanıcı hiç ayar yapmadıysa üç parçayı eşitle */
|
|
4449
|
+
const browserOpen = document.body.classList.contains('browser-open');
|
|
4450
|
+
if (browserOpen && !hasUserSetting) {
|
|
4451
|
+
beast.browserSetWidth(Math.round((window.innerWidth - 250) / 3)).catch(() => {});
|
|
4452
|
+
}
|
|
4453
|
+
}
|
|
4454
|
+
|
|
4455
|
+
if (els.ideSplit) {
|
|
4456
|
+
els.ideSplit.addEventListener('mousedown', (e) => {
|
|
4457
|
+
e.preventDefault();
|
|
4458
|
+
els.ideSplit.classList.add('dragging');
|
|
4459
|
+
/* tarayıcı dock'u drag sırasında sabit — sağ kenar referansı mousedown anında */
|
|
4460
|
+
const rightEdge = els.bcPanel ? els.bcPanel.getBoundingClientRect().right : window.innerWidth;
|
|
4461
|
+
const rowW = Math.max(600, els.ideRow && els.ideRow.clientWidth ? els.ideRow.clientWidth : window.innerWidth - 250);
|
|
4462
|
+
const move = (ev) => {
|
|
4463
|
+
const w = Math.max(280, Math.min(rightEdge - ev.clientX, rowW - 300));
|
|
4464
|
+
ideSplitFrac = Math.max(0.2, Math.min(w / rowW, 0.8));
|
|
4465
|
+
document.body.style.setProperty('--ideSplit', Math.round(w) + 'px');
|
|
4466
|
+
};
|
|
4467
|
+
const up = () => {
|
|
4468
|
+
document.removeEventListener('mousemove', move);
|
|
4469
|
+
document.removeEventListener('mouseup', up);
|
|
4470
|
+
els.ideSplit.classList.remove('dragging');
|
|
4471
|
+
ideSplitSave();
|
|
4472
|
+
};
|
|
4473
|
+
document.addEventListener('mousemove', move);
|
|
4474
|
+
document.addEventListener('mouseup', up);
|
|
4475
|
+
});
|
|
4476
|
+
window.addEventListener('resize', () => {
|
|
4477
|
+
if (ideModeOn()) ideSplitApplyFrac();
|
|
4478
|
+
});
|
|
4479
|
+
}
|
|
4480
|
+
|
|
4481
|
+
async function loadIdeTree() {
|
|
4482
|
+
ideState.cache.clear();
|
|
4483
|
+
await renderFileTree();
|
|
4484
|
+
}
|
|
4485
|
+
|
|
4486
|
+
function fmtFileSize(n) {
|
|
4487
|
+
n = Number(n) || 0;
|
|
4488
|
+
if (n >= 1048576) return (n / 1048576).toFixed(1) + 'M';
|
|
4489
|
+
if (n >= 1024) return Math.round(n / 1024) + 'K';
|
|
4490
|
+
return n ? n + 'B' : '';
|
|
4491
|
+
}
|
|
4492
|
+
|
|
4493
|
+
async function renderFileTree() {
|
|
4494
|
+
const tree = $('#fileTree');
|
|
4495
|
+
if (!tree) return;
|
|
4496
|
+
tree.innerHTML = '';
|
|
4497
|
+
const wsLabel = $('#filePanelPath');
|
|
4498
|
+
const build = async (rel, depth, container) => {
|
|
4499
|
+
let entries = ideState.cache.get(rel);
|
|
4500
|
+
if (!entries) {
|
|
4501
|
+
const r = await beast.ideTree(rel).catch(() => null);
|
|
4502
|
+
if (!r || !r.ok) {
|
|
4503
|
+
if (!rel) container.innerHTML = `<div class="file-empty">${escapeHtml((r && r.error) || 'okunamadı')}</div>`;
|
|
4504
|
+
return;
|
|
4505
|
+
}
|
|
4506
|
+
entries = r.entries;
|
|
4507
|
+
ideState.cache.set(rel, entries);
|
|
4508
|
+
if (r.workspace) {
|
|
4509
|
+
ideState.path = r.workspace;
|
|
4510
|
+
if (wsLabel) { wsLabel.textContent = r.workspace; wsLabel.title = r.workspace; }
|
|
4511
|
+
if (els.bcCwd) { els.bcCwd.textContent = r.workspace; els.bcCwd.title = r.workspace; }
|
|
4512
|
+
bcOnFolderChanged(r.workspace);
|
|
4513
|
+
}
|
|
4514
|
+
}
|
|
4515
|
+
for (const e of entries) {
|
|
4516
|
+
const child = rel ? rel + '/' + e.name : e.name;
|
|
4517
|
+
const row = document.createElement('div');
|
|
4518
|
+
row.className = 'file-row';
|
|
4519
|
+
row.style.paddingLeft = 6 + depth * 14 + 'px';
|
|
4520
|
+
const ico = e.dir ? (ideState.open.has(child) ? '📂' : '📁') : IDE_ICONS[e.name.slice(e.name.lastIndexOf('.')).toLowerCase()] || '📄';
|
|
4521
|
+
row.innerHTML =
|
|
4522
|
+
`<span class="f-ico">${ico}</span>` +
|
|
4523
|
+
`<span class="f-nm" title="${escapeHtml(e.name)}">${escapeHtml(e.name)}</span>` +
|
|
4524
|
+
(e.dir ? '' : `<span class="f-sz">${fmtFileSize(e.size)}</span>`);
|
|
4525
|
+
row.addEventListener('click', () => {
|
|
4526
|
+
if (e.dir) {
|
|
4527
|
+
if (ideState.open.has(child)) ideState.open.delete(child);
|
|
4528
|
+
else ideState.open.add(child);
|
|
4529
|
+
renderFileTree();
|
|
4530
|
+
} else {
|
|
4531
|
+
codeOpen(child);
|
|
4532
|
+
}
|
|
4533
|
+
});
|
|
4534
|
+
row.dataset.rel = child;
|
|
4535
|
+
row.dataset.dir = e.dir ? '1' : '0';
|
|
4536
|
+
container.appendChild(row);
|
|
4537
|
+
if (e.dir && ideState.open.has(child)) {
|
|
4538
|
+
const box = document.createElement('div');
|
|
4539
|
+
container.appendChild(box);
|
|
4540
|
+
await build(child, depth + 1, box);
|
|
4541
|
+
}
|
|
4542
|
+
}
|
|
4543
|
+
};
|
|
4544
|
+
await build('', 0, tree);
|
|
4545
|
+
}
|
|
4546
|
+
|
|
4547
|
+
/* ---------- sekmeli kod editörü (VS Code düzeni) ----------
|
|
4548
|
+
Dosya tıklaması MODAL değil, codePane içinde SEKME açar.
|
|
4549
|
+
Yanında Beast Code chat'i, onun sağındaki preview tarayıcı durur. */
|
|
4550
|
+
|
|
4551
|
+
const codeTabs = []; /* { rel, content, dirty } */
|
|
4552
|
+
let codeActive = -1;
|
|
4553
|
+
|
|
4554
|
+
function codeRenderTabs() {
|
|
4555
|
+
const bar = els.codeTabs;
|
|
4556
|
+
if (!bar) return;
|
|
4557
|
+
bar.innerHTML = '';
|
|
4558
|
+
codeTabs.forEach((t, i) => {
|
|
4559
|
+
const el = document.createElement('div');
|
|
4560
|
+
el.className = 'code-tab' + (i === codeActive ? ' active' : '') + (t.dirty ? ' dirty' : '');
|
|
4561
|
+
el.title = t.rel;
|
|
4562
|
+
el.innerHTML =
|
|
4563
|
+
'<span class="code-tab-name"></span>' +
|
|
4564
|
+
'<button class="code-tab-x" title="Sekmeyi kapat">\u00D7</button>';
|
|
4565
|
+
el.querySelector('.code-tab-name').textContent = t.rel;
|
|
4566
|
+
el.addEventListener('click', () => codeActivate(i));
|
|
4567
|
+
el.querySelector('.code-tab-x').addEventListener('click', (e) => {
|
|
4568
|
+
e.stopPropagation();
|
|
4569
|
+
codeClose(i);
|
|
4570
|
+
});
|
|
4571
|
+
bar.appendChild(el);
|
|
4572
|
+
});
|
|
4573
|
+
}
|
|
4574
|
+
|
|
4575
|
+
function codeFlushActive() {
|
|
4576
|
+
if (codeActive >= 0 && codeTabs[codeActive]) {
|
|
4577
|
+
codeTabs[codeActive].content = els.codeTa.value;
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
|
|
4581
|
+
function codeActivate(i) {
|
|
4582
|
+
codeFlushActive();
|
|
4583
|
+
if (i < 0 || i >= codeTabs.length) {
|
|
4584
|
+
codeActive = -1;
|
|
4585
|
+
els.codeTa.value = '';
|
|
4586
|
+
els.codePath.textContent = '\u2014';
|
|
4587
|
+
codeRenderTabs();
|
|
4588
|
+
return;
|
|
4589
|
+
}
|
|
4590
|
+
codeActive = i;
|
|
4591
|
+
const t = codeTabs[i];
|
|
4592
|
+
els.codeTa.value = t.content;
|
|
4593
|
+
els.codePath.textContent = t.rel;
|
|
4594
|
+
els.codePath.title = (ideState.path || '') + '\\' + t.rel.replace(/\//g, '\\');
|
|
4595
|
+
codeRenderTabs();
|
|
4596
|
+
}
|
|
4597
|
+
|
|
4598
|
+
async function codeOpen(rel) {
|
|
4599
|
+
const idx = codeTabs.findIndex((t) => t.rel === rel);
|
|
4600
|
+
if (idx >= 0) {
|
|
4601
|
+
codeActivate(idx);
|
|
4602
|
+
return;
|
|
4603
|
+
}
|
|
4604
|
+
const r = await beast.ideRead(rel).catch(() => null);
|
|
4605
|
+
if (!r || !r.ok) {
|
|
4606
|
+
toast((r && r.error) || 'okunamad\u0131');
|
|
4607
|
+
return;
|
|
4608
|
+
}
|
|
4609
|
+
codeTabs.push({ rel, content: r.content || '', dirty: false });
|
|
4610
|
+
codeActivate(codeTabs.length - 1);
|
|
4611
|
+
els.codeTa.focus();
|
|
4612
|
+
}
|
|
4613
|
+
|
|
4614
|
+
function codeClose(i) {
|
|
4615
|
+
if (i < 0 || i >= codeTabs.length) return;
|
|
4616
|
+
codeFlushActive();
|
|
4617
|
+
const oldActive = codeActive;
|
|
4618
|
+
const wasActive = i === oldActive;
|
|
4619
|
+
codeTabs.splice(i, 1);
|
|
4620
|
+
if (codeTabs.length === 0) {
|
|
4621
|
+
codeActive = -1;
|
|
4622
|
+
els.codeTa.value = '';
|
|
4623
|
+
els.codePath.textContent = '\u2014';
|
|
4624
|
+
codeRenderTabs();
|
|
4625
|
+
return;
|
|
4626
|
+
}
|
|
4627
|
+
let next = oldActive;
|
|
4628
|
+
if (wasActive) next = Math.min(i, codeTabs.length - 1);
|
|
4629
|
+
else if (i < oldActive) next = oldActive - 1;
|
|
4630
|
+
codeActive = -1; /* activate içindeki flush, eski içerikle yeni sekmeyi ezmesin */
|
|
4631
|
+
codeActivate(next);
|
|
4632
|
+
}
|
|
4633
|
+
|
|
4634
|
+
async function codeSave() {
|
|
4635
|
+
if (codeActive < 0 || !codeTabs[codeActive]) return;
|
|
4636
|
+
codeFlushActive();
|
|
4637
|
+
const t = codeTabs[codeActive];
|
|
4638
|
+
const r = await beast.ideWrite(t.rel, t.content).catch(() => null);
|
|
4639
|
+
if (r && r.ok) {
|
|
4640
|
+
t.dirty = false;
|
|
4641
|
+
codeRenderTabs();
|
|
4642
|
+
toast(_t('ide_saved'));
|
|
4643
|
+
} else {
|
|
4644
|
+
toast((r && r.error) || 'kaydedilemedi');
|
|
4645
|
+
}
|
|
4646
|
+
}
|
|
4647
|
+
|
|
4648
|
+
$('#ideBtn').addEventListener('click', () => setIdeMode(!ideModeOn()));
|
|
4649
|
+
$('#filePick').addEventListener('click', async () => {
|
|
4650
|
+
const r = await beast.ideSetRoot().catch(() => null);
|
|
4651
|
+
if (r && r.ok) {
|
|
4652
|
+
ideState.open = new Set(['']);
|
|
4653
|
+
await loadIdeTree();
|
|
4654
|
+
} else if (r && !r.canceled && r.error) {
|
|
4655
|
+
toast(r.error);
|
|
4656
|
+
}
|
|
4657
|
+
});
|
|
4658
|
+
$('#fileRefresh').addEventListener('click', () => loadIdeTree());
|
|
4659
|
+
$('#filePreview').addEventListener('click', async () => {
|
|
4660
|
+
const r = await beast.idePreview().catch(() => null);
|
|
4661
|
+
if (r && r.ok) {
|
|
4662
|
+
if (ideModeOn() === false) setIdeMode(true);
|
|
4663
|
+
} else {
|
|
4664
|
+
toast((r && r.error) || 'preview açılamadı');
|
|
4665
|
+
}
|
|
4666
|
+
});
|
|
4667
|
+
if (els.codeSave) els.codeSave.addEventListener('click', codeSave);
|
|
4668
|
+
if (els.codeTa) {
|
|
4669
|
+
els.codeTa.addEventListener('input', () => {
|
|
4670
|
+
if (codeActive < 0) return;
|
|
4671
|
+
codeTabs[codeActive].dirty = true;
|
|
4672
|
+
const el = els.codeTabs.children[codeActive];
|
|
4673
|
+
if (el) el.classList.add('dirty');
|
|
4674
|
+
});
|
|
4675
|
+
els.codeTa.addEventListener('keydown', (e) => {
|
|
4676
|
+
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 's') {
|
|
4677
|
+
e.preventDefault();
|
|
4678
|
+
codeSave();
|
|
4679
|
+
}
|
|
4680
|
+
});
|
|
4681
|
+
}
|
|
4682
|
+
|
|
4683
|
+
/* ---------- dosya ağacı sağ tık menüsü (Sil / Aç / Preview) ---------- */
|
|
4684
|
+
|
|
4685
|
+
function fileCtxHide() {
|
|
4686
|
+
if (els.fileCtxMenu) els.fileCtxMenu.hidden = true;
|
|
4687
|
+
}
|
|
4688
|
+
|
|
4689
|
+
function fileCtxShow(e, rel, isDir) {
|
|
4690
|
+
const menu = els.fileCtxMenu;
|
|
4691
|
+
if (!menu || !rel) return;
|
|
4692
|
+
e.preventDefault();
|
|
4693
|
+
const isHtml = /\.html?$/i.test(rel);
|
|
4694
|
+
const items = [];
|
|
4695
|
+
if (isDir) {
|
|
4696
|
+
items.push({
|
|
4697
|
+
label: ideState.open.has(rel) ? 'Kapat' : 'Aç',
|
|
4698
|
+
fn: () => {
|
|
4699
|
+
if (ideState.open.has(rel)) ideState.open.delete(rel);
|
|
4700
|
+
else ideState.open.add(rel);
|
|
4701
|
+
renderFileTree();
|
|
4702
|
+
},
|
|
4703
|
+
});
|
|
4704
|
+
} else {
|
|
4705
|
+
items.push({ label: 'Aç', fn: () => codeOpen(rel) });
|
|
4706
|
+
if (isHtml) {
|
|
4707
|
+
items.push({
|
|
4708
|
+
label: 'Preview',
|
|
4709
|
+
fn: async () => {
|
|
4710
|
+
const r = await beast.idePreviewFile(rel).catch(() => null);
|
|
4711
|
+
if (!r || !r.ok) toast((r && r.error) || 'preview açılamadı');
|
|
4712
|
+
},
|
|
4713
|
+
});
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4716
|
+
items.push({
|
|
4717
|
+
label: 'Sil',
|
|
4718
|
+
danger: true,
|
|
4719
|
+
fn: async () => {
|
|
4720
|
+
const r = await beast.ideDelete(rel).catch(() => null);
|
|
4721
|
+
if (r && r.ok) {
|
|
4722
|
+
/* silinen dosya/klasörün açık sekmelerini kapat */
|
|
4723
|
+
for (let i = codeTabs.length - 1; i >= 0; i--) {
|
|
4724
|
+
if (codeTabs[i].rel === rel || codeTabs[i].rel.startsWith(rel + '/')) codeClose(i);
|
|
4725
|
+
}
|
|
4726
|
+
await loadIdeTree();
|
|
4727
|
+
} else if (r && !r.canceled && r.error) {
|
|
4728
|
+
toast(r.error);
|
|
4729
|
+
}
|
|
4730
|
+
},
|
|
4731
|
+
});
|
|
4732
|
+
|
|
4733
|
+
menu.innerHTML = '';
|
|
4734
|
+
for (const it of items) {
|
|
4735
|
+
const el = document.createElement('div');
|
|
4736
|
+
el.className = 'ctx-item' + (it.danger ? ' danger' : '');
|
|
4737
|
+
el.textContent = it.label;
|
|
4738
|
+
el.addEventListener('click', () => {
|
|
4739
|
+
fileCtxHide();
|
|
4740
|
+
it.fn();
|
|
4741
|
+
});
|
|
4742
|
+
menu.appendChild(el);
|
|
4743
|
+
}
|
|
4744
|
+
menu.hidden = false;
|
|
4745
|
+
const mh = items.length * 33 + 8;
|
|
4746
|
+
menu.style.left = Math.max(4, Math.min(e.clientX, window.innerWidth - 170)) + 'px';
|
|
4747
|
+
menu.style.top = Math.max(4, Math.min(e.clientY, window.innerHeight - mh)) + 'px';
|
|
4748
|
+
}
|
|
4749
|
+
|
|
4750
|
+
if (els.fileCtxMenu) {
|
|
4751
|
+
document.addEventListener('click', fileCtxHide);
|
|
4752
|
+
window.addEventListener('blur', fileCtxHide);
|
|
4753
|
+
window.addEventListener('keydown', (e) => {
|
|
4754
|
+
if (e.key === 'Escape') fileCtxHide();
|
|
4755
|
+
});
|
|
4756
|
+
}
|
|
4757
|
+
$('#fileTree').addEventListener('contextmenu', (e) => {
|
|
4758
|
+
const row = e.target.closest('.file-row');
|
|
4759
|
+
if (!row || !row.dataset.rel) {
|
|
4760
|
+
fileCtxHide();
|
|
4761
|
+
return;
|
|
4762
|
+
}
|
|
4763
|
+
fileCtxShow(e, row.dataset.rel, row.dataset.dir === '1');
|
|
4764
|
+
});
|
|
4765
|
+
|
|
4766
|
+
/* ---------- IDE modu ortası: BEAST CODE paneli ----------
|
|
4767
|
+
Sohbet yerine Beast'in kendi ajanı (varsayılan model) çalışır;
|
|
4768
|
+
soldaki dosya panelindeki klasörü workspace olarak kullanır.
|
|
4769
|
+
Gizli engine oturumu → olaylar onEvent içinde panele yönlendirilir. */
|
|
4770
|
+
|
|
4771
|
+
const BC_MAX_LINES = 1200;
|
|
4772
|
+
let bcSessionId = null;
|
|
4773
|
+
let bcRunning = false;
|
|
4774
|
+
let bcBannerDone = false;
|
|
4775
|
+
let bcStreamEl = null;
|
|
4776
|
+
let bcStreamRaw = '';
|
|
4777
|
+
|
|
4778
|
+
function bcScroll(force) {
|
|
4779
|
+
const b = els.bcOut;
|
|
4780
|
+
if (!b) return;
|
|
4781
|
+
/* her yeni içerikte son mesaja doğru otomatik kaydır */
|
|
4782
|
+
b.scrollTop = b.scrollHeight;
|
|
4783
|
+
}
|
|
4784
|
+
|
|
4785
|
+
/* kutu içi gövdeyi (bc-toolbody) de son bölüme kaydır */
|
|
4786
|
+
function bcBodyScroll(el) {
|
|
4787
|
+
const b = el && el.closest ? el.closest('.bc-toolbody') : null;
|
|
4788
|
+
if (b) b.scrollTop = b.scrollHeight;
|
|
4789
|
+
}
|
|
4790
|
+
|
|
4791
|
+
function bcLine(cls, text, time = false) {
|
|
4792
|
+
if (!els.bcOut) return;
|
|
4793
|
+
const el = document.createElement('div');
|
|
4794
|
+
el.className = 't-line ' + (cls || '');
|
|
4795
|
+
const ts = time ? '<span class="t-time">' + new Date().toTimeString().slice(0, 8) + '</span> ' : '';
|
|
4796
|
+
el.innerHTML = ts + escapeHtml(String(text ?? ''));
|
|
4797
|
+
els.bcOut.appendChild(el);
|
|
4798
|
+
while (els.bcOut.childElementCount > BC_MAX_LINES) els.bcOut.removeChild(els.bcOut.firstChild);
|
|
4799
|
+
bcScroll();
|
|
4800
|
+
}
|
|
4801
|
+
|
|
4802
|
+
function bcBanner() {
|
|
4803
|
+
if (bcBannerDone) return;
|
|
4804
|
+
bcBannerDone = true;
|
|
4805
|
+
const cwd = ideState.path || '';
|
|
4806
|
+
if (els.bcCwd && cwd) { els.bcCwd.textContent = cwd; els.bcCwd.title = cwd; }
|
|
4807
|
+
bcLine('t-sys', 'BEAST CODE — çalışma klasörü: ' + (cwd || '?'));
|
|
4808
|
+
bcLine('t-dim', 'Beast\u2019in varsayılan modeliyle kod yazar; yazışma bu klasöre bağlıdır. + yeni oturum · Temizle çıktıyı siler.', false);
|
|
4809
|
+
}
|
|
4810
|
+
|
|
4811
|
+
/* Klasör değişimi (Klasör seç / kök değişti) → panel taze başlar;
|
|
4812
|
+
oturumlar main tarafında klasör bazlı tutulur (klasöre dönüşte aynı sohbet sürer).
|
|
4813
|
+
bcSessionId kasıtlı sıfırlanmaz: klasör değişse bile çalışan işin done/idle
|
|
4814
|
+
sinyali panele ulaşıp kilidi açar. */
|
|
4815
|
+
let bcWsPath = '';
|
|
4816
|
+
|
|
4817
|
+
function bcOnFolderChanged(p) {
|
|
4818
|
+
if (!p || bcWsPath === p) return;
|
|
4819
|
+
const first = !bcWsPath;
|
|
4820
|
+
bcWsPath = p;
|
|
4821
|
+
if (first) return; /* ilk yükleme — panel zaten boş, banner setIdeMode'da basılır */
|
|
4822
|
+
if (els.bcOut) els.bcOut.innerHTML = '';
|
|
4823
|
+
bcTools.clear();
|
|
4824
|
+
bcCloseToolGroup();
|
|
4825
|
+
bcHideTodos();
|
|
4826
|
+
bcStreamEl = null;
|
|
4827
|
+
bcStreamRaw = '';
|
|
4828
|
+
bcBannerDone = false;
|
|
4829
|
+
bcBanner();
|
|
4830
|
+
}
|
|
4831
|
+
|
|
4832
|
+
function bcSetBusy(v) {
|
|
4833
|
+
bcRunning = !!v;
|
|
4834
|
+
if (els.bcStop) els.bcStop.hidden = !v;
|
|
4835
|
+
}
|
|
4836
|
+
|
|
4837
|
+
function bcFlushStream() {
|
|
4838
|
+
bcStreamEl = null;
|
|
4839
|
+
bcStreamRaw = '';
|
|
4840
|
+
}
|
|
4841
|
+
|
|
4842
|
+
function bcStreamDelta(delta) {
|
|
4843
|
+
if (!bcStreamEl) {
|
|
4844
|
+
bcStreamEl = document.createElement('div');
|
|
4845
|
+
bcStreamEl.className = 't-line t-out';
|
|
4846
|
+
els.bcOut.appendChild(bcStreamEl);
|
|
4847
|
+
}
|
|
4848
|
+
bcStreamRaw += String(delta || '');
|
|
4849
|
+
bcStreamEl.textContent = bcStreamRaw.slice(-4000);
|
|
4850
|
+
bcScroll(true);
|
|
4851
|
+
}
|
|
4852
|
+
|
|
4853
|
+
/* İş bitti → preview açık ve IDE modundaysa workspace sayfasını otomatik yenile */
|
|
4854
|
+
let bcPrevAt = 0;
|
|
4855
|
+
|
|
4856
|
+
function bcRefreshPreview() {
|
|
4857
|
+
const now = Date.now();
|
|
4858
|
+
if (now - bcPrevAt < 1500) return; /* done + idle çift tetiklemesin */
|
|
4859
|
+
bcPrevAt = now;
|
|
4860
|
+
if (!ideModeOn() || !document.body.classList.contains('browser-open')) return;
|
|
4861
|
+
beast.idePreview().catch(() => {});
|
|
4862
|
+
}
|
|
4863
|
+
|
|
4864
|
+
/* ---------- Beast Code araç kutuları ----------
|
|
4865
|
+
Ard arda gelen araç çağrıları TEK kutuda toplanır. Kural: engine mesajı
|
|
4866
|
+
tool_calls İÇERİYORSA (iş turu) grup AÇIK kalır — aradaki ara yazılar kutu
|
|
4867
|
+
içine not olarak akar; tool_calls İÇERMEYEN saf metin (normal mesaj) gelirse
|
|
4868
|
+
grup kapanır, sonraki çağrılar YENİ kutu açar.
|
|
4869
|
+
Kutu çalışırken etrafında fosforlu açık mavi outline döner; her çağrı kutu
|
|
4870
|
+
içinde bir bölüm, çıktı max 180px scroll'lu. */
|
|
4871
|
+
const bcTools = new Map(); /* callId → { box, sec } */
|
|
4872
|
+
let bcCurBox = null; /* açık araç grubu — ard arda çağrılar buraya eklenir */
|
|
4873
|
+
let bcNoteEl = null; /* grup içindeki ara yazı notu */
|
|
4874
|
+
let bcNoteRaw = '';
|
|
4875
|
+
|
|
4876
|
+
function bcNoteDetach() {
|
|
4877
|
+
bcNoteEl = null;
|
|
4878
|
+
bcNoteRaw = '';
|
|
4879
|
+
}
|
|
4880
|
+
|
|
4881
|
+
function bcCloseToolGroup() {
|
|
4882
|
+
/* grup kapanınca (normal mesaj / iş bitti) ring durur */
|
|
4883
|
+
if (bcCurBox) bcCurBox.classList.remove('running');
|
|
4884
|
+
bcCurBox = null;
|
|
4885
|
+
bcNoteDetach();
|
|
4886
|
+
}
|
|
4887
|
+
|
|
4888
|
+
function bcToolGroupNew() {
|
|
4889
|
+
const box = document.createElement('div');
|
|
4890
|
+
box.className = 'bc-toolbox';
|
|
4891
|
+
const body = document.createElement('div');
|
|
4892
|
+
body.className = 'bc-toolbody';
|
|
4893
|
+
box.appendChild(body);
|
|
4894
|
+
els.bcOut.appendChild(box);
|
|
4895
|
+
while (els.bcOut.childElementCount > BC_MAX_LINES) els.bcOut.removeChild(els.bcOut.firstChild);
|
|
4896
|
+
return box;
|
|
4897
|
+
}
|
|
4898
|
+
|
|
4899
|
+
function bcToolBoxStart(callId, name, args) {
|
|
4900
|
+
bcFlushStream();
|
|
4901
|
+
if (!bcCurBox) bcCurBox = bcToolGroupNew();
|
|
4902
|
+
const sec = document.createElement('div');
|
|
4903
|
+
sec.className = 'bc-toolsection';
|
|
4904
|
+
const short = termShortTool(name, args);
|
|
4905
|
+
sec.innerHTML =
|
|
4906
|
+
'<div class="bc-toolhead">' +
|
|
4907
|
+
'<span class="bc-toolspin"></span>' +
|
|
4908
|
+
'<span class="bc-toolname">▸ ' + escapeHtml(String(name || 'araç')) + '</span>' +
|
|
4909
|
+
(short ? '<span class="bc-toolar" title="' + escapeHtml(short) + '">' + escapeHtml(short) + '</span>' : '') +
|
|
4910
|
+
'</div>' +
|
|
4911
|
+
'<div class="bc-toolout" hidden></div>';
|
|
4912
|
+
/* ring: iş bitene / grup kapanana kadar bu kutuda dönmeye devam eder */
|
|
4913
|
+
bcCurBox.classList.add('running');
|
|
4914
|
+
const body = bcCurBox.querySelector('.bc-toolbody') || bcCurBox;
|
|
4915
|
+
body.appendChild(sec);
|
|
4916
|
+
if (callId) bcTools.set(callId, { box: bcCurBox, sec });
|
|
4917
|
+
bcScroll();
|
|
4918
|
+
bcBodyScroll(body);
|
|
4919
|
+
}
|
|
4920
|
+
|
|
4921
|
+
function bcToolBoxEnd(callId, ok, result) {
|
|
4922
|
+
bcFlushStream();
|
|
4923
|
+
const t = (callId && bcTools.get(callId)) || null;
|
|
4924
|
+
if (callId) bcTools.delete(callId);
|
|
4925
|
+
const out = String(result || '').replace(/\s+$/, '');
|
|
4926
|
+
if (t) {
|
|
4927
|
+
const { sec } = t;
|
|
4928
|
+
/* ring burada SÖNDÜRÜLMEZ — iş bitene kadar (bcCloseToolGroup) döner */
|
|
4929
|
+
sec.classList.add('done');
|
|
4930
|
+
if (!ok) sec.classList.add('failed');
|
|
4931
|
+
const pre = sec.querySelector('.bc-toolout');
|
|
4932
|
+
if (out) {
|
|
4933
|
+
pre.hidden = false;
|
|
4934
|
+
pre.textContent = out.length > 4000 ? out.slice(0, 4000) + '\n… (kesildi)' : out;
|
|
4935
|
+
} else {
|
|
4936
|
+
pre.remove();
|
|
4937
|
+
}
|
|
4938
|
+
bcBodyScroll(sec);
|
|
4939
|
+
} else if (out || !ok) {
|
|
4940
|
+
/* başlangıcı kaçmış çağrı — düz satır olarak düş */
|
|
4941
|
+
bcLine(ok ? 't-out' : 't-err', out ? (out.length > 400 ? out.slice(0, 400) + ' …(kesildi)' : out) : '(hata)');
|
|
4942
|
+
}
|
|
4943
|
+
bcScroll();
|
|
4944
|
+
}
|
|
4945
|
+
|
|
4946
|
+
/* ---------- Beast Code todo kartı ----------
|
|
4947
|
+
Inputun (chat) hemen üstünde ortalı kompakt kutu; #bcTodoWrap içinde yaşar,
|
|
4948
|
+
scroll'lu çıktı akışını kirletmez. */
|
|
4949
|
+
function bcRenderTodos(todos) {
|
|
4950
|
+
const list = Array.isArray(todos) ? todos : [];
|
|
4951
|
+
if (!els.bcTodoWrap) return;
|
|
4952
|
+
bcFlushStream();
|
|
4953
|
+
if (!list.length) {
|
|
4954
|
+
bcHideTodos();
|
|
4955
|
+
return;
|
|
4956
|
+
}
|
|
4957
|
+
els.bcTodoWrap.hidden = false;
|
|
4958
|
+
els.bcTodoWrap.innerHTML =
|
|
4959
|
+
'<div class="bc-todobox">' +
|
|
4960
|
+
'<div class="bc-todotitle"><span>GÖREVLER</span><span class="bc-todocount"></span></div>' +
|
|
4961
|
+
'<div class="bc-todoitems"></div>';
|
|
4962
|
+
const done = list.filter((t) => t.status === 'done').length;
|
|
4963
|
+
els.bcTodoWrap.querySelector('.bc-todocount').textContent = done + '/' + list.length;
|
|
4964
|
+
const wrap = els.bcTodoWrap.querySelector('.bc-todoitems');
|
|
4965
|
+
wrap.innerHTML = '';
|
|
4966
|
+
for (const t of list) {
|
|
4967
|
+
const st = TODO_GLYPH[t.status] ? t.status : 'pending';
|
|
4968
|
+
const row = document.createElement('div');
|
|
4969
|
+
row.className = 'bc-todoitem ' + st;
|
|
4970
|
+
row.innerHTML =
|
|
4971
|
+
'<span class="bc-todocheck">' + TODO_GLYPH[st] + '</span>' +
|
|
4972
|
+
'<span class="bc-todotext"></span>';
|
|
4973
|
+
row.querySelector('.bc-todotext').textContent = String(t.title || '');
|
|
4974
|
+
wrap.appendChild(row);
|
|
4975
|
+
}
|
|
4976
|
+
}
|
|
4977
|
+
|
|
4978
|
+
function bcHideTodos() {
|
|
4979
|
+
if (els.bcTodoWrap) {
|
|
4980
|
+
els.bcTodoWrap.hidden = true;
|
|
4981
|
+
els.bcTodoWrap.innerHTML = '';
|
|
4982
|
+
}
|
|
4983
|
+
}
|
|
4984
|
+
|
|
4985
|
+
/* Ara yazı → açık grup varsa kutu İÇİNE not olarak akar (kronoloji korunur) */
|
|
4986
|
+
function bcNoteStream(delta) {
|
|
4987
|
+
if (!bcCurBox) return false;
|
|
4988
|
+
if (!bcNoteEl) {
|
|
4989
|
+
bcNoteEl = document.createElement('div');
|
|
4990
|
+
bcNoteEl.className = 'bc-toolnote';
|
|
4991
|
+
const body = bcCurBox.querySelector('.bc-toolbody') || bcCurBox;
|
|
4992
|
+
body.appendChild(bcNoteEl);
|
|
4993
|
+
}
|
|
4994
|
+
bcNoteRaw += String(delta || '');
|
|
4995
|
+
bcNoteEl.textContent = bcNoteRaw.slice(-2000);
|
|
4996
|
+
bcScroll(true);
|
|
4997
|
+
bcBodyScroll(bcNoteEl);
|
|
4998
|
+
return true;
|
|
4999
|
+
}
|
|
5000
|
+
|
|
5001
|
+
/* Beast Code oturumunun engine olayları → panel çıktısı */
|
|
5002
|
+
function bcIngest(ev) {
|
|
5003
|
+
switch (ev.type) {
|
|
5004
|
+
case 'token':
|
|
5005
|
+
/* grup açıksa ara not (kutu içi); değilse normal akış */
|
|
5006
|
+
if (bcNoteStream(ev.delta)) break;
|
|
5007
|
+
bcStreamDelta(ev.delta);
|
|
5008
|
+
break;
|
|
5009
|
+
case 'message':
|
|
5010
|
+
if (ev.message && ev.message.role === 'assistant') {
|
|
5011
|
+
const content = typeof ev.message.content === 'string' ? ev.message.content : '';
|
|
5012
|
+
const hasTools = Array.isArray(ev.message.tool_calls) && ev.message.tool_calls.length > 0;
|
|
5013
|
+
if (hasTools) {
|
|
5014
|
+
/* iş turu: grup AÇIK kalır — ard arda araçlar aynı kutuda sürer.
|
|
5015
|
+
Kutu içine akmış ara not kalıcı olur, işaretçileri bırak */
|
|
5016
|
+
bcNoteDetach();
|
|
5017
|
+
bcFlushStream();
|
|
5018
|
+
break;
|
|
5019
|
+
}
|
|
5020
|
+
/* NORMAL MESAJ: kutu içine düşen taslak notu çıkar, cevabı düz metin bas */
|
|
5021
|
+
if (bcNoteEl) { bcNoteEl.remove(); bcNoteDetach(); }
|
|
5022
|
+
if (content && !bcStreamRaw.trim()) bcLine('t-out', content);
|
|
5023
|
+
bcCloseToolGroup();
|
|
5024
|
+
bcFlushStream();
|
|
5025
|
+
}
|
|
5026
|
+
break;
|
|
5027
|
+
case 'todos':
|
|
5028
|
+
bcRenderTodos(ev.todos);
|
|
5029
|
+
break;
|
|
5030
|
+
case 'tool-start':
|
|
5031
|
+
bcToolBoxStart(ev.callId, ev.name, ev.args);
|
|
5032
|
+
break;
|
|
5033
|
+
case 'tool-end':
|
|
5034
|
+
bcToolBoxEnd(ev.callId, ev.ok, ev.result);
|
|
5035
|
+
break;
|
|
5036
|
+
case 'done':
|
|
5037
|
+
bcFlushStream();
|
|
5038
|
+
bcCloseToolGroup();
|
|
5039
|
+
bcSetBusy(false);
|
|
5040
|
+
bcLine('t-dim', '(tamamlandı)');
|
|
5041
|
+
bcRefreshPreview();
|
|
5042
|
+
if (ideModeOn() && els.bcInput) els.bcInput.focus();
|
|
5043
|
+
break;
|
|
5044
|
+
case 'error':
|
|
5045
|
+
bcFlushStream();
|
|
5046
|
+
bcCloseToolGroup();
|
|
5047
|
+
bcSetBusy(false);
|
|
5048
|
+
bcLine('t-err', '[hata] ' + (ev.error || ''));
|
|
5049
|
+
break;
|
|
5050
|
+
case 'status':
|
|
5051
|
+
/* 'idle' = engine oturumu GERÇEKTEN bıraktı (finally bloğu, her yolda gelir) —
|
|
5052
|
+
done kaçsa bile panel kilitli kalmaz, ■ otomatik kalkar, ring durur */
|
|
5053
|
+
if (ev.status === 'idle') {
|
|
5054
|
+
bcCloseToolGroup();
|
|
5055
|
+
bcSetBusy(false);
|
|
5056
|
+
bcRefreshPreview();
|
|
5057
|
+
}
|
|
5058
|
+
break;
|
|
5059
|
+
}
|
|
5060
|
+
}
|
|
5061
|
+
|
|
5062
|
+
function bcRunCurrent() {
|
|
5063
|
+
const msg = els.bcInput.value.trim();
|
|
5064
|
+
if (!msg) return;
|
|
5065
|
+
els.bcInput.value = '';
|
|
5066
|
+
bcLine('t-cmd', 'code> ' + msg);
|
|
5067
|
+
bcHideTodos(); /* yeni sorgu = eski todo list temizlenir; agent yeniden basacak */
|
|
5068
|
+
bcSetBusy(true);
|
|
5069
|
+
/* sürüyor/serbest kararını main'deki gerçek engine.isBusy verir —
|
|
5070
|
+
panel bayat kilitli kaldıysa ilk mesajla kendini düzeltir */
|
|
5071
|
+
beast.beastcodeSend(msg).then((r) => {
|
|
5072
|
+
if (r && r.ok) {
|
|
5073
|
+
bcSessionId = r.sessionId;
|
|
5074
|
+
} else if (r && r.busy) {
|
|
5075
|
+
/* iş gerçekten sürüyor — kilit açılmaz, bilgi satırı düşer */
|
|
5076
|
+
bcLine('t-dim', (r && r.error) || 'mesaj sürüyor');
|
|
5077
|
+
} else {
|
|
5078
|
+
bcSetBusy(false);
|
|
5079
|
+
bcLine('t-err', (r && r.error) || 'gönderilemedi');
|
|
5080
|
+
}
|
|
5081
|
+
}).catch((e) => {
|
|
5082
|
+
bcSetBusy(false);
|
|
5083
|
+
bcLine('t-err', String((e && e.message) || e));
|
|
5084
|
+
});
|
|
5085
|
+
}
|
|
5086
|
+
|
|
5087
|
+
if (els.bcInput) els.bcInput.addEventListener('keydown', (e) => {
|
|
5088
|
+
if (e.key === 'Enter') { e.preventDefault(); bcRunCurrent(); }
|
|
5089
|
+
});
|
|
5090
|
+
if (els.bcStop) els.bcStop.addEventListener('click', () => beast.beastcodeStop().catch(() => {}));
|
|
5091
|
+
if (els.bcClear) els.bcClear.addEventListener('click', () => {
|
|
5092
|
+
els.bcOut.innerHTML = '';
|
|
5093
|
+
bcTools.clear();
|
|
5094
|
+
bcCloseToolGroup();
|
|
5095
|
+
bcHideTodos();
|
|
5096
|
+
bcFlushStream();
|
|
5097
|
+
});
|
|
5098
|
+
if (els.bcNew) els.bcNew.addEventListener('click', async () => {
|
|
5099
|
+
if (bcRunning) { toast('Mesaj sürüyor — önce ■ ile durdur'); return; }
|
|
5100
|
+
const r = await beast.beastcodeNew().catch(() => null);
|
|
5101
|
+
if (r && r.ok) {
|
|
5102
|
+
bcSessionId = null;
|
|
5103
|
+
bcTools.clear();
|
|
5104
|
+
bcCloseToolGroup();
|
|
5105
|
+
bcHideTodos();
|
|
5106
|
+
bcFlushStream();
|
|
5107
|
+
bcLine('t-sys', 'yeni oturum — sonraki mesaj taze başlar');
|
|
5108
|
+
}
|
|
5109
|
+
});
|
|
5110
|
+
/* Uygulama her açılışta agent (chat) modunda başlar — IDE modu yalnız
|
|
5111
|
+
kullanıcı butona basınca girilir (oturumluk). */
|