beast-agent 1.4.2 → 1.5.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.
@@ -28,7 +28,6 @@ const els = {
28
28
  cfgNone: $('#cfgNone'),
29
29
  cfgList: $('#cfgList'),
30
30
  themeBtn: $('#themeBtn'),
31
- langBtn: $('#langBtn'),
32
31
  chatScroll: $('#chatScroll'),
33
32
  msgs: $('#msgs'),
34
33
  empty: $('#empty'),
@@ -60,6 +59,7 @@ const els = {
60
59
  bbShot: $('#bbShot'),
61
60
  bbClose: $('#bbClose'),
62
61
  bbResize: $('#bbResize'),
62
+ bbPhone: $('#bbPhone'),
63
63
  termCBtn: $('#termCBtn'),
64
64
  termPanel: $('#termPanel'),
65
65
  termCwd: $('#termCwd'),
@@ -78,9 +78,25 @@ const els = {
78
78
  bcNew: $('#bcNew'),
79
79
  bcTodoWrap: $('#bcTodoWrap'),
80
80
  bcStatus: $('#bcStatus'),
81
+ bcAttach: $('#bcAttach'),
82
+ bcMic: $('#bcMic'),
83
+ bcChips: $('#bcChips'),
81
84
  codeTabs: $('#codeTabs'),
82
85
  codeTa: $('#codeTa'),
83
86
  codeGutter: $('#codeGutter'),
87
+ codeLayer: $('#codeLayer'),
88
+ codeDiffBtn: $('#codeDiffBtn'),
89
+ codeDiffView: $('#codeDiffView'),
90
+ codeDiffTitle: $('#codeDiffTitle'),
91
+ codeDiffStats: $('#codeDiffStats'),
92
+ codeDiffRows: $('#codeDiffRows'),
93
+ codeDiffClose: $('#codeDiffClose'),
94
+ codeFind: $('#codeFind'),
95
+ codeFindInp: $('#codeFindInp'),
96
+ codeFindCount: $('#codeFindCount'),
97
+ codeFindPrev: $('#codeFindPrev'),
98
+ codeFindNext: $('#codeFindNext'),
99
+ codeFindClose: $('#codeFindClose'),
84
100
  codePath: $('#codePath'),
85
101
  codeSave: $('#codeSave'),
86
102
  codeHide: $('#codeHide'),
@@ -98,11 +114,14 @@ const els = {
98
114
  gearBtn: $('#gearBtn'),
99
115
  settingsOverlay: $('#settingsOverlay'),
100
116
  setClose: $('#setClose'),
117
+ setVersion: $('#setVersion'),
101
118
  beastCode: $('#beastCode'),
102
119
  bcCopy: $('#bcCopy'),
103
120
  attachBtn: $('#attachBtn'),
104
121
  micBtn: $('#micBtn'),
105
122
  fileInput: $('#fileInput'),
123
+ fileTasks: $('#fileTasks'),
124
+ tasksPanel: $('#tasksPanel'),
106
125
  chips: $('#chips'),
107
126
  toast: $('#toast'),
108
127
  railList: $('#railList'),
@@ -320,7 +339,7 @@ function toast(msg) {
320
339
 
321
340
  /* ---------------- messages ---------------- */
322
341
 
323
- function addUserBubble(content) {
342
+ function addUserBubble(content, atts) {
324
343
  showEmpty(false);
325
344
  const div = document.createElement('div');
326
345
  div.className = 'msg msg-user';
@@ -339,11 +358,27 @@ function addUserBubble(content) {
339
358
  text = String(content);
340
359
  }
341
360
  div.appendChild(document.createTextNode(text));
342
- if (nImg) {
343
- const badge = document.createElement('div');
344
- badge.style.cssText = 'color:var(--accent);font-size:12px;margin-top:6px';
345
- badge.textContent = `[${nImg} resim]`;
346
- div.appendChild(badge);
361
+ /* ekler İÇERİK AÇILMADAN kompakt kart olarak görünür: ≡ dosya · ▣ resim */
362
+ const list = Array.isArray(atts) ? atts : [];
363
+ if (nImg || list.length) {
364
+ const wrap = document.createElement('div');
365
+ wrap.className = 'att-row';
366
+ let named = 0;
367
+ for (const a of list) {
368
+ const isImg = a && a.type === 'image';
369
+ const chip = document.createElement('span');
370
+ chip.className = 'att-chip' + (isImg ? ' att-img' : '');
371
+ chip.textContent = (isImg ? '\u25A3 ' : '\u2261 ') + String((a && a.name) || (isImg ? 'resim' : 'dosya'));
372
+ wrap.appendChild(chip);
373
+ if (isImg) named++;
374
+ }
375
+ for (let k = named; k < nImg; k++) {
376
+ const chip = document.createElement('span');
377
+ chip.className = 'att-chip att-img';
378
+ chip.textContent = '\u25A3 resim';
379
+ wrap.appendChild(chip);
380
+ }
381
+ div.appendChild(wrap);
347
382
  }
348
383
  els.msgs.appendChild(div);
349
384
  scrollDown(true);
@@ -812,7 +847,7 @@ async function openSession(id) {
812
847
 
813
848
  for (let i = 0; i < s.messages.length; i++) {
814
849
  const m = s.messages[i];
815
- if (m.role === 'user') addUserBubble(m.content);
850
+ if (m.role === 'user') addUserBubble(m.content, m.attachments);
816
851
  else if (m.role === 'assistant') {
817
852
  if (m.tool_calls && m.tool_calls.length) {
818
853
  for (const tc of m.tool_calls) {
@@ -840,6 +875,9 @@ async function openSession(id) {
840
875
 
841
876
  const TEXT_EXT = /\.(txt|md|json|csv|log|js|ts|py|ps1|bat|cmd|html|css|yaml|yml|xml|ini)$/i;
842
877
 
878
+ /* ek hedefleri: chat + Beast Code panosu ayrı kuyruklar */
879
+ let bcPending = [];
880
+
843
881
  function renderChips() {
844
882
  els.chips.innerHTML = '';
845
883
  els.chips.hidden = pending.length === 0;
@@ -860,22 +898,46 @@ function renderChips() {
860
898
  });
861
899
  }
862
900
 
863
- function addFiles(files) {
901
+ function renderBcChips() {
902
+ if (!els.bcChips) return;
903
+ els.bcChips.innerHTML = '';
904
+ els.bcChips.hidden = bcPending.length === 0;
905
+ bcPending.forEach((a, i) => {
906
+ const c = document.createElement('span');
907
+ c.className = 'chip';
908
+ const label = a.type === 'image' ? `\u25A3 ${a.name}` : `\u2261 ${a.name}`;
909
+ c.appendChild(document.createTextNode(label));
910
+ const x = document.createElement('span');
911
+ x.className = 'x';
912
+ x.textContent = '×';
913
+ x.addEventListener('click', () => {
914
+ bcPending.splice(i, 1);
915
+ renderBcChips();
916
+ });
917
+ c.appendChild(x);
918
+ els.bcChips.appendChild(c);
919
+ });
920
+ }
921
+
922
+ function addFiles(files, mode) {
923
+ const isBc = mode === 'bc';
864
924
  for (const f of files) {
865
925
  if (f.size > 8 * 1024 * 1024) { toast('Çok büyük (max 8MB): ' + f.name); continue; }
866
926
  if (f.type.startsWith('image/')) {
867
927
  const r = new FileReader();
868
928
  r.onload = () => {
869
- if (pending.length >= 6) { toast('En fazla 6 ek'); return; }
870
- pending.push({ type: 'image', name: f.name, dataUrl: String(r.result) });
871
- renderChips();
929
+ const list = isBc ? bcPending : pending;
930
+ if (list.length >= 6) { toast('En fazla 6 ek'); return; }
931
+ list.push({ type: 'image', name: f.name, dataUrl: String(r.result) });
932
+ isBc ? renderBcChips() : renderChips();
872
933
  };
873
934
  r.readAsDataURL(f);
874
935
  } else if (TEXT_EXT.test(f.name)) {
875
936
  f.text().then((t) => {
876
- if (pending.length >= 6) { toast('En fazla 6 ek'); return; }
877
- pending.push({ type: 'file', name: f.name, content: t.slice(0, 200000) });
878
- renderChips();
937
+ const list = isBc ? bcPending : pending;
938
+ if (list.length >= 6) { toast('En fazla 6 ek'); return; }
939
+ list.push({ type: 'file', name: f.name, content: t.slice(0, 200000) });
940
+ isBc ? renderBcChips() : renderChips();
879
941
  });
880
942
  } else {
881
943
  toast('Desteklenmeyen tür: ' + f.name);
@@ -885,10 +947,42 @@ function addFiles(files) {
885
947
 
886
948
  /* ---------------- settings ---------------- */
887
949
 
888
- let setTab = 'provider';
950
+ let setTab = 'lang'; /* ayarlar artık DİL sekmesiyle açılır */
951
+
952
+ /* DİL sekmesi: arayüz dili seçimi (eski sağ-alt dil ikonunun yerine) */
953
+ function renderLangPane() {
954
+ const pane = $('#tab-lang');
955
+ if (!pane) return;
956
+ const cur = (window.I18N && window.I18N.lang) || 'tr';
957
+ /* iki büyük kart YAN YANA — aynı boyda (seçili dil vurgusu çerçeveyle verilir, tick yok) */
958
+ const item = (code, label, sub) =>
959
+ `<button class="prov-row lang-row${cur === code ? ' active' : ''}" data-lang="${code}" ` +
960
+ `style="flex:1;flex-direction:column;align-items:center;justify-content:center;gap:5px;min-height:88px;margin:0">` +
961
+ `<span style="font-size:16px;font-weight:800">${escapeHtml(label)}</span>` +
962
+ `<span class="sub" style="margin:0;text-align:center;font-size:11.5px">${escapeHtml(sub)}</span>` +
963
+ `</button>`;
964
+ pane.innerHTML =
965
+ `<h2 data-i18n="tab_lang">${_t('tab_lang')}</h2>` +
966
+ `<div class="sub">${_t('lang_sub')}</div>` +
967
+ `<div style="display:flex;gap:12px;max-width:460px;width:100%">` +
968
+ item('tr', 'T\u00FCrk\u00E7e', 'T\u00FCrk\u00E7e aray\u00FCz') +
969
+ item('en', 'English', 'English UI') +
970
+ `</div>`;
971
+ pane.querySelectorAll('.lang-row').forEach((b) => {
972
+ b.addEventListener('click', () => {
973
+ const l = b.dataset.lang;
974
+ if (window.I18N && window.I18N.lang !== l) {
975
+ window.I18N.setLang(l); /* langchange → paneler otomatik yeniden çizilir */
976
+ }
977
+ });
978
+ });
979
+ }
889
980
 
890
981
  async function openSettings() {
891
982
  els.settingsOverlay.hidden = false;
983
+ if (els.setVersion) {
984
+ beast.appVersion().then((v) => { els.setVersion.textContent = 'v' + String(v || '?'); }).catch(() => {});
985
+ }
892
986
  try {
893
987
  const s = await beast.getSettings();
894
988
  if (els.beastCode) els.beastCode.textContent = s.beastCode || '—';
@@ -909,6 +1003,7 @@ function closeSettings() {
909
1003
  async function renderActiveSettingsTab() {
910
1004
  if (els.settingsOverlay.hidden) return;
911
1005
  switch (setTab) {
1006
+ case 'lang': renderLangPane(); break;
912
1007
  case 'provider': await renderProviderPane(); break;
913
1008
  case 'fallout': await refreshFalloutPane(); break;
914
1009
  case 'skills': await renderSkillsPane(); break;
@@ -922,7 +1017,6 @@ async function renderActiveSettingsTab() {
922
1017
  case 'agents': await refreshAgentsPane(); break;
923
1018
  case 'logs': await renderLogPane(); break;
924
1019
  case 'dash': await renderDashboardPane(); break;
925
- case 'limits': await renderLimitsPane(); break;
926
1020
  case 'sec': await renderSecurityPane(); break;
927
1021
  case 'update': await renderUpdatePane(); break;
928
1022
  }
@@ -933,16 +1027,16 @@ function switchTab(name) {
933
1027
  document.querySelectorAll('#setTabs .tab').forEach((b) =>
934
1028
  b.classList.toggle('active', b.dataset.tab === name)
935
1029
  );
936
- for (const p of ['provider', 'fallout', 'skills', 'agents', 'tts', 'email', 'integrations', 'websearch', 'events', 'cron', 'usage', 'logs', 'dash', 'limits', 'sec', 'update']) {
1030
+ for (const p of ['lang', 'provider', 'fallout', 'skills', 'agents', 'tts', 'email', 'integrations', 'websearch', 'events', 'cron', 'usage', 'logs', 'dash', 'sec', 'update']) {
937
1031
  const el = $('#tab-' + p);
938
1032
  if (el) el.hidden = p !== name; // guard: eksik pane tüm sekmeleri kilitlemesin
939
1033
  }
1034
+ if (name === 'lang') renderLangPane();
940
1035
  if (name === 'cron') openCron();
941
1036
  if (name === 'usage') renderUsagePane();
942
1037
  if (name === 'events') renderEventsPane();
943
1038
  if (name === 'logs') renderLogPane();
944
1039
  if (name === 'dash') renderDashboardPane();
945
- if (name === 'limits') renderLimitsPane();
946
1040
  if (name === 'sec') renderSecurityPane();
947
1041
  if (name === 'update') renderUpdatePane(true);
948
1042
  if (name === 'agents') refreshAgentsPane();
@@ -1027,6 +1121,11 @@ async function renderUsagePane() {
1027
1121
  await beast.whereWasISet({ enabled: e.target.checked });
1028
1122
  toast(e.target.checked ? _t('us_where_on_toast') : _t('us_where_off_toast'));
1029
1123
  });
1124
+ /* LİMİT ayarları aynı sekmenin alt bölümünde (Maliyet · Limit birleşik) */
1125
+ const limBox = document.createElement('div');
1126
+ limBox.id = 'limBox';
1127
+ pane.appendChild(limBox);
1128
+ await renderLimitsPane();
1030
1129
  }
1031
1130
 
1032
1131
  async function refreshFalloutPane() {
@@ -2194,7 +2293,8 @@ async function renderDashboardPane() {
2194
2293
  /* ---------------- Limit: provider bazlı girdi limiti ---------------- */
2195
2294
 
2196
2295
  async function renderLimitsPane() {
2197
- const pane = $('#tab-limits');
2296
+ /* Maliyet sekmesine GÖMÜLÜ çalışır (ayrı sekme kaldırıldı) */
2297
+ const pane = $('#tab-limits') || $('#limBox');
2198
2298
  if (!pane) return;
2199
2299
  const cur = await beast.getLimits().catch(() => ({ enabled: false, compress: true, default: 0, perProvider: {} }));
2200
2300
  const provs = [...new Set((state.models || []).map((m) => m.providerName))].sort((a, b) => a.localeCompare(b));
@@ -4197,7 +4297,7 @@ function onEvent(ev) {
4197
4297
  beast.getState().then((s) => { state = s; applyState(); }).catch(() => {});
4198
4298
  break;
4199
4299
  case 'message':
4200
- if (ev.message.role === 'user') addUserBubble(ev.message.content);
4300
+ if (ev.message.role === 'user') addUserBubble(ev.message.content, ev.message.attachments);
4201
4301
  else if (ev.message.role === 'assistant') {
4202
4302
  finalizeAssistant(ev.message.content);
4203
4303
  /* tool_calls'lı iş turu grupları açık tutar; saf metin (normal cevap) kapatır */
@@ -4237,12 +4337,19 @@ function onEvent(ev) {
4237
4337
  case 'file':
4238
4338
  addFileCard(ev);
4239
4339
  break;
4340
+ case 'ide-tree-changed':
4341
+ /* dosya sistemi değişti (ajan, kullanıcı kaydı, git, harici program) —
4342
+ soldaki klasör paneli ELLE yenilemeden canlı tazelenir */
4343
+ ideRefreshTree();
4344
+ break;
4240
4345
  case 'browser': {
4241
4346
  /* visible=false → ajan tarayıcıyı GİZLİ kullanıyor: UI yer açmaz */
4242
4347
  const shown = !!ev.open && ev.visible !== false;
4243
4348
  const wasShown = document.body.classList.contains('browser-open');
4244
4349
  document.body.classList.toggle('browser-open', shown);
4245
4350
  if (shown && ev.width) document.body.style.setProperty('--bw', ev.width + 'px');
4351
+ document.body.classList.toggle('phone-mode', !!ev.phone);
4352
+ if (els.bbPhone) els.bbPhone.classList.toggle('on', !!ev.phone);
4246
4353
  els.browserBar.hidden = !shown;
4247
4354
  els.bbResize.hidden = !shown;
4248
4355
  /* terminal de sağ dock'u kullanır — tarayıcı açılınca yerini bırak */
@@ -4276,9 +4383,29 @@ function onEvent(ev) {
4276
4383
  if (!busy) break;
4277
4384
  setStatus(ev.status === 'idle' ? (busy ? 'düşünüyor…' : '') : ev.status === 'thinking' ? 'düşünüyor…' : ev.status + '…');
4278
4385
  break;
4279
- case 'term-out':
4280
- termLine(ev.stream === 'err' ? 't-err' : 't-out', ev.chunk, false);
4386
+ case 'term-out': {
4387
+ /* KALICI CMD: her komutun sonunda marker satırı gelir —
4388
+ __BEAST_EOF__<yol>__BEAST_EOF__ → yazdırılmaz, cwd güncellenir, kilit açılır */
4389
+ const chunk = String(ev.chunk || '');
4390
+ if (ev.stream === 'err') { termLine('t-err', chunk, false); break; }
4391
+ termPendBuf += chunk;
4392
+ const parts = termPendBuf.split(/\r?\n/);
4393
+ termPendBuf = parts.pop(); /* son yarım satır bekler */
4394
+ for (const line of parts) {
4395
+ const m = /__BEAST_EOF__(.*)__BEAST_EOF__/.exec(line.trim());
4396
+ if (m) {
4397
+ const cwd2 = m[1].trim();
4398
+ if (cwd2) {
4399
+ els.termCwd.textContent = cwd2;
4400
+ els.termCwd.title = cwd2;
4401
+ }
4402
+ termCmdDone({ code: 0, silent: true });
4403
+ continue;
4404
+ }
4405
+ if (line.trim()) termLine('t-out', line, false);
4406
+ }
4281
4407
  break;
4408
+ }
4282
4409
  case 'term-end':
4283
4410
  termCmdDone(ev);
4284
4411
  break;
@@ -4342,6 +4469,7 @@ function addFileCard(ev) {
4342
4469
  let termOpen = false;
4343
4470
  let termBannerDone = false;
4344
4471
  let termRunning = false;
4472
+ let termPendBuf = ''; /* kalıcı CMD: yarım satır tamponu (marker tespiti için) */
4345
4473
  let termHist = [];
4346
4474
  let termHistIdx = -1;
4347
4475
  let termShell = 'cmd';
@@ -4401,7 +4529,7 @@ function termBanner(cwd) {
4401
4529
  els.termCwd.title = cwd || '';
4402
4530
  termLine('t-sys', 'Beast Terminal — çalışma klasörü: ' + (cwd || '?'));
4403
4531
  termLine('t-sys', 'Kabuk: ' + TERM_SHELLS[termShell].label + ' · Ajanlar çalışırken araç çağrıları ve çıktıları burada canlı akar.', false);
4404
- termLine('t-dim', 'Komut yaz, Enter\'a bas her komut workspace\'te ayrı süreçle çalışır. Kabuk: CMD.', false);
4532
+ termLine('t-dim', 'KALICI CMD oturumucd ile geçtiğin klasör komutlar arasında KORUNUR; set ile tanımladığın değişkenler de öyle.', false);
4405
4533
  }
4406
4534
 
4407
4535
  function termShortTool(name, args) {
@@ -4431,8 +4559,9 @@ function termAgentEvent(ev) {
4431
4559
  function termCmdDone(ev) {
4432
4560
  termRunning = false;
4433
4561
  els.termStop.hidden = true;
4434
- if (ev.error) termLine('t-err', '[hata] ' + ev.error, false);
4435
- else termLine('t-dim', '(çıkış kodu: ' + ev.code + ')' + (ev.code ? ' — başarısız' : ''), false);
4562
+ if (ev && ev.silent) return; /* kalıcı CMD marker bitişi — çıkış satırı yazma */
4563
+ if (ev && ev.error) termLine('t-err', '[hata] ' + ev.error, false);
4564
+ else termLine('t-dim', '(çıkış kodu: ' + (ev ? ev.code : 0) + ')' + (ev && ev.code ? ' — başarısız' : ''), false);
4436
4565
  }
4437
4566
 
4438
4567
  async function termToggle(shell) {
@@ -4953,12 +5082,6 @@ async function init() {
4953
5082
  }
4954
5083
  };
4955
5084
 
4956
- const syncLangBtn = () => {
4957
- if (!els.langBtn) return;
4958
- els.langBtn.innerHTML =
4959
- '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="9"/><path d="M3 12h18"/><path d="M12 3c2.6 2.6 2.6 15.4 0 18M12 3c-2.6 2.6-2.6 15.4 0 18"/></svg>';
4960
- };
4961
-
4962
5085
  refreshAgentsPane();
4963
5086
 
4964
5087
  els.sendBtn.addEventListener('click', sendCurrent);
@@ -5052,29 +5175,29 @@ async function init() {
5052
5175
  await beast.setTheme(dark ? 'dark' : 'light');
5053
5176
  });
5054
5177
 
5055
- if (els.langBtn) {
5056
- syncLangBtn();
5057
- els.langBtn.addEventListener('click', () => {
5058
- const next = (window.I18N && window.I18N.lang) === 'en' ? 'tr' : 'en';
5059
- window.I18N.setLang(next);
5060
- syncLangBtn();
5061
- });
5062
- document.addEventListener('langchange', syncLangBtn);
5063
- document.addEventListener('langchange', renderActiveSettingsTab);
5064
- /* Whisper STT dili arayüz dilini izler (WA sesli notları da) */
5178
+ /* dil seçimi artık Ayarlar → DİL sekmesinde; STT dili arayüz dilini izler */
5179
+ document.addEventListener('langchange', renderActiveSettingsTab);
5180
+ {
5065
5181
  const syncSttLang = () => { try { beast.sttLangSet((window.I18N && window.I18N.lang) || 'tr'); } catch {} };
5066
5182
  syncSttLang();
5067
5183
  document.addEventListener('langchange', syncSttLang);
5068
5184
  }
5069
5185
 
5070
- els.attachBtn.addEventListener('click', () => els.fileInput.click());
5186
+ /* + butonu iki hedefe hizmet eder: chat composer VE Beast Code paneli */
5187
+ let fileSink = 'chat';
5188
+ els.attachBtn.addEventListener('click', () => { fileSink = 'chat'; els.fileInput.click(); });
5189
+ if (els.bcAttach) {
5190
+ els.bcAttach.addEventListener('click', () => { fileSink = 'bc'; els.fileInput.click(); });
5191
+ }
5071
5192
  els.fileInput.addEventListener('change', () => {
5072
- addFiles([...els.fileInput.files]);
5193
+ addFiles([...els.fileInput.files], fileSink);
5073
5194
  els.fileInput.value = '';
5074
5195
  });
5075
5196
 
5076
- /* ---- Sesli komut (yerel Whisper): kayıt → yazıya çevir → input'a koy ---- */
5077
- if (els.micBtn) {
5197
+ /* ---- Sesli komut (yerel Whisper): kayıt → yazıya çevir → hedef input'a koy ----
5198
+ Aynı düzenek chat composer VE Beast Code girişinde çalışır. */
5199
+ const setupMicButton = (btn, apply) => {
5200
+ if (!btn) return;
5078
5201
  let micRec = null;
5079
5202
  let micChunks = [];
5080
5203
  let micStream = null;
@@ -5084,7 +5207,7 @@ async function init() {
5084
5207
  fr.onload = () => res(fr.result);
5085
5208
  fr.readAsDataURL(b);
5086
5209
  });
5087
- els.micBtn.addEventListener('click', async () => {
5210
+ btn.addEventListener('click', async () => {
5088
5211
  if (micRec && micRec.state === 'recording') {
5089
5212
  micRec.stop();
5090
5213
  return;
@@ -5100,7 +5223,7 @@ async function init() {
5100
5223
  micRec.ondataavailable = (e) => { if (e.data && e.data.size) micChunks.push(e.data); };
5101
5224
  micRec.onstop = async () => {
5102
5225
  try { micStream.getTracks().forEach((t) => t.stop()); } catch {}
5103
- els.micBtn.classList.remove('rec');
5226
+ btn.classList.remove('rec');
5104
5227
  const blob = new Blob(micChunks, { type: micRec.mimeType || 'audio/webm' });
5105
5228
  if (!blob.size) return;
5106
5229
  toast(_t('mic_transcribing'));
@@ -5108,19 +5231,25 @@ async function init() {
5108
5231
  /* arayüz dili = Whisper dili: TR ise Türkçe, EN ise İngilizce algılar */
5109
5232
  const uiLang = (window.I18N && window.I18N.lang) || 'tr';
5110
5233
  const r = await beast.sttTranscribe(b64, uiLang).catch(() => ({ ok: false, error: 'ipc' }));
5111
- if (r && r.ok && r.text) {
5112
- els.input.value = (els.input.value ? els.input.value.replace(/\s+$/, '') + ' ' : '') + r.text;
5113
- els.input.focus();
5114
- els.input.dispatchEvent(new Event('input'));
5115
- } else {
5116
- toast(_t('mic_fail'));
5117
- }
5234
+ if (r && r.ok && r.text) apply(r.text);
5235
+ else toast(_t('mic_fail'));
5118
5236
  };
5119
5237
  micRec.start();
5120
- els.micBtn.classList.add('rec');
5238
+ btn.classList.add('rec');
5121
5239
  toast(_t('mic_listening'));
5122
5240
  });
5123
- }
5241
+ };
5242
+ setupMicButton(els.micBtn, (t) => {
5243
+ els.input.value = (els.input.value ? els.input.value.replace(/\s+$/, '') + ' ' : '') + t;
5244
+ els.input.focus();
5245
+ els.input.dispatchEvent(new Event('input'));
5246
+ });
5247
+ setupMicButton(els.bcMic, (t) => {
5248
+ if (!els.bcInput) return;
5249
+ els.bcInput.value = (els.bcInput.value ? els.bcInput.value.replace(/\s+$/, '') + ' ' : '') + t;
5250
+ els.bcInput.focus();
5251
+ bcInputResize();
5252
+ });
5124
5253
 
5125
5254
  beast.onWaEvent(onWaEvent);
5126
5255
  beast.onTgEvent(onTgEvent);
@@ -5179,6 +5308,13 @@ async function init() {
5179
5308
  els.bbBack.addEventListener('click', () => beast.browserCtrl('back'));
5180
5309
  els.bbFwd.addEventListener('click', () => beast.browserCtrl('forward'));
5181
5310
  els.bbReload.addEventListener('click', () => beast.browserCtrl('reload'));
5311
+ if (els.bbPhone) {
5312
+ els.bbPhone.addEventListener('click', () => {
5313
+ /* web ↔ mobil: aynı sitenin iki versiyonu arasındaki anahtar */
5314
+ const on = !document.body.classList.contains('phone-mode');
5315
+ beast.browserPhone(on).catch(() => {});
5316
+ });
5317
+ }
5182
5318
  els.bbClose.addEventListener('click', () => {
5183
5319
  document.body.classList.remove('browser-open');
5184
5320
  els.browserBar.hidden = true;
@@ -6020,6 +6156,9 @@ function ideModeOn() {
6020
6156
 
6021
6157
  async function setIdeMode(on) {
6022
6158
  document.body.classList.toggle('ide-mode', !!on);
6159
+ /* soldaki marka: chat modunda BEAST Agent, IDE modunda BEAST Code */
6160
+ const brandSub = document.querySelector('#brand .brand-sub');
6161
+ if (brandSub) brandSub.textContent = on ? 'Code' : 'Agent';
6023
6162
  /* IDE modu oturumluk: uygulama HER AÇILIŞTA agent (chat) modunda başlar */
6024
6163
  toggleRail(!!on);
6025
6164
  /* IDE'den çıkış: açık tarayıcı da kapansın — her şey default haline dönsün */
@@ -6287,6 +6426,148 @@ function codeGutterRender() {
6287
6426
  }
6288
6427
  g.innerHTML = html;
6289
6428
  g.scrollTop = ta.scrollTop;
6429
+ codeLayerRender();
6430
+ }
6431
+
6432
+ /* canlı satır boyama: base ile güncel içeriğin diff'ine göre textarea'nın
6433
+ ARKASINDAKİ katmana boya basılır — eklenen satırlar YEŞİL, silinme olan
6434
+ konumlar KIRMIZı çizgili; metin birebir aynı yazıldığı için satırlar
6435
+ birebir üst üste gelir (silinen metnin tamamı Diff görünümünde okunur) */
6436
+ function codeLayerRender() {
6437
+ const layer = els.codeLayer;
6438
+ const ta = els.codeTa;
6439
+ if (!layer || !ta) return;
6440
+ const hasFile = codeActive >= 0 && codeTabs[codeActive];
6441
+ if (!hasFile) {
6442
+ layer.textContent = '';
6443
+ return;
6444
+ }
6445
+ const t = codeTabs[codeActive];
6446
+ const diff = codeDiffLines(t.base, ta.value);
6447
+ const lines = ta.value.split('\n');
6448
+ let html = '';
6449
+ for (let i = 1; i <= lines.length; i++) {
6450
+ const isAdd = diff.added.has(i);
6451
+ const delN = diff.dels.get(i) || 0;
6452
+ const isFind = findLineSet.has(i);
6453
+ const cls = (isAdd ? ' add' : delN ? ' mod' : '') + (isFind ? ' find' : '');
6454
+ html += `<span class="row${cls}">${escapeHtml(lines[i - 1])}</span>`;
6455
+ }
6456
+ layer.innerHTML = html;
6457
+ layer.scrollTop = ta.scrollTop;
6458
+ }
6459
+
6460
+ /* ---------- kod içi arama (Shift+F) ----------
6461
+ VS Code find widget'ı: kutucuk editörün sağ üstünde yüzer; eşleşen satırlar
6462
+ katmanda sarı boyanır, güncel eşleşme textarea seçimiyle işaretlenir. */
6463
+ let findMatches = [];
6464
+ let findIdx = -1;
6465
+ let findTimer = null;
6466
+ const findLineSet = new Set();
6467
+
6468
+ function findOpen() {
6469
+ if (!els.codeFind) return;
6470
+ els.codeFind.hidden = false;
6471
+ els.codeFindInp.focus();
6472
+ els.codeFindInp.select();
6473
+ findCompute();
6474
+ }
6475
+
6476
+ function findClose() {
6477
+ if (!els.codeFind || els.codeFind.hidden) return;
6478
+ els.codeFind.hidden = true;
6479
+ findMatches = [];
6480
+ findIdx = -1;
6481
+ findLineSet.clear();
6482
+ codeGutterRender(); /* katmandaki sarı tint'i temizle */
6483
+ els.codeTa.focus();
6484
+ }
6485
+
6486
+ function findCompute() {
6487
+ const ta = els.codeTa;
6488
+ if (!ta) return;
6489
+ const q = els.codeFindInp.value;
6490
+ findMatches = [];
6491
+ findIdx = -1;
6492
+ findLineSet.clear();
6493
+ if (q) {
6494
+ const hay = ta.value.toLowerCase();
6495
+ const needle = q.toLowerCase();
6496
+ let i = hay.indexOf(needle);
6497
+ while (i !== -1 && findMatches.length < 2000) {
6498
+ findMatches.push({ s: i, e: i + needle.length });
6499
+ i = hay.indexOf(needle, i + Math.max(1, needle.length));
6500
+ }
6501
+ for (const m of findMatches) {
6502
+ findLineSet.add(ta.value.slice(0, m.s).split('\n').length);
6503
+ }
6504
+ }
6505
+ codeGutterRender(); /* satır tintlerini yeniden boya */
6506
+ if (findMatches.length) findGoto(0, { focus: false });
6507
+ else findCount();
6508
+ }
6509
+
6510
+ function findCount() {
6511
+ if (els.codeFindCount) {
6512
+ els.codeFindCount.textContent = findMatches.length
6513
+ ? (findIdx + 1) + '/' + findMatches.length
6514
+ : '0/0';
6515
+ }
6516
+ }
6517
+
6518
+ function findGoto(i, opts = {}) {
6519
+ if (!findMatches.length) return;
6520
+ findIdx = ((i % findMatches.length) + findMatches.length) % findMatches.length;
6521
+ const m = findMatches[findIdx];
6522
+ const ta = els.codeTa;
6523
+ /* YAZARKEN ODAK ÇALINMAZ: arama kutusuna yazıyorsa focus editöre geçmez —
6524
+ sadece seçim + kaydırma güncellenir (yoksa harfler koda düşer) */
6525
+ const keepBox = opts.focus === false || document.activeElement === els.codeFindInp;
6526
+ ta.setSelectionRange(m.s, m.e);
6527
+ if (!keepBox) ta.focus();
6528
+ const line = ta.value.slice(0, m.s).split('\n').length - 1;
6529
+ ta.scrollTop = Math.max(0, line * 20 - ta.clientHeight / 3);
6530
+ if (els.codeLayer) els.codeLayer.scrollTop = ta.scrollTop;
6531
+ findCount();
6532
+ }
6533
+
6534
+ function findNext() { findGoto(findIdx + 1); }
6535
+ function findPrev() { findGoto(findIdx - 1); }
6536
+
6537
+ /* okunur diff görünümü: silinen satırlar KIRMIZI, eklenenler YEŞİL —
6538
+ ajan dosyayı değiştirdiğinde OTOMATİK açılır; Diff butonuyla elle de açılır */
6539
+ function codeDiffHide() {
6540
+ if (els.codeDiffView) els.codeDiffView.hidden = true;
6541
+ }
6542
+
6543
+ function codeDiffShow() {
6544
+ const t = codeActive >= 0 ? codeTabs[codeActive] : null;
6545
+ if (!t || !els.codeDiffView) return;
6546
+ findClose(); /* diff görünümü editörü kapatır — arama widget'ı da kapanır */
6547
+ const ops = diffOps(t.base, els.codeTa.value);
6548
+ let adds = 0;
6549
+ let dels = 0;
6550
+ let o = 0;
6551
+ let n = 0;
6552
+ let html = '';
6553
+ for (const op of ops) {
6554
+ if (op.t === 'ctx') {
6555
+ o++; n++;
6556
+ html += `<div class="dr ctx"><span class="sign"> </span><span class="num">${o}</span><span class="tx">${escapeHtml(op.a || '')}</span></div>`;
6557
+ } else if (op.t === 'del') {
6558
+ o++; dels++;
6559
+ html += `<div class="dr del"><span class="sign">−</span><span class="num">${o}</span><span class="tx">${escapeHtml(op.a || '')}</span></div>`;
6560
+ } else {
6561
+ n++; adds++;
6562
+ html += `<div class="dr add"><span class="sign">+</span><span class="num">${n}</span><span class="tx">${escapeHtml(op.b || '')}</span></div>`;
6563
+ }
6564
+ }
6565
+ els.codeDiffRows.innerHTML =
6566
+ html || '<div class="dr ctx"><span class="tx"> (de\u011Fi\u015Fiklik yok)</span></div>';
6567
+ els.codeDiffTitle.textContent = t.rel;
6568
+ els.codeDiffStats.innerHTML =
6569
+ `<span class="plus">+${adds}</span> <span class="minus">\u2212${dels}</span>`;
6570
+ els.codeDiffView.hidden = false;
6290
6571
  }
6291
6572
 
6292
6573
  let codeGutterTimer = null;
@@ -6320,6 +6601,8 @@ function codeFlushActive() {
6320
6601
 
6321
6602
  function codeActivate(i) {
6322
6603
  codeFlushActive();
6604
+ codeDiffHide(); /* sekme değişince diff incelemesi kapanır */
6605
+ findClose(); /* arama eşleşmeleri sekmeye özeldir — yeni sekmede temiz başla */
6323
6606
  if (i < 0 || i >= codeTabs.length) {
6324
6607
  codeActive = -1;
6325
6608
  els.codeTa.value = '';
@@ -6356,6 +6639,16 @@ async function codeOpen(rel) {
6356
6639
 
6357
6640
  function codeClose(i) {
6358
6641
  if (i < 0 || i >= codeTabs.length) return;
6642
+ /* KAYIT DİSİPLİNİ: düzenlemeler yalnız Kaydet/Ctrl+S ile diske yazılır.
6643
+ Kaydedilmemiş sekme kapatılırken kullanıcıya sorulur — sessiz kayıp yok. */
6644
+ if (codeTabs[i].dirty) {
6645
+ const ok = confirm(
6646
+ '"' + codeTabs[i].rel + '" kaydedilmedi.\n\n' +
6647
+ 'Tamam = kaydetmeden kapat\n' +
6648
+ 'İptal = editörde kal (kaydetmek için Ctrl+S)'
6649
+ );
6650
+ if (!ok) return;
6651
+ }
6359
6652
  codeFlushActive();
6360
6653
  const oldActive = codeActive;
6361
6654
  const wasActive = i === oldActive;
@@ -6384,6 +6677,7 @@ async function codeSave() {
6384
6677
  t.base = t.content; /* kaydedildi → diff sıfırlanır (VS Code davranışı) */
6385
6678
  codeRenderTabs();
6386
6679
  codeGutterRender();
6680
+ codeDiffHide(); /* kaydedildi — incelenecek diff kalmadı */
6387
6681
  toast(_t('ide_saved'));
6388
6682
  } else {
6389
6683
  toast((r && r.error) || 'kaydedilemedi');
@@ -6415,6 +6709,8 @@ async function codeReloadIfOpen(p) {
6415
6709
  t.content = r.content || ''; /* ajanın yazdığı yeni içerik */
6416
6710
  if (i === codeActive) {
6417
6711
  els.codeTa.value = t.content;
6712
+ /* ajan aktif dosyayı değiştirdi → chat diffView gibi KIRMIZI/YEŞİL inceleme otomatik açılır */
6713
+ if (!editorHiddenOn()) codeDiffShow();
6418
6714
  }
6419
6715
  codeGutterRender();
6420
6716
  }
@@ -6430,6 +6726,74 @@ $('#filePick').addEventListener('click', async () => {
6430
6726
  }
6431
6727
  });
6432
6728
  $('#fileRefresh').addEventListener('click', () => loadIdeTree());
6729
+
6730
+ /* ---------- görev listesi + TEK TUŞ GERİ ALMA (dosya paneli) ---------- */
6731
+ let tasksPanelOpen = false;
6732
+
6733
+ function toggleTasksPanel(force) {
6734
+ tasksPanelOpen = typeof force === 'boolean' ? force : !tasksPanelOpen;
6735
+ if (els.tasksPanel) els.tasksPanel.hidden = !tasksPanelOpen;
6736
+ if (els.fileTasks) els.fileTasks.classList.toggle('on', tasksPanelOpen);
6737
+ if (tasksPanelOpen) renderTasksPanel();
6738
+ }
6739
+
6740
+ async function renderTasksPanel() {
6741
+ const box = els.tasksPanel;
6742
+ if (!box) return;
6743
+ if (!bcSessionId) {
6744
+ box.innerHTML = '<div class="tsk-empty">\u00D6nce Beast Code\u2019da bir mesaj yaz — g\u00F6rev listesi i\u00E7in oturum gerekli.</div>';
6745
+ return;
6746
+ }
6747
+ const r = await beast.bcTodos(bcSessionId).catch(() => null);
6748
+ if (!r || !r.ok) {
6749
+ box.innerHTML = '<div class="tsk-empty">' + escapeHtml((r && r.error) || 'okunamad\u0131') + '</div>';
6750
+ return;
6751
+ }
6752
+ const GLYPH = { pending: '\u25CB', active: '\u25D0', done: '\u25CF' };
6753
+ const undoMap = new Map((r.undo || []).map((u) => [u.id, u.files]));
6754
+ let html =
6755
+ '<div class="tsk-head"><span>G\u00D6REVLER</span>' +
6756
+ '<button id="tskUndoLast" ' + (r.lastTodoId ? '' : 'disabled') + ' title="Kay\u0131tl\u0131 son g\u00F6revi geri al">\u21B6 Son G\u00F6revi Geri Al</button>' +
6757
+ '<button id="tskClose" title="Kapat">\u00D7</button></div>';
6758
+ if (!(r.todos || []).length) {
6759
+ html += '<div class="tsk-empty">g\u00F6rev listesi yok</div>';
6760
+ }
6761
+ for (const t of r.todos || []) {
6762
+ const files = undoMap.get(t.id) || 0;
6763
+ html +=
6764
+ '<div class="tsk-row">' +
6765
+ '<span class="tsk-id">' + escapeHtml(t.id || '?') + '</span>' +
6766
+ '<span class="tsk-title" title="' + escapeHtml(t.title) + '">' + escapeHtml(t.title) + '</span>' +
6767
+ '<span class="tsk-st ' + escapeHtml(t.status || 'pending') + '">' + (GLYPH[t.status] || '\u25CB') + '</span>' +
6768
+ '<button class="tsk-undo" data-id="' + escapeHtml(t.id || '') + '" ' + (files ? '' : 'disabled') +
6769
+ ' title="' + files + ' dosya \u00F6nceki haline d\u00F6ner">\u21B6</button></div>';
6770
+ }
6771
+ box.innerHTML = html;
6772
+ const close = box.querySelector('#tskClose');
6773
+ if (close) close.addEventListener('click', () => toggleTasksPanel(false));
6774
+ const ul = box.querySelector('#tskUndoLast');
6775
+ if (ul) ul.addEventListener('click', () => undoTask('last'));
6776
+ box.querySelectorAll('.tsk-undo[data-id]').forEach((b) => {
6777
+ b.addEventListener('click', () => undoTask(b.dataset.id));
6778
+ });
6779
+ }
6780
+
6781
+ async function undoTask(id) {
6782
+ if (!bcSessionId) return;
6783
+ if (busy) { toast('Ajan \u00E7al\u0131\u015F\u0131yor — \u25A0 ile durdurup \u00F6yle geri al'); return; }
6784
+ const r = await beast.bcUndo(bcSessionId, id).catch(() => null);
6785
+ if (r && r.ok) {
6786
+ toast('\u21B6 Geri al\u0131nd\u0131 — ' + (r.reverted || 0) + ' dosya \u00F6nceki h\u00E2line d\u00F6nd\u00FC');
6787
+ ideRefreshTree();
6788
+ for (const p of r.paths || []) codeReloadIfOpen(p);
6789
+ renderTasksPanel();
6790
+ } else {
6791
+ toast((r && r.error) || 'geri al\u0131namad\u0131');
6792
+ }
6793
+ }
6794
+
6795
+ if (els.fileTasks) els.fileTasks.addEventListener('click', () => toggleTasksPanel());
6796
+
6433
6797
  $('#filePreview').addEventListener('click', async () => {
6434
6798
  const r = await beast.idePreview().catch(() => null);
6435
6799
  if (r && r.ok) {
@@ -6439,6 +6803,24 @@ $('#filePreview').addEventListener('click', async () => {
6439
6803
  }
6440
6804
  });
6441
6805
  if (els.codeSave) els.codeSave.addEventListener('click', codeSave);
6806
+ if (els.codeDiffBtn) {
6807
+ els.codeDiffBtn.addEventListener('click', () => {
6808
+ if (els.codeDiffView && !els.codeDiffView.hidden) {
6809
+ codeDiffHide();
6810
+ return;
6811
+ }
6812
+ if (codeActive < 0 || !codeTabs[codeActive]) {
6813
+ toast('Dosya a\u00E7\u0131k de\u011Fil');
6814
+ return;
6815
+ }
6816
+ if (els.codeTa.value === codeTabs[codeActive].base) {
6817
+ toast('De\u011Fi\u015Fiklik yok');
6818
+ return;
6819
+ }
6820
+ codeDiffShow();
6821
+ });
6822
+ }
6823
+ if (els.codeDiffClose) els.codeDiffClose.addEventListener('click', codeDiffHide);
6442
6824
  if (els.codeTa) {
6443
6825
  els.codeTa.addEventListener('input', () => {
6444
6826
  if (codeActive < 0) return;
@@ -6448,16 +6830,44 @@ if (els.codeTa) {
6448
6830
  /* gutter diff'i hafif gecikmeli — her tuşta LCS koşmasın */
6449
6831
  clearTimeout(codeGutterTimer);
6450
6832
  codeGutterTimer = setTimeout(codeGutterRender, 120);
6833
+ /* arama açıkken içerik değişti → eşleşmeler tazelensin */
6834
+ if (els.codeFind && !els.codeFind.hidden) {
6835
+ clearTimeout(findTimer);
6836
+ findTimer = setTimeout(findCompute, 220);
6837
+ }
6451
6838
  });
6452
6839
  els.codeTa.addEventListener('scroll', () => {
6453
6840
  if (els.codeGutter) els.codeGutter.scrollTop = els.codeTa.scrollTop;
6841
+ if (els.codeLayer) els.codeLayer.scrollTop = els.codeTa.scrollTop;
6454
6842
  });
6455
6843
  els.codeTa.addEventListener('keydown', (e) => {
6456
6844
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 's') {
6457
6845
  e.preventDefault();
6458
6846
  codeSave();
6847
+ return;
6848
+ }
6849
+ /* Shift+F: kod içinde arama (VS Code tarzı find widget'ı) */
6850
+ if (e.shiftKey && !e.ctrlKey && !e.altKey && (e.key === 'F' || e.key === 'f')) {
6851
+ e.preventDefault();
6852
+ findOpen();
6459
6853
  }
6460
6854
  });
6855
+ /* arama widget'ı bağlantıları */
6856
+ if (els.codeFindInp) {
6857
+ els.codeFindInp.addEventListener('keydown', (e) => {
6858
+ /* Enter/Shift+Enter: eşleşmeye GEÇ ve odağı editöre ver (yazma bitti) */
6859
+ if (e.key === 'Enter' && e.shiftKey) { e.preventDefault(); findGoto(findIdx - 1, { focus: true }); }
6860
+ else if (e.key === 'Enter') { e.preventDefault(); findGoto(findIdx + 1, { focus: true }); }
6861
+ else if (e.key === 'Escape') { e.preventDefault(); findClose(); }
6862
+ });
6863
+ els.codeFindInp.addEventListener('input', () => {
6864
+ clearTimeout(findTimer);
6865
+ findTimer = setTimeout(findCompute, 200);
6866
+ });
6867
+ }
6868
+ if (els.codeFindNext) els.codeFindNext.addEventListener('click', findNext);
6869
+ if (els.codeFindPrev) els.codeFindPrev.addEventListener('click', findPrev);
6870
+ if (els.codeFindClose) els.codeFindClose.addEventListener('click', findClose);
6461
6871
  }
6462
6872
 
6463
6873
  /* ---------- dosya ağacı sağ tık menüsü (Sil / Aç / Preview) ---------- */
@@ -6948,16 +7358,19 @@ function bcInputResize() {
6948
7358
 
6949
7359
  function bcRunCurrent() {
6950
7360
  const msg = els.bcInput.value.trim();
6951
- if (!msg) return;
7361
+ if (!msg && !bcPending.length) return;
7362
+ const atts = bcPending.slice();
7363
+ bcPending = [];
7364
+ renderBcChips();
6952
7365
  els.bcInput.value = '';
6953
7366
  bcInputResize();
6954
7367
  els.bcInput.focus();
6955
- bcLine('t-cmd', 'code> ' + msg);
7368
+ bcLine('t-cmd', 'code> ' + (msg || (atts.length ? '[' + atts.length + ' ek]' : '')));
6956
7369
  bcHideTodos(); /* yeni sorgu = eski todo list temizlenir; agent yeniden basacak */
6957
7370
  bcSetBusy(true);
6958
7371
  /* sürüyor/serbest kararını main'deki gerçek engine.isBusy verir —
6959
7372
  panel bayat kilitli kaldıysa ilk mesajla kendini düzeltir */
6960
- beast.beastcodeSend(msg).then((r) => {
7373
+ beast.beastcodeSend(msg || '[dosya ekleri]', atts).then((r) => {
6961
7374
  if (r && r.ok && r.mode) {
6962
7375
  /* /plan · /build · /auto: mod değişimi — tur AÇILMAZ, kilit anında açılır
6963
7376
  (bilgi satırı bc-mode olayıyla düşer, başlık rozeti güncellenir) */
@@ -6993,6 +7406,8 @@ if (els.bcInput) {
6993
7406
  });
6994
7407
  }
6995
7408
  if (els.bcStop) els.bcStop.addEventListener('click', () => {
7409
+ bcPending = [];
7410
+ renderBcChips();
6996
7411
  beast.beastcodeStop().then((r) => {
6997
7412
  /* kuyrukta bekleyen mesajlar da silindi; engine boştaysa kilidi aç */
6998
7413
  if (r && r.ok && r.wasBusy === false) {
@@ -7013,6 +7428,8 @@ if (els.bcNew) els.bcNew.addEventListener('click', async () => {
7013
7428
  const r = await beast.beastcodeNew().catch(() => null);
7014
7429
  if (r && r.ok) {
7015
7430
  bcSessionId = null;
7431
+ bcPending = [];
7432
+ renderBcChips();
7016
7433
  bcTools.clear();
7017
7434
  bcCloseToolGroup();
7018
7435
  bcHideTodos();