beast-agent 2.49.0 → 2.50.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "2.49.0",
4
+ "version": "2.50.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -97,16 +97,41 @@ async function compose(panels, opts) {
97
97
  if (layout === 'h') ctx.fillRect(x - GAP, 0, GAP, H);
98
98
  else ctx.fillRect(0, y - GAP, W, GAP);
99
99
  }
100
- /* etiket: sol üstte koyu şerit + parlak metin */
100
+ /* etiket: SAĞ ÜST köşede BÜYÜK zaman dilimi (üstünde küçük sembol) —
101
+ görsel ajan hangi panelin hangi periyot olduğunu bakışta okur */
101
102
  if (p.label) {
102
103
  try {
103
- ctx.font = pickFont(20);
104
- const tw = Math.min(cellW - 24, Math.ceil(ctx.measureText(p.label).width) + 20);
105
- ctx.fillStyle = 'rgba(0,0,0,0.78)';
106
- ctx.fillRect(x + 6, y + 6, Math.max(60, tw), LABEL_H);
107
- ctx.fillStyle = '#00e676';
104
+ const parts = String(p.label).split('·').map((s) => s.trim()).filter(Boolean);
105
+ const tf = parts.length > 1 ? parts[1] : parts[0];
106
+ const sym = parts.length > 1 ? parts[0] : '';
107
+ const fsTf = Math.max(22, Math.round(cellW / 44)); /* 1600 px panel → 36 px */
108
+ const fsSym = Math.max(12, Math.round(fsTf * 0.45));
109
+ const padX = Math.round(fsTf * 0.55);
110
+ const padY = Math.round(fsTf * 0.42);
111
+ const lineGap = Math.round(fsSym * 0.45);
108
112
  ctx.textBaseline = 'middle';
109
- ctx.fillText(p.label, x + 15, y + 6 + LABEL_H / 2 + 1);
113
+ ctx.textAlign = 'right';
114
+ ctx.font = pickFont(fsTf);
115
+ const tw = ctx.measureText(tf).width;
116
+ ctx.font = pickFont(fsSym);
117
+ const sw = sym ? ctx.measureText(sym).width : 0;
118
+ const boxW = Math.ceil(Math.max(tw, sw) + padX * 2);
119
+ const boxH = Math.ceil(padY * 2 + fsTf + (sym ? fsSym + lineGap : 0));
120
+ const bx = x + cellW - boxW - 10;
121
+ const by = y + 10;
122
+ ctx.fillStyle = 'rgba(0,0,0,0.80)';
123
+ ctx.fillRect(bx, by, boxW, boxH);
124
+ ctx.fillStyle = '#00e676';
125
+ ctx.fillRect(bx, by, boxW, 3); /* üst aksan çizgisi */
126
+ if (sym) {
127
+ ctx.font = pickFont(fsSym);
128
+ ctx.fillStyle = '#c9d2d9';
129
+ ctx.fillText(sym, bx + boxW - padX, by + padY + fsSym / 2);
130
+ }
131
+ ctx.font = pickFont(fsTf);
132
+ ctx.fillStyle = '#00e676';
133
+ ctx.fillText(tf, bx + boxW - padX, by + padY + (sym ? fsSym + lineGap : 0) + fsTf / 2);
134
+ ctx.textAlign = 'start';
110
135
  } catch (e) {}
111
136
  }
112
137
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mt5_shot",
3
- "defaultsVersion": 2,
4
- "description": "MT5 grafiğinin GERÇEK ekran görüntüsünü alır ve GÖRSELİ ÇAĞIRAN AJANA GÖSTERİR: MT5\\MQL5\\Files\\beast_cmd.json üzerinden BeastFinance EA'ya 'shot' komutu yazar, EA ChartScreenShot ile PNG üretir; PNG hem tool klasörüne hem <kullanıcı>\\beast_shots altına kopyalanır (dönen path send_file'a verilebilir) hem de data URL olarak sonuca gömülür — engine bunu sonraki turda ajana görsel olarak enjekte eder (embed:false kapatır). ÇOKLU ZAMAN DİLİMİ: timeframes:[\"M1\",\"M15\"] verirsen aynı sembolün periyotları SIRAYLA çekilip TEK PNG'de birleştirilir (layout:\"h\" soldan sağa — varsayılan; \"v\" üstten alta; 2-3 periyot) ve her panelin üstüne \"SEMBOL · PERİYOT\" etiketi çizilir — solda/sağda hangi periyot olduğu görüntüden de okunur. Aktif grafik istenen sembol/periyot ile aynıysa ondan çekilir (Beast panosu + seviye çizgileri görünür), değilse EA geçici grafik açar (ChartOpen/ChartSetSymbolPeriod + BeastShot.tpl açık tema). Komut id/ts ile yazılır, ack poll edilir ve PNG MQL5\\Files'ta belirene kadar beklenir; hata halinde gövde şu kodu taşır: err = no_terminal|ea_offline|cmd_write_fail|ea_no_ack|ea_error|png_yok|timeout|stitch_fail (+ cmdId, filesDir, diagnostic, ping). Grafik analizi/raporu ve ekip paylaşımı için birincil screenshot aracıdır.",
3
+ "defaultsVersion": 3,
4
+ "description": "MT5 grafiğinin GERÇEK ekran görüntüsünü alır ve GÖRSELİ ÇAĞIRAN AJANA GÖSTERİR: MT5\\MQL5\\Files\\beast_cmd.json üzerinden BeastFinance EA'ya 'shot' komutu yazar, EA ChartScreenShot ile PNG üretir; PNG hem tool klasörüne hem <kullanıcı>\\beast_shots altına kopyalanır (dönen path send_file'a verilebilir) hem de data URL olarak sonuca gömülür — engine bunu sonraki turda ajana görsel olarak enjekte eder (embed:false kapatır). ÇOKLU ZAMAN DİLİMİ: timeframes:[\"M1\",\"M15\"] verirsen aynı sembolün periyotları SIRAYLA çekilip TEK PNG'de birleştirilir (layout:\"h\" soldan sağa — varsayılan; \"v\" üstten alta; 2-3 periyot) ve her panelin SAĞ ÜST köşesine BÜYÜK periyot etiketi (üstünde küçük sembol) çizilir — solda/sağda hangi periyot olduğu görüntüden bakışta okunur. Aktif grafik istenen sembol/periyot ile aynıysa ondan çekilir (Beast panosu + seviye çizgileri görünür), değilse EA geçici grafik açar (ChartOpen/ChartSetSymbolPeriod + BeastShot.tpl açık tema). Çekim öncesi grafiğe sağ boşluk payı (chart shift %12) uygulanır — SON MUM sağ kenara yapışmaz; aktif grafikte önceki ayar çekimden sonra geri yazılır. Komut id/ts ile yazılır, ack poll edilir ve PNG MQL5\\Files'ta belirene kadar beklenir; hata halinde gövde şu kodu taşır: err = no_terminal|ea_offline|cmd_write_fail|ea_no_ack|ea_error|png_yok|timeout|stitch_fail (+ cmdId, filesDir, diagnostic, ping). Grafik analizi/raporu ve ekip paylaşımı için birincil screenshot aracıdır.",
5
5
  "parameters": {
6
6
  "type": "object",
7
7
  "properties": {
@@ -1,5 +1,5 @@
1
1
  //+------------------------------------------------------------------+
2
- //| BeastFinance.mq5 — Beast Finance köprü EA'sı (v1.20) |
2
+ //| BeastFinance.mq5 — Beast Finance köprü EA'sı (v1.21) |
3
3
  //| - Heartbeat: MQL5\Files\beast_ea.json (durum + izinler + equity) |
4
4
  //| - Komut köprüsü: beast_cmd.json (Beast yazar) → beast_cmd_ack.json|
5
5
  //| - Entegrasyon/Screenshot: grafik üstü pano (Comment) + seviye |
@@ -8,7 +8,7 @@
8
8
  //+------------------------------------------------------------------+
9
9
  #property copyright "Beast Agent"
10
10
  #property link "https://github.com/algokodcom/beast-agent"
11
- #property version "1.20"
11
+ #property version "1.21"
12
12
  #property description "Beast Finance köprü EA'sı — heartbeat + komut köprüsü + grafik panosu."
13
13
 
14
14
  #include <Trade\Trade.mqh>
@@ -72,7 +72,7 @@ void WriteBeat(const string stage)
72
72
  {
73
73
  int h = FileOpen(BeatFile, FILE_WRITE|FILE_TXT|FILE_UNICODE);
74
74
  if(h == INVALID_HANDLE) return;
75
- string js = StringFormat("{\"ok\":true,\"ea\":\"BeastFinance\",\"version\":\"1.20\",\"tag\":\"%s\",\"stage\":\"%s\",\"time\":%d,\"server_time\":\"%s\",\"symbol\":\"%s\",\"period\":%d,\"equity\":%.2f,\"balance\":%.2f,\"terminal_trade_allowed\":%s,\"mql_trade_allowed\":%s,\"positions\":%d,\"note\":\"%s\"}",
75
+ string js = StringFormat("{\"ok\":true,\"ea\":\"BeastFinance\",\"version\":\"1.21\",\"tag\":\"%s\",\"stage\":\"%s\",\"time\":%d,\"server_time\":\"%s\",\"symbol\":\"%s\",\"period\":%d,\"equity\":%.2f,\"balance\":%.2f,\"terminal_trade_allowed\":%s,\"mql_trade_allowed\":%s,\"positions\":%d,\"note\":\"%s\"}",
76
76
  JStr(InpTag), stage, (int)TimeCurrent(), TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS),
77
77
  _Symbol, (int)Period(),
78
78
  AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE),
@@ -93,7 +93,7 @@ void RefreshPanel()
93
93
  Comment("");
94
94
  return;
95
95
  }
96
- string lines = "Beast Finance — BeastFinance v1.20\n";
96
+ string lines = "Beast Finance — BeastFinance v1.21\n";
97
97
  lines += _Symbol + " · " + EnumToString((ENUM_TIMEFRAMES)Period()) + " · " + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS) + "\n";
98
98
  lines += StringFormat("Equity: %.2f · Balance: %.2f · Acik pozisyon: %d\n",
99
99
  AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE), PositionsTotal());
@@ -351,8 +351,16 @@ void ProcessCommands()
351
351
  }
352
352
  }
353
353
 
354
+ /* SON MUM SAĞ KENARA YAPIŞMASIN: grafik "shift" payı (%12 sağ boşluk).
355
+ Aktif grafikte önceki ayar çekimden SONRA geri yazılır — kullanıcının
356
+ grafiği kalıcı değişmez; geçici grafik zaten kapanır. */
357
+ long shiftPrev = ChartGetInteger(cid, CHART_SHIFT);
358
+ long shiftSizePrev = ChartGetInteger(cid, CHART_SHIFT_SIZE);
359
+
354
360
  if(ok)
355
361
  {
362
+ ChartSetInteger(cid, CHART_SHIFT, true);
363
+ ChartSetInteger(cid, CHART_SHIFT_SIZE, 12);
356
364
  ChartRedraw(cid);
357
365
  Sleep(1200); /* mumlar/olcek otursun */
358
366
  ChartRedraw(cid);
@@ -367,6 +375,12 @@ void ProcessCommands()
367
375
  result = StringFormat("{\"file\":\"%s\",\"width\":%d,\"height\":%d,\"symbol\":\"%s\",\"period\":%d,\"temp_chart\":%s,\"template_applied\":%s,\"theme_ok\":%s,\"bg\":%d}",
368
376
  JStr(file), w, hh, symWant, (int)tfUse, (tempChart ? "true" : "false"), (tplApplied ? "true" : "false"), (themeOk ? "true" : "false"), bgNow);
369
377
  }
378
+ if(!tempChart)
379
+ {
380
+ ChartSetInteger(cid, CHART_SHIFT, shiftPrev != 0);
381
+ ChartSetInteger(cid, CHART_SHIFT_SIZE, shiftSizePrev);
382
+ ChartRedraw(cid);
383
+ }
370
384
  }
371
385
  if(tempChart) ChartClose(cid);
372
386
  }
@@ -549,6 +549,12 @@
549
549
  </div>
550
550
  </div>
551
551
 
552
+ <!-- DM görselleri: tıkla → tam boy görüntüleyici (Esc / tıkla kapat) -->
553
+ <div id="imgOverlay" hidden>
554
+ <img id="imgOverlayImg" alt="görsel" />
555
+ <button id="imgOverlayClose" title="Kapat (Esc)">&#x2715;&#xFE0E;</button>
556
+ </div>
557
+
552
558
  <div id="toolOverlay" hidden>
553
559
  <div id="toolDialog">
554
560
  <div id="toolDialogHead">
@@ -53,6 +53,9 @@ const els = {
53
53
  dmColumnBtn: $('#dmColumnBtn'),
54
54
  dmMaxBtn: $('#dmMaxBtn'),
55
55
  dmBackBtn: $('#dmBackBtn'),
56
+ imgOverlay: $('#imgOverlay'),
57
+ imgOverlayImg: $('#imgOverlayImg'),
58
+ imgOverlayClose: $('#imgOverlayClose'),
56
59
  watchBtn: $('#watchBtn'),
57
60
  watchPaneList: $('#watchPaneList'),
58
61
  watchPaneOpen: $('#watchPaneOpen'),
@@ -5549,6 +5552,30 @@ function toggleDmRail(hide) {
5549
5552
 
5550
5553
  /* --- modal: SOHBET (yalnız burada tam döküm) --- */
5551
5554
 
5555
+ /* Yeni mesaj / geç decode olan görsellerde sohbeti otomatik en alta indir:
5556
+ anında + rAF + kısa gecikmeler + görsel load olayları (görsel sonradan
5557
+ yüklenince içerik uzar; tek seferlik scroll yetmez). */
5558
+ function dmStickBottom(box) {
5559
+ if (!box) return;
5560
+ const toBottom = () => {
5561
+ try {
5562
+ box.scrollTop = box.scrollHeight;
5563
+ const p = box.parentElement;
5564
+ if (p && p.scrollHeight > p.clientHeight) p.scrollTop = p.scrollHeight;
5565
+ } catch {}
5566
+ };
5567
+ toBottom();
5568
+ try { requestAnimationFrame(toBottom); } catch {}
5569
+ setTimeout(toBottom, 60);
5570
+ setTimeout(toBottom, 300);
5571
+ for (const img of box.querySelectorAll('img.dmt-img')) {
5572
+ if (img.dataset.dmStick) continue;
5573
+ img.dataset.dmStick = '1';
5574
+ img.addEventListener('load', toBottom, { once: true });
5575
+ img.addEventListener('error', toBottom, { once: true });
5576
+ }
5577
+ }
5578
+
5552
5579
  function renderDmModal() {
5553
5580
  const list = els.dmDialogList;
5554
5581
  if (!list) return;
@@ -5570,8 +5597,7 @@ function renderDmModal() {
5570
5597
  if (els.dmBackBtn) els.dmBackBtn.hidden = false;
5571
5598
  if (els.dmModalDel) els.dmModalDel.hidden = false;
5572
5599
  list.innerHTML = '<div class="dm-thread-msgs">' + msgs.map(dmMsgHtml).join('') + '</div>';
5573
- const box = list.querySelector('.dm-thread-msgs');
5574
- if (box) box.scrollTop = box.scrollHeight;
5600
+ dmStickBottom(list.querySelector('.dm-thread-msgs'));
5575
5601
  return;
5576
5602
  }
5577
5603
  /* OTURUM LİSTESİ — AKTİF + GEÇMİŞ bölümleri */
@@ -5665,7 +5691,8 @@ if (els.dmOverlay) {
5665
5691
  els.dmOverlay.addEventListener('keydown', (e) => {
5666
5692
  if (e.key === 'Escape') {
5667
5693
  e.stopPropagation();
5668
- if (els.dmOverlay.classList.contains('dm-max')) toggleDmMax(false);
5694
+ if (els.imgOverlay && !els.imgOverlay.hidden) closeImgViewer(); /* önce görüntüleyici kapansın */
5695
+ else if (els.dmOverlay.classList.contains('dm-max')) toggleDmMax(false);
5669
5696
  else closeDmModal();
5670
5697
  }
5671
5698
  });
@@ -5679,6 +5706,47 @@ if (els.dmColumnBtn) {
5679
5706
  });
5680
5707
  }
5681
5708
 
5709
+ /* --- DM görsel görüntüleyici: görsele tıkla → TAM BOY (tıkla/Esc kapat) --- */
5710
+ function openImgViewer(src) {
5711
+ if (!els.imgOverlay || !els.imgOverlayImg || !src) return;
5712
+ els.imgOverlayImg.src = String(src);
5713
+ els.imgOverlay.hidden = false;
5714
+ }
5715
+
5716
+ function closeImgViewer() {
5717
+ if (els.imgOverlay) els.imgOverlay.hidden = true;
5718
+ if (els.imgOverlayImg) els.imgOverlayImg.src = '';
5719
+ }
5720
+
5721
+ if (els.imgOverlay) {
5722
+ els.imgOverlay.addEventListener('click', () => closeImgViewer());
5723
+ }
5724
+ if (els.imgOverlayClose) {
5725
+ els.imgOverlayClose.addEventListener('click', (e) => {
5726
+ e.stopPropagation();
5727
+ closeImgViewer();
5728
+ });
5729
+ }
5730
+ if (els.dmDialogList) {
5731
+ /* delegasyon: modal her mesajda yeniden çizilir — dinleyici tek yerde kalır */
5732
+ els.dmDialogList.addEventListener('click', (e) => {
5733
+ const img = e.target && e.target.closest ? e.target.closest('img.dmt-img') : null;
5734
+ if (!img || !img.src) return;
5735
+ e.stopPropagation();
5736
+ openImgViewer(img.src);
5737
+ });
5738
+ }
5739
+ document.addEventListener(
5740
+ 'keydown',
5741
+ (e) => {
5742
+ if (e.key !== 'Escape' || !els.imgOverlay || els.imgOverlay.hidden) return;
5743
+ e.preventDefault();
5744
+ e.stopPropagation();
5745
+ closeImgViewer();
5746
+ },
5747
+ true
5748
+ );
5749
+
5682
5750
  /* ---------------- TOOLS konsolu (kişisel araçlar) ----------------
5683
5751
  %APPDATA%\beast\tools\<ad>\tool.json + run.js — kullanıcı kendi araçlarını
5684
5752
  yazar; finance dahil TÜM oturumlar tool__<ad> olarak çağırır. Sol panelde
@@ -2331,6 +2331,7 @@ body.browser-open #settingsDialog { width: min(70vw, calc(100vw - var(--bw, 480p
2331
2331
  border-radius: 6px;
2332
2332
  margin-top: 5px;
2333
2333
  border: 1px solid var(--border, rgba(255,255,255,0.12));
2334
+ cursor: zoom-in;
2334
2335
  }
2335
2336
  .dmt-msg .dmt-img-note {
2336
2337
  font-size: 10px;
@@ -2338,6 +2339,40 @@ body.browser-open #settingsDialog { width: min(70vw, calc(100vw - var(--bw, 480p
2338
2339
  font-style: italic;
2339
2340
  margin-top: 3px;
2340
2341
  }
2342
+ /* DM görsel görüntüleyici: tam boy, tıkla/Esc ile kapanır */
2343
+ #imgOverlay {
2344
+ position: fixed;
2345
+ inset: 0;
2346
+ z-index: 400;
2347
+ background: rgba(0, 0, 0, 0.88);
2348
+ display: flex;
2349
+ align-items: center;
2350
+ justify-content: center;
2351
+ cursor: zoom-out;
2352
+ }
2353
+ #imgOverlay[hidden] { display: none; }
2354
+ #imgOverlay img {
2355
+ max-width: 96vw;
2356
+ max-height: 94vh;
2357
+ object-fit: contain;
2358
+ border-radius: 6px;
2359
+ box-shadow: 0 9px 40px rgba(0, 0, 0, 0.55);
2360
+ background: #0b0b0b;
2361
+ }
2362
+ #imgOverlayClose {
2363
+ position: absolute;
2364
+ top: 14px;
2365
+ right: 18px;
2366
+ width: 34px;
2367
+ height: 34px;
2368
+ border: none;
2369
+ border-radius: 8px;
2370
+ background: rgba(255, 255, 255, 0.12);
2371
+ color: #fff;
2372
+ font-size: 17px;
2373
+ cursor: pointer;
2374
+ }
2375
+ #imgOverlayClose:hover { background: rgba(255, 255, 255, 0.26); }
2341
2376
  /* ajan adının yanındaki KOD rozeti (oturum id) */
2342
2377
  .dm-code {
2343
2378
  font-size: 9px; font-weight: 700; letter-spacing: 0.5px;