beast-agent 2.31.0 → 2.32.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 +1 -1
- package/src/main.js +57 -1
- package/src/preload.js +5 -0
- package/src/renderer/i18n.js +90 -0
- package/src/renderer/index.html +40 -0
- package/src/renderer/renderer.js +293 -11
- package/src/renderer/style.css +13 -0
package/package.json
CHANGED
package/src/main.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { app, BrowserWindow, WebContentsView, ipcMain, shell, dialog, Tray, Menu, nativeImage, desktopCapturer, session, net: electronNet, clipboard } = require('electron');
|
|
3
|
+
const { app, BrowserWindow, WebContentsView, ipcMain, shell, dialog, Tray, Menu, nativeImage, desktopCapturer, session, net: electronNet, clipboard, Notification } = require('electron');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const http = require('http');
|
|
@@ -272,6 +272,8 @@ try {
|
|
|
272
272
|
try { nodemailer = require('nodemailer'); } catch {}
|
|
273
273
|
|
|
274
274
|
const APP_DIR = path.join(app.getPath('appData'), 'beast');
|
|
275
|
+
/* Windows toast bildirimleri için AppUserModelID erken ayarlanmalı */
|
|
276
|
+
try { app.setAppUserModelId('com.quantumalgo.beastagent'); } catch {}
|
|
275
277
|
/* mem0-native embedding modeli whisper ile AYNI cache'i kullanır (src/agent/mem0.js okur) */
|
|
276
278
|
process.env.BEAST_MODELS_DIR = path.join(APP_DIR, 'models');
|
|
277
279
|
|
|
@@ -301,6 +303,8 @@ for (const d of [APP_DIR, SESSIONS_DIR]) fs.mkdirSync(d, { recursive: true });
|
|
|
301
303
|
|
|
302
304
|
let win = null;
|
|
303
305
|
let engine = null;
|
|
306
|
+
/* Paralel ajan durum takibi: bitişte BİR kez toast bildirimi (spam yok) */
|
|
307
|
+
const bgNotifSeen = new Map(); // sid -> son görülen durum
|
|
304
308
|
let settings = loadSettings();
|
|
305
309
|
ensureBeastCode();
|
|
306
310
|
startHealthServer(); /* splash/boot aşamasından itibaren /health ayakta */
|
|
@@ -3232,6 +3236,17 @@ function reloadBackend() {
|
|
|
3232
3236
|
},
|
|
3233
3237
|
emit: (ev) => {
|
|
3234
3238
|
if (win && !win.isDestroyed()) win.webContents.send('agent:event', ev);
|
|
3239
|
+
/* PARALEL AJAN BİTİŞİ: running → done/error/aborted geçişinde tek toast */
|
|
3240
|
+
if (ev.type === 'agents' && Array.isArray(ev.jobs)) {
|
|
3241
|
+
for (const j of ev.jobs) {
|
|
3242
|
+
const prev = bgNotifSeen.get(j.id);
|
|
3243
|
+
bgNotifSeen.set(j.id, j.status);
|
|
3244
|
+
if (prev === 'running' && j.status && j.status !== 'running' && j.status !== 'queued' && !j.continuous) {
|
|
3245
|
+
const durum = j.status === 'done' ? 'tamamlandı' : j.status === 'error' ? 'HATA ile bitti' : 'iptal edildi';
|
|
3246
|
+
toastNotify(`Ajan: ${j.title || j.code || 'görev'}`, durum, 'agents');
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3235
3250
|
/* opencode permission (BC): ask kanalı UI'a zaten yukarıda düştü — WA
|
|
3236
3251
|
sahibine de tek satır bildir gitsin (/approve · /approve always · /deny) */
|
|
3237
3252
|
if (ev.type === 'permission.asked' && ev.request && wa && wa.connected) {
|
|
@@ -3741,6 +3756,30 @@ function showWin() {
|
|
|
3741
3756
|
}
|
|
3742
3757
|
}
|
|
3743
3758
|
|
|
3759
|
+
/* ---------- Windows toast bildirimi ----------
|
|
3760
|
+
watcher/cron/paralel ajan bitişinde sistem bildirimi; tıklanınca pencere
|
|
3761
|
+
açılır ve renderer'a notify-click eventi gider (ilgili panel açılır).
|
|
3762
|
+
Ayarlardan kapatılabilir: settings.notifyToast === false */
|
|
3763
|
+
function toastNotify(title, body, target) {
|
|
3764
|
+
try {
|
|
3765
|
+
if (settings.notifyToast === false) return;
|
|
3766
|
+
if (typeof Notification === 'undefined' || !Notification.isSupported()) return;
|
|
3767
|
+
const n = new Notification({
|
|
3768
|
+
title: String(title || 'Beast Agent').slice(0, 120),
|
|
3769
|
+
body: String(body || '').slice(0, 240),
|
|
3770
|
+
icon: path.join(__dirname, '..', 'assets', 'tray.png'),
|
|
3771
|
+
silent: false,
|
|
3772
|
+
});
|
|
3773
|
+
n.on('click', () => {
|
|
3774
|
+
showWin();
|
|
3775
|
+
if (win && !win.isDestroyed()) {
|
|
3776
|
+
win.webContents.send('agent:event', { type: 'notify-click', target: String(target || '') });
|
|
3777
|
+
}
|
|
3778
|
+
});
|
|
3779
|
+
n.show();
|
|
3780
|
+
} catch {}
|
|
3781
|
+
}
|
|
3782
|
+
|
|
3744
3783
|
/* ---------------- dahili tarayıcı ---------------- */
|
|
3745
3784
|
|
|
3746
3785
|
const BROWSER_TOOLBAR_H = 46;
|
|
@@ -6875,6 +6914,11 @@ function cronFire(job) {
|
|
|
6875
6914
|
const sent = engine.send(sid, {
|
|
6876
6915
|
text: `[cron: ${job.name}]\n${job.prompt}`,
|
|
6877
6916
|
});
|
|
6917
|
+
toastNotify(
|
|
6918
|
+
`Cron: ${job.name}`,
|
|
6919
|
+
isReminderJob(job) ? 'Hatırlatma zamanı geldi' : String(job.prompt || '').slice(0, 160),
|
|
6920
|
+
'cron'
|
|
6921
|
+
);
|
|
6878
6922
|
if (!sent) cronAnswerPendingDrop(sid); // gönderilemedi — bayat bekleme bırakma
|
|
6879
6923
|
} catch {}
|
|
6880
6924
|
cronEmit();
|
|
@@ -6892,6 +6936,7 @@ function watcherFire(w, value) {
|
|
|
6892
6936
|
w.op === 'changed'
|
|
6893
6937
|
? 'izlenen değer değişti'
|
|
6894
6938
|
: `kural sağlandı (son değer ${value}, koşul ${w.op} ${w.value ?? ''})`;
|
|
6939
|
+
toastNotify(`İzleyici: ${w.name}`, target, 'watchers');
|
|
6895
6940
|
engine.send(sid, {
|
|
6896
6941
|
text:
|
|
6897
6942
|
`[IZLEYICI: ${w.name}] ${target}. ` +
|
|
@@ -7426,6 +7471,13 @@ ipcMain.handle('cron:runNow', (_e, id) => {
|
|
|
7426
7471
|
cronEmit();
|
|
7427
7472
|
return r;
|
|
7428
7473
|
});
|
|
7474
|
+
/* Masaüstü (toast) bildirimleri aç/kapa */
|
|
7475
|
+
ipcMain.handle('notify:get', () => ({ toast: settings.notifyToast !== false }));
|
|
7476
|
+
ipcMain.handle('notify:set', (_e, on) => {
|
|
7477
|
+
settings.notifyToast = !!on;
|
|
7478
|
+
saveSettings();
|
|
7479
|
+
return { toast: settings.notifyToast };
|
|
7480
|
+
});
|
|
7429
7481
|
|
|
7430
7482
|
/* ---------------- tarayıcı IPC ---------------- */
|
|
7431
7483
|
ipcMain.handle('browser:toggle', () => {
|
|
@@ -10902,6 +10954,10 @@ ipcMain.handle('email:set', (_e, cfg) => {
|
|
|
10902
10954
|
saveSettings();
|
|
10903
10955
|
return { ...settings.email, pass: settings.email.pass ? '***' : '' };
|
|
10904
10956
|
});
|
|
10957
|
+
/* E-posta paneli: gelen kutusu + okuma + gönderim (engine köprüsüyle aynı fonksiyonlar) */
|
|
10958
|
+
ipcMain.handle('email:list', (_e, opts) => emailList(opts || {}));
|
|
10959
|
+
ipcMain.handle('email:read', (_e, uid) => emailRead(uid));
|
|
10960
|
+
ipcMain.handle('email:send', (_e, msg) => emailSend(msg || {}));
|
|
10905
10961
|
|
|
10906
10962
|
/* ---------- ekran görüntüsü ---------- */
|
|
10907
10963
|
|
package/src/preload.js
CHANGED
|
@@ -147,6 +147,8 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
147
147
|
cronDelete: (id) => ipcRenderer.invoke('cron:delete', id),
|
|
148
148
|
cronToggle: (id) => ipcRenderer.invoke('cron:toggle', id),
|
|
149
149
|
cronRunNow: (id) => ipcRenderer.invoke('cron:runNow', id),
|
|
150
|
+
notifyGet: () => ipcRenderer.invoke('notify:get'),
|
|
151
|
+
notifySet: (on) => ipcRenderer.invoke('notify:set', on),
|
|
150
152
|
|
|
151
153
|
watchersList: () => ipcRenderer.invoke('watchers:list'),
|
|
152
154
|
|
|
@@ -222,6 +224,9 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
222
224
|
whereWasISet: (cfg) => ipcRenderer.invoke('wherewasi:set', cfg),
|
|
223
225
|
getEmail: () => ipcRenderer.invoke('email:get'),
|
|
224
226
|
setEmail: (cfg) => ipcRenderer.invoke('email:set', cfg),
|
|
227
|
+
emailList: (opts) => ipcRenderer.invoke('email:list', opts),
|
|
228
|
+
emailRead: (uid) => ipcRenderer.invoke('email:read', uid),
|
|
229
|
+
emailSend: (msg) => ipcRenderer.invoke('email:send', msg),
|
|
225
230
|
captureScreen: () => ipcRenderer.invoke('screen:capture'),
|
|
226
231
|
waListSessions: () => ipcRenderer.invoke('wa:sessions'),
|
|
227
232
|
botsList: () => ipcRenderer.invoke('bots:list'),
|
package/src/renderer/i18n.js
CHANGED
|
@@ -166,6 +166,7 @@
|
|
|
166
166
|
em_mem_clear: 'Hafızayı Temizle',
|
|
167
167
|
em_mem_cleared: 'Empati hafızası temizlendi',
|
|
168
168
|
tab_cron: 'Cron',
|
|
169
|
+
tab_agenda: 'Ajanda',
|
|
169
170
|
tab_usage: 'Maliyet',
|
|
170
171
|
tab_logs: 'Log',
|
|
171
172
|
tab_dash: 'Dashboard',
|
|
@@ -687,6 +688,50 @@
|
|
|
687
688
|
wa_queue_info: 'Mesaj kuyruğu: ${n} bekleyen',
|
|
688
689
|
wa_queue_failed: '${n} gönderilemedi',
|
|
689
690
|
wa_queue_empty_ok: 'Kuyruk temiz',
|
|
691
|
+
/* Cron düzenleme */
|
|
692
|
+
cr_update: 'Güncelle',
|
|
693
|
+
cr_cancel_edit: 'Vazgeç',
|
|
694
|
+
cr_edit_hint: 'tıkla: düzenle',
|
|
695
|
+
cr_edit_loaded: 'Görev forma yüklendi — düzenleyip Güncelle',
|
|
696
|
+
cr_updated: 'Görev güncellendi',
|
|
697
|
+
cr_added: 'Görev eklendi',
|
|
698
|
+
cr_edit_cancelled: 'Düzenleme iptal edildi',
|
|
699
|
+
/* İzleyici ekleme formu */
|
|
700
|
+
w_add: '+',
|
|
701
|
+
w_added: 'İzleyici eklendi',
|
|
702
|
+
w_add_name: 'İsim',
|
|
703
|
+
w_add_url: 'https://...',
|
|
704
|
+
w_add_path: 'json path (örn: data.price)',
|
|
705
|
+
w_add_value: 'eşik değer',
|
|
706
|
+
w_add_re: 'regex (opsiyonel)',
|
|
707
|
+
w_kind_web: 'Web',
|
|
708
|
+
w_kind_battery: 'Pil',
|
|
709
|
+
w_kind_logs: 'Log',
|
|
710
|
+
w_op_changed: 'değişti',
|
|
711
|
+
/* Ajanda */
|
|
712
|
+
aj_h2: 'Ajanda',
|
|
713
|
+
aj_sub: 'Yaklaşan cron görevleri, hatırlatıcılar ve izleyiciler — en yakın üstte.',
|
|
714
|
+
aj_empty: 'Planlanmış bir şey yok.',
|
|
715
|
+
aj_now: 'şimdi',
|
|
716
|
+
aj_later: 'sonra',
|
|
717
|
+
aj_ago: 'önce',
|
|
718
|
+
aj_notify: 'Masaüstü bildirimleri (toast) — cron, hatırlatıcı, izleyici ve ajan bitişinde',
|
|
719
|
+
aj_notify_on: 'Bildirimler açık',
|
|
720
|
+
aj_notify_off: 'Bildirimler kapalı',
|
|
721
|
+
/* E-posta paneli */
|
|
722
|
+
mail_inbox: 'Gelen Kutusu',
|
|
723
|
+
mail_refresh: 'Yenile',
|
|
724
|
+
mail_compose: 'Yeni Mail',
|
|
725
|
+
mail_to: 'alıcı@ornek.com',
|
|
726
|
+
mail_subject: 'Konu',
|
|
727
|
+
mail_body: 'Mesaj…',
|
|
728
|
+
mail_send: 'Gönder',
|
|
729
|
+
mail_sent: 'Mail gönderildi',
|
|
730
|
+
mail_send_fail: 'Mail gönderilemedi',
|
|
731
|
+
mail_empty: 'Gelen kutusu boş.',
|
|
732
|
+
mail_reply: 'Yanıtla',
|
|
733
|
+
mail_read_fail: 'Mail okunamadı',
|
|
734
|
+
mail_need_to: 'Alıcı adresi gerekli',
|
|
690
735
|
},
|
|
691
736
|
en: {
|
|
692
737
|
newChat: '+ New Chat',
|
|
@@ -842,6 +887,7 @@
|
|
|
842
887
|
em_mem_clear: 'Clear Memory',
|
|
843
888
|
em_mem_cleared: 'Empathy memory cleared',
|
|
844
889
|
tab_cron: 'Cron',
|
|
890
|
+
tab_agenda: 'Agenda',
|
|
845
891
|
tab_usage: 'Cost',
|
|
846
892
|
tab_logs: 'Logs',
|
|
847
893
|
tab_dash: 'Dashboard',
|
|
@@ -1107,6 +1153,50 @@
|
|
|
1107
1153
|
wa_queue_info: 'Message queue: ${n} pending',
|
|
1108
1154
|
wa_queue_failed: '${n} could not be sent',
|
|
1109
1155
|
wa_queue_empty_ok: 'Queue clear',
|
|
1156
|
+
/* Cron edit */
|
|
1157
|
+
cr_update: 'Update',
|
|
1158
|
+
cr_cancel_edit: 'Cancel',
|
|
1159
|
+
cr_edit_hint: 'click: edit',
|
|
1160
|
+
cr_edit_loaded: 'Task loaded into form — edit and Update',
|
|
1161
|
+
cr_updated: 'Task updated',
|
|
1162
|
+
cr_added: 'Task added',
|
|
1163
|
+
cr_edit_cancelled: 'Edit cancelled',
|
|
1164
|
+
/* Watcher add form */
|
|
1165
|
+
w_add: '+',
|
|
1166
|
+
w_added: 'Watcher added',
|
|
1167
|
+
w_add_name: 'Name',
|
|
1168
|
+
w_add_url: 'https://...',
|
|
1169
|
+
w_add_path: 'json path (e.g. data.price)',
|
|
1170
|
+
w_add_value: 'threshold value',
|
|
1171
|
+
w_add_re: 'regex (optional)',
|
|
1172
|
+
w_kind_web: 'Web',
|
|
1173
|
+
w_kind_battery: 'Battery',
|
|
1174
|
+
w_kind_logs: 'Log',
|
|
1175
|
+
w_op_changed: 'changed',
|
|
1176
|
+
/* Agenda */
|
|
1177
|
+
aj_h2: 'Agenda',
|
|
1178
|
+
aj_sub: 'Upcoming cron jobs, reminders and watchers — nearest first.',
|
|
1179
|
+
aj_empty: 'Nothing scheduled.',
|
|
1180
|
+
aj_now: 'now',
|
|
1181
|
+
aj_later: 'from now',
|
|
1182
|
+
aj_ago: 'ago',
|
|
1183
|
+
aj_notify: 'Desktop notifications (toast) — on cron, reminder, watcher and agent finish',
|
|
1184
|
+
aj_notify_on: 'Notifications on',
|
|
1185
|
+
aj_notify_off: 'Notifications off',
|
|
1186
|
+
/* Email panel */
|
|
1187
|
+
mail_inbox: 'Inbox',
|
|
1188
|
+
mail_refresh: 'Refresh',
|
|
1189
|
+
mail_compose: 'New Mail',
|
|
1190
|
+
mail_to: 'to@example.com',
|
|
1191
|
+
mail_subject: 'Subject',
|
|
1192
|
+
mail_body: 'Message…',
|
|
1193
|
+
mail_send: 'Send',
|
|
1194
|
+
mail_sent: 'Mail sent',
|
|
1195
|
+
mail_send_fail: 'Could not send mail',
|
|
1196
|
+
mail_empty: 'Inbox is empty.',
|
|
1197
|
+
mail_reply: 'Reply',
|
|
1198
|
+
mail_read_fail: 'Could not read mail',
|
|
1199
|
+
mail_need_to: 'Recipient address required',
|
|
1110
1200
|
us_reset_toast: 'Usage counters reset',
|
|
1111
1201
|
us_backup_ok: 'Backup taken: Desktop\\Beast-Backups',
|
|
1112
1202
|
us_where_on_toast: 'Startup summary on',
|
package/src/renderer/index.html
CHANGED
|
@@ -455,6 +455,7 @@
|
|
|
455
455
|
<button class="tab" data-tab="events" data-i18n="tab_events">Olay Merkezi</button>
|
|
456
456
|
<button class="tab" data-tab="empati" data-i18n="tab_empati">Empati Loop</button>
|
|
457
457
|
<button class="tab" data-tab="cron" data-i18n="tab_cron">Cron</button>
|
|
458
|
+
<button class="tab" data-tab="agenda" data-i18n="tab_agenda">Ajanda</button>
|
|
458
459
|
<button class="tab" data-tab="usage" data-i18n="tab_usage">Maliyet · Limit</button>
|
|
459
460
|
<button class="tab" data-tab="headroom" data-i18n="tab_headroom">Token Sıkıştırma</button>
|
|
460
461
|
<button class="tab" data-tab="logs" data-i18n="tab_logs">Log</button>
|
|
@@ -506,9 +507,11 @@
|
|
|
506
507
|
</div>
|
|
507
508
|
<textarea id="cronPrompt" rows="3" placeholder="Agent'a verilecek görev…" data-i18n-ph="cron_prompt_ph"></textarea>
|
|
508
509
|
<button id="cronAddBtn" data-i18n="cron_add">+ Görev Ekle</button>
|
|
510
|
+
<button id="cronCancel" class="ghost" hidden data-i18n="cr_cancel_edit">Vazgeç</button>
|
|
509
511
|
</div>
|
|
510
512
|
<div id="cronList"></div>
|
|
511
513
|
</div>
|
|
514
|
+
<div id="tab-agenda" class="pane" hidden></div>
|
|
512
515
|
</section>
|
|
513
516
|
</div>
|
|
514
517
|
</div>
|
|
@@ -612,6 +615,43 @@
|
|
|
612
615
|
<button class="mini-close" id="watchClose" title="Kapat" data-i18n-title="btn_close">×</button>
|
|
613
616
|
</div>
|
|
614
617
|
<div class="mini-body">
|
|
618
|
+
<div id="wAddBox">
|
|
619
|
+
<div class="mini-add-row">
|
|
620
|
+
<input id="wAddName" class="inp" data-i18n-ph="w_add_name" placeholder="İsim" maxlength="80" />
|
|
621
|
+
<select id="wAddKind" class="inp mini-select">
|
|
622
|
+
<option value="web" data-i18n="w_kind_web">Web</option>
|
|
623
|
+
<option value="battery" data-i18n="w_kind_battery">Pil</option>
|
|
624
|
+
<option value="logs" data-i18n="w_kind_logs">Log</option>
|
|
625
|
+
</select>
|
|
626
|
+
<input id="wAddEvery" class="inp" type="number" min="1" max="1440" value="15" title="dakika" style="width:70px" />
|
|
627
|
+
<button id="wAddBtn" data-i18n="w_add">+</button>
|
|
628
|
+
</div>
|
|
629
|
+
<div class="mini-add-row" id="wAddWeb">
|
|
630
|
+
<input id="wAddUrl" class="inp" data-i18n-ph="w_add_url" placeholder="https://..." />
|
|
631
|
+
<input id="wAddPath" class="inp" data-i18n-ph="w_add_path" placeholder="json path (örn: data.price)" />
|
|
632
|
+
</div>
|
|
633
|
+
<div class="mini-add-row" id="wAddCond">
|
|
634
|
+
<select id="wAddOp" class="inp mini-select">
|
|
635
|
+
<option value="lte"><=</option>
|
|
636
|
+
<option value="lt"><</option>
|
|
637
|
+
<option value="gte">>=</option>
|
|
638
|
+
<option value="gt">></option>
|
|
639
|
+
<option value="eq">=</option>
|
|
640
|
+
<option value="neq">≠</option>
|
|
641
|
+
<option value="changed" data-i18n="w_op_changed">değişti</option>
|
|
642
|
+
</select>
|
|
643
|
+
<input id="wAddValue" class="inp" data-i18n-ph="w_add_value" placeholder="eşik değer" />
|
|
644
|
+
</div>
|
|
645
|
+
<div class="mini-add-row" id="wAddLogs" hidden>
|
|
646
|
+
<select id="wAddLevel" class="inp mini-select">
|
|
647
|
+
<option value="error">error</option>
|
|
648
|
+
<option value="warn">warn</option>
|
|
649
|
+
<option value="info">info</option>
|
|
650
|
+
</select>
|
|
651
|
+
<input id="wAddWindow" class="inp" type="number" min="1" max="720" value="10" title="pencere (dk)" style="width:80px" />
|
|
652
|
+
<input id="wAddRe" class="inp" data-i18n-ph="w_add_re" placeholder="regex (opsiyonel)" />
|
|
653
|
+
</div>
|
|
654
|
+
</div>
|
|
615
655
|
<div id="watchList" class="mini-list"></div>
|
|
616
656
|
</div>
|
|
617
657
|
</div>
|
package/src/renderer/renderer.js
CHANGED
|
@@ -229,6 +229,7 @@ const els = {
|
|
|
229
229
|
cronSchedule: $('#cronSchedule'),
|
|
230
230
|
cronPrompt: $('#cronPrompt'),
|
|
231
231
|
cronAddBtn: $('#cronAddBtn'),
|
|
232
|
+
cronCancel: $('#cronCancel'),
|
|
232
233
|
cronList: $('#cronList'),
|
|
233
234
|
gearBtn: $('#gearBtn'),
|
|
234
235
|
settingsOverlay: $('#settingsOverlay'),
|
|
@@ -1242,6 +1243,7 @@ async function renderActiveSettingsTab() {
|
|
|
1242
1243
|
case 'events': await renderEventsPane(); break;
|
|
1243
1244
|
case 'empati': await renderEmpatiPane(); break;
|
|
1244
1245
|
case 'cron': await openCron(); break;
|
|
1246
|
+
case 'agenda': await renderAgendaPane(); break;
|
|
1245
1247
|
case 'usage': await renderUsagePane(); break;
|
|
1246
1248
|
case 'headroom': await renderHeadroomPane(); break;
|
|
1247
1249
|
case 'agents': await refreshAgentsPane(); break;
|
|
@@ -1327,12 +1329,14 @@ function switchTab(name) {
|
|
|
1327
1329
|
document.querySelectorAll('#setTabs .tab').forEach((b) =>
|
|
1328
1330
|
b.classList.toggle('active', b.dataset.tab === name)
|
|
1329
1331
|
);
|
|
1330
|
-
for (const p of ['lang', 'provider', 'fallout', 'skills', 'agents', 'tts', 'install', 'email', 'integrations', 'websearch', 'mcp', 'events', 'empati', 'cron', 'usage', 'headroom', 'logs', 'dash', 'sec', 'update']) {
|
|
1332
|
+
for (const p of ['lang', 'provider', 'fallout', 'skills', 'agents', 'tts', 'install', 'email', 'integrations', 'websearch', 'mcp', 'events', 'empati', 'cron', 'agenda', 'usage', 'headroom', 'logs', 'dash', 'sec', 'update']) {
|
|
1331
1333
|
const el = $('#tab-' + p);
|
|
1332
1334
|
if (el) el.hidden = p !== name; // guard: eksik pane tüm sekmeleri kilitlemesin
|
|
1333
1335
|
}
|
|
1334
1336
|
if (name === 'lang') renderLangPane();
|
|
1335
1337
|
if (name === 'cron') openCron();
|
|
1338
|
+
if (name === 'agenda') renderAgendaPane();
|
|
1339
|
+
if (name === 'email') renderEmailPane();
|
|
1336
1340
|
if (name === 'usage') renderUsagePane();
|
|
1337
1341
|
if (name === 'install') renderInstallPane();
|
|
1338
1342
|
if (name === 'headroom') renderHeadroomPane();
|
|
@@ -2640,7 +2644,25 @@ async function renderEmailPane() {
|
|
|
2640
2644
|
<input id="mailSmtpPort" class="inp" placeholder="465" autocomplete="off" />
|
|
2641
2645
|
<button id="mailSave" class="btn ghost">${_t('mail_save')}</button>
|
|
2642
2646
|
</div>` +
|
|
2643
|
-
'<div class="sub" style="margin-top:8px">' + _t('mail_note') + '</div>'
|
|
2647
|
+
'<div class="sub" style="margin-top:8px">' + _t('mail_note') + '</div>' +
|
|
2648
|
+
'<div class="divider"></div>' +
|
|
2649
|
+
'<h2>' + _t('mail_inbox') + '</h2>' +
|
|
2650
|
+
'<div style="display:flex;gap:8px;margin:6px 0;align-items:center">' +
|
|
2651
|
+
'<button id="mailRefresh" class="btn ghost">' + _t('mail_refresh') + '</button>' +
|
|
2652
|
+
'<button id="mailCompose" class="btn ghost">' + _t('mail_compose') + '</button>' +
|
|
2653
|
+
'<span id="mailMsg" class="sub" style="margin:0"></span></div>' +
|
|
2654
|
+
'<div id="mailList"></div>' +
|
|
2655
|
+
'<div id="mailRead" hidden style="margin-top:10px"></div>' +
|
|
2656
|
+
'<div id="mailWrite" hidden style="margin-top:10px">' +
|
|
2657
|
+
'<div class="form-grid" style="grid-template-columns:1fr 1fr">' +
|
|
2658
|
+
'<input id="mwTo" class="inp" placeholder="' + _t('mail_to') + '" autocomplete="off" />' +
|
|
2659
|
+
'<input id="mwSubject" class="inp" placeholder="' + _t('mail_subject') + '" autocomplete="off" />' +
|
|
2660
|
+
'</div>' +
|
|
2661
|
+
'<textarea id="mwBody" class="inp" rows="6" placeholder="' + _t('mail_body') + '" style="margin-top:6px;width:100%"></textarea>' +
|
|
2662
|
+
'<div style="display:flex;gap:8px;margin-top:6px">' +
|
|
2663
|
+
'<button id="mwSend" class="btn">' + _t('mail_send') + '</button>' +
|
|
2664
|
+
'<button id="mwCancel" class="btn ghost">' + _t('btn_close') + '</button>' +
|
|
2665
|
+
'</div></div>';
|
|
2644
2666
|
|
|
2645
2667
|
try {
|
|
2646
2668
|
const em = await beast.getEmail();
|
|
@@ -2672,6 +2694,86 @@ async function renderEmailPane() {
|
|
|
2672
2694
|
if (pass && pass !== PASS_MASK) { savedPass = pass; mailPass.value = PASS_MASK; passChanged = false; }
|
|
2673
2695
|
toast(_t('mail_saved'));
|
|
2674
2696
|
});
|
|
2697
|
+
|
|
2698
|
+
/* ---- gelen kutusu + okuma + gönderme ---- */
|
|
2699
|
+
const mailMsg = $('#mailMsg');
|
|
2700
|
+
const mailList = $('#mailList');
|
|
2701
|
+
const mailRead = $('#mailRead');
|
|
2702
|
+
const mailWrite = $('#mailWrite');
|
|
2703
|
+
const openWrite = (to, subject) => {
|
|
2704
|
+
if (!mailWrite) return;
|
|
2705
|
+
mailWrite.hidden = false;
|
|
2706
|
+
$('#mwTo').value = to || '';
|
|
2707
|
+
$('#mwSubject').value = subject || '';
|
|
2708
|
+
$('#mwBody').value = '';
|
|
2709
|
+
$('#mwBody').focus();
|
|
2710
|
+
};
|
|
2711
|
+
const loadInbox = async () => {
|
|
2712
|
+
if (!mailList) return;
|
|
2713
|
+
if (mailMsg) mailMsg.textContent = '…';
|
|
2714
|
+
if (mailRead) mailRead.hidden = true;
|
|
2715
|
+
const r = await beast.emailList({ limit: 15 }).catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
|
|
2716
|
+
if (mailMsg) mailMsg.textContent = '';
|
|
2717
|
+
if (!r || r.ok === false) {
|
|
2718
|
+
mailList.innerHTML = '<div class="sub">' + escapeHtml((r && r.error) || '?') + '</div>';
|
|
2719
|
+
return;
|
|
2720
|
+
}
|
|
2721
|
+
const msgs = r.messages || [];
|
|
2722
|
+
mailList.innerHTML = msgs.length
|
|
2723
|
+
? msgs
|
|
2724
|
+
.map(
|
|
2725
|
+
(m) =>
|
|
2726
|
+
'<div class="cj-job" data-uid="' + escapeHtml(String(m.uid)) + '" style="cursor:pointer">' +
|
|
2727
|
+
'<div class="cj-info"><div class="cj-name">' + escapeHtml(m.subject || '(konu yok)') + '</div>' +
|
|
2728
|
+
'<div class="cj-meta">' + escapeHtml(m.from || '') + ' · ' + escapeHtml(fmtWhen(m.date)) + '</div></div></div>'
|
|
2729
|
+
)
|
|
2730
|
+
.join('')
|
|
2731
|
+
: '<div class="sub">' + _t('mail_empty') + '</div>';
|
|
2732
|
+
mailList.querySelectorAll('[data-uid]').forEach((row) =>
|
|
2733
|
+
row.addEventListener('click', async () => {
|
|
2734
|
+
const uid = Number(row.dataset.uid);
|
|
2735
|
+
const subjectEl = row.querySelector('.cj-name');
|
|
2736
|
+
if (mailRead) {
|
|
2737
|
+
mailRead.hidden = false;
|
|
2738
|
+
mailRead.innerHTML = '<div class="sub">…</div>';
|
|
2739
|
+
}
|
|
2740
|
+
const rr = await beast.emailRead(uid).catch(() => null);
|
|
2741
|
+
if (!mailRead) return;
|
|
2742
|
+
if (rr && rr.ok) {
|
|
2743
|
+
mailRead.innerHTML =
|
|
2744
|
+
'<div class="sub" style="white-space:pre-wrap;max-height:220px;overflow:auto">' +
|
|
2745
|
+
escapeHtml(rr.content || '') +
|
|
2746
|
+
'</div><button id="mailReply" class="btn ghost" style="margin-top:6px">' + _t('mail_reply') + '</button>';
|
|
2747
|
+
const rp = $('#mailReply');
|
|
2748
|
+
if (rp)
|
|
2749
|
+
rp.addEventListener('click', () =>
|
|
2750
|
+
openWrite('', 'Re: ' + (subjectEl ? subjectEl.textContent : ''))
|
|
2751
|
+
);
|
|
2752
|
+
} else {
|
|
2753
|
+
mailRead.innerHTML = '<div class="sub">' + escapeHtml((rr && rr.error) || _t('mail_read_fail')) + '</div>';
|
|
2754
|
+
}
|
|
2755
|
+
})
|
|
2756
|
+
);
|
|
2757
|
+
};
|
|
2758
|
+
if ($('#mailRefresh')) $('#mailRefresh').addEventListener('click', loadInbox);
|
|
2759
|
+
if ($('#mailCompose')) $('#mailCompose').addEventListener('click', () => openWrite('', ''));
|
|
2760
|
+
if ($('#mwCancel')) $('#mwCancel').addEventListener('click', () => { if (mailWrite) mailWrite.hidden = true; });
|
|
2761
|
+
if ($('#mwSend')) $('#mwSend').addEventListener('click', async () => {
|
|
2762
|
+
const to = $('#mwTo').value.trim();
|
|
2763
|
+
const subject = $('#mwSubject').value.trim();
|
|
2764
|
+
const body = $('#mwBody').value;
|
|
2765
|
+
if (!to) { toast(_t('mail_need_to')); return; }
|
|
2766
|
+
const r = await beast.emailSend({ to, subject, body }).catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
|
|
2767
|
+
if (r && r.ok) {
|
|
2768
|
+
toast(_t('mail_sent'));
|
|
2769
|
+
if (mailWrite) mailWrite.hidden = true;
|
|
2770
|
+
loadInbox();
|
|
2771
|
+
} else {
|
|
2772
|
+
toast((r && r.error) || _t('mail_send_fail'));
|
|
2773
|
+
}
|
|
2774
|
+
});
|
|
2775
|
+
/* ayar kayıtlıysa ve sekme GÖRÜNÜRse gelen kutusunu yükle (boşuna IMAP bağlantısı yok) */
|
|
2776
|
+
if (savedPass && $('#mailUser').value.trim() && !pane.hidden) loadInbox();
|
|
2675
2777
|
}
|
|
2676
2778
|
|
|
2677
2779
|
/* ---------------- integrations (WhatsApp) ---------------- */
|
|
@@ -5572,7 +5674,15 @@ function renderAgentsPane() {
|
|
|
5572
5674
|
/* ---------------- events from engine ---------------- */
|
|
5573
5675
|
|
|
5574
5676
|
function onEvent(ev) {
|
|
5575
|
-
|
|
5677
|
+
if (ev.type === 'notify-click') {
|
|
5678
|
+
/* toast tıklandı: ilgili panele atla */
|
|
5679
|
+
if (ev.target === 'watchers') openWatchModal();
|
|
5680
|
+
else if (ev.target === 'cron') openSettings().then(() => switchTab('cron')).catch(() => {});
|
|
5681
|
+
else if (ev.target === 'agents') { toggleRail(true); refreshAgentsPane(); }
|
|
5682
|
+
else openSettings().catch(() => {});
|
|
5683
|
+
return;
|
|
5684
|
+
}
|
|
5685
|
+
if (ev.type === 'sessions') { refreshSessions(); return; }
|
|
5576
5686
|
if (ev.type === 'approval') {
|
|
5577
5687
|
/* Beast Studio oturumunun onayı: panelde ipucu + kart sohbete düşer */
|
|
5578
5688
|
if (stSessionId && ev.sessionId === stSessionId) {
|
|
@@ -6333,6 +6443,13 @@ async function openWatchLogModal(id) {
|
|
|
6333
6443
|
}
|
|
6334
6444
|
|
|
6335
6445
|
let lastCronKey = '';
|
|
6446
|
+
/* Cron DÜZENLEME: satıra tıkla → form dolar, buton "Güncelle"ye döner */
|
|
6447
|
+
let editingCronId = '';
|
|
6448
|
+
function resetCronEdit() {
|
|
6449
|
+
editingCronId = '';
|
|
6450
|
+
if (els.cronAddBtn) els.cronAddBtn.textContent = _t('cron_add');
|
|
6451
|
+
if (els.cronCancel) els.cronCancel.hidden = true;
|
|
6452
|
+
}
|
|
6336
6453
|
async function renderCronModal(force) {
|
|
6337
6454
|
let jobs = [];
|
|
6338
6455
|
try { jobs = (await beast.cronList()) || []; } catch {}
|
|
@@ -6755,18 +6872,116 @@ function renderCronList(jobs) {
|
|
|
6755
6872
|
btns.appendChild(mkBtn(j.enabled ? '❚❚' : '▶', j.enabled ? _t('cr_pause') : _t('cr_start'), async () => { await beast.cronToggle(j.id); refreshCron(); }));
|
|
6756
6873
|
btns.appendChild(mkBtn('×', _t('cr_del'), async () => { await beast.cronDelete(j.id); refreshCron(); }));
|
|
6757
6874
|
row.appendChild(btns);
|
|
6758
|
-
|
|
6759
|
-
|
|
6875
|
+
const loadEdit = () => {
|
|
6876
|
+
editingCronId = j.id;
|
|
6877
|
+
els.cronName.value = j.name || '';
|
|
6760
6878
|
els.cronPreset.value = '__custom';
|
|
6761
|
-
els.cronSchedule.value = j.schedule;
|
|
6762
|
-
els.cronPrompt.value = j.prompt;
|
|
6763
|
-
|
|
6879
|
+
els.cronSchedule.value = j.schedule || '';
|
|
6880
|
+
els.cronPrompt.value = j.prompt || '';
|
|
6881
|
+
if (els.cronAddBtn) els.cronAddBtn.textContent = _t('cr_update');
|
|
6882
|
+
if (els.cronCancel) els.cronCancel.hidden = false;
|
|
6883
|
+
els.cronName.focus();
|
|
6884
|
+
toast(_t('cr_edit_loaded'));
|
|
6885
|
+
};
|
|
6886
|
+
dot.addEventListener('click', loadEdit);
|
|
6887
|
+
info.addEventListener('click', loadEdit);
|
|
6888
|
+
dot.title = (j.enabled ? _t('cr_dot_on') : _t('cr_dot_off')) + ' — ' + _t('cr_edit_hint');
|
|
6764
6889
|
els.cronList.appendChild(row);
|
|
6765
6890
|
}
|
|
6766
6891
|
}
|
|
6767
6892
|
|
|
6768
6893
|
function openCron() { refreshCron(); }
|
|
6769
6894
|
|
|
6895
|
+
/* ---------------- AJANDA: yaklaşan cron + hatırlatıcı + izleyiciler ---------------- */
|
|
6896
|
+
|
|
6897
|
+
function isReminderLike(j) {
|
|
6898
|
+
if (!j) return false;
|
|
6899
|
+
if (j.kind === 'reminder') return true;
|
|
6900
|
+
const n = String(j.name || '');
|
|
6901
|
+
const p = String(j.prompt || '');
|
|
6902
|
+
return (
|
|
6903
|
+
/^Hatırlatma:/.test(n) ||
|
|
6904
|
+
/^Tekrarlı hatırlatma:/.test(n) ||
|
|
6905
|
+
p.startsWith('[HATIRLATMA ZAMANI]') ||
|
|
6906
|
+
p.startsWith('[TEKRARLI HATIRLATMA]')
|
|
6907
|
+
);
|
|
6908
|
+
}
|
|
6909
|
+
|
|
6910
|
+
function relTime(ms) {
|
|
6911
|
+
const diff = ms - Date.now();
|
|
6912
|
+
const abs = Math.abs(diff);
|
|
6913
|
+
const dk = Math.round(abs / 60000);
|
|
6914
|
+
const saat = Math.floor(dk / 60);
|
|
6915
|
+
const gun = Math.floor(saat / 24);
|
|
6916
|
+
let label;
|
|
6917
|
+
if (dk < 1) return _t('aj_now');
|
|
6918
|
+
else if (dk < 60) label = dk + ' dk';
|
|
6919
|
+
else if (saat < 24) label = saat + ' sa';
|
|
6920
|
+
else label = gun + ' gün';
|
|
6921
|
+
return diff >= 0 ? label + ' ' + _t('aj_later') : label + ' ' + _t('aj_ago');
|
|
6922
|
+
}
|
|
6923
|
+
|
|
6924
|
+
async function renderAgendaPane() {
|
|
6925
|
+
const pane = $('#tab-agenda');
|
|
6926
|
+
if (!pane) return;
|
|
6927
|
+
let jobs = [];
|
|
6928
|
+
let ws = [];
|
|
6929
|
+
try { jobs = (await beast.cronList()) || []; } catch {}
|
|
6930
|
+
try { ws = (await beast.watchersList()) || []; } catch {}
|
|
6931
|
+
const items = [];
|
|
6932
|
+
for (const j of jobs) {
|
|
6933
|
+
if (!j.enabled) continue;
|
|
6934
|
+
const t = j.nextRunAt ? new Date(j.nextRunAt).getTime() : 0;
|
|
6935
|
+
if (!t) continue;
|
|
6936
|
+
items.push({ type: isReminderLike(j) ? 'rem' : 'cron', name: j.name || j.id, at: t, meta: j.schedule, id: j.id });
|
|
6937
|
+
}
|
|
6938
|
+
for (const w of ws) {
|
|
6939
|
+
if (!w.enabled) continue;
|
|
6940
|
+
const every = w.everySec ? w.everySec * 1000 : (w.everyMin || 15) * 60000;
|
|
6941
|
+
const last = w.lastCheckAt ? new Date(w.lastCheckAt).getTime() : 0;
|
|
6942
|
+
items.push({
|
|
6943
|
+
type: 'watch',
|
|
6944
|
+
name: w.name,
|
|
6945
|
+
at: last ? last + every : Date.now(),
|
|
6946
|
+
meta: opText(w) + ' · her ' + fmtEvery(w),
|
|
6947
|
+
id: w.id,
|
|
6948
|
+
});
|
|
6949
|
+
}
|
|
6950
|
+
items.sort((a, b) => a.at - b.at);
|
|
6951
|
+
const badge = { cron: '🕐', rem: '⏰', watch: '👁' };
|
|
6952
|
+
const rows = items.length
|
|
6953
|
+
? items
|
|
6954
|
+
.map(
|
|
6955
|
+
(it) =>
|
|
6956
|
+
'<div class="cron-job ag-item" data-type="' + it.type + '" data-id="' + escapeHtml(it.id) + '" style="cursor:pointer">' +
|
|
6957
|
+
'<div class="cj-info"><div class="cj-name">' + badge[it.type] + ' ' + escapeHtml(it.name) + '</div>' +
|
|
6958
|
+
'<div class="cj-meta">' + escapeHtml(String(it.meta || '')) + ' · <b>' + escapeHtml(relTime(it.at)) + '</b> · ' + escapeHtml(fmtTime(it.at)) + '</div></div></div>'
|
|
6959
|
+
)
|
|
6960
|
+
.join('')
|
|
6961
|
+
: '<div class="cron-empty">' + _t('aj_empty') + '</div>';
|
|
6962
|
+
let notifOn = true;
|
|
6963
|
+
try { notifOn = !!(await beast.notifyGet()).toast; } catch {}
|
|
6964
|
+
pane.innerHTML =
|
|
6965
|
+
'<h2>' + _t('aj_h2') + '</h2><div class="sub">' + _t('aj_sub') + '</div>' +
|
|
6966
|
+
'<label class="lock-row" style="margin-top:8px"><input type="checkbox" id="ajNotify" ' + (notifOn ? 'checked' : '') + '/><span>' + _t('aj_notify') + '</span></label>' +
|
|
6967
|
+
'<div style="margin-top:10px">' + rows + '</div>';
|
|
6968
|
+
const nj = $('#ajNotify');
|
|
6969
|
+
if (nj) nj.addEventListener('change', async (e) => {
|
|
6970
|
+
await beast.notifySet(e.target.checked).catch(() => {});
|
|
6971
|
+
toast(e.target.checked ? _t('aj_notify_on') : _t('aj_notify_off'));
|
|
6972
|
+
});
|
|
6973
|
+
pane.querySelectorAll('.ag-item').forEach((row) => {
|
|
6974
|
+
row.addEventListener('click', () => {
|
|
6975
|
+
if (row.dataset.type === 'watch') {
|
|
6976
|
+
closeSettings();
|
|
6977
|
+
openWatchModal();
|
|
6978
|
+
} else {
|
|
6979
|
+
switchTab('cron');
|
|
6980
|
+
}
|
|
6981
|
+
});
|
|
6982
|
+
});
|
|
6983
|
+
}
|
|
6984
|
+
|
|
6770
6985
|
/* ---------------- boot ---------------- */
|
|
6771
6986
|
|
|
6772
6987
|
async function init() {
|
|
@@ -7161,6 +7376,59 @@ async function init() {
|
|
|
7161
7376
|
if (els.watchLogOverlay) els.watchLogOverlay.addEventListener('click', (e) => {
|
|
7162
7377
|
if (e.target === els.watchLogOverlay) closeWatchLogModal();
|
|
7163
7378
|
});
|
|
7379
|
+
|
|
7380
|
+
/* watcher EKLEME formu: tür seçimine göre alanlar açılır/kapanır */
|
|
7381
|
+
const wKindSel = $('#wAddKind');
|
|
7382
|
+
const wSyncKind = () => {
|
|
7383
|
+
const k = wKindSel ? wKindSel.value : 'web';
|
|
7384
|
+
const web = $('#wAddWeb');
|
|
7385
|
+
const cond = $('#wAddCond');
|
|
7386
|
+
const logs = $('#wAddLogs');
|
|
7387
|
+
if (web) web.hidden = k !== 'web';
|
|
7388
|
+
if (cond) cond.hidden = k === 'logs';
|
|
7389
|
+
if (logs) logs.hidden = k !== 'logs';
|
|
7390
|
+
};
|
|
7391
|
+
if (wKindSel) {
|
|
7392
|
+
wKindSel.addEventListener('change', wSyncKind);
|
|
7393
|
+
wSyncKind();
|
|
7394
|
+
}
|
|
7395
|
+
const wAddBtn = $('#wAddBtn');
|
|
7396
|
+
if (wAddBtn) {
|
|
7397
|
+
wAddBtn.addEventListener('click', async () => {
|
|
7398
|
+
const kind = wKindSel ? wKindSel.value : 'web';
|
|
7399
|
+
const input = {
|
|
7400
|
+
name: $('#wAddName').value.trim(),
|
|
7401
|
+
kind,
|
|
7402
|
+
everyMin: Number($('#wAddEvery').value) || 15,
|
|
7403
|
+
};
|
|
7404
|
+
if (kind === 'web') {
|
|
7405
|
+
input.url = $('#wAddUrl').value.trim();
|
|
7406
|
+
input.path = $('#wAddPath').value.trim();
|
|
7407
|
+
}
|
|
7408
|
+
if (kind !== 'logs') {
|
|
7409
|
+
input.op = $('#wAddOp').value;
|
|
7410
|
+
const v = $('#wAddValue').value.trim();
|
|
7411
|
+
input.value = input.op === 'changed' || v === '' ? null : (Number.isNaN(Number(v)) ? v : Number(v));
|
|
7412
|
+
} else {
|
|
7413
|
+
input.level = $('#wAddLevel').value;
|
|
7414
|
+
input.windowMin = Number($('#wAddWindow').value) || 10;
|
|
7415
|
+
input.re = $('#wAddRe').value.trim();
|
|
7416
|
+
input.op = 'gt';
|
|
7417
|
+
input.value = 0;
|
|
7418
|
+
}
|
|
7419
|
+
const r = await beast.watchersAdd(input).catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
|
|
7420
|
+
if (!r || r.ok === false) { toast((r && r.error) || 'İzleyici eklenemedi'); return; }
|
|
7421
|
+
for (const id of ['#wAddName', '#wAddUrl', '#wAddPath', '#wAddValue', '#wAddRe']) {
|
|
7422
|
+
const el = $(id);
|
|
7423
|
+
if (el) el.value = '';
|
|
7424
|
+
}
|
|
7425
|
+
lastWatchKey = '';
|
|
7426
|
+
lastWatchPaneKey = '';
|
|
7427
|
+
await renderWatchersModal(true);
|
|
7428
|
+
await renderWatchersPane(true);
|
|
7429
|
+
toast(_t('w_added'));
|
|
7430
|
+
});
|
|
7431
|
+
}
|
|
7164
7432
|
if (els.cronOverlay) els.cronOverlay.addEventListener('click', (e) => {
|
|
7165
7433
|
if (e.target === els.cronOverlay) closeCronModal();
|
|
7166
7434
|
});
|
|
@@ -7173,12 +7441,26 @@ async function init() {
|
|
|
7173
7441
|
const schedule = els.cronSchedule.value.trim();
|
|
7174
7442
|
const prompt = els.cronPrompt.value.trim();
|
|
7175
7443
|
if (!schedule || !prompt) { toast('Cron ifadesi ve görev metni gerekli'); return; }
|
|
7176
|
-
|
|
7177
|
-
|
|
7444
|
+
if (editingCronId) {
|
|
7445
|
+
const r = await beast.cronUpdate(editingCronId, { name, schedule, prompt });
|
|
7446
|
+
if (!r || r.ok === false) { toast((r && r.error) || 'Güncellenemedi'); return; }
|
|
7447
|
+
toast(_t('cr_updated'));
|
|
7448
|
+
} else {
|
|
7449
|
+
const r = await beast.cronAdd({ name, schedule, prompt });
|
|
7450
|
+
if (!r.ok) { toast(r.error || 'Eklenemedi'); return; }
|
|
7451
|
+
toast(_t('cr_added'));
|
|
7452
|
+
}
|
|
7178
7453
|
els.cronName.value = ''; els.cronSchedule.value = ''; els.cronPrompt.value = '';
|
|
7179
|
-
|
|
7454
|
+
resetCronEdit();
|
|
7180
7455
|
refreshCron();
|
|
7181
7456
|
});
|
|
7457
|
+
if (els.cronCancel) {
|
|
7458
|
+
els.cronCancel.addEventListener('click', () => {
|
|
7459
|
+
els.cronName.value = ''; els.cronSchedule.value = ''; els.cronPrompt.value = '';
|
|
7460
|
+
resetCronEdit();
|
|
7461
|
+
toast(_t('cr_edit_cancelled'));
|
|
7462
|
+
});
|
|
7463
|
+
}
|
|
7182
7464
|
document.addEventListener('mousemove', (e) => {
|
|
7183
7465
|
if (!rz) return;
|
|
7184
7466
|
const w = Math.max(300, Math.min(rz.sw - (e.clientX - rz.sx), window.innerWidth - 340));
|
package/src/renderer/style.css
CHANGED
|
@@ -1484,6 +1484,19 @@ body.browser-open .mini-overlay { right: var(--bw, 480px); }
|
|
|
1484
1484
|
font-size: 12px;
|
|
1485
1485
|
padding: 4px 6px;
|
|
1486
1486
|
}
|
|
1487
|
+
/* ---------- İzleyici EKLEME formu ---------- */
|
|
1488
|
+
#wAddBox {
|
|
1489
|
+
display: flex; flex-direction: column; gap: 6px;
|
|
1490
|
+
background: var(--panel); border: 1px solid var(--border);
|
|
1491
|
+
border-radius: 9px; padding: 8px; margin-bottom: 10px;
|
|
1492
|
+
}
|
|
1493
|
+
.mini-add-row { display: flex; gap: 6px; align-items: center; }
|
|
1494
|
+
.mini-add-row .inp { flex: 1; min-width: 0; margin: 0; padding: 5px 8px; font-size: 12px; }
|
|
1495
|
+
#wAddBtn {
|
|
1496
|
+
flex: none; background: var(--accent); color: #fff; border: none;
|
|
1497
|
+
border-radius: 7px; padding: 5px 11px; font-size: 14px; cursor: pointer;
|
|
1498
|
+
}
|
|
1499
|
+
#wAddBtn:hover { filter: brightness(1.1); }
|
|
1487
1500
|
.wl-row {
|
|
1488
1501
|
display: flex; align-items: baseline; gap: 7px;
|
|
1489
1502
|
background: var(--panel); border: 1px solid var(--border);
|