@memtensor/memos-local-openclaw-plugin 1.0.4-beta.5 → 1.0.4-beta.7
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/README.md +23 -23
- package/dist/capture/index.d.ts +1 -1
- package/dist/capture/index.d.ts.map +1 -1
- package/dist/capture/index.js +28 -2
- package/dist/capture/index.js.map +1 -1
- package/dist/client/connector.d.ts +1 -2
- package/dist/client/connector.d.ts.map +1 -1
- package/dist/client/connector.js +18 -19
- package/dist/client/connector.js.map +1 -1
- package/dist/client/hub.d.ts.map +1 -1
- package/dist/client/hub.js +22 -0
- package/dist/client/hub.js.map +1 -1
- package/dist/client/skill-sync.d.ts +7 -0
- package/dist/client/skill-sync.d.ts.map +1 -1
- package/dist/client/skill-sync.js +10 -0
- package/dist/client/skill-sync.js.map +1 -1
- package/dist/hub/server.d.ts.map +1 -1
- package/dist/hub/server.js +101 -81
- package/dist/hub/server.js.map +1 -1
- package/dist/hub/user-manager.d.ts +2 -0
- package/dist/hub/user-manager.d.ts.map +1 -1
- package/dist/hub/user-manager.js +5 -1
- package/dist/hub/user-manager.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/storage/sqlite.d.ts +54 -20
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +185 -101
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/tools/memory-search.d.ts +3 -1
- package/dist/tools/memory-search.d.ts.map +1 -1
- package/dist/tools/memory-search.js +3 -1
- package/dist/tools/memory-search.js.map +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +1619 -629
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts +14 -8
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +545 -141
- package/dist/viewer/server.js.map +1 -1
- package/index.ts +355 -41
- package/package.json +1 -1
- package/skill/memos-memory-guide/SKILL.md +64 -26
- package/src/capture/index.ts +29 -1
- package/src/client/connector.ts +15 -21
- package/src/client/hub.ts +18 -0
- package/src/client/skill-sync.ts +14 -0
- package/src/hub/server.ts +88 -74
- package/src/hub/user-manager.ts +7 -3
- package/src/index.ts +7 -2
- package/src/storage/sqlite.ts +192 -122
- package/src/tools/memory-search.ts +2 -1
- package/src/viewer/html.ts +1619 -629
- package/src/viewer/server.ts +506 -128
package/src/viewer/html.ts
CHANGED
|
@@ -221,13 +221,71 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
221
221
|
.admin-tab.active .at-count{background:rgba(99,102,241,.15)}
|
|
222
222
|
.admin-panel{display:none}
|
|
223
223
|
.admin-panel.active{display:block}
|
|
224
|
-
.admin-card{border:1px solid var(--border);border-radius:12px;padding:
|
|
224
|
+
.admin-card{border:1px solid var(--border);border-radius:12px;padding:16px 18px;background:var(--bg-card);margin-bottom:10px;transition:all .2s;position:relative;overflow:hidden}
|
|
225
|
+
.admin-card-clickable{cursor:pointer}
|
|
225
226
|
.admin-card::before{content:'';position:absolute;top:0;left:0;bottom:0;width:3px;border-radius:3px 0 0 3px;background:var(--pri);opacity:.5}
|
|
226
227
|
.admin-card:hover{border-color:rgba(99,102,241,.25);box-shadow:0 2px 12px rgba(0,0,0,.04);transform:translateY(-1px)}
|
|
227
228
|
.admin-card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}
|
|
228
|
-
.admin-card-title{font-size:14px;font-weight:700;color:var(--text)}
|
|
229
|
+
.admin-card-title{font-size:14px;font-weight:700;color:var(--text);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
229
230
|
.admin-card-meta{font-size:12px;color:var(--text-muted);line-height:1.5}
|
|
230
|
-
.
|
|
231
|
+
.au-contrib{display:flex;gap:16px;padding:10px 0;margin:6px 0;border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
|
|
232
|
+
.au-contrib-item{font-size:12px;color:var(--text-sec);display:flex;align-items:center;gap:4px}
|
|
233
|
+
.au-contrib-num{font-size:18px;font-weight:700;line-height:1}
|
|
234
|
+
.au-info{display:flex;flex-wrap:wrap;gap:6px 14px;padding:8px 0;font-size:12px}
|
|
235
|
+
.au-info-item{color:var(--text-muted);white-space:nowrap}
|
|
236
|
+
.admin-card-tags{display:flex;gap:5px;margin:8px 0;align-items:center}
|
|
237
|
+
.admin-card-tags-left{display:flex;flex-wrap:wrap;gap:5px;align-items:center;flex:1;min-width:0}
|
|
238
|
+
.admin-card-tag{display:inline-flex;align-items:center;gap:3px;padding:2px 8px;border-radius:6px;font-size:11px;font-weight:500}
|
|
239
|
+
.admin-card-tag.tag-role{background:rgba(99,102,241,.1);color:var(--pri)}
|
|
240
|
+
.admin-card-tag.tag-kind{background:rgba(245,158,11,.1);color:#f59e0b}
|
|
241
|
+
.admin-card-tag.tag-owner{background:rgba(34,197,94,.1);color:#22c55e}
|
|
242
|
+
.admin-card-tag.tag-status{background:rgba(6,182,212,.1);color:#06b6d4}
|
|
243
|
+
.admin-card-tag.tag-version{background:rgba(139,92,246,.1);color:#8b5cf6}
|
|
244
|
+
.admin-card-tag.tag-visibility{background:rgba(99,102,241,.08);color:var(--pri)}
|
|
245
|
+
.admin-card-tag.tag-group{background:rgba(139,92,246,.08);color:#8b5cf6}
|
|
246
|
+
.admin-card-preview{font-size:12px;color:var(--text-sec);line-height:1.5;margin:8px 0;padding:8px 10px;background:var(--bg);border-radius:8px;border:1px solid var(--border);max-height:60px;overflow:hidden;white-space:pre-wrap;word-break:break-all}
|
|
247
|
+
.admin-card-actions{display:inline-flex;gap:4px;margin-left:auto;align-items:center;flex-shrink:0}
|
|
248
|
+
.admin-card-time{font-size:11px;color:var(--text-muted)}
|
|
249
|
+
.admin-card-detail{display:none;margin-top:0;padding:20px 24px 24px;border-top:1px dashed var(--border);background:linear-gradient(180deg,rgba(99,102,241,.015) 0%,var(--bg) 60%);animation:adminDetailIn .25s ease}
|
|
250
|
+
@keyframes adminDetailIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}
|
|
251
|
+
.admin-card.expanded .admin-card-detail{display:block}
|
|
252
|
+
.admin-card.expanded{border-color:rgba(99,102,241,.3);box-shadow:0 4px 24px rgba(99,102,241,.08),0 0 0 1px rgba(99,102,241,.06)}
|
|
253
|
+
.admin-card-detail-meta{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:18px;font-size:12px;color:var(--text-muted)}
|
|
254
|
+
.admin-card-detail-meta .meta-item{display:inline-flex;align-items:center;gap:4px;padding:5px 12px;background:var(--bg-card);border:1px solid var(--border);border-radius:20px;font-size:11px;color:var(--text-sec);transition:border-color .15s,background .15s}
|
|
255
|
+
.admin-card-detail-meta .meta-item:hover{border-color:rgba(99,102,241,.25);background:rgba(99,102,241,.03)}
|
|
256
|
+
.admin-card-detail-section{margin-top:20px}
|
|
257
|
+
.admin-card-detail-section .detail-label{font-size:11px;font-weight:700;color:var(--pri);margin-bottom:12px;text-transform:uppercase;letter-spacing:.06em;display:flex;align-items:center;gap:8px}
|
|
258
|
+
.admin-card-detail-section .detail-label::before{content:'';width:3px;height:14px;border-radius:2px;background:linear-gradient(180deg,var(--pri),rgba(99,102,241,.3))}
|
|
259
|
+
.admin-card-detail-content{font-size:13px;line-height:1.75;color:var(--text-sec);white-space:pre-wrap;word-break:break-all;max-height:400px;overflow-y:auto;padding:16px 20px;background:var(--bg-card);border-radius:10px;border:1px solid var(--border);scrollbar-width:thin;scrollbar-color:rgba(99,102,241,.2) transparent}
|
|
260
|
+
.admin-card-detail-content::-webkit-scrollbar{width:5px}
|
|
261
|
+
.admin-card-detail-content::-webkit-scrollbar-thumb{background:rgba(99,102,241,.2);border-radius:4px}
|
|
262
|
+
.admin-card-detail-content::-webkit-scrollbar-track{background:transparent}
|
|
263
|
+
.admin-task-meta .meta-item .task-status-badge{border:none;padding:0;font-size:11px}
|
|
264
|
+
.admin-task-summary-body{font-size:13.5px;line-height:1.8;color:var(--text);padding:16px 20px;background:var(--bg-card);border-radius:10px;border:1px solid var(--border)}
|
|
265
|
+
.admin-task-summary-body .summary-section-title{font-size:12px;font-weight:700;color:var(--pri);margin:16px 0 6px;padding-bottom:5px;border-bottom:1px dashed var(--border)}
|
|
266
|
+
.admin-task-summary-body .summary-section-title:first-child{margin-top:0}
|
|
267
|
+
.admin-task-summary-body ul{margin:6px 0 12px;padding-left:18px}
|
|
268
|
+
.admin-task-summary-body li{margin:4px 0;color:var(--text-sec);line-height:1.7;font-size:13px}
|
|
269
|
+
.admin-task-summary-body p{margin:6px 0;font-size:13px;line-height:1.7}
|
|
270
|
+
.admin-task-chunks{display:flex;flex-direction:column;gap:0;padding:0;border:1px solid var(--border);border-radius:10px;overflow:hidden;background:var(--bg-card)}
|
|
271
|
+
.adm-msg{display:flex;gap:0;border-bottom:1px solid var(--border)}
|
|
272
|
+
.adm-msg:last-child{border-bottom:none}
|
|
273
|
+
.adm-msg-side{width:144px;flex-shrink:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:12px 10px;border-right:1px solid var(--border)}
|
|
274
|
+
.adm-msg-side.user{background:rgba(99,102,241,.04)}
|
|
275
|
+
.adm-msg-side.assistant{background:rgba(52,211,153,.04)}
|
|
276
|
+
.adm-msg-role{font-size:10px;font-weight:700;letter-spacing:.03em;text-transform:uppercase}
|
|
277
|
+
.adm-msg-side.user .adm-msg-role{color:var(--pri)}
|
|
278
|
+
.adm-msg-side.assistant .adm-msg-role{color:var(--green)}
|
|
279
|
+
.adm-msg-time{font-size:9px;color:var(--text-muted)}
|
|
280
|
+
.adm-msg-body{flex:1;min-width:0;padding:12px 16px;font-size:13px;line-height:1.75;color:var(--text);white-space:pre-wrap;word-break:break-word}
|
|
281
|
+
.adm-msg-body.collapsed{max-height:120px;overflow:hidden;-webkit-mask-image:linear-gradient(180deg,#000 65%,transparent);mask-image:linear-gradient(180deg,#000 65%,transparent)}
|
|
282
|
+
.adm-msg-toggle{display:none;padding:0 16px 8px;font-size:11px;color:var(--pri);cursor:pointer;transition:color .15s}
|
|
283
|
+
.adm-msg-toggle:hover{color:var(--pri-dark)}
|
|
284
|
+
.admin-card-expand-btn{font-size:12px;color:var(--pri);cursor:pointer;background:none;border:none;padding:2px 6px;font-family:inherit}
|
|
285
|
+
.admin-card-clickable{cursor:pointer}
|
|
286
|
+
.admin-toolbar{display:flex;align-items:center;gap:10px;margin-bottom:14px;flex-wrap:wrap}
|
|
287
|
+
.admin-toolbar h3{font-size:14px;font-weight:600;color:var(--text);white-space:nowrap;margin:0;margin-right:auto;line-height:32px}
|
|
288
|
+
.admin-toolbar select{box-sizing:border-box;height:32px;font-size:12px;border:1px solid var(--border);border-radius:8px;background:var(--bg-card);color:var(--text);vertical-align:middle;margin:0;padding:0 10px}
|
|
231
289
|
.admin-badge{display:inline-flex;align-items:center;gap:4px;font-size:10px;font-weight:700;padding:3px 10px;border-radius:999px;letter-spacing:.02em}
|
|
232
290
|
.admin-badge.admin{background:rgba(52,199,89,.15);color:#34c759}
|
|
233
291
|
.admin-badge.member{background:rgba(142,142,147,.12);color:var(--text-muted)}
|
|
@@ -240,6 +298,19 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
240
298
|
[data-theme="light"] .admin-badge.member{background:rgba(0,0,0,.06);color:#6b7280}
|
|
241
299
|
[data-theme="light"] .admin-badge.pending{background:rgba(245,158,11,.1);color:#d97706}
|
|
242
300
|
[data-theme="light"] .admin-header{background:linear-gradient(135deg,rgba(99,102,241,.05) 0%,rgba(139,92,246,.04) 50%,rgba(6,182,212,.03) 100%)}
|
|
301
|
+
.confirm-overlay{display:none;position:fixed;inset:0;align-items:center;justify-content:center;background:rgba(0,0,0,.45);backdrop-filter:blur(4px);z-index:9999;padding:20px}
|
|
302
|
+
.confirm-overlay.show{display:flex}
|
|
303
|
+
.confirm-panel{width:min(400px,90vw);background:var(--bg-card);border:1px solid var(--border);border-radius:16px;box-shadow:0 20px 60px rgba(0,0,0,.2);overflow:hidden;animation:confirmIn .2s ease}
|
|
304
|
+
@keyframes confirmIn{from{opacity:0;transform:scale(.95) translateY(8px)}to{opacity:1;transform:scale(1) translateY(0)}}
|
|
305
|
+
.confirm-panel-header{padding:20px 24px 0;font-size:15px;font-weight:700;color:var(--text)}
|
|
306
|
+
.confirm-panel-body{padding:14px 24px 20px;font-size:13px;line-height:1.65;color:var(--text-sec);white-space:pre-wrap;word-break:break-word}
|
|
307
|
+
.confirm-panel-footer{display:flex;justify-content:flex-end;gap:10px;padding:0 24px 20px}
|
|
308
|
+
.confirm-panel-footer .btn-confirm-cancel{padding:8px 20px;border:1px solid var(--border);border-radius:10px;background:var(--bg);color:var(--text-sec);font-size:13px;font-weight:500;cursor:pointer;transition:all .15s;font-family:inherit}
|
|
309
|
+
.confirm-panel-footer .btn-confirm-cancel:hover{background:var(--bg-card);border-color:var(--text-muted)}
|
|
310
|
+
.confirm-panel-footer .btn-confirm-ok{padding:8px 20px;border:none;border-radius:10px;background:var(--pri);color:#fff;font-size:13px;font-weight:600;cursor:pointer;transition:all .15s;font-family:inherit}
|
|
311
|
+
.confirm-panel-footer .btn-confirm-ok:hover{opacity:.9;box-shadow:0 2px 8px rgba(99,102,241,.3)}
|
|
312
|
+
.confirm-panel-footer .btn-confirm-ok.danger{background:var(--rose)}
|
|
313
|
+
.confirm-panel-footer .btn-confirm-ok.danger:hover{box-shadow:0 2px 8px rgba(244,63,94,.3)}
|
|
243
314
|
.task-detail-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
244
315
|
.shared-memory-overlay,.shared-memory-overlay.show{display:none}
|
|
245
316
|
.shared-memory-overlay.show{display:flex;position:fixed;inset:0;align-items:center;justify-content:center;background:rgba(0,0,0,.55);z-index:1200;padding:24px}
|
|
@@ -270,6 +341,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
270
341
|
.card-content.show{max-height:600px;overflow-y:auto}
|
|
271
342
|
.card-content pre{white-space:pre-wrap;word-break:break-all;background:rgba(0,0,0,.25);padding:14px;border-radius:10px;font-size:12px;font-family:ui-monospace,monospace;margin-top:10px;border:1px solid var(--border);color:var(--text-sec)}
|
|
272
343
|
.card-actions{display:flex;align-items:center;gap:8px;margin-top:14px}
|
|
344
|
+
.card-actions-inline{display:inline-flex;align-items:center;gap:4px;margin-left:auto;flex-shrink:0}
|
|
273
345
|
.vscore-badge{display:inline-flex;align-items:center;background:rgba(59,130,246,.15);color:#60a5fa;font-size:10px;font-weight:700;padding:4px 10px;border-radius:8px;margin-left:auto}
|
|
274
346
|
.merge-badge{display:inline-flex;align-items:center;gap:4px;background:rgba(16,185,129,.12);color:#10b981;font-size:10px;font-weight:600;padding:3px 10px;border-radius:8px}
|
|
275
347
|
.merge-history{margin-top:12px;padding:12px 14px;background:rgba(0,0,0,.15);border-radius:10px;border:1px solid var(--border);font-size:12px;line-height:1.7;color:var(--text-sec);max-height:200px;overflow-y:auto}
|
|
@@ -409,8 +481,36 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
409
481
|
.toast.success{background:var(--green-bg);color:var(--green);border-color:rgba(16,185,129,.3)}
|
|
410
482
|
.toast.error{background:var(--rose-bg);color:var(--rose);border-color:rgba(244,63,94,.3)}
|
|
411
483
|
.toast.info{background:var(--pri-glow);color:var(--pri);border-color:rgba(99,102,241,.15)}
|
|
484
|
+
.toast.warn{background:rgba(245,158,11,.1);color:#f59e0b;border-color:rgba(245,158,11,.3)}
|
|
412
485
|
@keyframes slideIn{from{transform:translateX(100px);opacity:0}to{transform:translateX(0);opacity:1}}
|
|
413
486
|
|
|
487
|
+
.notif-bell{position:relative;cursor:pointer;font-size:16px;padding:5px 7px;border-radius:8px;background:none;border:1px solid transparent;color:var(--text-sec);transition:all .2s}
|
|
488
|
+
.notif-bell:hover{background:var(--bg-card-hover);color:var(--text)}
|
|
489
|
+
.notif-bell .notif-badge{position:absolute;top:1px;right:1px;min-width:16px;height:16px;line-height:16px;text-align:center;font-size:10px;font-weight:700;color:#fff;background:var(--rose);border-radius:8px;padding:0 4px;display:none;pointer-events:none}
|
|
490
|
+
.notif-bell .notif-badge.show{display:block;animation:notifPop .3s ease}
|
|
491
|
+
@keyframes notifPop{0%{transform:scale(0)}60%{transform:scale(1.2)}100%{transform:scale(1)}}
|
|
492
|
+
.notif-panel{position:absolute;top:calc(100% + 8px);right:0;width:380px;max-height:440px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-lg);box-shadow:var(--shadow-lg);z-index:200;display:none;flex-direction:column;overflow:hidden;backdrop-filter:blur(16px)}
|
|
493
|
+
.notif-panel.show{display:flex;animation:notifSlide .2s ease}
|
|
494
|
+
@keyframes notifSlide{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}
|
|
495
|
+
.notif-panel-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--border);font-size:13px;font-weight:600;color:var(--text)}
|
|
496
|
+
.notif-panel-header .notif-mark-all{font-size:11px;font-weight:500;color:var(--pri);cursor:pointer;background:none;border:none;padding:4px 8px;border-radius:6px;transition:background .15s}
|
|
497
|
+
.notif-panel-header .notif-mark-all:hover{background:var(--pri-glow)}
|
|
498
|
+
.notif-panel-body{overflow-y:auto;flex:1;max-height:380px}
|
|
499
|
+
.notif-item{display:flex;gap:10px;padding:12px 16px;border-bottom:1px solid var(--border);cursor:pointer;transition:background .15s;align-items:flex-start}
|
|
500
|
+
.notif-item:last-child{border-bottom:none}
|
|
501
|
+
.notif-item:hover{background:var(--bg-card-hover)}
|
|
502
|
+
.notif-item.unread{background:rgba(99,102,241,.04)}
|
|
503
|
+
.notif-item-icon{font-size:18px;flex-shrink:0;width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:8px;background:rgba(244,63,94,.08)}
|
|
504
|
+
.notif-item-body{flex:1;min-width:0}
|
|
505
|
+
.notif-item-title{font-size:12px;color:var(--text-sec);margin-bottom:2px}
|
|
506
|
+
.notif-item-name{font-size:13px;font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
507
|
+
.notif-item-time{font-size:11px;color:var(--text-muted);margin-top:2px}
|
|
508
|
+
.notif-item-dot{flex-shrink:0;width:8px;height:8px;border-radius:50%;background:var(--pri);margin-top:6px;display:none}
|
|
509
|
+
.notif-item.unread .notif-item-dot{display:block}
|
|
510
|
+
.notif-empty{text-align:center;padding:32px 16px;color:var(--text-muted);font-size:13px}
|
|
511
|
+
[data-theme="light"] .notif-panel{background:rgba(255,255,255,.96)}
|
|
512
|
+
[data-theme="light"] .notif-item.unread{background:rgba(99,102,241,.06)}
|
|
513
|
+
|
|
414
514
|
.empty{text-align:center;padding:64px 20px;color:var(--text-sec)}
|
|
415
515
|
.empty .icon{font-size:52px;margin-bottom:16px;opacity:.5}
|
|
416
516
|
.empty p{font-size:15px;font-weight:500}
|
|
@@ -484,28 +584,35 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
484
584
|
.task-detail-summary .summary-section-title:first-child{margin-top:0}
|
|
485
585
|
.task-detail-summary ul{margin:4px 0 8px 0;padding-left:20px}
|
|
486
586
|
.task-detail-summary li{margin:3px 0;color:var(--text-sec);line-height:1.6}
|
|
487
|
-
.task-detail-chunks-title{font-size:12px;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:12px}
|
|
488
|
-
.task-detail-chunks{
|
|
489
|
-
.task-
|
|
490
|
-
.task-chunk-item
|
|
491
|
-
.task-chunk-item.role-
|
|
492
|
-
.task-chunk-
|
|
587
|
+
.task-detail-chunks-title{font-size:12px;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:12px;display:flex;align-items:center;gap:8px}
|
|
588
|
+
.task-detail-chunks-title::before{content:'';width:3px;height:14px;border-radius:2px;background:linear-gradient(180deg,var(--pri),rgba(99,102,241,.3))}
|
|
589
|
+
.task-detail-chunks{display:flex;flex-direction:column;gap:12px;padding:8px 0}
|
|
590
|
+
.task-chunk-item{display:flex;gap:12px;align-items:flex-start;width:100%;font-size:13px;line-height:1.6}
|
|
591
|
+
.task-chunk-item.role-user{flex-direction:row-reverse}
|
|
592
|
+
.task-chunk-avatar{width:32px;height:32px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0;font-weight:700}
|
|
593
|
+
.role-user .task-chunk-avatar{background:linear-gradient(135deg,var(--pri),var(--pri-dark));color:#fff}
|
|
594
|
+
.role-assistant .task-chunk-avatar{background:linear-gradient(135deg,var(--green),#059669);color:#fff}
|
|
595
|
+
.role-tool .task-chunk-avatar{background:linear-gradient(135deg,var(--amber),#d97706);color:#fff}
|
|
596
|
+
.task-chunk-body{flex:1;min-width:0;max-width:85%}
|
|
597
|
+
.task-chunk-header{display:flex;align-items:center;gap:8px;margin-bottom:4px}
|
|
598
|
+
.role-user .task-chunk-header{flex-direction:row-reverse}
|
|
599
|
+
.task-chunk-role{font-size:11px;font-weight:700;letter-spacing:.02em}
|
|
493
600
|
.task-chunk-role.user{color:var(--pri)}
|
|
494
601
|
.task-chunk-role.assistant{color:var(--green)}
|
|
495
602
|
.task-chunk-role.tool{color:var(--amber)}
|
|
496
|
-
.task-chunk-
|
|
603
|
+
.task-chunk-time{font-size:10px;color:var(--text-muted)}
|
|
604
|
+
.task-chunk-bubble{padding:14px 18px;border-radius:12px;white-space:pre-wrap;word-break:break-word;max-height:none;overflow:hidden;position:relative;transition:all .2s}
|
|
497
605
|
.task-chunk-bubble.collapsed{max-height:200px}
|
|
498
|
-
.task-chunk-expand{display:none;align-items:center;justify-content:center;gap:4px;margin-top:
|
|
606
|
+
.task-chunk-expand{display:none;align-items:center;justify-content:center;gap:4px;margin-top:6px;padding:4px 12px;font-size:12px;font-weight:600;color:var(--text-sec);cursor:pointer;user-select:none;border-radius:8px;transition:all .15s}
|
|
499
607
|
.task-chunk-expand:hover{color:var(--pri);background:rgba(99,102,241,.08)}
|
|
500
608
|
.task-chunk-expand .expand-arrow{display:inline-block;font-size:10px;transition:transform .2s}
|
|
501
609
|
.task-chunk-expand.is-expanded .expand-arrow{transform:rotate(180deg)}
|
|
502
|
-
.role-user .task-chunk-bubble{background:var(--
|
|
503
|
-
.role-assistant .task-chunk-bubble{background:var(--bg-card);border:1px solid var(--border);color:var(--text-sec);border-
|
|
504
|
-
.role-tool .task-chunk-bubble{background:rgba(245,158,11,.
|
|
505
|
-
.task-chunk-bubble:hover{
|
|
506
|
-
.task-chunk-
|
|
507
|
-
[data-theme="light"] .role-
|
|
508
|
-
[data-theme="light"] .role-assistant .task-chunk-bubble{background:#f0f0f0;border:none;color:#333}
|
|
610
|
+
.role-user .task-chunk-bubble{background:linear-gradient(135deg,rgba(99,102,241,.12),rgba(99,102,241,.06));border:1px solid rgba(99,102,241,.2);color:var(--text);border-radius:12px 12px 4px 12px}
|
|
611
|
+
.role-assistant .task-chunk-bubble{background:var(--bg-card);border:1px solid var(--border);color:var(--text-sec);border-radius:12px 12px 12px 4px}
|
|
612
|
+
.role-tool .task-chunk-bubble{background:rgba(245,158,11,.06);border:1px solid rgba(245,158,11,.15);color:var(--text-sec);border-radius:12px 12px 12px 4px;font-family:'SF Mono',Monaco,Consolas,monospace;font-size:12px}
|
|
613
|
+
.task-chunk-bubble:hover{border-color:rgba(99,102,241,.3);box-shadow:0 2px 8px rgba(0,0,0,.1)}
|
|
614
|
+
[data-theme="light"] .role-user .task-chunk-bubble{background:linear-gradient(135deg,rgba(79,70,229,.08),rgba(79,70,229,.04));border-color:rgba(79,70,229,.15);color:#111827}
|
|
615
|
+
[data-theme="light"] .role-assistant .task-chunk-bubble{background:#f8f9fb;border-color:#e2e4e9;color:#4b5563}
|
|
509
616
|
[data-theme="light"] .task-detail-panel{background:#fff}
|
|
510
617
|
[data-theme="light"] .task-card{background:#fff}
|
|
511
618
|
[data-theme="light"] .tasks-stat{background:#fff}
|
|
@@ -521,7 +628,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
521
628
|
.skill-card.archived::before{background:var(--text-muted)}
|
|
522
629
|
.skill-card-top{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:6px}
|
|
523
630
|
.skill-card-name{font-size:15px;font-weight:700;color:var(--text);flex:1}
|
|
524
|
-
.skill-card-badges{display:flex;gap:6px;align-items:center}
|
|
631
|
+
.task-card-badges,.skill-card-badges{display:flex;gap:6px;align-items:center}
|
|
525
632
|
.skill-badge{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;padding:3px 10px;border-radius:20px}
|
|
526
633
|
.skill-badge.version{color:var(--violet);background:rgba(139,92,246,.15)}
|
|
527
634
|
.skill-badge.installed{color:var(--green);background:var(--green-bg)}
|
|
@@ -1043,13 +1150,20 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1043
1150
|
<button class="tab" data-view="analytics" onclick="switchView('analytics')" data-i18n="tab.analytics">\u{1F4CA} Analytics</button>
|
|
1044
1151
|
<button class="tab" data-view="logs" onclick="switchView('logs')" data-i18n="tab.logs">\u{1F4DD} Logs</button>
|
|
1045
1152
|
<button class="tab" data-view="import" onclick="switchView('import')" data-i18n="tab.import">\u{1F4E5} Import</button>
|
|
1046
|
-
<button class="tab" data-view="admin" onclick="switchView('admin')" data-i18n="tab.admin">\u{1F6E1} Admin
|
|
1153
|
+
<button class="tab" data-view="admin" onclick="switchView('admin')" data-i18n="tab.admin">\u{1F6E1} Admin<span id="adminPendingBadge" style="display:none;background:var(--rose);color:#fff;font-size:10px;font-weight:700;padding:1px 5px;border-radius:8px;margin-left:4px;vertical-align:top"></span></button>
|
|
1047
1154
|
<button class="tab" data-view="settings" onclick="switchView('settings')" data-i18n="tab.settings">\u2699 Settings</button>
|
|
1048
1155
|
</nav>
|
|
1049
1156
|
</div>
|
|
1050
1157
|
<div class="actions">
|
|
1051
1158
|
<button class="btn btn-icon" onclick="toggleLang()" aria-label="Switch language" style="font-size:12px;font-weight:700;padding:4px 8px"><span data-i18n="lang.switch">EN</span></button>
|
|
1052
1159
|
<button class="btn btn-icon theme-toggle" onclick="toggleViewerTheme()" title="Toggle light/dark" aria-label="Toggle theme"><span class="theme-icon-dark">\u{1F319}</span><span class="theme-icon-light">\u2600</span></button>
|
|
1160
|
+
<div style="position:relative;display:inline-block" id="notifBellWrap">
|
|
1161
|
+
<button class="notif-bell" onclick="toggleNotifPanel(event)" title="Notifications" aria-label="Notifications">\u{1F514}<span class="notif-badge" id="notifBadge"></span></button>
|
|
1162
|
+
<div class="notif-panel" id="notifPanel">
|
|
1163
|
+
<div class="notif-panel-header"><span data-i18n="notif.title">\u{1F514} Notifications</span><div style="display:flex;gap:6px"><button class="notif-mark-all" onclick="markAllNotifsRead()" data-i18n="notif.markAll">Mark all read</button><button class="notif-mark-all" onclick="clearAllNotifs()" style="color:var(--rose)" data-i18n="notif.clearAll">Clear all</button></div></div>
|
|
1164
|
+
<div class="notif-panel-body" id="notifPanelBody"><div class="notif-empty" data-i18n="notif.empty">No notifications</div></div>
|
|
1165
|
+
</div>
|
|
1166
|
+
</div>
|
|
1053
1167
|
<button class="btn btn-ghost btn-sm" onclick="loadAll()" data-i18n="refresh">\u21BB Refresh</button>
|
|
1054
1168
|
<button class="btn btn-ghost btn-sm" onclick="doLogout()" data-i18n="logout">Logout</button>
|
|
1055
1169
|
</div>
|
|
@@ -1086,7 +1200,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1086
1200
|
</select>
|
|
1087
1201
|
<select id="memorySearchScope" class="filter-select" onchange="onMemoryScopeChange()">
|
|
1088
1202
|
<option value="local" data-i18n="scope.local">Local</option>
|
|
1089
|
-
<option value="all" data-i18n="scope.hub">
|
|
1203
|
+
<option value="all" data-i18n="scope.hub">Team</option>
|
|
1090
1204
|
</select>
|
|
1091
1205
|
</div>
|
|
1092
1206
|
<div class="search-meta" id="searchMeta"></div>
|
|
@@ -1130,7 +1244,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1130
1244
|
<button class="filter-chip" data-task-status="skipped" onclick="setTaskStatusFilter(this,'skipped')" data-i18n="tasks.status.skipped">Skipped</button>
|
|
1131
1245
|
<select id="taskSearchScope" class="scope-select" onchange="onTaskScopeChange()">
|
|
1132
1246
|
<option value="local" data-i18n="scope.local">Local</option>
|
|
1133
|
-
<option value="all" data-i18n="scope.hub">
|
|
1247
|
+
<option value="all" data-i18n="scope.hub">Team</option>
|
|
1134
1248
|
</select>
|
|
1135
1249
|
<button class="btn btn-sm btn-ghost" onclick="loadTasks()" style="margin-left:auto" data-i18n="refresh">\u21BB Refresh</button>
|
|
1136
1250
|
</div>
|
|
@@ -1172,7 +1286,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1172
1286
|
<input type="text" id="skillSearchInput" placeholder="Search skills..." data-i18n-ph="skills.search.placeholder" oninput="debounceSkillSearch()">
|
|
1173
1287
|
<select id="skillSearchScope" class="scope-select" onchange="onSkillScopeChange()">
|
|
1174
1288
|
<option value="local" data-i18n="scope.local">Local</option>
|
|
1175
|
-
<option value="all" data-i18n="scope.hub">
|
|
1289
|
+
<option value="all" data-i18n="scope.hub">Team</option>
|
|
1176
1290
|
</select>
|
|
1177
1291
|
</div>
|
|
1178
1292
|
<div class="search-meta" id="skillSearchMeta"></div>
|
|
@@ -1200,7 +1314,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1200
1314
|
</div>
|
|
1201
1315
|
<div class="tasks-list" id="skillsList"><div class="spinner"></div></div>
|
|
1202
1316
|
<div id="hubSkillsSection" style="display:none;margin-top:16px">
|
|
1203
|
-
<div class="section-title" style="margin-bottom:12px" data-i18n="skills.hub.title">\u{1F310}
|
|
1317
|
+
<div class="section-title" style="margin-bottom:12px" data-i18n="skills.hub.title">\u{1F310} Team Skills</div>
|
|
1204
1318
|
<div class="tasks-list" id="hubSkillsList"></div>
|
|
1205
1319
|
</div>
|
|
1206
1320
|
</div>
|
|
@@ -1209,6 +1323,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1209
1323
|
<div class="task-detail-header">
|
|
1210
1324
|
<h2 id="skillDetailTitle"></h2>
|
|
1211
1325
|
<div style="display:flex;gap:8px;align-items:center">
|
|
1326
|
+
<span id="skillScopeBadge"></span>
|
|
1212
1327
|
<button class="skill-vis-btn" id="skillVisibilityBtn" onclick="toggleSkillVisibility()"></button>
|
|
1213
1328
|
<button class="skill-download-btn" id="skillDownloadBtn" onclick="downloadSkill()" data-i18n="skills.download">\u2B07 Download</button>
|
|
1214
1329
|
<button class="btn btn-icon" onclick="closeSkillDetail()" title="Close">\u2715</button>
|
|
@@ -1216,7 +1331,6 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1216
1331
|
</div>
|
|
1217
1332
|
<div class="task-detail-meta" id="skillDetailMeta"></div>
|
|
1218
1333
|
<div class="skill-detail-desc" id="skillDetailDesc"></div>
|
|
1219
|
-
<div id="skillShareActions" style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:8px 0"></div>
|
|
1220
1334
|
<div class="task-detail-chunks-title" data-i18n="skills.files">Skill Files</div>
|
|
1221
1335
|
<div class="skill-files-list" id="skillFilesList"></div>
|
|
1222
1336
|
<div class="task-detail-chunks-title" id="skillContentTitle" data-i18n="skills.content">SKILL.md Content</div>
|
|
@@ -1250,10 +1364,18 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1250
1364
|
<div class="analytics-section" id="toolPerfSection" style="position:relative">
|
|
1251
1365
|
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px">
|
|
1252
1366
|
<h3 style="margin-bottom:0"><span class="icon">\u26A1</span> <span data-i18n="chart.toolperf">Tool Response Time</span> <span style="font-size:10px;color:var(--text-muted);font-weight:500;text-transform:none;letter-spacing:0;margin-left:4px">(per minute avg)</span></h3>
|
|
1253
|
-
<div style="display:flex;gap:6px;align-items:center">
|
|
1367
|
+
<div style="display:flex;gap:6px;align-items:center;flex-wrap:wrap">
|
|
1254
1368
|
<button class="range-btn tool-range active" data-mins="60" onclick="setToolMinutes(60)">1h</button>
|
|
1255
1369
|
<button class="range-btn tool-range" data-mins="360" onclick="setToolMinutes(360)">6h</button>
|
|
1256
1370
|
<button class="range-btn tool-range" data-mins="1440" onclick="setToolMinutes(1440)">24h</button>
|
|
1371
|
+
<button class="range-btn tool-range" data-mins="4320" onclick="setToolMinutes(4320)">3d</button>
|
|
1372
|
+
<button class="range-btn tool-range" data-mins="10080" onclick="setToolMinutes(10080)">7d</button>
|
|
1373
|
+
<button class="range-btn tool-range" data-mins="43200" onclick="setToolMinutes(43200)">30d</button>
|
|
1374
|
+
<span style="color:var(--border);margin:0 2px">|</span>
|
|
1375
|
+
<input type="datetime-local" id="toolRangeFrom" style="font-size:11px;padding:3px 6px;border:1px solid var(--border);border-radius:6px;background:var(--bg-card);color:var(--text)" title="From">
|
|
1376
|
+
<span style="font-size:11px;color:var(--text-muted)">–</span>
|
|
1377
|
+
<input type="datetime-local" id="toolRangeTo" style="font-size:11px;padding:3px 6px;border:1px solid var(--border);border-radius:6px;background:var(--bg-card);color:var(--text)" title="To">
|
|
1378
|
+
<button class="range-btn" onclick="applyCustomToolRange()" data-i18n="chart.apply">Apply</button>
|
|
1257
1379
|
</div>
|
|
1258
1380
|
</div>
|
|
1259
1381
|
<div id="toolChart" style="width:100%;height:280px;position:relative;overflow:hidden;border-radius:12px"></div>
|
|
@@ -1284,7 +1406,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1284
1406
|
<div class="settings-view" id="settingsView">
|
|
1285
1407
|
<div class="settings-tabs-bar">
|
|
1286
1408
|
<button class="settings-tab-btn active" data-tab="models" onclick="switchSettingsTab('models',this)"><span class="stab-dot"></span><span data-i18n="settings.models">AI Models</span></button>
|
|
1287
|
-
<button class="settings-tab-btn" data-tab="hub" onclick="switchSettingsTab('hub',this)"><span class="stab-dot"></span><span data-i18n="settings.hub">
|
|
1409
|
+
<button class="settings-tab-btn" data-tab="hub" onclick="switchSettingsTab('hub',this)"><span class="stab-dot"></span><span data-i18n="settings.hub">Team Sharing</span></button>
|
|
1288
1410
|
<button class="settings-tab-btn" data-tab="general" onclick="switchSettingsTab('general',this)"><span class="stab-dot"></span><span data-i18n="settings.general">General</span></button>
|
|
1289
1411
|
</div>
|
|
1290
1412
|
<div class="settings-cards-grid">
|
|
@@ -1336,7 +1458,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1336
1458
|
<div class="test-conn-row">
|
|
1337
1459
|
<button class="btn btn-sm btn-ghost" onclick="testModel('embedding')" id="testEmbBtn" data-i18n="settings.test">Test Connection</button>
|
|
1338
1460
|
<span class="test-result" id="testEmbResult"></span>
|
|
1339
|
-
|
|
1461
|
+
</div>
|
|
1340
1462
|
|
|
1341
1463
|
<div class="settings-card-divider"></div>
|
|
1342
1464
|
|
|
@@ -1381,7 +1503,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1381
1503
|
<div class="test-conn-row">
|
|
1382
1504
|
<button class="btn btn-sm btn-ghost" onclick="testModel('summarizer')" id="testSumBtn" data-i18n="settings.test">Test Connection</button>
|
|
1383
1505
|
<span class="test-result" id="testSumResult"></span>
|
|
1384
|
-
|
|
1506
|
+
</div>
|
|
1385
1507
|
|
|
1386
1508
|
<div class="settings-card-divider"></div>
|
|
1387
1509
|
|
|
@@ -1408,44 +1530,44 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1408
1530
|
</div>
|
|
1409
1531
|
<div style="margin-top:14px">
|
|
1410
1532
|
<div class="settings-card-subtitle" style="margin-bottom:4px" data-i18n="settings.skill.model">Skill Dedicated Model</div>
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
</div>
|
|
1431
|
-
<div class="settings-field">
|
|
1432
|
-
<label data-i18n="settings.model">Model</label>
|
|
1433
|
-
<input type="text" id="cfgSkillModel" placeholder="e.g. claude-4.6-opus">
|
|
1434
|
-
</div>
|
|
1435
|
-
<div class="settings-field full-width">
|
|
1436
|
-
<label>Endpoint</label>
|
|
1437
|
-
<input type="text" id="cfgSkillEndpoint" placeholder="https://...">
|
|
1438
|
-
</div>
|
|
1439
|
-
<div class="settings-field">
|
|
1440
|
-
<label>API Key</label>
|
|
1441
|
-
<input type="password" id="cfgSkillApiKey" placeholder="\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022">
|
|
1442
|
-
</div>
|
|
1533
|
+
<div class="field-hint" style="margin-bottom:12px" data-i18n="settings.skill.model.hint">If not configured, the main Summarizer Model above will be used for skill generation. Configure a dedicated model here for higher quality skill output.</div>
|
|
1534
|
+
<div class="settings-grid">
|
|
1535
|
+
<div class="settings-field">
|
|
1536
|
+
<label data-i18n="settings.provider">Provider</label>
|
|
1537
|
+
<select id="cfgSkillProvider" onchange="onProviderChange('skill')">
|
|
1538
|
+
<option value="">\u2014 <span data-i18n="settings.skill.usemain">Use main summarizer</span> \u2014</option>
|
|
1539
|
+
<option value="openai_compatible">OpenAI Compatible</option>
|
|
1540
|
+
<option value="openai">OpenAI</option>
|
|
1541
|
+
<option value="siliconflow">SiliconFlow (\u7845\u57FA\u6D41\u52A8)</option>
|
|
1542
|
+
<option value="zhipu">Zhipu AI (\u667A\u8C31)</option>
|
|
1543
|
+
<option value="deepseek">DeepSeek</option>
|
|
1544
|
+
<option value="bailian">Alibaba Bailian (\u767E\u70BC)</option>
|
|
1545
|
+
<option value="moonshot">Moonshot (Kimi)</option>
|
|
1546
|
+
<option value="anthropic">Anthropic</option>
|
|
1547
|
+
<option value="gemini">Gemini</option>
|
|
1548
|
+
<option value="azure_openai">Azure OpenAI</option>
|
|
1549
|
+
<option value="bedrock">Bedrock</option>
|
|
1550
|
+
<option value="openclaw">OpenClaw Host</option>
|
|
1551
|
+
</select>
|
|
1443
1552
|
</div>
|
|
1444
|
-
<div class="
|
|
1445
|
-
<
|
|
1446
|
-
<
|
|
1553
|
+
<div class="settings-field">
|
|
1554
|
+
<label data-i18n="settings.model">Model</label>
|
|
1555
|
+
<input type="text" id="cfgSkillModel" placeholder="e.g. claude-4.6-opus">
|
|
1556
|
+
</div>
|
|
1557
|
+
<div class="settings-field full-width">
|
|
1558
|
+
<label>Endpoint</label>
|
|
1559
|
+
<input type="text" id="cfgSkillEndpoint" placeholder="https://...">
|
|
1560
|
+
</div>
|
|
1561
|
+
<div class="settings-field">
|
|
1562
|
+
<label>API Key</label>
|
|
1563
|
+
<input type="password" id="cfgSkillApiKey" placeholder="\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022">
|
|
1447
1564
|
</div>
|
|
1448
1565
|
</div>
|
|
1566
|
+
<div class="test-conn-row">
|
|
1567
|
+
<button class="btn btn-sm btn-ghost" onclick="testModel('skill')" id="testSkillBtn" data-i18n="settings.test">Test Connection</button>
|
|
1568
|
+
<span class="test-result" id="testSkillResult"></span>
|
|
1569
|
+
</div>
|
|
1570
|
+
</div>
|
|
1449
1571
|
|
|
1450
1572
|
<div class="settings-card-divider"></div>
|
|
1451
1573
|
<div class="settings-actions">
|
|
@@ -1457,12 +1579,12 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1457
1579
|
</div>
|
|
1458
1580
|
</div>
|
|
1459
1581
|
|
|
1460
|
-
<!-- ══ Card:
|
|
1582
|
+
<!-- ══ Card: Team Sharing ══ -->
|
|
1461
1583
|
<div class="settings-card card-hub" id="settingsSharingConfig" data-stab="hub">
|
|
1462
1584
|
<div class="settings-card-header">
|
|
1463
1585
|
<div class="settings-card-icon" style="background:rgba(6,182,212,.1);border-color:rgba(6,182,212,.15)">\u{1F310}</div>
|
|
1464
1586
|
<div class="settings-card-title-wrap">
|
|
1465
|
-
<div class="settings-card-title" data-i18n="settings.hub">
|
|
1587
|
+
<div class="settings-card-title" data-i18n="settings.hub">Team Sharing</div>
|
|
1466
1588
|
<div class="settings-card-desc" data-i18n="settings.hub.desc">Share memories, tasks and skills with your team</div>
|
|
1467
1589
|
</div>
|
|
1468
1590
|
</div>
|
|
@@ -1478,11 +1600,11 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1478
1600
|
<div class="team-guide-opt-icon" style="background:rgba(6,182,212,.1);border:1px solid rgba(6,182,212,.15)">\u{1F310}</div>
|
|
1479
1601
|
<div class="team-guide-opt-title" data-i18n="guide.join.title">Join a Remote Team</div>
|
|
1480
1602
|
</div>
|
|
1481
|
-
<div class="team-guide-opt-desc" data-i18n="guide.join.desc">Your team already has a
|
|
1603
|
+
<div class="team-guide-opt-desc" data-i18n="guide.join.desc">Your team already has a server running? Join it to share memories, tasks and skills with team members.</div>
|
|
1482
1604
|
<ol class="team-guide-steps">
|
|
1483
|
-
<li><span data-i18n="guide.join.s1">Ask your
|
|
1605
|
+
<li><span data-i18n="guide.join.s1">Ask your team admin for the Server Address and Team Token</span></li>
|
|
1484
1606
|
<li><span data-i18n="guide.join.s2">Enable sharing above, select "Client" mode</span></li>
|
|
1485
|
-
<li><span data-i18n="guide.join.s3">Fill in
|
|
1607
|
+
<li><span data-i18n="guide.join.s3">Fill in Server Address and Team Token, click "Test Connection"</span></li>
|
|
1486
1608
|
<li><span data-i18n="guide.join.s4">Save settings and restart the OpenClaw gateway (page refreshes automatically)</span></li>
|
|
1487
1609
|
</ol>
|
|
1488
1610
|
<button class="btn-guide" onclick="guideGoToHub('client')" data-i18n="guide.join.btn">\u2192 Configure Client Mode</button>
|
|
@@ -1490,16 +1612,16 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1490
1612
|
<div class="team-guide-opt">
|
|
1491
1613
|
<div class="team-guide-opt-header">
|
|
1492
1614
|
<div class="team-guide-opt-icon" style="background:rgba(139,92,246,.1);border:1px solid rgba(139,92,246,.15)">\u{1F5A5}\uFE0F</div>
|
|
1493
|
-
<div class="team-guide-opt-title" data-i18n="guide.hub.title">Start Your Own
|
|
1615
|
+
<div class="team-guide-opt-title" data-i18n="guide.hub.title">Start Your Own Team Server</div>
|
|
1494
1616
|
</div>
|
|
1495
|
-
<div class="team-guide-opt-desc" data-i18n="guide.hub.desc">Be the team server. Run
|
|
1617
|
+
<div class="team-guide-opt-desc" data-i18n="guide.hub.desc">Be the team server. Run it on this device so others can connect and share memories with you.</div>
|
|
1496
1618
|
<ol class="team-guide-steps">
|
|
1497
|
-
<li><span data-i18n="guide.hub.s1">Enable sharing above, select "
|
|
1619
|
+
<li><span data-i18n="guide.hub.s1">Enable sharing above, select "Server" mode</span></li>
|
|
1498
1620
|
<li><span data-i18n="guide.hub.s2">Set a team name, save settings, and restart the gateway (page refreshes automatically)</span></li>
|
|
1499
|
-
<li><span data-i18n="guide.hub.s3">Share the
|
|
1621
|
+
<li><span data-i18n="guide.hub.s3">Share the Server Address and Team Token with your team members</span></li>
|
|
1500
1622
|
<li><span data-i18n="guide.hub.s4">Approve join requests in the Admin Panel</span></li>
|
|
1501
1623
|
</ol>
|
|
1502
|
-
<button class="btn-guide" onclick="guideGoToHub('hub')" data-i18n="guide.hub.btn">\u2192 Configure
|
|
1624
|
+
<button class="btn-guide" onclick="guideGoToHub('hub')" data-i18n="guide.hub.btn">\u2192 Configure Server Mode</button>
|
|
1503
1625
|
</div>
|
|
1504
1626
|
</div>
|
|
1505
1627
|
</div>
|
|
@@ -1510,26 +1632,26 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1510
1632
|
<input type="checkbox" id="cfgSharingEnabled" onchange="onSharingToggle()">
|
|
1511
1633
|
<span class="toggle-slider"></span>
|
|
1512
1634
|
</label>
|
|
1513
|
-
<label data-i18n="settings.hub.enable.label">Enable
|
|
1635
|
+
<label data-i18n="settings.hub.enable.label">Enable Team Sharing</label>
|
|
1514
1636
|
</div>
|
|
1515
1637
|
|
|
1516
1638
|
<div id="sharingConfigPanel" style="display:none">
|
|
1517
1639
|
<div style="margin-bottom:14px">
|
|
1518
1640
|
<label style="font-size:11px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.04em;display:block;margin-bottom:6px" data-i18n="settings.hub.role">Role</label>
|
|
1519
1641
|
<div style="display:flex;gap:8px">
|
|
1520
|
-
<button class="btn btn-sm" id="btnRoleHub" onclick="selectSharingRole('hub')" data-i18n="settings.hub.role.hub">
|
|
1521
|
-
<button class="btn btn-sm" id="btnRoleClient" onclick="selectSharingRole('client')" data-i18n="settings.hub.role.client">Client (
|
|
1642
|
+
<button class="btn btn-sm" id="btnRoleHub" onclick="selectSharingRole('hub')" data-i18n="settings.hub.role.hub">Server (Host a team)</button>
|
|
1643
|
+
<button class="btn btn-sm" id="btnRoleClient" onclick="selectSharingRole('client')" data-i18n="settings.hub.role.client">Client (Join a team)</button>
|
|
1522
1644
|
</div>
|
|
1523
|
-
<div class="field-hint" style="margin-top:6px" data-i18n="settings.hub.role.hint">
|
|
1645
|
+
<div class="field-hint" style="margin-top:6px" data-i18n="settings.hub.role.hint">Server = host the team server; Client = join an existing team. These two modes are mutually exclusive.</div>
|
|
1524
1646
|
</div>
|
|
1525
1647
|
|
|
1526
1648
|
<div id="hubModeConfig" style="display:none">
|
|
1527
1649
|
<input type="hidden" id="cfgHubTeamToken" value="">
|
|
1528
1650
|
<div class="settings-grid">
|
|
1529
1651
|
<div class="settings-field">
|
|
1530
|
-
<label data-i18n="settings.hub.port">
|
|
1652
|
+
<label data-i18n="settings.hub.port">Server Port</label>
|
|
1531
1653
|
<input type="number" id="cfgHubPort" placeholder="18800" value="18800">
|
|
1532
|
-
<div class="field-hint" data-i18n="settings.hub.port.hint">Port for
|
|
1654
|
+
<div class="field-hint" data-i18n="settings.hub.port.hint">Port for team server. Default: 18800</div>
|
|
1533
1655
|
</div>
|
|
1534
1656
|
<div class="settings-field">
|
|
1535
1657
|
<label data-i18n="settings.hub.teamName">Team Name</label>
|
|
@@ -1549,20 +1671,20 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1549
1671
|
<div id="clientModeConfig" style="display:none">
|
|
1550
1672
|
<div style="background:rgba(99,102,241,.08);border:1px solid rgba(99,102,241,.2);border-radius:10px;padding:14px 18px;margin-bottom:14px;font-size:12px;line-height:1.7;color:var(--text-sec)">
|
|
1551
1673
|
<div style="font-weight:700;color:var(--text);margin-bottom:4px" data-i18n="settings.hub.clientSteps.title">Quick Setup (3 steps)</div>
|
|
1552
|
-
<div><span style="color:var(--accent)">1.</span> <span data-i18n="settings.hub.clientSteps.s1">Ask your
|
|
1674
|
+
<div><span style="color:var(--accent)">1.</span> <span data-i18n="settings.hub.clientSteps.s1">Ask your team admin for the Server Address and Team Token</span></div>
|
|
1553
1675
|
<div><span style="color:var(--accent)">2.</span> <span data-i18n="settings.hub.clientSteps.s2">Fill them in below, click "Test Connection" to verify</span></div>
|
|
1554
1676
|
<div><span style="color:var(--accent)">3.</span> <span data-i18n="settings.hub.clientSteps.s3">Click "Save Settings", then restart OpenClaw gateway (page refreshes automatically)</span></div>
|
|
1555
1677
|
</div>
|
|
1556
1678
|
<div class="settings-grid">
|
|
1557
1679
|
<div class="settings-field full-width">
|
|
1558
|
-
<label data-i18n="settings.hub.hubAddress">
|
|
1680
|
+
<label data-i18n="settings.hub.hubAddress">Server Address</label>
|
|
1559
1681
|
<input type="text" id="cfgClientHubAddress" placeholder="e.g. 192.168.1.100:18800">
|
|
1560
|
-
<div class="field-hint" data-i18n="settings.hub.hubAddress.hint">
|
|
1682
|
+
<div class="field-hint" data-i18n="settings.hub.hubAddress.hint">Team server address, e.g. 192.168.1.100:18800</div>
|
|
1561
1683
|
</div>
|
|
1562
1684
|
<div class="settings-field">
|
|
1563
1685
|
<label data-i18n="settings.hub.teamTokenClient">Team Token</label>
|
|
1564
1686
|
<input type="text" id="cfgClientTeamToken" placeholder="">
|
|
1565
|
-
<div class="field-hint" data-i18n="settings.hub.teamTokenClient.hint">Get this from your
|
|
1687
|
+
<div class="field-hint" data-i18n="settings.hub.teamTokenClient.hint">Get this from your team admin to join</div>
|
|
1566
1688
|
</div>
|
|
1567
1689
|
<input type="hidden" id="cfgClientUserToken" value="">
|
|
1568
1690
|
</div>
|
|
@@ -1574,13 +1696,12 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1574
1696
|
</div>
|
|
1575
1697
|
|
|
1576
1698
|
<div id="sharingPanelsWrap" style="display:none">
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1699
|
+
<div class="settings-card-divider"></div>
|
|
1700
|
+
<div id="sharingStatusPanel"></div>
|
|
1701
|
+
<div id="sharingTeamPanel"></div>
|
|
1702
|
+
<div id="sharingAdminPanel"></div>
|
|
1581
1703
|
</div>
|
|
1582
1704
|
|
|
1583
|
-
<div class="settings-card-divider"></div>
|
|
1584
1705
|
<div class="settings-actions">
|
|
1585
1706
|
<span class="settings-saved" id="hubSaved">\u2713 <span data-i18n="settings.saved">Saved</span></span>
|
|
1586
1707
|
<button class="btn btn-ghost" onclick="loadConfig()" data-i18n="settings.reset">Reset</button>
|
|
@@ -1637,24 +1758,24 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1637
1758
|
<div class="admin-view" id="adminView">
|
|
1638
1759
|
<div id="adminNotEnabled" style="display:none"></div>
|
|
1639
1760
|
<div id="adminMainContent">
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
<h2><span class="ah-icon">\u{1F6E1}</span> <span data-i18n="admin.title">
|
|
1643
|
-
|
|
1644
|
-
</div>
|
|
1645
|
-
<div class="admin-header-sub" data-i18n="admin.subtitle">Manage team members, groups, and shared resources</div>
|
|
1646
|
-
<div class="admin-stat-row" id="adminStats"></div>
|
|
1647
|
-
</div>
|
|
1648
|
-
<div class="admin-tabs" id="adminTabsBar">
|
|
1649
|
-
<button class="admin-tab active" onclick="switchAdminTab('users',this)"><span class="at-icon">\u{1F465}</span> <span data-i18n="admin.tab.users">Users</span> <span class="at-count" id="adminTabCountUsers">0</span></button>
|
|
1650
|
-
<button class="admin-tab" onclick="switchAdminTab('sharedMemories',this)"><span class="at-icon">\u{1F4AD}</span> <span data-i18n="admin.tab.sharedMemories">Shared Memories</span> <span class="at-count" id="adminTabCountMemories">0</span></button>
|
|
1651
|
-
<button class="admin-tab" onclick="switchAdminTab('memories',this)"><span class="at-icon">\u{1F4CB}</span> <span data-i18n="admin.tab.memories">Shared Tasks</span> <span class="at-count" id="adminTabCountTasks">0</span></button>
|
|
1652
|
-
<button class="admin-tab" onclick="switchAdminTab('skills',this)"><span class="at-icon">\u{1F9E0}</span> <span data-i18n="admin.tab.skills">Shared Skills</span> <span class="at-count" id="adminTabCountSkills">0</span></button>
|
|
1761
|
+
<div class="admin-header">
|
|
1762
|
+
<div class="admin-header-top">
|
|
1763
|
+
<h2><span class="ah-icon">\u{1F6E1}</span> <span data-i18n="admin.title">Team Admin Panel</span></h2>
|
|
1764
|
+
<button class="btn btn-sm btn-ghost" onclick="loadAdminData()" style="backdrop-filter:blur(8px)" data-i18n="admin.refresh">\u21BB Refresh</button>
|
|
1653
1765
|
</div>
|
|
1654
|
-
|
|
1655
|
-
<div class="admin-
|
|
1656
|
-
|
|
1657
|
-
|
|
1766
|
+
<div class="admin-header-sub" data-i18n="admin.subtitle">Manage team members and shared resources</div>
|
|
1767
|
+
<div class="admin-stat-row" id="adminStats"></div>
|
|
1768
|
+
</div>
|
|
1769
|
+
<div class="admin-tabs" id="adminTabsBar">
|
|
1770
|
+
<button class="admin-tab active" onclick="switchAdminTab('users',this)"><span class="at-icon">\u{1F465}</span> <span data-i18n="admin.tab.users">Users</span> <span class="at-count" id="adminTabCountUsers">0</span></button>
|
|
1771
|
+
<button class="admin-tab" onclick="switchAdminTab('memories',this)"><span class="at-icon">\u{1F4AD}</span> <span data-i18n="admin.tab.memories">Shared Memories</span> <span class="at-count" id="adminTabCountMemories">0</span></button>
|
|
1772
|
+
<button class="admin-tab" onclick="switchAdminTab('tasks',this)"><span class="at-icon">\u{1F4CB}</span> <span data-i18n="admin.tab.tasks">Shared Tasks</span> <span class="at-count" id="adminTabCountTasks">0</span></button>
|
|
1773
|
+
<button class="admin-tab" onclick="switchAdminTab('skills',this)"><span class="at-icon">\u{1F9E0}</span> <span data-i18n="admin.tab.skills">Shared Skills</span> <span class="at-count" id="adminTabCountSkills">0</span></button>
|
|
1774
|
+
</div>
|
|
1775
|
+
<div class="admin-panel active" id="adminUsersPanel"></div>
|
|
1776
|
+
<div class="admin-panel" id="adminTasksPanel"></div>
|
|
1777
|
+
<div class="admin-panel" id="adminMemoriesPanel"></div>
|
|
1778
|
+
<div class="admin-panel" id="adminSkillsPanel"></div>
|
|
1658
1779
|
</div>
|
|
1659
1780
|
</div>
|
|
1660
1781
|
|
|
@@ -1901,14 +2022,14 @@ const I18N={
|
|
|
1901
2022
|
'skills.search.local':'Local',
|
|
1902
2023
|
'skills.search.noresult':'No matching skills found',
|
|
1903
2024
|
'skills.load.error':'Failed to load skills',
|
|
1904
|
-
'skills.hub.title':'\u{1F310}
|
|
2025
|
+
'skills.hub.title':'\u{1F310} Team Skills',
|
|
1905
2026
|
'scope.local':'Local',
|
|
1906
2027
|
'scope.group':'Group',
|
|
1907
2028
|
'scope.all':'All',
|
|
1908
|
-
'skills.visibility.public':'
|
|
1909
|
-
'skills.visibility.private':'
|
|
1910
|
-
'skills.setPublic':'
|
|
1911
|
-
'skills.setPrivate':'
|
|
2029
|
+
'skills.visibility.public':'Shared Locally',
|
|
2030
|
+
'skills.visibility.private':'This Agent Only',
|
|
2031
|
+
'skills.setPublic':'Share Locally',
|
|
2032
|
+
'skills.setPrivate':'This Agent Only',
|
|
1912
2033
|
'tasks.total':'Total Tasks',
|
|
1913
2034
|
'tasks.active':'Active',
|
|
1914
2035
|
'tasks.completed':'Completed',
|
|
@@ -1925,6 +2046,17 @@ const I18N={
|
|
|
1925
2046
|
'tasks.skipped.default':'This conversation was too brief to generate a summary. It will not appear in search results.',
|
|
1926
2047
|
'refresh':'\\u21BB Refresh',
|
|
1927
2048
|
'logout':'Logout',
|
|
2049
|
+
'notif.title':'\\u{1F514} Notifications',
|
|
2050
|
+
'notif.markAll':'Mark all read',
|
|
2051
|
+
'notif.empty':'No notifications yet',
|
|
2052
|
+
'notif.removed.memory':'Your shared memory was removed by admin',
|
|
2053
|
+
'notif.removed.task':'Your shared task was removed by admin',
|
|
2054
|
+
'notif.removed.skill':'Your shared skill was removed by admin',
|
|
2055
|
+
'notif.clearAll':'Clear all',
|
|
2056
|
+
'notif.timeAgo.just':'just now',
|
|
2057
|
+
'notif.timeAgo.min':'{n}m ago',
|
|
2058
|
+
'notif.timeAgo.hour':'{n}h ago',
|
|
2059
|
+
'notif.timeAgo.day':'{n}d ago',
|
|
1928
2060
|
'stat.memories':'Memories',
|
|
1929
2061
|
'stat.sessions':'Sessions',
|
|
1930
2062
|
'stat.embeddings':'Embeddings',
|
|
@@ -1979,6 +2111,8 @@ const I18N={
|
|
|
1979
2111
|
'chart.nodata':'No data in this range',
|
|
1980
2112
|
'chart.nocalls':'No viewer calls in this range',
|
|
1981
2113
|
'chart.toolperf':'Tool Response Time',
|
|
2114
|
+
'chart.apply':'Apply',
|
|
2115
|
+
'chart.selectRange':'Please select a time range',
|
|
1982
2116
|
'chart.list':'List',
|
|
1983
2117
|
'chart.search':'Search',
|
|
1984
2118
|
'modal.edit':'Edit Memory',
|
|
@@ -1994,11 +2128,14 @@ const I18N={
|
|
|
1994
2128
|
'toast.deleted':'Memory deleted',
|
|
1995
2129
|
'toast.opfail':'Operation failed',
|
|
1996
2130
|
'toast.delfail':'Delete failed',
|
|
1997
|
-
'toast.setPublic':'
|
|
1998
|
-
'toast.setPrivate':'
|
|
2131
|
+
'toast.setPublic':'Shared locally',
|
|
2132
|
+
'toast.setPrivate':'Now visible to this agent only',
|
|
1999
2133
|
'toast.cleared':'All memories cleared',
|
|
2000
2134
|
'toast.clearfail':'Clear failed',
|
|
2001
2135
|
'toast.notfound':'Memory not found in cache',
|
|
2136
|
+
'confirm.title':'Confirm',
|
|
2137
|
+
'confirm.ok':'Confirm',
|
|
2138
|
+
'confirm.cancel':'Cancel',
|
|
2002
2139
|
'confirm.delete':'Delete this memory?',
|
|
2003
2140
|
'confirm.clearall':'Delete ALL memories? This cannot be undone.',
|
|
2004
2141
|
'confirm.clearall2':'Are you absolutely sure?',
|
|
@@ -2174,16 +2311,16 @@ const I18N={
|
|
|
2174
2311
|
'tasks.error.detail':'Failed to load task details',
|
|
2175
2312
|
'tasks.untitled.related':'Untitled',
|
|
2176
2313
|
'tab.admin':'\u{1F6E1} Admin',
|
|
2177
|
-
'settings.hub':'
|
|
2178
|
-
'settings.hub.enable':'Enable
|
|
2314
|
+
'settings.hub':'Team Sharing',
|
|
2315
|
+
'settings.hub.enable':'Enable Team Sharing',
|
|
2179
2316
|
'settings.hub.enable.hint':'When off, everything works locally as usual.',
|
|
2180
|
-
'settings.hub.enable.label':'Enable
|
|
2317
|
+
'settings.hub.enable.label':'Enable Team Sharing',
|
|
2181
2318
|
'settings.hub.role':'Role',
|
|
2182
|
-
'settings.hub.role.hub':'
|
|
2183
|
-
'settings.hub.role.client':'Client (
|
|
2184
|
-
'settings.hub.role.hint':'
|
|
2185
|
-
'settings.hub.port':'
|
|
2186
|
-
'settings.hub.port.hint':'Port for
|
|
2319
|
+
'settings.hub.role.hub':'Server (Host a team)',
|
|
2320
|
+
'settings.hub.role.client':'Client (Join a team)',
|
|
2321
|
+
'settings.hub.role.hint':'Server = host the team server; Client = join an existing team. These two modes are mutually exclusive.',
|
|
2322
|
+
'settings.hub.port':'Server Port',
|
|
2323
|
+
'settings.hub.port.hint':'Port for team server. Default: 18800',
|
|
2187
2324
|
'settings.hub.teamName':'Team Name',
|
|
2188
2325
|
'settings.hub.teamName.hint':'Display name for your team',
|
|
2189
2326
|
'settings.hub.teamToken':'Team Token',
|
|
@@ -2192,23 +2329,23 @@ const I18N={
|
|
|
2192
2329
|
'settings.hub.hubSteps.title':'Quick Setup (3 steps)',
|
|
2193
2330
|
'settings.hub.hubSteps.s1':'Fill in Team Name below (or keep default)',
|
|
2194
2331
|
'settings.hub.hubSteps.s2':'Click "Save Settings", then restart OpenClaw gateway',
|
|
2195
|
-
'settings.hub.hubSteps.s3':'Share the
|
|
2332
|
+
'settings.hub.hubSteps.s3':'Share the Server Address and Team Token below with your team members',
|
|
2196
2333
|
'settings.hub.clientSteps.title':'Quick Setup (3 steps)',
|
|
2197
|
-
'settings.hub.clientSteps.s1':'Ask your
|
|
2334
|
+
'settings.hub.clientSteps.s1':'Ask your team admin for the Server Address and Team Token',
|
|
2198
2335
|
'settings.hub.clientSteps.s2':'Fill them in below, click "Test Connection" to verify',
|
|
2199
2336
|
'settings.hub.clientSteps.s3':'Click "Save Settings", then restart OpenClaw gateway (page refreshes automatically)',
|
|
2200
2337
|
'settings.hub.shareInfo.title':'Share this info with your team members:',
|
|
2201
2338
|
'settings.hub.shareInfo.yourIP':'your-IP',
|
|
2202
2339
|
'settings.hub.shareInfo.clickCopy':'Click to copy',
|
|
2203
|
-
'settings.hub.restartAlert':'
|
|
2204
|
-
'settings.hub.hubAddress':'
|
|
2205
|
-
'settings.hub.hubAddress.hint':'
|
|
2340
|
+
'settings.hub.restartAlert':'Team sharing config saved! Please restart the OpenClaw gateway for changes to take effect.\\n\\nRun: openclaw gateway stop && openclaw gateway start',
|
|
2341
|
+
'settings.hub.hubAddress':'Server Address',
|
|
2342
|
+
'settings.hub.hubAddress.hint':'Team server address, e.g. 192.168.1.100:18800',
|
|
2206
2343
|
'settings.hub.teamTokenClient':'Team Token',
|
|
2207
|
-
'settings.hub.teamTokenClient.hint':'Get this from your
|
|
2344
|
+
'settings.hub.teamTokenClient.hint':'Get this from your team admin to join',
|
|
2208
2345
|
'settings.hub.userToken':'User Token',
|
|
2209
2346
|
'settings.hub.userToken.hint':'Usually auto-obtained after joining. Only fill if given by admin.',
|
|
2210
2347
|
'settings.hub.testConnection':'Test Connection',
|
|
2211
|
-
'settings.hub.test.noAddr':'Please enter
|
|
2348
|
+
'settings.hub.test.noAddr':'Please enter server address first',
|
|
2212
2349
|
'settings.hub.test.testing':'Testing...',
|
|
2213
2350
|
'settings.hub.test.ok':'Connected successfully',
|
|
2214
2351
|
'settings.hub.test.fail':'Connection failed',
|
|
@@ -2224,16 +2361,16 @@ const I18N={
|
|
|
2224
2361
|
'sharing.sidebar.notConfigured':'Not configured',
|
|
2225
2362
|
'sharing.sidebar.identity':'Identity:',
|
|
2226
2363
|
'sharing.sidebar.admin':'Admin',
|
|
2227
|
-
'sharing.sidebar.targetHub':'
|
|
2228
|
-
'sharing.pendingApproval.hint':'Your join request has been submitted. Please wait for the
|
|
2229
|
-
'sharing.rejected.hint':'Your join request was rejected by the
|
|
2364
|
+
'sharing.sidebar.targetHub':'Team Server:',
|
|
2365
|
+
'sharing.pendingApproval.hint':'Your join request has been submitted. Please wait for the team admin to approve.',
|
|
2366
|
+
'sharing.rejected.hint':'Your join request was rejected by the team admin. Please contact the admin or retry.',
|
|
2230
2367
|
'sharing.retryJoin':'Retry Join',
|
|
2231
2368
|
'sharing.retryJoin.hint':'Clears local data and re-submits the join request',
|
|
2232
2369
|
'sharing.retryJoin.confirm':'This will clear your current connection and re-submit a join request. Continue?',
|
|
2233
2370
|
'sharing.retryJoin.success':'Join request re-submitted. Waiting for admin approval.',
|
|
2234
2371
|
'sharing.retryJoin.fail':'Failed to retry join',
|
|
2235
|
-
'sharing.cannotJoinSelf':'Cannot join your own
|
|
2236
|
-
'scope.hub':'
|
|
2372
|
+
'sharing.cannotJoinSelf':'Cannot join your own server. Please enter a remote server address.',
|
|
2373
|
+
'scope.hub':'Team',
|
|
2237
2374
|
'memory.detail.title':'Memory Detail',
|
|
2238
2375
|
'memory.detail.loading':'Loading...',
|
|
2239
2376
|
'memory.detail.notFound':'Memory not found',
|
|
@@ -2241,12 +2378,13 @@ const I18N={
|
|
|
2241
2378
|
'memory.detail.created':'Created ',
|
|
2242
2379
|
'memory.detail.updated':'Updated ',
|
|
2243
2380
|
'memory.detail.viewTarget':'View target: ',
|
|
2244
|
-
'admin.title':'
|
|
2381
|
+
'admin.title':'Team Admin Panel',
|
|
2245
2382
|
'admin.subtitle':'Manage team members and shared resources',
|
|
2246
2383
|
'admin.refresh':'\u21BB Refresh',
|
|
2247
2384
|
'admin.tab.users':'Users',
|
|
2385
|
+
'admin.tab.tasks':'Shared Tasks',
|
|
2248
2386
|
'admin.tab.groups':'Groups',
|
|
2249
|
-
'admin.tab.memories':'Shared
|
|
2387
|
+
'admin.tab.memories':'Shared Memories',
|
|
2250
2388
|
'admin.tab.skills':'Shared Skills',
|
|
2251
2389
|
'admin.stat.activeUsers':'Active Users',
|
|
2252
2390
|
'admin.stat.pending':'Pending',
|
|
@@ -2260,6 +2398,43 @@ const I18N={
|
|
|
2260
2398
|
'admin.approve':'Approve',
|
|
2261
2399
|
'admin.reject':'Reject',
|
|
2262
2400
|
'admin.device':'Device: ',
|
|
2401
|
+
'admin.joined':'Joined: ',
|
|
2402
|
+
'admin.approved':'Approved: ',
|
|
2403
|
+
'admin.lastActive':'Last Active: ',
|
|
2404
|
+
'admin.ip':'IP: ',
|
|
2405
|
+
'admin.contrib.memories':'Memories',
|
|
2406
|
+
'admin.contrib.tasks':'Tasks',
|
|
2407
|
+
'admin.contrib.skills':'Skills',
|
|
2408
|
+
'admin.groups':'Groups: ',
|
|
2409
|
+
'admin.neverActive':'Never',
|
|
2410
|
+
'admin.promoteAdmin':'Promote to Admin',
|
|
2411
|
+
'admin.demoteMember':'Demote to Member',
|
|
2412
|
+
'admin.editName':'Edit Name',
|
|
2413
|
+
'admin.lastAdminHint':'Last admin — cannot remove or demote',
|
|
2414
|
+
'admin.editNamePrompt':'Enter new username:',
|
|
2415
|
+
'confirm.promoteAdmin':'Promote this user to admin? They will be able to manage all team members and resources.',
|
|
2416
|
+
'confirm.demoteMember':'Demote this admin to member?',
|
|
2417
|
+
'toast.roleChanged':'Role updated',
|
|
2418
|
+
'toast.roleChangeFail':'Role change failed',
|
|
2419
|
+
'toast.usernameChanged':'Username updated',
|
|
2420
|
+
'toast.renameFail':'Rename failed',
|
|
2421
|
+
'toast.invalidUsername':'Username must be 2-32 characters',
|
|
2422
|
+
'admin.filterAll':'All Users',
|
|
2423
|
+
'admin.filterAllValues':'All',
|
|
2424
|
+
'admin.filter.owner':'All Users',
|
|
2425
|
+
'admin.filter.visibility':'Visibility',
|
|
2426
|
+
'admin.filter.group':'Group',
|
|
2427
|
+
'admin.search.placeholder':'Search...',
|
|
2428
|
+
'admin.sort.newest':'Newest first',
|
|
2429
|
+
'admin.sort.oldest':'Oldest first',
|
|
2430
|
+
'admin.filter.role':'Role',
|
|
2431
|
+
'admin.filter.status':'Status',
|
|
2432
|
+
'admin.expand':'Expand',
|
|
2433
|
+
'admin.collapse':'Collapse',
|
|
2434
|
+
'admin.noContent':'No content',
|
|
2435
|
+
'admin.summary':'Summary',
|
|
2436
|
+
'admin.description':'Description',
|
|
2437
|
+
'admin.contentLabel':'Content',
|
|
2263
2438
|
'admin.groups':'Groups',
|
|
2264
2439
|
'admin.newGroup':'+ New Group',
|
|
2265
2440
|
'admin.groupName':'Group name',
|
|
@@ -2273,16 +2448,20 @@ const I18N={
|
|
|
2273
2448
|
'admin.add':'Add',
|
|
2274
2449
|
'admin.remove':'Remove',
|
|
2275
2450
|
'admin.sharedTasks':'Shared Tasks',
|
|
2276
|
-
'admin.noSharedTasks':'No shared tasks
|
|
2451
|
+
'admin.noSharedTasks':'No shared tasks in team.',
|
|
2277
2452
|
'admin.owner':'Owner: ',
|
|
2278
2453
|
'admin.group':'Group: ',
|
|
2454
|
+
'admin.kind':'Kind: ',
|
|
2455
|
+
'admin.role':'Role: ',
|
|
2456
|
+
'admin.visibility':'Visibility: ',
|
|
2457
|
+
'admin.session':'Session',
|
|
2458
|
+
'admin.content':'Content',
|
|
2279
2459
|
'admin.chunks':'Chunks: ',
|
|
2280
2460
|
'admin.updated':'Updated: ',
|
|
2281
2461
|
'admin.sharedSkills':'Shared Skills',
|
|
2282
|
-
'admin.noSharedSkills':'No shared skills
|
|
2462
|
+
'admin.noSharedSkills':'No shared skills in team.',
|
|
2283
2463
|
'admin.sharedMemories':'Shared Memories',
|
|
2284
|
-
'admin.noSharedMemories':'No shared memories
|
|
2285
|
-
'admin.tab.sharedMemories':'Shared Memories',
|
|
2464
|
+
'admin.noSharedMemories':'No shared memories in team.',
|
|
2286
2465
|
'admin.version':'v',
|
|
2287
2466
|
'admin.quality':'Quality: ',
|
|
2288
2467
|
'admin.membersCount':'Members ({n}):',
|
|
@@ -2291,6 +2470,7 @@ const I18N={
|
|
|
2291
2470
|
'admin.noPermission':'You do not have admin permissions to access this panel.',
|
|
2292
2471
|
'admin.groupsFailed':'Failed to load groups: ',
|
|
2293
2472
|
'toast.userApproved':'User approved',
|
|
2473
|
+
'sharing.approved.toast':'Your join request has been approved!',
|
|
2294
2474
|
'toast.userRejected':'User rejected',
|
|
2295
2475
|
'toast.approveFail':'Approve failed',
|
|
2296
2476
|
'toast.rejectFail':'Reject failed',
|
|
@@ -2307,27 +2487,32 @@ const I18N={
|
|
|
2307
2487
|
'toast.removeFail':'Remove failed',
|
|
2308
2488
|
'toast.groupNameRequired':'Group name is required',
|
|
2309
2489
|
'confirm.rejectUser':'Reject this user?',
|
|
2490
|
+
'confirm.removeUser':'Remove this user from the team? This will revoke their access.',
|
|
2491
|
+
'confirm.cleanResources':'Also delete all shared resources (tasks, skills, memories) from this user?',
|
|
2492
|
+
'toast.userRemoved':'User removed',
|
|
2493
|
+
'toast.removeFail':'Remove failed',
|
|
2494
|
+
'admin.remove':'Remove',
|
|
2310
2495
|
'confirm.removeGroupMember':'Remove this member from the group?',
|
|
2311
2496
|
'confirm.removeMember':'Remove this member?',
|
|
2312
2497
|
'confirm.deleteGroup':'Delete group "{name}"? Members will be removed.',
|
|
2313
2498
|
'confirm.deleteGroupShort':'Delete group "{name}"?',
|
|
2314
|
-
'confirm.removeTask':'Remove shared task "{name}" from
|
|
2315
|
-
'confirm.removeSkill':'Remove shared skill "{name}" from
|
|
2316
|
-
'confirm.removeMemory':'Remove shared memory "{name}" from
|
|
2499
|
+
'confirm.removeTask':'Remove shared task "{name}" from team? This cannot be undone.',
|
|
2500
|
+
'confirm.removeSkill':'Remove shared skill "{name}" from team? This cannot be undone.',
|
|
2501
|
+
'confirm.removeMemory':'Remove shared memory "{name}" from team? This cannot be undone.',
|
|
2317
2502
|
'sharing.disabled':'Sharing disabled',
|
|
2318
|
-
'sharing.disabled.hint':'Enable sharing in
|
|
2319
|
-
'sharing.hubAdmin':'
|
|
2503
|
+
'sharing.disabled.hint':'Enable sharing in settings to connect a team.',
|
|
2504
|
+
'sharing.hubAdmin':'Team Admin',
|
|
2320
2505
|
'sharing.client':'Client',
|
|
2321
|
-
'sharing.hubMode':'
|
|
2506
|
+
'sharing.hubMode':'Server mode',
|
|
2322
2507
|
'sharing.hubMode.status':'Status: not connected to self',
|
|
2323
|
-
'sharing.hubMode.hint':'Configure sharing.client with hubAddress and userToken pointing to this
|
|
2508
|
+
'sharing.hubMode.hint':'Configure sharing.client with hubAddress and userToken pointing to this server to enable admin UI.',
|
|
2324
2509
|
'sharing.clientConfigured':'Client configured',
|
|
2325
2510
|
'sharing.clientDisconnected':'Status: disconnected',
|
|
2326
|
-
'sharing.clientDisconnected.hint':'Viewer will keep showing local data;
|
|
2511
|
+
'sharing.clientDisconnected.hint':'Viewer will keep showing local data; team actions may fail until the connection is restored.',
|
|
2327
2512
|
'sharing.clientNotConfigured':'Client not configured',
|
|
2328
2513
|
'sharing.clientNotConfigured.hint':'Set hubAddress and userToken in sharing.client to enable team features.',
|
|
2329
2514
|
'sharing.settingsDisabled':'Sharing is disabled.',
|
|
2330
|
-
'sharing.settingsDisabled.hint':'Enable sharing in
|
|
2515
|
+
'sharing.settingsDisabled.hint':'Enable sharing in settings to use team memory and skill collaboration.',
|
|
2331
2516
|
'sharing.noTeam':'No team connection.',
|
|
2332
2517
|
'sharing.adminUnavailable':'Admin tools unavailable.',
|
|
2333
2518
|
'sharing.adminEnabled':'Admin controls enabled',
|
|
@@ -2342,15 +2527,15 @@ const I18N={
|
|
|
2342
2527
|
'sharing.username.taken':'Username already taken',
|
|
2343
2528
|
'sharing.username.updated':'Username updated',
|
|
2344
2529
|
'sharing.username.error':'Failed to update username',
|
|
2345
|
-
'sharing.hubNotConfigured':'
|
|
2346
|
-
'sharing.hubNotConfigured.hint':'Add sharing.client.hubAddress and sharing.client.userToken pointing to this
|
|
2347
|
-
'sharing.notConnected':'Not connected to
|
|
2530
|
+
'sharing.hubNotConfigured':'Server is running but client connection is not configured.',
|
|
2531
|
+
'sharing.hubNotConfigured.hint':'Add sharing.client.hubAddress and sharing.client.userToken pointing to this server to enable the admin interface.',
|
|
2532
|
+
'sharing.notConnected':'Not connected to team server.',
|
|
2348
2533
|
'sharing.role':'Role:',
|
|
2349
2534
|
'sharing.mode':'Mode:',
|
|
2350
|
-
'sharing.role.hub':'Server (hosting the
|
|
2351
|
-
'sharing.role.client':'Member (connected to
|
|
2535
|
+
'sharing.role.hub':'Server (hosting the team)',
|
|
2536
|
+
'sharing.role.client':'Member (connected to team)',
|
|
2352
2537
|
'sharing.clientConfiguredLabel':'Client configured:',
|
|
2353
|
-
'sharing.configuredHub':'
|
|
2538
|
+
'sharing.configuredHub':'Team Server:',
|
|
2354
2539
|
'sharing.connected':'Connected:',
|
|
2355
2540
|
'sharing.yes':'yes',
|
|
2356
2541
|
'sharing.no':'no',
|
|
@@ -2361,9 +2546,9 @@ const I18N={
|
|
|
2361
2546
|
'sharing.loadingGroups':'Loading groups...',
|
|
2362
2547
|
'sharing.noGroupsYet':'No groups yet.',
|
|
2363
2548
|
'search.localResults':'Local Results',
|
|
2364
|
-
'search.hubResults':'
|
|
2549
|
+
'search.hubResults':'Team Results',
|
|
2365
2550
|
'search.noLocal':'No local results.',
|
|
2366
|
-
'search.noHub':'No
|
|
2551
|
+
'search.noHub':'No team results.',
|
|
2367
2552
|
'search.viewDetail':'View Detail',
|
|
2368
2553
|
'search.sharedMemory':'Shared Memory',
|
|
2369
2554
|
'search.loadFailed':'Failed to load shared memory',
|
|
@@ -2371,20 +2556,48 @@ const I18N={
|
|
|
2371
2556
|
'share.shareBtn':'Share',
|
|
2372
2557
|
'share.updateBtn':'Update',
|
|
2373
2558
|
'share.unshareBtn':'Unshare',
|
|
2374
|
-
'
|
|
2559
|
+
'share.hub.sharedBadge':'Shared to Team',
|
|
2560
|
+
'share.hub.shareBtn':'Share to Team',
|
|
2561
|
+
'share.hub.unshareBtn':'Remove from Team',
|
|
2562
|
+
'share.status.thisAgent':'This Agent',
|
|
2563
|
+
'share.status.agents':'Local',
|
|
2564
|
+
'share.status.hub':'Team',
|
|
2565
|
+
'share.scope.title':'Sharing Scope',
|
|
2566
|
+
'share.scope.private':'This Agent Only',
|
|
2567
|
+
'share.scope.local':'All Local Agents',
|
|
2568
|
+
'share.scope.team':'Team',
|
|
2569
|
+
'share.scope.current':'Current',
|
|
2570
|
+
'share.scope.teamDisabled':'Not connected to team server',
|
|
2571
|
+
'share.scope.teamIncludes':'Includes visibility to all local agents',
|
|
2572
|
+
'share.scope.confirm':'Confirm',
|
|
2573
|
+
'share.scope.cancel':'Cancel',
|
|
2574
|
+
'share.scope.shrinkToLocal':'This will remove the content from team. Team members will no longer be able to find it. Continue?',
|
|
2575
|
+
'share.scope.shrinkToPrivate':'This will remove the content from team and local sharing. Continue?',
|
|
2576
|
+
'share.scope.shrinkLocalToPrivate':'This will stop sharing with other local agents. Continue?',
|
|
2577
|
+
'share.scope.changed':'Sharing scope updated',
|
|
2578
|
+
'share.scope.changeFail':'Failed to update sharing scope',
|
|
2579
|
+
'share.scope.inactiveDisabled':'Merged/duplicate memories cannot be shared (not included in search results)',
|
|
2580
|
+
'share.scope.taskNotCompleted':'Only completed tasks can be shared (active/skipped tasks are excluded)',
|
|
2581
|
+
'share.scope.skillNotActive':'Only active skills can be shared (draft/archived skills are excluded)',
|
|
2582
|
+
'skill.pullToLocal':'Pull to Local',
|
|
2583
|
+
'toast.taskShared':'Task shared to team',
|
|
2375
2584
|
'toast.taskShareFail':'Task share failed',
|
|
2376
|
-
'toast.taskUnshared':'Task
|
|
2585
|
+
'toast.taskUnshared':'Task removed from team',
|
|
2377
2586
|
'toast.taskUnshareFail':'Task unshare failed',
|
|
2378
|
-
'toast.memoryShared':'Memory shared',
|
|
2587
|
+
'toast.memoryShared':'Memory shared to team',
|
|
2379
2588
|
'toast.memoryShareFail':'Memory share failed',
|
|
2380
|
-
'toast.memoryUnshared':'Memory
|
|
2589
|
+
'toast.memoryUnshared':'Memory removed from team',
|
|
2381
2590
|
'toast.memoryUnshareFail':'Memory unshare failed',
|
|
2382
|
-
'toast.skillShared':'Skill shared',
|
|
2591
|
+
'toast.skillShared':'Skill shared to team',
|
|
2383
2592
|
'toast.skillShareFail':'Skill share failed',
|
|
2384
|
-
'toast.skillUnshared':'Skill
|
|
2593
|
+
'toast.skillUnshared':'Skill removed from team',
|
|
2385
2594
|
'toast.skillUnshareFail':'Skill unshare failed',
|
|
2386
2595
|
'share.memoryVisibilityPrompt':'Share visibility (public or group):',
|
|
2387
|
-
'share.memoryUnshareConfirm':'
|
|
2596
|
+
'share.memoryUnshareConfirm':'Remove this memory from team?',
|
|
2597
|
+
'share.memoryLocalUnshareConfirm':'Stop sharing this memory locally?',
|
|
2598
|
+
'share.localUnshareConfirm':'Stop sharing locally?',
|
|
2599
|
+
'share.teamUnshareConfirm':'Remove from team sharing?',
|
|
2600
|
+
'share.local.originalOwnerMissing':'This shared memory has no recorded original owner, so it cannot be restored automatically.',
|
|
2388
2601
|
'share.group':'Group',
|
|
2389
2602
|
'share.public':'Public',
|
|
2390
2603
|
'toast.skillPulled':'Skill pulled to local storage',
|
|
@@ -2415,35 +2628,35 @@ const I18N={
|
|
|
2415
2628
|
'update.failed':'Update failed',
|
|
2416
2629
|
'update.restarting':'Restarting service, page will refresh automatically...',
|
|
2417
2630
|
'update.dismiss':'Dismiss',
|
|
2418
|
-
'sharing.disable.confirm.hub':'You are about to shut down the
|
|
2419
|
-
'sharing.disable.confirm.client':'You are about to disconnect from the team
|
|
2631
|
+
'sharing.disable.confirm.hub':'You are about to shut down the team server.\\n\\nWhat will happen:\\n\\u2022 All connected team members will be disconnected\\n\\u2022 They will no longer be able to sync memories, tasks, or skills\\n\\u2022 Shared data is preserved and will be available when you re-enable\\n\\nAre you sure?',
|
|
2632
|
+
'sharing.disable.confirm.client':'You are about to disconnect from the team.\\n\\nWhat will happen:\\n\\u2022 You will no longer receive shared memories, tasks, or skills from the team\\n\\u2022 Your local data is preserved and will not be affected\\n\\u2022 You can reconnect later by re-enabling sharing\\n\\nAre you sure?',
|
|
2420
2633
|
'sharing.disable.restartAlert':'Sharing has been disabled. Please restart the OpenClaw gateway for the change to take effect.\\n\\nRun: openclaw gateway stop && openclaw gateway start',
|
|
2421
2634
|
'admin.notEnabled.title':'Team sharing is not enabled',
|
|
2422
|
-
'admin.notEnabled.desc':'The Admin Panel is used to manage team members, shared memories, tasks, and skills. To use this feature, you need to enable
|
|
2423
|
-
'admin.notEnabled.setupHub':'Set Up as
|
|
2635
|
+
'admin.notEnabled.desc':'The Admin Panel is used to manage team members, shared memories, tasks, and skills. To use this feature, you need to enable team sharing first.',
|
|
2636
|
+
'admin.notEnabled.setupHub':'Set Up as Team Server',
|
|
2424
2637
|
'admin.notEnabled.joinTeam':'Join an Existing Team',
|
|
2425
2638
|
'admin.notEnabled.hint':'If you have previously configured sharing, your data is still preserved. Re-enabling sharing will restore access to all shared content.',
|
|
2426
|
-
'sharing.disconnected.hint':'Unable to reach the
|
|
2639
|
+
'sharing.disconnected.hint':'Unable to reach the team server. The server may be offline or the network is unavailable.',
|
|
2427
2640
|
'sharing.retryConnection':'Retry Connection',
|
|
2428
2641
|
'sharing.retryConnection.loading':'Connecting...',
|
|
2429
2642
|
'sharing.retryConnection.success':'Connected successfully!',
|
|
2430
|
-
'sharing.retryConnection.fail':'Still unable to connect. Check if the
|
|
2643
|
+
'sharing.retryConnection.fail':'Still unable to connect. Check if the team server is online.',
|
|
2431
2644
|
'guide.title':'Get Started with Team Collaboration',
|
|
2432
2645
|
'guide.subtitle':'MemOS supports team memory sharing. Choose one of the following options to enable collaboration, or continue using local-only mode.',
|
|
2433
2646
|
'guide.join.title':'Join a Remote Team',
|
|
2434
|
-
'guide.join.desc':'Your team already has a
|
|
2435
|
-
'guide.join.s1':'Ask your
|
|
2436
|
-
'guide.join.s2':'Go to Settings \u2192
|
|
2437
|
-
'guide.join.s3':'Fill in
|
|
2647
|
+
'guide.join.desc':'Your team already has a server running? Join it to share memories, tasks and skills with team members.',
|
|
2648
|
+
'guide.join.s1':'Ask your team admin for the Server Address and Team Token',
|
|
2649
|
+
'guide.join.s2':'Go to Settings \u2192 Team Sharing, enable sharing, select "Client" mode',
|
|
2650
|
+
'guide.join.s3':'Fill in Server Address and Team Token, click "Test Connection"',
|
|
2438
2651
|
'guide.join.s4':'Save settings and restart the OpenClaw gateway (page refreshes automatically)',
|
|
2439
2652
|
'guide.join.btn':'\u2192 Configure Client Mode',
|
|
2440
|
-
'guide.hub.title':'Start Your Own
|
|
2441
|
-
'guide.hub.desc':'Be the team server. Run
|
|
2442
|
-
'guide.hub.s1':'Go to Settings \u2192
|
|
2653
|
+
'guide.hub.title':'Start Your Own Team Server',
|
|
2654
|
+
'guide.hub.desc':'Be the team server. Run it on this device so others can connect and share memories with you.',
|
|
2655
|
+
'guide.hub.s1':'Go to Settings \u2192 Team Sharing, enable sharing, select "Server" mode',
|
|
2443
2656
|
'guide.hub.s2':'Set a team name, save settings, and restart the gateway (page refreshes automatically)',
|
|
2444
|
-
'guide.hub.s3':'Share the
|
|
2657
|
+
'guide.hub.s3':'Share the Server Address and Team Token with your team members',
|
|
2445
2658
|
'guide.hub.s4':'Approve join requests in the Admin Panel',
|
|
2446
|
-
'guide.hub.btn':'\u2192 Configure
|
|
2659
|
+
'guide.hub.btn':'\u2192 Configure Server Mode'
|
|
2447
2660
|
},
|
|
2448
2661
|
zh:{
|
|
2449
2662
|
'title':'MemOS 记忆',
|
|
@@ -2490,14 +2703,14 @@ const I18N={
|
|
|
2490
2703
|
'skills.search.local':'本地',
|
|
2491
2704
|
'skills.search.noresult':'未找到匹配的技能',
|
|
2492
2705
|
'skills.load.error':'加载技能失败',
|
|
2493
|
-
'skills.hub.title':'\u{1F310}
|
|
2706
|
+
'skills.hub.title':'\u{1F310} 团队共享技能',
|
|
2494
2707
|
'scope.local':'本地',
|
|
2495
2708
|
'scope.group':'团队',
|
|
2496
2709
|
'scope.all':'全部',
|
|
2497
|
-
'skills.visibility.public':'
|
|
2498
|
-
'skills.visibility.private':'
|
|
2499
|
-
'skills.setPublic':'
|
|
2500
|
-
'skills.setPrivate':'
|
|
2710
|
+
'skills.visibility.public':'本机共享',
|
|
2711
|
+
'skills.visibility.private':'仅本智能体',
|
|
2712
|
+
'skills.setPublic':'共享给本机智能体',
|
|
2713
|
+
'skills.setPrivate':'仅本智能体',
|
|
2501
2714
|
'tasks.total':'任务总数',
|
|
2502
2715
|
'tasks.active':'进行中',
|
|
2503
2716
|
'tasks.completed':'已完成',
|
|
@@ -2514,6 +2727,17 @@ const I18N={
|
|
|
2514
2727
|
'tasks.skipped.default':'对话内容过少,未生成摘要。该任务不会出现在检索结果中。',
|
|
2515
2728
|
'refresh':'\\u21BB 刷新',
|
|
2516
2729
|
'logout':'退出',
|
|
2730
|
+
'notif.title':'\\u{1F514} 通知',
|
|
2731
|
+
'notif.markAll':'全部已读',
|
|
2732
|
+
'notif.empty':'暂无通知',
|
|
2733
|
+
'notif.removed.memory':'你共享的记忆被管理员移除',
|
|
2734
|
+
'notif.removed.task':'你共享的任务被管理员移除',
|
|
2735
|
+
'notif.removed.skill':'你共享的技能被管理员移除',
|
|
2736
|
+
'notif.clearAll':'清除全部',
|
|
2737
|
+
'notif.timeAgo.just':'刚刚',
|
|
2738
|
+
'notif.timeAgo.min':'{n}分钟前',
|
|
2739
|
+
'notif.timeAgo.hour':'{n}小时前',
|
|
2740
|
+
'notif.timeAgo.day':'{n}天前',
|
|
2517
2741
|
'stat.memories':'记忆',
|
|
2518
2742
|
'stat.sessions':'会话',
|
|
2519
2743
|
'stat.embeddings':'嵌入',
|
|
@@ -2568,6 +2792,8 @@ const I18N={
|
|
|
2568
2792
|
'chart.nodata':'此范围内暂无数据',
|
|
2569
2793
|
'chart.nocalls':'此范围内暂无查看器调用',
|
|
2570
2794
|
'chart.toolperf':'工具响应耗时',
|
|
2795
|
+
'chart.apply':'应用',
|
|
2796
|
+
'chart.selectRange':'请选择时间范围',
|
|
2571
2797
|
'chart.list':'列表',
|
|
2572
2798
|
'chart.search':'搜索',
|
|
2573
2799
|
'modal.edit':'编辑记忆',
|
|
@@ -2583,11 +2809,14 @@ const I18N={
|
|
|
2583
2809
|
'toast.deleted':'记忆已删除',
|
|
2584
2810
|
'toast.opfail':'操作失败',
|
|
2585
2811
|
'toast.delfail':'删除失败',
|
|
2586
|
-
'toast.setPublic':'
|
|
2587
|
-
'toast.setPrivate':'
|
|
2812
|
+
'toast.setPublic':'已共享给本机智能体',
|
|
2813
|
+
'toast.setPrivate':'现在仅本智能体可见',
|
|
2588
2814
|
'toast.cleared':'所有记忆已清除',
|
|
2589
2815
|
'toast.clearfail':'清除失败',
|
|
2590
2816
|
'toast.notfound':'缓存中未找到此记忆',
|
|
2817
|
+
'confirm.title':'确认',
|
|
2818
|
+
'confirm.ok':'确定',
|
|
2819
|
+
'confirm.cancel':'取消',
|
|
2591
2820
|
'confirm.delete':'确定要删除这条记忆吗?',
|
|
2592
2821
|
'confirm.clearall':'确定要删除所有记忆?此操作不可撤销。',
|
|
2593
2822
|
'confirm.clearall2':'你真的确定吗?',
|
|
@@ -2763,16 +2992,16 @@ const I18N={
|
|
|
2763
2992
|
'tasks.error.detail':'加载任务详情失败',
|
|
2764
2993
|
'tasks.untitled.related':'未命名',
|
|
2765
2994
|
'tab.admin':'\u{1F6E1} 管理',
|
|
2766
|
-
'settings.hub':'
|
|
2767
|
-
'settings.hub.enable':'
|
|
2995
|
+
'settings.hub':'团队共享',
|
|
2996
|
+
'settings.hub.enable':'启用团队共享',
|
|
2768
2997
|
'settings.hub.enable.hint':'关闭时仅本地使用,不影响其他功能。',
|
|
2769
|
-
'settings.hub.enable.label':'
|
|
2998
|
+
'settings.hub.enable.label':'启用团队共享',
|
|
2770
2999
|
'settings.hub.role':'角色',
|
|
2771
|
-
'settings.hub.role.hub':'
|
|
2772
|
-
'settings.hub.role.client':'
|
|
2773
|
-
'settings.hub.role.hint':'
|
|
2774
|
-
'settings.hub.port':'
|
|
2775
|
-
'settings.hub.port.hint':'
|
|
3000
|
+
'settings.hub.role.hub':'服务端(托管团队)',
|
|
3001
|
+
'settings.hub.role.client':'客户端(加入团队)',
|
|
3002
|
+
'settings.hub.role.hint':'服务端 = 本机托管团队;客户端 = 加入别人的团队。两种模式互斥,不能同时使用。',
|
|
3003
|
+
'settings.hub.port':'服务端口',
|
|
3004
|
+
'settings.hub.port.hint':'团队服务端口,默认 18800',
|
|
2776
3005
|
'settings.hub.teamName':'团队名称',
|
|
2777
3006
|
'settings.hub.teamName.hint':'你的团队显示名称',
|
|
2778
3007
|
'settings.hub.teamToken':'团队令牌',
|
|
@@ -2781,23 +3010,23 @@ const I18N={
|
|
|
2781
3010
|
'settings.hub.hubSteps.title':'快速配置(3 步)',
|
|
2782
3011
|
'settings.hub.hubSteps.s1':'填写下方团队名称(或保持默认)',
|
|
2783
3012
|
'settings.hub.hubSteps.s2':'点击"保存设置",然后重启 OpenClaw 网关',
|
|
2784
|
-
'settings.hub.hubSteps.s3':'
|
|
3013
|
+
'settings.hub.hubSteps.s3':'将下方的服务器地址和团队令牌分享给团队成员',
|
|
2785
3014
|
'settings.hub.clientSteps.title':'快速配置(3 步)',
|
|
2786
|
-
'settings.hub.clientSteps.s1':'
|
|
3015
|
+
'settings.hub.clientSteps.s1':'向团队管理员获取服务器地址和团队令牌',
|
|
2787
3016
|
'settings.hub.clientSteps.s2':'填入下方,点击"测试连接"验证连通性',
|
|
2788
3017
|
'settings.hub.clientSteps.s3':'点击「保存设置」,然后重启 OpenClaw 网关(页面会自动刷新)',
|
|
2789
3018
|
'settings.hub.shareInfo.title':'请将以下信息分享给团队成员:',
|
|
2790
3019
|
'settings.hub.shareInfo.yourIP':'你的IP',
|
|
2791
3020
|
'settings.hub.shareInfo.clickCopy':'点击复制',
|
|
2792
|
-
'settings.hub.restartAlert':'
|
|
2793
|
-
'settings.hub.hubAddress':'
|
|
2794
|
-
'settings.hub.hubAddress.hint':'
|
|
3021
|
+
'settings.hub.restartAlert':'团队共享配置已保存!请重启 OpenClaw 网关使配置生效。\\n\\n执行命令:openclaw gateway stop && openclaw gateway start',
|
|
3022
|
+
'settings.hub.hubAddress':'服务器地址',
|
|
3023
|
+
'settings.hub.hubAddress.hint':'团队服务器地址,如 192.168.1.100:18800',
|
|
2795
3024
|
'settings.hub.teamTokenClient':'团队令牌',
|
|
2796
|
-
'settings.hub.teamTokenClient.hint':'
|
|
3025
|
+
'settings.hub.teamTokenClient.hint':'向团队管理员获取此令牌以加入团队',
|
|
2797
3026
|
'settings.hub.userToken':'用户令牌',
|
|
2798
3027
|
'settings.hub.userToken.hint':'通常在加入团队后自动获取,无需手动填写。',
|
|
2799
3028
|
'settings.hub.testConnection':'测试连接',
|
|
2800
|
-
'settings.hub.test.noAddr':'
|
|
3029
|
+
'settings.hub.test.noAddr':'请先输入服务器地址',
|
|
2801
3030
|
'settings.hub.test.testing':'测试中...',
|
|
2802
3031
|
'settings.hub.test.ok':'连接成功',
|
|
2803
3032
|
'settings.hub.test.fail':'连接失败',
|
|
@@ -2813,16 +3042,16 @@ const I18N={
|
|
|
2813
3042
|
'sharing.sidebar.notConfigured':'未配置',
|
|
2814
3043
|
'sharing.sidebar.identity':'身份:',
|
|
2815
3044
|
'sharing.sidebar.admin':'管理员',
|
|
2816
|
-
'sharing.sidebar.targetHub':'
|
|
2817
|
-
'sharing.pendingApproval.hint':'
|
|
2818
|
-
'sharing.rejected.hint':'
|
|
3045
|
+
'sharing.sidebar.targetHub':'团队服务器:',
|
|
3046
|
+
'sharing.pendingApproval.hint':'加入申请已提交,请等待团队管理员审核通过。',
|
|
3047
|
+
'sharing.rejected.hint':'您的加入申请已被团队管理员拒绝,请联系管理员或重新申请。',
|
|
2819
3048
|
'sharing.retryJoin':'重新申请',
|
|
2820
3049
|
'sharing.retryJoin.hint':'清除本地连接数据并重新提交加入申请',
|
|
2821
3050
|
'sharing.retryJoin.confirm':'这将清除当前连接数据并重新提交加入申请,是否继续?',
|
|
2822
3051
|
'sharing.retryJoin.success':'加入申请已重新提交,请等待管理员审核。',
|
|
2823
3052
|
'sharing.retryJoin.fail':'重新申请失败',
|
|
2824
|
-
'sharing.cannotJoinSelf':'
|
|
2825
|
-
'scope.hub':'
|
|
3053
|
+
'sharing.cannotJoinSelf':'不能加入自己的服务端,请输入远程服务器地址。',
|
|
3054
|
+
'scope.hub':'团队',
|
|
2826
3055
|
'memory.detail.title':'记忆详情',
|
|
2827
3056
|
'memory.detail.loading':'加载中...',
|
|
2828
3057
|
'memory.detail.notFound':'未找到该记忆',
|
|
@@ -2830,12 +3059,13 @@ const I18N={
|
|
|
2830
3059
|
'memory.detail.created':'创建于 ',
|
|
2831
3060
|
'memory.detail.updated':'更新于 ',
|
|
2832
3061
|
'memory.detail.viewTarget':'查看目标: ',
|
|
2833
|
-
'admin.title':'
|
|
3062
|
+
'admin.title':'团队管理面板',
|
|
2834
3063
|
'admin.subtitle':'管理团队成员和共享资源',
|
|
2835
3064
|
'admin.refresh':'\u21BB 刷新',
|
|
2836
3065
|
'admin.tab.users':'用户',
|
|
3066
|
+
'admin.tab.tasks':'共享任务',
|
|
2837
3067
|
'admin.tab.groups':'分组',
|
|
2838
|
-
'admin.tab.memories':'
|
|
3068
|
+
'admin.tab.memories':'共享记忆',
|
|
2839
3069
|
'admin.tab.skills':'共享技能',
|
|
2840
3070
|
'admin.stat.activeUsers':'活跃用户',
|
|
2841
3071
|
'admin.stat.pending':'待审核',
|
|
@@ -2849,6 +3079,43 @@ const I18N={
|
|
|
2849
3079
|
'admin.approve':'批准',
|
|
2850
3080
|
'admin.reject':'拒绝',
|
|
2851
3081
|
'admin.device':'设备:',
|
|
3082
|
+
'admin.joined':'加入:',
|
|
3083
|
+
'admin.approved':'审批:',
|
|
3084
|
+
'admin.lastActive':'最后活跃:',
|
|
3085
|
+
'admin.ip':'IP:',
|
|
3086
|
+
'admin.contrib.memories':'记忆',
|
|
3087
|
+
'admin.contrib.tasks':'任务',
|
|
3088
|
+
'admin.contrib.skills':'技能',
|
|
3089
|
+
'admin.groups':'分组:',
|
|
3090
|
+
'admin.neverActive':'从未',
|
|
3091
|
+
'admin.promoteAdmin':'提升为管理员',
|
|
3092
|
+
'admin.demoteMember':'降为成员',
|
|
3093
|
+
'admin.editName':'编辑名称',
|
|
3094
|
+
'admin.lastAdminHint':'唯一管理员 — 无法删除或降级',
|
|
3095
|
+
'admin.editNamePrompt':'请输入新用户名:',
|
|
3096
|
+
'confirm.promoteAdmin':'确定要将此用户提升为管理员吗?管理员可以管理所有团队成员和资源。',
|
|
3097
|
+
'confirm.demoteMember':'确定要将此管理员降为普通成员吗?',
|
|
3098
|
+
'toast.roleChanged':'角色已更新',
|
|
3099
|
+
'toast.roleChangeFail':'角色更新失败',
|
|
3100
|
+
'toast.usernameChanged':'用户名已更新',
|
|
3101
|
+
'toast.renameFail':'重命名失败',
|
|
3102
|
+
'toast.invalidUsername':'用户名长度需为 2-32 个字符',
|
|
3103
|
+
'admin.filterAll':'全部用户',
|
|
3104
|
+
'admin.filterAllValues':'全部',
|
|
3105
|
+
'admin.filter.owner':'全部用户',
|
|
3106
|
+
'admin.filter.visibility':'可见性',
|
|
3107
|
+
'admin.filter.group':'团队',
|
|
3108
|
+
'admin.search.placeholder':'搜索...',
|
|
3109
|
+
'admin.sort.newest':'最新优先',
|
|
3110
|
+
'admin.sort.oldest':'最早优先',
|
|
3111
|
+
'admin.filter.role':'角色',
|
|
3112
|
+
'admin.filter.status':'状态',
|
|
3113
|
+
'admin.expand':'展开',
|
|
3114
|
+
'admin.collapse':'收起',
|
|
3115
|
+
'admin.noContent':'暂无内容',
|
|
3116
|
+
'admin.summary':'摘要',
|
|
3117
|
+
'admin.description':'描述',
|
|
3118
|
+
'admin.contentLabel':'内容',
|
|
2852
3119
|
'admin.groups':'分组',
|
|
2853
3120
|
'admin.newGroup':'+ 新建分组',
|
|
2854
3121
|
'admin.groupName':'分组名称',
|
|
@@ -2862,16 +3129,20 @@ const I18N={
|
|
|
2862
3129
|
'admin.add':'添加',
|
|
2863
3130
|
'admin.remove':'移除',
|
|
2864
3131
|
'admin.sharedTasks':'共享任务',
|
|
2865
|
-
'admin.noSharedTasks':'
|
|
3132
|
+
'admin.noSharedTasks':'团队暂无共享任务。',
|
|
2866
3133
|
'admin.owner':'归属:',
|
|
2867
3134
|
'admin.group':'分组:',
|
|
3135
|
+
'admin.kind':'类型:',
|
|
3136
|
+
'admin.role':'角色:',
|
|
3137
|
+
'admin.visibility':'可见性:',
|
|
3138
|
+
'admin.session':'会话',
|
|
3139
|
+
'admin.content':'内容',
|
|
2868
3140
|
'admin.chunks':'记忆片段:',
|
|
2869
3141
|
'admin.updated':'更新于:',
|
|
2870
3142
|
'admin.sharedSkills':'共享技能',
|
|
2871
|
-
'admin.noSharedSkills':'
|
|
3143
|
+
'admin.noSharedSkills':'团队暂无共享技能。',
|
|
2872
3144
|
'admin.sharedMemories':'共享记忆',
|
|
2873
|
-
'admin.noSharedMemories':'
|
|
2874
|
-
'admin.tab.sharedMemories':'共享记忆',
|
|
3145
|
+
'admin.noSharedMemories':'团队暂无共享记忆。',
|
|
2875
3146
|
'admin.version':'v',
|
|
2876
3147
|
'admin.quality':'质量:',
|
|
2877
3148
|
'admin.membersCount':'成员({n}):',
|
|
@@ -2880,6 +3151,7 @@ const I18N={
|
|
|
2880
3151
|
'admin.noPermission':'您没有管理员权限,无法访问此面板。',
|
|
2881
3152
|
'admin.groupsFailed':'加载分组失败:',
|
|
2882
3153
|
'toast.userApproved':'用户已批准',
|
|
3154
|
+
'sharing.approved.toast':'您的加入申请已通过审核!',
|
|
2883
3155
|
'toast.userRejected':'用户已拒绝',
|
|
2884
3156
|
'toast.approveFail':'批准失败',
|
|
2885
3157
|
'toast.rejectFail':'拒绝失败',
|
|
@@ -2896,27 +3168,32 @@ const I18N={
|
|
|
2896
3168
|
'toast.removeFail':'移除失败',
|
|
2897
3169
|
'toast.groupNameRequired':'请输入分组名称',
|
|
2898
3170
|
'confirm.rejectUser':'确定要拒绝此用户吗?',
|
|
3171
|
+
'confirm.removeUser':'确定要移除此用户吗?移除后该用户将无法访问团队资源。',
|
|
3172
|
+
'confirm.cleanResources':'是否同时删除该用户共享的所有资源(任务、技能、记忆)?',
|
|
3173
|
+
'toast.userRemoved':'用户已移除',
|
|
3174
|
+
'toast.removeFail':'移除失败',
|
|
3175
|
+
'admin.remove':'移除',
|
|
2899
3176
|
'confirm.removeGroupMember':'确定要将此成员移出分组吗?',
|
|
2900
3177
|
'confirm.removeMember':'确定要移除此成员吗?',
|
|
2901
3178
|
'confirm.deleteGroup':'确定要删除分组「{name}」吗?成员将被移除。',
|
|
2902
3179
|
'confirm.deleteGroupShort':'确定要删除分组「{name}」吗?',
|
|
2903
|
-
'confirm.removeTask':'
|
|
2904
|
-
'confirm.removeSkill':'
|
|
2905
|
-
'confirm.removeMemory':'
|
|
3180
|
+
'confirm.removeTask':'确定要从团队移除共享任务「{name}」吗?此操作不可撤销。',
|
|
3181
|
+
'confirm.removeSkill':'确定要从团队移除共享技能「{name}」吗?此操作不可撤销。',
|
|
3182
|
+
'confirm.removeMemory':'确定要从团队移除共享记忆「{name}」吗?此操作不可撤销。',
|
|
2906
3183
|
'sharing.disabled':'共享已禁用',
|
|
2907
|
-
'sharing.disabled.hint':'
|
|
2908
|
-
'sharing.hubAdmin':'
|
|
3184
|
+
'sharing.disabled.hint':'在设置中启用共享以连接团队。',
|
|
3185
|
+
'sharing.hubAdmin':'团队管理员',
|
|
2909
3186
|
'sharing.client':'客户端',
|
|
2910
|
-
'sharing.hubMode':'
|
|
3187
|
+
'sharing.hubMode':'服务端模式',
|
|
2911
3188
|
'sharing.hubMode.status':'状态:未连接到自身',
|
|
2912
|
-
'sharing.hubMode.hint':'配置 sharing.client 的 hubAddress 和 userToken
|
|
3189
|
+
'sharing.hubMode.hint':'配置 sharing.client 的 hubAddress 和 userToken 指向此服务端以启用管理界面。',
|
|
2913
3190
|
'sharing.clientConfigured':'客户端已配置',
|
|
2914
3191
|
'sharing.clientDisconnected':'状态:已断开',
|
|
2915
|
-
'sharing.clientDisconnected.hint':'
|
|
3192
|
+
'sharing.clientDisconnected.hint':'查看器将继续显示本地数据;团队操作可能在连接恢复前失败。',
|
|
2916
3193
|
'sharing.clientNotConfigured':'客户端未配置',
|
|
2917
3194
|
'sharing.clientNotConfigured.hint':'设置 sharing.client 中的 hubAddress 和 userToken 以启用团队功能。',
|
|
2918
3195
|
'sharing.settingsDisabled':'共享已禁用。',
|
|
2919
|
-
'sharing.settingsDisabled.hint':'
|
|
3196
|
+
'sharing.settingsDisabled.hint':'在设置中启用共享以使用团队记忆和技能协作。',
|
|
2920
3197
|
'sharing.noTeam':'无团队连接。',
|
|
2921
3198
|
'sharing.adminUnavailable':'管理工具不可用。',
|
|
2922
3199
|
'sharing.adminEnabled':'管理控制已启用',
|
|
@@ -2931,15 +3208,15 @@ const I18N={
|
|
|
2931
3208
|
'sharing.username.taken':'用户名已被占用',
|
|
2932
3209
|
'sharing.username.updated':'用户名已更新',
|
|
2933
3210
|
'sharing.username.error':'更新用户名失败',
|
|
2934
|
-
'sharing.hubNotConfigured':'
|
|
2935
|
-
'sharing.hubNotConfigured.hint':'添加 sharing.client.hubAddress 和 sharing.client.userToken
|
|
2936
|
-
'sharing.notConnected':'
|
|
3211
|
+
'sharing.hubNotConfigured':'服务端正在运行,但客户端连接未配置。',
|
|
3212
|
+
'sharing.hubNotConfigured.hint':'添加 sharing.client.hubAddress 和 sharing.client.userToken 指向此服务端以启用管理界面。',
|
|
3213
|
+
'sharing.notConnected':'未连接到团队服务器。',
|
|
2937
3214
|
'sharing.role':'角色:',
|
|
2938
3215
|
'sharing.mode':'身份:',
|
|
2939
|
-
'sharing.role.hub':'
|
|
2940
|
-
'sharing.role.client':'
|
|
3216
|
+
'sharing.role.hub':'服务端(托管团队)',
|
|
3217
|
+
'sharing.role.client':'成员(连接到团队)',
|
|
2941
3218
|
'sharing.clientConfiguredLabel':'客户端已配置:',
|
|
2942
|
-
'sharing.configuredHub':'
|
|
3219
|
+
'sharing.configuredHub':'团队服务器:',
|
|
2943
3220
|
'sharing.connected':'已连接:',
|
|
2944
3221
|
'sharing.yes':'是',
|
|
2945
3222
|
'sharing.no':'否',
|
|
@@ -2950,9 +3227,9 @@ const I18N={
|
|
|
2950
3227
|
'sharing.loadingGroups':'正在加载分组...',
|
|
2951
3228
|
'sharing.noGroupsYet':'暂无分组。',
|
|
2952
3229
|
'search.localResults':'本地结果',
|
|
2953
|
-
'search.hubResults':'
|
|
3230
|
+
'search.hubResults':'团队结果',
|
|
2954
3231
|
'search.noLocal':'无本地结果。',
|
|
2955
|
-
'search.noHub':'
|
|
3232
|
+
'search.noHub':'无团队结果。',
|
|
2956
3233
|
'search.viewDetail':'查看详情',
|
|
2957
3234
|
'search.sharedMemory':'共享记忆',
|
|
2958
3235
|
'search.loadFailed':'加载共享记忆失败',
|
|
@@ -2960,20 +3237,48 @@ const I18N={
|
|
|
2960
3237
|
'share.shareBtn':'共享',
|
|
2961
3238
|
'share.updateBtn':'更新共享',
|
|
2962
3239
|
'share.unshareBtn':'取消共享',
|
|
2963
|
-
'
|
|
3240
|
+
'share.hub.sharedBadge':'已共享到团队',
|
|
3241
|
+
'share.hub.shareBtn':'共享到团队',
|
|
3242
|
+
'share.hub.unshareBtn':'从团队移除',
|
|
3243
|
+
'share.status.thisAgent':'当前智能体',
|
|
3244
|
+
'share.status.agents':'本机',
|
|
3245
|
+
'share.status.hub':'团队',
|
|
3246
|
+
'share.scope.title':'共享范围',
|
|
3247
|
+
'share.scope.private':'仅本智能体',
|
|
3248
|
+
'share.scope.local':'本机所有智能体',
|
|
3249
|
+
'share.scope.team':'团队',
|
|
3250
|
+
'share.scope.current':'当前',
|
|
3251
|
+
'share.scope.teamDisabled':'未连接团队服务器',
|
|
3252
|
+
'share.scope.teamIncludes':'包含本机所有智能体的可见性',
|
|
3253
|
+
'share.scope.confirm':'确认',
|
|
3254
|
+
'share.scope.cancel':'取消',
|
|
3255
|
+
'share.scope.shrinkToLocal':'将从团队中移除此内容,团队成员将无法搜索到。确认?',
|
|
3256
|
+
'share.scope.shrinkToPrivate':'将从团队和本机共享中移除此内容。确认?',
|
|
3257
|
+
'share.scope.shrinkLocalToPrivate':'将停止共享给其他智能体。确认?',
|
|
3258
|
+
'share.scope.changed':'共享范围已更新',
|
|
3259
|
+
'share.scope.changeFail':'更新共享范围失败',
|
|
3260
|
+
'share.scope.inactiveDisabled':'已合并/重复的记忆无法共享(不会出现在检索结果中)',
|
|
3261
|
+
'share.scope.taskNotCompleted':'仅已完成的任务可以共享(进行中/已跳过的任务无法共享)',
|
|
3262
|
+
'share.scope.skillNotActive':'仅活跃的技能可以共享(草稿/已归档的技能无法共享)',
|
|
3263
|
+
'skill.pullToLocal':'拉取到本地',
|
|
3264
|
+
'toast.taskShared':'任务已共享到团队',
|
|
2964
3265
|
'toast.taskShareFail':'任务共享失败',
|
|
2965
|
-
'toast.taskUnshared':'
|
|
3266
|
+
'toast.taskUnshared':'任务已从团队移除',
|
|
2966
3267
|
'toast.taskUnshareFail':'取消共享失败',
|
|
2967
|
-
'toast.memoryShared':'
|
|
3268
|
+
'toast.memoryShared':'记忆已共享到团队',
|
|
2968
3269
|
'toast.memoryShareFail':'记忆共享失败',
|
|
2969
|
-
'toast.memoryUnshared':'
|
|
3270
|
+
'toast.memoryUnshared':'记忆已从团队移除',
|
|
2970
3271
|
'toast.memoryUnshareFail':'记忆取消共享失败',
|
|
2971
|
-
'toast.skillShared':'
|
|
3272
|
+
'toast.skillShared':'技能已共享到团队',
|
|
2972
3273
|
'toast.skillShareFail':'技能共享失败',
|
|
2973
|
-
'toast.skillUnshared':'
|
|
3274
|
+
'toast.skillUnshared':'技能已从团队移除',
|
|
2974
3275
|
'toast.skillUnshareFail':'技能取消共享失败',
|
|
2975
|
-
'share.memoryVisibilityPrompt':'
|
|
2976
|
-
'share.memoryUnshareConfirm':'
|
|
3276
|
+
'share.memoryVisibilityPrompt':'共享范围(public 或 group):',
|
|
3277
|
+
'share.memoryUnshareConfirm':'从团队移除该记忆?',
|
|
3278
|
+
'share.memoryLocalUnshareConfirm':'停止本机共享该记忆?',
|
|
3279
|
+
'share.localUnshareConfirm':'停止本机共享?',
|
|
3280
|
+
'share.teamUnshareConfirm':'从团队共享中移除?',
|
|
3281
|
+
'share.local.originalOwnerMissing':'该共享记忆没有记录原始所有者,无法自动恢复。',
|
|
2977
3282
|
'share.group':'团队',
|
|
2978
3283
|
'share.public':'公开',
|
|
2979
3284
|
'toast.skillPulled':'技能已拉取到本地',
|
|
@@ -3004,35 +3309,35 @@ const I18N={
|
|
|
3004
3309
|
'update.failed':'更新失败',
|
|
3005
3310
|
'update.restarting':'正在重启服务,页面将自动刷新...',
|
|
3006
3311
|
'update.dismiss':'关闭',
|
|
3007
|
-
'sharing.disable.confirm.hub':'
|
|
3008
|
-
'sharing.disable.confirm.client':'
|
|
3312
|
+
'sharing.disable.confirm.hub':'你即将关闭团队服务。\\n\\n关闭后将会:\\n\\u2022 所有已连接的团队成员将断开连接\\n\\u2022 他们将无法继续同步记忆、任务和技能\\n\\u2022 已共享的数据会保留,重新开启后仍可使用\\n\\n确定要关闭吗?',
|
|
3313
|
+
'sharing.disable.confirm.client':'你即将断开与团队的连接。\\n\\n断开后将会:\\n\\u2022 你将无法再接收团队共享的记忆、任务和技能\\n\\u2022 你的本地数据不受影响,会完整保留\\n\\u2022 之后可以随时重新开启共享来恢复连接\\n\\n确定要断开吗?',
|
|
3009
3314
|
'sharing.disable.restartAlert':'共享已关闭。请重启 OpenClaw 网关使更改生效。\\n\\n执行命令:openclaw gateway stop && openclaw gateway start',
|
|
3010
3315
|
'admin.notEnabled.title':'团队共享尚未开启',
|
|
3011
|
-
'admin.notEnabled.desc':'
|
|
3012
|
-
'admin.notEnabled.setupHub':'
|
|
3316
|
+
'admin.notEnabled.desc':'管理面板用于管理团队成员、共享的记忆、任务和技能。使用此功能前,需要先开启团队共享。',
|
|
3317
|
+
'admin.notEnabled.setupHub':'配置为团队服务端',
|
|
3013
3318
|
'admin.notEnabled.joinTeam':'加入已有团队',
|
|
3014
3319
|
'admin.notEnabled.hint':'如果之前配置过共享,你的数据仍然保留。重新开启共享即可恢复访问所有共享内容。',
|
|
3015
|
-
'sharing.disconnected.hint':'
|
|
3320
|
+
'sharing.disconnected.hint':'无法连接到团队服务器,服务器可能已下线或网络不可用。',
|
|
3016
3321
|
'sharing.retryConnection':'重试连接',
|
|
3017
3322
|
'sharing.retryConnection.loading':'连接中...',
|
|
3018
3323
|
'sharing.retryConnection.success':'连接成功!',
|
|
3019
|
-
'sharing.retryConnection.fail':'
|
|
3324
|
+
'sharing.retryConnection.fail':'仍然无法连接,请检查团队服务器是否在线。',
|
|
3020
3325
|
'guide.title':'开始团队协作',
|
|
3021
3326
|
'guide.subtitle':'MemOS 支持团队记忆共享。选择以下方式之一开启协作,或继续使用纯本地模式。',
|
|
3022
3327
|
'guide.join.title':'加入远程团队',
|
|
3023
|
-
'guide.join.desc':'
|
|
3024
|
-
'guide.join.s1':'
|
|
3025
|
-
'guide.join.s2':'前往「设置 →
|
|
3026
|
-
'guide.join.s3':'
|
|
3328
|
+
'guide.join.desc':'你的团队已有服务器在运行?加入即可与团队成员共享记忆、任务和技能。',
|
|
3329
|
+
'guide.join.s1':'向团队管理员索取服务器地址和团队令牌',
|
|
3330
|
+
'guide.join.s2':'前往「设置 → 团队共享」,开启共享,选择「客户端」模式',
|
|
3331
|
+
'guide.join.s3':'填写服务器地址和团队令牌,点击「测试连接」',
|
|
3027
3332
|
'guide.join.s4':'保存设置并重启 OpenClaw 网关(页面会自动刷新)',
|
|
3028
|
-
'guide.join.btn':'\u2192
|
|
3029
|
-
'guide.hub.title':'
|
|
3333
|
+
'guide.join.btn':'\u2192 配置客户端模式',
|
|
3334
|
+
'guide.hub.title':'自建团队服务',
|
|
3030
3335
|
'guide.hub.desc':'将本机作为团队服务端,让其他成员连接过来共享记忆。',
|
|
3031
|
-
'guide.hub.s1':'前往「设置 →
|
|
3336
|
+
'guide.hub.s1':'前往「设置 → 团队共享」,开启共享,选择「服务端」模式',
|
|
3032
3337
|
'guide.hub.s2':'设置团队名称,保存设置后重启网关(页面会自动刷新)',
|
|
3033
|
-
'guide.hub.s3':'
|
|
3338
|
+
'guide.hub.s3':'将服务器地址和团队令牌分享给团队成员',
|
|
3034
3339
|
'guide.hub.s4':'在管理面板中审批加入请求',
|
|
3035
|
-
'guide.hub.btn':'\u2192
|
|
3340
|
+
'guide.hub.btn':'\u2192 配置服务端模式'
|
|
3036
3341
|
}
|
|
3037
3342
|
};
|
|
3038
3343
|
const LANG_KEY='memos-viewer-lang';
|
|
@@ -3142,6 +3447,10 @@ function _genToken(len){
|
|
|
3142
3447
|
function onSharingToggle(){
|
|
3143
3448
|
var on=document.getElementById('cfgSharingEnabled').checked;
|
|
3144
3449
|
document.getElementById('sharingConfigPanel').style.display=on?'block':'none';
|
|
3450
|
+
var pw=document.getElementById('sharingPanelsWrap');
|
|
3451
|
+
if(pw) pw.style.display=on?'':'none';
|
|
3452
|
+
var tg=document.getElementById('teamSetupGuide');
|
|
3453
|
+
if(tg) tg.style.display=on?'none':'';
|
|
3145
3454
|
if(on) selectSharingRole(_sharingRole);
|
|
3146
3455
|
}
|
|
3147
3456
|
function selectSharingRole(role){
|
|
@@ -3184,10 +3493,10 @@ function updateHubShareInfo(){
|
|
|
3184
3493
|
var addr=ip?(ip+':'+esc(port)):('<'+t('settings.hub.shareInfo.yourIP')+'>:'+esc(port));
|
|
3185
3494
|
var tip=t('settings.hub.shareInfo.clickCopy');
|
|
3186
3495
|
content.innerHTML=
|
|
3187
|
-
'<span style="font-size:11px;color:var(--text-muted);font-weight:500">
|
|
3188
|
-
'<span style="'+cpStyle+'" onclick="navigator.clipboard.writeText(this.textContent);toast('
|
|
3496
|
+
'<span style="font-size:11px;color:var(--text-muted);font-weight:500">'+t('settings.hub.hubAddress')+'</span>'+
|
|
3497
|
+
'<span style="'+cpStyle+'" onclick="navigator.clipboard.writeText(this.textContent);toast(t('copy.done'),'success')" title="'+tip+'">'+addr+'</span>'+
|
|
3189
3498
|
'<span style="font-size:11px;color:var(--text-muted);font-weight:500">Team Token</span>'+
|
|
3190
|
-
'<span style="'+cpStyle+'" onclick="navigator.clipboard.writeText(this.textContent);toast('
|
|
3499
|
+
'<span style="'+cpStyle+'" onclick="navigator.clipboard.writeText(this.textContent);toast(t('copy.done'),'success')" title="'+tip+'">'+esc(token)+'</span>';
|
|
3191
3500
|
};
|
|
3192
3501
|
if(_cachedLocalIP){renderShare(_cachedLocalIP);return;}
|
|
3193
3502
|
renderShare('');
|
|
@@ -3306,6 +3615,7 @@ function onTaskScopeChange(){
|
|
|
3306
3615
|
loadTasks();
|
|
3307
3616
|
}
|
|
3308
3617
|
|
|
3618
|
+
var _clientPendingPollTimer=null;
|
|
3309
3619
|
async function loadSharingStatus(forcePending){
|
|
3310
3620
|
try{
|
|
3311
3621
|
const r=await fetch('/api/sharing/status');
|
|
@@ -3315,6 +3625,14 @@ async function loadSharingStatus(forcePending){
|
|
|
3315
3625
|
renderSharingSettings(d);
|
|
3316
3626
|
updateTeamGuide(d);
|
|
3317
3627
|
if(forcePending && d && d.admin && d.admin.canManageUsers) loadSharingPendingUsers();
|
|
3628
|
+
var conn=d&&d.connection||{};
|
|
3629
|
+
if(conn.pendingApproval&&!_clientPendingPollTimer){
|
|
3630
|
+
_clientPendingPollTimer=setInterval(function(){loadSharingStatus(false);},10000);
|
|
3631
|
+
}else if(!conn.pendingApproval&&_clientPendingPollTimer){
|
|
3632
|
+
clearInterval(_clientPendingPollTimer);
|
|
3633
|
+
_clientPendingPollTimer=null;
|
|
3634
|
+
if(conn.connected) toast(t('sharing.approved.toast'),'success');
|
|
3635
|
+
}
|
|
3318
3636
|
}catch(e){
|
|
3319
3637
|
renderSharingSidebar(null);
|
|
3320
3638
|
renderSharingSettings(null);
|
|
@@ -3397,6 +3715,7 @@ function renderSharingSettings(data){
|
|
|
3397
3715
|
if(data.role) _sharingRole=data.role;
|
|
3398
3716
|
var isAdmin=(data.admin&&data.admin.canManageUsers)||(conn.connected&&user.role==='admin')||(actualRole==='hub');
|
|
3399
3717
|
window._isHubAdmin=isAdmin;
|
|
3718
|
+
if(isAdmin) startAdminPoll();
|
|
3400
3719
|
var hubAdminBtn=document.getElementById('hubAdminEntryBtn');
|
|
3401
3720
|
|
|
3402
3721
|
if(actualRole==='hub'){
|
|
@@ -3470,7 +3789,7 @@ async function retryConnection(){
|
|
|
3470
3789
|
}
|
|
3471
3790
|
|
|
3472
3791
|
async function retryHubJoin(){
|
|
3473
|
-
if(!
|
|
3792
|
+
if(!(await confirmModal(t('sharing.retryJoin.confirm')))) return;
|
|
3474
3793
|
try{
|
|
3475
3794
|
var r=await fetch('/api/sharing/retry-join',{method:'POST',headers:{'Content-Type':'application/json'}});
|
|
3476
3795
|
var d=await r.json();
|
|
@@ -3590,152 +3909,48 @@ function guideGoToHub(role){
|
|
|
3590
3909
|
if(card) card.scrollIntoView({behavior:'smooth',block:'start'});
|
|
3591
3910
|
}
|
|
3592
3911
|
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
}catch(e){panel.innerHTML=t('admin.groupsFailed')+esc(String(e));}
|
|
3609
|
-
}
|
|
3610
|
-
function renderGroupManager(panel,groups){
|
|
3611
|
-
var html='<div style="margin-bottom:8px;display:flex;gap:8px;align-items:center;font-size:12px">'+
|
|
3612
|
-
'<strong>'+t('admin.groups')+' ('+groups.length+')</strong>'+
|
|
3613
|
-
'<button class="btn btn-sm" onclick="showCreateGroupForm()" style="font-size:11px">'+t('admin.newGroup')+'</button>'+
|
|
3614
|
-
'</div>';
|
|
3615
|
-
html+='<div id="createGroupForm" style="display:none;margin-bottom:10px;padding:10px;background:var(--bg);border:1px solid var(--border);border-radius:8px;font-size:12px">'+
|
|
3616
|
-
'<input id="newGroupName" type="text" placeholder="'+t('admin.groupName')+'" style="width:60%;padding:4px 8px;border:1px solid var(--border);border-radius:6px;font-size:12px;margin-right:6px;background:var(--bg);color:var(--text)">'+
|
|
3617
|
-
'<input id="newGroupDesc" type="text" placeholder="'+t('admin.groupDesc')+'" style="width:60%;padding:4px 8px;border:1px solid var(--border);border-radius:6px;font-size:12px;margin-right:6px;margin-top:6px;background:var(--bg);color:var(--text)">'+
|
|
3618
|
-
'<div style="margin-top:6px"><button class="btn btn-sm btn-primary" onclick="createGroup()" style="font-size:11px">'+t('admin.create')+'</button> '+
|
|
3619
|
-
'<button class="btn btn-sm btn-ghost" onclick="hideCreateGroupForm()" style="font-size:11px">'+t('admin.cancel')+'</button></div>'+
|
|
3620
|
-
'</div>';
|
|
3621
|
-
if(groups.length===0){
|
|
3622
|
-
html+='<div style="font-size:12px;color:var(--text-muted);padding:6px 0">'+t('sharing.noGroupsYet')+'</div>';
|
|
3623
|
-
}else{
|
|
3624
|
-
html+='<div style="display:flex;flex-direction:column;gap:6px">';
|
|
3625
|
-
for(var i=0;i<groups.length;i++){
|
|
3626
|
-
var g=groups[i];
|
|
3627
|
-
html+='<div style="padding:8px 12px;background:var(--bg);border:1px solid var(--border);border-radius:8px;font-size:12px">'+
|
|
3628
|
-
'<div style="display:flex;justify-content:space-between;align-items:center">'+
|
|
3629
|
-
'<div><strong style="font-size:12px">'+esc(g.name)+'</strong>'+(g.description?' — <span style="color:var(--text-sec);font-size:11px">'+esc(g.description)+'</span>':'')+'</div>'+
|
|
3630
|
-
'<div style="display:flex;gap:6px">'+
|
|
3631
|
-
'<button class="btn btn-sm" onclick="toggleGroupMembers("'+escAttr(g.id)+'")" style="font-size:11px;padding:2px 8px">'+t('admin.members')+'</button>'+
|
|
3632
|
-
'<button class="btn btn-sm btn-ghost" onclick="deleteGroup("'+escAttr(g.id)+'","'+escAttr(g.name)+'")" style="color:#ef4444;font-size:11px;padding:2px 8px">'+t('admin.delete')+'</button>'+
|
|
3633
|
-
'</div>'+
|
|
3634
|
-
'</div>'+
|
|
3635
|
-
'<div id="groupMembers_'+escAttr(g.id)+'" style="display:none;margin-top:6px;font-size:12px"></div>'+
|
|
3636
|
-
'</div>';
|
|
3637
|
-
}
|
|
3638
|
-
html+='</div>';
|
|
3912
|
+
|
|
3913
|
+
/* ─── Hub Admin Panel ─── */
|
|
3914
|
+
var adminDataCache={users:[],groups:[],tasks:[],skills:[],memories:[]};
|
|
3915
|
+
var ADMIN_PAGE_SIZE=20;
|
|
3916
|
+
var adminPage={users:0,tasks:0,skills:0,memories:0};
|
|
3917
|
+
|
|
3918
|
+
function adminPaginateHtml(total,page,refilterFn){
|
|
3919
|
+
var pages=Math.ceil(total/ADMIN_PAGE_SIZE);
|
|
3920
|
+
if(pages<=1) return '';
|
|
3921
|
+
var html='<div class="pagination" style="padding:16px 0">';
|
|
3922
|
+
html+='<button class="pg-btn'+(page===0?' disabled':'')+'" onclick="'+refilterFn+'Page(-1)">\\u2190</button>';
|
|
3923
|
+
var start=Math.max(0,page-2),end=Math.min(pages,page+3);
|
|
3924
|
+
if(start>0) html+='<button class="pg-btn" onclick="'+refilterFn+'Page(0)">1</button>'+(start>1?'<span class="pg-info">...</span>':'');
|
|
3925
|
+
for(var i=start;i<end;i++){
|
|
3926
|
+
html+='<button class="pg-btn'+(i===page?' active':'')+'" onclick="'+refilterFn+'Page('+i+')">'+(i+1)+'</button>';
|
|
3639
3927
|
}
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
var name=(document.getElementById('newGroupName')).value.trim();
|
|
3646
|
-
var desc=(document.getElementById('newGroupDesc')).value.trim();
|
|
3647
|
-
if(!name){toast(t('toast.groupNameRequired'),'error');return;}
|
|
3648
|
-
try{
|
|
3649
|
-
var r=await fetch('/api/sharing/groups',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:name,description:desc})});
|
|
3650
|
-
var d=await r.json();
|
|
3651
|
-
if(d.ok){toast(t('toast.groupCreated'),'success');hideCreateGroupForm();loadGroupManager();}else{toast(d.error||t('toast.createFail'),'error');}
|
|
3652
|
-
}catch(e){toast(t('toast.createFail')+': '+e.message,'error');}
|
|
3928
|
+
if(end<pages) html+=(end<pages-1?'<span class="pg-info">...</span>':'')+'<button class="pg-btn" onclick="'+refilterFn+'Page('+(pages-1)+')">'+pages+'</button>';
|
|
3929
|
+
html+='<button class="pg-btn'+(page>=pages-1?' disabled':'')+'" onclick="'+refilterFn+'Page('+(page+1)+')">\\u2192</button>';
|
|
3930
|
+
html+='<span class="pg-info">'+total+' '+t('pagination.total')+'</span>';
|
|
3931
|
+
html+='</div>';
|
|
3932
|
+
return html;
|
|
3653
3933
|
}
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
}catch(e){toast(t('toast.deleteFail')+': '+e.message,'error');}
|
|
3934
|
+
|
|
3935
|
+
var _adminPollTimer=null;
|
|
3936
|
+
function startAdminPoll(){
|
|
3937
|
+
if(_adminPollTimer) return;
|
|
3938
|
+
_adminPollTimer=setInterval(function(){pollAdminPending();},30000);
|
|
3939
|
+
pollAdminPending();
|
|
3661
3940
|
}
|
|
3662
|
-
async function
|
|
3663
|
-
var el=document.getElementById('groupMembers_'+groupId);
|
|
3664
|
-
if(!el) return;
|
|
3665
|
-
if(el.style.display!=='none'){el.style.display='none';return;}
|
|
3666
|
-
el.style.display='block';
|
|
3667
|
-
el.innerHTML=t('sharing.loading');
|
|
3941
|
+
async function pollAdminPending(){
|
|
3668
3942
|
try{
|
|
3669
|
-
var r=await fetch('/api/sharing/
|
|
3943
|
+
var r=await fetch('/api/sharing/pending-users');
|
|
3670
3944
|
var d=await r.json();
|
|
3671
|
-
var
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
}
|
|
3675
|
-
|
|
3676
|
-
var html='<div style="font-size:12px;margin-bottom:6px;color:var(--text-sec)">'+t('admin.membersCount').replace('{n}',members.length)+'</div>';
|
|
3677
|
-
if(members.length>0){
|
|
3678
|
-
html+='<div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px">';
|
|
3679
|
-
for(var i=0;i<members.length;i++){
|
|
3680
|
-
var m=members[i];
|
|
3681
|
-
html+='<span style="display:inline-flex;align-items:center;gap:4px;padding:3px 10px;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;font-size:12px">'+
|
|
3682
|
-
esc(m.username||m.userId)+
|
|
3683
|
-
' <button style="background:none;border:none;color:#ef4444;cursor:pointer;font-size:11px;padding:0 2px" onclick="removeGroupMember("'+escAttr(groupId)+'","'+escAttr(m.userId)+'")">×</button>'+
|
|
3684
|
-
'</span>';
|
|
3685
|
-
}
|
|
3686
|
-
html+='</div>';
|
|
3687
|
-
}else{
|
|
3688
|
-
html+='<div style="font-size:12px;color:var(--text-muted);margin-bottom:8px">'+t('admin.noMembersYet')+'</div>';
|
|
3689
|
-
}
|
|
3690
|
-
var memberIds=new Set(members.map(function(m){return m.userId;}));
|
|
3691
|
-
var available=groupManagerUsers.filter(function(u){return !memberIds.has(u.id);});
|
|
3692
|
-
if(available.length>0){
|
|
3693
|
-
html+='<div style="display:flex;gap:6px;align-items:center">'+
|
|
3694
|
-
'<select id="addMemberSelect_'+escAttr(groupId)+'" style="padding:4px 8px;border:1px solid var(--border);border-radius:6px;font-size:12px">';
|
|
3695
|
-
for(var j=0;j<available.length;j++){
|
|
3696
|
-
html+='<option value="'+escAttr(available[j].id)+'">'+esc(available[j].username)+'</option>';
|
|
3945
|
+
var count=Array.isArray(d.users)?d.users.length:0;
|
|
3946
|
+
var badge=document.getElementById('adminPendingBadge');
|
|
3947
|
+
if(badge){
|
|
3948
|
+
if(count>0){badge.textContent=count;badge.style.display='inline';}
|
|
3949
|
+
else{badge.style.display='none';}
|
|
3697
3950
|
}
|
|
3698
|
-
|
|
3699
|
-
'<button class="btn btn-sm" onclick="addGroupMember("'+escAttr(groupId)+'")">'+t('admin.add')+'</button>'+
|
|
3700
|
-
'</div>';
|
|
3701
|
-
}
|
|
3702
|
-
el.innerHTML=html;
|
|
3703
|
-
}
|
|
3704
|
-
async function addGroupMember(groupId){
|
|
3705
|
-
var sel=document.getElementById('addMemberSelect_'+groupId);
|
|
3706
|
-
if(!sel) return;
|
|
3707
|
-
var userId=sel.value;
|
|
3708
|
-
if(!userId) return;
|
|
3709
|
-
try{
|
|
3710
|
-
var r=await fetch('/api/sharing/groups/'+encodeURIComponent(groupId)+'/members',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:userId})});
|
|
3711
|
-
var d=await r.json();
|
|
3712
|
-
if(d.ok){toast(t('toast.memberAdded'),'success');reloadGroupMembers(groupId);}else{toast(d.error||t('toast.addFail'),'error');}
|
|
3713
|
-
}catch(e){toast(t('toast.addFail')+': '+e.message,'error');}
|
|
3714
|
-
}
|
|
3715
|
-
async function removeGroupMember(groupId,userId){
|
|
3716
|
-
if(!confirm(t('confirm.removeGroupMember'))) return;
|
|
3717
|
-
try{
|
|
3718
|
-
var r=await fetch('/api/sharing/groups/'+encodeURIComponent(groupId)+'/members',{method:'DELETE',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:userId})});
|
|
3719
|
-
var d=await r.json();
|
|
3720
|
-
if(d.ok){toast(t('toast.memberRemoved'),'success');reloadGroupMembers(groupId);}else{toast(d.error||t('toast.removeFail'),'error');}
|
|
3721
|
-
}catch(e){toast(t('toast.removeFail')+': '+e.message,'error');}
|
|
3722
|
-
}
|
|
3723
|
-
async function reloadGroupMembers(groupId){
|
|
3724
|
-
var el=document.getElementById('groupMembers_'+groupId);
|
|
3725
|
-
if(!el) return;
|
|
3726
|
-
el.style.display='block';
|
|
3727
|
-
el.innerHTML=t('sharing.loading');
|
|
3728
|
-
try{
|
|
3729
|
-
var r=await fetch('/api/sharing/groups/'+encodeURIComponent(groupId)+'/members');
|
|
3730
|
-
var d=await r.json();
|
|
3731
|
-
var members=Array.isArray(d.members)?d.members:[];
|
|
3732
|
-
renderGroupMembers(el,groupId,members);
|
|
3733
|
-
}catch(e){el.innerHTML=t('admin.groupsFailed')+esc(String(e));}
|
|
3951
|
+
}catch(e){}
|
|
3734
3952
|
}
|
|
3735
3953
|
|
|
3736
|
-
/* ─── Hub Admin Panel ─── */
|
|
3737
|
-
var adminDataCache={users:[],groups:[],tasks:[],skills:[],memories:[]};
|
|
3738
|
-
|
|
3739
3954
|
function switchAdminTab(tab,btn){
|
|
3740
3955
|
document.querySelectorAll('.admin-tabs .admin-tab').forEach(function(t){t.classList.remove('active');});
|
|
3741
3956
|
btn.classList.add('active');
|
|
@@ -3787,16 +4002,18 @@ async function loadAdminData(){
|
|
|
3787
4002
|
fetch('/api/admin/shared-memories').then(function(r){return r.json();})
|
|
3788
4003
|
]);
|
|
3789
4004
|
adminDataCache.users=Array.isArray(usersR.users)?usersR.users:[];
|
|
3790
|
-
adminDataCache.groups=[];
|
|
3791
4005
|
adminDataCache.tasks=Array.isArray(tasksR.tasks)?tasksR.tasks:[];
|
|
3792
4006
|
adminDataCache.skills=Array.isArray(skillsR.skills)?skillsR.skills:[];
|
|
3793
4007
|
adminDataCache.memories=Array.isArray(memoriesR.memories)?memoriesR.memories:[];
|
|
3794
4008
|
var pending=Array.isArray(pendingR.users)?pendingR.users:[];
|
|
4009
|
+
adminDataCache._pending=pending;
|
|
4010
|
+
var badge=document.getElementById('adminPendingBadge');
|
|
4011
|
+
if(badge){if(pending.length>0){badge.textContent=pending.length;badge.style.display='inline';}else{badge.style.display='none';}}
|
|
3795
4012
|
renderAdminStats(pending.length);
|
|
3796
4013
|
renderAdminUsers(adminDataCache.users, pending);
|
|
3797
|
-
|
|
4014
|
+
renderAdminTasks(adminDataCache.tasks);
|
|
3798
4015
|
renderAdminSkills(adminDataCache.skills);
|
|
3799
|
-
|
|
4016
|
+
renderAdminMemories(adminDataCache.memories);
|
|
3800
4017
|
}catch(e){
|
|
3801
4018
|
var statsEl=document.getElementById('adminStats');
|
|
3802
4019
|
if(statsEl) statsEl.innerHTML='<div class="admin-empty">'+t('admin.loadFailed')+esc(String(e))+'</div>';
|
|
@@ -3809,9 +4026,9 @@ function renderAdminStats(pendingCount){
|
|
|
3809
4026
|
el.innerHTML=
|
|
3810
4027
|
'<div class="admin-stat-box"><span class="as-icon">\u{1F465}</span><div class="val">'+adminDataCache.users.length+'</div><div class="lbl">'+t('admin.stat.activeUsers')+'</div></div>'+
|
|
3811
4028
|
'<div class="admin-stat-box"><span class="as-icon">\u{23F3}</span><div class="val">'+pendingCount+'</div><div class="lbl">'+t('admin.stat.pending')+'</div></div>'+
|
|
4029
|
+
'<div class="admin-stat-box"><span class="as-icon">\u{1F4AD}</span><div class="val">'+(adminDataCache.memories||[]).length+'</div><div class="lbl">'+t('admin.stat.sharedMemories')+'</div></div>'+
|
|
3812
4030
|
'<div class="admin-stat-box"><span class="as-icon">\u{1F4CB}</span><div class="val">'+adminDataCache.tasks.length+'</div><div class="lbl">'+t('admin.stat.sharedTasks')+'</div></div>'+
|
|
3813
|
-
'<div class="admin-stat-box"><span class="as-icon">\u{1F9E0}</span><div class="val">'+adminDataCache.skills.length+'</div><div class="lbl">'+t('admin.stat.sharedSkills')+'</div></div>'
|
|
3814
|
-
'<div class="admin-stat-box"><span class="as-icon">\u{1F4AD}</span><div class="val">'+(adminDataCache.memories||[]).length+'</div><div class="lbl">'+t('admin.stat.sharedMemories')+'</div></div>';
|
|
4031
|
+
'<div class="admin-stat-box"><span class="as-icon">\u{1F9E0}</span><div class="val">'+adminDataCache.skills.length+'</div><div class="lbl">'+t('admin.stat.sharedSkills')+'</div></div>';
|
|
3815
4032
|
var tc=document.getElementById('adminTabCountUsers');if(tc)tc.textContent=adminDataCache.users.length+pendingCount;
|
|
3816
4033
|
tc=document.getElementById('adminTabCountMemories');if(tc)tc.textContent=(adminDataCache.memories||[]).length;
|
|
3817
4034
|
tc=document.getElementById('adminTabCountTasks');if(tc)tc.textContent=adminDataCache.tasks.length;
|
|
@@ -3839,15 +4056,61 @@ function renderAdminUsers(users,pending){
|
|
|
3839
4056
|
if(users.length===0){
|
|
3840
4057
|
html+='<div class="admin-empty"><span class="ae-icon">\u{1F465}</span>'+t('admin.noActiveUsers')+'</div>';
|
|
3841
4058
|
}else{
|
|
3842
|
-
|
|
4059
|
+
var totalUsers=users.length;
|
|
4060
|
+
var usersPages=Math.ceil(totalUsers/ADMIN_PAGE_SIZE);
|
|
4061
|
+
if(adminPage.users>=usersPages) adminPage.users=Math.max(0,usersPages-1);
|
|
4062
|
+
var usersStart=adminPage.users*ADMIN_PAGE_SIZE;
|
|
4063
|
+
var usersEnd=Math.min(usersStart+ADMIN_PAGE_SIZE,totalUsers);
|
|
4064
|
+
var adminCount=users.filter(function(x){return x.role==='admin';}).length;
|
|
4065
|
+
for(var i=usersStart;i<usersEnd;i++){
|
|
3843
4066
|
var u=users[i];
|
|
3844
|
-
|
|
4067
|
+
var uid=escAttr(u.id);
|
|
4068
|
+
var uname=escAttr(u.username||'');
|
|
4069
|
+
|
|
4070
|
+
var titleDisplay='<span class="admin-card-title" id="au_name_'+uid+'">'+esc(u.username||u.id)+
|
|
4071
|
+
' <button onclick="adminStartEditName(this,"'+uid+'","'+uname+'")" style="background:none;border:none;cursor:pointer;color:var(--text-muted);padding:2px;font-size:13px;vertical-align:middle;opacity:.5;transition:opacity .15s" onmouseenter="this.style.opacity=1" onmouseleave="this.style.opacity=.5" title="'+t('admin.editName')+'">\u270E</button></span>';
|
|
4072
|
+
|
|
4073
|
+
var editRow='<div id="au_edit_'+uid+'" style="display:none;align-items:center;gap:6px">'+
|
|
4074
|
+
'<input id="au_input_'+uid+'" type="text" value="'+uname+'" style="flex:1;padding:5px 10px;border:1px solid var(--pri);border-radius:8px;font-size:13px;font-weight:600;background:var(--bg);color:var(--text);outline:none;min-width:0" onkeydown="if(event.key==="Enter")adminSaveEditName("'+uid+'");if(event.key==="Escape")adminCancelEditName("'+uid+'")">'+
|
|
4075
|
+
'<button onclick="adminSaveEditName("'+uid+'")" class="btn btn-sm btn-primary" style="padding:4px 12px;font-size:11px;white-space:nowrap">\u2713</button>'+
|
|
4076
|
+
'<button onclick="adminCancelEditName("'+uid+'")" class="btn btn-sm btn-ghost" style="padding:4px 8px;font-size:11px;color:var(--text-muted)">\u2717</button></div>';
|
|
4077
|
+
|
|
4078
|
+
var mc=u.memoryCount||0, tc=u.taskCount||0, sc=u.skillCount||0;
|
|
4079
|
+
var contribHtml='<div class="au-contrib">'+
|
|
4080
|
+
'<span class="au-contrib-item"><span class="au-contrib-num" style="color:var(--pri)">'+mc+'</span> '+t('admin.contrib.memories')+'</span>'+
|
|
4081
|
+
'<span class="au-contrib-item"><span class="au-contrib-num" style="color:var(--green)">'+tc+'</span> '+t('admin.contrib.tasks')+'</span>'+
|
|
4082
|
+
'<span class="au-contrib-item"><span class="au-contrib-num" style="color:var(--amber)">'+sc+'</span> '+t('admin.contrib.skills')+'</span>'+
|
|
4083
|
+
'</div>';
|
|
4084
|
+
|
|
4085
|
+
var infoRows=[];
|
|
4086
|
+
if(u.deviceName) infoRows.push('<span class="au-info-item">\u{1F4BB} '+t('admin.device')+esc(u.deviceName)+'</span>');
|
|
4087
|
+
if(u.lastIp) infoRows.push('<span class="au-info-item">\u{1F310} '+t('admin.ip')+esc(u.lastIp)+'</span>');
|
|
4088
|
+
if(u.createdAt) infoRows.push('<span class="au-info-item">\u{1F4C5} '+t('admin.joined')+formatDateTimeSeconds(u.createdAt)+'</span>');
|
|
4089
|
+
if(u.approvedAt) infoRows.push('<span class="au-info-item">\u2705 '+t('admin.approved')+formatDateTimeSeconds(u.approvedAt)+'</span>');
|
|
4090
|
+
infoRows.push('<span class="au-info-item">\u{1F552} '+t('admin.lastActive')+(u.lastActiveAt?formatDateTimeSeconds(u.lastActiveAt):t('admin.neverActive'))+'</span>');
|
|
4091
|
+
var infoHtml='<div class="au-info">'+infoRows.join('')+'</div>';
|
|
4092
|
+
|
|
4093
|
+
var actions='';
|
|
4094
|
+
if(u.role!=='admin'){
|
|
4095
|
+
actions+='<button class="btn btn-sm btn-ghost" onclick="adminToggleRole("'+uid+'","admin")" style="color:var(--accent)">'+t('admin.promoteAdmin')+'</button>';
|
|
4096
|
+
actions+='<button class="btn btn-sm btn-ghost" onclick="adminRemoveUser("'+uid+'","'+uname+'")" style="color:var(--rose)">'+t('admin.remove')+'</button>';
|
|
4097
|
+
}else if(adminCount>1){
|
|
4098
|
+
actions+='<button class="btn btn-sm btn-ghost" onclick="adminToggleRole("'+uid+'","member")">'+t('admin.demoteMember')+'</button>';
|
|
4099
|
+
actions+='<button class="btn btn-sm btn-ghost" onclick="adminRemoveUser("'+uid+'","'+uname+'")" style="color:var(--rose)">'+t('admin.remove')+'</button>';
|
|
4100
|
+
}else{
|
|
4101
|
+
actions+='<span style="font-size:11px;color:var(--text-muted);padding:4px 0">'+t('admin.lastAdminHint')+'</span>';
|
|
4102
|
+
}
|
|
4103
|
+
html+='<div class="admin-card au-card"><div class="admin-card-header"><div style="flex:1;min-width:0">'+titleDisplay+editRow+'</div>'+
|
|
3845
4104
|
'<span class="admin-badge '+(u.role==='admin'?'admin':'member')+'">'+esc(u.role||'member')+'</span></div>'+
|
|
3846
|
-
|
|
4105
|
+
contribHtml+infoHtml+
|
|
4106
|
+
(actions?'<div class="admin-card-actions" style="border-top:1px dashed var(--border);padding-top:10px;margin-top:4px">'+actions+'</div>':'')+
|
|
4107
|
+
'</div>';
|
|
3847
4108
|
}
|
|
4109
|
+
html+=adminPaginateHtml(totalUsers,adminPage.users,'adminUsers');
|
|
3848
4110
|
}
|
|
3849
4111
|
el.innerHTML=html;
|
|
3850
4112
|
}
|
|
4113
|
+
function adminUsersPage(p){var maxP=Math.max(0,Math.ceil(adminDataCache.users.length/ADMIN_PAGE_SIZE)-1);if(p<0){adminPage.users=Math.max(0,adminPage.users-1);}else{adminPage.users=Math.min(p,maxP);}renderAdminUsers(adminDataCache.users,adminDataCache._pending||[]);}
|
|
3851
4114
|
|
|
3852
4115
|
async function adminApproveUser(userId,username){
|
|
3853
4116
|
try{
|
|
@@ -3857,140 +4120,151 @@ async function adminApproveUser(userId,username){
|
|
|
3857
4120
|
}catch(e){toast(t('toast.approveFail')+': '+e.message,'error');}
|
|
3858
4121
|
}
|
|
3859
4122
|
async function adminRejectUser(userId){
|
|
3860
|
-
if(!
|
|
4123
|
+
if(!(await confirmModal(t('confirm.rejectUser'),{danger:true}))) return;
|
|
3861
4124
|
try{
|
|
3862
4125
|
var r=await fetch('/api/sharing/reject-user',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:userId})});
|
|
3863
4126
|
var d=await r.json();
|
|
3864
4127
|
if(d.ok){toast(t('toast.userRejected'),'success');loadAdminData();}else{toast(d.error||t('toast.rejectFail'),'error');}
|
|
3865
4128
|
}catch(e){toast(t('toast.rejectFail')+': '+e.message,'error');}
|
|
3866
4129
|
}
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
if(!el) return;
|
|
3871
|
-
var html='<div style="margin-bottom:12px;display:flex;justify-content:space-between;align-items:center">'+
|
|
3872
|
-
'<h3 style="font-size:14px;font-weight:600;color:var(--text)">'+t('admin.groups')+' ('+groups.length+')</h3>'+
|
|
3873
|
-
'<button class="btn btn-sm btn-primary" onclick="showAdminCreateGroup()">'+t('admin.newGroup')+'</button></div>';
|
|
3874
|
-
html+='<div id="adminCreateGroupForm" style="display:none;margin-bottom:14px;padding:14px;background:var(--bg);border:1px solid var(--border);border-radius:10px">'+
|
|
3875
|
-
'<div style="display:flex;flex-direction:column;gap:8px">'+
|
|
3876
|
-
'<input id="adminNewGroupName" type="text" placeholder="'+t('admin.groupName')+'" style="padding:8px 12px;border:1px solid var(--border);border-radius:8px;font-size:13px;background:var(--bg-card);color:var(--text)">'+
|
|
3877
|
-
'<input id="adminNewGroupDesc" type="text" placeholder="'+t('admin.groupDesc')+'" style="padding:8px 12px;border:1px solid var(--border);border-radius:8px;font-size:13px;background:var(--bg-card);color:var(--text)">'+
|
|
3878
|
-
'<div style="display:flex;gap:8px"><button class="btn btn-sm btn-primary" onclick="adminCreateGroup()">'+t('admin.create')+'</button>'+
|
|
3879
|
-
'<button class="btn btn-sm btn-ghost" onclick="hideAdminCreateGroup()">'+t('admin.cancel')+'</button></div></div></div>';
|
|
3880
|
-
if(groups.length===0){
|
|
3881
|
-
html+='<div class="admin-empty"><span class="ae-icon">\u{1F4C2}</span>'+t('admin.noGroups')+'</div>';
|
|
3882
|
-
}else{
|
|
3883
|
-
for(var i=0;i<groups.length;i++){
|
|
3884
|
-
var g=groups[i];
|
|
3885
|
-
html+='<div class="admin-card"><div class="admin-card-header"><div class="admin-card-title">'+esc(g.name)+'</div>'+
|
|
3886
|
-
'<button class="btn btn-sm btn-ghost" onclick="adminDeleteGroup("'+escAttr(g.id)+'","'+escAttr(g.name)+'")" style="color:var(--rose);font-size:11px">'+t('admin.delete')+'</button></div>'+
|
|
3887
|
-
(g.description?'<div class="admin-card-meta">'+esc(g.description)+'</div>':'')+
|
|
3888
|
-
'<div id="adminGroupMembers_'+escAttr(g.id)+'" style="margin-top:10px"></div>'+
|
|
3889
|
-
'</div>';
|
|
3890
|
-
}
|
|
3891
|
-
}
|
|
3892
|
-
el.innerHTML=html;
|
|
3893
|
-
for(var i=0;i<groups.length;i++){adminLoadGroupMembers(groups[i].id);}
|
|
3894
|
-
}
|
|
3895
|
-
function showAdminCreateGroup(){var f=document.getElementById('adminCreateGroupForm');if(f)f.style.display='block';}
|
|
3896
|
-
function hideAdminCreateGroup(){var f=document.getElementById('adminCreateGroupForm');if(f)f.style.display='none';}
|
|
3897
|
-
async function adminCreateGroup(){
|
|
3898
|
-
var name=(document.getElementById('adminNewGroupName')).value.trim();
|
|
3899
|
-
var desc=(document.getElementById('adminNewGroupDesc')).value.trim();
|
|
3900
|
-
if(!name){toast(t('toast.groupNameRequired'),'error');return;}
|
|
4130
|
+
async function adminToggleRole(userId,newRole){
|
|
4131
|
+
var msg=newRole==='admin'?t('confirm.promoteAdmin'):t('confirm.demoteMember');
|
|
4132
|
+
if(!(await confirmModal(msg))) return;
|
|
3901
4133
|
try{
|
|
3902
|
-
var r=await fetch('/api/sharing/
|
|
4134
|
+
var r=await fetch('/api/sharing/change-role',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:userId,role:newRole})});
|
|
3903
4135
|
var d=await r.json();
|
|
3904
|
-
if(d.ok
|
|
3905
|
-
}catch(e){toast(t('toast.
|
|
3906
|
-
}
|
|
3907
|
-
|
|
3908
|
-
|
|
4136
|
+
if(d.ok){toast(t('toast.roleChanged'),'success');loadAdminData();}else{toast(d.error||t('toast.roleChangeFail'),'error');}
|
|
4137
|
+
}catch(e){toast(t('toast.roleChangeFail')+': '+e.message,'error');}
|
|
4138
|
+
}
|
|
4139
|
+
function adminStartEditName(btn,userId,currentName){
|
|
4140
|
+
var nameEl=document.getElementById('au_name_'+userId);
|
|
4141
|
+
var editEl=document.getElementById('au_edit_'+userId);
|
|
4142
|
+
var inputEl=document.getElementById('au_input_'+userId);
|
|
4143
|
+
if(!nameEl||!editEl||!inputEl) return;
|
|
4144
|
+
nameEl.style.display='none';
|
|
4145
|
+
editEl.style.display='flex';
|
|
4146
|
+
inputEl.value=currentName;
|
|
4147
|
+
inputEl.focus();
|
|
4148
|
+
inputEl.select();
|
|
4149
|
+
}
|
|
4150
|
+
function adminCancelEditName(userId){
|
|
4151
|
+
var nameEl=document.getElementById('au_name_'+userId);
|
|
4152
|
+
var editEl=document.getElementById('au_edit_'+userId);
|
|
4153
|
+
if(nameEl) nameEl.style.display='';
|
|
4154
|
+
if(editEl) editEl.style.display='none';
|
|
4155
|
+
}
|
|
4156
|
+
async function adminSaveEditName(userId){
|
|
4157
|
+
var inputEl=document.getElementById('au_input_'+userId);
|
|
4158
|
+
if(!inputEl) return;
|
|
4159
|
+
var newName=inputEl.value.trim();
|
|
4160
|
+
if(!newName||newName.length<2||newName.length>32){toast(t('toast.invalidUsername'),'warn');return;}
|
|
4161
|
+
inputEl.disabled=true;
|
|
3909
4162
|
try{
|
|
3910
|
-
var r=await fetch('/api/sharing/
|
|
4163
|
+
var r=await fetch('/api/sharing/rename-user',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:userId,username:newName})});
|
|
3911
4164
|
var d=await r.json();
|
|
3912
|
-
if(d.ok){toast(t('toast.
|
|
3913
|
-
}catch(e){toast(t('toast.
|
|
4165
|
+
if(d.ok){toast(t('toast.usernameChanged'),'success');loadAdminData();}else{inputEl.disabled=false;toast(d.error||t('toast.renameFail'),'error');}
|
|
4166
|
+
}catch(e){inputEl.disabled=false;toast(t('toast.renameFail')+': '+e.message,'error');}
|
|
3914
4167
|
}
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
if(!
|
|
3918
|
-
|
|
3919
|
-
try{
|
|
3920
|
-
var r=await fetch('/api/sharing/groups/'+encodeURIComponent(groupId)+'/members');
|
|
3921
|
-
var d=await r.json();
|
|
3922
|
-
var members=Array.isArray(d.members)?d.members:[];
|
|
3923
|
-
var html='<div style="font-size:12px;margin-bottom:6px;color:var(--text-sec)">'+t('admin.membersCount').replace('{n}',members.length)+'</div>';
|
|
3924
|
-
if(members.length>0){
|
|
3925
|
-
html+='<div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px">';
|
|
3926
|
-
for(var i=0;i<members.length;i++){
|
|
3927
|
-
var m=members[i];
|
|
3928
|
-
html+='<span style="display:inline-flex;align-items:center;gap:4px;padding:3px 10px;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;font-size:12px">'+
|
|
3929
|
-
esc(m.username||m.userId)+
|
|
3930
|
-
' <button style="background:none;border:none;color:#ef4444;cursor:pointer;font-size:11px;padding:0 2px" onclick="adminRemoveGroupMember("'+escAttr(groupId)+'","'+escAttr(m.userId)+'")">×</button></span>';
|
|
3931
|
-
}
|
|
3932
|
-
html+='</div>';
|
|
3933
|
-
}else{
|
|
3934
|
-
html+='<div style="font-size:12px;color:var(--text-muted);margin-bottom:8px">'+t('admin.noMembers')+'</div>';
|
|
3935
|
-
}
|
|
3936
|
-
var memberIds=new Set(members.map(function(m){return m.userId;}));
|
|
3937
|
-
var available=adminDataCache.users.filter(function(u){return !memberIds.has(u.id);});
|
|
3938
|
-
if(available.length>0){
|
|
3939
|
-
html+='<div style="display:flex;gap:6px;align-items:center">'+
|
|
3940
|
-
'<select id="adminAddMember_'+escAttr(groupId)+'" style="padding:4px 8px;border:1px solid var(--border);border-radius:6px;font-size:12px;background:var(--bg-card);color:var(--text)">';
|
|
3941
|
-
for(var j=0;j<available.length;j++){
|
|
3942
|
-
html+='<option value="'+escAttr(available[j].id)+'">'+esc(available[j].username)+'</option>';
|
|
3943
|
-
}
|
|
3944
|
-
html+='</select><button class="btn btn-sm" onclick="adminAddGroupMember("'+escAttr(groupId)+'")">'+t('admin.add')+'</button></div>';
|
|
3945
|
-
}
|
|
3946
|
-
el.innerHTML=html;
|
|
3947
|
-
}catch(e){el.innerHTML=t('admin.groupsFailed')+esc(String(e));}
|
|
3948
|
-
}
|
|
3949
|
-
async function adminAddGroupMember(groupId){
|
|
3950
|
-
var sel=document.getElementById('adminAddMember_'+groupId);
|
|
3951
|
-
if(!sel) return;
|
|
3952
|
-
try{
|
|
3953
|
-
var r=await fetch('/api/sharing/groups/'+encodeURIComponent(groupId)+'/members',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:sel.value})});
|
|
3954
|
-
var d=await r.json();
|
|
3955
|
-
if(d.ok){toast(t('toast.memberAdded'),'success');adminLoadGroupMembers(groupId);}else{toast(d.error||t('toast.addFail'),'error');}
|
|
3956
|
-
}catch(e){toast(t('toast.addFail')+': '+e.message,'error');}
|
|
3957
|
-
}
|
|
3958
|
-
async function adminRemoveGroupMember(groupId,userId){
|
|
3959
|
-
if(!confirm(t('confirm.removeMember'))) return;
|
|
4168
|
+
|
|
4169
|
+
async function adminRemoveUser(userId,username){
|
|
4170
|
+
if(!(await confirmModal(t('confirm.removeUser'),{danger:true}))) return;
|
|
4171
|
+
var clean=await confirmModal(t('confirm.cleanResources'));
|
|
3960
4172
|
try{
|
|
3961
|
-
var r=await fetch('/api/sharing/
|
|
4173
|
+
var r=await fetch('/api/sharing/remove-user',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:userId,cleanResources:clean})});
|
|
3962
4174
|
var d=await r.json();
|
|
3963
|
-
if(d.ok){toast(t('toast.
|
|
4175
|
+
if(d.ok){toast(t('toast.userRemoved'),'success');loadAdminData();}else{toast(d.error||t('toast.removeFail'),'error');}
|
|
3964
4176
|
}catch(e){toast(t('toast.removeFail')+': '+e.message,'error');}
|
|
3965
4177
|
}
|
|
3966
4178
|
|
|
3967
|
-
|
|
3968
|
-
|
|
4179
|
+
|
|
4180
|
+
function adminToolbarHtml(items,keyFn,filterId,searchId,onchangeFn,opts){
|
|
4181
|
+
opts=opts||{};
|
|
4182
|
+
var owners={};
|
|
4183
|
+
for(var i=0;i<items.length;i++){var n=keyFn(items[i]);if(n)owners[n]=1;}
|
|
4184
|
+
var names=Object.keys(owners).sort();
|
|
4185
|
+
var html='';
|
|
4186
|
+
if(names.length>=1){
|
|
4187
|
+
html+='<select id="'+filterId+'" onchange="'+onchangeFn+'()" class="admin-toolbar select"><option value="">'+t('admin.filter.owner')+'</option>';
|
|
4188
|
+
for(var j=0;j<names.length;j++) html+='<option value="'+escAttr(names[j])+'">'+esc(names[j])+'</option>';
|
|
4189
|
+
html+='</select>';
|
|
4190
|
+
}
|
|
4191
|
+
if(opts.sortId){
|
|
4192
|
+
html+='<select id="'+opts.sortId+'" onchange="'+onchangeFn+'()" class="admin-toolbar select">'+
|
|
4193
|
+
'<option value="newest">'+t('admin.sort.newest')+'</option>'+
|
|
4194
|
+
'<option value="oldest">'+t('admin.sort.oldest')+'</option></select>';
|
|
4195
|
+
}
|
|
4196
|
+
if(opts.extraFilters){
|
|
4197
|
+
for(var ef=0;ef<opts.extraFilters.length;ef++){
|
|
4198
|
+
var f=opts.extraFilters[ef];
|
|
4199
|
+
var vals={};
|
|
4200
|
+
for(var fi=0;fi<items.length;fi++){var fv=f.keyFn(items[fi]);if(fv)vals[fv]=1;}
|
|
4201
|
+
var fkeys=Object.keys(vals).sort();
|
|
4202
|
+
if(fkeys.length>=1){
|
|
4203
|
+
html+='<select id="'+f.id+'" onchange="'+onchangeFn+'()" class="admin-toolbar select"><option value="">'+esc(f.label)+'</option>';
|
|
4204
|
+
for(var fk=0;fk<fkeys.length;fk++) html+='<option value="'+escAttr(fkeys[fk])+'">'+esc(fkeys[fk])+'</option>';
|
|
4205
|
+
html+='</select>';
|
|
4206
|
+
}
|
|
4207
|
+
}
|
|
4208
|
+
}
|
|
4209
|
+
return html;
|
|
4210
|
+
}
|
|
4211
|
+
function renderAdminTasks(tasks){
|
|
4212
|
+
var el=document.getElementById('adminTasksPanel');
|
|
3969
4213
|
if(!el) return;
|
|
3970
4214
|
adminTasksCache=tasks;
|
|
3971
|
-
var
|
|
3972
|
-
|
|
4215
|
+
var filterVal=document.getElementById('adminTaskFilter')?document.getElementById('adminTaskFilter').value:'';
|
|
4216
|
+
var sortVal=document.getElementById('adminTaskSort')?document.getElementById('adminTaskSort').value:'newest';
|
|
4217
|
+
var statusVal=document.getElementById('adminTaskStatusFilter')?document.getElementById('adminTaskStatusFilter').value:'';
|
|
4218
|
+
var filtered=tasks;
|
|
4219
|
+
if(filterVal) filtered=filtered.filter(function(tk){return (tk.ownerName||tk.sourceUserId||'')===filterVal;});
|
|
4220
|
+
if(statusVal) filtered=filtered.filter(function(tk){return (tk.status||'')===statusVal;});
|
|
4221
|
+
filtered=filtered.slice().sort(function(a,b){var ta=a.updatedAt||a.createdAt||0,tb=b.updatedAt||b.createdAt||0;return sortVal==='oldest'?ta-tb:tb-ta;});
|
|
4222
|
+
var html='<div class="admin-toolbar"><h3>'+t('admin.sharedTasks')+' ('+filtered.length+'/'+tasks.length+')</h3>'+
|
|
4223
|
+
adminToolbarHtml(tasks,function(tk){return tk.ownerName||tk.sourceUserId||'';},'adminTaskFilter','adminTaskSearch','refilterAdminTasks',{sortId:'adminTaskSort',extraFilters:[
|
|
4224
|
+
{id:'adminTaskStatusFilter',label:t('admin.filter.status'),keyFn:function(tk){return tk.status||''}}
|
|
4225
|
+
]})+'</div>';
|
|
4226
|
+
if(filtered.length===0){
|
|
3973
4227
|
html+='<div class="admin-empty"><span class="ae-icon">\u{1F4CB}</span>'+t('admin.noSharedTasks')+'</div>';
|
|
3974
4228
|
}else{
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
4229
|
+
var tPages=Math.ceil(filtered.length/ADMIN_PAGE_SIZE);
|
|
4230
|
+
if(adminPage.tasks>=tPages) adminPage.tasks=Math.max(0,tPages-1);
|
|
4231
|
+
var tStart=adminPage.tasks*ADMIN_PAGE_SIZE,tEnd=Math.min(tStart+ADMIN_PAGE_SIZE,filtered.length);
|
|
4232
|
+
for(var i=tStart;i<tEnd;i++){
|
|
4233
|
+
var tk=filtered[i];
|
|
4234
|
+
var cardId='adminTaskCard_'+i;
|
|
4235
|
+
var timeRange='';
|
|
4236
|
+
if(tk.startedAt) timeRange+=formatTime(tk.startedAt);
|
|
4237
|
+
if(tk.endedAt) timeRange+=' \u2192 '+formatTime(tk.endedAt);
|
|
4238
|
+
html+='<div class="admin-card admin-card-clickable" id="'+cardId+'" onclick="toggleAdminTaskCard("'+cardId+'",'+i+')">'+
|
|
4239
|
+
'<div class="admin-card-header"><div class="admin-card-title">'+esc(tk.title||tk.id)+'</div></div>'+
|
|
4240
|
+
'<div class="admin-card-tags">'+
|
|
4241
|
+
'<div class="admin-card-tags-left">'+
|
|
4242
|
+
'<span class="admin-card-tag tag-owner">\u{1F464} '+esc(tk.ownerName||tk.sourceUserId||'unknown')+'</span>'+
|
|
4243
|
+
(tk.status?'<span class="admin-card-tag tag-status">'+esc(tk.status)+'</span>':'')+
|
|
4244
|
+
(tk.chunkCount!=null?'<span class="admin-card-tag tag-kind">\u{1F4DD} '+tk.chunkCount+' '+t('admin.chunks')+'</span>':'')+
|
|
3978
4245
|
'</div>'+
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
4246
|
+
'<span class="admin-card-actions" onclick="event.stopPropagation()">'+
|
|
4247
|
+
'<button class="btn btn-sm btn-ghost" onclick="adminDeleteTask("'+escAttr(tk.id)+'","'+escAttr(tk.title||tk.id)+'")" style="color:var(--rose)">'+t('admin.remove')+'</button>'+
|
|
4248
|
+
'<button class="btn btn-sm btn-ghost admin-card-expand-btn" onclick="toggleAdminTaskCard("'+cardId+'",'+i+')">'+t('admin.expand')+'</button>'+
|
|
4249
|
+
'<span class="admin-card-time">'+(timeRange||formatDateTimeSeconds(tk.updatedAt||tk.createdAt))+'</span>'+
|
|
4250
|
+
'</span>'+
|
|
3983
4251
|
'</div>'+
|
|
3984
|
-
'<div class="admin-card-
|
|
3985
|
-
|
|
3986
|
-
|
|
4252
|
+
(tk.summary?'<div class="admin-card-preview">'+esc(tk.summary.slice(0,120))+'</div>':'')+
|
|
4253
|
+
'<div class="admin-card-detail" id="'+cardId+'_detail" onclick="event.stopPropagation()"></div>'+
|
|
4254
|
+
'</div>';
|
|
3987
4255
|
}
|
|
4256
|
+
html+=adminPaginateHtml(filtered.length,adminPage.tasks,'adminTasks');
|
|
3988
4257
|
}
|
|
3989
4258
|
el.innerHTML=html;
|
|
4259
|
+
if(filterVal){var sel=document.getElementById('adminTaskFilter');if(sel)sel.value=filterVal;}
|
|
4260
|
+
if(sortVal!=='newest'){var so=document.getElementById('adminTaskSort');if(so)so.value=sortVal;}
|
|
4261
|
+
if(statusVal){var sf=document.getElementById('adminTaskStatusFilter');if(sf)sf.value=statusVal;}
|
|
3990
4262
|
}
|
|
4263
|
+
function refilterAdminTasks(){adminPage.tasks=0;renderAdminTasks(adminTasksCache||[]);}
|
|
4264
|
+
function adminTasksPage(p){if(p<0){adminPage.tasks=Math.max(0,adminPage.tasks-1);}else{adminPage.tasks=p;}renderAdminTasks(adminTasksCache||[]);}
|
|
3991
4265
|
|
|
3992
4266
|
async function adminDeleteTask(taskId,taskTitle){
|
|
3993
|
-
if(!
|
|
4267
|
+
if(!(await confirmModal(t('confirm.removeTask').replace('{name}',taskTitle),{danger:true}))) return;
|
|
3994
4268
|
try{
|
|
3995
4269
|
var r=await fetch('/api/admin/shared-tasks/'+encodeURIComponent(taskId),{method:'DELETE'});
|
|
3996
4270
|
var d=await r.json();
|
|
@@ -4002,30 +4276,59 @@ function renderAdminSkills(skills){
|
|
|
4002
4276
|
var el=document.getElementById('adminSkillsPanel');
|
|
4003
4277
|
if(!el) return;
|
|
4004
4278
|
adminSkillsCache=skills;
|
|
4005
|
-
var
|
|
4006
|
-
|
|
4279
|
+
var filterVal=document.getElementById('adminSkillFilter')?document.getElementById('adminSkillFilter').value:'';
|
|
4280
|
+
var sortVal=document.getElementById('adminSkillSort')?document.getElementById('adminSkillSort').value:'newest';
|
|
4281
|
+
var statusVal=document.getElementById('adminSkillStatusFilter')?document.getElementById('adminSkillStatusFilter').value:'';
|
|
4282
|
+
var filtered=skills;
|
|
4283
|
+
if(filterVal) filtered=filtered.filter(function(s){return (s.ownerName||s.sourceUserId||'')===filterVal;});
|
|
4284
|
+
if(statusVal) filtered=filtered.filter(function(s){return (s.status||'')===statusVal;});
|
|
4285
|
+
filtered=filtered.slice().sort(function(a,b){var ta=a.updatedAt||a.createdAt||0,tb=b.updatedAt||b.createdAt||0;return sortVal==='oldest'?ta-tb:tb-ta;});
|
|
4286
|
+
var html='<div class="admin-toolbar"><h3>'+t('admin.sharedSkills')+' ('+filtered.length+'/'+skills.length+')</h3>'+
|
|
4287
|
+
adminToolbarHtml(skills,function(s){return s.ownerName||s.sourceUserId||'';},'adminSkillFilter','adminSkillSearch','refilterAdminSkills',{sortId:'adminSkillSort',extraFilters:[
|
|
4288
|
+
{id:'adminSkillStatusFilter',label:t('admin.filter.status'),keyFn:function(s){return s.status||''}}
|
|
4289
|
+
]})+'</div>';
|
|
4290
|
+
if(filtered.length===0){
|
|
4007
4291
|
html+='<div class="admin-empty"><span class="ae-icon">\u{1F9E0}</span>'+t('admin.noSharedSkills')+'</div>';
|
|
4008
4292
|
}else{
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4293
|
+
var sPages=Math.ceil(filtered.length/ADMIN_PAGE_SIZE);
|
|
4294
|
+
if(adminPage.skills>=sPages) adminPage.skills=Math.max(0,sPages-1);
|
|
4295
|
+
var sStart=adminPage.skills*ADMIN_PAGE_SIZE,sEnd=Math.min(sStart+ADMIN_PAGE_SIZE,filtered.length);
|
|
4296
|
+
for(var i=sStart;i<sEnd;i++){
|
|
4297
|
+
var s=filtered[i];
|
|
4298
|
+
var cardId='adminSkillCard_'+i;
|
|
4299
|
+
var qs=s.qualityScore;
|
|
4300
|
+
html+='<div class="admin-card admin-card-clickable" id="'+cardId+'" onclick="toggleAdminSkillCard("'+cardId+'",'+i+')">'+
|
|
4301
|
+
'<div class="admin-card-header"><div class="admin-card-title">'+esc(s.name||s.id)+'</div></div>'+
|
|
4302
|
+
'<div class="admin-card-tags">'+
|
|
4303
|
+
'<div class="admin-card-tags-left">'+
|
|
4304
|
+
'<span class="admin-card-tag tag-owner">\u{1F464} '+esc(s.ownerName||s.sourceUserId||'unknown')+'</span>'+
|
|
4305
|
+
(s.status?'<span class="admin-card-tag tag-status">'+esc(s.status)+'</span>':'')+
|
|
4306
|
+
(s.version!=null?'<span class="admin-card-tag tag-version">v'+s.version+'</span>':'')+
|
|
4307
|
+
(qs!=null?'<span class="admin-card-tag tag-kind">\u2605 '+Number(qs).toFixed(1)+'</span>':'')+
|
|
4012
4308
|
'</div>'+
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4309
|
+
'<span class="admin-card-actions" onclick="event.stopPropagation()">'+
|
|
4310
|
+
'<button class="btn btn-sm btn-ghost" onclick="adminDeleteSkill("'+escAttr(s.id)+'","'+escAttr(s.name||s.id)+'")" style="color:var(--rose)">'+t('admin.remove')+'</button>'+
|
|
4311
|
+
'<button class="btn btn-sm btn-ghost admin-card-expand-btn" onclick="toggleAdminSkillCard("'+cardId+'",'+i+')">'+t('admin.expand')+'</button>'+
|
|
4312
|
+
(s.sourceSkillId?'<button class="btn btn-sm btn-ghost" onclick="window.open("/api/skill/'+encodeURIComponent(s.sourceSkillId)+'/download","_blank")" style="color:var(--pri)">\u2B07</button>':'')+
|
|
4313
|
+
'<span class="admin-card-time">'+formatDateTimeSeconds(s.updatedAt||s.createdAt)+'</span>'+
|
|
4314
|
+
'</span>'+
|
|
4018
4315
|
'</div>'+
|
|
4019
|
-
'<div class="admin-card-
|
|
4020
|
-
|
|
4021
|
-
|
|
4316
|
+
(s.description?'<div class="admin-card-preview">'+esc(s.description.slice(0,150))+'</div>':'')+
|
|
4317
|
+
'<div class="admin-card-detail" id="'+cardId+'_detail" onclick="event.stopPropagation()"></div>'+
|
|
4318
|
+
'</div>';
|
|
4022
4319
|
}
|
|
4320
|
+
html+=adminPaginateHtml(filtered.length,adminPage.skills,'adminSkills');
|
|
4023
4321
|
}
|
|
4024
4322
|
el.innerHTML=html;
|
|
4323
|
+
if(filterVal){var sel=document.getElementById('adminSkillFilter');if(sel)sel.value=filterVal;}
|
|
4324
|
+
if(sortVal!=='newest'){var so=document.getElementById('adminSkillSort');if(so)so.value=sortVal;}
|
|
4325
|
+
if(statusVal){var sf=document.getElementById('adminSkillStatusFilter');if(sf)sf.value=statusVal;}
|
|
4025
4326
|
}
|
|
4327
|
+
function refilterAdminSkills(){adminPage.skills=0;renderAdminSkills(adminSkillsCache||[]);}
|
|
4328
|
+
function adminSkillsPage(p){if(p<0){adminPage.skills=Math.max(0,adminPage.skills-1);}else{adminPage.skills=p;}renderAdminSkills(adminSkillsCache||[]);}
|
|
4026
4329
|
|
|
4027
4330
|
async function adminDeleteSkill(skillId,skillName){
|
|
4028
|
-
if(!
|
|
4331
|
+
if(!(await confirmModal(t('confirm.removeSkill').replace('{name}',skillName),{danger:true}))) return;
|
|
4029
4332
|
try{
|
|
4030
4333
|
var r=await fetch('/api/admin/shared-skills/'+encodeURIComponent(skillId),{method:'DELETE'});
|
|
4031
4334
|
var d=await r.json();
|
|
@@ -4033,34 +4336,242 @@ async function adminDeleteSkill(skillId,skillName){
|
|
|
4033
4336
|
}catch(e){toast(t('toast.removeFail')+': '+e.message,'error');}
|
|
4034
4337
|
}
|
|
4035
4338
|
|
|
4036
|
-
function
|
|
4037
|
-
var el=document.getElementById('
|
|
4339
|
+
function renderAdminMemories(memories){
|
|
4340
|
+
var el=document.getElementById('adminMemoriesPanel');
|
|
4038
4341
|
if(!el) return;
|
|
4039
4342
|
adminMemoriesCache=memories||[];
|
|
4040
|
-
var
|
|
4041
|
-
|
|
4343
|
+
var all=memories||[];
|
|
4344
|
+
var filterVal=document.getElementById('adminMemoryFilter')?document.getElementById('adminMemoryFilter').value:'';
|
|
4345
|
+
var sortVal=document.getElementById('adminMemorySort')?document.getElementById('adminMemorySort').value:'newest';
|
|
4346
|
+
var roleVal=document.getElementById('adminMemoryRoleFilter')?document.getElementById('adminMemoryRoleFilter').value:'';
|
|
4347
|
+
var filtered=all;
|
|
4348
|
+
if(filterVal) filtered=filtered.filter(function(m){return (m.ownerName||m.sourceUserId||'')===filterVal;});
|
|
4349
|
+
if(roleVal) filtered=filtered.filter(function(m){return (m.role||'')===roleVal;});
|
|
4350
|
+
filtered=filtered.slice().sort(function(a,b){var ta=a.updatedAt||a.createdAt||0,tb=b.updatedAt||b.createdAt||0;return sortVal==='oldest'?ta-tb:tb-ta;});
|
|
4351
|
+
var html='<div class="admin-toolbar"><h3>'+t('admin.sharedMemories')+' ('+filtered.length+'/'+all.length+')</h3>'+
|
|
4352
|
+
adminToolbarHtml(all,function(m){return m.ownerName||m.sourceUserId||'';},'adminMemoryFilter','adminMemorySearch','refilterAdminMemories',{sortId:'adminMemorySort',extraFilters:[
|
|
4353
|
+
{id:'adminMemoryRoleFilter',label:t('admin.filter.role'),keyFn:function(m){return m.role||''}}
|
|
4354
|
+
]})+'</div>';
|
|
4355
|
+
if(filtered.length===0){
|
|
4042
4356
|
html+='<div class="admin-empty"><span class="ae-icon">\u{1F4AD}</span>'+t('admin.noSharedMemories')+'</div>';
|
|
4043
4357
|
}else{
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4358
|
+
var mPages=Math.ceil(filtered.length/ADMIN_PAGE_SIZE);
|
|
4359
|
+
if(adminPage.memories>=mPages) adminPage.memories=Math.max(0,mPages-1);
|
|
4360
|
+
var mStart=adminPage.memories*ADMIN_PAGE_SIZE,mEnd=Math.min(mStart+ADMIN_PAGE_SIZE,filtered.length);
|
|
4361
|
+
for(var i=mStart;i<mEnd;i++){
|
|
4362
|
+
var m=filtered[i];
|
|
4363
|
+
var cardId='adminMemCard_'+i;
|
|
4364
|
+
var preview=m.content?esc(m.content.slice(0,120)):'';
|
|
4365
|
+
html+='<div class="admin-card" id="'+cardId+'">'+
|
|
4366
|
+
'<div class="admin-card-header"><div class="admin-card-title">'+esc(m.summary||m.content?.slice(0,80)||m.id)+'</div></div>'+
|
|
4367
|
+
'<div class="admin-card-tags">'+
|
|
4368
|
+
'<div class="admin-card-tags-left">'+
|
|
4369
|
+
'<span class="admin-card-tag tag-owner">\u{1F464} '+esc(m.ownerName||m.sourceUserId||'unknown')+'</span>'+
|
|
4370
|
+
(m.role?'<span class="admin-card-tag tag-role">'+esc(m.role)+'</span>':'')+
|
|
4371
|
+
(m.kind?'<span class="admin-card-tag tag-kind">'+esc(m.kind)+'</span>':'')+
|
|
4053
4372
|
'</div>'+
|
|
4054
|
-
|
|
4373
|
+
'<span class="admin-card-actions">'+
|
|
4055
4374
|
'<button class="btn btn-sm btn-ghost" onclick="event.stopPropagation();adminDeleteMemory("'+escAttr(m.id)+'","'+escAttr(m.summary||m.id)+'")" style="color:var(--rose)">'+t('admin.remove')+'</button>'+
|
|
4056
|
-
|
|
4375
|
+
'<button class="btn btn-sm btn-ghost admin-card-expand-btn" onclick="event.stopPropagation();toggleAdminMemoryCard("'+cardId+'",'+i+')">'+t('admin.expand')+'</button>'+
|
|
4376
|
+
'<span class="admin-card-time">'+formatDateTimeSeconds(m.updatedAt||m.createdAt)+'</span>'+
|
|
4377
|
+
'</span>'+
|
|
4378
|
+
'</div>'+
|
|
4379
|
+
(preview?'<div class="admin-card-preview">'+preview+'</div>':'')+
|
|
4380
|
+
'<div class="admin-card-detail" id="'+cardId+'_detail"></div>'+
|
|
4381
|
+
'</div>';
|
|
4057
4382
|
}
|
|
4383
|
+
html+=adminPaginateHtml(filtered.length,adminPage.memories,'adminMemories');
|
|
4058
4384
|
}
|
|
4059
4385
|
el.innerHTML=html;
|
|
4386
|
+
if(filterVal){var sel=document.getElementById('adminMemoryFilter');if(sel)sel.value=filterVal;}
|
|
4387
|
+
if(sortVal!=='newest'){var so=document.getElementById('adminMemorySort');if(so)so.value=sortVal;}
|
|
4388
|
+
if(roleVal){var rf=document.getElementById('adminMemoryRoleFilter');if(rf)rf.value=roleVal;}
|
|
4389
|
+
}
|
|
4390
|
+
function refilterAdminMemories(){adminPage.memories=0;renderAdminMemories(adminMemoriesCache||[]);}
|
|
4391
|
+
function adminMemoriesPage(p){if(p<0){adminPage.memories=Math.max(0,adminPage.memories-1);}else{adminPage.memories=p;}renderAdminMemories(adminMemoriesCache||[]);}
|
|
4392
|
+
|
|
4393
|
+
function toggleAdminMemoryCard(cardId,idx){
|
|
4394
|
+
var card=document.getElementById(cardId);
|
|
4395
|
+
if(!card) return;
|
|
4396
|
+
var detail=document.getElementById(cardId+'_detail');
|
|
4397
|
+
var btn=card.querySelector('.admin-card-expand-btn');
|
|
4398
|
+
if(card.classList.contains('expanded')){
|
|
4399
|
+
card.classList.remove('expanded');
|
|
4400
|
+
if(btn) btn.textContent=t('admin.expand');
|
|
4401
|
+
return;
|
|
4402
|
+
}
|
|
4403
|
+
card.classList.add('expanded');
|
|
4404
|
+
if(btn) btn.textContent=t('admin.collapse');
|
|
4405
|
+
if(detail.getAttribute('data-loaded')) return;
|
|
4406
|
+
detail.setAttribute('data-loaded','1');
|
|
4407
|
+
var m=(adminMemoriesCache||[])[idx];
|
|
4408
|
+
if(!m){detail.innerHTML='<div style="color:var(--text-muted);font-size:13px">'+t('admin.noContent')+'</div>';return;}
|
|
4409
|
+
var metaHtml='<div class="admin-card-detail-meta">'+
|
|
4410
|
+
(m.kind?'<span class="meta-item">'+t('admin.kind')+esc(m.kind)+'</span>':'')+
|
|
4411
|
+
(m.role?'<span class="meta-item">'+t('admin.role')+esc(m.role)+'</span>':'')+
|
|
4412
|
+
(m.visibility?'<span class="meta-item">'+t('admin.visibility')+esc(m.visibility)+'</span>':'')+
|
|
4413
|
+
'<span class="meta-item">'+t('admin.owner')+esc(m.ownerName||m.sourceUserId||'unknown')+'</span>'+
|
|
4414
|
+
(m.groupName?'<span class="meta-item">'+t('admin.group')+esc(m.groupName)+'</span>':'')+
|
|
4415
|
+
'<span class="meta-item">'+new Date(m.updatedAt||m.createdAt||0).toLocaleString(dateLoc())+'</span>'+
|
|
4416
|
+
'</div>';
|
|
4417
|
+
var summaryHtml=m.summary?'<div class="admin-card-detail-section"><div class="detail-label">'+t('admin.summary')+'</div><div style="font-size:13px;color:var(--text);line-height:1.6">'+esc(m.summary)+'</div></div>':'';
|
|
4418
|
+
var contentHtml='<div class="admin-card-detail-section"><div class="detail-label">'+t('admin.contentLabel')+'</div><div class="admin-card-detail-content" id="'+cardId+'_content">'+
|
|
4419
|
+
(m.content?esc(m.content):t('sharing.loading'))+'</div></div>';
|
|
4420
|
+
detail.innerHTML=metaHtml+summaryHtml+contentHtml;
|
|
4421
|
+
if(!m.content&&(m.remoteHitId||m.id)){
|
|
4422
|
+
fetch('/api/sharing/memory-detail',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({remoteHitId:m.remoteHitId||m.id})})
|
|
4423
|
+
.then(function(r){return r.json();}).then(function(d){
|
|
4424
|
+
var ce=document.getElementById(cardId+'_content');
|
|
4425
|
+
if(!ce) return;
|
|
4426
|
+
if(!d.error&&d.content){ce.textContent=d.content;}
|
|
4427
|
+
else{ce.textContent=t('admin.noContent');}
|
|
4428
|
+
}).catch(function(){
|
|
4429
|
+
var ce=document.getElementById(cardId+'_content');
|
|
4430
|
+
if(ce) ce.textContent=t('admin.noContent');
|
|
4431
|
+
});
|
|
4432
|
+
}
|
|
4433
|
+
}
|
|
4434
|
+
|
|
4435
|
+
async function toggleAdminTaskCard(cardId,idx){
|
|
4436
|
+
var card=document.getElementById(cardId);
|
|
4437
|
+
if(!card) return;
|
|
4438
|
+
var detail=document.getElementById(cardId+'_detail');
|
|
4439
|
+
var btn=card.querySelector('.admin-card-expand-btn');
|
|
4440
|
+
if(card.classList.contains('expanded')){
|
|
4441
|
+
card.classList.remove('expanded');
|
|
4442
|
+
if(btn) btn.textContent=t('admin.expand');
|
|
4443
|
+
return;
|
|
4444
|
+
}
|
|
4445
|
+
card.classList.add('expanded');
|
|
4446
|
+
if(btn) btn.textContent=t('admin.collapse');
|
|
4447
|
+
if(detail.getAttribute('data-loaded')) return;
|
|
4448
|
+
detail.setAttribute('data-loaded','1');
|
|
4449
|
+
var tk=(adminTasksCache||[])[idx];
|
|
4450
|
+
if(!tk){detail.innerHTML='<div style="color:var(--text-muted);font-size:14px;padding:12px">'+t('admin.noContent')+'</div>';return;}
|
|
4451
|
+
var localTaskId=tk.sourceTaskId||tk.id;
|
|
4452
|
+
detail.innerHTML='<div class="spinner"></div>';
|
|
4453
|
+
var task=null;
|
|
4454
|
+
try{
|
|
4455
|
+
var r=await fetch('/api/task/'+encodeURIComponent(localTaskId));
|
|
4456
|
+
if(r.ok) task=await r.json();
|
|
4457
|
+
}catch(e){}
|
|
4458
|
+
if(!task){detail.innerHTML='<div style="color:var(--text-muted);font-size:14px;padding:12px">'+t('admin.noContent')+'</div>';return;}
|
|
4459
|
+
var metaHtml='<div class="admin-card-detail-meta admin-task-meta">'+
|
|
4460
|
+
(tk.status?'<span class="meta-item"><span class="task-status-badge '+tk.status+'">'+esc(tk.status)+'</span></span>':'')+
|
|
4461
|
+
(tk.visibility?'<span class="meta-item">'+t('admin.visibility')+esc(tk.visibility)+'</span>':'')+
|
|
4462
|
+
'<span class="meta-item">'+t('admin.owner')+esc(tk.ownerName||'unknown')+'</span>'+
|
|
4463
|
+
(tk.groupName?'<span class="meta-item">'+t('admin.group')+esc(tk.groupName)+'</span>':'')+
|
|
4464
|
+
(task.chunks&&task.chunks.length?'<span class="meta-item">\u{1F4AC} '+task.chunks.length+' '+t('tasks.chunks.label')+'</span>':'')+
|
|
4465
|
+
(task.startedAt?'<span class="meta-item">\u{1F4C5} '+formatDateTimeSeconds(task.startedAt)+'</span>':'')+
|
|
4466
|
+
(task.endedAt?'<span class="meta-item">\u2192 '+formatDateTimeSeconds(task.endedAt)+'</span>':'')+
|
|
4467
|
+
'<span class="meta-item">'+t('admin.updated')+new Date(tk.updatedAt||tk.createdAt||0).toLocaleString(dateLoc())+'</span>'+
|
|
4468
|
+
'</div>';
|
|
4469
|
+
var summaryHtml=(task.summary||tk.summary)?
|
|
4470
|
+
'<div class="admin-card-detail-section admin-task-summary"><div class="detail-label">'+t('admin.summary')+'</div><div class="admin-task-summary-body">'+renderSummaryHtml(task.summary||tk.summary)+'</div></div>':
|
|
4471
|
+
'<div class="admin-card-detail-section"><div style="color:var(--text-muted);font-size:14px">'+t('admin.noContent')+'</div></div>';
|
|
4472
|
+
var chunksHtml='';
|
|
4473
|
+
if(task.chunks&&task.chunks.length>0){
|
|
4474
|
+
var safeId=cardId.replace(/[^a-zA-Z0-9_-]/g,'_');
|
|
4475
|
+
chunksHtml='<div class="admin-card-detail-section"><div class="detail-label">'+t('tasks.chunks')+' ('+task.chunks.length+')</div><div class="admin-task-chunks">'+
|
|
4476
|
+
task.chunks.map(function(c,i){
|
|
4477
|
+
var role=c.role||'assistant';
|
|
4478
|
+
var roleLabel=role==='user'?t('tasks.role.user'):role==='assistant'?t('tasks.role.assistant'):role.toUpperCase();
|
|
4479
|
+
var bid='admchunk_b_'+safeId+'_'+i;
|
|
4480
|
+
var eid='admchunk_e_'+safeId+'_'+i;
|
|
4481
|
+
return '<div class="adm-msg">'+
|
|
4482
|
+
'<div class="adm-msg-side '+role+'"><div class="adm-msg-role">'+roleLabel+'</div><div class="adm-msg-time">'+formatDateTimeSeconds(c.createdAt)+'</div></div>'+
|
|
4483
|
+
'<div style="flex:1;min-width:0"><div class="adm-msg-body collapsed" id="'+bid+'">'+esc(c.content||'')+'</div>'+
|
|
4484
|
+
'<div class="adm-msg-toggle" id="'+eid+'" onclick="event.stopPropagation();toggleAdminChunkExpand("'+cardId.replace(/"/g,'&quot;')+'",'+i+')">'+t('tasks.expand')+'</div></div></div>';
|
|
4485
|
+
}).join('')+'</div></div>';
|
|
4486
|
+
}else{
|
|
4487
|
+
chunksHtml='<div class="admin-card-detail-section"><div class="detail-label">'+t('tasks.chunks')+'</div><div style="color:var(--text-muted);font-size:13px;padding:8px 0">'+t('tasks.nochunks')+'</div></div>';
|
|
4488
|
+
}
|
|
4489
|
+
detail.innerHTML=metaHtml+summaryHtml+chunksHtml;
|
|
4490
|
+
if(task.chunks&&task.chunks.length>0) setTimeout(function(){initAdminChunkExpanders(cardId,task.chunks.length)},50);
|
|
4491
|
+
}
|
|
4492
|
+
function toggleAdminChunkExpand(cardId,i){
|
|
4493
|
+
var safeId=cardId.replace(/[^a-zA-Z0-9_-]/g,'_');
|
|
4494
|
+
var b=document.getElementById('admchunk_b_'+safeId+'_'+i);
|
|
4495
|
+
var e=document.getElementById('admchunk_e_'+safeId+'_'+i);
|
|
4496
|
+
if(!b||!e)return;
|
|
4497
|
+
if(b.classList.contains('collapsed')){b.classList.remove('collapsed');e.textContent=t('tasks.collapse');}
|
|
4498
|
+
else{b.classList.add('collapsed');e.textContent=t('tasks.expand');}
|
|
4499
|
+
}
|
|
4500
|
+
function initAdminChunkExpanders(cardId,count){
|
|
4501
|
+
var safeId=cardId.replace(/[^a-zA-Z0-9_-]/g,'_');
|
|
4502
|
+
for(var i=0;i<count;i++){
|
|
4503
|
+
var b=document.getElementById('admchunk_b_'+safeId+'_'+i);
|
|
4504
|
+
var e=document.getElementById('admchunk_e_'+safeId+'_'+i);
|
|
4505
|
+
if(b&&b.scrollHeight>b.clientHeight+4&&e)e.style.display='block';
|
|
4506
|
+
else if(b)b.classList.remove('collapsed');
|
|
4507
|
+
}
|
|
4508
|
+
}
|
|
4509
|
+
|
|
4510
|
+
async function toggleAdminSkillCard(cardId,idx){
|
|
4511
|
+
var card=document.getElementById(cardId);
|
|
4512
|
+
if(!card) return;
|
|
4513
|
+
var detail=document.getElementById(cardId+'_detail');
|
|
4514
|
+
var btn=card.querySelector('.admin-card-expand-btn');
|
|
4515
|
+
if(card.classList.contains('expanded')){
|
|
4516
|
+
card.classList.remove('expanded');
|
|
4517
|
+
if(btn) btn.textContent=t('admin.expand');
|
|
4518
|
+
return;
|
|
4519
|
+
}
|
|
4520
|
+
card.classList.add('expanded');
|
|
4521
|
+
if(btn) btn.textContent=t('admin.collapse');
|
|
4522
|
+
if(detail.getAttribute('data-loaded')) return;
|
|
4523
|
+
detail.setAttribute('data-loaded','1');
|
|
4524
|
+
var sk=(adminSkillsCache||[])[idx];
|
|
4525
|
+
if(!sk){detail.innerHTML='<div style="color:var(--text-muted);font-size:13px">'+t('admin.noContent')+'</div>';return;}
|
|
4526
|
+
detail.innerHTML='<div class="spinner"></div>';
|
|
4527
|
+
var localSkillId=sk.sourceSkillId||sk.id;
|
|
4528
|
+
var localData=null;
|
|
4529
|
+
try{
|
|
4530
|
+
var lr=await fetch('/api/skill/'+encodeURIComponent(localSkillId));
|
|
4531
|
+
if(lr.ok) localData=await lr.json();
|
|
4532
|
+
}catch(e){}
|
|
4533
|
+
var localSkill=localData&&localData.skill?localData.skill:sk;
|
|
4534
|
+
var files=localData&&localData.files?localData.files:[];
|
|
4535
|
+
var versions=localData&&localData.versions?localData.versions:[];
|
|
4536
|
+
var qs=localSkill.qualityScore!=null?localSkill.qualityScore:sk.qualityScore;
|
|
4537
|
+
var metaHtml='<div class="admin-card-detail-meta">'+
|
|
4538
|
+
(localSkill.version!=null?'<span class="meta-item"><span class="skill-badge version">v'+localSkill.version+'</span></span>':'')+
|
|
4539
|
+
(localSkill.status?'<span class="meta-item"><span class="skill-badge status-'+localSkill.status+'">'+esc(localSkill.status)+'</span></span>':'')+
|
|
4540
|
+
(sk.visibility?'<span class="meta-item">'+t('admin.visibility')+esc(sk.visibility||'hub')+'</span>':'')+
|
|
4541
|
+
(qs!=null?'<span class="meta-item"><span class="skill-badge quality '+(qs>=7?'high':qs>=5?'mid':'low')+'">\u2605 '+Number(qs).toFixed(1)+'/10</span></span>':'')+
|
|
4542
|
+
'<span class="meta-item">'+t('admin.owner')+esc(sk.ownerName||'unknown')+'</span>'+
|
|
4543
|
+
(sk.groupName?'<span class="meta-item">'+t('admin.group')+esc(sk.groupName)+'</span>':'')+
|
|
4544
|
+
'<span class="meta-item">'+t('admin.updated')+new Date(sk.updatedAt||sk.createdAt||0).toLocaleString(dateLoc())+'</span>'+
|
|
4545
|
+
'</div>';
|
|
4546
|
+
var descHtml=(localSkill.description||sk.description)?'<div class="admin-card-detail-section"><div class="detail-label">'+t('admin.description')+'</div><div style="font-size:13px;color:var(--text);line-height:1.6">'+esc(localSkill.description||sk.description)+'</div></div>':'';
|
|
4547
|
+
var filesHtml='';
|
|
4548
|
+
if(files.length>0){
|
|
4549
|
+
var fileIcons={'skill':'\u{1F4D6}','script':'\u{2699}','reference':'\u{1F4CE}','file':'\u{1F4C4}'};
|
|
4550
|
+
filesHtml='<div class="admin-card-detail-section"><div class="detail-label" style="display:flex;align-items:center;justify-content:space-between">'+t('skills.files')+
|
|
4551
|
+
'<button class="btn btn-sm btn-ghost" onclick="event.stopPropagation();window.open(\\x27/api/skill/'+encodeURIComponent(localSkillId)+'/download\\x27,\\x27_blank\\x27)" style="font-size:11px">\u2B07 '+t('skills.download')+'</button>'+
|
|
4552
|
+
'</div><div class="skill-files-list">'+
|
|
4553
|
+
files.map(function(f){return '<div class="skill-file-item">'+
|
|
4554
|
+
'<span class="skill-file-icon">'+(fileIcons[f.type]||'\u{1F4C4}')+'</span>'+
|
|
4555
|
+
'<span class="skill-file-name">'+esc(f.path)+'</span>'+
|
|
4556
|
+
'<span class="skill-file-type">'+f.type+'</span>'+
|
|
4557
|
+
'<span class="skill-file-size">'+(f.size>1024?(f.size/1024).toFixed(1)+'KB':f.size+'B')+'</span>'+
|
|
4558
|
+
'</div>';}).join('')+
|
|
4559
|
+
'</div></div>';
|
|
4560
|
+
}
|
|
4561
|
+
var contentHtml='';
|
|
4562
|
+
if(versions.length>0&&versions[0].content){
|
|
4563
|
+
contentHtml='<div class="admin-card-detail-section"><div class="detail-label">SKILL.md (v'+versions[0].version+')</div><div class="admin-card-detail-content">'+renderSkillMarkdown(versions[0].content)+'</div></div>';
|
|
4564
|
+
}else if(localSkill.content||sk.content){
|
|
4565
|
+
contentHtml='<div class="admin-card-detail-section"><div class="detail-label">'+t('admin.contentLabel')+'</div><div class="admin-card-detail-content"><pre style="margin:0;white-space:pre-wrap">'+esc(localSkill.content||sk.content)+'</pre></div></div>';
|
|
4566
|
+
}
|
|
4567
|
+
detail.innerHTML=metaHtml+descHtml+filesHtml+contentHtml;
|
|
4568
|
+
if(!descHtml&&!filesHtml&&!contentHtml){
|
|
4569
|
+
detail.innerHTML+=('<div style="color:var(--text-muted);font-size:13px;padding:8px 0">'+t('admin.noContent')+'</div>');
|
|
4570
|
+
}
|
|
4060
4571
|
}
|
|
4061
4572
|
|
|
4062
4573
|
async function adminDeleteMemory(memoryId,memoryTitle){
|
|
4063
|
-
if(!
|
|
4574
|
+
if(!(await confirmModal(t('confirm.removeMemory').replace('{name}',memoryTitle),{danger:true}))) return;
|
|
4064
4575
|
try{
|
|
4065
4576
|
var r=await fetch('/api/admin/shared-memories/'+encodeURIComponent(memoryId),{method:'DELETE'});
|
|
4066
4577
|
var d=await r.json();
|
|
@@ -4073,7 +4584,7 @@ function renderSharingMemorySearchResults(data,query){
|
|
|
4073
4584
|
const localHits=(data&&data.local&&Array.isArray(data.local.hits))?data.local.hits:[];
|
|
4074
4585
|
const hubHits=(data&&data.hub&&Array.isArray(data.hub.hits))?data.hub.hits:[];
|
|
4075
4586
|
document.getElementById('searchMeta').textContent='Search results for "'+query+'"';
|
|
4076
|
-
document.getElementById('sharingSearchMeta').textContent='
|
|
4587
|
+
document.getElementById('sharingSearchMeta').textContent=t('scope.local')+' '+localHits.length+' · '+t('scope.hub')+' '+hubHits.length;
|
|
4077
4588
|
document.getElementById('pagination').innerHTML='';
|
|
4078
4589
|
list.innerHTML=''+
|
|
4079
4590
|
'<div class="result-section">'+
|
|
@@ -4113,7 +4624,7 @@ async function openSharedMemoryDetail(remoteHitId,title,owner,groupName){
|
|
|
4113
4624
|
currentSharedMemoryHitId=remoteHitId;
|
|
4114
4625
|
document.getElementById('sharedMemoryOverlay').classList.add('show');
|
|
4115
4626
|
document.getElementById('sharedMemoryTitle').textContent=title||t('search.sharedMemory');
|
|
4116
|
-
document.getElementById('sharedMemoryMeta').innerHTML='<span class="meta-item">
|
|
4627
|
+
document.getElementById('sharedMemoryMeta').innerHTML='<span class="meta-item">'+t('scope.hub')+'</span>'+(owner?'<span class="meta-item">'+t('admin.owner')+esc(owner)+'</span>':'')+(groupName?'<span class="meta-item">'+t('admin.group')+esc(groupName)+'</span>':'');
|
|
4117
4628
|
document.getElementById('sharedMemorySummary').textContent=t('sharing.loading');
|
|
4118
4629
|
document.getElementById('sharedMemoryContent').textContent='';
|
|
4119
4630
|
try{
|
|
@@ -4139,18 +4650,19 @@ async function openHubMemoryDetail(cacheKey,idx){
|
|
|
4139
4650
|
if(!m) return;
|
|
4140
4651
|
var overlay=document.getElementById('sharedMemoryOverlay');
|
|
4141
4652
|
overlay.classList.add('show');
|
|
4142
|
-
|
|
4143
|
-
|
|
4653
|
+
var titleText=m.summary||m.content?.slice(0,80)||'(no summary)';
|
|
4654
|
+
document.getElementById('sharedMemoryTitle').textContent=titleText;
|
|
4655
|
+
var metaHtml='<span class="meta-item">\\u{1F310} '+t('scope.hub')+'</span>'+
|
|
4144
4656
|
(m.ownerName?'<span class="meta-item">'+t('admin.owner')+esc(m.ownerName)+'</span>':'')+
|
|
4145
4657
|
(m.groupName?'<span class="meta-item">'+t('admin.group')+esc(m.groupName)+'</span>':'')+
|
|
4146
|
-
(m.kind?'<span class="meta-item">
|
|
4147
|
-
(m.role?'<span class="meta-item">
|
|
4148
|
-
'<span class="meta-item">visibility
|
|
4658
|
+
(m.kind?'<span class="meta-item">'+t('admin.kind')+esc(m.kind)+'</span>':'')+
|
|
4659
|
+
(m.role?'<span class="meta-item">'+t('admin.role')+esc(m.role)+'</span>':'')+
|
|
4660
|
+
(m.visibility?'<span class="meta-item">'+t('admin.visibility')+esc(m.visibility)+'</span>':'')+
|
|
4149
4661
|
'<span class="meta-item">'+new Date(m.updatedAt||m.createdAt||0).toLocaleString(dateLoc())+'</span>';
|
|
4150
4662
|
document.getElementById('sharedMemoryMeta').innerHTML=metaHtml;
|
|
4151
4663
|
document.getElementById('sharedMemorySummary').textContent=m.summary||'';
|
|
4152
|
-
|
|
4153
|
-
|
|
4664
|
+
var hasContent=m.content&&m.content.length>0;
|
|
4665
|
+
document.getElementById('sharedMemoryContent').textContent=hasContent?m.content:t('sharing.loading');
|
|
4154
4666
|
var remoteId=m.remoteHitId||m.id;
|
|
4155
4667
|
if(remoteId){
|
|
4156
4668
|
try{
|
|
@@ -4159,8 +4671,14 @@ async function openHubMemoryDetail(cacheKey,idx){
|
|
|
4159
4671
|
if(!d.error&&(d.content||d.summary)){
|
|
4160
4672
|
if(d.summary) document.getElementById('sharedMemorySummary').textContent=d.summary;
|
|
4161
4673
|
document.getElementById('sharedMemoryContent').textContent=d.content||m.content||'';
|
|
4674
|
+
}else if(!hasContent){
|
|
4675
|
+
document.getElementById('sharedMemoryContent').textContent=m.content||t('memory.detail.notFound');
|
|
4162
4676
|
}
|
|
4163
|
-
}catch(e){
|
|
4677
|
+
}catch(e){
|
|
4678
|
+
if(!hasContent) document.getElementById('sharedMemoryContent').textContent=m.content||t('memory.detail.notFound');
|
|
4679
|
+
}
|
|
4680
|
+
}else if(!hasContent){
|
|
4681
|
+
document.getElementById('sharedMemoryContent').textContent=t('memory.detail.notFound');
|
|
4164
4682
|
}
|
|
4165
4683
|
}
|
|
4166
4684
|
|
|
@@ -4173,11 +4691,11 @@ function openHubTaskDetailFromCache(cacheKey,idx){
|
|
|
4173
4691
|
document.getElementById('taskDetailTitle').textContent=task.title||'(no title)';
|
|
4174
4692
|
document.getElementById('taskShareActions').innerHTML='';
|
|
4175
4693
|
var meta=[
|
|
4176
|
-
'<span class="meta-item">\\u{1F310}
|
|
4694
|
+
'<span class="meta-item">\\u{1F310} '+t('scope.hub')+'</span>',
|
|
4177
4695
|
task.status?'<span class="meta-item"><span class="task-status-badge '+task.status+'">'+esc(task.status)+'</span></span>':'',
|
|
4178
4696
|
'<span class="meta-item">'+t('admin.owner')+esc(task.ownerName||'unknown')+'</span>',
|
|
4179
4697
|
task.groupName?'<span class="meta-item">'+t('admin.group')+esc(task.groupName)+'</span>':'',
|
|
4180
|
-
'<span class="meta-item">visibility
|
|
4698
|
+
task.visibility?'<span class="meta-item">'+t('admin.visibility')+esc(task.visibility)+'</span>':'',
|
|
4181
4699
|
task.chunkCount!=null?'<span class="meta-item">\\u{1F4DD} '+esc(String(task.chunkCount))+' '+t('tasks.chunks.label')+'</span>':'',
|
|
4182
4700
|
task.startedAt?'<span class="meta-item">\\u{1F4C5} '+formatTime(task.startedAt)+'</span>':'',
|
|
4183
4701
|
task.endedAt?'<span class="meta-item">\\u2192 '+formatTime(task.endedAt)+'</span>':'',
|
|
@@ -4201,7 +4719,7 @@ function openHubSkillDetailFromCache(cacheKey,idx){
|
|
|
4201
4719
|
var qs=skill.qualityScore;
|
|
4202
4720
|
var qsBadge=(qs!==null&&qs!==undefined)?'<span class="meta-item"><span class="skill-badge quality '+(qs>=7?'high':qs>=5?'mid':'low')+'">\\u2605 '+(+qs).toFixed(1)+'/10</span></span>':'';
|
|
4203
4721
|
var meta=[
|
|
4204
|
-
'<span class="meta-item">\\u{1F310}
|
|
4722
|
+
'<span class="meta-item">\\u{1F310} '+t('scope.hub')+'</span>',
|
|
4205
4723
|
skill.version!=null?'<span class="meta-item"><span class="skill-badge version">v'+skill.version+'</span></span>':'',
|
|
4206
4724
|
skill.status?'<span class="meta-item"><span class="skill-badge status-'+skill.status+'">'+esc(skill.status)+'</span></span>':'',
|
|
4207
4725
|
'<span class="meta-item">visibility: '+esc(skill.visibility||'hub')+'</span>',
|
|
@@ -4220,26 +4738,163 @@ function openHubSkillDetailFromCache(cacheKey,idx){
|
|
|
4220
4738
|
if(visBtn) visBtn.style.display='none';
|
|
4221
4739
|
var dlBtn=document.getElementById('skillDownloadBtn');
|
|
4222
4740
|
if(dlBtn) dlBtn.style.display='none';
|
|
4223
|
-
var
|
|
4224
|
-
if(
|
|
4741
|
+
var scopeBadge=document.getElementById('skillScopeBadge');
|
|
4742
|
+
if(scopeBadge) scopeBadge.innerHTML='';
|
|
4225
4743
|
}
|
|
4226
4744
|
|
|
4227
4745
|
function escAttr(s){return String(s||'').replace(/&/g,'&').replace(/'/g,''').replace(/"/g,'"').replace(/</g,'<').replace(/>/g,'>');}
|
|
4228
4746
|
|
|
4747
|
+
/* ─── Unified Sharing Scope Selector ─── */
|
|
4748
|
+
|
|
4749
|
+
function getScopeLabel(scope){
|
|
4750
|
+
if(scope==='team') return t('share.scope.team');
|
|
4751
|
+
if(scope==='local') return t('share.scope.local');
|
|
4752
|
+
return t('share.scope.private');
|
|
4753
|
+
}
|
|
4754
|
+
function getScopeIcon(scope){
|
|
4755
|
+
if(scope==='team') return '\\u{1F310}';
|
|
4756
|
+
if(scope==='local') return '\\u{1F465}';
|
|
4757
|
+
return '\\u{1F512}';
|
|
4758
|
+
}
|
|
4759
|
+
function getScopeColor(scope){
|
|
4760
|
+
if(scope==='team') return '#22c55e';
|
|
4761
|
+
if(scope==='local') return '#3b82f6';
|
|
4762
|
+
return '#f59e0b';
|
|
4763
|
+
}
|
|
4764
|
+
|
|
4765
|
+
function renderScopeBadge(scope){
|
|
4766
|
+
var color=getScopeColor(scope);
|
|
4767
|
+
var icon=getScopeIcon(scope);
|
|
4768
|
+
var label=getScopeLabel(scope);
|
|
4769
|
+
if(scope==='private') return '<span style="display:inline-flex;align-items:center;gap:3px;padding:2px 8px;background:'+color+'14;border:1px solid '+color+'33;border-radius:10px;font-size:11px;color:'+color+'">'+icon+' '+label+'</span>';
|
|
4770
|
+
return '<span style="display:inline-flex;align-items:center;gap:3px;padding:2px 8px;background:'+color+'18;border:1px solid '+color+'44;border-radius:10px;font-size:11px;color:'+color+'">'+icon+' '+label+'</span>';
|
|
4771
|
+
}
|
|
4772
|
+
|
|
4773
|
+
function openScopeSelectorModal(resourceType, resourceId, currentScope, onConfirm){
|
|
4774
|
+
var existing=document.getElementById('scopeSelectorOverlay');
|
|
4775
|
+
if(existing) existing.remove();
|
|
4776
|
+
var teamEnabled=sharingStatusCache&&sharingStatusCache.enabled;
|
|
4777
|
+
var overlay=document.createElement('div');
|
|
4778
|
+
overlay.id='scopeSelectorOverlay';
|
|
4779
|
+
overlay.style.cssText='position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);backdrop-filter:blur(6px);z-index:10000;display:flex;align-items:center;justify-content:center;animation:fadeIn 0.12s ease';
|
|
4780
|
+
var scopeDescs={private:'',local:'',team:t('share.scope.teamIncludes')};
|
|
4781
|
+
var scopes=['private','local','team'];
|
|
4782
|
+
var h='<div style="background:var(--bg-card);border:1px solid var(--border-glow);border-radius:12px;padding:0;width:340px;box-shadow:0 16px 48px rgba(0,0,0,0.35);overflow:hidden">';
|
|
4783
|
+
h+='<div style="padding:14px 18px 10px;border-bottom:1px solid var(--border)">';
|
|
4784
|
+
h+='<div style="font-size:14px;font-weight:600;color:var(--text)">'+t('share.scope.title')+'</div>';
|
|
4785
|
+
h+='</div>';
|
|
4786
|
+
h+='<div style="padding:6px 8px">';
|
|
4787
|
+
for(var i=0;i<scopes.length;i++){
|
|
4788
|
+
var sc=scopes[i];
|
|
4789
|
+
var isCurrent=sc===currentScope;
|
|
4790
|
+
var isDisabled=sc==='team'&&!teamEnabled;
|
|
4791
|
+
var color=getScopeColor(sc);
|
|
4792
|
+
var cursor=isDisabled?'not-allowed':'pointer';
|
|
4793
|
+
var opacity=isDisabled?'0.4':'1';
|
|
4794
|
+
var selBg=isCurrent?color+'16':'transparent';
|
|
4795
|
+
var selBorder=isCurrent?'2px solid '+color+'55':'2px solid transparent';
|
|
4796
|
+
h+='<div class="scope-option'+(isCurrent?' selected':'')+'" data-scope="'+sc+'" data-color="'+color+'" style="display:flex;align-items:center;gap:12px;padding:10px 12px;margin:3px 0;border:'+selBorder+';border-radius:10px;background:'+selBg+';cursor:'+cursor+';opacity:'+opacity+';transition:all 0.12s ease"';
|
|
4797
|
+
if(!isDisabled) h+=' onclick="selectScopeOption(this,\\''+sc+'\\')"';
|
|
4798
|
+
h+='>';
|
|
4799
|
+
h+='<div style="width:34px;height:34px;border-radius:9px;background:'+color+'22;display:flex;align-items:center;justify-content:center;font-size:17px;flex-shrink:0">'+getScopeIcon(sc)+'</div>';
|
|
4800
|
+
h+='<div style="flex:1;min-width:0">';
|
|
4801
|
+
h+='<div style="font-size:13px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:6px">'+getScopeLabel(sc);
|
|
4802
|
+
if(isCurrent) h+='<span style="font-size:10px;font-weight:600;color:#fff;background:'+color+';padding:1px 7px;border-radius:4px">'+t('share.scope.current')+'</span>';
|
|
4803
|
+
h+='</div>';
|
|
4804
|
+
var desc=isDisabled?t('share.scope.teamDisabled'):(scopeDescs[sc]||'');
|
|
4805
|
+
if(desc) h+='<div style="font-size:11px;color:var(--text-sec);margin-top:2px;line-height:1.3">'+desc+'</div>';
|
|
4806
|
+
h+='</div></div>';
|
|
4807
|
+
}
|
|
4808
|
+
h+='</div>';
|
|
4809
|
+
h+='<div style="padding:8px 14px 12px;border-top:1px solid var(--border);display:flex;gap:8px;justify-content:flex-end">';
|
|
4810
|
+
h+='<button class="btn btn-sm btn-ghost" onclick="closeScopeSelectorModal()" style="font-size:13px">'+t('share.scope.cancel')+'</button>';
|
|
4811
|
+
h+='<button class="btn btn-sm" id="scopeConfirmBtn" onclick="confirmScopeSelection()" disabled style="font-size:13px;min-width:56px">'+t('share.scope.confirm')+'</button>';
|
|
4812
|
+
h+='</div></div>';
|
|
4813
|
+
overlay.innerHTML=h;
|
|
4814
|
+
overlay.addEventListener('click',function(e){if(e.target===overlay)closeScopeSelectorModal();});
|
|
4815
|
+
document.body.appendChild(overlay);
|
|
4816
|
+
window._scopeSelectionState={resourceType:resourceType,resourceId:resourceId,currentScope:currentScope,selectedScope:currentScope,onConfirm:onConfirm};
|
|
4817
|
+
}
|
|
4818
|
+
function selectScopeOption(el,scope){
|
|
4819
|
+
if(!window._scopeSelectionState)return;
|
|
4820
|
+
var overlay=document.getElementById('scopeSelectorOverlay');
|
|
4821
|
+
if(!overlay)return;
|
|
4822
|
+
var color=getScopeColor(scope);
|
|
4823
|
+
var options=overlay.querySelectorAll('.scope-option');
|
|
4824
|
+
options.forEach(function(opt){opt.classList.remove('selected');opt.style.border='2px solid transparent';opt.style.background='transparent';});
|
|
4825
|
+
el.classList.add('selected');
|
|
4826
|
+
el.style.border='2px solid '+color+'55';
|
|
4827
|
+
el.style.background=color+'16';
|
|
4828
|
+
window._scopeSelectionState.selectedScope=scope;
|
|
4829
|
+
document.getElementById('scopeConfirmBtn').disabled=(scope===window._scopeSelectionState.currentScope);
|
|
4830
|
+
}
|
|
4831
|
+
function closeScopeSelectorModal(){
|
|
4832
|
+
var overlay=document.getElementById('scopeSelectorOverlay');
|
|
4833
|
+
if(overlay) overlay.remove();
|
|
4834
|
+
window._scopeSelectionState=null;
|
|
4835
|
+
}
|
|
4836
|
+
async function confirmScopeSelection(){
|
|
4837
|
+
if(!window._scopeSelectionState)return;
|
|
4838
|
+
var st=Object.assign({},window._scopeSelectionState);
|
|
4839
|
+
var newScope=st.selectedScope;
|
|
4840
|
+
var oldScope=st.currentScope;
|
|
4841
|
+
if(newScope===oldScope){closeScopeSelectorModal();return;}
|
|
4842
|
+
closeScopeSelectorModal();
|
|
4843
|
+
var shrinking=(oldScope==='team'&&newScope!=='team')||(oldScope==='local'&&newScope==='private');
|
|
4844
|
+
if(shrinking){
|
|
4845
|
+
var msg=oldScope==='team'&&newScope==='local'?t('share.scope.shrinkToLocal'):
|
|
4846
|
+
oldScope==='team'&&newScope==='private'?t('share.scope.shrinkToPrivate'):
|
|
4847
|
+
t('share.scope.shrinkLocalToPrivate');
|
|
4848
|
+
if(!(await confirmModal(msg,{danger:true})))return;
|
|
4849
|
+
}
|
|
4850
|
+
try{
|
|
4851
|
+
var url='/api/'+st.resourceType+'/'+st.resourceId+'/scope';
|
|
4852
|
+
var r=await fetch(url,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify({scope:newScope})});
|
|
4853
|
+
var d=await r.json();
|
|
4854
|
+
if(d.ok){
|
|
4855
|
+
toast(t('share.scope.changed'),'success');
|
|
4856
|
+
if(st.onConfirm) st.onConfirm(newScope);
|
|
4857
|
+
else loadAll();
|
|
4858
|
+
}else{
|
|
4859
|
+
toast(d.error||t('share.scope.changeFail'),'error');
|
|
4860
|
+
}
|
|
4861
|
+
}catch(e){toast(t('share.scope.changeFail')+': '+e.message,'error');}
|
|
4862
|
+
}
|
|
4863
|
+
|
|
4229
4864
|
function renderTaskShareActions(task){
|
|
4230
4865
|
currentTaskDetail=task||null;
|
|
4231
4866
|
const el=document.getElementById('taskShareActions');
|
|
4232
4867
|
if(!el){return;}
|
|
4233
4868
|
if(!task||!task.id){el.innerHTML='';return;}
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
var
|
|
4237
|
-
if(
|
|
4238
|
-
|
|
4869
|
+
var isLocalShared=task.owner==='public';
|
|
4870
|
+
var isTeamShared=!!(task.sharingVisibility||task.hubTaskId);
|
|
4871
|
+
var currentScope=isTeamShared?'team':isLocalShared?'local':'private';
|
|
4872
|
+
if(task.status==='completed'){
|
|
4873
|
+
el.innerHTML=renderScopeBadge(currentScope)+
|
|
4874
|
+
'<button class="btn btn-sm btn-ghost" onclick="openTaskScopeModal()">\u270F '+t('share.shareBtn')+'</button>';
|
|
4875
|
+
}else{
|
|
4876
|
+
el.innerHTML=renderScopeBadge(currentScope)+
|
|
4877
|
+
'<button class="btn btn-sm btn-ghost" style="opacity:0.45;cursor:not-allowed" onclick="toast(t(\\x27share.scope.taskNotCompleted\\x27),\\x27warn\\x27)">\u270F '+t('share.shareBtn')+'</button>';
|
|
4239
4878
|
}
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4879
|
+
}
|
|
4880
|
+
function openTaskScopeModal(){
|
|
4881
|
+
if(!currentTaskDetail) return;
|
|
4882
|
+
var task=currentTaskDetail;
|
|
4883
|
+
var isLocalShared=task.owner==='public';
|
|
4884
|
+
var isTeamShared=!!(task.sharingVisibility||task.hubTaskId);
|
|
4885
|
+
var cs=isTeamShared?'team':isLocalShared?'local':'private';
|
|
4886
|
+
openScopeSelectorModal('task',task.id,cs,function(s){
|
|
4887
|
+
if(s==='team'){task.sharingVisibility='public';task.hubTaskId=task.hubTaskId||'shared';}
|
|
4888
|
+
else if(s==='local'){task.sharingVisibility=null;task.owner='public';}
|
|
4889
|
+
else{task.sharingVisibility=null;task.owner=task._origOwner||'agent:main';}
|
|
4890
|
+
renderTaskShareActions(task);
|
|
4891
|
+
updateTaskCardBadge(task.id,s);
|
|
4892
|
+
});
|
|
4893
|
+
}
|
|
4894
|
+
function openTaskScopeModalFromList(taskId,currentScope){
|
|
4895
|
+
openScopeSelectorModal('task',taskId,currentScope,function(s){
|
|
4896
|
+
updateTaskCardBadge(taskId,s);
|
|
4897
|
+
});
|
|
4243
4898
|
}
|
|
4244
4899
|
|
|
4245
4900
|
async function shareCurrentTask(){
|
|
@@ -4262,18 +4917,34 @@ async function unshareCurrentTask(){
|
|
|
4262
4917
|
}
|
|
4263
4918
|
|
|
4264
4919
|
function renderSkillShareActions(skill){
|
|
4265
|
-
|
|
4920
|
+
var el=document.getElementById('skillScopeBadge');
|
|
4266
4921
|
if(!el){return;}
|
|
4267
4922
|
if(!skill||!skill.id){el.innerHTML='';return;}
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
var
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4923
|
+
var isLocalShared=skill.visibility==='public';
|
|
4924
|
+
var isTeamShared=!!(skill.sharingVisibility);
|
|
4925
|
+
var currentScope=isTeamShared?'team':isLocalShared?'local':'private';
|
|
4926
|
+
el.innerHTML=renderScopeBadge(currentScope);
|
|
4927
|
+
}
|
|
4928
|
+
function openSkillScopeModal(){
|
|
4929
|
+
if(!currentSkillDetail) return;
|
|
4930
|
+
var skill=currentSkillDetail;
|
|
4931
|
+
var isLocalShared=skill.visibility==='public';
|
|
4932
|
+
var isTeamShared=!!skill.sharingVisibility;
|
|
4933
|
+
var cs=isTeamShared?'team':isLocalShared?'local':'private';
|
|
4934
|
+
openScopeSelectorModal('skill',skill.id,cs,function(s){
|
|
4935
|
+
if(s==='team'){skill.sharingVisibility='public';}
|
|
4936
|
+
else if(s==='local'){skill.sharingVisibility=null;skill.visibility='public';}
|
|
4937
|
+
else{skill.sharingVisibility=null;skill.visibility='private';}
|
|
4938
|
+
renderSkillShareActions(skill);
|
|
4939
|
+
var scopeBadgeEl=document.getElementById('skillScopeBadge');
|
|
4940
|
+
if(scopeBadgeEl) scopeBadgeEl.innerHTML=renderScopeBadge(s);
|
|
4941
|
+
updateSkillCardBadge(skill.id,s);
|
|
4942
|
+
});
|
|
4943
|
+
}
|
|
4944
|
+
function openSkillScopeModalFromList(skillId,currentScope){
|
|
4945
|
+
openScopeSelectorModal('skill',skillId,currentScope,function(s){
|
|
4946
|
+
updateSkillCardBadge(skillId,s);
|
|
4947
|
+
});
|
|
4277
4948
|
}
|
|
4278
4949
|
|
|
4279
4950
|
async function shareCurrentSkill(){
|
|
@@ -4299,18 +4970,34 @@ async function shareMemoryPrompt(chunkId){
|
|
|
4299
4970
|
try{
|
|
4300
4971
|
const r=await fetch('/api/sharing/memories/share',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({chunkId:chunkId,visibility:'public'})});
|
|
4301
4972
|
const d=await r.json();
|
|
4302
|
-
if(d.ok){
|
|
4973
|
+
if(d.ok){
|
|
4974
|
+
toast(t('toast.memoryShared'),'success');
|
|
4975
|
+
if(memoryCache[chunkId]){memoryCache[chunkId].sharingVisibility='public';}
|
|
4976
|
+
updateMemoryCardBadge(chunkId,'team');
|
|
4977
|
+
} else {toast(d.error||t('toast.memoryShareFail'),'error');}
|
|
4303
4978
|
}catch(e){toast(t('toast.memoryShareFail')+': '+e.message,'error');}
|
|
4304
4979
|
}
|
|
4305
4980
|
|
|
4306
4981
|
async function unshareMemory(chunkId){
|
|
4982
|
+
if(!(await confirmModal(t('share.memoryUnshareConfirm'),{danger:true}))) return;
|
|
4307
4983
|
try{
|
|
4308
4984
|
const r=await fetch('/api/sharing/memories/unshare',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({chunkId:chunkId})});
|
|
4309
4985
|
const d=await r.json();
|
|
4310
|
-
if(d.ok){
|
|
4986
|
+
if(d.ok){
|
|
4987
|
+
toast(t('toast.memoryUnshared'),'success');
|
|
4988
|
+
if(memoryCache[chunkId]){memoryCache[chunkId].sharingVisibility=null;}
|
|
4989
|
+
var m=memoryCache[chunkId];
|
|
4990
|
+
var newScope=(m&&m.owner==='public')?'local':'private';
|
|
4991
|
+
updateMemoryCardBadge(chunkId,newScope);
|
|
4992
|
+
} else {toast(d.error||t('toast.memoryUnshareFail'),'error');}
|
|
4311
4993
|
}catch(e){toast(t('toast.memoryUnshareFail')+': '+e.message,'error');}
|
|
4312
4994
|
}
|
|
4313
4995
|
|
|
4996
|
+
function localMemoryErrorMessage(err){
|
|
4997
|
+
if(err==='original_owner_missing') return t('share.local.originalOwnerMissing');
|
|
4998
|
+
return err||t('toast.opfail');
|
|
4999
|
+
}
|
|
5000
|
+
|
|
4314
5001
|
function debounceSkillSearch(){
|
|
4315
5002
|
clearTimeout(skillSearchTimer);
|
|
4316
5003
|
skillSearchTimer=setTimeout(function(){loadSkills();},300);
|
|
@@ -4700,10 +5387,13 @@ async function loadTasks(){
|
|
|
4700
5387
|
const timeStr=formatTime(task.startedAt);
|
|
4701
5388
|
const endStr=task.endedAt?formatTime(task.endedAt):'';
|
|
4702
5389
|
const durationStr=task.endedAt?formatDuration(task.endedAt-task.startedAt):'';
|
|
5390
|
+
var taskIsLocalShared=task.owner==='public';
|
|
5391
|
+
var taskIsTeamShared=!!task.sharingVisibility;
|
|
5392
|
+
var taskScope=taskIsTeamShared?'team':taskIsLocalShared?'local':'private';
|
|
4703
5393
|
return '<div class="task-card status-'+task.status+'" onclick="openTaskDetail(\\''+task.id+'\\')">'+
|
|
4704
5394
|
'<div class="task-card-top">'+
|
|
4705
5395
|
'<div class="task-card-title">'+esc(task.title)+'</div>'+
|
|
4706
|
-
'<span class="task-status-badge '+task.status+'">'+t('tasks.status.'+task.status)+'</span>'+
|
|
5396
|
+
'<div class="task-card-badges">'+renderScopeBadge(taskScope)+'<span class="task-status-badge '+task.status+'">'+t('tasks.status.'+task.status)+'</span></div>'+
|
|
4707
5397
|
'</div>'+
|
|
4708
5398
|
(task.summary?'<div class="task-card-summary'+(task.status==='skipped'?' skipped-reason':'')+'">'+esc(task.summary)+'</div>':'')+
|
|
4709
5399
|
'<div class="task-card-bottom">'+
|
|
@@ -4715,6 +5405,9 @@ async function loadTasks(){
|
|
|
4715
5405
|
'<div class="card-actions" onclick="event.stopPropagation()">'+
|
|
4716
5406
|
'<button class="btn btn-sm btn-ghost" onclick="openTaskDetail(\\''+task.id+'\\')">'+t('card.expand')+'</button>'+
|
|
4717
5407
|
(task.status==='completed'&&(!task.skillStatus||task.skillStatus==='not_generated'||task.skillStatus==='skipped')?'<button class="btn btn-sm btn-ghost" onclick="retrySkillGen(\\''+task.id+'\\')">'+t('task.retrySkill.short')+'</button>':'')+
|
|
5408
|
+
(task.status==='completed'
|
|
5409
|
+
?'<button class="btn btn-sm btn-ghost" onclick="openTaskScopeModalFromList(\\''+task.id+'\\',\\''+taskScope+'\\')">\\u270F '+t('share.shareBtn')+'</button>'
|
|
5410
|
+
:'<button class="btn btn-sm btn-ghost" style="opacity:0.45;cursor:not-allowed" onclick="toast(t(\\x27share.scope.taskNotCompleted\\x27),\\x27warn\\x27)">\\u270F '+t('share.shareBtn')+'</button>')+
|
|
4718
5411
|
'<button class="btn btn-sm btn-ghost" style="color:var(--accent)" onclick="deleteTask(\\''+task.id+'\\')">'+t('task.delete')+'</button>'+
|
|
4719
5412
|
'</div>'+
|
|
4720
5413
|
'</div>';
|
|
@@ -4727,6 +5420,20 @@ async function loadTasks(){
|
|
|
4727
5420
|
}
|
|
4728
5421
|
}
|
|
4729
5422
|
|
|
5423
|
+
function updateTaskCardBadge(taskId,newScope){
|
|
5424
|
+
var cards=document.querySelectorAll('.task-card');
|
|
5425
|
+
for(var i=0;i<cards.length;i++){
|
|
5426
|
+
var onclick=cards[i].getAttribute('onclick')||'';
|
|
5427
|
+
if(onclick.indexOf(taskId)===-1) continue;
|
|
5428
|
+
var badges=cards[i].querySelector('.task-card-badges');
|
|
5429
|
+
if(!badges) continue;
|
|
5430
|
+
var oldBadge=badges.querySelectorAll('span[style*="border-radius:10px"]');
|
|
5431
|
+
for(var j=0;j<oldBadge.length;j++) oldBadge[j].remove();
|
|
5432
|
+
badges.insertAdjacentHTML('afterbegin',renderScopeBadge(newScope));
|
|
5433
|
+
break;
|
|
5434
|
+
}
|
|
5435
|
+
}
|
|
5436
|
+
|
|
4730
5437
|
function renderTasksPagination(total){
|
|
4731
5438
|
const el=document.getElementById('tasksPagination');
|
|
4732
5439
|
const pages=Math.ceil(total/TASKS_PER_PAGE);
|
|
@@ -4766,9 +5473,9 @@ async function openTaskDetail(taskId){
|
|
|
4766
5473
|
|
|
4767
5474
|
const meta=[
|
|
4768
5475
|
'<span class="meta-item"><span class="task-status-badge '+task.status+'">'+t('tasks.status.'+task.status)+'</span></span>',
|
|
4769
|
-
'<span class="meta-item">\\u{1F4C5} '+
|
|
5476
|
+
'<span class="meta-item">\\u{1F4C5} '+formatDateTimeSeconds(task.startedAt)+'</span>',
|
|
4770
5477
|
];
|
|
4771
|
-
if(task.endedAt) meta.push('<span class="meta-item">\\u2192 '+
|
|
5478
|
+
if(task.endedAt) meta.push('<span class="meta-item">\\u2192 '+formatDateTimeSeconds(task.endedAt)+'</span>');
|
|
4772
5479
|
meta.push('<span class="meta-item">\\u{1F4C2} '+task.sessionKey+'</span>');
|
|
4773
5480
|
meta.push('<span class="meta-item">\\u{1F4DD} '+task.chunks.length+' '+t('tasks.chunks.label')+'</span>');
|
|
4774
5481
|
meta.push('<div style="width:100%;margin-top:4px"><span class="meta-item" style="width:100%">'+t('tasks.taskid')+'<span class="task-id-full">'+esc(task.id)+'</span></span></div>');
|
|
@@ -4793,11 +5500,17 @@ async function openTaskDetail(taskId){
|
|
|
4793
5500
|
}else{
|
|
4794
5501
|
document.getElementById('taskDetailChunks').innerHTML=task.chunks.map(function(c,i){
|
|
4795
5502
|
var roleLabel=c.role==='user'?t('tasks.role.user'):c.role==='assistant'?t('tasks.role.assistant'):c.role.toUpperCase();
|
|
5503
|
+
var avatarIcon=c.role==='user'?'U':c.role==='assistant'?'A':'T';
|
|
4796
5504
|
return '<div class="task-chunk-item role-'+c.role+'">'+
|
|
5505
|
+
'<div class="task-chunk-avatar">'+avatarIcon+'</div>'+
|
|
5506
|
+
'<div class="task-chunk-body">'+
|
|
5507
|
+
'<div class="task-chunk-header">'+
|
|
4797
5508
|
'<div class="task-chunk-role '+c.role+'">'+roleLabel+'</div>'+
|
|
5509
|
+
'<div class="task-chunk-time">'+formatDateTimeSeconds(c.createdAt)+'</div>'+
|
|
5510
|
+
'</div>'+
|
|
4798
5511
|
'<div class="task-chunk-bubble collapsed" id="chunk_b_'+i+'">'+esc(c.content)+'</div>'+
|
|
4799
5512
|
'<div class="task-chunk-expand" id="chunk_e_'+i+'" onclick="toggleChunkExpand('+i+')"><span class="expand-arrow">▼</span> <span class="expand-label">'+t('tasks.expand')+'</span></div>'+
|
|
4800
|
-
'
|
|
5513
|
+
'</div>'+
|
|
4801
5514
|
'</div>';
|
|
4802
5515
|
}).join('');
|
|
4803
5516
|
setTimeout(function(){initChunkExpanders(task.chunks.length)},50);
|
|
@@ -4890,14 +5603,13 @@ function toggleChunkExpand(i){
|
|
|
4890
5603
|
e.querySelector('.expand-label').textContent=t('tasks.expand');
|
|
4891
5604
|
}
|
|
4892
5605
|
}
|
|
4893
|
-
|
|
4894
5606
|
function closeTaskDetail(event){
|
|
4895
5607
|
if(event && event.target!==document.getElementById('taskDetailOverlay')) return;
|
|
4896
5608
|
document.getElementById('taskDetailOverlay').classList.remove('show');
|
|
4897
5609
|
}
|
|
4898
5610
|
|
|
4899
5611
|
async function retrySkillGen(taskId){
|
|
4900
|
-
if(!
|
|
5612
|
+
if(!(await confirmModal(t('task.retrySkill.confirm')))) return;
|
|
4901
5613
|
try{
|
|
4902
5614
|
const r=await fetch('/api/task/'+taskId+'/retry-skill',{method:'POST'});
|
|
4903
5615
|
const d=await r.json();
|
|
@@ -4907,7 +5619,7 @@ async function retrySkillGen(taskId){
|
|
|
4907
5619
|
}
|
|
4908
5620
|
|
|
4909
5621
|
async function deleteTask(taskId){
|
|
4910
|
-
if(!
|
|
5622
|
+
if(!(await confirmModal(t('task.delete.confirm'),{danger:true}))) return;
|
|
4911
5623
|
try{
|
|
4912
5624
|
const r=await fetch('/api/task/'+taskId,{method:'DELETE'});
|
|
4913
5625
|
const d=await r.json();
|
|
@@ -4929,6 +5641,22 @@ function setSkillStatusFilter(btn,status){
|
|
|
4929
5641
|
loadSkills();
|
|
4930
5642
|
}
|
|
4931
5643
|
|
|
5644
|
+
function updateSkillCardBadge(skillId,newScope){
|
|
5645
|
+
var cards=document.querySelectorAll('.skill-card');
|
|
5646
|
+
for(var i=0;i<cards.length;i++){
|
|
5647
|
+
var onclick=cards[i].getAttribute('onclick')||'';
|
|
5648
|
+
if(onclick.indexOf(skillId)===-1) continue;
|
|
5649
|
+
var badges=cards[i].querySelector('.skill-card-badges');
|
|
5650
|
+
if(!badges) continue;
|
|
5651
|
+
var oldBadge=badges.querySelectorAll('span[style*="border-radius:10px"]');
|
|
5652
|
+
for(var j=0;j<oldBadge.length;j++) oldBadge[j].remove();
|
|
5653
|
+
var versionBadge=badges.querySelector('.version');
|
|
5654
|
+
if(versionBadge) versionBadge.insertAdjacentHTML('afterend',renderScopeBadge(newScope));
|
|
5655
|
+
else badges.insertAdjacentHTML('afterbegin',renderScopeBadge(newScope));
|
|
5656
|
+
break;
|
|
5657
|
+
}
|
|
5658
|
+
}
|
|
5659
|
+
|
|
4932
5660
|
async function loadSkills(){
|
|
4933
5661
|
const list=document.getElementById('skillsList');
|
|
4934
5662
|
const hubList=document.getElementById('hubSkillsList');
|
|
@@ -4976,14 +5704,16 @@ async function loadSkills(){
|
|
|
4976
5704
|
const sourceLabel=tags.includes('hub-import')?'hub-import':skill.sourceType;
|
|
4977
5705
|
const qs=skill.qualityScore;
|
|
4978
5706
|
const qsBadge=qs!==null&&qs!==undefined?'<span class="skill-badge quality '+(qs>=7?'high':qs>=5?'mid':'low')+'">★ '+qs.toFixed(1)+'</span>':'';
|
|
4979
|
-
const
|
|
5707
|
+
const skillIsLocalShared=skill.visibility==='public';
|
|
5708
|
+
const skillIsTeamShared=!!skill.sharingVisibility;
|
|
5709
|
+
const skillScope=skillIsTeamShared?'team':skillIsLocalShared?'local':'private';
|
|
4980
5710
|
return '<div class="skill-card '+installedClass+' '+statusClass+'" onclick="openSkillDetail("'+escAttr(skill.id)+'")">'+
|
|
4981
5711
|
'<div class="skill-card-top">'+
|
|
4982
5712
|
'<div class="skill-card-name">🧠 '+esc(skill.name)+'</div>'+
|
|
4983
5713
|
'<div class="skill-card-badges">'+
|
|
4984
5714
|
qsBadge+
|
|
4985
5715
|
'<span class="skill-badge version">v'+skill.version+'</span>'+
|
|
4986
|
-
|
|
5716
|
+
renderScopeBadge(skillScope)+
|
|
4987
5717
|
(skill.installed?'<span class="skill-badge installed">'+t('skills.installed.badge')+'</span>':'')+
|
|
4988
5718
|
'<span class="skill-badge status-'+skill.status+'">'+t('skills.status.'+skill.status)+'</span>'+
|
|
4989
5719
|
'</div>'+
|
|
@@ -4992,7 +5722,13 @@ async function loadSkills(){
|
|
|
4992
5722
|
'<div class="skill-card-bottom">'+
|
|
4993
5723
|
'<span class="tag"><span class="icon">📅</span> '+timeStr+'</span>'+
|
|
4994
5724
|
'<span class="tag"><span class="icon">📦</span> '+sourceLabel+'</span>'+
|
|
4995
|
-
(tags.length>0?'<div class="skill-card-tags">'+tags.map(
|
|
5725
|
+
(tags.length>0?'<div class="skill-card-tags">'+tags.map(tg=>'<span class="skill-tag">'+esc(tg)+'</span>').join('')+'</div>':'')+
|
|
5726
|
+
'<span class="card-actions-inline" onclick="event.stopPropagation()">'+
|
|
5727
|
+
'<button class="btn btn-sm btn-ghost" onclick="openSkillDetail("'+escAttr(skill.id)+'")">'+t('card.expand')+'</button>'+
|
|
5728
|
+
(skill.status==='active'
|
|
5729
|
+
?'<button class="btn btn-sm btn-ghost" onclick="openSkillScopeModalFromList("'+escAttr(skill.id)+'","'+skillScope+'")">\\u270F '+t('share.shareBtn')+'</button>'
|
|
5730
|
+
:'<button class="btn btn-sm btn-ghost" style="opacity:0.45;cursor:not-allowed" onclick="toast(t(\\x27share.scope.skillNotActive\\x27),\\x27warn\\x27)">\\u270F '+t('share.shareBtn')+'</button>')+
|
|
5731
|
+
'</span>'+
|
|
4996
5732
|
'</div>'+
|
|
4997
5733
|
'</div>';
|
|
4998
5734
|
}).join('');
|
|
@@ -5052,12 +5788,12 @@ async function loadSkills(){
|
|
|
5052
5788
|
'<span class="meta-chip">visibility: '+esc(skill.visibility||'hub')+'</span>'+
|
|
5053
5789
|
(skill.version!=null?'<span class="meta-chip">v'+skill.version+'</span>':'')+
|
|
5054
5790
|
'</div>'+
|
|
5055
|
-
'<div class="hub-skill-actions"><button class="btn btn-sm" onclick="event.stopPropagation();pullHubSkill("'+escAttr(skill.skillId)+'")">
|
|
5791
|
+
'<div class="hub-skill-actions"><button class="btn btn-sm" onclick="event.stopPropagation();pullHubSkill("'+escAttr(skill.skillId)+'")">'+t('skill.pullToLocal')+'</button></div>'+
|
|
5056
5792
|
'</div>';
|
|
5057
5793
|
}).join(''):'';
|
|
5058
5794
|
}
|
|
5059
5795
|
|
|
5060
|
-
document.getElementById('skillSearchMeta').textContent=t('skills.search.local')+' '+localHits.length+(hubHits.length?' ·
|
|
5796
|
+
document.getElementById('skillSearchMeta').textContent=t('skills.search.local')+' '+localHits.length+(hubHits.length?' · '+t('scope.hub')+' '+hubHits.length:'');
|
|
5061
5797
|
document.getElementById('skillsTotalCount').textContent=formatNum(localHits.length+hubHits.length);
|
|
5062
5798
|
document.getElementById('skillsActiveCount').textContent=formatNum(localHits.length);
|
|
5063
5799
|
document.getElementById('skillsDraftCount').textContent='0';
|
|
@@ -5096,7 +5832,7 @@ async function loadHubSkills(hubList){
|
|
|
5096
5832
|
'<span class="meta-chip">visibility: '+esc(skill.visibility||'hub')+'</span>'+
|
|
5097
5833
|
(skill.version!=null?'<span class="meta-chip">v'+skill.version+'</span>':'')+
|
|
5098
5834
|
'</div>'+
|
|
5099
|
-
'<div class="hub-skill-actions"><button class="btn btn-sm" onclick="event.stopPropagation();pullHubSkill(\\''+escAttr(skill.sourceSkillId)+'\\')">
|
|
5835
|
+
'<div class="hub-skill-actions"><button class="btn btn-sm" onclick="event.stopPropagation();pullHubSkill(\\''+escAttr(skill.sourceSkillId)+'\\')">'+t('skill.pullToLocal')+'</button></div>'+
|
|
5100
5836
|
'</div>';
|
|
5101
5837
|
}).join('');
|
|
5102
5838
|
}catch(e){
|
|
@@ -5125,6 +5861,7 @@ async function openSkillDetail(skillId){
|
|
|
5125
5861
|
document.getElementById('skillRelatedTasks').innerHTML='';
|
|
5126
5862
|
var vb=document.getElementById('skillVisibilityBtn');if(vb)vb.style.display='';
|
|
5127
5863
|
var db=document.getElementById('skillDownloadBtn');if(db)db.style.display='';
|
|
5864
|
+
var sb=document.getElementById('skillScopeBadge');if(sb)sb.innerHTML='';
|
|
5128
5865
|
document.getElementById('skillDetailActions').innerHTML='';
|
|
5129
5866
|
|
|
5130
5867
|
try{
|
|
@@ -5146,32 +5883,29 @@ async function openSkillDetail(skillId){
|
|
|
5146
5883
|
|
|
5147
5884
|
const qs=skill.qualityScore;
|
|
5148
5885
|
const qsBadge=qs!==null&&qs!==undefined?'<span class="meta-item"><span class="skill-badge quality '+(qs>=7?'high':qs>=5?'mid':'low')+'">\\u2605 '+qs.toFixed(1)+'/10</span></span>':'';
|
|
5149
|
-
const
|
|
5886
|
+
const detailSkillIsLocalShared=skill.visibility==='public';
|
|
5887
|
+
const detailSkillIsTeamShared=!!skill.sharingVisibility;
|
|
5888
|
+
const detailSkillScope=detailSkillIsTeamShared?'team':detailSkillIsLocalShared?'local':'private';
|
|
5150
5889
|
document.getElementById('skillDetailMeta').innerHTML=[
|
|
5151
5890
|
'<span class="meta-item"><span class="skill-badge version">v'+skill.version+'</span></span>',
|
|
5152
5891
|
'<span class="meta-item"><span class="skill-badge status-'+skill.status+'">'+t('skills.status.'+skill.status)+'</span></span>',
|
|
5153
|
-
visMeta,
|
|
5154
5892
|
qsBadge,
|
|
5155
5893
|
skill.installed?'<span class="meta-item"><span class="skill-badge installed">'+t('skills.installed.badge')+'</span></span>':'',
|
|
5156
5894
|
'<span class="meta-item">\\u{1F4C5} '+formatTime(skill.createdAt)+'</span>',
|
|
5157
5895
|
'<span class="meta-item">\\u270F '+t('skills.updated')+formatTime(skill.updatedAt)+'</span>',
|
|
5158
5896
|
].filter(Boolean).join('');
|
|
5159
5897
|
|
|
5898
|
+
var scopeBadgeEl=document.getElementById('skillScopeBadge');
|
|
5899
|
+
if(scopeBadgeEl) scopeBadgeEl.innerHTML=renderScopeBadge(detailSkillScope);
|
|
5900
|
+
|
|
5160
5901
|
const visBtn=document.getElementById('skillVisibilityBtn');
|
|
5161
5902
|
visBtn.className='skill-vis-btn';
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
visBtn.dataset.vis='public';
|
|
5166
|
-
} else {
|
|
5167
|
-
visBtn.textContent='\\u{1F310} '+t('skills.setPublic');
|
|
5168
|
-
visBtn.classList.add('is-private');
|
|
5169
|
-
visBtn.dataset.vis='private';
|
|
5170
|
-
}
|
|
5903
|
+
visBtn.textContent='\\u270F '+t('share.shareBtn');
|
|
5904
|
+
visBtn.dataset.vis=detailSkillScope;
|
|
5905
|
+
visBtn.onclick=function(){openSkillScopeModal();};
|
|
5171
5906
|
|
|
5172
5907
|
document.getElementById('skillDetailDesc').textContent=skill.description;
|
|
5173
5908
|
currentSkillDetail=skill;
|
|
5174
|
-
renderSkillShareActions(skill);
|
|
5175
5909
|
|
|
5176
5910
|
if(files.length>0){
|
|
5177
5911
|
const fileIcons={'skill':'\\u{1F4D6}','script':'\\u{2699}','reference':'\\u{1F4CE}','file':'\\u{1F4C4}'};
|
|
@@ -5577,7 +6311,7 @@ async function saveModelsConfig(){
|
|
|
5577
6311
|
if(!hasSkillConfig){msgs.push(t('settings.save.skill.fallback'));}
|
|
5578
6312
|
var fbInfo=fb.available?(fb.model+' ('+fb.baseUrl+')'):t('settings.save.fallback.none');
|
|
5579
6313
|
var confirmMsg=msgs.join('\\n')+'\\n\\n'+t('settings.save.fallback.model')+fbInfo+'\\n\\n'+t('settings.save.fallback.confirm');
|
|
5580
|
-
if(!
|
|
6314
|
+
if(!(await confirmModal(confirmMsg))){done();return;}
|
|
5581
6315
|
}catch(e){}
|
|
5582
6316
|
}
|
|
5583
6317
|
|
|
@@ -5631,7 +6365,7 @@ async function saveHubConfig(){
|
|
|
5631
6365
|
var prevRole=sharingStatusCache&&sharingStatusCache.role;
|
|
5632
6366
|
if(prevSharingEnabled&&!sharingEnabled){
|
|
5633
6367
|
var confirmMsg=prevRole==='hub'?t('sharing.disable.confirm.hub'):t('sharing.disable.confirm.client');
|
|
5634
|
-
if(!
|
|
6368
|
+
if(!(await confirmModal(confirmMsg,{danger:true}))){done();return;}
|
|
5635
6369
|
}
|
|
5636
6370
|
|
|
5637
6371
|
var ok=await doSaveConfig(cfg, saveBtn, 'hubSaved');
|
|
@@ -5734,7 +6468,7 @@ function closeSkillDetail(event){
|
|
|
5734
6468
|
}
|
|
5735
6469
|
|
|
5736
6470
|
async function deleteSkill(skillId){
|
|
5737
|
-
if(!
|
|
6471
|
+
if(!(await confirmModal(t('skill.delete.confirm'),{danger:true}))) return;
|
|
5738
6472
|
try{
|
|
5739
6473
|
const r=await fetch('/api/skill/'+skillId,{method:'DELETE'});
|
|
5740
6474
|
const d=await r.json();
|
|
@@ -5762,6 +6496,19 @@ function formatTime(ts){
|
|
|
5762
6496
|
return new Date(ts).toLocaleString(dateLoc(),{month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit'});
|
|
5763
6497
|
}
|
|
5764
6498
|
|
|
6499
|
+
function formatDateTimeSeconds(ts){
|
|
6500
|
+
if(!ts) return '-';
|
|
6501
|
+
return new Date(ts).toLocaleString(dateLoc(),{
|
|
6502
|
+
year:'numeric',
|
|
6503
|
+
month:'2-digit',
|
|
6504
|
+
day:'2-digit',
|
|
6505
|
+
hour:'2-digit',
|
|
6506
|
+
minute:'2-digit',
|
|
6507
|
+
second:'2-digit',
|
|
6508
|
+
hour12:false
|
|
6509
|
+
});
|
|
6510
|
+
}
|
|
6511
|
+
|
|
5765
6512
|
function fillDays(rows,days){
|
|
5766
6513
|
const map=new Map((rows||[]).map(r=>[r.date,{...r}]));
|
|
5767
6514
|
const out=[];const now=new Date();
|
|
@@ -5846,19 +6593,43 @@ function renderChartCalls(rows){
|
|
|
5846
6593
|
|
|
5847
6594
|
/* ─── Tool Performance Chart ─── */
|
|
5848
6595
|
let toolMinutes=60;
|
|
6596
|
+
let toolCustomFrom='';
|
|
6597
|
+
let toolCustomTo='';
|
|
5849
6598
|
const TOOL_COLORS=['#818cf8','#34d399','#fbbf24','#f87171','#38bdf8','#a78bfa','#fb923c'];
|
|
5850
6599
|
|
|
5851
6600
|
function setToolMinutes(m){
|
|
5852
6601
|
toolMinutes=m;
|
|
6602
|
+
toolCustomFrom='';
|
|
6603
|
+
toolCustomTo='';
|
|
6604
|
+
var fi=document.getElementById('toolRangeFrom');
|
|
6605
|
+
var ti=document.getElementById('toolRangeTo');
|
|
6606
|
+
if(fi) fi.value='';
|
|
6607
|
+
if(ti) ti.value='';
|
|
5853
6608
|
document.querySelectorAll('.tool-range').forEach(b=>{
|
|
5854
6609
|
b.classList.toggle('active',Number(b.dataset.mins)===m);
|
|
5855
6610
|
});
|
|
5856
6611
|
loadToolMetrics();
|
|
5857
6612
|
}
|
|
5858
6613
|
|
|
6614
|
+
function applyCustomToolRange(){
|
|
6615
|
+
var fi=document.getElementById('toolRangeFrom');
|
|
6616
|
+
var ti=document.getElementById('toolRangeTo');
|
|
6617
|
+
var from=fi?fi.value:'';
|
|
6618
|
+
var to=ti?ti.value:'';
|
|
6619
|
+
if(!from&&!to){toast(t('chart.selectRange'),'warn');return;}
|
|
6620
|
+
toolCustomFrom=from;
|
|
6621
|
+
toolCustomTo=to;
|
|
6622
|
+
document.querySelectorAll('.tool-range').forEach(b=>b.classList.remove('active'));
|
|
6623
|
+
loadToolMetrics();
|
|
6624
|
+
}
|
|
6625
|
+
|
|
5859
6626
|
async function loadToolMetrics(){
|
|
5860
6627
|
try{
|
|
5861
|
-
|
|
6628
|
+
var qs='minutes='+toolMinutes;
|
|
6629
|
+
if(toolCustomFrom) qs='from='+encodeURIComponent(new Date(toolCustomFrom).toISOString());
|
|
6630
|
+
if(toolCustomTo) qs+='&to='+encodeURIComponent(new Date(toolCustomTo).toISOString());
|
|
6631
|
+
else if(toolCustomFrom) qs+='&to='+encodeURIComponent(new Date().toISOString());
|
|
6632
|
+
const r=await fetch('/api/tool-metrics?'+qs);
|
|
5862
6633
|
if(!r.ok) return;
|
|
5863
6634
|
const d=await r.json();
|
|
5864
6635
|
if(d.error) return;
|
|
@@ -6032,6 +6803,147 @@ function stopSharingPoll(){
|
|
|
6032
6803
|
if(_sharingPollTimer){clearInterval(_sharingPollTimer);_sharingPollTimer=null;}
|
|
6033
6804
|
}
|
|
6034
6805
|
|
|
6806
|
+
/* ─── Notifications ─── */
|
|
6807
|
+
var _notifCache=[];
|
|
6808
|
+
var _notifUnread=0;
|
|
6809
|
+
var _notifPollTimer=null;
|
|
6810
|
+
var _notifPanelOpen=false;
|
|
6811
|
+
|
|
6812
|
+
function toggleNotifPanel(e){
|
|
6813
|
+
if(e)e.stopPropagation();
|
|
6814
|
+
var panel=document.getElementById('notifPanel');
|
|
6815
|
+
_notifPanelOpen=!_notifPanelOpen;
|
|
6816
|
+
if(_notifPanelOpen){
|
|
6817
|
+
panel.classList.add('show');
|
|
6818
|
+
loadNotifications();
|
|
6819
|
+
}else{
|
|
6820
|
+
panel.classList.remove('show');
|
|
6821
|
+
}
|
|
6822
|
+
}
|
|
6823
|
+
|
|
6824
|
+
document.addEventListener('click',function(e){
|
|
6825
|
+
if(!_notifPanelOpen) return;
|
|
6826
|
+
var wrap=document.getElementById('notifBellWrap');
|
|
6827
|
+
if(wrap&&!wrap.contains(e.target)){
|
|
6828
|
+
_notifPanelOpen=false;
|
|
6829
|
+
document.getElementById('notifPanel').classList.remove('show');
|
|
6830
|
+
}
|
|
6831
|
+
});
|
|
6832
|
+
|
|
6833
|
+
function notifTimeAgo(ts){
|
|
6834
|
+
var diff=Date.now()-ts;
|
|
6835
|
+
if(diff<60000) return t('notif.timeAgo.just');
|
|
6836
|
+
if(diff<3600000) return t('notif.timeAgo.min').replace('{n}',Math.floor(diff/60000));
|
|
6837
|
+
if(diff<86400000) return t('notif.timeAgo.hour').replace('{n}',Math.floor(diff/3600000));
|
|
6838
|
+
return t('notif.timeAgo.day').replace('{n}',Math.floor(diff/86400000));
|
|
6839
|
+
}
|
|
6840
|
+
|
|
6841
|
+
function notifIcon(resource){
|
|
6842
|
+
if(resource==='memory') return '\\u{1F4DD}';
|
|
6843
|
+
if(resource==='task') return '\\u{1F4CB}';
|
|
6844
|
+
if(resource==='skill') return '\\u{1F9E0}';
|
|
6845
|
+
return '\\u{1F514}';
|
|
6846
|
+
}
|
|
6847
|
+
|
|
6848
|
+
function notifTypeText(n){
|
|
6849
|
+
if(n.type==='resource_removed'){
|
|
6850
|
+
return t('notif.removed.'+n.resource)||t('notif.removed.memory');
|
|
6851
|
+
}
|
|
6852
|
+
return n.message||n.type;
|
|
6853
|
+
}
|
|
6854
|
+
|
|
6855
|
+
async function loadNotifications(){
|
|
6856
|
+
try{
|
|
6857
|
+
var r=await fetch('/api/sharing/notifications');
|
|
6858
|
+
var d=await r.json();
|
|
6859
|
+
_notifCache=d.notifications||[];
|
|
6860
|
+
_notifUnread=d.unreadCount||0;
|
|
6861
|
+
renderNotifBadge();
|
|
6862
|
+
renderNotifPanel();
|
|
6863
|
+
}catch(e){}
|
|
6864
|
+
}
|
|
6865
|
+
|
|
6866
|
+
async function pollNotifCount(){
|
|
6867
|
+
try{
|
|
6868
|
+
var r=await fetch('/api/sharing/notifications?unread=1');
|
|
6869
|
+
var d=await r.json();
|
|
6870
|
+
_notifUnread=d.unreadCount||0;
|
|
6871
|
+
renderNotifBadge();
|
|
6872
|
+
}catch(e){}
|
|
6873
|
+
}
|
|
6874
|
+
|
|
6875
|
+
function renderNotifBadge(){
|
|
6876
|
+
var badge=document.getElementById('notifBadge');
|
|
6877
|
+
if(!badge) return;
|
|
6878
|
+
if(_notifUnread>0){
|
|
6879
|
+
badge.textContent=_notifUnread>99?'99+':_notifUnread;
|
|
6880
|
+
badge.classList.add('show');
|
|
6881
|
+
}else{
|
|
6882
|
+
badge.classList.remove('show');
|
|
6883
|
+
}
|
|
6884
|
+
}
|
|
6885
|
+
|
|
6886
|
+
function renderNotifPanel(){
|
|
6887
|
+
var body=document.getElementById('notifPanelBody');
|
|
6888
|
+
if(!body) return;
|
|
6889
|
+
if(_notifCache.length===0){
|
|
6890
|
+
body.innerHTML='<div class="notif-empty">'+t('notif.empty')+'</div>';
|
|
6891
|
+
return;
|
|
6892
|
+
}
|
|
6893
|
+
body.innerHTML=_notifCache.map(function(n){
|
|
6894
|
+
var cls='notif-item'+(n.read?'':' unread');
|
|
6895
|
+
return '<div class="'+cls+'" onclick="markNotifRead("'+esc(n.id)+'")">'+
|
|
6896
|
+
'<div class="notif-item-icon">'+notifIcon(n.resource)+'</div>'+
|
|
6897
|
+
'<div class="notif-item-body">'+
|
|
6898
|
+
'<div class="notif-item-title">'+esc(notifTypeText(n))+'</div>'+
|
|
6899
|
+
'<div class="notif-item-name">'+esc(n.title)+'</div>'+
|
|
6900
|
+
'<div class="notif-item-time">'+notifTimeAgo(n.createdAt)+'</div>'+
|
|
6901
|
+
'</div>'+
|
|
6902
|
+
'<div class="notif-item-dot"></div>'+
|
|
6903
|
+
'</div>';
|
|
6904
|
+
}).join('');
|
|
6905
|
+
}
|
|
6906
|
+
|
|
6907
|
+
async function markNotifRead(id){
|
|
6908
|
+
try{
|
|
6909
|
+
await fetch('/api/sharing/notifications/read',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({ids:[id]})});
|
|
6910
|
+
_notifCache.forEach(function(n){if(n.id===id)n.read=true;});
|
|
6911
|
+
_notifUnread=Math.max(0,_notifUnread-1);
|
|
6912
|
+
renderNotifBadge();
|
|
6913
|
+
renderNotifPanel();
|
|
6914
|
+
}catch(e){}
|
|
6915
|
+
}
|
|
6916
|
+
|
|
6917
|
+
async function markAllNotifsRead(){
|
|
6918
|
+
try{
|
|
6919
|
+
await fetch('/api/sharing/notifications/read',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({all:true})});
|
|
6920
|
+
_notifCache.forEach(function(n){n.read=true;});
|
|
6921
|
+
_notifUnread=0;
|
|
6922
|
+
renderNotifBadge();
|
|
6923
|
+
renderNotifPanel();
|
|
6924
|
+
}catch(e){}
|
|
6925
|
+
}
|
|
6926
|
+
|
|
6927
|
+
async function clearAllNotifs(){
|
|
6928
|
+
try{
|
|
6929
|
+
await fetch('/api/sharing/notifications/clear',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({})});
|
|
6930
|
+
_notifCache=[];
|
|
6931
|
+
_notifUnread=0;
|
|
6932
|
+
renderNotifBadge();
|
|
6933
|
+
renderNotifPanel();
|
|
6934
|
+
}catch(e){}
|
|
6935
|
+
}
|
|
6936
|
+
|
|
6937
|
+
function startNotifPoll(){
|
|
6938
|
+
if(_notifPollTimer) return;
|
|
6939
|
+
pollNotifCount();
|
|
6940
|
+
_notifPollTimer=setInterval(pollNotifCount,15000);
|
|
6941
|
+
}
|
|
6942
|
+
|
|
6943
|
+
function stopNotifPoll(){
|
|
6944
|
+
if(_notifPollTimer){clearInterval(_notifPollTimer);_notifPollTimer=null;}
|
|
6945
|
+
}
|
|
6946
|
+
|
|
6035
6947
|
/* ─── Data loading ─── */
|
|
6036
6948
|
async function loadAll(){
|
|
6037
6949
|
await Promise.all([loadStats(),loadMemories(),loadSharingStatus(false)]);
|
|
@@ -6039,6 +6951,7 @@ async function loadAll(){
|
|
|
6039
6951
|
connectPPSSE();
|
|
6040
6952
|
checkForUpdate();
|
|
6041
6953
|
startSharingPoll();
|
|
6954
|
+
startNotifPoll();
|
|
6042
6955
|
}
|
|
6043
6956
|
|
|
6044
6957
|
async function loadStats(ownerFilter){
|
|
@@ -6320,9 +7233,10 @@ function renderMemories(items){
|
|
|
6320
7233
|
const importBadge=isImported?'<span class="import-badge">\u{1F990} '+t('card.imported')+'</span>':'';
|
|
6321
7234
|
const ownerVal=m.owner||'agent:main';
|
|
6322
7235
|
const isPublicMem=ownerVal==='public';
|
|
6323
|
-
const
|
|
7236
|
+
const localManaged=!!m.localSharingManaged;
|
|
6324
7237
|
const memShared=m.sharingVisibility||null;
|
|
6325
|
-
const
|
|
7238
|
+
const memScope=memShared?'team':isPublicMem?'local':'private';
|
|
7239
|
+
const memScopeBadge=renderScopeBadge(memScope);
|
|
6326
7240
|
let dedupInfo='';
|
|
6327
7241
|
if(ds==='duplicate'||ds==='merged'){
|
|
6328
7242
|
const reason=m.dedup_reason?'<span style="font-size:11px;color:var(--text-muted)">'+t('card.dedupReason')+esc(m.dedup_reason)+'</span>':'';
|
|
@@ -6347,7 +7261,7 @@ function renderMemories(items){
|
|
|
6347
7261
|
}catch(e){}
|
|
6348
7262
|
}
|
|
6349
7263
|
return '<div class="memory-card'+(isInactive?' dedup-inactive':'')+'">'+
|
|
6350
|
-
'<div class="card-header"><div class="meta"><span class="role-tag '+role+'">'+role+'</span>'+
|
|
7264
|
+
'<div class="card-header"><div class="meta"><span class="role-tag '+role+'">'+role+'</span>'+memScopeBadge+importBadge+dedupBadge+mergeBadge+'</div><span class="card-time"><span class="session-tag" title="'+esc(sid)+'">'+esc(sidShort)+'</span> '+time+updatedAt+'</span></div>'+
|
|
6351
7265
|
'<div class="card-summary">'+cardTitle+'</div>'+
|
|
6352
7266
|
(function(){
|
|
6353
7267
|
if(mc<=0) return '';
|
|
@@ -6371,14 +7285,37 @@ function renderMemories(items){
|
|
|
6371
7285
|
'<button class="btn btn-sm btn-ghost" onclick="toggleContent(\\''+id+'\\')">'+t('card.expand')+'</button>'+
|
|
6372
7286
|
(mc>0?'<button class="btn btn-sm btn-ghost" onclick="toggleHistory(\\''+id+'\\')">'+t('card.evolveHistory')+'</button>':'')+
|
|
6373
7287
|
'<button class="btn btn-sm btn-ghost" onclick="openEditModal(\\''+id+'\\')">'+t('card.edit')+'</button>'+
|
|
6374
|
-
(
|
|
6375
|
-
|
|
7288
|
+
(isInactive
|
|
7289
|
+
?'<button class="btn btn-sm btn-ghost" style="opacity:0.45;cursor:not-allowed" onclick="toast(t(\\x27share.scope.inactiveDisabled\\x27),\\x27warn\\x27)">\\u270F '+t('share.shareBtn')+'</button>'
|
|
7290
|
+
:'<button class="btn btn-sm btn-ghost" onclick="openMemoryScopeModal(\\''+id+'\\',\\''+memScope+'\\')">\\u270F '+t('share.shareBtn')+'</button>')+
|
|
6376
7291
|
'<button class="btn btn-sm btn-ghost" style="color:var(--accent)" onclick="deleteMemory(\\''+id+'\\')">'+t('card.delete')+'</button>'+
|
|
6377
7292
|
vscore+
|
|
6378
7293
|
'</div></div>';
|
|
6379
7294
|
}).join('');
|
|
6380
7295
|
}
|
|
6381
7296
|
|
|
7297
|
+
function updateMemoryCardBadge(chunkId,newScope){
|
|
7298
|
+
var cards=document.querySelectorAll('.memory-card');
|
|
7299
|
+
for(var i=0;i<cards.length;i++){
|
|
7300
|
+
var btns=cards[i].querySelectorAll('button');
|
|
7301
|
+
var shareBtn=null;
|
|
7302
|
+
for(var b=0;b<btns.length;b++){
|
|
7303
|
+
var oc=btns[b].getAttribute('onclick')||'';
|
|
7304
|
+
if(oc.indexOf('openMemoryScopeModal')>=0&&oc.indexOf(chunkId)>=0){shareBtn=btns[b];break;}
|
|
7305
|
+
}
|
|
7306
|
+
if(!shareBtn) continue;
|
|
7307
|
+
var metaDiv=cards[i].querySelector('.meta');
|
|
7308
|
+
if(!metaDiv) continue;
|
|
7309
|
+
var oldBadge=metaDiv.querySelectorAll('span[style*="border-radius:10px"]');
|
|
7310
|
+
for(var j=0;j<oldBadge.length;j++) oldBadge[j].remove();
|
|
7311
|
+
var roleTag=metaDiv.querySelector('.role-tag');
|
|
7312
|
+
if(roleTag&&roleTag.nextSibling) roleTag.insertAdjacentHTML('afterend',renderScopeBadge(newScope));
|
|
7313
|
+
else metaDiv.insertAdjacentHTML('beforeend',renderScopeBadge(newScope));
|
|
7314
|
+
shareBtn.setAttribute('onclick','openMemoryScopeModal(\\x27'+chunkId+'\\x27,\\x27'+newScope+'\\x27)');
|
|
7315
|
+
break;
|
|
7316
|
+
}
|
|
7317
|
+
}
|
|
7318
|
+
|
|
6382
7319
|
function renderPagination(){
|
|
6383
7320
|
const el=document.getElementById('pagination');
|
|
6384
7321
|
if(totalPages<=1){el.innerHTML='';return}
|
|
@@ -6459,13 +7396,13 @@ async function showMemoryModal(chunkId){
|
|
|
6459
7396
|
if(m.summary) h+='<div class="mm-summary">'+esc(m.summary)+'</div>';
|
|
6460
7397
|
h+='</div>';
|
|
6461
7398
|
if(m.content){
|
|
6462
|
-
h+='<div class="mm-section"><div class="mm-section-label">
|
|
7399
|
+
h+='<div class="mm-section"><div class="mm-section-label">'+t('admin.content')+'</div><pre class="mm-content">'+esc(m.content)+'</pre></div>';
|
|
6463
7400
|
}
|
|
6464
7401
|
h+='<div class="mm-meta">';
|
|
6465
|
-
if(m.session_key) h+='<div class="mm-meta-chip"><strong>
|
|
7402
|
+
if(m.session_key) h+='<div class="mm-meta-chip"><strong>'+t('admin.session')+'</strong><span>'+esc(m.session_key.slice(0,12))+'</span></div>';
|
|
6466
7403
|
h+='<div class="mm-meta-chip"><strong>'+t('memory.detail.created')+'</strong><span>'+fmtModalDate(m.created_at)+'</span></div>';
|
|
6467
7404
|
if(m.updated_at) h+='<div class="mm-meta-chip"><strong>'+t('memory.detail.updated')+'</strong><span>'+fmtModalDate(m.updated_at)+'</span></div>';
|
|
6468
|
-
if(m.kind) h+='<div class="mm-meta-chip"><strong>
|
|
7405
|
+
if(m.kind) h+='<div class="mm-meta-chip"><strong>'+t('admin.kind')+'</strong><span>'+esc(m.kind)+'</span></div>';
|
|
6469
7406
|
h+='</div>';
|
|
6470
7407
|
if(m.dedup_reason){
|
|
6471
7408
|
h+='<div class="mm-dedup"><div class="mm-dedup-box">'+esc(m.dedup_reason)+'</div></div>';
|
|
@@ -6548,27 +7485,46 @@ async function submitModal(){
|
|
|
6548
7485
|
}
|
|
6549
7486
|
|
|
6550
7487
|
async function deleteMemory(id){
|
|
6551
|
-
if(!
|
|
7488
|
+
if(!(await confirmModal(t('confirm.delete'),{danger:true})))return;
|
|
6552
7489
|
const r=await fetch('/api/memory/'+id,{method:'DELETE'});
|
|
6553
7490
|
const d=await r.json();
|
|
6554
7491
|
if(d.ok){toast(t('toast.deleted'),'success');loadAll();}
|
|
6555
7492
|
else{toast(t('toast.delfail'),'error')}
|
|
6556
7493
|
}
|
|
6557
7494
|
|
|
7495
|
+
function openMemoryScopeModal(id,currentScope){
|
|
7496
|
+
openScopeSelectorModal('memory',id,currentScope,function(newScope){
|
|
7497
|
+
if(memoryCache[id]){
|
|
7498
|
+
if(newScope==='team'){memoryCache[id].sharingVisibility='public';}
|
|
7499
|
+
else if(newScope==='local'){memoryCache[id].sharingVisibility=null;memoryCache[id].owner='public';}
|
|
7500
|
+
else{memoryCache[id].sharingVisibility=null;memoryCache[id].owner='agent:main';}
|
|
7501
|
+
}
|
|
7502
|
+
updateMemoryCardBadge(id,newScope);
|
|
7503
|
+
});
|
|
7504
|
+
}
|
|
7505
|
+
|
|
6558
7506
|
async function toggleMemoryPublic(id,setPublic){
|
|
6559
|
-
const newOwner=setPublic?'public':'agent:main';
|
|
6560
7507
|
try{
|
|
6561
|
-
|
|
7508
|
+
if(!setPublic && !(await confirmModal(t('share.memoryLocalUnshareConfirm'),{danger:true}))) return;
|
|
7509
|
+
const memory=memoryCache[id]||{};
|
|
7510
|
+
const url=setPublic?'/api/memories/share-local':'/api/memories/unshare-local';
|
|
7511
|
+
const body=setPublic?{chunkId:id}:{chunkId:id,privateOwner:memory.localOriginalOwner||undefined};
|
|
7512
|
+
const r=await fetch(url,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
|
|
6562
7513
|
const d=await r.json();
|
|
6563
|
-
if(d.ok){
|
|
6564
|
-
|
|
6565
|
-
|
|
7514
|
+
if(d.ok){
|
|
7515
|
+
toast(setPublic?t('toast.setPublic'):t('toast.setPrivate'),'success');
|
|
7516
|
+
var newScope=setPublic?'local':'private';
|
|
7517
|
+
if(memoryCache[id]){memoryCache[id].owner=setPublic?'public':'agent:main';}
|
|
7518
|
+
updateMemoryCardBadge(id,newScope);
|
|
7519
|
+
}
|
|
7520
|
+
else{toast(localMemoryErrorMessage(d.error),'error')}
|
|
7521
|
+
}catch(e){toast(localMemoryErrorMessage(e.message),'error')}
|
|
6566
7522
|
}
|
|
6567
7523
|
|
|
6568
7524
|
async function clearAll(){
|
|
6569
7525
|
try{
|
|
6570
|
-
if(!
|
|
6571
|
-
if(!
|
|
7526
|
+
if(!(await confirmModal(t('confirm.clearall'),{danger:true})))return;
|
|
7527
|
+
if(!(await confirmModal(t('confirm.clearall2'),{danger:true})))return;
|
|
6572
7528
|
const r=await fetch('/api/memories',{method:'DELETE'});
|
|
6573
7529
|
if(r.status===401){toast(t('settings.session.expired'),'error');return;}
|
|
6574
7530
|
const d=await r.json();
|
|
@@ -6670,14 +7626,14 @@ async function migrateScan(showToast){
|
|
|
6670
7626
|
}
|
|
6671
7627
|
}
|
|
6672
7628
|
|
|
6673
|
-
function migrateStart(){
|
|
7629
|
+
async function migrateStart(){
|
|
6674
7630
|
const isResume=document.getElementById('migrateStartBtn').textContent===t('migrate.resume');
|
|
6675
7631
|
if(!isResume){
|
|
6676
7632
|
if(!migrateScanData||!migrateScanData.configReady){
|
|
6677
7633
|
toast(t('migrate.scan.required'),'error');
|
|
6678
7634
|
return;
|
|
6679
7635
|
}
|
|
6680
|
-
if(!
|
|
7636
|
+
if(!(await confirmModal(t('migrate.start')+'?')))return;
|
|
6681
7637
|
}
|
|
6682
7638
|
|
|
6683
7639
|
const concSel=document.getElementById('migrateConcurrency');
|
|
@@ -7177,12 +8133,32 @@ function toast(msg,type='info'){
|
|
|
7177
8133
|
const c=document.getElementById('toasts');
|
|
7178
8134
|
const t=document.createElement('div');
|
|
7179
8135
|
t.className='toast '+type;
|
|
7180
|
-
const icons={success:'\\u2705',error:'\\u274C',info:'\\u2139\\uFE0F'};
|
|
8136
|
+
const icons={success:'\\u2705',error:'\\u274C',info:'\\u2139\\uFE0F',warn:'\\u26A0\\uFE0F'};
|
|
7181
8137
|
t.innerHTML=(icons[type]||'')+' '+esc(msg);
|
|
7182
8138
|
c.appendChild(t);
|
|
7183
8139
|
setTimeout(()=>t.remove(),3500);
|
|
7184
8140
|
}
|
|
7185
8141
|
|
|
8142
|
+
var _confirmResolve=null;
|
|
8143
|
+
function confirmModal(message,opts){
|
|
8144
|
+
opts=opts||{};
|
|
8145
|
+
return new Promise(function(resolve){
|
|
8146
|
+
_confirmResolve=resolve;
|
|
8147
|
+
var overlay=document.getElementById('confirmOverlay');
|
|
8148
|
+
document.getElementById('confirmTitle').textContent=opts.title||t('confirm.title')||'\u786E\u8BA4';
|
|
8149
|
+
document.getElementById('confirmBody').textContent=message||'';
|
|
8150
|
+
var okBtn=document.getElementById('confirmOkBtn');
|
|
8151
|
+
okBtn.textContent=opts.okText||t('confirm.ok')||'\u786E\u5B9A';
|
|
8152
|
+
okBtn.className='btn-confirm-ok'+(opts.danger?' danger':'');
|
|
8153
|
+
document.getElementById('confirmCancelBtn').textContent=opts.cancelText||t('confirm.cancel')||'\u53D6\u6D88';
|
|
8154
|
+
overlay.classList.add('show');
|
|
8155
|
+
});
|
|
8156
|
+
}
|
|
8157
|
+
function confirmModalClose(result){
|
|
8158
|
+
document.getElementById('confirmOverlay').classList.remove('show');
|
|
8159
|
+
if(_confirmResolve){var r=_confirmResolve;_confirmResolve=null;r(result);}
|
|
8160
|
+
}
|
|
8161
|
+
|
|
7186
8162
|
/* ─── Theme ─── */
|
|
7187
8163
|
const VIEWER_THEME_KEY='memos-viewer-theme';
|
|
7188
8164
|
function initViewerTheme(){const s=localStorage.getItem(VIEWER_THEME_KEY);const theme=(s==='light'||s==='dark')?s:'dark';document.documentElement.setAttribute('data-theme',theme);}
|
|
@@ -7247,30 +8223,34 @@ async function checkForUpdate(){
|
|
|
7247
8223
|
const pkgSpec=d.installCommand?d.installCommand.replace(/^(?:npx\s+)?openclaw\s+plugins\s+install\s+/,''):(d.packageName+'@'+d.latest);
|
|
7248
8224
|
var banner=document.createElement('div');
|
|
7249
8225
|
banner.id='updateBanner';
|
|
7250
|
-
banner.style.cssText='display:flex;align-items:center;gap:
|
|
8226
|
+
banner.style.cssText='display:flex;align-items:center;gap:12px;padding:10px max(32px,calc((100% - 1400px)/2 + 32px));font-size:13px;font-weight:500;box-sizing:border-box;animation:slideIn .3s ease;background:linear-gradient(135deg,rgba(99,102,241,.08),rgba(139,92,246,.06));color:var(--pri);border-bottom:1px solid rgba(99,102,241,.18);backdrop-filter:blur(8px)';
|
|
7251
8227
|
var textNode=document.createElement('div');
|
|
7252
|
-
textNode.style.cssText='display:flex;align-items:center;gap:8px;flex-shrink:0';
|
|
7253
|
-
textNode.innerHTML='
|
|
8228
|
+
textNode.style.cssText='display:flex;align-items:center;gap:8px;flex-shrink:0;font-size:13px';
|
|
8229
|
+
textNode.innerHTML='<span style="font-size:15px">\u2728</span> '+t('update.available')+' <span style="padding:2px 8px;border-radius:6px;background:rgba(99,102,241,.1);font-size:12px;font-weight:600">v'+esc(d.current)+'</span> <span style="opacity:.5">\u2192</span> <span style="padding:2px 8px;border-radius:6px;background:rgba(52,211,153,.12);color:var(--green);font-size:12px;font-weight:600">v'+esc(d.latest)+'</span>';
|
|
7254
8230
|
var btnUpdate=document.createElement('button');
|
|
7255
|
-
btnUpdate.
|
|
8231
|
+
btnUpdate.style.cssText='background:var(--pri);color:#fff;border:none;border-radius:8px;padding:5px 16px;font-size:12px;font-weight:600;cursor:pointer;white-space:nowrap;transition:opacity .15s,transform .1s;margin-left:4px';
|
|
7256
8232
|
btnUpdate.textContent=t('update.btn');
|
|
8233
|
+
btnUpdate.onmouseenter=function(){this.style.opacity='.9';this.style.transform='scale(1.02)'};
|
|
8234
|
+
btnUpdate.onmouseleave=function(){this.style.opacity='1';this.style.transform='scale(1)'};
|
|
7257
8235
|
var statusDiv=document.createElement('div');
|
|
7258
|
-
statusDiv.style.cssText='font-size:11px;opacity:.
|
|
8236
|
+
statusDiv.style.cssText='font-size:11px;opacity:.7;flex-shrink:0';
|
|
7259
8237
|
btnUpdate.onclick=function(){doUpdateInstall(pkgSpec,btnUpdate,statusDiv)};
|
|
7260
8238
|
textNode.appendChild(btnUpdate);
|
|
7261
8239
|
var spacer=document.createElement('div');
|
|
7262
8240
|
spacer.style.cssText='flex:1';
|
|
7263
8241
|
var btnClose=document.createElement('button');
|
|
7264
|
-
btnClose.
|
|
8242
|
+
btnClose.style.cssText='background:none;border:none;font-size:16px;color:var(--text-muted);cursor:pointer;opacity:.5;padding:0 2px;line-height:1;transition:opacity .15s';
|
|
7265
8243
|
btnClose.innerHTML='×';
|
|
8244
|
+
btnClose.onmouseenter=function(){this.style.opacity='1'};
|
|
8245
|
+
btnClose.onmouseleave=function(){this.style.opacity='.5'};
|
|
7266
8246
|
btnClose.onclick=function(){banner.remove()};
|
|
7267
8247
|
banner.appendChild(textNode);
|
|
7268
8248
|
banner.appendChild(statusDiv);
|
|
7269
8249
|
banner.appendChild(spacer);
|
|
7270
8250
|
banner.appendChild(btnClose);
|
|
7271
|
-
var
|
|
7272
|
-
if(
|
|
7273
|
-
else{
|
|
8251
|
+
var tb=document.querySelector('.topbar');
|
|
8252
|
+
if(tb&&tb.parentNode){tb.parentNode.insertBefore(banner,tb);}
|
|
8253
|
+
else{document.body.insertBefore(banner,document.body.firstChild);}
|
|
7274
8254
|
}catch(e){}
|
|
7275
8255
|
}
|
|
7276
8256
|
|
|
@@ -7292,6 +8272,16 @@ checkAuth();
|
|
|
7292
8272
|
</div>
|
|
7293
8273
|
</div>
|
|
7294
8274
|
|
|
8275
|
+
<div class="confirm-overlay" id="confirmOverlay" onclick="confirmModalClose(false)">
|
|
8276
|
+
<div class="confirm-panel" onclick="event.stopPropagation()">
|
|
8277
|
+
<div class="confirm-panel-header" id="confirmTitle"></div>
|
|
8278
|
+
<div class="confirm-panel-body" id="confirmBody"></div>
|
|
8279
|
+
<div class="confirm-panel-footer">
|
|
8280
|
+
<button class="btn-confirm-cancel" id="confirmCancelBtn" onclick="confirmModalClose(false)"></button>
|
|
8281
|
+
<button class="btn-confirm-ok" id="confirmOkBtn" onclick="confirmModalClose(true)"></button>
|
|
8282
|
+
</div>
|
|
8283
|
+
</div>
|
|
8284
|
+
</div>
|
|
7295
8285
|
</body>
|
|
7296
8286
|
</html>`;
|
|
7297
8287
|
}
|