myagent-ai 1.23.83 → 1.24.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/agents/base.py +1 -0
- package/package.json +2 -2
- package/web/ui/admin/admin-core.js +28 -3
- package/web/ui/chat/chat.css +317 -55
- package/web/ui/chat/chat_container.html +49 -2
- package/web/ui/chat/chat_main.js +385 -5
- package/web/ui/index.html +20 -26
package/agents/base.py
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "myagent-ai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.24.0",
|
|
4
4
|
"description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
|
|
5
5
|
"main": "main.py",
|
|
6
6
|
"bin": {
|
|
@@ -43,4 +43,4 @@
|
|
|
43
43
|
"python": ">=3.10",
|
|
44
44
|
"node": ">=18"
|
|
45
45
|
}
|
|
46
|
-
}
|
|
46
|
+
}
|
|
@@ -47,7 +47,7 @@ async function checkUpdate(manual){
|
|
|
47
47
|
if(r.error){if(manual) showToast('检查更新失败: '+r.error,'danger'); return}
|
|
48
48
|
if(r.has_update){
|
|
49
49
|
$('updateBadge').style.display='inline';
|
|
50
|
-
showToast('发现新版本 v'+r.latest_version,'warn');
|
|
50
|
+
showToast('发现新版本 v'+r.latest_version+',更新后任务将自动继续','warn',5000);
|
|
51
51
|
}else{
|
|
52
52
|
$('updateBadge').style.display='none';
|
|
53
53
|
if(manual) showToast('已是最新版本 v'+r.current_version,'success');
|
|
@@ -55,14 +55,39 @@ async function checkUpdate(manual){
|
|
|
55
55
|
}catch(e){if(manual) showToast('检查更新失败','danger')}
|
|
56
56
|
}
|
|
57
57
|
async function doUpdate(){
|
|
58
|
-
if(!confirm('
|
|
58
|
+
if(!confirm('确定要更新到最新版本吗?\n\n更新过程中服务会自动重启,重启后任务将自动继续执行。'))return;
|
|
59
59
|
try{
|
|
60
|
+
showToast('正在准备更新...','info');
|
|
60
61
|
const r=await api('/api/update/apply',{method:'POST',body:JSON.stringify({type:'full'})});
|
|
61
62
|
if(r.error){showToast('更新失败: '+r.error,'danger');return}
|
|
62
|
-
showToast('
|
|
63
|
+
showToast('更新已开始,正在轮询进度...','info',5000);
|
|
64
|
+
// 开始轮询更新状态
|
|
65
|
+
pollAdminUpdateStatus();
|
|
63
66
|
}catch(e){showToast('更新失败','danger')}
|
|
64
67
|
}
|
|
65
68
|
|
|
69
|
+
var _adminUpdatePollTimer=null;
|
|
70
|
+
function pollAdminUpdateStatus(){
|
|
71
|
+
if(_adminUpdatePollTimer)clearInterval(_adminUpdatePollTimer);
|
|
72
|
+
var startTime=Date.now();
|
|
73
|
+
_adminUpdatePollTimer=setInterval(async function(){
|
|
74
|
+
if(Date.now()-startTime>5*60*1000){clearInterval(_adminUpdatePollTimer);showToast('更新超时,请刷新页面查看状态','warn');return}
|
|
75
|
+
try{
|
|
76
|
+
var data=await api('/api/update/status');
|
|
77
|
+
var s=data.status||'idle';
|
|
78
|
+
var statusMsg={draining:'正在安全停止任务...',downloading:'正在下载更新...',updating:'正在安装更新...',reloading:'正在重载模块...',restarting:'即将重启...',success:'更新完成!正在刷新...',failed:'更新失败'};
|
|
79
|
+
if(s==='success'){clearInterval(_adminUpdatePollTimer);showToast('更新完成!','success');setTimeout(()=>location.reload(),2000);return}
|
|
80
|
+
if(s==='failed'||s==='rollback'){clearInterval(_adminUpdatePollTimer);showToast('更新失败,请稍后重试','danger');return}
|
|
81
|
+
if(statusMsg[s]){showToast(statusMsg[s],'info',4000)}
|
|
82
|
+
}catch(e){
|
|
83
|
+
// 服务器可能在重启中
|
|
84
|
+
clearInterval(_adminUpdatePollTimer);
|
|
85
|
+
showToast('连接中断,服务可能正在重启...','warn',5000);
|
|
86
|
+
setTimeout(()=>location.reload(),10000);
|
|
87
|
+
}
|
|
88
|
+
},5000);
|
|
89
|
+
}
|
|
90
|
+
|
|
66
91
|
// ── Theme Management ──
|
|
67
92
|
function initTheme(){const s=localStorage.getItem('myagent-theme')||'claude';document.documentElement.setAttribute('data-theme',s);updateThemeIcon(s)}
|
|
68
93
|
function toggleTheme(){const c=document.documentElement.getAttribute('data-theme')||'claude';const n=c==='claude'?'dark':'claude';document.documentElement.setAttribute('data-theme',n);localStorage.setItem('myagent-theme',n);updateThemeIcon(n)}
|
package/web/ui/chat/chat.css
CHANGED
|
@@ -29,16 +29,17 @@
|
|
|
29
29
|
--shadow-lg:0 10px 25px rgba(0,0,0,.1);
|
|
30
30
|
}
|
|
31
31
|
[data-theme="dark"]{
|
|
32
|
-
--bg:#
|
|
33
|
-
--text:#
|
|
34
|
-
--accent:#
|
|
35
|
-
--
|
|
36
|
-
--
|
|
37
|
-
--
|
|
38
|
-
--
|
|
39
|
-
--
|
|
40
|
-
--shadow:0 1px
|
|
41
|
-
--shadow
|
|
32
|
+
--bg:#1a1816;--bg2:#211f1b;--bg3:#2a2720;--bg4:#353128;--bg5:#433c32;
|
|
33
|
+
--text:#e5ddd0;--text2:#a39b8c;--text3:#6d665b;
|
|
34
|
+
--accent:#c96442;--accent2:#d97a5a;--accent-light:#2a1f18;--accent-dark:#a0502e;
|
|
35
|
+
--card:#262320;
|
|
36
|
+
--user-bubble:#c96442;--user-text:#ffffff;
|
|
37
|
+
--bot-bubble:#211f1b;--bot-text:#e5ddd0;
|
|
38
|
+
--ok:#6ee7a0;--warn:#fcd34d;--danger:#f87171;--info:#7db8f0;
|
|
39
|
+
--border:#322c24;--border-light:#2a2520;
|
|
40
|
+
--shadow-sm:0 1px 2px rgba(0,0,0,.3);
|
|
41
|
+
--shadow:0 1px 3px rgba(0,0,0,.4),0 1px 2px rgba(0,0,0,.3);
|
|
42
|
+
--shadow-lg:0 10px 25px rgba(0,0,0,.55);
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
html,body{width:100%;height:100%;margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,'Noto Sans SC',sans-serif;background:var(--bg);color:var(--text);font-size:14px;line-height:1.6;overflow:hidden}
|
|
@@ -90,7 +91,7 @@ input,textarea,select{font:inherit}
|
|
|
90
91
|
}
|
|
91
92
|
.sidebar-logo{
|
|
92
93
|
width:36px;height:36px;border-radius:10px;
|
|
93
|
-
background:linear-gradient(135deg,var(--accent)
|
|
94
|
+
background:linear-gradient(135deg,var(--accent),var(--accent-dark));
|
|
94
95
|
display:grid;place-items:center;flex-shrink:0;
|
|
95
96
|
}
|
|
96
97
|
.sidebar-logo svg{width:20px;height:20px;color:#fff}
|
|
@@ -226,7 +227,7 @@ input,textarea,select{font:inherit}
|
|
|
226
227
|
display:grid;place-items:center;font-size:14px;
|
|
227
228
|
}
|
|
228
229
|
.message-row.user .message-avatar{background:var(--accent);color:#fff}
|
|
229
|
-
.message-row.assistant .message-avatar{background:linear-gradient(135deg
|
|
230
|
+
.message-row.assistant .message-avatar{background:linear-gradient(135deg,var(--accent),var(--accent-dark));color:#fff}
|
|
230
231
|
|
|
231
232
|
.message-bubble{
|
|
232
233
|
max-width:85%;padding:12px 16px;border-radius:var(--radius);
|
|
@@ -256,7 +257,7 @@ input,textarea,select{font:inherit}
|
|
|
256
257
|
}
|
|
257
258
|
.message-row.user .message-bubble code{background:rgba(255,255,255,.2)}
|
|
258
259
|
.message-bubble pre{
|
|
259
|
-
background
|
|
260
|
+
background:var(--bg3);color:var(--text);padding:14px 16px;border-radius:var(--radius-sm);
|
|
260
261
|
overflow-x:auto;margin:8px 0;font-size:12.5px;line-height:1.5;
|
|
261
262
|
font-family:'SF Mono','Fira Code','Cascadia Code',monospace;
|
|
262
263
|
max-width:100%;white-space:pre-wrap;word-break:break-all;
|
|
@@ -283,7 +284,7 @@ input,textarea,select{font:inherit}
|
|
|
283
284
|
}
|
|
284
285
|
.exec-timer-icon{
|
|
285
286
|
width:28px;height:28px;border-radius:6px;
|
|
286
|
-
background:linear-gradient(135deg,var(--accent)
|
|
287
|
+
background:linear-gradient(135deg,var(--accent),var(--accent-dark));
|
|
287
288
|
display:grid;place-items:center;flex-shrink:0;
|
|
288
289
|
font-size:14px;
|
|
289
290
|
}
|
|
@@ -359,7 +360,7 @@ input,textarea,select{font:inherit}
|
|
|
359
360
|
background:var(--accent);border-radius:1.5px;
|
|
360
361
|
margin-left:1px;vertical-align:text-bottom;
|
|
361
362
|
animation:cursorBlink 1.06s step-end infinite;
|
|
362
|
-
box-shadow:0 0 6px rgba(
|
|
363
|
+
box-shadow:0 0 6px rgba(201,100,66,.35);
|
|
363
364
|
position:relative;
|
|
364
365
|
}
|
|
365
366
|
@keyframes cursorBlink{0%,100%{opacity:1}50%{opacity:0}}
|
|
@@ -397,8 +398,8 @@ input,textarea,select{font:inherit}
|
|
|
397
398
|
animation:toolRunPulse 2s ease-in-out infinite;
|
|
398
399
|
}
|
|
399
400
|
@keyframes toolRunPulse{
|
|
400
|
-
0%,100%{box-shadow:0 0 0 0 rgba(
|
|
401
|
-
50%{box-shadow:0 0 0 4px rgba(
|
|
401
|
+
0%,100%{box-shadow:0 0 0 0 rgba(201,100,66,.08)}
|
|
402
|
+
50%{box-shadow:0 0 0 4px rgba(201,100,66,.12)}
|
|
402
403
|
}
|
|
403
404
|
.tool-elapsed-timer{
|
|
404
405
|
font-size:10px;font-variant-numeric:tabular-nums;
|
|
@@ -469,13 +470,13 @@ input,textarea,select{font:inherit}
|
|
|
469
470
|
animation:completeFlash .4s ease-out;
|
|
470
471
|
}
|
|
471
472
|
@keyframes completeFlash{
|
|
472
|
-
0%{background:rgba(
|
|
473
|
+
0%{background:rgba(217,119,6,.06)}
|
|
473
474
|
100%{background:transparent}
|
|
474
475
|
}
|
|
475
476
|
|
|
476
477
|
/* ── Thought Block (Agent Thinking) ── */
|
|
477
478
|
.thought-block{width:100%!important;max-width:100%!important;display:flex;flex-direction:column;margin:0 0 10px 0;border:1px solid var(--border-light);border-radius:var(--radius-sm);overflow:hidden;background:linear-gradient(135deg,var(--accent-light),var(--bg2));animation:thoughtFadeIn .4s ease-out;flex-shrink:0;box-sizing:border-box;align-self:stretch}
|
|
478
|
-
.thought-block.streaming{border-color:var(--accent);box-shadow:0 0 12px rgba(
|
|
479
|
+
.thought-block.streaming{border-color:var(--accent);box-shadow:0 0 12px rgba(217,119,6,.15)}
|
|
479
480
|
@keyframes thoughtFadeIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}
|
|
480
481
|
.thought-block summary{display:flex;align-items:center;gap:8px;padding:8px 14px;cursor:pointer;font-size:12px;font-weight:600;color:var(--text2);user-select:none;transition:var(--transition);text-transform:uppercase;letter-spacing:.3px}
|
|
481
482
|
.thought-block summary:hover{background:var(--bg3)}
|
|
@@ -510,7 +511,7 @@ input,textarea,select{font:inherit}
|
|
|
510
511
|
.exec-event-icon.code{background:#fefce8;color:#eab308}
|
|
511
512
|
.exec-event-icon.success{background:#ecfdf5;color:#10b981}
|
|
512
513
|
.exec-event-icon.error{background:#fef2f2;color:#ef4444}
|
|
513
|
-
.exec-event-icon.running{background
|
|
514
|
+
.exec-event-icon.running{background:rgba(217,119,6,.12);color:#D97706;animation:brainPulse 1.2s infinite}
|
|
514
515
|
.exec-event-body{flex:1;min-width:0}
|
|
515
516
|
.exec-event-title{font-size:12px;font-weight:600;color:var(--text);margin-bottom:2px}
|
|
516
517
|
.exec-event-meta{font-size:11px;color:var(--text3);display:flex;gap:8px;align-items:center}
|
|
@@ -600,7 +601,7 @@ input,textarea,select{font:inherit}
|
|
|
600
601
|
.attach-btn svg{width:18px;height:18px}
|
|
601
602
|
|
|
602
603
|
/* [v1.16.12] 拖拽高亮 */
|
|
603
|
-
.input-box.drag-over{border-color:var(--accent) !important;box-shadow:0 0 0 2px rgba(
|
|
604
|
+
.input-box.drag-over{border-color:var(--accent) !important;box-shadow:0 0 0 2px rgba(217,119,6,.2)}
|
|
604
605
|
|
|
605
606
|
/* [v1.16.12] 附件预览 */
|
|
606
607
|
.attachment-preview {
|
|
@@ -1008,9 +1009,9 @@ input,textarea,select{font:inherit}
|
|
|
1008
1009
|
}
|
|
1009
1010
|
}
|
|
1010
1011
|
|
|
1011
|
-
/* Dark theme */
|
|
1012
|
+
/* Dark theme early overrides (voice recording) */
|
|
1012
1013
|
[data-theme="dark"] .voice-record-btn.recording {
|
|
1013
|
-
background: rgba(239, 68, 68, 0.
|
|
1014
|
+
background: rgba(239, 68, 68, 0.2);
|
|
1014
1015
|
}
|
|
1015
1016
|
|
|
1016
1017
|
/* ── Toast ── */
|
|
@@ -1021,10 +1022,10 @@ input,textarea,select{font:inherit}
|
|
|
1021
1022
|
display:flex;align-items:center;gap:8px;
|
|
1022
1023
|
}
|
|
1023
1024
|
@keyframes toastIn{from{transform:translateX(20px);opacity:0}to{transform:translateX(0);opacity:1}}
|
|
1024
|
-
.toast-error{background:#
|
|
1025
|
-
.toast-success{background:#
|
|
1026
|
-
.toast-info{background:#
|
|
1027
|
-
.toast-warning{background:#
|
|
1025
|
+
.toast-error{background:#2d1515;color:#f87171;border:1px solid #3d1f1f}
|
|
1026
|
+
.toast-success{background:#152d1e;color:#4ade80;border:1px solid #1f3d28}
|
|
1027
|
+
.toast-info{background:#1a2535;color:#60a5fa;border:1px solid #253545}
|
|
1028
|
+
.toast-warning{background:#2d2515;color:#fbbf24;border:1px solid #3d3019}
|
|
1028
1029
|
|
|
1029
1030
|
/* ── Confirm Dialog ── */
|
|
1030
1031
|
.modal-overlay{
|
|
@@ -1158,11 +1159,11 @@ input,textarea,select{font:inherit}
|
|
|
1158
1159
|
background:linear-gradient(135deg,var(--accent-light),var(--bg));
|
|
1159
1160
|
}
|
|
1160
1161
|
.rp-master-card:hover{border-color:var(--accent);background:var(--accent-light)}
|
|
1161
|
-
.rp-master-card.active{border-color:var(--accent);background:var(--accent-light);box-shadow:0 0 16px rgba(
|
|
1162
|
+
.rp-master-card.active{border-color:var(--accent);background:var(--accent-light);box-shadow:0 0 16px rgba(217,119,6,.15)}
|
|
1162
1163
|
.rp-master-avatar{
|
|
1163
1164
|
width:44px;height:44px;border-radius:12px;flex-shrink:0;
|
|
1164
1165
|
display:grid;place-items:center;font-size:22px;color:#fff;
|
|
1165
|
-
background:linear-gradient(135deg
|
|
1166
|
+
background:linear-gradient(135deg,var(--accent),var(--accent-dark));
|
|
1166
1167
|
}
|
|
1167
1168
|
.rp-master-info{flex:1;min-width:0}
|
|
1168
1169
|
.rp-master-name{font-size:14px;font-weight:700;color:var(--text)}
|
|
@@ -1263,8 +1264,8 @@ input,textarea,select{font:inherit}
|
|
|
1263
1264
|
font-size:9px;font-weight:600;padding:1px 5px;border-radius:4px;
|
|
1264
1265
|
white-space:nowrap;flex-shrink:0;
|
|
1265
1266
|
}
|
|
1266
|
-
.exec-badge.local{background
|
|
1267
|
-
.exec-badge.sandbox{background
|
|
1267
|
+
.exec-badge.local{background:rgba(74,222,128,.15);color:#4ade80}
|
|
1268
|
+
.exec-badge.sandbox{background:rgba(251,191,36,.15);color:#fbbf24}
|
|
1268
1269
|
|
|
1269
1270
|
/* Agent add-child button (shows on hover) */
|
|
1270
1271
|
.agent-add-child-btn{
|
|
@@ -1818,7 +1819,7 @@ input,textarea,select{font:inherit}
|
|
|
1818
1819
|
font-family:'SF Mono','Fira Code','Cascadia Code',monospace;font-size:12.5px;
|
|
1819
1820
|
}
|
|
1820
1821
|
.group-msg-bubble pre{
|
|
1821
|
-
background
|
|
1822
|
+
background:var(--bg3);color:var(--text);padding:14px 16px;border-radius:var(--radius-sm);
|
|
1822
1823
|
overflow-x:auto;margin:8px 0;font-size:12.5px;line-height:1.5;
|
|
1823
1824
|
font-family:'SF Mono','Fira Code','Cascadia Code',monospace;
|
|
1824
1825
|
}
|
|
@@ -2100,31 +2101,31 @@ input,textarea,select{font:inherit}
|
|
|
2100
2101
|
|
|
2101
2102
|
/* ── Dark Theme Overrides ── */
|
|
2102
2103
|
[data-theme="dark"] .message-bubble code{background:rgba(255,255,255,.1)}
|
|
2103
|
-
[data-theme="dark"] .message-row.user .message-bubble code{background:rgba(
|
|
2104
|
-
[data-theme="dark"] .message-bubble pre{background:#
|
|
2105
|
-
[data-theme="dark"] .group-msg-bubble code{background:rgba(255,255,255,.
|
|
2106
|
-
[data-theme="dark"] .group-msg-bubble pre{background:#
|
|
2104
|
+
[data-theme="dark"] .message-row.user .message-bubble code{background:rgba(0,0,0,.15)}
|
|
2105
|
+
[data-theme="dark"] .message-bubble pre{background:#161412;color:#d4cdc0}
|
|
2106
|
+
[data-theme="dark"] .group-msg-bubble code{background:rgba(255,255,255,.06)}
|
|
2107
|
+
[data-theme="dark"] .group-msg-bubble pre{background:#161412;color:#d4cdc0}
|
|
2107
2108
|
[data-theme="dark"] .toast-error{background:rgba(127,29,29,.8);color:#fca5a5;border-color:rgba(239,68,68,.4)}
|
|
2108
2109
|
[data-theme="dark"] .toast-success{background:rgba(6,78,59,.8);color:#6ee7b7;border-color:rgba(16,185,129,.4)}
|
|
2109
2110
|
[data-theme="dark"] .toast-info{background:rgba(30,64,175,.8);color:#93c5fd;border-color:rgba(59,130,246,.4)}
|
|
2110
2111
|
[data-theme="dark"] .toast-warning{background:rgba(113,63,18,.8);color:#fcd34d;border-color:rgba(245,158,11,.4)}
|
|
2111
|
-
[data-theme="dark"] .modal-overlay{background:rgba(0,0,0,.
|
|
2112
|
-
[data-theme="dark"] .exec-badge.local{background:rgba(
|
|
2113
|
-
[data-theme="dark"] .exec-badge.sandbox{background:rgba(
|
|
2112
|
+
[data-theme="dark"] .modal-overlay{background:rgba(0,0,0,.6)}
|
|
2113
|
+
[data-theme="dark"] .exec-badge.local{background:rgba(74,222,128,.15);color:#4ade80}
|
|
2114
|
+
[data-theme="dark"] .exec-badge.sandbox{background:rgba(251,191,36,.15);color:#fbbf24}
|
|
2114
2115
|
[data-theme="dark"] .mode-btn.active-chat{background:var(--bg2);color:var(--accent);box-shadow:var(--shadow-sm)}
|
|
2115
2116
|
[data-theme="dark"] .mode-btn.active-exec{background:var(--accent);color:#fff;box-shadow:var(--shadow-sm)}
|
|
2116
|
-
[data-theme="dark"] .exec-mode-btn.active-local{background:rgba(
|
|
2117
|
-
[data-theme="dark"] .exec-mode-btn.active-local:hover{background:rgba(
|
|
2118
|
-
[data-theme="dark"] .lock-indicator{background:rgba(127,29,29,.3);border-color:rgba(
|
|
2117
|
+
[data-theme="dark"] .exec-mode-btn.active-local{background:rgba(74,222,128,.15);color:#4ade80;border-color:rgba(74,222,128,.3)}
|
|
2118
|
+
[data-theme="dark"] .exec-mode-btn.active-local:hover{background:rgba(74,222,128,.25)}
|
|
2119
|
+
[data-theme="dark"] .lock-indicator{background:rgba(127,29,29,.3);border-color:rgba(248,113,113,.3);color:#f87171}
|
|
2119
2120
|
[data-theme="dark"] .lock-indicator:hover{background:rgba(127,29,29,.5)}
|
|
2120
|
-
[data-theme="dark"] .code-block-wrapper pre{background:#
|
|
2121
|
+
[data-theme="dark"] .code-block-wrapper pre{background:#161412;color:#d4cdc0}
|
|
2121
2122
|
[data-theme="dark"] .code-copy-btn{background:var(--bg4);color:var(--text2)}
|
|
2122
2123
|
[data-theme="dark"] .code-copy-btn:hover{background:var(--bg5);color:var(--text)}
|
|
2123
|
-
[data-theme="dark"] .code-copy-btn.copied{background:rgba(
|
|
2124
|
-
[data-theme="dark"] .group-member-role.owner{background:rgba(
|
|
2125
|
-
[data-theme="dark"] .group-member-role.admin{background:rgba(
|
|
2126
|
-
[data-theme="dark"] .setup-wizard-overlay{background:rgba(
|
|
2127
|
-
[data-theme="dark"] .largetext-option:hover,[data-theme="dark"] .largetext-option.selected{border-color:var(--accent);background:rgba(
|
|
2124
|
+
[data-theme="dark"] .code-copy-btn.copied{background:rgba(74,222,128,.3);color:#a7f3d0}
|
|
2125
|
+
[data-theme="dark"] .group-member-role.owner{background:rgba(251,191,36,.15);color:#fbbf24}
|
|
2126
|
+
[data-theme="dark"] .group-member-role.admin{background:rgba(96,165,250,.15);color:#60a5fa}
|
|
2127
|
+
[data-theme="dark"] .setup-wizard-overlay{background:rgba(0,0,0,.9)}
|
|
2128
|
+
[data-theme="dark"] .largetext-option:hover,[data-theme="dark"] .largetext-option.selected{border-color:var(--accent);background:rgba(217,119,6,.1)}
|
|
2128
2129
|
[data-theme="dark"] .platform-toggle{background:var(--bg4)}
|
|
2129
2130
|
[data-theme="dark"] .quick-action{background:var(--bg2);border-color:var(--border)}
|
|
2130
2131
|
[data-theme="dark"] .quick-action:hover{border-color:var(--accent);color:var(--accent);background:var(--accent-light)}
|
|
@@ -2173,7 +2174,7 @@ input,textarea,select{font:inherit}
|
|
|
2173
2174
|
.timeline-segment p{margin-bottom:8px}
|
|
2174
2175
|
.timeline-segment p:last-child{margin-bottom:0}
|
|
2175
2176
|
.timeline-segment code{background:rgba(0,0,0,.06);padding:2px 6px;border-radius:4px;font-family:'SF Mono','Fira Code','Cascadia Code',monospace;font-size:12.5px}
|
|
2176
|
-
.timeline-segment pre{background
|
|
2177
|
+
.timeline-segment pre{background:var(--bg3);color:var(--text);padding:14px 16px;border-radius:var(--radius-sm);overflow-x:auto;margin:8px 0;font-size:12.5px;line-height:1.5;max-width:100%;white-space:pre-wrap;word-break:break-all}
|
|
2177
2178
|
.timeline-segment strong{font-weight:600}
|
|
2178
2179
|
.timeline-segment em{font-style:italic}
|
|
2179
2180
|
.timeline-segment ul,.timeline-segment ol{padding-left:20px;margin:2px 0}
|
|
@@ -2239,10 +2240,10 @@ input,textarea,select{font:inherit}
|
|
|
2239
2240
|
[data-theme="dark"] .exec-event-icon.code-ok{background:rgba(22,163,74,.15);color:#86efac}
|
|
2240
2241
|
[data-theme="dark"] .exec-event-icon.code-fail{background:rgba(220,38,38,.15);color:#fca5a5}
|
|
2241
2242
|
[data-theme="dark"] .exec-event-icon.code-timeout{background:rgba(219,39,119,.15);color:#f9a8d4}
|
|
2242
|
-
[data-theme="dark"] .exec-event-icon.skill{background:rgba(
|
|
2243
|
+
[data-theme="dark"] .exec-event-icon.skill{background:rgba(217,119,6,.15);color:#fbbf24}
|
|
2243
2244
|
[data-theme="dark"] .exec-events-panel{background:var(--bg2);border-color:var(--border)}
|
|
2244
2245
|
[data-theme="dark"] .exec-result-modal{background:var(--bg2);border-color:var(--border)}
|
|
2245
|
-
[data-theme="dark"] .exec-result-modal-body pre{background:#
|
|
2246
|
+
[data-theme="dark"] .exec-result-modal-body pre{background:#161412;color:#d4cdc0}
|
|
2246
2247
|
[data-theme="dark"] .exec-result-info-item{background:var(--bg3)}
|
|
2247
2248
|
[data-theme="dark"] .inline-exec-event{background:var(--bg3);border-left-color:var(--border)}
|
|
2248
2249
|
[data-theme="dark"] .inline-exec-event.tool-call-pending{border-left-color:rgba(96,165,250,.6)}
|
|
@@ -2481,7 +2482,7 @@ body.popout-mode #popoutBtn{display:none !important}
|
|
|
2481
2482
|
transition: border-color .3s ease, box-shadow .3s ease;
|
|
2482
2483
|
}
|
|
2483
2484
|
.v2-tool-event.tool-running {
|
|
2484
|
-
border-left-color: var(--accent, #
|
|
2485
|
+
border-left-color: var(--accent, #D97706);
|
|
2485
2486
|
}
|
|
2486
2487
|
.v2-tool-event.tool-success {
|
|
2487
2488
|
border-left-color: var(--ok, #10b981);
|
|
@@ -2567,15 +2568,15 @@ body.popout-mode #popoutBtn{display:none !important}
|
|
|
2567
2568
|
border: 1px solid var(--border, #e0e0e8);
|
|
2568
2569
|
border-radius: 4px;
|
|
2569
2570
|
font-size: 12px;
|
|
2570
|
-
color: var(--accent, #
|
|
2571
|
+
color: var(--accent, #D97706);
|
|
2571
2572
|
cursor: pointer;
|
|
2572
2573
|
transition: all 0.15s ease;
|
|
2573
2574
|
}
|
|
2574
2575
|
|
|
2575
2576
|
.v2-tool-event .inline-exec-result-btn:hover {
|
|
2576
|
-
background: var(--accent, #
|
|
2577
|
+
background: var(--accent, #D97706);
|
|
2577
2578
|
color: white;
|
|
2578
|
-
border-color: var(--accent, #
|
|
2579
|
+
border-color: var(--accent, #D97706);
|
|
2579
2580
|
}
|
|
2580
2581
|
|
|
2581
2582
|
.v2-tool-event .inline-exec-result-btn svg {
|
|
@@ -2740,3 +2741,264 @@ body.popout-mode #popoutBtn{display:none !important}
|
|
|
2740
2741
|
background:var(--accent);
|
|
2741
2742
|
color:#fff;
|
|
2742
2743
|
}
|
|
2744
|
+
|
|
2745
|
+
/* ── [v1.24.0] Update Notification Banner ── */
|
|
2746
|
+
.update-notification-banner{
|
|
2747
|
+
position:fixed;top:0;left:0;right:0;z-index:99998;
|
|
2748
|
+
transform:translateY(-100%);
|
|
2749
|
+
transition:transform .35s cubic-bezier(.4,0,.2,1);
|
|
2750
|
+
pointer-events:none;
|
|
2751
|
+
}
|
|
2752
|
+
.update-notification-banner.visible{
|
|
2753
|
+
transform:translateY(0);
|
|
2754
|
+
pointer-events:auto;
|
|
2755
|
+
}
|
|
2756
|
+
.update-banner-content{
|
|
2757
|
+
display:flex;align-items:center;gap:14px;
|
|
2758
|
+
padding:12px 20px;
|
|
2759
|
+
background:var(--bg2);
|
|
2760
|
+
border-bottom:1px solid var(--border);
|
|
2761
|
+
box-shadow:0 4px 20px rgba(0,0,0,.12);
|
|
2762
|
+
}
|
|
2763
|
+
.update-banner-icon{
|
|
2764
|
+
width:36px;height:36px;border-radius:10px;
|
|
2765
|
+
background:var(--accent-light);
|
|
2766
|
+
display:grid;place-items:center;flex-shrink:0;
|
|
2767
|
+
color:var(--accent);
|
|
2768
|
+
animation:bannerIconSpin 3s linear infinite;
|
|
2769
|
+
}
|
|
2770
|
+
@keyframes bannerIconSpin{
|
|
2771
|
+
0%{transform:rotate(0deg)}
|
|
2772
|
+
100%{transform:rotate(360deg)}
|
|
2773
|
+
}
|
|
2774
|
+
.update-banner-info{flex:1;min-width:0}
|
|
2775
|
+
.update-banner-title{
|
|
2776
|
+
font-size:14px;font-weight:600;color:var(--text);
|
|
2777
|
+
display:flex;align-items:center;gap:6px;
|
|
2778
|
+
margin-bottom:2px;
|
|
2779
|
+
}
|
|
2780
|
+
.update-banner-ver{
|
|
2781
|
+
display:inline-block;padding:1px 8px;border-radius:10px;
|
|
2782
|
+
background:var(--accent);color:#fff;font-size:11px;font-weight:600;
|
|
2783
|
+
animation:bannerVerPulse 2s ease-in-out infinite;
|
|
2784
|
+
}
|
|
2785
|
+
@keyframes bannerVerPulse{
|
|
2786
|
+
0%,100%{opacity:1}
|
|
2787
|
+
50%{opacity:.75}
|
|
2788
|
+
}
|
|
2789
|
+
.update-banner-desc{
|
|
2790
|
+
display:flex;align-items:center;gap:12px;flex-wrap:wrap;
|
|
2791
|
+
font-size:12px;color:var(--text2);
|
|
2792
|
+
}
|
|
2793
|
+
.update-banner-countdown-wrap{
|
|
2794
|
+
display:flex;align-items:center;gap:2px;
|
|
2795
|
+
}
|
|
2796
|
+
.update-banner-cd-num{
|
|
2797
|
+
display:inline-block;min-width:20px;text-align:center;
|
|
2798
|
+
font-size:18px;font-weight:800;color:var(--accent);
|
|
2799
|
+
font-variant-numeric:tabular-nums;
|
|
2800
|
+
animation:bannerCdPulse 1s ease-in-out infinite;
|
|
2801
|
+
line-height:1;
|
|
2802
|
+
}
|
|
2803
|
+
@keyframes bannerCdPulse{
|
|
2804
|
+
0%,100%{transform:scale(1)}
|
|
2805
|
+
50%{transform:scale(1.15)}
|
|
2806
|
+
}
|
|
2807
|
+
.update-banner-reassure{
|
|
2808
|
+
display:inline-flex;align-items:center;gap:4px;
|
|
2809
|
+
padding:2px 10px;border-radius:12px;
|
|
2810
|
+
background:rgba(16,185,129,.08);color:var(--ok);
|
|
2811
|
+
font-size:11px;font-weight:500;
|
|
2812
|
+
border:1px solid rgba(16,185,129,.15);
|
|
2813
|
+
}
|
|
2814
|
+
.update-banner-reassure::before{
|
|
2815
|
+
content:'';display:inline-block;width:6px;height:6px;
|
|
2816
|
+
border-radius:50%;background:var(--ok);flex-shrink:0;
|
|
2817
|
+
animation:reassureDot 2s ease-in-out infinite;
|
|
2818
|
+
}
|
|
2819
|
+
@keyframes reassureDot{
|
|
2820
|
+
0%,100%{opacity:1;transform:scale(1)}
|
|
2821
|
+
50%{opacity:.4;transform:scale(.7)}
|
|
2822
|
+
}
|
|
2823
|
+
.update-banner-actions{
|
|
2824
|
+
display:flex;gap:8px;flex-shrink:0;
|
|
2825
|
+
}
|
|
2826
|
+
.update-banner-btn{
|
|
2827
|
+
padding:6px 16px;border-radius:var(--radius-xs);
|
|
2828
|
+
font-size:12px;font-weight:600;cursor:pointer;
|
|
2829
|
+
transition:var(--transition);white-space:nowrap;
|
|
2830
|
+
border:1px solid var(--border);
|
|
2831
|
+
}
|
|
2832
|
+
.update-banner-btn-primary{
|
|
2833
|
+
background:var(--accent);color:#fff;border-color:var(--accent);
|
|
2834
|
+
}
|
|
2835
|
+
.update-banner-btn-primary:hover{
|
|
2836
|
+
background:var(--accent2);transform:translateY(-1px);
|
|
2837
|
+
box-shadow:0 2px 8px rgba(201,100,66,.3);
|
|
2838
|
+
}
|
|
2839
|
+
.update-banner-btn-secondary{
|
|
2840
|
+
background:var(--bg);color:var(--text2);
|
|
2841
|
+
}
|
|
2842
|
+
.update-banner-btn-secondary:hover{
|
|
2843
|
+
background:var(--bg3);color:var(--text);
|
|
2844
|
+
}
|
|
2845
|
+
.update-banner-close{
|
|
2846
|
+
width:28px;height:28px;border-radius:50%;
|
|
2847
|
+
display:grid;place-items:center;flex-shrink:0;
|
|
2848
|
+
color:var(--text3);cursor:pointer;transition:var(--transition);
|
|
2849
|
+
border:none;background:none;
|
|
2850
|
+
}
|
|
2851
|
+
.update-banner-close:hover{
|
|
2852
|
+
background:var(--bg3);color:var(--text);
|
|
2853
|
+
}
|
|
2854
|
+
/* Banner dark theme */
|
|
2855
|
+
[data-theme="dark"] .update-banner-content{
|
|
2856
|
+
background:var(--bg2);
|
|
2857
|
+
border-bottom-color:var(--border);
|
|
2858
|
+
box-shadow:0 4px 20px rgba(0,0,0,.35);
|
|
2859
|
+
}
|
|
2860
|
+
[data-theme="dark"] .update-banner-reassure{
|
|
2861
|
+
background:rgba(74,222,128,.08);
|
|
2862
|
+
border-color:rgba(74,222,128,.15);
|
|
2863
|
+
}
|
|
2864
|
+
/* Banner mobile responsive */
|
|
2865
|
+
@media(max-width:768px){
|
|
2866
|
+
.update-banner-content{
|
|
2867
|
+
padding:10px 14px;gap:10px;flex-wrap:wrap;
|
|
2868
|
+
}
|
|
2869
|
+
.update-banner-desc{gap:6px}
|
|
2870
|
+
.update-banner-actions{width:100%;justify-content:flex-end}
|
|
2871
|
+
.update-banner-icon{width:32px;height:32px}
|
|
2872
|
+
.update-banner-cd-num{font-size:16px}
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2875
|
+
/* ── [v1.24.0] Update Overlay ── */
|
|
2876
|
+
.update-overlay{
|
|
2877
|
+
position:fixed;inset:0;z-index:99999;
|
|
2878
|
+
display:flex;align-items:center;justify-content:center;
|
|
2879
|
+
background:rgba(0,0,0,.65);
|
|
2880
|
+
animation:updateOverlayIn .4s ease;
|
|
2881
|
+
backdrop-filter:blur(4px);
|
|
2882
|
+
}
|
|
2883
|
+
@keyframes updateOverlayIn{from{opacity:0}to{opacity:1}}
|
|
2884
|
+
.update-overlay-content{
|
|
2885
|
+
background:var(--bg2);border-radius:16px;
|
|
2886
|
+
padding:40px 48px;text-align:center;
|
|
2887
|
+
max-width:420px;width:90%;
|
|
2888
|
+
box-shadow:0 25px 60px rgba(0,0,0,.3);
|
|
2889
|
+
border:1px solid var(--border);
|
|
2890
|
+
animation:updateContentIn .5s ease;
|
|
2891
|
+
}
|
|
2892
|
+
@keyframes updateContentIn{from{opacity:0;transform:scale(.92) translateY(16px)}to{opacity:1;transform:scale(1) translateY(0)}}
|
|
2893
|
+
.update-overlay-icon{
|
|
2894
|
+
width:56px;height:56px;border-radius:50%;
|
|
2895
|
+
margin:0 auto 20px;
|
|
2896
|
+
display:flex;align-items:center;justify-content:center;
|
|
2897
|
+
background:var(--accent-light);
|
|
2898
|
+
}
|
|
2899
|
+
.update-spinner{
|
|
2900
|
+
width:28px;height:28px;
|
|
2901
|
+
border:3px solid var(--bg4);
|
|
2902
|
+
border-top-color:var(--accent);
|
|
2903
|
+
border-radius:50%;
|
|
2904
|
+
animation:spin 1s linear infinite;
|
|
2905
|
+
}
|
|
2906
|
+
.update-overlay-title{
|
|
2907
|
+
font-size:20px;font-weight:700;color:var(--text);
|
|
2908
|
+
margin-bottom:8px;
|
|
2909
|
+
}
|
|
2910
|
+
.update-overlay-status{
|
|
2911
|
+
font-size:14px;color:var(--text2);
|
|
2912
|
+
margin-bottom:24px;line-height:1.6;
|
|
2913
|
+
}
|
|
2914
|
+
.update-overlay-countdown{
|
|
2915
|
+
display:flex;align-items:baseline;justify-content:center;gap:6px;
|
|
2916
|
+
margin-bottom:20px;
|
|
2917
|
+
}
|
|
2918
|
+
.update-countdown-num{
|
|
2919
|
+
font-size:48px;font-weight:800;color:var(--accent);
|
|
2920
|
+
font-variant-numeric:tabular-nums;
|
|
2921
|
+
line-height:1;
|
|
2922
|
+
animation:countdownPulse 1.5s ease-in-out infinite;
|
|
2923
|
+
}
|
|
2924
|
+
@keyframes countdownPulse{0%,100%{opacity:1}50%{opacity:.7}}
|
|
2925
|
+
.update-countdown-label{
|
|
2926
|
+
font-size:14px;color:var(--text2);font-weight:500;
|
|
2927
|
+
}
|
|
2928
|
+
.update-overlay-progress{
|
|
2929
|
+
width:100%;height:4px;background:var(--bg4);
|
|
2930
|
+
border-radius:2px;overflow:hidden;
|
|
2931
|
+
margin-bottom:20px;
|
|
2932
|
+
}
|
|
2933
|
+
.update-progress-bar{
|
|
2934
|
+
height:100%;width:0%;border-radius:2px;
|
|
2935
|
+
background:linear-gradient(90deg,var(--accent),var(--accent2));
|
|
2936
|
+
transition:width .5s ease;
|
|
2937
|
+
}
|
|
2938
|
+
.update-overlay-progress.active .update-progress-bar{
|
|
2939
|
+
animation:updateProgress 2s ease-in-out infinite;
|
|
2940
|
+
}
|
|
2941
|
+
@keyframes updateProgress{
|
|
2942
|
+
0%{width:5%;margin-left:0}
|
|
2943
|
+
50%{width:45%;margin-left:25%}
|
|
2944
|
+
100%{width:5%;margin-left:90%}
|
|
2945
|
+
}
|
|
2946
|
+
.update-overlay-reassure{
|
|
2947
|
+
display:flex;align-items:center;justify-content:center;gap:8px;
|
|
2948
|
+
padding:12px 20px;
|
|
2949
|
+
background:rgba(16,185,129,.08);
|
|
2950
|
+
border-radius:var(--radius-sm);
|
|
2951
|
+
color:var(--ok);font-size:13px;font-weight:500;
|
|
2952
|
+
line-height:1.5;
|
|
2953
|
+
border:1px solid rgba(16,185,129,.12);
|
|
2954
|
+
}
|
|
2955
|
+
.update-overlay-reassure svg{flex-shrink:0;color:var(--ok);opacity:.7}
|
|
2956
|
+
.update-overlay.restarting .update-overlay-icon{
|
|
2957
|
+
background:rgba(239,68,68,.12);
|
|
2958
|
+
}
|
|
2959
|
+
.update-overlay.restarting .update-spinner{
|
|
2960
|
+
border-top-color:var(--danger);
|
|
2961
|
+
}
|
|
2962
|
+
.update-overlay.success .update-overlay-icon{
|
|
2963
|
+
background:rgba(16,185,129,.12);
|
|
2964
|
+
}
|
|
2965
|
+
.update-overlay.success .update-spinner{
|
|
2966
|
+
animation:none;
|
|
2967
|
+
border:none;
|
|
2968
|
+
border-top:none;
|
|
2969
|
+
font-size:24px;
|
|
2970
|
+
color:var(--ok);
|
|
2971
|
+
}
|
|
2972
|
+
.update-overlay.success .update-spinner::after{
|
|
2973
|
+
content:'✓';
|
|
2974
|
+
font-weight:700;
|
|
2975
|
+
}
|
|
2976
|
+
.update-overlay.failed .update-overlay-icon{
|
|
2977
|
+
background:rgba(239,68,68,.12);
|
|
2978
|
+
}
|
|
2979
|
+
.update-overlay.failed .update-spinner{
|
|
2980
|
+
animation:none;
|
|
2981
|
+
border:none;
|
|
2982
|
+
border-top:none;
|
|
2983
|
+
font-size:24px;
|
|
2984
|
+
color:var(--danger);
|
|
2985
|
+
}
|
|
2986
|
+
.update-overlay.failed .update-spinner::after{
|
|
2987
|
+
content:'✗';
|
|
2988
|
+
font-weight:700;
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
/* Update overlay dark theme */
|
|
2992
|
+
[data-theme="dark"] .update-overlay{background:rgba(0,0,0,.8)}
|
|
2993
|
+
[data-theme="dark"] .update-overlay-content{background:var(--bg2);border-color:var(--border)}
|
|
2994
|
+
[data-theme="dark"] .update-overlay-reassure{
|
|
2995
|
+
background:rgba(74,222,128,.06);
|
|
2996
|
+
border-color:rgba(74,222,128,.1);
|
|
2997
|
+
}
|
|
2998
|
+
|
|
2999
|
+
/* Mobile responsive for update overlay */
|
|
3000
|
+
@media(max-width:480px){
|
|
3001
|
+
.update-overlay-content{padding:28px 24px}
|
|
3002
|
+
.update-countdown-num{font-size:36px}
|
|
3003
|
+
.update-overlay-title{font-size:18px}
|
|
3004
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
6
6
|
<title>MyAgent - AI 助手</title>
|
|
7
|
-
<link rel="stylesheet" href="chat.css?v=
|
|
7
|
+
<link rel="stylesheet" href="chat.css?v=12">
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
<div class="app">
|
|
@@ -320,6 +320,53 @@
|
|
|
320
320
|
</div>
|
|
321
321
|
</div>
|
|
322
322
|
|
|
323
|
+
<!-- [v1.24.0] Update Notification Banner -->
|
|
324
|
+
<div id="updateNotificationBanner" class="update-notification-banner" style="display:none">
|
|
325
|
+
<div class="update-banner-content">
|
|
326
|
+
<div class="update-banner-icon">
|
|
327
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><path d="M21 12a9 9 0 11-6.22-8.56"/><polyline points="21 3 21 9 15 9"/></svg>
|
|
328
|
+
</div>
|
|
329
|
+
<div class="update-banner-info">
|
|
330
|
+
<div class="update-banner-title">发现新版本 <span id="updateBannerVersion" class="update-banner-ver"></span></div>
|
|
331
|
+
<div class="update-banner-desc">
|
|
332
|
+
<span class="update-banner-countdown-wrap">
|
|
333
|
+
更新将在 <span id="updateBannerCountdown" class="update-banner-cd-num">60</span> 秒后自动开始
|
|
334
|
+
</span>
|
|
335
|
+
<span class="update-banner-reassure">不用担心,更新完成后任务将自动继续执行</span>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
<div class="update-banner-actions">
|
|
339
|
+
<button class="update-banner-btn update-banner-btn-primary" onclick="bannerUpdateNow()">立即更新</button>
|
|
340
|
+
<button class="update-banner-btn update-banner-btn-secondary" onclick="dismissUpdateBanner()">稍后提醒</button>
|
|
341
|
+
</div>
|
|
342
|
+
<button class="update-banner-close" onclick="dismissUpdateBanner()" title="关闭">
|
|
343
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
|
344
|
+
</button>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
|
|
348
|
+
<!-- [v1.24.0] Update Countdown Overlay -->
|
|
349
|
+
<div id="updateOverlay" style="display:none" class="update-overlay">
|
|
350
|
+
<div class="update-overlay-content">
|
|
351
|
+
<div class="update-overlay-icon" id="updateOverlayIcon">
|
|
352
|
+
<span class="update-spinner"></span>
|
|
353
|
+
</div>
|
|
354
|
+
<h2 class="update-overlay-title" id="updateOverlayTitle">正在准备更新...</h2>
|
|
355
|
+
<p class="update-overlay-status" id="updateOverlayStatus">正在安全停止当前任务</p>
|
|
356
|
+
<div class="update-overlay-countdown" id="updateOverlayCountdown" style="display:none">
|
|
357
|
+
<span class="update-countdown-num" id="updateCountdownNum">30</span>
|
|
358
|
+
<span class="update-countdown-label">秒后重启</span>
|
|
359
|
+
</div>
|
|
360
|
+
<div class="update-overlay-progress">
|
|
361
|
+
<div class="update-progress-bar" id="updateProgressBar"></div>
|
|
362
|
+
</div>
|
|
363
|
+
<div class="update-overlay-reassure" id="updateOverlayReassure">
|
|
364
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>
|
|
365
|
+
<span>更新完成后任务将自动继续,无需担心</span>
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
</div>
|
|
369
|
+
|
|
323
370
|
<!-- Mobile Overlay -->
|
|
324
371
|
<div class="mobile-overlay" id="chatMobileOverlay"></div>
|
|
325
372
|
<!-- Toast Container -->
|
|
@@ -330,6 +377,6 @@
|
|
|
330
377
|
<div id="groupModalContainer"></div>
|
|
331
378
|
|
|
332
379
|
<!-- Load JS scripts (no HTML fragments to fetch) -->
|
|
333
|
-
<script src="chat.js?v=
|
|
380
|
+
<script src="chat.js?v=10"></script>
|
|
334
381
|
</body>
|
|
335
382
|
</html>
|
package/web/ui/chat/chat_main.js
CHANGED
|
@@ -980,6 +980,12 @@ async function loadStatus() {
|
|
|
980
980
|
}
|
|
981
981
|
|
|
982
982
|
// ── 版本号 & 更新检查 (chat 页面) ──
|
|
983
|
+
var _bannerCountdownTimer = null;
|
|
984
|
+
var _bannerCountdownSec = 0;
|
|
985
|
+
var _bannerDismissed = false;
|
|
986
|
+
var _updateStatusPollTimer = null;
|
|
987
|
+
var _updateLatestInfo = null; // 存储最新版本信息
|
|
988
|
+
|
|
983
989
|
async function loadChatVersion() {
|
|
984
990
|
try {
|
|
985
991
|
const r = await api('/api/status');
|
|
@@ -992,10 +998,119 @@ async function loadChatVersion() {
|
|
|
992
998
|
try {
|
|
993
999
|
const r = await api('/api/update/check', {method: 'POST'});
|
|
994
1000
|
if (r.has_update) {
|
|
1001
|
+
_updateLatestInfo = r;
|
|
995
1002
|
document.getElementById('chatUpdateBadge').style.display = 'inline';
|
|
1003
|
+
showUpdateBanner(r);
|
|
996
1004
|
}
|
|
997
1005
|
} catch (e) {}
|
|
998
1006
|
}, 30000);
|
|
1007
|
+
// 启动定期轮询更新状态(检测从其他来源触发的更新)
|
|
1008
|
+
startUpdateStatusPolling();
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* 显示更新通知横幅,带倒计时自动更新。
|
|
1013
|
+
* @param {object} updateInfo - {latest_version, changelog, ...}
|
|
1014
|
+
*/
|
|
1015
|
+
function showUpdateBanner(updateInfo) {
|
|
1016
|
+
var banner = document.getElementById('updateNotificationBanner');
|
|
1017
|
+
if (!banner) return;
|
|
1018
|
+
_bannerDismissed = false;
|
|
1019
|
+
// 设置版本号
|
|
1020
|
+
var verEl = document.getElementById('updateBannerVersion');
|
|
1021
|
+
if (verEl && updateInfo && updateInfo.latest_version) {
|
|
1022
|
+
verEl.textContent = 'v' + updateInfo.latest_version;
|
|
1023
|
+
}
|
|
1024
|
+
// 重置倒计时
|
|
1025
|
+
_bannerCountdownSec = 60;
|
|
1026
|
+
var cdEl = document.getElementById('updateBannerCountdown');
|
|
1027
|
+
if (cdEl) cdEl.textContent = _bannerCountdownSec;
|
|
1028
|
+
// 显示横幅
|
|
1029
|
+
banner.style.display = 'block';
|
|
1030
|
+
banner.className = 'update-notification-banner visible';
|
|
1031
|
+
// 启动倒计时
|
|
1032
|
+
if (_bannerCountdownTimer) clearInterval(_bannerCountdownTimer);
|
|
1033
|
+
_bannerCountdownTimer = setInterval(function() {
|
|
1034
|
+
_bannerCountdownSec--;
|
|
1035
|
+
if (cdEl) cdEl.textContent = Math.max(0, _bannerCountdownSec);
|
|
1036
|
+
if (_bannerCountdownSec <= 0) {
|
|
1037
|
+
clearInterval(_bannerCountdownTimer);
|
|
1038
|
+
_bannerCountdownTimer = null;
|
|
1039
|
+
// 倒计时结束,自动开始更新
|
|
1040
|
+
bannerUpdateNow();
|
|
1041
|
+
}
|
|
1042
|
+
}, 1000);
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
/**
|
|
1046
|
+
* 关闭更新通知横幅(稍后提醒)。
|
|
1047
|
+
*/
|
|
1048
|
+
function dismissUpdateBanner() {
|
|
1049
|
+
_bannerDismissed = true;
|
|
1050
|
+
if (_bannerCountdownTimer) { clearInterval(_bannerCountdownTimer); _bannerCountdownTimer = null; }
|
|
1051
|
+
var banner = document.getElementById('updateNotificationBanner');
|
|
1052
|
+
if (banner) {
|
|
1053
|
+
banner.className = 'update-notification-banner';
|
|
1054
|
+
setTimeout(function() { banner.style.display = 'none'; }, 300);
|
|
1055
|
+
}
|
|
1056
|
+
toast('将在 10 分钟后再次提醒更新', 'info');
|
|
1057
|
+
// 10 分钟后再次检查更新
|
|
1058
|
+
setTimeout(async function() {
|
|
1059
|
+
try {
|
|
1060
|
+
// 如果当前没有在更新中,重新检查
|
|
1061
|
+
var statusData = await api('/api/update/status');
|
|
1062
|
+
if (statusData.is_updating) return; // 正在更新中,不重复提醒
|
|
1063
|
+
var r = await api('/api/update/check', {method: 'POST'});
|
|
1064
|
+
if (r.has_update) {
|
|
1065
|
+
_updateLatestInfo = r;
|
|
1066
|
+
showUpdateBanner(r);
|
|
1067
|
+
}
|
|
1068
|
+
} catch (e) {}
|
|
1069
|
+
}, 10 * 60 * 1000);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* 从横幅立即开始更新。
|
|
1074
|
+
*/
|
|
1075
|
+
function bannerUpdateNow() {
|
|
1076
|
+
// 先关闭横幅倒计时
|
|
1077
|
+
if (_bannerCountdownTimer) { clearInterval(_bannerCountdownTimer); _bannerCountdownTimer = null; }
|
|
1078
|
+
// 隐藏横幅
|
|
1079
|
+
var banner = document.getElementById('updateNotificationBanner');
|
|
1080
|
+
if (banner) { banner.style.display = 'none'; banner.className = 'update-notification-banner'; }
|
|
1081
|
+
// 调用主更新流程(跳过确认对话框,用户已通过横幅确认)
|
|
1082
|
+
doChatUpdate(true);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* 定期轮询 /api/update/status,检测从其他来源触发的更新(如管理后台、CLI等)。
|
|
1087
|
+
* 当检测到更新正在进行时,自动显示 update overlay。
|
|
1088
|
+
*/
|
|
1089
|
+
function startUpdateStatusPolling() {
|
|
1090
|
+
if (_updateStatusPollTimer) clearInterval(_updateStatusPollTimer);
|
|
1091
|
+
_updateStatusPollTimer = setInterval(async function() {
|
|
1092
|
+
try {
|
|
1093
|
+
var data = await api('/api/update/status');
|
|
1094
|
+
// 检测是否有更新正在进行(可能是从其他地方触发的)
|
|
1095
|
+
if (data.is_updating || (data.status && data.status !== 'idle' && data.status !== 'checking' && data.status !== 'available')) {
|
|
1096
|
+
var overlay = document.getElementById('updateOverlay');
|
|
1097
|
+
// 如果 overlay 没有显示,自动显示它
|
|
1098
|
+
if (overlay && overlay.style.display === 'none') {
|
|
1099
|
+
showUpdateOverlay();
|
|
1100
|
+
updateOverlayUI(data);
|
|
1101
|
+
// 开始轮询更新进度
|
|
1102
|
+
pollUpdateStatus();
|
|
1103
|
+
// 关闭横幅(如果正在显示)
|
|
1104
|
+
if (_bannerCountdownTimer) { clearInterval(_bannerCountdownTimer); _bannerCountdownTimer = null; }
|
|
1105
|
+
var banner = document.getElementById('updateNotificationBanner');
|
|
1106
|
+
if (banner) { banner.style.display = 'none'; banner.className = 'update-notification-banner'; }
|
|
1107
|
+
toast('检测到更新正在进行中...', 'info');
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
} catch (e) {
|
|
1111
|
+
// 静默失败 — 服务器可能在重启中
|
|
1112
|
+
}
|
|
1113
|
+
}, 15000); // 每 15 秒检查一次
|
|
999
1114
|
}
|
|
1000
1115
|
|
|
1001
1116
|
// ══════════════════════════════════════════════════════
|
|
@@ -1153,13 +1268,278 @@ async function stopVNC() {
|
|
|
1153
1268
|
}
|
|
1154
1269
|
}
|
|
1155
1270
|
|
|
1156
|
-
|
|
1157
|
-
|
|
1271
|
+
// ── Update Overlay Controller ──
|
|
1272
|
+
var _updatePollTimer = null;
|
|
1273
|
+
var _updateCountdownTimer = null;
|
|
1274
|
+
var _updateCountdownSec = 0;
|
|
1275
|
+
|
|
1276
|
+
/**
|
|
1277
|
+
* Show the update overlay with initial state.
|
|
1278
|
+
* Called right after the user confirms the update.
|
|
1279
|
+
*/
|
|
1280
|
+
function showUpdateOverlay() {
|
|
1281
|
+
var overlay = document.getElementById('updateOverlay');
|
|
1282
|
+
if (!overlay) return;
|
|
1283
|
+
overlay.style.display = 'flex';
|
|
1284
|
+
overlay.className = 'update-overlay';
|
|
1285
|
+
document.getElementById('updateOverlayTitle').textContent = '正在准备更新...';
|
|
1286
|
+
document.getElementById('updateOverlayStatus').textContent = '正在安全停止当前任务';
|
|
1287
|
+
document.getElementById('updateOverlayCountdown').style.display = 'none';
|
|
1288
|
+
document.getElementById('updateProgressBar').style.width = '10%';
|
|
1289
|
+
document.getElementById('updateProgressBar').parentElement.classList.remove('active');
|
|
1290
|
+
document.getElementById('updateOverlayReassure').style.display = 'flex';
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
/**
|
|
1294
|
+
* Update overlay UI based on backend /api/update/status response.
|
|
1295
|
+
*/
|
|
1296
|
+
function updateOverlayUI(statusData) {
|
|
1297
|
+
var overlay = document.getElementById('updateOverlay');
|
|
1298
|
+
if (!overlay || overlay.style.display === 'none') return;
|
|
1299
|
+
|
|
1300
|
+
var s = statusData.status || 'idle';
|
|
1301
|
+
var titleEl = document.getElementById('updateOverlayTitle');
|
|
1302
|
+
var statusEl = document.getElementById('updateOverlayStatus');
|
|
1303
|
+
var countdownEl = document.getElementById('updateOverlayCountdown');
|
|
1304
|
+
var progressEl = document.getElementById('updateProgressBar');
|
|
1305
|
+
var progressBarParent = progressEl ? progressEl.parentElement : null;
|
|
1306
|
+
var iconEl = document.getElementById('updateOverlayIcon');
|
|
1307
|
+
|
|
1308
|
+
// Status → UI mapping
|
|
1309
|
+
var progressWidth = '10%';
|
|
1310
|
+
var title = '正在准备更新...';
|
|
1311
|
+
var status = '请稍候...';
|
|
1312
|
+
var showCountdown = false;
|
|
1313
|
+
|
|
1314
|
+
if (s === 'draining') {
|
|
1315
|
+
progressWidth = '20%';
|
|
1316
|
+
title = '正在安全停止任务';
|
|
1317
|
+
status = '等待运行中的任务完成,请勿操作...';
|
|
1318
|
+
var bc = statusData.broadcaster;
|
|
1319
|
+
var paused = bc ? (bc.paused_tasks || 0) : 0;
|
|
1320
|
+
var total = bc ? (bc.active_tasks || 0) : 0;
|
|
1321
|
+
if (total > 0) {
|
|
1322
|
+
status = '已暂停 ' + paused + '/' + total + ' 个任务';
|
|
1323
|
+
}
|
|
1324
|
+
} else if (s === 'downloading') {
|
|
1325
|
+
progressWidth = '40%';
|
|
1326
|
+
title = '正在下载更新';
|
|
1327
|
+
status = '正在从远程仓库下载最新版本...';
|
|
1328
|
+
progressBarParent && progressBarParent.classList.add('active');
|
|
1329
|
+
} else if (s === 'updating') {
|
|
1330
|
+
progressWidth = '60%';
|
|
1331
|
+
title = '正在安装更新';
|
|
1332
|
+
status = '正在安装新版本,请耐心等待...';
|
|
1333
|
+
progressBarParent && progressBarParent.classList.add('active');
|
|
1334
|
+
} else if (s === 'reloading') {
|
|
1335
|
+
progressWidth = '80%';
|
|
1336
|
+
title = '正在重载模块';
|
|
1337
|
+
status = '正在热更新代码模块...';
|
|
1338
|
+
} else if (s === 'restarting') {
|
|
1339
|
+
progressWidth = '95%';
|
|
1340
|
+
title = '即将重启';
|
|
1341
|
+
status = '系统即将自动重启以完成更新';
|
|
1342
|
+
showCountdown = true;
|
|
1343
|
+
overlay.className = 'update-overlay restarting';
|
|
1344
|
+
} else if (s === 'success') {
|
|
1345
|
+
progressWidth = '100%';
|
|
1346
|
+
title = '更新完成';
|
|
1347
|
+
status = '服务已成功更新,即将自动刷新页面...';
|
|
1348
|
+
overlay.className = 'update-overlay success';
|
|
1349
|
+
// Auto-reload after short delay
|
|
1350
|
+
setTimeout(function() { location.reload(); }, 2000);
|
|
1351
|
+
} else if (s === 'failed' || s === 'rollback') {
|
|
1352
|
+
title = '更新失败';
|
|
1353
|
+
status = '更新过程中遇到错误,请稍后重试或联系管理员';
|
|
1354
|
+
overlay.className = 'update-overlay failed';
|
|
1355
|
+
// Auto-close overlay after delay
|
|
1356
|
+
setTimeout(hideUpdateOverlay, 5000);
|
|
1357
|
+
} else {
|
|
1358
|
+
// idle / checking
|
|
1359
|
+
title = '正在检查版本...';
|
|
1360
|
+
status = '请稍候...';
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
titleEl.textContent = title;
|
|
1364
|
+
statusEl.textContent = status;
|
|
1365
|
+
progressEl.style.width = progressWidth;
|
|
1366
|
+
countdownEl.style.display = showCountdown ? 'flex' : 'none';
|
|
1367
|
+
|
|
1368
|
+
// Countdown when restarting
|
|
1369
|
+
if (showCountdown && _updateCountdownSec <= 0) {
|
|
1370
|
+
_updateCountdownSec = 30;
|
|
1371
|
+
startUpdateCountdown();
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
/**
|
|
1376
|
+
* Start a countdown timer (shown when status is "restarting").
|
|
1377
|
+
* After countdown reaches 0, try to reload the page.
|
|
1378
|
+
*/
|
|
1379
|
+
function startUpdateCountdown() {
|
|
1380
|
+
if (_updateCountdownTimer) clearInterval(_updateCountdownTimer);
|
|
1381
|
+
_updateCountdownSec = 30;
|
|
1382
|
+
document.getElementById('updateCountdownNum').textContent = _updateCountdownSec;
|
|
1383
|
+
_updateCountdownTimer = setInterval(function() {
|
|
1384
|
+
_updateCountdownSec--;
|
|
1385
|
+
var el = document.getElementById('updateCountdownNum');
|
|
1386
|
+
if (el) el.textContent = Math.max(0, _updateCountdownSec);
|
|
1387
|
+
if (_updateCountdownSec <= 0) {
|
|
1388
|
+
clearInterval(_updateCountdownTimer);
|
|
1389
|
+
_updateCountdownTimer = null;
|
|
1390
|
+
// Force reload — the server may have already restarted
|
|
1391
|
+
location.reload();
|
|
1392
|
+
}
|
|
1393
|
+
}, 1000);
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
/**
|
|
1397
|
+
* Stop update status polling and clean up timers.
|
|
1398
|
+
*/
|
|
1399
|
+
function stopUpdatePolling() {
|
|
1400
|
+
if (_updatePollTimer) { clearInterval(_updatePollTimer); _updatePollTimer = null; }
|
|
1401
|
+
if (_updateCountdownTimer) { clearInterval(_updateCountdownTimer); _updateCountdownTimer = null; }
|
|
1402
|
+
_updateCountdownSec = 0;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
/**
|
|
1406
|
+
* Hide the update overlay and stop polling.
|
|
1407
|
+
*/
|
|
1408
|
+
function hideUpdateOverlay() {
|
|
1409
|
+
stopUpdatePolling();
|
|
1410
|
+
var overlay = document.getElementById('updateOverlay');
|
|
1411
|
+
if (overlay) overlay.style.display = 'none';
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
/**
|
|
1415
|
+
* Poll /api/update/status every 3 seconds to track update progress.
|
|
1416
|
+
* Stops when status reaches "success", "failed", "idle", or after 5 minutes timeout.
|
|
1417
|
+
*/
|
|
1418
|
+
function pollUpdateStatus() {
|
|
1419
|
+
if (_updatePollTimer) clearInterval(_updatePollTimer);
|
|
1420
|
+
var startTime = Date.now();
|
|
1421
|
+
var maxDuration = 5 * 60 * 1000; // 5 minutes max
|
|
1422
|
+
|
|
1423
|
+
_updatePollTimer = setInterval(async function() {
|
|
1424
|
+
// Timeout safety
|
|
1425
|
+
if (Date.now() - startTime > maxDuration) {
|
|
1426
|
+
stopUpdatePolling();
|
|
1427
|
+
hideUpdateOverlay();
|
|
1428
|
+
toast('更新超时,请刷新页面查看状态', 'warning');
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1431
|
+
try {
|
|
1432
|
+
var data = await api('/api/update/status');
|
|
1433
|
+
updateOverlayUI(data);
|
|
1434
|
+
// Stop polling on terminal states
|
|
1435
|
+
if (['success', 'failed', 'idle'].includes(data.status) && data.status !== 'idle') {
|
|
1436
|
+
// success and failed will auto-handle via updateOverlayUI
|
|
1437
|
+
if (data.status === 'idle' && !data.is_updating) {
|
|
1438
|
+
// Somehow back to idle without completing — might be an error
|
|
1439
|
+
stopUpdatePolling();
|
|
1440
|
+
} else if (data.status !== 'idle') {
|
|
1441
|
+
stopUpdatePolling();
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
} catch (e) {
|
|
1445
|
+
// Connection error — server might have restarted
|
|
1446
|
+
console.log('Update status poll failed (server may be restarting):', e.message);
|
|
1447
|
+
// If we were in restarting phase, just reload
|
|
1448
|
+
var overlay = document.getElementById('updateOverlay');
|
|
1449
|
+
if (overlay && overlay.className.includes('restarting')) {
|
|
1450
|
+
stopUpdatePolling();
|
|
1451
|
+
location.reload();
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
}, 3000);
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
/**
|
|
1458
|
+
* Main entry point: user clicks the [可更新] badge or banner "立即更新" button.
|
|
1459
|
+
* When called from the banner (skipConfirm=true), skip the confirm dialog.
|
|
1460
|
+
* Shows a 10-second pre-update countdown in the overlay, then triggers the update.
|
|
1461
|
+
*/
|
|
1462
|
+
var _preUpdateTimer = null;
|
|
1463
|
+
var _preUpdateSec = 0;
|
|
1464
|
+
|
|
1465
|
+
async function doChatUpdate(skipConfirm) {
|
|
1466
|
+
if (!skipConfirm && !confirm('确定要更新到最新版本吗?\n\n更新过程中服务会自动重启,重启后任务将自动继续执行。')) return;
|
|
1467
|
+
|
|
1468
|
+
// Show overlay with pre-update countdown
|
|
1469
|
+
showPreUpdateCountdown();
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
/**
|
|
1473
|
+
* Show a 10-second pre-update countdown in the overlay.
|
|
1474
|
+
* During this time, the user sees a reassuring message and can cancel.
|
|
1475
|
+
*/
|
|
1476
|
+
function showPreUpdateCountdown() {
|
|
1477
|
+
var overlay = document.getElementById('updateOverlay');
|
|
1478
|
+
if (!overlay) return;
|
|
1479
|
+
overlay.style.display = 'flex';
|
|
1480
|
+
overlay.className = 'update-overlay';
|
|
1481
|
+
|
|
1482
|
+
document.getElementById('updateOverlayTitle').textContent = '准备开始更新';
|
|
1483
|
+
document.getElementById('updateOverlayStatus').textContent = '系统将在更新开始前安全保存所有任务状态';
|
|
1484
|
+
document.getElementById('updateOverlayCountdown').style.display = 'flex';
|
|
1485
|
+
document.getElementById('updateCountdownNum').textContent = '10';
|
|
1486
|
+
document.getElementById('updateProgressBar').style.width = '5%';
|
|
1487
|
+
document.getElementById('updateProgressBar').parentElement.classList.remove('active');
|
|
1488
|
+
document.getElementById('updateOverlayReassure').style.display = 'flex';
|
|
1489
|
+
|
|
1490
|
+
// 更新倒计时标签为"秒后开始更新"
|
|
1491
|
+
var countdownLabel = document.querySelector('#updateOverlayCountdown .update-countdown-label');
|
|
1492
|
+
if (countdownLabel) countdownLabel.textContent = '秒后开始更新';
|
|
1493
|
+
|
|
1494
|
+
// 图标改为旋转的更新图标
|
|
1495
|
+
var iconEl = document.getElementById('updateOverlayIcon');
|
|
1496
|
+
if (iconEl) iconEl.innerHTML = '<span class="update-spinner"></span>';
|
|
1497
|
+
|
|
1498
|
+
_preUpdateSec = 10;
|
|
1499
|
+
if (_preUpdateTimer) clearInterval(_preUpdateTimer);
|
|
1500
|
+
_preUpdateTimer = setInterval(function() {
|
|
1501
|
+
_preUpdateSec--;
|
|
1502
|
+
var el = document.getElementById('updateCountdownNum');
|
|
1503
|
+
if (el) el.textContent = Math.max(0, _preUpdateSec);
|
|
1504
|
+
// 更新进度条
|
|
1505
|
+
var progress = document.getElementById('updateProgressBar');
|
|
1506
|
+
if (progress) progress.style.width = (5 + (10 - _preUpdateSec) * 0.5) + '%';
|
|
1507
|
+
|
|
1508
|
+
if (_preUpdateSec <= 0) {
|
|
1509
|
+
clearInterval(_preUpdateTimer);
|
|
1510
|
+
_preUpdateTimer = null;
|
|
1511
|
+
// 倒计时结束,开始实际更新
|
|
1512
|
+
triggerUpdate();
|
|
1513
|
+
}
|
|
1514
|
+
}, 1000);
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
/**
|
|
1518
|
+
* Actually call the update API and start polling.
|
|
1519
|
+
*/
|
|
1520
|
+
async function triggerUpdate() {
|
|
1521
|
+
// 重置overlay UI进入更新状态
|
|
1522
|
+
document.getElementById('updateOverlayTitle').textContent = '正在准备更新...';
|
|
1523
|
+
document.getElementById('updateOverlayStatus').textContent = '正在安全停止当前任务';
|
|
1524
|
+
document.getElementById('updateOverlayCountdown').style.display = 'none';
|
|
1525
|
+
// 恢复倒计时标签
|
|
1526
|
+
var countdownLabel = document.querySelector('#updateOverlayCountdown .update-countdown-label');
|
|
1527
|
+
if (countdownLabel) countdownLabel.textContent = '秒后重启';
|
|
1528
|
+
|
|
1158
1529
|
try {
|
|
1159
1530
|
const r = await api('/api/update/apply', {method: 'POST', body: JSON.stringify({type: 'full'})});
|
|
1160
|
-
if (r.error) {
|
|
1161
|
-
|
|
1162
|
-
|
|
1531
|
+
if (r.error) {
|
|
1532
|
+
toast('更新失败: ' + r.error, 'error');
|
|
1533
|
+
hideUpdateOverlay();
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
toast('更新已开始...', 'info');
|
|
1537
|
+
// Start polling for status updates
|
|
1538
|
+
pollUpdateStatus();
|
|
1539
|
+
} catch (e) {
|
|
1540
|
+
toast('更新请求失败: ' + e.message, 'error');
|
|
1541
|
+
hideUpdateOverlay();
|
|
1542
|
+
}
|
|
1163
1543
|
}
|
|
1164
1544
|
|
|
1165
1545
|
// ══════════════════════════════════════════════════════
|
package/web/ui/index.html
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
<style>
|
|
8
8
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
9
9
|
:root{
|
|
10
|
-
--bg:#
|
|
11
|
-
--surface:#
|
|
12
|
-
--text:#
|
|
13
|
-
--accent:#
|
|
14
|
-
--primary:#
|
|
15
|
-
--ok:#
|
|
16
|
-
--border:#
|
|
10
|
+
--bg:#1a1816;--bg2:#211f1b;--bg3:#2a2720;--bg4:#353128;--bg5:#433c32;
|
|
11
|
+
--surface:#211f1b;--surface2:#2a2720;
|
|
12
|
+
--text:#e5ddd0;--text2:#a39b8c;--text3:#6d665b;
|
|
13
|
+
--accent:#c96442;--accent2:#d97a5a;--accent-light:#2a1f18;--accent-dark:#a0502e;
|
|
14
|
+
--primary:#c96442;--primary-h:#d97a5a;
|
|
15
|
+
--ok:#6ee7a0;--success:#6ee7a0;--warn:#fcd34d;--danger:#f87171;--info:#7db8f0;
|
|
16
|
+
--border:#322c24;--border-light:#2a2520;
|
|
17
17
|
--radius:8px;
|
|
18
18
|
}
|
|
19
19
|
[data-theme="claude"]{
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
--border:#d6cfc3;--border-light:#e2dace;
|
|
27
27
|
}
|
|
28
28
|
[data-theme="dark"]{
|
|
29
|
-
--bg:#
|
|
30
|
-
--surface:#
|
|
31
|
-
--text:#
|
|
32
|
-
--accent:#
|
|
33
|
-
--primary:#
|
|
34
|
-
--ok:#
|
|
35
|
-
--border:#
|
|
29
|
+
--bg:#1a1816;--bg2:#211f1b;--bg3:#2a2720;--bg4:#353128;--bg5:#433c32;
|
|
30
|
+
--surface:#211f1b;--surface2:#2a2720;
|
|
31
|
+
--text:#e5ddd0;--text2:#a39b8c;--text3:#6d665b;
|
|
32
|
+
--accent:#c96442;--accent2:#d97a5a;--accent-light:#2a1f18;--accent-dark:#a0502e;
|
|
33
|
+
--primary:#c96442;--primary-h:#d97a5a;
|
|
34
|
+
--ok:#6ee7a0;--success:#6ee7a0;--warn:#fcd34d;--danger:#f87171;--info:#7db8f0;
|
|
35
|
+
--border:#322c24;--border-light:#2a2520;
|
|
36
36
|
}
|
|
37
37
|
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:var(--bg);color:var(--text);height:100vh;height:100dvh;display:flex}
|
|
38
38
|
.sidebar{width:220px;background:var(--surface);border-right:1px solid var(--border);display:flex;flex-direction:column;flex-shrink:0}
|
|
@@ -124,10 +124,12 @@ tr:hover{background:var(--surface2)}
|
|
|
124
124
|
.modal{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:24px;width:90%;max-width:720px;max-height:85vh;overflow-y:auto}
|
|
125
125
|
.modal-wide{max-width:900px}
|
|
126
126
|
.modal h3{margin-bottom:16px}
|
|
127
|
-
.log-viewer{background:#
|
|
128
|
-
.log-viewer .INFO{color:#
|
|
129
|
-
.
|
|
130
|
-
.
|
|
127
|
+
.log-viewer{background:#161412;border:1px solid var(--border);border-radius:var(--radius);padding:12px;font-family:monospace;font-size:12px;line-height:1.6;max-height:500px;overflow-y:auto;white-space:pre-wrap;color:#a39b8c}
|
|
128
|
+
.log-viewer .INFO{color:#6ee7a0}.log-viewer .WARNING{color:#fcd34d}.log-viewer .ERROR{color:#f87171}.log-viewer .DEBUG{color:#c96442}
|
|
129
|
+
.log-viewer{background:#161412;border:1px solid var(--border);border-radius:var(--radius);padding:12px;font-family:monospace;font-size:12px;line-height:1.6;max-height:500px;overflow-y:auto;white-space:pre-wrap;color:#a39b8c}
|
|
130
|
+
.log-viewer .INFO{color:#6ee7a0}.log-viewer .WARNING{color:#fcd34d}.log-viewer .ERROR{color:#f87171}.log-viewer .DEBUG{color:#c96442}
|
|
131
|
+
.config-preview{background:#161412;border:1px solid var(--border);border-radius:var(--radius);padding:16px;font-family:'Cascadia Code',Consolas,monospace;font-size:12px;line-height:1.8;white-space:pre-wrap;color:#a39b8c;max-height:400px;overflow-y:auto}
|
|
132
|
+
.config-preview .key{color:#c96442;font-weight:600}
|
|
131
133
|
.drop-zone{border:2px dashed var(--border);border-radius:var(--radius);padding:24px;text-align:center;cursor:pointer;transition:all .2s;color:var(--text2)}
|
|
132
134
|
.drop-zone:hover,.drop-zone.dragover{border-color:var(--primary);background:var(--surface2)}
|
|
133
135
|
.status-msg{margin-top:8px;padding:8px 12px;border-radius:6px;font-size:12px;display:none}
|
|
@@ -165,14 +167,6 @@ tr:hover{background:var(--surface2)}
|
|
|
165
167
|
.confirm-box h4{margin-bottom:12px}
|
|
166
168
|
.confirm-box p{font-size:13px;color:var(--text2);margin-bottom:16px}
|
|
167
169
|
.confirm-box .btns{display:flex;gap:8px;justify-content:center}
|
|
168
|
-
.config-preview{background:#0a0c10;border:1px solid var(--border);border-radius:var(--radius);padding:16px;font-family:'Cascadia Code',Consolas,monospace;font-size:12px;line-height:1.8;white-space:pre-wrap;color:#a0a8c0;max-height:400px;overflow-y:auto}
|
|
169
|
-
.config-preview .key{color:#6366f1;font-weight:600}
|
|
170
|
-
.drop-zone{border:2px dashed var(--border);border-radius:var(--radius);padding:24px;text-align:center;cursor:pointer;transition:all .2s;color:var(--text2)}
|
|
171
|
-
.drop-zone:hover,.drop-zone.dragover{border-color:var(--primary);background:var(--surface2)}
|
|
172
|
-
.status-msg{margin-top:8px;padding:8px 12px;border-radius:6px;font-size:12px}
|
|
173
|
-
.status-msg.loading{color:var(--info)}
|
|
174
|
-
.status-msg.success{color:var(--success);background:rgba(34,197,94,.1)}
|
|
175
|
-
.status-msg.error{color:var(--danger);background:rgba(239,68,68,.1)}
|
|
176
170
|
/* ── Theme & Sidebar ── */
|
|
177
171
|
.sidebar{overflow:hidden;position:relative;transition:width .3s cubic-bezier(.4,0,.2,1)}
|
|
178
172
|
.sidebar.collapsed{width:56px}
|