beast-agent 2.38.0 → 2.38.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agent/engine.js +17 -10
- package/src/main.js +52 -12
- package/src/renderer/index.html +10 -10
- package/src/renderer/renderer.js +121 -14
- package/src/renderer/style.css +24 -0
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -1919,18 +1919,25 @@ class Engine {
|
|
|
1919
1919
|
macro:
|
|
1920
1920
|
'ROL: MAKRO AJANI 🌍 — büyük resim uzmanısın, İşlem AÇMAZSIN (mt5_trade/mt5_pending/mt5_close KULLANMA). Her turda web_search ile güncel makro manşetleri + ekonomik takvim riskleri (faiz, CPI, jeopolitik); DXY/altın/petrol bağıntılarını odak sembollere çevir; sembol başına yön eğilimi + TEMKİN/BEKLE notu ver.',
|
|
1921
1921
|
})[String((session && session.financeRole) || '')] || '';
|
|
1922
|
-
/* ROL → SKILL eşleştirmesi (ayarlar modalı):
|
|
1923
|
-
|
|
1924
|
-
|
|
1922
|
+
/* ROL → SKILL eşleştirmesi (ayarlar modalı): rol başına TEK ve ZORUNLU
|
|
1923
|
+
skill. Seçili SKILL.md'nin TAM GÖVDESİ prompta gömülür — ajan skill
|
|
1924
|
+
aracını çağırmayı atlasa bile prosedür KESİN uygulanır. Eşleştirme
|
|
1925
|
+
boşsa satır eklenmez; ajan skill'i göreve göre kendi seçer (SKILLS). */
|
|
1925
1926
|
const roleSkills = Array.isArray(session && session.financeRoleSkills)
|
|
1926
|
-
? session.financeRoleSkills.map((s) => String(s || '').trim()).filter(Boolean).slice(0,
|
|
1927
|
+
? session.financeRoleSkills.map((s) => String(s || '').trim()).filter(Boolean).slice(0, 1)
|
|
1927
1928
|
: [];
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1929
|
+
let roleSkillBlock = '';
|
|
1930
|
+
if (roleBlock && roleSkills.length) {
|
|
1931
|
+
const roleSkillName = roleSkills[0];
|
|
1932
|
+
const sb = this._skillBody({ name: roleSkillName });
|
|
1933
|
+
roleSkillBlock =
|
|
1934
|
+
` Bu rolün ZORUNLU skill'i: skill("${roleSkillName}")` +
|
|
1935
|
+
(sb && sb.ok && sb.content
|
|
1936
|
+
? ' — aşağıda TAM prosedürü verilmiştir; HER TURDA birebir uygula (ayrıca okumana gerek yok):\n\n' +
|
|
1937
|
+
`===== SKILL: ${roleSkillName} =====\n${sb.content}\n===== /SKILL =====`
|
|
1938
|
+
: ' — ilk turda bu skill\'i mutlaka oku ve prosedürüne birebir uy.');
|
|
1939
|
+
}
|
|
1940
|
+
const roleBlockFull = roleBlock ? roleBlock + roleSkillBlock : '';
|
|
1934
1941
|
/* FİNANS EKİBİ: tüm finance ajanları tek DM grubundadır — sohbet grup
|
|
1935
1942
|
thread'inde toplanır (ayrı ayrı 1:1 thread'lere dağılmaz) */
|
|
1936
1943
|
const finJob = this._bgJobs && this._bgJobs.get(String(session && session.id));
|
package/src/main.js
CHANGED
|
@@ -5144,11 +5144,17 @@ ipcMain.handle('agent:send', (_e, { sessionId, text }) => {
|
|
|
5144
5144
|
const fsid = String(sessionId);
|
|
5145
5145
|
let fsess = engine.cache.get(fsid);
|
|
5146
5146
|
if (!fsess) { try { fsess = engine._load(fsid); } catch {} }
|
|
5147
|
-
if (fsess && !fsess.botId && !fsess.bgJob && financeState.mode
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5147
|
+
if (fsess && !fsess.botId && !fsess.bgJob && financeState.mode) {
|
|
5148
|
+
if (!fsess.finance) {
|
|
5149
|
+
engine.markFinance(fsid, false);
|
|
5150
|
+
finApplyTraderFields(fsess);
|
|
5151
|
+
fsess.financeTrader = false; /* chat copilot'ı — trader değil */
|
|
5152
|
+
engine.cache.set(fsid, fsess);
|
|
5153
|
+
} else if (!financeState.agents.has(fsid)) {
|
|
5154
|
+
/* finance SOHBET botu: ajan talimatı/ayarlar HER MESAJDA tazelenir —
|
|
5155
|
+
asıl bot paralel ajanlardan geri kalmaz */
|
|
5156
|
+
finApplyChatFields(fsess);
|
|
5157
|
+
}
|
|
5152
5158
|
}
|
|
5153
5159
|
}
|
|
5154
5160
|
} catch {}
|
|
@@ -8477,10 +8483,9 @@ function finCfg() {
|
|
|
8477
8483
|
f.analysisAuto = true;
|
|
8478
8484
|
if (!Number.isFinite(Number(f.analysisCount))) f.analysisCount = 2;
|
|
8479
8485
|
f.analysisCount = Math.max(0, Math.min(FIN_ROLES_AUTO.length, Math.round(Number(f.analysisCount) || 0)));
|
|
8480
|
-
/* ROL → SKILL eşleştirmesi:
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
doldurduğu varsayılanlar bir kez temizlenir. */
|
|
8486
|
+
/* ROL → SKILL eşleştirmesi: rol başına TEK skill — modalda zorunlu tek
|
|
8487
|
+
seçim yapılır. Eski çoklu seçimlerden yalnız İLKİ korunur (göç). Skill
|
|
8488
|
+
adları kurulu katalogdan gelir. */
|
|
8484
8489
|
if (f.roleSkillsDefaultsCleared !== true) {
|
|
8485
8490
|
f.roleSkills = {};
|
|
8486
8491
|
f.roleSkillsDefaultsCleared = true;
|
|
@@ -8488,7 +8493,7 @@ function finCfg() {
|
|
|
8488
8493
|
if (!f.roleSkills || typeof f.roleSkills !== 'object') f.roleSkills = {};
|
|
8489
8494
|
for (const d of FIN_ROLES) {
|
|
8490
8495
|
const cur = Array.isArray(f.roleSkills[d.id]) ? f.roleSkills[d.id] : [];
|
|
8491
|
-
f.roleSkills[d.id] = cur.map((s) => String(s || '').trim()).filter(Boolean).slice(0,
|
|
8496
|
+
f.roleSkills[d.id] = cur.map((s) => String(s || '').trim()).filter(Boolean).slice(0, 1);
|
|
8492
8497
|
}
|
|
8493
8498
|
if (!Number(f.intervalSec)) f.intervalSec = 120;
|
|
8494
8499
|
if (!Number(f.maxLot)) f.maxLot = 0.1;
|
|
@@ -9354,6 +9359,19 @@ function finApplyTraderFields(s, symbolsOverride, role) {
|
|
|
9354
9359
|
engine.cache.set(String(s.id), s);
|
|
9355
9360
|
}
|
|
9356
9361
|
|
|
9362
|
+
/* Finance SOHBET oturumu (chat copilot) alanlarını tazele — trader bayrağı
|
|
9363
|
+
KORUNUR. Strateji/sembol/limit/rol-skill değişiklikleri "asıl bot"a da
|
|
9364
|
+
ulaşsın; paralel ajanlar tur başında zaten tazeleniyor. */
|
|
9365
|
+
function finApplyChatFields(s) {
|
|
9366
|
+
if (!s) return;
|
|
9367
|
+
const wasTrader = !!s.financeTrader;
|
|
9368
|
+
finApplyTraderFields(s);
|
|
9369
|
+
s.financeTrader = wasTrader;
|
|
9370
|
+
if (!wasTrader) s.financeAuto = false; /* sohbet copilot'ı — otonom trader değil */
|
|
9371
|
+
engine.cache.set(String(s.id), s);
|
|
9372
|
+
return s;
|
|
9373
|
+
}
|
|
9374
|
+
|
|
9357
9375
|
/* Yeni finance ajanı aç (ana trader YA DA sembol işçisi YA DA analiz ekibi rolü) */
|
|
9358
9376
|
function finAgentCreate(symbols, isMain, role) {
|
|
9359
9377
|
const f = finCfg();
|
|
@@ -9713,12 +9731,22 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
9713
9731
|
if (p.analysisTeam !== undefined) f.analysisTeam = finRolesValid(p.analysisTeam);
|
|
9714
9732
|
if (p.analysisAuto !== undefined) f.analysisAuto = !!p.analysisAuto;
|
|
9715
9733
|
if (p.analysisCount !== undefined) f.analysisCount = Math.max(0, Math.min(FIN_ROLES_AUTO.length, Math.round(Number(p.analysisCount) || 0)));
|
|
9716
|
-
/* ROL → SKILL eşleştirmesi güncellemesi (modal) */
|
|
9734
|
+
/* ROL → SKILL eşleştirmesi güncellemesi (modal): rol başına TEK skill */
|
|
9717
9735
|
if (p.roleSkills !== undefined && p.roleSkills && typeof p.roleSkills === 'object') {
|
|
9718
9736
|
for (const d of FIN_ROLES) {
|
|
9719
9737
|
if (p.roleSkills[d.id] === undefined) continue;
|
|
9720
9738
|
f.roleSkills[d.id] = (Array.isArray(p.roleSkills[d.id]) ? p.roleSkills[d.id] : [])
|
|
9721
|
-
.map((s) => String(s || '').trim()).filter(Boolean).slice(0,
|
|
9739
|
+
.map((s) => String(s || '').trim()).filter(Boolean).slice(0, 1);
|
|
9740
|
+
}
|
|
9741
|
+
/* koşan rol ajanlarını da tazele — seçim sonraki turdan itibaren sistem
|
|
9742
|
+
promptuna girer (ajanı durdurmaya gerek yok) */
|
|
9743
|
+
if (financeState.agents && engine) {
|
|
9744
|
+
for (const [sid, a] of financeState.agents) {
|
|
9745
|
+
try {
|
|
9746
|
+
const ss = engine.cache.get(sid);
|
|
9747
|
+
if (ss && a && a.role) finApplyTraderFields(ss, a.symbols, a.role);
|
|
9748
|
+
} catch {}
|
|
9749
|
+
}
|
|
9722
9750
|
}
|
|
9723
9751
|
}
|
|
9724
9752
|
if (p.pythonPath !== undefined) f.pythonPath = String(p.pythonPath || '').trim();
|
|
@@ -9737,6 +9765,18 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
9737
9765
|
if (ts) finApplyTraderFields(ts);
|
|
9738
9766
|
} catch {}
|
|
9739
9767
|
}
|
|
9768
|
+
/* finance SOHBET oturumları: strateji/sembol/limit/rol-skill değişince ANINDA
|
|
9769
|
+
tazelenir — "asıl bot" (Beast Finance sohbeti) de aynı ajan talimatını görür */
|
|
9770
|
+
if (engine) {
|
|
9771
|
+
try {
|
|
9772
|
+
for (const [ssid, ss] of engine.cache) {
|
|
9773
|
+
if (!ss || !ss.finance || ss.botId || ss.bgJob) continue;
|
|
9774
|
+
if (financeState.agents.has(String(ssid))) continue; /* ajanlar tur başında tazelenir */
|
|
9775
|
+
if (ssid === String(financeState.traderSid || '')) continue;
|
|
9776
|
+
finApplyChatFields(ss);
|
|
9777
|
+
}
|
|
9778
|
+
} catch {}
|
|
9779
|
+
}
|
|
9740
9780
|
return { ok: true, cfg: f };
|
|
9741
9781
|
});
|
|
9742
9782
|
|
package/src/renderer/index.html
CHANGED
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
<button id="finSettings" title="Görünüm ayarları — bölümleri göster/gizle">⚙</button>
|
|
154
154
|
</div>
|
|
155
155
|
<div id="finBody">
|
|
156
|
-
<div class="fin-card">
|
|
156
|
+
<div class="fin-card" data-fin-card="account">
|
|
157
157
|
<div class="fin-card-title">HESAP</div>
|
|
158
158
|
<div class="fin-grid">
|
|
159
159
|
<div class="fin-kv"><span>Bakiye</span><b id="finBalance">—</b></div>
|
|
@@ -162,8 +162,8 @@
|
|
|
162
162
|
<div class="fin-kv"><span>Serbest Marj</span><b id="finFree">—</b></div>
|
|
163
163
|
</div>
|
|
164
164
|
</div>
|
|
165
|
-
<div class="fin-card fin-card-trader">
|
|
166
|
-
<div class="fin-card-title">TRADE AJANI <span id="finTraderDot" class="fin-dot off" title="Trader durumu"></span><span class="file-spacer"></span><button id="finModelRefreshBtn" title="Modelleri yeniden çek — eksik/güncel modelleri tazeler">⟳</button><button id="finSkillsBtn" title="Rol → skill eşleştirmesi —
|
|
165
|
+
<div class="fin-card fin-card-trader" data-fin-card="trader">
|
|
166
|
+
<div class="fin-card-title">TRADE AJANI <span id="finTraderDot" class="fin-dot off" title="Trader durumu"></span><span class="file-spacer"></span><button id="finModelRefreshBtn" title="Modelleri yeniden çek — eksik/güncel modelleri tazeler">⟳</button><button id="finSkillsBtn" title="Rol → skill eşleştirmesi — her role ZORUNLU tek skill (ajan her turda onu okur)">⚙</button></div>
|
|
167
167
|
<div class="fin-field">
|
|
168
168
|
<span>Model</span>
|
|
169
169
|
<select id="finTraderModel" title="Trader ajanının modeli — farklı API/model seçilebilir"></select>
|
|
@@ -195,7 +195,7 @@
|
|
|
195
195
|
</div>
|
|
196
196
|
<div id="finTraderStatus">Kapalı</div>
|
|
197
197
|
</div>
|
|
198
|
-
<div class="fin-card" id="finRiskCard" hidden>
|
|
198
|
+
<div class="fin-card" id="finRiskCard" data-fin-card="risk" hidden>
|
|
199
199
|
<div class="fin-card-title">RİSK OTOMASYONU <span id="finWatchDot" class="fin-dot off" title="Koruma döngüsü"></span><span id="finWatchInfo" class="fin-hint"></span></div>
|
|
200
200
|
<div class="fin-field fin-check">
|
|
201
201
|
<label title="5 saniyelik döngü: +R'da SL'yi başabaşa çeker, trailing uygular, kısmi TP alır — ajan turunu beklemez">
|
|
@@ -226,7 +226,7 @@
|
|
|
226
226
|
</div>
|
|
227
227
|
<div id="finAlertList" class="fin-alerts"><div class="fin-empty">Aktif fiyat alarmı yok</div></div>
|
|
228
228
|
</div>
|
|
229
|
-
<div class="fin-card" id="finNotifyCard" hidden>
|
|
229
|
+
<div class="fin-card" id="finNotifyCard" data-fin-card="notify" hidden>
|
|
230
230
|
<div class="fin-card-title">BİLDİRİM <span class="fin-hint">— kanal seçimi ve olay filtresi</span></div>
|
|
231
231
|
<div class="fin-field">
|
|
232
232
|
<span>Bildirim kanalı</span>
|
|
@@ -244,7 +244,7 @@
|
|
|
244
244
|
</label>
|
|
245
245
|
</div>
|
|
246
246
|
</div>
|
|
247
|
-
<div class="fin-card" id="finPerfCard" hidden>
|
|
247
|
+
<div class="fin-card" id="finPerfCard" data-fin-card="perf" hidden>
|
|
248
248
|
<div class="fin-card-title">PERFORMANS <span id="finStatsInfo" class="fin-hint"></span><span class="file-spacer"></span><button id="finReportBtn" title="Haftalık rapor üret — istatistik + işlem günlüğü + ajan değerlendirmesi">📄 Rapor</button></div>
|
|
249
249
|
<div id="finEquityChart" class="fin-equity"></div>
|
|
250
250
|
<div class="fin-grid">
|
|
@@ -256,13 +256,13 @@
|
|
|
256
256
|
<div class="fin-kv"><span>İşlem (90g)</span><b id="finStatTrades">—</b></div>
|
|
257
257
|
</div>
|
|
258
258
|
</div>
|
|
259
|
-
<div class="fin-card">
|
|
259
|
+
<div class="fin-card" data-fin-card="positions">
|
|
260
260
|
<div class="fin-card-title">POZİSYONLAR <span id="finPosCount"></span></div>
|
|
261
261
|
<div id="finPosList"><div class="fin-empty">Açık pozisyon yok</div></div>
|
|
262
262
|
<div class="fin-sect-title">BEKLEYEN EMİRLER <span id="finOrdCount"></span></div>
|
|
263
263
|
<div id="finOrdList"><div class="fin-empty">Bekleyen emir yok</div></div>
|
|
264
264
|
</div>
|
|
265
|
-
<div class="fin-card">
|
|
265
|
+
<div class="fin-card" data-fin-card="market">
|
|
266
266
|
<div class="fin-card-title">
|
|
267
267
|
PİYASA
|
|
268
268
|
<span class="file-spacer"></span>
|
|
@@ -271,7 +271,7 @@
|
|
|
271
271
|
</div>
|
|
272
272
|
<div id="finSymList"><div class="fin-empty">—</div></div>
|
|
273
273
|
</div>
|
|
274
|
-
<div class="fin-card fin-grow" id="finFlowCard" hidden>
|
|
274
|
+
<div class="fin-card fin-grow" id="finFlowCard" data-fin-card="flow" hidden>
|
|
275
275
|
<div class="fin-card-title">AKIŞ</div>
|
|
276
276
|
<div id="finLog"></div>
|
|
277
277
|
</div>
|
|
@@ -761,7 +761,7 @@
|
|
|
761
761
|
</div>
|
|
762
762
|
<div class="mini-body">
|
|
763
763
|
<div id="finSkillsList" class="mini-list"></div>
|
|
764
|
-
<div class="fin-sym-hint"
|
|
764
|
+
<div class="fin-sym-hint">Her rol için TEK skill ZORUNLUDUR — aynı role birden fazla skill seçilemez. Seçili skill kaldırılamaz; değiştirmek için başka bir skill'e tıkla. Seçim, koşan ajanlar dahil sonraki turdan itibaren rol ajanının sistem promptuna zorunlu skill olarak girer.</div>
|
|
765
765
|
</div>
|
|
766
766
|
</div>
|
|
767
767
|
</div>
|
package/src/renderer/renderer.js
CHANGED
|
@@ -162,6 +162,7 @@ const els = {
|
|
|
162
162
|
stSplit: $('#stSplit'),
|
|
163
163
|
finBtn: $('#finBtn'),
|
|
164
164
|
finPanel: $('#finPanel'),
|
|
165
|
+
finBody: $('#finBody'),
|
|
165
166
|
finDot: $('#finDot'),
|
|
166
167
|
finTerm: $('#finTerm'),
|
|
167
168
|
finReconnect: $('#finReconnect'),
|
|
@@ -4949,7 +4950,9 @@ async function refreshAgentsPane() {
|
|
|
4949
4950
|
kullanıcı çalışırken elle kapatırsa ezilmez; dahili tarayıcı ve IDE modu
|
|
4950
4951
|
açıkken karışılmaz — orada railBtn ile elle açılır)
|
|
4951
4952
|
- Tüm işler bitince otomatik KAPANIR (yalnız açılışı biz yaptıysak;
|
|
4952
|
-
kullanıcı elle açtıysa açık kalır)
|
|
4953
|
+
kullanıcı elle açtıysa açık kalır). BEAST FINANCE istisnası: trade ajanı
|
|
4954
|
+
durdurulunca konsol KAPANMAZ — duran ajanların raporları/geçmişi görünür
|
|
4955
|
+
kalsın; kullanıcı isterse railBtn ile kendisi kapatır. */
|
|
4953
4956
|
function maybeAutoOpenRail() {
|
|
4954
4957
|
const hasRunning = agentState.jobs.some((j) => j.status === 'running');
|
|
4955
4958
|
const hidden = document.body.classList.contains('rail-hidden');
|
|
@@ -4959,7 +4962,8 @@ function maybeAutoOpenRail() {
|
|
|
4959
4962
|
railPrefBeforeBrowser = true;
|
|
4960
4963
|
}
|
|
4961
4964
|
if (!hasRunning && agentState.autoOpened && !hidden &&
|
|
4962
|
-
!railManualOpen && !document.body.classList.contains('browser-open')
|
|
4965
|
+
!railManualOpen && !document.body.classList.contains('browser-open') &&
|
|
4966
|
+
!financeModeOn()) {
|
|
4963
4967
|
toggleRail(true);
|
|
4964
4968
|
railManualOpen = false;
|
|
4965
4969
|
}
|
|
@@ -9769,6 +9773,100 @@ if (els.finShowPerf) els.finShowPerf.addEventListener('change', () => finViewTog
|
|
|
9769
9773
|
if (els.finShowFlow) els.finShowFlow.addEventListener('change', () => finViewToggle('flow', els.finShowFlow.checked));
|
|
9770
9774
|
finViewApply();
|
|
9771
9775
|
|
|
9776
|
+
/* ---------- BÖLÜM SIRALAMA (sürükle-bırak) ----------
|
|
9777
|
+
Kartların sol üstündeki tutamak (⠿) ile bölümlerin yerini değiştir;
|
|
9778
|
+
sıra localStorage'a yazılır — panel kapatılıp açılsa da korunur. */
|
|
9779
|
+
const finOrderKey = 'beast.financeOrder';
|
|
9780
|
+
let finDragCard = null;
|
|
9781
|
+
|
|
9782
|
+
function finOrderLoad() {
|
|
9783
|
+
try {
|
|
9784
|
+
const v = JSON.parse(localStorage.getItem(finOrderKey) || 'null');
|
|
9785
|
+
return Array.isArray(v) ? v.map(String).filter(Boolean) : [];
|
|
9786
|
+
} catch { return []; }
|
|
9787
|
+
}
|
|
9788
|
+
|
|
9789
|
+
function finOrderApply() {
|
|
9790
|
+
const body = els.finBody;
|
|
9791
|
+
if (!body) return;
|
|
9792
|
+
const cards = [...body.querySelectorAll(':scope > .fin-card')];
|
|
9793
|
+
const byKey = new Map();
|
|
9794
|
+
for (const c of cards) if (c.dataset.finCard) byKey.set(c.dataset.finCard, c);
|
|
9795
|
+
for (const key of finOrderLoad()) {
|
|
9796
|
+
const c = byKey.get(key);
|
|
9797
|
+
if (c) { body.appendChild(c); byKey.delete(key); }
|
|
9798
|
+
}
|
|
9799
|
+
for (const c of byKey.values()) body.appendChild(c); /* yeni kartlar sona */
|
|
9800
|
+
}
|
|
9801
|
+
|
|
9802
|
+
function finOrderSave() {
|
|
9803
|
+
const body = els.finBody;
|
|
9804
|
+
if (!body) return;
|
|
9805
|
+
const keys = [...body.querySelectorAll(':scope > .fin-card')]
|
|
9806
|
+
.map((c) => c.dataset.finCard)
|
|
9807
|
+
.filter(Boolean);
|
|
9808
|
+
try { localStorage.setItem(finOrderKey, JSON.stringify(keys)); } catch {}
|
|
9809
|
+
}
|
|
9810
|
+
|
|
9811
|
+
function finCardsInit() {
|
|
9812
|
+
const body = els.finBody;
|
|
9813
|
+
if (!body) return;
|
|
9814
|
+
finOrderApply();
|
|
9815
|
+
const cards = [...body.querySelectorAll(':scope > .fin-card')];
|
|
9816
|
+
for (const card of cards) {
|
|
9817
|
+
const title = card.querySelector('.fin-card-title');
|
|
9818
|
+
if (!title) continue;
|
|
9819
|
+
const grip = document.createElement('span');
|
|
9820
|
+
grip.className = 'fin-grip';
|
|
9821
|
+
grip.title = 'Sürükle-bırak: bölümün yerini değiştir';
|
|
9822
|
+
grip.textContent = '⠿';
|
|
9823
|
+
title.prepend(grip);
|
|
9824
|
+
grip.addEventListener('mousedown', () => { card.draggable = true; });
|
|
9825
|
+
card.addEventListener('dragstart', (e) => {
|
|
9826
|
+
finDragCard = card;
|
|
9827
|
+
card.classList.add('fin-dragging');
|
|
9828
|
+
try {
|
|
9829
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
9830
|
+
e.dataTransfer.setData('text/plain', card.dataset.finCard || '');
|
|
9831
|
+
} catch {}
|
|
9832
|
+
});
|
|
9833
|
+
card.addEventListener('dragend', () => {
|
|
9834
|
+
finDragCard = null;
|
|
9835
|
+
card.draggable = false;
|
|
9836
|
+
card.classList.remove('fin-dragging');
|
|
9837
|
+
body.querySelectorAll('.fin-drop-top, .fin-drop-bottom')
|
|
9838
|
+
.forEach((x) => x.classList.remove('fin-drop-top', 'fin-drop-bottom'));
|
|
9839
|
+
finOrderSave();
|
|
9840
|
+
});
|
|
9841
|
+
card.addEventListener('dragover', (e) => {
|
|
9842
|
+
if (!finDragCard || finDragCard === card) return;
|
|
9843
|
+
e.preventDefault();
|
|
9844
|
+
try { e.dataTransfer.dropEffect = 'move'; } catch {}
|
|
9845
|
+
const r = card.getBoundingClientRect();
|
|
9846
|
+
const after = e.clientY > r.top + r.height / 2;
|
|
9847
|
+
body.querySelectorAll('.fin-drop-top, .fin-drop-bottom')
|
|
9848
|
+
.forEach((x) => x.classList.remove('fin-drop-top', 'fin-drop-bottom'));
|
|
9849
|
+
card.classList.add(after ? 'fin-drop-bottom' : 'fin-drop-top');
|
|
9850
|
+
});
|
|
9851
|
+
card.addEventListener('dragleave', () => card.classList.remove('fin-drop-top', 'fin-drop-bottom'));
|
|
9852
|
+
card.addEventListener('drop', (e) => {
|
|
9853
|
+
e.preventDefault();
|
|
9854
|
+
if (!finDragCard || finDragCard === card) return;
|
|
9855
|
+
const r = card.getBoundingClientRect();
|
|
9856
|
+
const after = e.clientY > r.top + r.height / 2;
|
|
9857
|
+
if (after) card.after(finDragCard);
|
|
9858
|
+
else card.before(finDragCard);
|
|
9859
|
+
card.classList.remove('fin-drop-top', 'fin-drop-bottom');
|
|
9860
|
+
finOrderSave();
|
|
9861
|
+
});
|
|
9862
|
+
}
|
|
9863
|
+
document.addEventListener('mouseup', () => {
|
|
9864
|
+
if (finDragCard) return;
|
|
9865
|
+
body.querySelectorAll('.fin-card[draggable="true"]').forEach((c) => { c.draggable = false; });
|
|
9866
|
+
});
|
|
9867
|
+
}
|
|
9868
|
+
finCardsInit();
|
|
9869
|
+
|
|
9772
9870
|
if (els.finInterval) els.finInterval.addEventListener('change', () => {
|
|
9773
9871
|
const v = Math.max(30, Math.min(3600, Math.round(Number(els.finInterval.value) || 120)));
|
|
9774
9872
|
els.finInterval.value = v;
|
|
@@ -10076,31 +10174,40 @@ function finRenderSkills() {
|
|
|
10076
10174
|
head.innerHTML =
|
|
10077
10175
|
'<b>' + escapeHtml(String(r.label || r.id)) + '</b>' +
|
|
10078
10176
|
'<span>' + escapeHtml(String(r.desc || '').slice(0, 110)) + '</span>';
|
|
10177
|
+
const cur = Array.isArray(map[r.id]) ? map[r.id].map((x) => String(x || '').trim()).filter(Boolean) : [];
|
|
10178
|
+
const active = cur.length ? cur[0] : '';
|
|
10179
|
+
if (!active) {
|
|
10180
|
+
const warn = document.createElement('span');
|
|
10181
|
+
warn.className = 'fin-skill-req';
|
|
10182
|
+
warn.textContent = 'ZORUNLU — skill seçilmedi';
|
|
10183
|
+
head.appendChild(warn);
|
|
10184
|
+
}
|
|
10079
10185
|
row.appendChild(head);
|
|
10080
10186
|
const chips = document.createElement('div');
|
|
10081
10187
|
chips.className = 'fin-skill-chips';
|
|
10082
10188
|
for (const s of finSkillNames) {
|
|
10083
|
-
const
|
|
10189
|
+
const on = active === s.name;
|
|
10084
10190
|
const chip = document.createElement('label');
|
|
10085
|
-
chip.className = 'fin-skill-chip' + (
|
|
10191
|
+
chip.className = 'fin-skill-chip' + (on ? ' on' : '');
|
|
10086
10192
|
chip.title = s.description || s.name;
|
|
10087
10193
|
const cb = document.createElement('input');
|
|
10088
|
-
cb.type = '
|
|
10089
|
-
cb.
|
|
10194
|
+
cb.type = 'radio';
|
|
10195
|
+
cb.name = 'fin-skill-role-' + r.id;
|
|
10196
|
+
cb.checked = on;
|
|
10197
|
+
/* TEK + ZORUNLU: yeni skill seçilir; mevcut seçim kaldırılamaz */
|
|
10090
10198
|
cb.addEventListener('change', () => {
|
|
10091
|
-
|
|
10092
|
-
|
|
10093
|
-
: [];
|
|
10094
|
-
const set = new Set(liveBase);
|
|
10095
|
-
if (cb.checked) set.add(s.name); else set.delete(s.name);
|
|
10096
|
-
const next = [...set];
|
|
10199
|
+
if (!cb.checked) return;
|
|
10200
|
+
const next = [s.name];
|
|
10097
10201
|
/* yerel önbelleğe anında yaz — 3 sn'lik snapshot eski değeri geri getirmesin */
|
|
10098
10202
|
finCfgCache = finCfgCache || {};
|
|
10099
10203
|
finCfgCache.roleSkills = finCfgCache.roleSkills || {};
|
|
10100
10204
|
finCfgCache.roleSkills[r.id] = next;
|
|
10101
|
-
chip.classList.
|
|
10205
|
+
chips.querySelectorAll('.fin-skill-chip').forEach((el) => el.classList.remove('on'));
|
|
10206
|
+
chip.classList.add('on');
|
|
10207
|
+
const w = head.querySelector('.fin-skill-req');
|
|
10208
|
+
if (w) w.remove();
|
|
10102
10209
|
finSaveCfg({ roleSkills: { [r.id]: next } });
|
|
10103
|
-
toast((r.label || r.id) + ' → ' +
|
|
10210
|
+
toast((r.label || r.id) + ' → ' + s.name + ' (zorunlu tek skill)');
|
|
10104
10211
|
});
|
|
10105
10212
|
chip.appendChild(cb);
|
|
10106
10213
|
chip.appendChild(document.createTextNode(s.name));
|
package/src/renderer/style.css
CHANGED
|
@@ -4816,6 +4816,22 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
|
|
|
4816
4816
|
align-items: center;
|
|
4817
4817
|
gap: 6px;
|
|
4818
4818
|
}
|
|
4819
|
+
.fin-grip {
|
|
4820
|
+
flex: none;
|
|
4821
|
+
cursor: grab;
|
|
4822
|
+
color: var(--muted);
|
|
4823
|
+
font-size: 12px;
|
|
4824
|
+
line-height: 1;
|
|
4825
|
+
letter-spacing: -1px;
|
|
4826
|
+
opacity: 0.7;
|
|
4827
|
+
user-select: none;
|
|
4828
|
+
-webkit-user-select: none;
|
|
4829
|
+
}
|
|
4830
|
+
.fin-grip:hover { color: var(--accent); opacity: 1; }
|
|
4831
|
+
.fin-grip:active { cursor: grabbing; }
|
|
4832
|
+
.fin-card.fin-dragging { opacity: 0.45; border-style: dashed; }
|
|
4833
|
+
.fin-card.fin-drop-top { box-shadow: 0 -3px 0 0 var(--accent); }
|
|
4834
|
+
.fin-card.fin-drop-bottom { box-shadow: 0 3px 0 0 var(--accent); }
|
|
4819
4835
|
|
|
4820
4836
|
.fin-grid {
|
|
4821
4837
|
display: grid;
|
|
@@ -5209,6 +5225,14 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
|
|
|
5209
5225
|
.fin-skill-role-head { display: flex; flex-direction: column; gap: 1px; margin-bottom: 7px; }
|
|
5210
5226
|
.fin-skill-role-head b { font-size: 11.5px; color: var(--text); }
|
|
5211
5227
|
.fin-skill-role-head span { font-size: 10px; color: var(--muted); }
|
|
5228
|
+
.fin-skill-req {
|
|
5229
|
+
align-self: flex-start;
|
|
5230
|
+
margin-top: 2px;
|
|
5231
|
+
font-size: 9.5px;
|
|
5232
|
+
font-weight: 800;
|
|
5233
|
+
letter-spacing: 0.5px;
|
|
5234
|
+
color: #ef4444;
|
|
5235
|
+
}
|
|
5212
5236
|
.fin-skill-chips { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
5213
5237
|
.fin-skill-chip {
|
|
5214
5238
|
display: inline-flex;
|