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/src/main.js CHANGED
@@ -2193,9 +2193,9 @@ function setBrowserOpen(v, forceVisible) {
2193
2193
  }
2194
2194
 
2195
2195
  // AÇMA — görünürlük: kullanıcı kendi açtıysa (forceVisible) MUTLAKA görünür;
2196
- // ajan açtıysa göz ikonu tercihine bak (varsayılan: GİZLİ/headless)
2196
+ // ajan açtıysa DA VARSAYILAN GÖRÜNÜR (göz ikonuyla gizli mod seçilmedikçe)
2197
2197
  browser.open = true;
2198
- browser.visible = forceVisible === true ? true : settings.browserHeadless === false;
2198
+ browser.visible = forceVisible === true ? true : settings.browserHeadless !== true;
2199
2199
  ensureBrowser();
2200
2200
  if (!browser.started) {
2201
2201
  browser.started = true;
@@ -2770,7 +2770,8 @@ function createWindow() {
2770
2770
  icon: path.join(__dirname, '..', 'assets', 'app.ico'),
2771
2771
  titleBarStyle: 'hidden',
2772
2772
  titleBarOverlay: {
2773
- color: 'transparent',
2773
+ /* transparent Windows'ta beyaz buton arka planı veriyor — tema rengiyle başlat */
2774
+ color: dark ? '#0d0d0f' : '#f7f7f8',
2774
2775
  symbolColor: dark ? '#9a9aa2' : '#707078',
2775
2776
  height: 46,
2776
2777
  },
@@ -3993,6 +3994,74 @@ ipcMain.handle('terminal:stop', () => {
3993
3994
  return { ok: true };
3994
3995
  });
3995
3996
 
3997
+ /* ---------------- Beast Code paneli (IDE modu ortası) ----------------
3998
+ IDE modunda ortadaki sohbet yerine Beast'in KENDİ ajanı çalışır:
3999
+ varsayılan model zinciri + soldaki dosya panelindeki klasör.
4000
+ Yazışma SOLDAKİ KLASÖRE BAĞLIDIR: her klasörün kendi gizli engine
4001
+ oturumu vardır (klasör değişince sohbet de değişir); sohbet geçmişi
4002
+ listesine karışmaz. Olayları renderer zaten agent:event ile alır,
4003
+ panele orada akıtılır. */
4004
+ const bcSessions = new Map(); /* klasör yolu → sessionId */
4005
+
4006
+ function bcGetSession(folder) {
4007
+ let sid = bcSessions.get(folder);
4008
+ if (sid) {
4009
+ try {
4010
+ const s = engine.cache.get(sid);
4011
+ if (s) return s;
4012
+ } catch {}
4013
+ bcSessions.delete(folder);
4014
+ }
4015
+ const s = engine._load(engine.createSession().id);
4016
+ s.messages = s.messages || [];
4017
+ s.bgTitle = 'Beast Code'; /* _view.isBg → sohbet geçmişi listesinde gizli */
4018
+ s.bcCode = true; /* engine: her işte todo planı çıkar (BEAST CODE MODU bloğu) */
4019
+ try {
4020
+ fs.appendFileSync(
4021
+ engine._file(s.id),
4022
+ JSON.stringify({ t: 'meta2', bgOf: '', title: 'Beast Code', at: new Date().toISOString() }) + '\n'
4023
+ );
4024
+ } catch {}
4025
+ engine.cache.set(s.id, s);
4026
+ bcSessions.set(folder, s.id);
4027
+ return s;
4028
+ }
4029
+
4030
+ ipcMain.handle('beastcode:send', (_e, payload) => {
4031
+ const text = String((payload && payload.msg) || '').trim();
4032
+ if (!text) return { ok: false, error: 'boş mesaj' };
4033
+ if (!engine) return { ok: false, error: 'ajan hazır değil' };
4034
+ if (!engine.publicState().hasModel) return { ok: false, error: 'model yok — Ayarlar → Provider sekmesinden ekle' };
4035
+ const ws = ideRoot();
4036
+ if (engine.isBusy(bcSessions.get(ws))) return { ok: false, busy: true, error: 'önceki mesaj sürüyor — ■ ile durdurabilirsin' };
4037
+ const s = bcGetSession(ws);
4038
+ s.workspace = ws; /* soldaki klasörde çalış */
4039
+ s.bcCode = true; /* todo disiplini + iş sonu hızlı kapanış (engine) */
4040
+ engine.cache.set(s.id, s);
4041
+ const ok = engine.send(s.id, text);
4042
+ if (!ok) return { ok: false, error: 'mesaj gönderilemedi' };
4043
+ return { ok: true, sessionId: s.id };
4044
+ });
4045
+
4046
+ ipcMain.handle('beastcode:stop', () => {
4047
+ const sid = bcSessions.get(ideRoot());
4048
+ if (!sid) return { ok: false };
4049
+ let r = false;
4050
+ try { r = engine.interrupt(sid); } catch {}
4051
+ return { ok: !!r };
4052
+ });
4053
+
4054
+ ipcMain.handle('beastcode:new', () => {
4055
+ const ws = ideRoot();
4056
+ const sid = bcSessions.get(ws);
4057
+ if (sid && engine.isBusy(sid)) return { ok: false, error: 'mesaj sürüyor — önce ■ ile durdur' };
4058
+ if (sid) {
4059
+ try { engine.deleteSession(sid); } catch {}
4060
+ bcSessions.delete(ws);
4061
+ }
4062
+ return { ok: true };
4063
+ });
4064
+
3996
4065
  /* düşünme (reasoning) seviyesi */
3997
4066
  ipcMain.handle('think:set', (_e, v) => {
3998
4067
  setThinkLevel(v);
@@ -4110,6 +4179,166 @@ ipcMain.handle('store:remove', (_e, id) =>
4110
4179
 
4111
4180
  ipcMain.handle('store:export', (_e, id) => storeMod.exportEntry(id));
4112
4181
 
4182
+ /* ---------- IDE MODU (sol: dosya gezgini · orta: chat · sağ: preview) ---------- */
4183
+
4184
+ const IDE_TEXT_EXT = new Set([
4185
+ '.html', '.htm', '.css', '.js', '.mjs', '.cjs', '.ts', '.jsx', '.tsx', '.json',
4186
+ '.md', '.txt', '.csv', '.py', '.ps1', '.bat', '.cmd', '.sh', '.yaml', '.yml',
4187
+ '.xml', '.ini', '.svg', '.gitignore',
4188
+ ]);
4189
+ const IDE_MAX_BYTES = 400 * 1024;
4190
+
4191
+ function ideRoot() {
4192
+ /* kullanıcı panelde başka klasör seçtiyse o kök alınır; yoksa agent workspace'i */
4193
+ return path.resolve(settings.ideRoot || settings.workspace || app.getPath('home'));
4194
+ }
4195
+
4196
+ ipcMain.handle('ide:setroot', async () => {
4197
+ try {
4198
+ const r = await dialog.showOpenDialog({
4199
+ title: 'Klasör seç — dosya paneli ve preview bu klasörü kullanır',
4200
+ defaultPath: ideRoot(),
4201
+ properties: ['openDirectory'],
4202
+ });
4203
+ if (r.canceled || !r.filePaths || !r.filePaths[0]) return { ok: false, canceled: true };
4204
+ settings.ideRoot = r.filePaths[0];
4205
+ saveSettings();
4206
+ return { ok: true, root: settings.ideRoot };
4207
+ } catch (e) {
4208
+ return { ok: false, error: String((e && e.message) || e) };
4209
+ }
4210
+ });
4211
+
4212
+ /* rel yol → workspace içinde kal (path traversal kilidi) */
4213
+ function ideSafe(rel) {
4214
+ const root = ideRoot();
4215
+ const p = path.resolve(root, String(rel || ''));
4216
+ if (p !== root && !p.startsWith(root + path.sep)) return null;
4217
+ return p;
4218
+ }
4219
+
4220
+ ipcMain.handle('ide:tree', (_e, rel) => {
4221
+ const p = ideSafe(rel);
4222
+ if (!p) return { ok: false, error: 'geçersiz yol' };
4223
+ try {
4224
+ const entries = fs.readdirSync(p, { withFileTypes: true })
4225
+ .filter((e) => e.name !== 'node_modules' && e.name !== '.git')
4226
+ .map((e) => {
4227
+ let size = 0;
4228
+ try { if (e.isFile()) size = fs.statSync(path.join(p, e.name)).size; } catch {}
4229
+ return { name: e.name, dir: e.isDirectory(), size };
4230
+ })
4231
+ .sort((a, b) => (a.dir === b.dir ? a.name.localeCompare(b.name) : a.dir ? -1 : 1));
4232
+ return { ok: true, workspace: ideRoot(), entries };
4233
+ } catch (e) {
4234
+ return { ok: false, error: String((e && e.message) || e) };
4235
+ }
4236
+ });
4237
+
4238
+ ipcMain.handle('ide:read', (_e, rel) => {
4239
+ const p = ideSafe(rel);
4240
+ if (!p) return { ok: false, error: 'geçersiz yol' };
4241
+ const ext = path.extname(p).toLowerCase();
4242
+ if (ext && !IDE_TEXT_EXT.has(ext)) return { ok: false, error: 'metin dosyası değil — düzenlenemez' };
4243
+ try {
4244
+ const st = fs.statSync(p);
4245
+ if (st.isDirectory()) return { ok: false, error: 'klasör' };
4246
+ if (st.size > IDE_MAX_BYTES) return { ok: false, error: 'dosya çok büyük (max 400KB)' };
4247
+ const buf = fs.readFileSync(p);
4248
+ if (buf.slice(0, 4096).includes(0)) return { ok: false, error: 'ikili (binary) dosya' };
4249
+ return { ok: true, content: buf.toString('utf8') };
4250
+ } catch (e) {
4251
+ return { ok: false, error: String((e && e.message) || e) };
4252
+ }
4253
+ });
4254
+
4255
+ ipcMain.handle('ide:write', (_e, p) => {
4256
+ const target = ideSafe(p && p.rel);
4257
+ if (!target) return { ok: false, error: 'geçersiz yol' };
4258
+ const ext = path.extname(target).toLowerCase();
4259
+ if (ext && !IDE_TEXT_EXT.has(ext)) return { ok: false, error: 'metin dosyası değil — yazılamaz' };
4260
+ try {
4261
+ const body = String((p && p.content) ?? '');
4262
+ if (Buffer.byteLength(body, 'utf8') > IDE_MAX_BYTES) return { ok: false, error: 'içerik çok büyük (max 400KB)' };
4263
+ fs.writeFileSync(target, body, 'utf8');
4264
+ return { ok: true };
4265
+ } catch (e) {
4266
+ return { ok: false, error: String((e && e.message) || e) };
4267
+ }
4268
+ });
4269
+
4270
+ /* Sağ tık menüsü: dosya/klasör sil (onay diyaloglu) */
4271
+ ipcMain.handle('ide:delete', async (_e, rel) => {
4272
+ const p = ideSafe(rel);
4273
+ if (!p || p === ideRoot()) return { ok: false, error: 'geçersiz yol' };
4274
+ try {
4275
+ const st = fs.statSync(p);
4276
+ const isDir = st.isDirectory();
4277
+ const owner = win && !win.isDestroyed() ? win : undefined;
4278
+ const r = owner
4279
+ ? await dialog.showMessageBox(owner, {
4280
+ type: 'warning',
4281
+ title: 'Sil',
4282
+ message: `"${rel}" ${isDir ? 'klasörünü (içi dahil)' : 'dosyasını'} silmek istiyor musun?`,
4283
+ buttons: ['Sil', 'Vazgeç'],
4284
+ defaultId: 1,
4285
+ cancelId: 1,
4286
+ })
4287
+ : { response: 1 };
4288
+ if (r.response !== 0) return { ok: false, canceled: true };
4289
+ fs.rmSync(p, { recursive: true, force: true });
4290
+ return { ok: true };
4291
+ } catch (e) {
4292
+ return { ok: false, error: String((e && e.message) || e) };
4293
+ }
4294
+ });
4295
+
4296
+ /* Sağ tık menüsü: HTML dosyasını dahili tarayıcıda GÖRÜNÜR aç */
4297
+ ipcMain.handle('ide:previewFile', (_e, rel) => {
4298
+ try {
4299
+ const p = ideSafe(rel);
4300
+ if (!p) return { ok: false, error: 'geçersiz yol' };
4301
+ if (!/\.html?$/i.test(p)) return { ok: false, error: 'önizleme yalnız .html/.htm dosyaları için' };
4302
+ setBrowserOpen(true, true);
4303
+ const url = 'file:///' + p.replace(/\\/g, '/');
4304
+ browser.view.webContents.loadURL(url).catch(() => {});
4305
+ browserEmit({ open: true, width: browser.width, url });
4306
+ return { ok: true };
4307
+ } catch (e) {
4308
+ return { ok: false, error: String((e && e.message) || e) };
4309
+ }
4310
+ });
4311
+
4312
+ /* PREVIEW: workspace kökündeki entry sayfayı (index.html → ilk *.html) sağdaki
4313
+ dahili tarayıcıda aç. file:// yükleme browserNavigate'i BYPASS eder — o https
4314
+ olmayan adresi aramaya çevirir. */
4315
+ ipcMain.handle('ide:preview', () => { try {
4316
+ const root = ideRoot();
4317
+ const pick = (name) => {
4318
+ const p = path.join(root, name);
4319
+ try { return fs.existsSync(p) ? p : null; } catch { return null; }
4320
+ };
4321
+ let entry = pick('index.html');
4322
+ if (!entry) {
4323
+ const htmls = fs
4324
+ .readdirSync(root, { withFileTypes: true })
4325
+ .filter((e) => e.isFile() && /\.html?$/i.test(e.name))
4326
+ .map((e) => e.name);
4327
+ entry = htmls.length ? path.join(root, htmls.sort()[0]) : null;
4328
+ }
4329
+ if (!entry) return { ok: false, error: 'workspace kökünde index.html yok — önce agent\'a siteyi yazdır' };
4330
+ /* forceVisible: preview'a basınca tarayıcı ikonuna basmaya gerek kalmasın —
4331
+ dahili tarayıcı otomatik ve GÖRÜNÜR açılır */
4332
+ setBrowserOpen(true, true);
4333
+ const url = 'file:///' + entry.replace(/\\/g, '/');
4334
+ browser.view.webContents.loadURL(url).catch(() => {});
4335
+ browserEmit({ open: true, width: browser.width, url });
4336
+ return { ok: true, url };
4337
+ } catch (e) {
4338
+ return { ok: false, error: String((e && e.message) || e) };
4339
+ }
4340
+ });
4341
+
4113
4342
  ipcMain.handle('custom:set', (_e, list) => {
4114
4343
  settings.customProviders = Array.isArray(list) ? list : [];
4115
4344
  saveSettings();
package/src/preload.js CHANGED
@@ -64,6 +64,9 @@ contextBridge.exposeInMainWorld('beast', {
64
64
  terminalToggle: () => ipcRenderer.invoke('terminal:toggle'),
65
65
  terminalRun: (cmd, shell) => ipcRenderer.invoke('terminal:run', { cmd, shell }),
66
66
  terminalStop: () => ipcRenderer.invoke('terminal:stop'),
67
+ beastcodeSend: (msg) => ipcRenderer.invoke('beastcode:send', { msg }),
68
+ beastcodeStop: () => ipcRenderer.invoke('beastcode:stop'),
69
+ beastcodeNew: () => ipcRenderer.invoke('beastcode:new'),
67
70
  thinkSet: (v) => ipcRenderer.invoke('think:set', v),
68
71
  exaGet: () => ipcRenderer.invoke('exa:get'),
69
72
  exaSet: (key) => ipcRenderer.invoke('exa:set', key),
@@ -144,6 +147,13 @@ contextBridge.exposeInMainWorld('beast', {
144
147
  storeLike: (id) => ipcRenderer.invoke('store:like', id),
145
148
  storeRemove: (id) => ipcRenderer.invoke('store:remove', id),
146
149
  storeExport: (id) => ipcRenderer.invoke('store:export', id),
150
+ ideTree: (rel) => ipcRenderer.invoke('ide:tree', rel),
151
+ ideSetRoot: () => ipcRenderer.invoke('ide:setroot'),
152
+ ideRead: (rel) => ipcRenderer.invoke('ide:read', rel),
153
+ ideWrite: (rel, content) => ipcRenderer.invoke('ide:write', { rel, content }),
154
+ idePreview: () => ipcRenderer.invoke('ide:preview'),
155
+ idePreviewFile: (rel) => ipcRenderer.invoke('ide:previewFile', rel),
156
+ ideDelete: (rel) => ipcRenderer.invoke('ide:delete', rel),
147
157
  onWaEvent: (cb) => ipcRenderer.on('wa:event', (_e, ev) => cb(ev)),
148
158
  onEvent: (cb) => ipcRenderer.on('agent:event', (_e, ev) => cb(ev)),
149
159
  });
@@ -451,6 +451,16 @@
451
451
  p_zen_fail: 'Model kurulumu başarısız',
452
452
  bot_num_ro_hint: 'Numara ekleme/kişi adı Ayarlar → Entegrasyonlar (WhatsApp izin listesi) üzerinden yapılır; burada yalnızca bağlı numaralar görünür.',
453
453
  tipStore: 'Skills Store',
454
+ tipIde: 'IDE Modu — dosyalar + chat + preview',
455
+ ide_files: 'DOSYALAR',
456
+ ide_pick: 'Klasör seç',
457
+ ide_refresh: 'Yenile',
458
+ ide_preview: '▶ Preview',
459
+ ide_saved: 'Kaydedildi — preview\u2019ı yenile',
460
+ bc_ph: 'Beast Code mesajı yaz — Enter ile gönder…',
461
+ bc_new: 'Yeni oturum',
462
+ bc_clear: 'Çıktıyı temizle',
463
+ bc_stop: 'Çalışan mesajı durdur',
454
464
  store_title: 'SKILLS STORE',
455
465
  store_tab_trending: 'Trending',
456
466
  store_tab_stars: 'Stars',
@@ -997,6 +1007,16 @@
997
1007
  p_zen_fail: 'Model setup failed',
998
1008
  bot_num_ro_hint: 'Add numbers / person names via Settings → Integrations (WhatsApp allow list); only linked numbers are shown here.',
999
1009
  tipStore: 'Skills Store',
1010
+ tipIde: 'IDE Mode — files + chat + preview',
1011
+ ide_files: 'FILES',
1012
+ ide_pick: 'Choose folder',
1013
+ ide_refresh: 'Refresh',
1014
+ ide_preview: '▶ Preview',
1015
+ ide_saved: 'Saved — refresh the preview',
1016
+ bc_ph: 'Type a message for Beast Code — press Enter to send…',
1017
+ bc_new: 'New session',
1018
+ bc_clear: 'Clear output',
1019
+ bc_stop: 'Stop running message',
1000
1020
  store_title: 'SKILLS STORE',
1001
1021
  store_tab_trending: 'Trending',
1002
1022
  store_tab_stars: 'Stars',
@@ -23,11 +23,23 @@
23
23
  </div>
24
24
  <div id="botList"></div>
25
25
  </div>
26
+ <aside id="filePanel">
27
+ <div id="filePanelHead">
28
+ <span id="filePanelTitle" data-i18n="ide_files">DOSYALAR</span>
29
+ <span class="file-spacer"></span>
30
+ <button id="filePick" title="Klasör seç" data-i18n-title="ide_pick"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7z"/></svg></button>
31
+ <button id="fileRefresh" title="Yenile" data-i18n-title="ide_refresh">&#x27F3;</button>
32
+ <button id="filePreview" title="Preview" data-i18n="ide_preview">&#9654; Preview</button>
33
+ </div>
34
+ <div id="filePanelPath" title="">—</div>
35
+ <div id="fileTree"></div>
36
+ </aside>
26
37
  <div id="sideFoot">
27
38
  <button id="gearBtn" title="Ayarlar" data-i18n-title="tipGear">&#x2699;&#xFE0E;</button>
28
39
  <button id="themeBtn" title="Koyu tema">&#x263E;</button>
29
40
  <button id="langBtn" title="Dil değiştir / Switch language" data-i18n-title="tipLang"><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></button>
30
41
  <button id="storeBtn" title="Skills Store" data-i18n-title="tipStore"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h16l-1.3 12.1a2 2 0 0 1-2 1.9H7.3a2 2 0 0 1-2-1.9L4 7z"/><path d="M8 10V6a4 4 0 0 1 8 0v4"/></svg></button>
42
+ <button id="ideBtn" title="IDE Modu" data-i18n-title="tipIde"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 6l-5 6 5 6"/><path d="M16 6l5 6-5 6"/><path d="M13.5 4l-3 16"/></svg></button>
31
43
  </div>
32
44
  </aside>
33
45
 
@@ -65,7 +77,7 @@
65
77
  <button id="termGBtn" class="dd-btn dd-icon-btn" title="Git Bash terminali" data-i18n-title="tipTermG">&#x25C8;</button>
66
78
  <button id="termCBtn" class="dd-btn dd-icon-btn" title="CMD terminali" data-i18n-title="tipTermC">&#x276F;&#xFE0E;</button>
67
79
  <button id="browserBtn" class="dd-btn dd-icon-btn" title="Dahili tarayıcı" data-i18n-title="tipBrowser">&#x29C9;</button>
68
- <button id="eyeBtn" class="dd-btn dd-icon-btn" title="Ajan tarayıcısı görünür/gizli — göz açıkken aramalar panelde izlenir, kapalıyken gizli çalışır" data-i18n-title="tipEye"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/></svg></button>
80
+ <button id="eyeBtn" class="dd-btn dd-icon-btn" title="Ajan tarayıcısı görünür/gizli — göz açıkken aramalar panelde izlenir, kapalıyken gizli çalışır" data-i18n-title="tipEye"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/><line class="eye-slash" x1="4" y1="4" x2="20" y2="20"/></svg></button>
69
81
  <div class="drag-spacer"></div>
70
82
  </header>
71
83
 
@@ -90,6 +102,36 @@
90
102
  <button id="sendBtn" title="Gönder" data-i18n-title="tipSend">&#x27A4;&#xFE0E;</button>
91
103
  </div>
92
104
  </div>
105
+
106
+ <div id="ideRow">
107
+ <section id="codePane">
108
+ <div id="codeTabsBar">
109
+ <div id="codeTabs"></div>
110
+ <code id="codePath" title="">—</code>
111
+ <button id="codeSave" title="Kaydet (Ctrl+S)">Kaydet</button>
112
+ </div>
113
+ <textarea id="codeTa" spellcheck="false"></textarea>
114
+ </section>
115
+
116
+ <div id="ideSplit" title="Sürükleyerek boyutlandır"></div>
117
+
118
+ <div id="bcPanel">
119
+ <div id="bcHead">
120
+ <span id="bcTitle">BEAST CODE</span>
121
+ <span id="bcCwd" title=""></span>
122
+ <span class="file-spacer"></span>
123
+ <button id="bcNew" title="Yeni oturum" data-i18n-title="bc_new">+</button>
124
+ <button id="bcClear" title="Çıktıyı temizle" data-i18n-title="bc_clear">Temizle</button>
125
+ </div>
126
+ <div id="bcOut"></div>
127
+ <div id="bcTodoWrap" hidden></div>
128
+ <div id="bcInputRow">
129
+ <span id="bcPrompt">code&gt;</span>
130
+ <input id="bcInput" spellcheck="false" autocomplete="off" placeholder="Beast Code mesajı yaz — Enter ile gönder…" data-i18n-ph="bc_ph" />
131
+ <button id="bcStop" title="Çalışan mesajı durdur" hidden data-i18n-title="bc_stop">&#x25A0;&#xFE0E;</button>
132
+ </div>
133
+ </div>
134
+ </div>
93
135
  </main>
94
136
 
95
137
  <aside id="rail">
@@ -279,6 +321,8 @@
279
321
 
280
322
  <div id="toast"></div>
281
323
 
324
+ <div id="fileCtxMenu" hidden></div>
325
+
282
326
  <input type="file" id="fileInput" multiple hidden
283
327
  accept="image/*,.txt,.md,.json,.csv,.log,.js,.ts,.py,.ps1,.bat,.cmd,.html,.css,.yaml,.yml,.xml,.ini" />
284
328