beast-agent 2.40.0 → 2.42.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/src/preload.js CHANGED
@@ -185,8 +185,14 @@ contextBridge.exposeInMainWorld('beast', {
185
185
  sandboxOpen: (payload) => ipcRenderer.invoke('sandbox:open', payload),
186
186
  sandboxTree: (payload) => ipcRenderer.invoke('sandbox:tree', payload),
187
187
  sandboxRun: (payload) => ipcRenderer.invoke('sandbox:run', payload),
188
- sandboxRunStop: () => ipcRenderer.invoke('sandbox:runstop'),
188
+ sandboxRunStop: (payload) => ipcRenderer.invoke('sandbox:runstop', payload),
189
+ sandboxDetect: (payload) => ipcRenderer.invoke('sandbox:detect', payload),
190
+ sandboxInstall: (payload) => ipcRenderer.invoke('sandbox:install', payload),
191
+ sandboxStart: (payload) => ipcRenderer.invoke('sandbox:start', payload),
192
+ sandboxProcState: (payload) => ipcRenderer.invoke('sandbox:procstate', payload),
193
+ sandboxCfgSet: (payload) => ipcRenderer.invoke('sandbox:cfg:set', payload),
189
194
  sandboxOpenUrl: (url) => ipcRenderer.invoke('sandbox:openurl', url),
195
+ sbPreviewSet: (payload) => ipcRenderer.invoke('sbpreview:set', payload),
190
196
  waStart: () => ipcRenderer.invoke('wa:start'),
191
197
  waStop: () => ipcRenderer.invoke('wa:stop'),
192
198
  waReset: () => ipcRenderer.invoke('wa:reset'),
@@ -419,28 +419,35 @@
419
419
  <div id="sbStatus" hidden></div>
420
420
  <div id="sbInputRow">
421
421
  <textarea id="sbInput" rows="1" spellcheck="false" autocomplete="off" placeholder="Sandbox mesajı — ajan bu repo klasöründe çalışır…"></textarea>
422
+ <button id="sbSend" title="Gönder (Enter)"><svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg></button>
422
423
  <button id="sbStop" title="Çalışan mesajı durdur" hidden data-i18n-title="bc_stop"><svg viewBox="0 0 24 24" width="13" height="13" fill="currentColor" stroke="none"><rect x="6" y="6" width="12" height="12" rx="2"/></svg></button>
423
424
  </div>
424
425
  </section>
425
426
 
427
+ <div id="sbSplit" title="Sürükleyerek boyutlandır"></div>
428
+
426
429
  <section id="sbRun">
427
430
  <div id="sbRunHead">
428
431
  <span id="sbRunTitle">ÇALIŞTIR</span>
432
+ <span id="sbRunKind"></span>
429
433
  <span id="sbRunCwd" title="">—</span>
434
+ <span class="file-spacer"></span>
435
+ <button id="sbRunView" class="on" title="Görsel önizleme açık — çıktıya dön">Görsel</button>
430
436
  </div>
431
437
  <div id="sbRunBtns">
432
- <button id="sbRunInstall" title="Ajana talimat: projeyi kur — doğru komutu ajan bulur"><svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:3px"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>Kur</button>
433
- <button id="sbRunStart" title="Ajana talimat: projeyi çalıştır — uzun süreli süreç ÇALIŞTIR panelinde başlar"><svg viewBox="0 0 24 24" width="12" height="12" fill="currentColor" stroke="none" style="vertical-align:-2px;margin-right:3px"><polygon points="6 3 20 12 6 21 6 3"/></svg>Başlat</button>
434
- <button id="sbRunDev" title="Ajana talimat: geliştirme modunda başlat (dev)"><svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:3px"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>Dev</button>
438
+ <button id="sbRunStart" title="Başlat — gerekirse bağımlılıkları kurar, sonra çalıştırır (adres panelde canlı önizlemede açılır)"><svg viewBox="0 0 24 24" width="12" height="12" fill="currentColor" stroke="none" style="vertical-align:-2px;margin-right:3px"><polygon points="6 3 20 12 6 21 6 3"/></svg>Başlat</button>
439
+ <button id="sbRunDev" title="Geliştirme modunda başlat (gerekirse önce kurar)"><svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:3px"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>Dev</button>
435
440
  <button id="sbRunStop" title="Süreci durdur" hidden><svg viewBox="0 0 24 24" width="12" height="12" fill="currentColor" stroke="none" style="vertical-align:-2px;margin-right:3px"><rect x="6" y="6" width="12" height="12" rx="2"/></svg>Durdur</button>
436
441
  </div>
437
442
  <div id="sbRunCustom">
438
443
  <input id="sbRunCmd" placeholder="özel komut: python main.py, cargo run, node index.js…" spellcheck="false" autocomplete="off" />
439
444
  <button id="sbRunGo" title="Çalıştır"><svg viewBox="0 0 24 24" width="12" height="12" fill="currentColor" stroke="none"><polygon points="6 3 20 12 6 21 6 3"/></svg></button>
445
+ <button id="sbRunSave" title="Yazılan komutu bu reponun başlatma komutu olarak kaydet"><svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg></button>
440
446
  </div>
447
+ <div id="sbPreviewHost" hidden><div id="sbPreviewHint">🌐 canlı önizleme — Başlat/Dev ile sunucu açılınca burada görünür</div></div>
441
448
  <div id="sbRunOut"></div>
442
449
  <div id="sbRunFoot">
443
- <button id="sbRunOpen" title="Ajana talimat: çalışma adresini bulup tarayıcıda açar" disabled><svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:3px"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg><span id="sbRunOpenLabel">Tarayıcıda aç</span></button>
450
+ <button id="sbRunOpen" title="Önizlemeyi sağdaki dahili tarayıcıda aç" disabled><svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:3px"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg><span id="sbRunOpenLabel">Tarayıcıda aç</span></button>
444
451
  <span id="sbRunUrl" title=""></span>
445
452
  </div>
446
453
  </section>
@@ -135,13 +135,18 @@ const els = {
135
135
  sbStatus: $('#sbStatus'),
136
136
  sbInput: $('#sbInput'),
137
137
  sbStop: $('#sbStop'),
138
+ sbSend: $('#sbSend'),
139
+ sbSplit: $('#sbSplit'),
140
+ sbRun: $('#sbRun'),
138
141
  sbRunCwd: $('#sbRunCwd'),
139
- sbRunInstall: $('#sbRunInstall'),
142
+ sbRunView: $('#sbRunView'),
143
+ sbRunKind: $('#sbRunKind'),
140
144
  sbRunStart: $('#sbRunStart'),
141
145
  sbRunDev: $('#sbRunDev'),
142
146
  sbRunStop: $('#sbRunStop'),
143
147
  sbRunCmd: $('#sbRunCmd'),
144
148
  sbRunGo: $('#sbRunGo'),
149
+ sbRunSave: $('#sbRunSave'),
145
150
  sbRunOut: $('#sbRunOut'),
146
151
  sbRunOpen: $('#sbRunOpen'),
147
152
  sbRunUrl: $('#sbRunUrl'),
@@ -5998,6 +6003,7 @@ function onEvent(ev) {
5998
6003
  /* IDE modunda tarayıcı açılır/kapanır/genişlik değişirse row yeniden bölünür —
5999
6004
  editör + Beast Code ORTAK kırpılıp ORTAK açılır (kullanıcı payı korunur) */
6000
6005
  if (ideModeOn()) ideSplitApplyFrac();
6006
+ if (sbModeOn()) sbSplitApplyFrac();
6001
6007
  if (shown) {
6002
6008
  els.browserBtn.classList.add('on');
6003
6009
  if (ev.url && document.activeElement !== els.bbUrl) {
@@ -11964,6 +11970,11 @@ let sbCloning = false;
11964
11970
  const sbTreeState = { cache: new Map(), open: new Set(['']), path: '' };
11965
11971
  let sbRunUrl = '';
11966
11972
  let sbRunRunning = false;
11973
+ let sbRunInfo = null;
11974
+ let sbAutoAssist = null; /* butonla başlatılan süreç hızlı çökerse: 'install' | 'run' */
11975
+ let sbPreviewOn = true; /* ÇALIŞTIR içi görsel önizleme tercihi */
11976
+ let sbPreviewShown = false;
11977
+ let sbPreviewSuspend = false; /* ayırıcı sürüklenirken native view gizli tutulur */
11967
11978
 
11968
11979
  function sbModeOn() {
11969
11980
  return document.body.classList.contains('sandbox-mode');
@@ -12095,7 +12106,7 @@ async function loadSandboxList() {
12095
12106
  for (const it of sbItems) {
12096
12107
  const o = document.createElement('option');
12097
12108
  o.value = it.folder;
12098
- o.textContent = it.name + (it.busy ? ' ●' : '');
12109
+ o.textContent = it.name + (it.busy ? ' ●' : '') + (it.running ? ' ▶' : '');
12099
12110
  sel.appendChild(o);
12100
12111
  }
12101
12112
  if (sbFolder && sbItems.some((x) => x.folder === sbFolder)) sel.value = sbFolder;
@@ -12140,6 +12151,32 @@ async function sbSelectCurrent() {
12140
12151
  sbLine('t-err', (r && r.error) || 'oturum açılamadı');
12141
12152
  }
12142
12153
  sbSetBusy(!!(r && r.busy));
12154
+ /* yönetilen süreç durumu: repo değişince arka plandaki süreç + tampon çıktı geri yüklenir */
12155
+ sbRunInfo = null;
12156
+ const st = await beast.sandboxProcState({ folder: sbFolder }).catch(() => null);
12157
+ if (st && st.ok) {
12158
+ sbRunUrl = st.url || '';
12159
+ sbRunRunning = !!st.running;
12160
+ if (els.sbRunStop) els.sbRunStop.hidden = !sbRunRunning;
12161
+ if (els.sbRunOpen) els.sbRunOpen.disabled = !sbRunUrl;
12162
+ if (els.sbRunUrl) els.sbRunUrl.textContent = sbRunUrl || '';
12163
+ for (const l of st.lines || []) sbRunLine(l);
12164
+ }
12165
+ /* proje tipi algıla: butonlar hangi komutu çalıştıracağını bilir */
12166
+ const det = await beast.sandboxDetect({ folder: sbFolder }).catch(() => null);
12167
+ if (det && det.ok && det.info) {
12168
+ sbRunInfo = det.info;
12169
+ if (els.sbRunKind) els.sbRunKind.textContent = det.info.label || '';
12170
+ if (els.sbRunCmd) {
12171
+ els.sbRunCmd.placeholder = det.info.run
12172
+ ? 'özel komut (algılanan: ' + det.info.run + ')…'
12173
+ : 'özel komut: python main.py, cargo run, node index.js…';
12174
+ }
12175
+ if (!sbRunRunning && !(st && (st.lines || []).length)) {
12176
+ sbRunLine('ℹ ' + (det.info.label || 'proje') + ' · kur: ' + (det.info.install || '—') + ' · çalıştır: ' + (det.info.run || '—'), 't-dim');
12177
+ }
12178
+ }
12179
+ sbPreviewApply();
12143
12180
  /* sol konsol: seçilen reponun klasörü — oturum açıldıktan sonra da garanti tazele */
12144
12181
  sbTreeState.cache.clear();
12145
12182
  if (sbModeOn()) renderFileTree().catch(() => {});
@@ -12155,7 +12192,8 @@ function sbInputResize() {
12155
12192
 
12156
12193
  function sbRunCurrent() {
12157
12194
  const msg = els.sbInput.value.trim();
12158
- if (!msg || !sbFolder || sbCloning) return;
12195
+ if (!msg || sbCloning) return;
12196
+ if (!sbFolder) { toast('Önce repo indir ya da seç'); return; }
12159
12197
  els.sbInput.value = '';
12160
12198
  sbInputResize();
12161
12199
  sbLine('t-cmd', 'sen> ' + msg);
@@ -12226,6 +12264,7 @@ function setSandboxMode(on) {
12226
12264
  if (on && financeModeOn()) { setFinanceMode(false); }
12227
12265
  document.body.classList.toggle('sandbox-mode', !!on);
12228
12266
  if (els.sbBtn) els.sbBtn.classList.toggle('on', !!on);
12267
+ sbPreviewApply();
12229
12268
  /* marka + sol konsol başlığı: moduna göre BEAST Agent/Code/Studio/Sandbox */
12230
12269
  const brandSub = document.querySelector('#brand .brand-sub');
12231
12270
  if (brandSub) brandSub.textContent = on ? 'Sandbox' : 'Agent';
@@ -12233,6 +12272,7 @@ function setSandboxMode(on) {
12233
12272
  if (ftp && on) ftp.textContent = 'REPO DOSYALARI';
12234
12273
  if (on) {
12235
12274
  loadSandboxList();
12275
+ sbSplitRestore();
12236
12276
  sbTreeState.cache.clear();
12237
12277
  renderFileTree().catch(() => {});
12238
12278
  } else if (!ideModeOn() && !studioModeOn()) {
@@ -12242,26 +12282,27 @@ function setSandboxMode(on) {
12242
12282
  }
12243
12283
  }
12244
12284
 
12245
- /* ---------- ÇALIŞTIRICI: butonlar KOMUT DEĞİL AJANA TALİMATTIR ----------
12246
- Her proje farklı çalışır; komutu kullanıcı bilemez, AJAN bilir:
12247
- paket bildirim dosyasına + README'ye bakıp doğru komutu bulur, uzun süreli
12248
- süreci panel_run aracıyla ÇALIŞTIR panelinde (turu kilitlemeden) başlatır. */
12249
- function sbInstruct(text) {
12285
+ /* ---------- ÇALIŞTIRICI v2: butonlar DETERMİNİSTİK ----------
12286
+ Proje tipi algılanır (package.json/requirements.txt/pyproject/Cargo.toml/
12287
+ go.mod/hugo/jekyll/mkdocs/index.html); Başlat/Dev gerekirse bağımlılıkları
12288
+ ÖNCE kurar (npm install && npm run dev gibi), süreç panelde canlı akar. */
12289
+ async function sbRunLaunch(mode) {
12250
12290
  if (!sbFolder) { toast('Önce repo indir ya da seç'); return; }
12251
- sbLine('t-cmd', 'sen> ' + text);
12252
- sbSetBusy(true);
12253
- beast.sandboxSend({ folder: sbFolder, msg: text }).then((r) => {
12254
- if (r && r.ok) {
12255
- if (r.sessionId) sbSessionId = r.sessionId;
12256
- if (r.queued) sbLine('t-dim', '⏳ kuyrukta (' + r.count + ') — iş bitince gönderilir');
12257
- } else {
12258
- sbSetBusy(false);
12259
- sbLine('t-err', (r && r.error) || 'gönderilemedi');
12260
- }
12261
- }).catch((e) => {
12262
- sbSetBusy(false);
12263
- sbLine('t-err', String((e && e.message) || e));
12264
- });
12291
+ if (sbRunRunning) { toast('Bu repoda süreç çalışıyor — önce ■ Durdur'); return; }
12292
+ sbAutoAssist = mode;
12293
+ const r = await beast.sandboxStart({ folder: sbFolder, mode: mode === 'dev' ? 'dev' : 'run' })
12294
+ .catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
12295
+ if (!r || !r.ok) {
12296
+ sbAutoAssist = null;
12297
+ const err = (r && r.error) || 'başlatılamadı';
12298
+ sbRunLine('[hata] ' + err, 't-err');
12299
+ /* algılama komut bulamadıysa ajan devralır: repo yapısından doğru komutu bulur */
12300
+ if (/algılanamadı/.test(err)) sbAskAgent(mode);
12301
+ return;
12302
+ }
12303
+ sbRunRunning = true;
12304
+ if (els.sbRunStop) els.sbRunStop.hidden = false;
12305
+ sbBadge(sbFolder, true);
12265
12306
  }
12266
12307
 
12267
12308
  function sbRunLine(text, cls) {
@@ -12274,8 +12315,28 @@ function sbRunLine(text, cls) {
12274
12315
  return div;
12275
12316
  }
12276
12317
 
12277
- /* süreç çıktısı akışı: sessionId'siz global olaylar — en üstte yönlendirilir */
12318
+ /* repo listesindeki rozet: ▶ = bu repoda süreç çalışıyor */
12319
+ function sbBadge(folder, on) {
12320
+ if (!els.sbRepo || !folder) return;
12321
+ for (const o of Array.from(els.sbRepo.options)) {
12322
+ if (o.value !== folder) continue;
12323
+ const it = sbItems.find((x) => x.folder === folder);
12324
+ const name = it ? it.name : o.textContent.replace(/ (?:●|▶)+$/g, '');
12325
+ o.textContent = name + (it && it.busy ? ' ●' : '') + (on ? ' ▶' : '');
12326
+ return;
12327
+ }
12328
+ }
12329
+
12330
+ /* süreç çıktısı akışı: klasör etiketli olaylar — seçili repo paneli süzer,
12331
+ arka plandaki repolar yalnızca rozetlerini günceller */
12278
12332
  function sbRunIngest(ev) {
12333
+ const folder = String(ev.folder || '');
12334
+ const mine = !folder || !sbFolder || folder === sbFolder;
12335
+ if (!mine) {
12336
+ if (ev.type === 'sb-run-url') sbBadge(folder, true);
12337
+ if (ev.type === 'sb-run-end') sbBadge(folder, false);
12338
+ return;
12339
+ }
12279
12340
  if (ev.type === 'sb-run') {
12280
12341
  sbRunLine(ev.data);
12281
12342
  return;
@@ -12285,26 +12346,106 @@ function sbRunIngest(ev) {
12285
12346
  sbRunLine('🌐 dev server: ' + sbRunUrl, 't-dim');
12286
12347
  if (els.sbRunOpen) els.sbRunOpen.disabled = !sbRunUrl;
12287
12348
  if (els.sbRunUrl) els.sbRunUrl.textContent = sbRunUrl;
12349
+ sbBadge(folder || sbFolder, true);
12350
+ /* ÇALIŞTIR panelinde canlı önizleme otomatik açılır */
12351
+ sbPreviewApply();
12288
12352
  return;
12289
12353
  }
12290
12354
  if (ev.type === 'sb-run-end') {
12291
12355
  sbRunRunning = false;
12292
12356
  if (els.sbRunStop) els.sbRunStop.hidden = true;
12357
+ sbBadge(folder || sbFolder, false);
12358
+ const assist = sbAutoAssist;
12359
+ sbAutoAssist = null;
12360
+ if (ev.quickFail && assist) {
12361
+ const kind = ev.kind === 'install' ? 'install' : 'run';
12362
+ sbRunLine('[hata] komut başarısız bitti — ajan doğru komutu arıyor…', 't-err');
12363
+ sbAskAgent(kind);
12364
+ }
12365
+ }
12366
+ }
12367
+
12368
+ /* ---------- ÇALIŞTIR içi CANLI ÖNİZLEME: DOM dikdörtgenini main'e bildir ---------- */
12369
+ function sbPreviewLayout() {
12370
+ if (!sbPreviewShown || sbPreviewSuspend) return;
12371
+ const host = document.getElementById('sbPreviewHost');
12372
+ if (!host) return;
12373
+ const r = host.getBoundingClientRect();
12374
+ if (!r.width || !r.height) return;
12375
+ beast.sbPreviewSet({
12376
+ url: sbRunUrl,
12377
+ visible: true,
12378
+ bounds: { x: r.left, y: r.top, width: r.width, height: r.height },
12379
+ }).catch(() => {});
12380
+ }
12381
+
12382
+ function sbPreviewApply() {
12383
+ const show = sbPreviewOn && sbModeOn() && !!sbFolder && !!sbRunUrl && !sbPreviewSuspend;
12384
+ sbPreviewShown = show;
12385
+ const host = document.getElementById('sbPreviewHost');
12386
+ if (host) host.hidden = !show;
12387
+ if (els.sbRunOut) els.sbRunOut.hidden = show;
12388
+ if (show) {
12389
+ sbPreviewLayout();
12390
+ /* görünür olunca yerleşim oturana kadar birkaç kez tazele */
12391
+ setTimeout(sbPreviewLayout, 50);
12392
+ setTimeout(sbPreviewLayout, 250);
12393
+ } else {
12394
+ beast.sbPreviewSet({ visible: false }).catch(() => {});
12395
+ }
12396
+ }
12397
+
12398
+ function sbPreviewToggle() {
12399
+ sbPreviewOn = !sbPreviewOn;
12400
+ if (els.sbRunView) {
12401
+ els.sbRunView.classList.toggle('on', sbPreviewOn);
12402
+ els.sbRunView.textContent = sbPreviewOn ? 'Görsel' : 'Çıktı';
12403
+ els.sbRunView.title = sbPreviewOn ? 'Görsel önizleme açık — çıktıya dön' : 'Çıktı görünümü — görsele dön';
12293
12404
  }
12405
+ sbPreviewApply();
12406
+ }
12407
+
12408
+ /* Başlat/Dev başarısız olunca ajanı devreye sokar: repo yapısına göre
12409
+ DOĞRU komutu bulup panel_run ile başlatmasını ister (otomatik devralma). */
12410
+ function sbAskAgent(kind) {
12411
+ if (!sbFolder) return;
12412
+ const repoName = (sbItems.find((x) => x.folder === sbFolder) || {}).name || 'repo';
12413
+ const msg = kind === 'install'
12414
+ ? `Kur butonu bu repoda başarısız oldu. ${repoName} reposunun dosyalarını incele (README, requirements.txt, package.json, pyproject.toml, Gemfile, mkdocs/hugo ayarları, alt klasörler) ve DOĞRU kurulum komutunu bulup panel_run ile ÇALIŞTIR panelinde çalıştır. Sonucu tek cümleyle bildir.`
12415
+ : `Başlat/Dev butonu bu repoda başarısız oldu. ${repoName} reposunun yapısını incele (README, package.json scripts, hugo/mkdocs/jekyll ayarları, main/app.py, Cargo.toml, go.mod…) ve GERÇEK çalıştırma komutunu bulup panel_run ile ÇALIŞTIR panelinde başlat. Web adresi çıkarsa kullanıcıya bildir.`;
12416
+ sbLine('t-sys', '⚙ ajan devrede — doğru komutu bulup başlatacak…');
12417
+ sbSetBusy(true);
12418
+ beast.sandboxSend({ folder: sbFolder, msg }).then((r) => {
12419
+ if (r && r.ok) {
12420
+ if (r.sessionId) sbSessionId = r.sessionId;
12421
+ if (r.queued) sbLine('t-dim', '⏳ kuyrukta (' + r.count + ') — iş bitince gönderilir');
12422
+ } else {
12423
+ sbSetBusy(false);
12424
+ sbLine('t-err', (r && r.error) || 'gönderilemedi');
12425
+ }
12426
+ }).catch((e) => {
12427
+ sbSetBusy(false);
12428
+ sbLine('t-err', String((e && e.message) || e));
12429
+ });
12294
12430
  }
12295
12431
 
12296
12432
  function sbRunExec(cmd) {
12297
12433
  if (!sbFolder) { toast('Önce repo indir ya da seç'); return; }
12298
12434
  if (sbRunRunning) { toast('Zaten çalışıyor — önce ■ ile durdur'); return; }
12299
12435
  sbRunRunning = true;
12436
+ sbAutoAssist = 'run';
12300
12437
  if (els.sbRunStop) els.sbRunStop.hidden = false;
12301
12438
  beast.sandboxRun({ folder: sbFolder, cmd }).then((r) => {
12302
12439
  if (!r || !r.ok) {
12440
+ sbAutoAssist = null;
12303
12441
  sbRunRunning = false;
12304
12442
  if (els.sbRunStop) els.sbRunStop.hidden = true;
12305
12443
  sbRunLine('[hata] ' + ((r && r.error) || 'çalıştırılamadı'), 't-err');
12444
+ } else {
12445
+ sbBadge(sbFolder, true);
12306
12446
  }
12307
12447
  }).catch((e) => {
12448
+ sbAutoAssist = null;
12308
12449
  sbRunRunning = false;
12309
12450
  if (els.sbRunStop) els.sbRunStop.hidden = true;
12310
12451
  sbRunLine('[hata] ' + String((e && e.message) || e), 't-err');
@@ -12360,6 +12501,10 @@ if (els.sbInput) {
12360
12501
  }
12361
12502
  });
12362
12503
  }
12504
+ if (els.sbSend) els.sbSend.addEventListener('click', () => {
12505
+ sbRunCurrent();
12506
+ if (els.sbInput) els.sbInput.focus();
12507
+ });
12363
12508
  if (els.sbStop) {
12364
12509
  els.sbStop.addEventListener('click', () => {
12365
12510
  beast.sandboxStop({ folder: sbFolder }).then((r) => {
@@ -12370,17 +12515,9 @@ if (els.sbStop) {
12370
12515
  }).catch(() => {});
12371
12516
  });
12372
12517
  }
12373
- /* ÇALIŞTIRICI butonları — KOMUT DEĞİL AJANA TALİMAT (komutu ajan bulur):
12374
- uzun süreli süreci panel_run ile ÇALIŞTIR paneline bırakır, tur kilitlenmez */
12375
- if (els.sbRunInstall) els.sbRunInstall.addEventListener('click', () =>
12376
- sbInstruct('Bu projeyi KUR — paket bildirim dosyasına (package.json, requirements.txt, Cargo.toml, go.mod, pom.xml vb.) göre doğru kurulum komutunu bul ve çalıştır (run_command); sonucu 1-2 satırla raporla.')
12377
- );
12378
- if (els.sbRunStart) els.sbRunStart.addEventListener('click', () =>
12379
- sbInstruct('Bu projeyi ÇALIŞTIR — README/package.json scripts\u2019e göre doğru başlatma komutunu bul; uzun süreli sunucu/süreçse panel_run ile ÇALIŞTIR panelinde başlat (tur kilitleme), adresini/çalıştığını kullanıcıya bildir.')
12380
- );
12381
- if (els.sbRunDev) els.sbRunDev.addEventListener('click', () =>
12382
- sbInstruct('Bu projeyi GELİŞTİRME modunda başlat — package.json scripts\u2019e bak (npm run dev, vite, expo start, ng serve vb. hangisi varsa) ve panel_run ile ÇALIŞTIR panelinde başlat; adresini kullanıcıya bildir.')
12383
- );
12518
+ /* ÇALIŞTIRICI butonları: Başlat/Dev algılanan komutu (gerekirse kurup) çalıştırır */
12519
+ if (els.sbRunStart) els.sbRunStart.addEventListener('click', () => sbRunLaunch('run'));
12520
+ if (els.sbRunDev) els.sbRunDev.addEventListener('click', () => sbRunLaunch('dev'));
12384
12521
  if (els.sbRunGo) els.sbRunGo.addEventListener('click', () => {
12385
12522
  const c = els.sbRunCmd.value.trim();
12386
12523
  if (!c) { toast('Komut yaz'); return; }
@@ -12395,17 +12532,110 @@ if (els.sbRunCmd) {
12395
12532
  }
12396
12533
  });
12397
12534
  }
12535
+ if (els.sbRunSave) els.sbRunSave.addEventListener('click', async () => {
12536
+ if (!sbFolder) { toast('Önce repo seç'); return; }
12537
+ const c = els.sbRunCmd ? els.sbRunCmd.value.trim() : '';
12538
+ if (!c) { toast('Kaydetmek için komut yaz'); return; }
12539
+ const r = await beast.sandboxCfgSet({ folder: sbFolder, run: c }).catch(() => null);
12540
+ if (r && r.ok) {
12541
+ toast('kaydedildi — Başlat/Dev artık bu komutu kullanır');
12542
+ const det = await beast.sandboxDetect({ folder: sbFolder }).catch(() => null);
12543
+ if (det && det.ok && det.info) {
12544
+ sbRunInfo = det.info;
12545
+ if (els.sbRunCmd) els.sbRunCmd.placeholder = 'özel komut (algılanan: ' + (det.info.run || c) + ')…';
12546
+ }
12547
+ } else {
12548
+ toast((r && r.error) || 'kaydedilemedi');
12549
+ }
12550
+ });
12398
12551
  if (els.sbRunStop) {
12399
12552
  els.sbRunStop.addEventListener('click', () => {
12400
- beast.sandboxRunStop().then((r) => {
12401
- if (r && r.ok) sbRunLine('⏹ durduruldu', 't-dim');
12553
+ beast.sandboxRunStop({ folder: sbFolder }).then((r) => {
12554
+ if (r && r.ok) {
12555
+ sbRunLine('⏹ durduruldu', 't-dim');
12556
+ sbRunRunning = false;
12557
+ if (els.sbRunStop) els.sbRunStop.hidden = true;
12558
+ sbBadge(sbFolder, false);
12559
+ }
12402
12560
  }).catch(() => {});
12403
12561
  });
12404
12562
  }
12405
12563
  if (els.sbRunOpen) {
12406
- els.sbRunOpen.addEventListener('click', () => {
12407
- /* Tarayıcıda aç da AJANA TALİMAT: ajan çalışma adresini bulup kendi
12408
- browser_open aracıyla dahili tarayıcıda açar */
12409
- sbInstruct('Bu projenin web arayüzü çalışıyorsa adresini tespit et (çıktılar/README/port taraması) ve browser_open aracıyla DAHİLİ TARAYICIDA aç.')
12564
+ els.sbRunOpen.addEventListener('click', async () => {
12565
+ if (!sbRunUrl) { toast('Adres henüz yakalanmadı — Başlat/Dev ile sunucuyu çalıştır'); return; }
12566
+ const r = await beast.sandboxOpenUrl(sbRunUrl).catch(() => null);
12567
+ if (!r || !r.ok) toast('Açılamadı: ' + ((r && r.error) || 'bilinmeyen hata'));
12568
+ });
12569
+ }
12570
+ if (els.sbRunView) els.sbRunView.addEventListener('click', sbPreviewToggle);
12571
+
12572
+ /* görsel önizleme boyutu her yerleşim değişiminde main'e bildirilir
12573
+ (ayırıcı, tarayıcı dock'u, pencere boyutu, mod geçişi) */
12574
+ if (window.ResizeObserver) {
12575
+ const host = document.getElementById('sbPreviewHost');
12576
+ if (host) new ResizeObserver(() => sbPreviewLayout()).observe(host);
12577
+ }
12578
+
12579
+ /* ---------- sohbet/çalıştır ayırıcı (Beast Code ideSplit ile aynı desen) ----------
12580
+ Orantılı model: kaydedilen değer sohbet panelinin sandboxRow içindeki PAYI (0-1);
12581
+ sürükle-bırak anında --sbSplit yazar, bırakınca localStorage'a kaydeder. */
12582
+ let sbSplitFrac = 0;
12583
+
12584
+ function sbSplitApplyFrac() {
12585
+ const rowW = els.sandboxRow && els.sandboxRow.clientWidth ? els.sandboxRow.clientWidth : window.innerWidth - 250;
12586
+ /* kayıt yoksa eski varsayılan görünüm korunur: sohbet 480px, çalıştır kalanı */
12587
+ if (!sbSplitFrac) {
12588
+ const w = Math.max(280, Math.min(rowW - 480, Math.max(320, rowW - 300)));
12589
+ document.body.style.setProperty('--sbSplit', Math.round(w) + 'px');
12590
+ return;
12591
+ }
12592
+ sbSplitFrac = Math.max(0.2, Math.min(sbSplitFrac, 0.8));
12593
+ const w = Math.max(280, Math.min(Math.round(rowW * sbSplitFrac), Math.max(320, rowW - 300)));
12594
+ document.body.style.setProperty('--sbSplit', w + 'px');
12595
+ }
12596
+
12597
+ function sbSplitSave() {
12598
+ try { localStorage.setItem('beast.sbSplit', String(sbSplitFrac)); } catch {}
12599
+ }
12600
+
12601
+ function sbSplitRestore() {
12602
+ let saved = 0;
12603
+ try { saved = parseFloat(localStorage.getItem('beast.sbSplit')) || 0; } catch {}
12604
+ sbSplitFrac = saved > 0.05 && saved < 0.95 ? saved : 0;
12605
+ sbSplitApplyFrac();
12606
+ }
12607
+
12608
+ if (els.sbSplit) {
12609
+ els.sbSplit.addEventListener('mousedown', (e) => {
12610
+ e.preventDefault();
12611
+ els.sbSplit.classList.add('dragging');
12612
+ /* native önizleme view'i fare olaylarını yutabilir — sürükleme boyunca gizle,
12613
+ bırakınca geri getir (IDE'deki ayırıcı da aynı nedenle DOM üstünde çizilir) */
12614
+ sbPreviewSuspend = true;
12615
+ if (sbPreviewShown) beast.sbPreviewSet({ visible: false }).catch(() => {});
12616
+ const rightEdge = els.sbRun ? els.sbRun.getBoundingClientRect().right : window.innerWidth;
12617
+ const rowW = Math.max(600, els.sandboxRow && els.sandboxRow.clientWidth ? els.sandboxRow.clientWidth : window.innerWidth - 250);
12618
+ const move = (ev) => {
12619
+ const w = Math.max(280, Math.min(rightEdge - ev.clientX, rowW - 300));
12620
+ sbSplitFrac = Math.max(0.2, Math.min(w / rowW, 0.8));
12621
+ document.body.style.setProperty('--sbSplit', Math.round(w) + 'px');
12622
+ sbPreviewLayout();
12623
+ };
12624
+ const up = () => {
12625
+ document.removeEventListener('mousemove', move);
12626
+ document.removeEventListener('mouseup', up);
12627
+ els.sbSplit.classList.remove('dragging');
12628
+ sbSplitSave();
12629
+ sbPreviewSuspend = false;
12630
+ sbPreviewApply();
12631
+ };
12632
+ document.addEventListener('mousemove', move);
12633
+ document.addEventListener('mouseup', up);
12634
+ });
12635
+ window.addEventListener('resize', () => {
12636
+ if (sbModeOn()) {
12637
+ sbSplitApplyFrac();
12638
+ sbPreviewLayout();
12639
+ }
12410
12640
  });
12411
12641
  }
@@ -4271,7 +4271,7 @@ body.sandbox-mode #sandboxRow { display: flex; flex-direction: row; }
4271
4271
  flex-direction: column;
4272
4272
  min-height: 0;
4273
4273
  min-width: 300px;
4274
- flex: 0 0 var(--sbSplit, 480px);
4274
+ flex: 1 1 auto;
4275
4275
  background: var(--bg);
4276
4276
  overflow: hidden;
4277
4277
  }
@@ -4280,10 +4280,20 @@ body.sandbox-mode #sandboxRow { display: flex; flex-direction: row; }
4280
4280
  flex-direction: column;
4281
4281
  min-height: 0;
4282
4282
  min-width: 240px;
4283
- flex: 1;
4283
+ flex: 0 0 var(--sbSplit, 480px);
4284
4284
  background: var(--bg);
4285
4285
  overflow: hidden;
4286
- border-left: 1px solid var(--border);
4286
+ }
4287
+ #sbSplit {
4288
+ flex: none;
4289
+ width: 6px;
4290
+ cursor: col-resize;
4291
+ background: var(--border);
4292
+ opacity: 0.55;
4293
+ transition: opacity 0.15s;
4294
+ }
4295
+ #sbSplit:hover {
4296
+ opacity: 1;
4287
4297
  }
4288
4298
 
4289
4299
  #sbHead {
@@ -4394,9 +4404,31 @@ body.sandbox-mode #sandboxRow { display: flex; flex-direction: row; }
4394
4404
  padding: 7px 9px;
4395
4405
  border-radius: 8px;
4396
4406
  font-family: inherit;
4407
+ overflow-y: hidden;
4408
+ max-height: 140px;
4409
+ line-height: 1.45;
4410
+ display: block;
4397
4411
  }
4398
4412
  #sbInput:focus { border-color: var(--accent); outline: none; }
4399
4413
  #sbInput.expand { overflow-y: auto; }
4414
+ #sbInputRow button {
4415
+ flex: none;
4416
+ background: var(--panel2);
4417
+ border: 1px solid var(--border);
4418
+ color: var(--text);
4419
+ border-radius: 7px;
4420
+ cursor: pointer;
4421
+ font-size: 11px;
4422
+ padding: 6px 9px;
4423
+ line-height: 1;
4424
+ margin-bottom: 1px;
4425
+ }
4426
+ #sbInputRow button:hover { border-color: var(--accent); }
4427
+ #sbSend { color: var(--accent); }
4428
+ #sbSend:hover { background: var(--accent-dim); }
4429
+ #sbStop { color: var(--err); }
4430
+ #sbStop[hidden] { display: none; }
4431
+ #sbStop:hover { border-color: var(--err); }
4400
4432
 
4401
4433
  /* ---------- ÇALIŞTIR bölmesi ---------- */
4402
4434
  #sbRunHead {
@@ -4409,6 +4441,18 @@ body.sandbox-mode #sandboxRow { display: flex; flex-direction: row; }
4409
4441
  background: var(--panel);
4410
4442
  }
4411
4443
  #sbRunTitle { font-size: 11px; font-weight: 800; letter-spacing: 1px; color: var(--accent); flex: none; }
4444
+ #sbRunKind {
4445
+ flex: none;
4446
+ font-size: 10px;
4447
+ font-weight: 700;
4448
+ letter-spacing: 0.5px;
4449
+ text-transform: uppercase;
4450
+ color: var(--muted);
4451
+ border: 1px solid var(--border);
4452
+ border-radius: 4px;
4453
+ padding: 1px 6px;
4454
+ }
4455
+ #sbRunKind:empty { display: none; }
4412
4456
  #sbRunCwd {
4413
4457
  color: var(--muted);
4414
4458
  font-family: Consolas, monospace;
@@ -4418,6 +4462,33 @@ body.sandbox-mode #sandboxRow { display: flex; flex-direction: row; }
4418
4462
  text-overflow: ellipsis;
4419
4463
  white-space: nowrap;
4420
4464
  }
4465
+ #sbRunHead button {
4466
+ flex: none;
4467
+ background: var(--panel2);
4468
+ border: 1px solid var(--border);
4469
+ color: var(--muted);
4470
+ font-size: 11px;
4471
+ padding: 3px 9px;
4472
+ border-radius: 6px;
4473
+ cursor: pointer;
4474
+ }
4475
+ #sbRunHead button:hover { color: var(--text); border-color: var(--accent); }
4476
+ #sbRunHead button.on { color: var(--accent); border-color: var(--accent); }
4477
+ #sbPreviewHost {
4478
+ flex: 1;
4479
+ min-height: 0;
4480
+ display: flex;
4481
+ align-items: center;
4482
+ justify-content: center;
4483
+ background: var(--bg);
4484
+ }
4485
+ #sbPreviewHost[hidden] { display: none; }
4486
+ #sbPreviewHint {
4487
+ color: var(--muted);
4488
+ font-size: 12px;
4489
+ padding: 0 16px;
4490
+ text-align: center;
4491
+ }
4421
4492
  #sbRunBtns {
4422
4493
  flex: none;
4423
4494
  display: flex;