@memtensor/memos-local-openclaw-plugin 1.0.4-beta.8 → 1.0.4-beta.9

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.
@@ -194,65 +194,90 @@ input,textarea,select{font-family:inherit;font-size:inherit}
194
194
  .pending-user-name{font-size:14px;font-weight:700;color:var(--text)}
195
195
  .pending-user-meta{font-size:12px;color:var(--text-sec);margin-top:4px}
196
196
  .pending-user-actions{display:flex;gap:8px;margin-top:10px}
197
- /* ─── Admin Panel ─── */
198
- .admin-header{position:relative;padding:28px 28px 20px;background:linear-gradient(135deg,rgba(99,102,241,.08) 0%,rgba(139,92,246,.06) 50%,rgba(6,182,212,.05) 100%);border:1px solid rgba(99,102,241,.12);border-radius:16px;margin-bottom:20px;overflow:hidden}
199
- .admin-header::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--violet),var(--pri),var(--cyan));border-radius:16px 16px 0 0}
200
- .admin-header-top{display:flex;justify-content:space-between;align-items:center}
201
- .admin-header h2{font-size:20px;font-weight:800;color:var(--text);display:flex;align-items:center;gap:10px;margin:0}
202
- .admin-header h2 .ah-icon{width:36px;height:36px;border-radius:10px;background:linear-gradient(135deg,var(--pri),var(--violet));display:flex;align-items:center;justify-content:center;font-size:18px;box-shadow:0 4px 12px rgba(99,102,241,.25)}
203
- .admin-header-sub{font-size:12px;color:var(--text-muted);margin-top:6px}
204
- .admin-stat-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:10px;margin-top:18px}
205
- .admin-stat-box{position:relative;text-align:center;padding:16px 8px 14px;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;overflow:hidden;transition:all .2s}
206
- .admin-stat-box:hover{border-color:rgba(99,102,241,.25);transform:translateY(-1px);box-shadow:0 4px 12px rgba(0,0,0,.06)}
207
- .admin-stat-box::before{content:'';position:absolute;top:0;left:50%;transform:translateX(-50%);width:40px;height:2px;border-radius:0 0 4px 4px}
208
- .admin-stat-box:nth-child(1)::before{background:var(--green)}
209
- .admin-stat-box:nth-child(2)::before{background:var(--amber)}
210
- .admin-stat-box:nth-child(3)::before{background:var(--violet)}
211
- .admin-stat-box:nth-child(4)::before{background:var(--cyan)}
212
- .admin-stat-box:nth-child(5)::before{background:var(--pri)}
213
- .admin-stat-box:nth-child(6)::before{background:var(--rose)}
214
- .admin-stat-box .as-icon{font-size:16px;margin-bottom:4px;display:block}
215
- .admin-stat-box .val{font-size:22px;font-weight:800;color:var(--text);font-variant-numeric:tabular-nums}
216
- .admin-stat-box .lbl{font-size:10px;color:var(--text-muted);margin-top:3px;letter-spacing:.03em}
217
- .admin-tabs{display:flex;gap:4px;padding:4px;background:var(--bg);border:1px solid var(--border);border-radius:14px;overflow-x:auto;-webkit-overflow-scrolling:touch;margin-bottom:20px}
218
- .admin-tab{position:relative;display:flex;align-items:center;gap:6px;padding:9px 16px;border:none;background:transparent;color:var(--text-muted);font-size:13px;font-weight:500;cursor:pointer;border-radius:10px;transition:all .2s;white-space:nowrap;font-family:inherit}
219
- .admin-tab:hover{background:rgba(99,102,241,.06);color:var(--text)}
220
- .admin-tab.active{background:var(--bg-card);color:var(--text);font-weight:600;box-shadow:0 2px 8px rgba(0,0,0,.06),0 0 0 1px rgba(99,102,241,.1)}
197
+ /* ─── Admin Panel (Cyber) ─── */
198
+ @keyframes adminGlow{0%,100%{opacity:.6}50%{opacity:1}}
199
+ @keyframes adminPulse{0%{box-shadow:0 0 0 0 rgba(99,102,241,.4)}70%{box-shadow:0 0 0 8px rgba(99,102,241,0)}100%{box-shadow:0 0 0 0 rgba(99,102,241,0)}}
200
+ @keyframes adminScanline{0%{background-position:0 0}100%{background-position:0 100%}}
201
+ @keyframes adminSlideIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
202
+ @keyframes adminCountUp{from{opacity:0;transform:translateY(8px) scale(.8)}to{opacity:1;transform:translateY(0) scale(1)}}
203
+ @keyframes pendingPulse{0%,100%{border-color:rgba(251,191,36,.3)}50%{border-color:rgba(251,191,36,.7)}}
204
+ @keyframes dotBreathe{0%,100%{transform:scale(1);opacity:.8}50%{transform:scale(1.3);opacity:1}}
205
+ .admin-header{position:relative;padding:32px 32px 24px;background:linear-gradient(135deg,rgba(99,102,241,.06) 0%,rgba(6,182,212,.04) 50%,rgba(139,92,246,.06) 100%);border:1px solid rgba(99,102,241,.15);border-radius:20px;margin-bottom:24px;overflow:hidden}
206
+ .admin-header::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent,var(--pri),var(--cyan),var(--violet),transparent);animation:adminGlow 3s ease-in-out infinite}
207
+ .admin-header::after{content:'';position:absolute;inset:0;background:repeating-linear-gradient(0deg,transparent,transparent 2px,rgba(99,102,241,.015) 2px,rgba(99,102,241,.015) 4px);background-size:100% 8px;animation:adminScanline 8s linear infinite;pointer-events:none}
208
+ .admin-header-top{display:flex;justify-content:space-between;align-items:center;position:relative;z-index:1}
209
+ .admin-header h2{font-size:18px;font-weight:800;color:var(--text);display:flex;align-items:center;gap:12px;margin:0;letter-spacing:-.01em}
210
+ .admin-header h2 .ah-icon{width:40px;height:40px;border-radius:12px;background:linear-gradient(135deg,var(--pri),var(--violet));display:flex;align-items:center;justify-content:center;font-size:20px;box-shadow:0 4px 20px rgba(99,102,241,.3),inset 0 1px 0 rgba(255,255,255,.15);animation:adminPulse 2.5s infinite}
211
+ .admin-header-sub{font-size:11px;color:var(--text-muted);margin-top:6px;position:relative;z-index:1;letter-spacing:.02em}
212
+ .admin-stat-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:10px;margin-top:20px;position:relative;z-index:1}
213
+ .admin-stat-box{position:relative;text-align:center;padding:18px 10px 16px;background:rgba(var(--bg-card-rgb,30,30,40),.6);backdrop-filter:blur(12px);border:1px solid rgba(99,102,241,.12);border-radius:14px;overflow:hidden;transition:all .25s cubic-bezier(.4,0,.2,1)}
214
+ .admin-stat-box:hover{border-color:rgba(99,102,241,.35);transform:translateY(-2px);box-shadow:0 8px 24px rgba(99,102,241,.12)}
215
+ .admin-stat-box::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;opacity:.8}
216
+ .admin-stat-box:nth-child(1)::before{background:linear-gradient(90deg,transparent,#22c55e,transparent)}
217
+ .admin-stat-box:nth-child(2)::before{background:linear-gradient(90deg,transparent,#fbbf24,transparent)}
218
+ .admin-stat-box:nth-child(3)::before{background:linear-gradient(90deg,transparent,#8b5cf6,transparent)}
219
+ .admin-stat-box:nth-child(4)::before{background:linear-gradient(90deg,transparent,#06b6d4,transparent)}
220
+ .admin-stat-box:nth-child(5)::before{background:linear-gradient(90deg,transparent,#6366f1,transparent)}
221
+ .admin-stat-box:nth-child(6)::before{background:linear-gradient(90deg,transparent,#f43f5e,transparent)}
222
+ .admin-stat-box::after{content:'';position:absolute;inset:0;background:radial-gradient(circle at 50% 0%,rgba(99,102,241,.06),transparent 70%);pointer-events:none}
223
+ .admin-stat-box .as-icon{font-size:18px;margin-bottom:6px;display:block;filter:drop-shadow(0 2px 4px rgba(0,0,0,.15))}
224
+ .admin-stat-box .val{font-size:26px;font-weight:800;color:var(--text);font-variant-numeric:tabular-nums;animation:adminCountUp .4s ease-out;text-shadow:0 0 20px rgba(99,102,241,.15)}
225
+ .admin-stat-box .lbl{font-size:10px;color:var(--text-muted);margin-top:4px;letter-spacing:.05em;text-transform:uppercase}
226
+ .admin-tabs{display:flex;gap:3px;padding:3px;background:rgba(99,102,241,.03);border:1px solid rgba(99,102,241,.1);border-radius:14px;overflow-x:auto;-webkit-overflow-scrolling:touch;margin-bottom:24px;backdrop-filter:blur(8px)}
227
+ .admin-tab{position:relative;display:flex;align-items:center;gap:6px;padding:10px 18px;border:none;background:transparent;color:var(--text-muted);font-size:13px;font-weight:500;cursor:pointer;border-radius:11px;transition:all .25s cubic-bezier(.4,0,.2,1);white-space:nowrap;font-family:inherit}
228
+ .admin-tab:hover{background:rgba(99,102,241,.08);color:var(--text)}
229
+ .admin-tab.active{background:linear-gradient(135deg,rgba(99,102,241,.12),rgba(139,92,246,.08));color:var(--text);font-weight:600;box-shadow:0 2px 12px rgba(99,102,241,.1),inset 0 1px 0 rgba(255,255,255,.05);border:1px solid rgba(99,102,241,.15)}
221
230
  .admin-tab .at-icon{font-size:14px;line-height:1}
222
- .admin-tab .at-count{font-size:10px;font-weight:700;padding:1px 6px;border-radius:8px;background:rgba(99,102,241,.1);color:var(--pri);min-width:18px;text-align:center}
223
- .admin-tab.active .at-count{background:rgba(99,102,241,.15)}
231
+ .admin-tab .at-count{font-size:10px;font-weight:700;padding:2px 7px;border-radius:8px;background:rgba(99,102,241,.12);color:var(--pri);min-width:18px;text-align:center;transition:all .2s}
232
+ .admin-tab.active .at-count{background:rgba(99,102,241,.2);box-shadow:0 0 8px rgba(99,102,241,.15)}
224
233
  .admin-panel{display:none}
225
- .admin-panel.active{display:block}
226
- .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}
234
+ .admin-panel.active{display:block;animation:adminSlideIn .3s ease-out}
235
+ .admin-card{border:1px solid var(--border);border-radius:14px;padding:18px 20px;background:var(--bg-card);margin-bottom:12px;transition:all .25s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden}
227
236
  .admin-card-clickable{cursor:pointer}
228
- .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
- .admin-card:hover{border-color:rgba(99,102,241,.25);box-shadow:0 2px 12px rgba(0,0,0,.04);transform:translateY(-1px)}
237
+ .admin-card::before{content:'';position:absolute;top:0;left:0;bottom:0;width:3px;border-radius:3px 0 0 3px;background:var(--pri);opacity:.4;transition:opacity .2s}
238
+ .admin-card:hover{border-color:rgba(99,102,241,.25);box-shadow:0 4px 20px rgba(99,102,241,.06);transform:translateY(-1px)}
239
+ .admin-card:hover::before{opacity:.8}
230
240
  .admin-card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}
231
241
  .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
242
  .admin-card-meta{font-size:12px;color:var(--text-muted);line-height:1.5}
233
- .au-contrib{display:flex;gap:16px;padding:10px 0;margin:6px 0;border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
234
- .au-contrib-item{font-size:12px;color:var(--text-sec);display:flex;align-items:center;gap:4px}
235
- .au-contrib-num{font-size:18px;font-weight:700;line-height:1}
236
- .au-info{display:flex;flex-wrap:wrap;gap:6px 14px;padding:8px 0;font-size:12px}
237
- .au-info-item{color:var(--text-muted);white-space:nowrap}
238
- .au-status-dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:4px;vertical-align:middle;flex-shrink:0}
239
- .au-status-dot.online{background:#22c55e;box-shadow:0 0 6px rgba(34,197,94,.5)}
240
- .au-status-dot.offline{background:#9ca3af}
241
- .au-status-text{font-size:11px;font-weight:500}
242
- .au-status-text.online{color:#22c55e}
243
- .au-status-text.offline{color:#9ca3af}
244
- .au-group-header{font-size:13px;font-weight:600;color:var(--text-sec);margin:18px 0 8px;display:flex;align-items:center;gap:8px}
243
+ .au-contrib{display:flex;gap:20px;padding:12px 0;margin:8px 0;border-top:1px solid rgba(99,102,241,.08);border-bottom:1px solid rgba(99,102,241,.08)}
244
+ .au-contrib-item{font-size:12px;color:var(--text-sec);display:flex;align-items:center;gap:5px}
245
+ .au-contrib-num{font-size:20px;font-weight:800;line-height:1;font-variant-numeric:tabular-nums}
246
+ .au-info{display:flex;flex-wrap:wrap;gap:6px 16px;padding:8px 0;font-size:11px}
247
+ .au-info-item{color:var(--text-muted);white-space:nowrap;display:inline-flex;align-items:center;gap:3px}
248
+ .au-status-dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:5px;vertical-align:middle;flex-shrink:0;transition:all .3s}
249
+ .au-status-dot.online{background:#22c55e;box-shadow:0 0 8px rgba(34,197,94,.6),0 0 16px rgba(34,197,94,.2);animation:dotBreathe 2s ease-in-out infinite}
250
+ .au-status-dot.offline{background:#6b7280;box-shadow:none}
251
+ .au-status-text{font-size:11px;font-weight:600;letter-spacing:.02em}
252
+ .au-status-text.online{color:#22c55e;text-shadow:0 0 8px rgba(34,197,94,.3)}
253
+ .au-status-text.offline{color:#6b7280}
254
+ .au-group-header{font-size:13px;font-weight:700;color:var(--text-sec);margin:20px 0 10px;display:flex;align-items:center;gap:8px;letter-spacing:.02em}
245
255
  .au-group-header:first-child{margin-top:0}
246
256
  .au-group-header .au-group-dot{display:inline-block;width:8px;height:8px;border-radius:50%}
247
- .au-group-header .au-group-dot.online{background:#22c55e}
248
- .au-group-header .au-group-dot.offline{background:#9ca3af}
257
+ .au-group-header .au-group-dot.online{background:#22c55e;box-shadow:0 0 6px rgba(34,197,94,.5)}
258
+ .au-group-header .au-group-dot.offline{background:#6b7280}
249
259
  .au-group-header .au-group-count{font-size:12px;font-weight:400;color:var(--text-muted)}
250
- .au-card.au-offline{opacity:.7}
251
- .au-card.au-offline::before{background:#9ca3af}
252
- .au-card.au-online::before{background:#22c55e}
260
+ .au-card.au-offline{opacity:.55}
261
+ .au-card.au-offline:hover{opacity:.8}
262
+ .au-card.au-offline::before{background:#6b7280}
263
+ .au-card.au-online::before{background:#22c55e;box-shadow:0 0 8px rgba(34,197,94,.3)}
264
+ .admin-pending-section{position:relative;margin-bottom:20px;padding:20px;border-radius:16px;border:1px solid rgba(251,191,36,.25);background:linear-gradient(135deg,rgba(251,191,36,.04),rgba(245,158,11,.02));animation:pendingPulse 3s ease-in-out infinite;overflow:hidden}
265
+ .admin-pending-section::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent,#fbbf24,#f59e0b,transparent)}
266
+ .admin-pending-section h3{font-size:14px;font-weight:700;color:#fbbf24;margin:0 0 14px;display:flex;align-items:center;gap:8px}
267
+ .admin-pending-section h3 .pending-count{display:inline-flex;align-items:center;justify-content:center;min-width:22px;height:22px;padding:0 6px;border-radius:11px;background:rgba(251,191,36,.2);font-size:12px;font-weight:800;color:#fbbf24}
268
+ .admin-pending-card{border:1px solid rgba(251,191,36,.15);border-radius:12px;padding:16px 18px;background:rgba(251,191,36,.03);margin-bottom:8px;transition:all .2s;position:relative;overflow:hidden}
269
+ .admin-pending-card::before{content:'';position:absolute;top:0;left:0;bottom:0;width:3px;background:linear-gradient(180deg,#fbbf24,#f59e0b);border-radius:3px 0 0 3px}
270
+ .admin-pending-card:hover{border-color:rgba(251,191,36,.35);box-shadow:0 4px 16px rgba(251,191,36,.08)}
271
+ .admin-pending-card .apc-name{font-size:14px;font-weight:700;color:var(--text)}
272
+ .admin-pending-card .apc-meta{font-size:11px;color:var(--text-muted);margin-top:4px;display:flex;align-items:center;gap:6px}
273
+ .admin-pending-card .apc-actions{display:flex;gap:8px;margin-top:12px}
274
+ .admin-pending-card .apc-actions .btn-approve{background:linear-gradient(135deg,#22c55e,#16a34a);color:#fff;border:none;padding:6px 18px;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer;transition:all .2s;box-shadow:0 2px 8px rgba(34,197,94,.25)}
275
+ .admin-pending-card .apc-actions .btn-approve:hover{transform:translateY(-1px);box-shadow:0 4px 16px rgba(34,197,94,.35)}
276
+ .admin-pending-card .apc-actions .btn-reject{background:transparent;color:var(--rose);border:1px solid rgba(244,63,94,.2);padding:6px 14px;border-radius:8px;font-size:12px;font-weight:500;cursor:pointer;transition:all .2s}
277
+ .admin-pending-card .apc-actions .btn-reject:hover{background:rgba(244,63,94,.06);border-color:rgba(244,63,94,.4)}
253
278
  .admin-card-tags{display:flex;gap:5px;margin:8px 0;align-items:center}
254
279
  .admin-card-tags-left{display:flex;flex-wrap:wrap;gap:5px;align-items:center;flex:1;min-width:0}
255
- .admin-card-tag{display:inline-flex;align-items:center;gap:3px;padding:2px 8px;border-radius:6px;font-size:11px;font-weight:500}
280
+ .admin-card-tag{display:inline-flex;align-items:center;gap:3px;padding:3px 10px;border-radius:6px;font-size:11px;font-weight:600;letter-spacing:.01em}
256
281
  .admin-card-tag.tag-role{background:rgba(99,102,241,.1);color:var(--pri)}
257
282
  .admin-card-tag.tag-kind{background:rgba(245,158,11,.1);color:#f59e0b}
258
283
  .admin-card-tag.tag-owner{background:rgba(34,197,94,.1);color:#22c55e}
@@ -260,16 +285,16 @@ input,textarea,select{font-family:inherit;font-size:inherit}
260
285
  .admin-card-tag.tag-version{background:rgba(139,92,246,.1);color:#8b5cf6}
261
286
  .admin-card-tag.tag-visibility{background:rgba(99,102,241,.08);color:var(--pri)}
262
287
  .admin-card-tag.tag-group{background:rgba(139,92,246,.08);color:#8b5cf6}
263
- .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}
264
- .admin-card-actions{display:inline-flex;gap:4px;margin-left:auto;align-items:center;flex-shrink:0}
288
+ .admin-card-preview{font-size:12px;color:var(--text-sec);line-height:1.5;margin:8px 0;padding:10px 12px;background:rgba(99,102,241,.02);border-radius:10px;border:1px solid rgba(99,102,241,.08);max-height:60px;overflow:hidden;white-space:pre-wrap;word-break:break-all}
289
+ .admin-card-actions{display:inline-flex;gap:6px;margin-left:auto;align-items:center;flex-shrink:0}
265
290
  .admin-card-time{font-size:11px;color:var(--text-muted)}
266
- .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}
291
+ .admin-card-detail{display:none;margin-top:0;padding:20px 24px 24px;border-top:1px dashed rgba(99,102,241,.12);background:linear-gradient(180deg,rgba(99,102,241,.02) 0%,transparent 60%);animation:adminDetailIn .25s ease}
267
292
  @keyframes adminDetailIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}
268
293
  .admin-card.expanded .admin-card-detail{display:block}
269
- .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)}
294
+ .admin-card.expanded{border-color:rgba(99,102,241,.3);box-shadow:0 4px 24px rgba(99,102,241,.1),0 0 0 1px rgba(99,102,241,.08)}
270
295
  .admin-card-detail-meta{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:18px;font-size:12px;color:var(--text-muted)}
271
- .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}
272
- .admin-card-detail-meta .meta-item:hover{border-color:rgba(99,102,241,.25);background:rgba(99,102,241,.03)}
296
+ .admin-card-detail-meta .meta-item{display:inline-flex;align-items:center;gap:4px;padding:5px 12px;background:rgba(99,102,241,.03);border:1px solid rgba(99,102,241,.08);border-radius:20px;font-size:11px;color:var(--text-sec);transition:all .2s}
297
+ .admin-card-detail-meta .meta-item:hover{border-color:rgba(99,102,241,.25);background:rgba(99,102,241,.06)}
273
298
  .admin-card-detail-section{margin-top:20px}
274
299
  .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}
275
300
  .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))}
@@ -303,18 +328,20 @@ input,textarea,select{font-family:inherit;font-size:inherit}
303
328
  .admin-toolbar{display:flex;align-items:center;gap:10px;margin-bottom:14px;flex-wrap:wrap}
304
329
  .admin-toolbar h3{font-size:14px;font-weight:600;color:var(--text);white-space:nowrap;margin:0;margin-right:auto;line-height:32px}
305
330
  .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}
306
- .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}
307
- .admin-badge.admin{background:rgba(52,199,89,.15);color:#34c759}
308
- .admin-badge.member{background:rgba(142,142,147,.12);color:var(--text-muted)}
309
- .admin-badge.pending{background:rgba(255,159,10,.15);color:#ff9f0a}
331
+ .admin-badge{display:inline-flex;align-items:center;gap:4px;font-size:10px;font-weight:700;padding:3px 10px;border-radius:999px;letter-spacing:.03em;text-transform:uppercase}
332
+ .admin-badge.admin{background:linear-gradient(135deg,rgba(34,197,94,.15),rgba(16,185,129,.1));color:#22c55e;box-shadow:0 0 8px rgba(34,197,94,.1)}
333
+ .admin-badge.member{background:rgba(99,102,241,.08);color:var(--text-muted)}
334
+ .admin-badge.pending{background:linear-gradient(135deg,rgba(251,191,36,.2),rgba(245,158,11,.1));color:#fbbf24;box-shadow:0 0 8px rgba(251,191,36,.1)}
310
335
  .admin-badge.public{background:rgba(99,102,241,.1);color:var(--pri)}
311
336
  .admin-badge.group{background:rgba(139,92,246,.1);color:var(--violet)}
312
- .admin-empty{font-size:13px;color:var(--text-muted);padding:40px 20px;text-align:center;border:1px dashed var(--border);border-radius:12px;background:var(--bg)}
313
- .admin-empty .ae-icon{font-size:28px;display:block;margin-bottom:8px;opacity:.5}
337
+ .admin-empty{font-size:13px;color:var(--text-muted);padding:48px 24px;text-align:center;border:1px dashed rgba(99,102,241,.15);border-radius:16px;background:rgba(99,102,241,.02)}
338
+ .admin-empty .ae-icon{font-size:32px;display:block;margin-bottom:10px;opacity:.4}
314
339
  [data-theme="light"] .admin-badge.admin{background:rgba(5,150,105,.1);color:#059669}
315
340
  [data-theme="light"] .admin-badge.member{background:rgba(0,0,0,.06);color:#6b7280}
316
341
  [data-theme="light"] .admin-badge.pending{background:rgba(245,158,11,.1);color:#d97706}
317
- [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%)}
342
+ [data-theme="light"] .admin-header{background:linear-gradient(135deg,rgba(99,102,241,.04) 0%,rgba(6,182,212,.03) 50%,rgba(139,92,246,.04) 100%)}
343
+ [data-theme="light"] .admin-stat-box{background:rgba(255,255,255,.8)}
344
+ [data-theme="light"] .admin-pending-section{background:linear-gradient(135deg,rgba(251,191,36,.03),rgba(245,158,11,.015))}
318
345
  .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}
319
346
  .confirm-overlay.show{display:flex}
320
347
  .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}
@@ -1786,8 +1813,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
1786
1813
  <div id="adminMainContent">
1787
1814
  <div class="admin-header">
1788
1815
  <div class="admin-header-top">
1789
- <h2><span class="ah-icon">\u{1F6E1}</span> <span data-i18n="admin.title">Team Admin Panel</span></h2>
1790
- <button class="btn btn-sm btn-ghost" onclick="loadAdminData()" style="backdrop-filter:blur(8px)" data-i18n="admin.refresh">\u21BB Refresh</button>
1816
+ <h2><span class="ah-icon">\u{26A1}</span> <span data-i18n="admin.title">Team Admin Panel</span></h2>
1791
1817
  </div>
1792
1818
  <div class="admin-header-sub" data-i18n="admin.subtitle">Manage team members and shared resources</div>
1793
1819
  <div class="admin-stat-row" id="adminStats"></div>
@@ -2000,7 +2026,9 @@ input,textarea,select{font-family:inherit;font-size:inherit}
2000
2026
  <script>
2001
2027
  let activeSession=null,activeRole='',editingId=null,searchTimer=null,memoryCache={},currentPage=1,totalPages=1,totalCount=0,PAGE_SIZE=40,metricsDays=30;
2002
2028
  let memorySearchScope='local',skillSearchScope='local',taskSearchScope='local';
2029
+ let _lastMemoriesFingerprint='',_lastTasksFingerprint='',_lastSkillsFingerprint='';
2003
2030
  let _embeddingWarningShown=false;
2031
+ let _currentAgentOwner='agent:main';
2004
2032
 
2005
2033
  /* ─── i18n ─── */
2006
2034
  const I18N={
@@ -2389,6 +2417,7 @@ const I18N={
2389
2417
  'settings.hub.userToken.hint':'Usually auto-obtained after joining. Only fill if given by admin.',
2390
2418
  'settings.hub.testConnection':'Test Connection',
2391
2419
  'settings.hub.test.noAddr':'Please enter server address first',
2420
+ 'settings.hub.teamToken.required':'Please enter team token',
2392
2421
  'settings.hub.test.testing':'Testing...',
2393
2422
  'settings.hub.test.ok':'Connected successfully',
2394
2423
  'settings.hub.test.fail':'Connection failed',
@@ -2522,6 +2551,7 @@ const I18N={
2522
2551
  'admin.groupsFailed':'Failed to load groups: ',
2523
2552
  'toast.userApproved':'User approved',
2524
2553
  'sharing.approved.toast':'Your join request has been approved!',
2554
+ 'sharing.rejected.toast':'Your join request was rejected by the admin.',
2525
2555
  'toast.userRejected':'User rejected',
2526
2556
  'toast.approveFail':'Approve failed',
2527
2557
  'toast.rejectFail':'Reject failed',
@@ -2682,6 +2712,8 @@ const I18N={
2682
2712
  '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?',
2683
2713
  '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?',
2684
2714
  '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',
2715
+ 'sharing.switch.hubToClient':'You are about to switch from Server to Client mode.\\n\\nWhat will happen:\\n\\u2022 The Hub server will shut down after restart\\n\\u2022 All connected team members will be disconnected\\n\\u2022 Shared data on the Hub is preserved for future use\\n\\u2022 You will join the specified remote team as a client\\n\\nAre you sure?',
2716
+ 'sharing.switch.clientToHub':'You are about to switch from Client to Server mode.\\n\\nWhat will happen:\\n\\u2022 You will disconnect from the current team\\n\\u2022 A new Hub server will start after restart\\n\\u2022 Your local data is not affected\\n\\nAre you sure?',
2685
2717
  'admin.notEnabled.title':'Team sharing is not enabled',
2686
2718
  '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.',
2687
2719
  'admin.notEnabled.setupHub':'Set Up as Team Server',
@@ -3094,6 +3126,7 @@ const I18N={
3094
3126
  'settings.hub.userToken.hint':'通常在加入团队后自动获取,无需手动填写。',
3095
3127
  'settings.hub.testConnection':'测试连接',
3096
3128
  'settings.hub.test.noAddr':'请先输入服务器地址',
3129
+ 'settings.hub.teamToken.required':'请输入团队令牌',
3097
3130
  'settings.hub.test.testing':'测试中...',
3098
3131
  'settings.hub.test.ok':'连接成功',
3099
3132
  'settings.hub.test.fail':'连接失败',
@@ -3227,6 +3260,7 @@ const I18N={
3227
3260
  'admin.groupsFailed':'加载分组失败:',
3228
3261
  'toast.userApproved':'用户已批准',
3229
3262
  'sharing.approved.toast':'您的加入申请已通过审核!',
3263
+ 'sharing.rejected.toast':'您的加入申请已被管理员拒绝。',
3230
3264
  'toast.userRejected':'用户已拒绝',
3231
3265
  'toast.approveFail':'批准失败',
3232
3266
  'toast.rejectFail':'拒绝失败',
@@ -3387,6 +3421,8 @@ const I18N={
3387
3421
  'sharing.disable.confirm.hub':'你即将关闭团队服务。\\n\\n关闭后将会:\\n\\u2022 所有已连接的团队成员将断开连接\\n\\u2022 他们将无法继续同步记忆、任务和技能\\n\\u2022 已共享的数据会保留,重新开启后仍可使用\\n\\n确定要关闭吗?',
3388
3422
  'sharing.disable.confirm.client':'你即将断开与团队的连接。\\n\\n断开后将会:\\n\\u2022 你将无法再接收团队共享的记忆、任务和技能\\n\\u2022 你的本地数据不受影响,会完整保留\\n\\u2022 之后可以随时重新开启共享来恢复连接\\n\\n确定要断开吗?',
3389
3423
  'sharing.disable.restartAlert':'共享已关闭。请重启 OpenClaw 网关使更改生效。\\n\\n执行命令:openclaw gateway stop && openclaw gateway start',
3424
+ 'sharing.switch.hubToClient':'你即将从服务端模式切换为客户端模式。\\n\\n切换后将会:\\n\\u2022 Hub 服务将在重启后关闭\\n\\u2022 所有已连接的团队成员将断开连接\\n\\u2022 Hub 上的共享数据会保留,以后可恢复使用\\n\\u2022 你将作为客户端加入指定的远程团队\\n\\n确定要切换吗?',
3425
+ 'sharing.switch.clientToHub':'你即将从客户端模式切换为服务端模式。\\n\\n切换后将会:\\n\\u2022 你将断开与当前团队的连接\\n\\u2022 重启后将启动新的 Hub 服务\\n\\u2022 你的本地数据不受影响\\n\\n确定要切换吗?',
3390
3426
  'admin.notEnabled.title':'团队共享尚未开启',
3391
3427
  'admin.notEnabled.desc':'管理面板用于管理团队成员、共享的记忆、任务和技能。使用此功能前,需要先开启团队共享。',
3392
3428
  'admin.notEnabled.setupHub':'配置为团队服务端',
@@ -3649,16 +3685,24 @@ function switchView(view){
3649
3685
 
3650
3686
  function onMemoryScopeChange(){
3651
3687
  memorySearchScope=document.getElementById('memorySearchScope')?.value||'local';
3688
+ currentPage=1;
3689
+ activeSession=null;activeRole='';
3690
+ _lastMemoriesFingerprint='';
3652
3691
  var isHub=memorySearchScope==='hub';
3692
+ var isLocal=memorySearchScope==='local';
3653
3693
  var ownerSel=document.getElementById('filterOwner');
3654
3694
  var filterBar=document.getElementById('filterBar');
3655
3695
  var dateFilter=document.querySelector('.date-filter');
3656
- if(ownerSel) ownerSel.style.display=isHub?'none':'';
3696
+ if(ownerSel) ownerSel.style.display=(isHub||isLocal)?'none':'';
3657
3697
  if(filterBar) filterBar.style.display=isHub?'none':'';
3658
3698
  if(dateFilter) dateFilter.style.display=isHub?'none':'';
3659
3699
  if(document.getElementById('searchInput').value.trim()) doSearch(document.getElementById('searchInput').value);
3660
3700
  else if(isHub) { document.getElementById('sharingSearchMeta').textContent=''; loadHubMemories(); }
3661
- else { document.getElementById('sharingSearchMeta').textContent=''; loadStats(); loadMemories(); }
3701
+ else {
3702
+ document.getElementById('sharingSearchMeta').textContent='';
3703
+ var ownerArg=isLocal?_currentAgentOwner:undefined;
3704
+ loadStats(ownerArg); loadMemories();
3705
+ }
3662
3706
  }
3663
3707
 
3664
3708
  function onSkillScopeChange(){
@@ -3673,6 +3717,7 @@ function onTaskScopeChange(){
3673
3717
  }
3674
3718
 
3675
3719
  var _clientPendingPollTimer=null;
3720
+ var _lastSharingConnStatus='';
3676
3721
  async function loadSharingStatus(forcePending){
3677
3722
  try{
3678
3723
  const r=await fetch('/api/sharing/status');
@@ -3682,13 +3727,26 @@ async function loadSharingStatus(forcePending){
3682
3727
  renderSharingSettings(d);
3683
3728
  updateTeamGuide(d);
3684
3729
  if(forcePending && d && d.admin && d.admin.canManageUsers) loadSharingPendingUsers();
3685
- var conn=d&&d.connection||{};
3686
- if(conn.pendingApproval&&!_clientPendingPollTimer){
3730
+ if(!d||!d.enabled){
3731
+ if(_clientPendingPollTimer){clearInterval(_clientPendingPollTimer);_clientPendingPollTimer=null;}
3732
+ _lastSharingConnStatus='';
3733
+ return;
3734
+ }
3735
+ var conn=d.connection||{};
3736
+ var curStatus=conn.rejected?'rejected':conn.pendingApproval?'pending':conn.connected?'connected':'none';
3737
+ if(_lastSharingConnStatus==='pending'&&curStatus==='rejected'){
3738
+ toast(t('sharing.rejected.toast'),'error');
3739
+ }
3740
+ if(_lastSharingConnStatus==='pending'&&curStatus==='connected'){
3741
+ toast(t('sharing.approved.toast'),'success');
3742
+ }
3743
+ _lastSharingConnStatus=curStatus;
3744
+ if(curStatus==='pending'&&!_clientPendingPollTimer){
3687
3745
  _clientPendingPollTimer=setInterval(function(){loadSharingStatus(false);},10000);
3688
- }else if(!conn.pendingApproval&&_clientPendingPollTimer){
3746
+ }
3747
+ if(curStatus!=='pending'&&_clientPendingPollTimer){
3689
3748
  clearInterval(_clientPendingPollTimer);
3690
3749
  _clientPendingPollTimer=null;
3691
- if(conn.connected) toast(t('sharing.approved.toast'),'success');
3692
3750
  }
3693
3751
  }catch(e){
3694
3752
  renderSharingSidebar(null);
@@ -3697,12 +3755,17 @@ async function loadSharingStatus(forcePending){
3697
3755
  }
3698
3756
  }
3699
3757
 
3758
+ var _lastSidebarFingerprint='';
3700
3759
  function renderSharingSidebar(data){
3701
3760
  var section=document.getElementById('sidebarSharingSection');
3702
3761
  var statusEl=document.getElementById('sharingSidebarStatus');
3703
3762
  var hintEl=document.getElementById('sharingSidebarHint');
3704
3763
  var badgeEl=document.getElementById('sharingSidebarConnBadge');
3705
3764
  if(!statusEl||!hintEl) return;
3765
+ var conn=data&&data.connection||{};
3766
+ var fp=JSON.stringify({e:!!data&&!!data.enabled,r:data&&data.role,pa:!!conn.pendingApproval,rj:!!conn.rejected,c:!!conn.connected,u:conn.user&&conn.user.username,tn:conn.teamName,cc:!!data&&!!data.clientConfigured,hu:data&&data.hubUrl});
3767
+ if(fp===_lastSidebarFingerprint) return;
3768
+ _lastSidebarFingerprint=fp;
3706
3769
  if(!data||!data.enabled){
3707
3770
  if(section) section.style.display='none';
3708
3771
  window._isHubAdmin=false;
@@ -3754,12 +3817,17 @@ function renderSharingSidebar(data){
3754
3817
  }
3755
3818
  }
3756
3819
 
3820
+ var _lastSettingsFingerprint='';
3757
3821
  function renderSharingSettings(data){
3758
3822
  var statusEl=document.getElementById('sharingStatusPanel');
3759
3823
  var teamEl=document.getElementById('sharingTeamPanel');
3760
3824
  var adminEl=document.getElementById('sharingAdminPanel');
3761
3825
  var panelsWrap=document.getElementById('sharingPanelsWrap');
3762
3826
  if(!statusEl||!teamEl||!adminEl) return;
3827
+ var conn2=data&&data.connection||{};
3828
+ var fp2=JSON.stringify({e:!!data&&!!data.enabled,r:data&&data.role,pa:!!conn2.pendingApproval,rj:!!conn2.rejected,c:!!conn2.connected,u:conn2.user&&conn2.user.username,ur:conn2.user&&conn2.user.role,tn:conn2.teamName,cc:!!data&&!!data.clientConfigured,cm:data&&data.admin&&data.admin.canManageUsers});
3829
+ if(fp2===_lastSettingsFingerprint) return;
3830
+ _lastSettingsFingerprint=fp2;
3763
3831
  if(!data||!data.enabled){
3764
3832
  statusEl.innerHTML='';teamEl.innerHTML='';adminEl.innerHTML='';
3765
3833
  if(panelsWrap) panelsWrap.style.display='none';
@@ -3973,6 +4041,7 @@ function guideGoToHub(role){
3973
4041
 
3974
4042
  /* ─── Hub Admin Panel ─── */
3975
4043
  var adminDataCache={users:[],groups:[],tasks:[],skills:[],memories:[]};
4044
+ var _lastAdminFingerprint='';
3976
4045
  var hubTasksCache=[];
3977
4046
  var hubSkillsCache=[];
3978
4047
  var ADMIN_PAGE_SIZE=20;
@@ -4069,11 +4138,23 @@ async function loadAdminData(){
4069
4138
  ]);
4070
4139
  }
4071
4140
  var usersR=fetches[0],tasksR=fetches[1],skillsR=fetches[2],pendingR=fetches[3],memoriesR=fetches[4];
4072
- adminDataCache.users=Array.isArray(usersR.users)?usersR.users:[];
4073
- adminDataCache.tasks=Array.isArray(tasksR.tasks)?tasksR.tasks:[];
4074
- adminDataCache.skills=Array.isArray(skillsR.skills)?skillsR.skills:[];
4075
- adminDataCache.memories=Array.isArray(memoriesR.memories)?memoriesR.memories:[];
4141
+ var _newUsers=Array.isArray(usersR.users)?usersR.users:[];
4142
+ var _newTasks=Array.isArray(tasksR.tasks)?tasksR.tasks:[];
4143
+ var _newSkills=Array.isArray(skillsR.skills)?skillsR.skills:[];
4144
+ var _newMemories=Array.isArray(memoriesR.memories)?memoriesR.memories:[];
4076
4145
  var pending=isAdmin?(Array.isArray(pendingR.users)?pendingR.users:[]):[];
4146
+ var _fp=_newUsers.length+':'+_newTasks.length+':'+_newSkills.length+':'+_newMemories.length+':'+pending.length
4147
+ +':'+_newUsers.map(function(u){return u.id+'|'+(u.isOnline?1:0)+'|'+(u.role||'')}).join(',')
4148
+ +':'+_newMemories.map(function(m){return m.id}).join(',')
4149
+ +':'+_newTasks.map(function(t){return t.id+'|'+(t.status||'')}).join(',')
4150
+ +':'+_newSkills.map(function(s){return s.id+'|'+(s.status||'')}).join(',')
4151
+ +':'+pending.map(function(p){return p.id}).join(',');
4152
+ if(_fp===_lastAdminFingerprint) return;
4153
+ _lastAdminFingerprint=_fp;
4154
+ adminDataCache.users=_newUsers;
4155
+ adminDataCache.tasks=_newTasks;
4156
+ adminDataCache.skills=_newSkills;
4157
+ adminDataCache.memories=_newMemories;
4077
4158
  adminDataCache._pending=pending;
4078
4159
  var badge=document.getElementById('adminPendingBadge');
4079
4160
  if(badge){if(pending.length>0){badge.textContent=pending.length;badge.style.display='inline';}else{badge.style.display='none';}}
@@ -4189,14 +4270,14 @@ function renderAdminUserCard(u,adminCount){
4189
4270
  }else{
4190
4271
  actions+='<span style="font-size:11px;color:var(--text-muted);padding:4px 0">'+t('admin.lastAdminHint')+'</span>';
4191
4272
  }
4192
- var ownerBadge=u.isOwner?' <span style="font-size:10px;background:linear-gradient(135deg,var(--amber),#f59e0b);color:#fff;padding:1px 6px;border-radius:4px;font-weight:600;vertical-align:middle;margin-left:4px">Owner</span>':'';
4273
+ var ownerBadge=u.isOwner?' <span style="font-size:9px;background:linear-gradient(135deg,#fbbf24,#f59e0b);color:#000;padding:2px 8px;border-radius:6px;font-weight:800;vertical-align:middle;margin-left:4px;letter-spacing:.04em;text-transform:uppercase;box-shadow:0 2px 8px rgba(251,191,36,.3)">Owner</span>':'';
4193
4274
 
4194
4275
  return '<div class="admin-card au-card au-'+statusCls+'"><div class="admin-card-header"><div style="flex:1;min-width:0;display:flex;align-items:center;gap:8px;flex-wrap:wrap">'+
4195
4276
  '<div style="flex:1;min-width:0">'+titleDisplay+editRow+'</div>'+statusLabel+
4196
4277
  '</div>'+
4197
- '<span class="admin-badge '+(u.role==='admin'?'admin':'member')+'">'+esc(u.role||'member')+'</span>'+ownerBadge+'</div>'+
4278
+ '<div style="display:flex;align-items:center;gap:6px"><span class="admin-badge '+(u.role==='admin'?'admin':'member')+'">'+esc(u.role||'member')+'</span>'+ownerBadge+'</div></div>'+
4198
4279
  contribHtml+infoHtml+
4199
- (actions?'<div class="admin-card-actions" style="border-top:1px dashed var(--border);padding-top:10px;margin-top:4px">'+actions+'</div>':'')+
4280
+ (actions?'<div class="admin-card-actions" style="border-top:1px solid rgba(99,102,241,.08);padding-top:12px;margin-top:6px">'+actions+'</div>':'')+
4200
4281
  '</div>';
4201
4282
  }
4202
4283
 
@@ -4210,14 +4291,15 @@ function renderAdminUsers(users,pending){
4210
4291
  }
4211
4292
  var html='';
4212
4293
  if(pending&&pending.length>0){
4213
- html+='<div style="margin-bottom:16px"><h3 style="font-size:14px;font-weight:600;color:var(--amber);margin-bottom:10px">'+t('admin.pendingApproval')+' ('+pending.length+')</h3>';
4294
+ html+='<div class="admin-pending-section"><h3>'+t('admin.pendingApproval')+' <span class="pending-count">'+pending.length+'</span></h3>';
4214
4295
  for(var p=0;p<pending.length;p++){
4215
4296
  var pu=pending[p];
4216
- html+='<div class="admin-card"><div class="admin-card-header"><div class="admin-card-title">'+esc(pu.username||pu.id||'Unknown')+'</div><span class="admin-badge pending">pending</span></div>'+
4217
- '<div class="admin-card-meta">'+t('admin.device')+esc(pu.deviceName||'unknown')+'</div>'+
4218
- '<div class="admin-card-actions">'+
4219
- '<button class="btn btn-sm btn-primary" onclick="adminApproveUser(&quot;'+escAttr(pu.id)+'&quot;,&quot;'+escAttr(pu.username||'')+'&quot;)">'+t('admin.approve')+'</button>'+
4220
- '<button class="btn btn-sm btn-ghost" onclick="adminRejectUser(&quot;'+escAttr(pu.id)+'&quot;)" style="color:var(--rose)">'+t('admin.reject')+'</button>'+
4297
+ html+='<div class="admin-pending-card">'+
4298
+ '<div class="apc-name">'+esc(pu.username||pu.id||'Unknown')+'</div>'+
4299
+ '<div class="apc-meta"><span>\u{1F4BB} '+esc(pu.deviceName||'unknown')+'</span>'+(pu.createdAt?'<span>\u{1F552} '+formatDateTimeSeconds(pu.createdAt)+'</span>':'')+'</div>'+
4300
+ '<div class="apc-actions">'+
4301
+ '<button class="btn-approve" onclick="adminApproveUser(&quot;'+escAttr(pu.id)+'&quot;,&quot;'+escAttr(pu.username||'')+'&quot;)">'+t('admin.approve')+'</button>'+
4302
+ '<button class="btn-reject" onclick="adminRejectUser(&quot;'+escAttr(pu.id)+'&quot;)">'+t('admin.reject')+'</button>'+
4221
4303
  '</div></div>';
4222
4304
  }
4223
4305
  html+='</div>';
@@ -5511,12 +5593,12 @@ function setTaskStatusFilter(btn,status){
5511
5593
  loadTasks();
5512
5594
  }
5513
5595
 
5514
- async function loadTasks(){
5596
+ async function loadTasks(silent){
5515
5597
  const scope=document.getElementById('taskSearchScope')?document.getElementById('taskSearchScope').value:taskSearchScope;
5516
5598
  taskSearchScope=scope||'local';
5517
5599
  if(taskSearchScope==='hub'){ return loadHubTasks(); }
5518
5600
  const list=document.getElementById('tasksList');
5519
- list.innerHTML='<div class="spinner"></div>';
5601
+ if(!silent) list.innerHTML='<div class="spinner"></div>';
5520
5602
  try{
5521
5603
  const params=new URLSearchParams({limit:String(TASKS_PER_PAGE),offset:String(tasksPage*TASKS_PER_PAGE)});
5522
5604
  if(tasksStatusFilter) params.set('status',tasksStatusFilter);
@@ -5530,6 +5612,14 @@ async function loadTasks(){
5530
5612
  fetch('/api/tasks?status=completed&limit=1&offset=0&'+baseP).then(r=>r.json()),
5531
5613
  fetch('/api/tasks?status=skipped&limit=1&offset=0&'+baseP).then(r=>r.json())
5532
5614
  ]);
5615
+ if(silent){
5616
+ var fp=JSON.stringify((data.tasks||[]).map(function(tk){return tk.id+'|'+tk.status+'|'+(tk.updatedAt||tk.startedAt)}));
5617
+ fp+=':'+allD.total+':'+activeD.total+':'+compD.total+':'+skipD.total;
5618
+ if(fp===_lastTasksFingerprint) return;
5619
+ _lastTasksFingerprint=fp;
5620
+ }else{
5621
+ _lastTasksFingerprint='';
5622
+ }
5533
5623
  document.getElementById('tasksTotalCount').textContent=formatNum(allD.total);
5534
5624
  document.getElementById('tasksActiveCount').textContent=formatNum(activeD.total);
5535
5625
  document.getElementById('tasksCompletedCount').textContent=formatNum(compD.total);
@@ -5815,17 +5905,17 @@ function updateSkillCardBadge(skillId,newScope){
5815
5905
  }
5816
5906
  }
5817
5907
 
5818
- async function loadSkills(){
5908
+ async function loadSkills(silent){
5819
5909
  const list=document.getElementById('skillsList');
5820
5910
  const hubList=document.getElementById('hubSkillsList');
5821
- list.innerHTML='<div class="spinner"></div>';
5911
+ if(!silent) list.innerHTML='<div class="spinner"></div>';
5822
5912
  var hubSection=document.getElementById('hubSkillsSection');
5823
5913
  if(hubList){
5824
5914
  if(skillSearchScope==='local'||skillSearchScope==='allLocal'){
5825
5915
  if(hubSection) hubSection.style.display='none';
5826
5916
  }else{
5827
5917
  if(hubSection) hubSection.style.display='block';
5828
- hubList.innerHTML='<div class="spinner"></div>';
5918
+ if(!silent) hubList.innerHTML='<div class="spinner"></div>';
5829
5919
  }
5830
5920
  }
5831
5921
 
@@ -5849,6 +5939,13 @@ async function loadSkills(){
5849
5939
  return haystack.includes(q);
5850
5940
  });
5851
5941
  }
5942
+ if(silent){
5943
+ var fp=JSON.stringify(localSkills.map(function(s){return s.id+'|'+s.status+'|'+s.version+'|'+(s.visibility||'')}));
5944
+ if(fp===_lastSkillsFingerprint) return;
5945
+ _lastSkillsFingerprint=fp;
5946
+ }else{
5947
+ _lastSkillsFingerprint='';
5948
+ }
5852
5949
 
5853
5950
  const renderLocalCards=function(skills){
5854
5951
  if(!skills||skills.length===0){
@@ -5907,7 +6004,8 @@ async function loadSkills(){
5907
6004
 
5908
6005
  if(!query){
5909
6006
  if(hubSection) hubSection.style.display='block';
5910
- if(hubList){ loadHubSkills(hubList); }
6007
+ var localIds=new Set(localSkills.map(function(s){return s.id;}));
6008
+ if(hubList){ loadHubSkills(hubList, localIds); }
5911
6009
  document.getElementById('skillSearchMeta').textContent=t('skills.search.local')+' '+localSkills.length;
5912
6010
  document.getElementById('skillsTotalCount').textContent=formatNum(localSkills.length);
5913
6011
  document.getElementById('skillsActiveCount').textContent=formatNum(localSkills.filter(s=>s.status==='active').length);
@@ -6005,7 +6103,7 @@ async function loadHubTasks(){
6005
6103
  }
6006
6104
  }
6007
6105
 
6008
- async function loadHubSkills(hubList){
6106
+ async function loadHubSkills(hubList, localIds){
6009
6107
  if(!hubList) hubList=document.getElementById('hubSkillsList');
6010
6108
  if(!hubList) return;
6011
6109
  var hubSection=document.getElementById('hubSkillsSection');
@@ -6013,7 +6111,8 @@ async function loadHubSkills(hubList){
6013
6111
  try{
6014
6112
  const r=await fetch('/api/sharing/skills/list?limit=40');
6015
6113
  const d=await r.json();
6016
- const skills=Array.isArray(d.skills)?d.skills:[];
6114
+ var allSkills=Array.isArray(d.skills)?d.skills:[];
6115
+ const skills=localIds?allSkills.filter(function(s){return !localIds.has(s.sourceSkillId);}):allSkills;
6017
6116
  hubSkillsCache=skills;
6018
6117
  if(!skills.length){
6019
6118
  if(hubSection) hubSection.style.display='none';
@@ -6545,6 +6644,8 @@ async function saveHubConfig(){
6545
6644
  var clientTeamToken=document.getElementById('cfgClientTeamToken').value.trim();
6546
6645
  var clientUserToken=document.getElementById('cfgClientUserToken').value.trim();
6547
6646
  var clientNickname=document.getElementById('cfgClientNickname').value.trim();
6647
+ if(!clientAddr){done();toast(t('settings.hub.test.noAddr'),'error');return;}
6648
+ if(!clientTeamToken){done();toast(t('settings.hub.teamToken.required'),'error');return;}
6548
6649
  cfg.sharing.client={};
6549
6650
  if(clientAddr) cfg.sharing.client.hubAddress=clientAddr;
6550
6651
  if(clientNickname) cfg.sharing.client.nickname=clientNickname;
@@ -6560,6 +6661,18 @@ async function saveHubConfig(){
6560
6661
  done();toast(t('sharing.cannotJoinSelf'),'error');return;
6561
6662
  }
6562
6663
  }catch(e){}
6664
+ try{
6665
+ var testUrl=clientAddr.indexOf('://')>-1?clientAddr:'http://'+clientAddr;
6666
+ testUrl=testUrl.replace(/\\/+$/,'');
6667
+ var tr=await fetch('/api/sharing/test-hub',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({hubUrl:testUrl})});
6668
+ var td=await tr.json();
6669
+ if(!td.ok){
6670
+ var errMsg=td.error==='cannot_join_self'?t('sharing.cannotJoinSelf'):(td.error||t('settings.hub.test.fail'));
6671
+ done();toast(errMsg,'error');return;
6672
+ }
6673
+ }catch(e){
6674
+ done();toast(t('settings.hub.test.fail')+': '+String(e),'error');return;
6675
+ }
6563
6676
  }
6564
6677
  }
6565
6678
 
@@ -6569,6 +6682,10 @@ async function saveHubConfig(){
6569
6682
  var confirmMsg=prevRole==='hub'?t('sharing.disable.confirm.hub'):t('sharing.disable.confirm.client');
6570
6683
  if(!(await confirmModal(confirmMsg,{danger:true}))){done();return;}
6571
6684
  }
6685
+ if(prevSharingEnabled&&sharingEnabled&&prevRole&&prevRole!==_sharingRole){
6686
+ var switchMsg=prevRole==='hub'?t('sharing.switch.hubToClient'):t('sharing.switch.clientToHub');
6687
+ if(!(await confirmModal(switchMsg,{danger:true}))){done();return;}
6688
+ }
6572
6689
 
6573
6690
  var ok=await doSaveConfig(cfg, saveBtn, 'hubSaved');
6574
6691
  if(ok){
@@ -6578,8 +6695,12 @@ async function saveHubConfig(){
6578
6695
  try{await fetch('/api/sharing/update-username',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({username:adminNameEl.value.trim()})});}catch(e){}
6579
6696
  }
6580
6697
  }
6698
+ _lastSidebarFingerprint='';
6699
+ _lastSettingsFingerprint='';
6581
6700
  loadSharingStatus(false);
6582
- var needsRestart=sharingEnabled||(prevSharingEnabled&&!sharingEnabled);
6701
+ var enabledChanged=(!!prevSharingEnabled)!==(!!sharingEnabled);
6702
+ var roleChanged=prevRole!==_sharingRole;
6703
+ var needsRestart=enabledChanged||roleChanged;
6583
6704
  if(sharingEnabled) updateHubShareInfo();
6584
6705
  if(needsRestart){
6585
6706
  setTimeout(function(){showRestartOverlay(t('settings.restart.waiting'));},300);
@@ -7008,13 +7129,19 @@ async function _livePollTick(){
7008
7129
  if(_livePollBusy||document.hidden) return;
7009
7130
  _livePollBusy=true;
7010
7131
  try{
7011
- if(sharingStatusCache&&sharingStatusCache.enabled) loadSharingStatus(false);
7132
+ if(sharingStatusCache&&sharingStatusCache.enabled&&_lastSharingConnStatus!=='rejected') loadSharingStatus(false);
7012
7133
  if(!_notifSSEConnected) pollNotifCount();
7013
7134
  pollAdminPending();
7014
7135
  if(_activeView==='admin') loadAdminData();
7015
- else if(_activeView==='memories'){loadStats();loadMemories();}
7016
- else if(_activeView==='tasks') loadTasks();
7017
- else if(_activeView==='skills') loadSkills();
7136
+ else if(_activeView==='memories'){
7137
+ var _searchVal=(document.getElementById('searchInput')||{}).value||'';
7138
+ if(!_searchVal.trim()){
7139
+ if(memorySearchScope==='hub') loadHubMemories(true);
7140
+ else{loadStats();loadMemories(null,true);}
7141
+ }
7142
+ }
7143
+ else if(_activeView==='tasks') loadTasks(true);
7144
+ else if(_activeView==='skills') loadSkills(true);
7018
7145
  else if(_activeView==='analytics') loadMetrics();
7019
7146
  }catch(e){}
7020
7147
  _livePollBusy=false;
@@ -7225,7 +7352,10 @@ function stopNotifPoll(){ }
7225
7352
 
7226
7353
  /* ─── Data loading ─── */
7227
7354
  async function loadAll(){
7228
- await Promise.all([loadStats(),loadMemories(),loadSharingStatus(false)]);
7355
+ await loadStats();
7356
+ var initOwner=memorySearchScope==='local'?_currentAgentOwner:undefined;
7357
+ if(initOwner) await loadStats(initOwner);
7358
+ await Promise.all([loadMemories(),loadSharingStatus(false)]);
7229
7359
  checkMigrateStatus();
7230
7360
  connectPPSSE();
7231
7361
  checkForUpdate();
@@ -7242,6 +7372,7 @@ async function loadStats(ownerFilter){
7242
7372
  d=await r.json();
7243
7373
  }catch(e){ d={}; }
7244
7374
  if(!d||typeof d!=='object') d={};
7375
+ if(d.currentAgentOwner) _currentAgentOwner=d.currentAgentOwner;
7245
7376
  const tm=d.totalMemories||0;
7246
7377
  const dedupB=d.dedupBreakdown||{};
7247
7378
  const activeCount=dedupB.active||tm;
@@ -7355,55 +7486,88 @@ function getFilterParams(){
7355
7486
  if(dt) p.set('dateTo',dt);
7356
7487
  const sort=document.getElementById('filterSort').value;
7357
7488
  if(sort==='oldest') p.set('sort','oldest');
7358
- const owner=document.getElementById('filterOwner').value;
7359
- if(owner) p.set('owner',owner);
7489
+ const scope=memorySearchScope||'local';
7490
+ if(scope==='local'){
7491
+ p.set('owner',_currentAgentOwner);
7492
+ }else{
7493
+ const owner=document.getElementById('filterOwner').value;
7494
+ if(owner) p.set('owner',owner);
7495
+ }
7360
7496
  return p;
7361
7497
  }
7362
7498
 
7363
- async function loadMemories(page){
7499
+ async function loadMemories(page,silent){
7364
7500
  if(page) currentPage=page;
7365
7501
  const list=document.getElementById('memoryList');
7366
- list.innerHTML='<div class="spinner"></div>';
7502
+ if(!silent) list.innerHTML='<div class="spinner"></div>';
7367
7503
  try{
7368
7504
  const p=getFilterParams();
7369
7505
  p.set('limit',PAGE_SIZE);
7370
7506
  p.set('page',currentPage);
7371
7507
  const r=await fetch('/api/memories?'+p.toString());
7372
7508
  const d=await r.json();
7509
+ var items=d.memories||[];
7510
+ if(silent){
7511
+ var fp=JSON.stringify(items.map(function(m){return m.id+'|'+m.updated_at}));
7512
+ if(fp===_lastMemoriesFingerprint) return;
7513
+ _lastMemoriesFingerprint=fp;
7514
+ }else{
7515
+ _lastMemoriesFingerprint=JSON.stringify(items.map(function(m){return m.id+'|'+m.updated_at}));
7516
+ }
7373
7517
  totalPages=d.totalPages||1;
7374
7518
  totalCount=d.total||0;
7375
7519
  document.getElementById('searchMeta').textContent=totalCount+t('search.meta.total');
7376
- renderMemories(d.memories||[]);
7520
+ renderMemories(items);
7377
7521
  renderPagination();
7378
7522
  }catch(e){
7379
- list.innerHTML='';
7380
- totalPages=1;totalCount=0;
7381
- renderMemories([]);
7382
- renderPagination();
7523
+ if(!silent){
7524
+ list.innerHTML='';
7525
+ totalPages=1;totalCount=0;
7526
+ _lastMemoriesFingerprint='';
7527
+ renderMemories([]);
7528
+ renderPagination();
7529
+ }
7383
7530
  }
7384
7531
  }
7385
7532
 
7386
- async function loadHubMemories(){
7533
+ async function loadHubMemories(silent){
7387
7534
  const list=document.getElementById('memoryList');
7388
- list.innerHTML='<div class="spinner"></div>';
7535
+ if(!silent) list.innerHTML='<div class="spinner"></div>';
7389
7536
  try{
7390
7537
  const r=await fetch('/api/sharing/memories/list?limit='+PAGE_SIZE);
7391
7538
  const d=await r.json();
7392
7539
  const items=d.memories||[];
7540
+ if(silent){
7541
+ var fp=JSON.stringify(items.map(function(m){return m.id+'|'+(m.updated_at||m.created_at)}));
7542
+ if(fp===_lastMemoriesFingerprint) return;
7543
+ _lastMemoriesFingerprint=fp;
7544
+ }else{
7545
+ _lastMemoriesFingerprint=JSON.stringify(items.map(function(m){return m.id+'|'+(m.updated_at||m.created_at)}));
7546
+ }
7547
+ totalPages=1;totalCount=items.length;currentPage=1;
7393
7548
  document.getElementById('searchMeta').textContent=items.length+t('search.meta.total');
7394
7549
  document.getElementById('sharingSearchMeta').textContent='';
7395
7550
  renderMemories(items);
7396
7551
  document.getElementById('pagination').innerHTML='';
7397
7552
  }catch(e){
7398
- document.getElementById('searchMeta').textContent='0'+t('search.meta.results');
7399
- renderMemories([]);
7400
- document.getElementById('pagination').innerHTML='';
7553
+ if(!silent){
7554
+ _lastMemoriesFingerprint='';
7555
+ document.getElementById('searchMeta').textContent='0'+t('search.meta.results');
7556
+ renderMemories([]);
7557
+ document.getElementById('pagination').innerHTML='';
7558
+ }
7401
7559
  }
7402
7560
  }
7403
7561
 
7404
7562
  async function doSearch(query){
7405
7563
  query=(query||'').trim();
7406
- if(!query){loadMemories();return;}
7564
+ if(!query){
7565
+ currentPage=1;
7566
+ if(memorySearchScope==='hub') loadHubMemories();
7567
+ else loadMemories();
7568
+ return;
7569
+ }
7570
+ currentPage=1;
7407
7571
  var scope=document.getElementById('memorySearchScope')?.value||memorySearchScope||'local';
7408
7572
  var list=document.getElementById('memoryList');
7409
7573
  list.innerHTML='<div class="spinner"></div>';
@@ -7415,6 +7579,7 @@ async function doSearch(query){
7415
7579
  body:JSON.stringify({query:query,scope:scope,maxResults:20,role:activeRole||undefined})
7416
7580
  });
7417
7581
  var data=await r.json();
7582
+ totalPages=1;totalCount=(data.results||[]).length;
7418
7583
  renderSharingMemorySearchResults(data,query);
7419
7584
  }catch(e){
7420
7585
  document.getElementById('searchMeta').textContent='0'+t('search.meta.results');
@@ -7429,6 +7594,7 @@ async function doSearch(query){
7429
7594
  var r=await fetch('/api/search?'+p.toString());
7430
7595
  var d=await r.json();
7431
7596
  var total=d.total||0;
7597
+ totalPages=1;totalCount=total;
7432
7598
  var meta=[];
7433
7599
  if(d.vectorCount>0) meta.push(d.vectorCount+t('search.meta.semantic'));
7434
7600
  if(d.ftsCount>0) meta.push(d.ftsCount+t('search.meta.text'));
@@ -7517,7 +7683,8 @@ function renderMemories(items){
7517
7683
  const isPublicMem=ownerVal==='public';
7518
7684
  const localManaged=!!m.localSharingManaged;
7519
7685
  const memShared=m.sharingVisibility||null;
7520
- const memScope=memShared?'team':isPublicMem?'local':'private';
7686
+ const isHubScope=memorySearchScope==='hub';
7687
+ const memScope=isHubScope?'team':memShared?'team':isPublicMem?'local':'private';
7521
7688
  const memScopeBadge=renderScopeBadge(memScope);
7522
7689
  let dedupInfo='';
7523
7690
  if(ds==='duplicate'||ds==='merged'){
@@ -7622,7 +7789,8 @@ function renderPagination(){
7622
7789
  function goPage(p){
7623
7790
  if(p<1||p>totalPages||p===currentPage) return;
7624
7791
  currentPage=p;
7625
- loadMemories();
7792
+ if(memorySearchScope==='hub') loadHubMemories();
7793
+ else loadMemories();
7626
7794
  document.getElementById('memoryList').scrollIntoView({behavior:'smooth',block:'start'});
7627
7795
  }
7628
7796
 
@@ -8538,7 +8706,7 @@ async function checkForUpdate(){
8538
8706
 
8539
8707
  /* ─── Init ─── */
8540
8708
  document.getElementById('modalOverlay').addEventListener('click',e=>{if(e.target.id==='modalOverlay')closeModal()});
8541
- document.getElementById('searchInput').addEventListener('keydown',e=>{if(e.key==='Escape'){e.target.value='';loadMemories()}});
8709
+ document.getElementById('searchInput').addEventListener('keydown',e=>{if(e.key==='Escape'){e.target.value='';currentPage=1;if(memorySearchScope==='hub')loadHubMemories();else loadMemories();}});
8542
8710
  applyI18n();
8543
8711
  checkAuth();
8544
8712
  </script>