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