@memtensor/memos-local-openclaw-plugin 1.0.7 → 1.0.8-beta.2

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.
@@ -126,19 +126,25 @@ input,textarea,select{font-family:inherit;font-size:inherit}
126
126
  .main-content{display:flex;flex:1;max-width:1400px;margin:0 auto;width:100%;padding:28px 32px;gap:28px}
127
127
 
128
128
  /* ─── Sidebar ─── */
129
- .sidebar{width:260px;min-width:260px;flex-shrink:0}
130
- .sidebar .stats-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:24px}
131
- .stat-card{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:18px;transition:all .2s}
129
+ .sidebar{width:260px;min-width:260px;flex-shrink:0;position:sticky;top:84px;max-height:calc(100vh - 112px);display:flex;flex-direction:column}
130
+ .sidebar > * {flex-shrink:0}
131
+ .sidebar .stats-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:20px}
132
+ .stat-card{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:14px 16px;transition:all .2s;position:relative;overflow:hidden}
133
+ .stat-card::before{content:'';position:absolute;top:0;left:0;bottom:0;width:3px;border-radius:3px 0 0 3px;background:var(--border)}
132
134
  .stat-card:hover{border-color:var(--border-glow);background:var(--bg-card-hover)}
133
- .stat-card .stat-value{font-size:22px;font-weight:700;color:var(--text);letter-spacing:-.02em}
134
- .stat-card .stat-label{font-size:12px;color:var(--text-sec);margin-top:4px;font-weight:500}
135
+ .stat-card .stat-value{font-size:20px;font-weight:700;color:var(--text);letter-spacing:-.02em}
136
+ .stat-card .stat-label{font-size:11px;color:var(--text-sec);margin-top:2px;font-weight:500}
137
+ .stat-card.pri{border-left-color:transparent}.stat-card.pri::before{background:var(--pri)}
135
138
  .stat-card.pri .stat-value{color:var(--pri)}
139
+ .stat-card.green{border-left-color:transparent}.stat-card.green::before{background:var(--green)}
136
140
  .stat-card.green .stat-value{color:var(--green)}
141
+ .stat-card.amber{border-left-color:transparent}.stat-card.amber::before{background:var(--amber)}
137
142
  .stat-card.amber .stat-value{color:var(--amber)}
143
+ .stat-card.rose{border-left-color:transparent}.stat-card.rose::before{background:var(--rose)}
138
144
  .stat-card.rose .stat-value{color:var(--rose)}
139
145
 
140
146
  .sidebar .section-title{font-size:11px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;margin:24px 0 12px;padding:0 2px}
141
- .sidebar .session-list{display:flex;flex-direction:column;gap:6px;max-height:280px;overflow-y:auto}
147
+ .sidebar .session-list{display:flex;flex-direction:column;gap:6px;flex:1;min-height:0;overflow-y:auto;padding-right:4px;flex-shrink:1}
142
148
  .session-item{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;background:var(--bg-card);border:1px solid var(--border);border-radius:10px;cursor:pointer;transition:all .15s;font-size:13px;color:var(--text)}
143
149
  .session-item:hover{border-color:var(--pri);background:var(--pri-glow)}
144
150
  .session-item.active{border-color:var(--pri);background:var(--pri-glow);font-weight:600;color:var(--pri)}
@@ -149,11 +155,12 @@ input,textarea,select{font-family:inherit;font-size:inherit}
149
155
 
150
156
  /* ─── Feed ─── */
151
157
  .feed{flex:1;min-width:0}
152
- .search-bar{display:flex;gap:10px;margin-bottom:16px;position:relative;align-items:center}
158
+ .search-bar{display:flex;gap:10px;margin-bottom:14px;position:relative;align-items:center}
153
159
  .search-bar input{flex:1;padding:10px 16px 10px 40px;border:1px solid var(--border);border-radius:10px;font-size:14px;outline:none;background:var(--bg-card);color:var(--text);transition:all .2s}
154
160
  .search-bar input::placeholder{color:var(--text-muted)}
155
161
  .search-bar input:focus{border-color:var(--pri);box-shadow:0 0 0 3px var(--pri-glow)}
156
162
  .search-bar .search-icon{position:absolute;left:14px;top:50%;transform:translateY(-50%);color:var(--text-muted);font-size:14px;pointer-events:none}
163
+ .search-bar .filter-select{padding:8px 14px;padding-right:30px;border-radius:10px;font-size:13px;background:var(--bg-card);flex-shrink:0}
157
164
  .search-meta{font-size:12px;color:var(--text-sec);padding:0 2px}.search-meta:not(:empty){margin-bottom:14px}
158
165
  .scope-select{padding:10px 12px;border:1px solid var(--border);border-radius:10px;background:var(--bg-card);color:var(--text);font-size:13px;min-width:110px;outline:none}
159
166
  .sharing-inline-meta{font-size:12px;color:var(--text-muted);margin:-8px 0 14px 2px}
@@ -370,13 +377,13 @@ input,textarea,select{font-family:inherit;font-size:inherit}
370
377
  .hub-source-badge{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;border-radius:999px;background:rgba(34,197,94,.12);color:var(--green);font-size:11px;font-weight:700;border:1px solid rgba(34,197,94,.22)}
371
378
  @media (max-width: 960px){.sharing-settings-grid{grid-template-columns:1fr}.search-bar{flex-wrap:wrap}.scope-select{width:100%}.task-detail-actions{width:100%;justify-content:flex-start}}
372
379
 
373
- .filter-bar{display:flex;gap:8px;margin-bottom:16px;flex-wrap:wrap}
374
- .filter-chip{padding:5px 14px;border:1px solid var(--border);border-radius:6px;background:transparent;color:var(--text-sec);font-size:12px;font-weight:500;transition:all .15s}
375
- .filter-chip:hover{border-color:var(--pri);color:var(--pri)}
376
- .filter-chip.active{background:rgba(99,102,241,.08);color:var(--pri);border-color:rgba(99,102,241,.25)}
380
+ .filter-bar{display:flex;gap:8px;margin-bottom:12px;flex-wrap:wrap;align-items:center}
381
+ .filter-chip{padding:5px 14px;border:1px solid var(--border);border-radius:8px;background:transparent;color:var(--text-sec);font-size:12px;font-weight:500;cursor:pointer;transition:all .15s}
382
+ .filter-chip:hover{border-color:var(--pri);color:var(--pri);background:rgba(99,102,241,.04)}
383
+ .filter-chip.active{background:rgba(99,102,241,.1);color:var(--pri);border-color:rgba(99,102,241,.3);font-weight:600}
377
384
 
378
- .memory-list{display:flex;flex-direction:column;gap:16px}
379
- .memory-card{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-lg);padding:20px 24px;transition:all .2s}
385
+ .memory-list{display:flex;flex-direction:column;gap:10px}
386
+ .memory-card{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:16px 20px;transition:all .2s}
380
387
  .memory-card:hover{border-color:var(--border-glow);background:var(--bg-card-hover)}
381
388
  .memory-card .card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;flex-wrap:wrap;gap:8px}
382
389
  .memory-card .meta{display:flex;align-items:center;gap:8px}
@@ -392,6 +399,13 @@ input,textarea,select{font-family:inherit;font-size:inherit}
392
399
  .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)}
393
400
  .card-actions{display:flex;align-items:center;gap:8px;margin-top:14px}
394
401
  .card-actions-inline{display:inline-flex;align-items:center;gap:4px;margin-left:auto;flex-shrink:0}
402
+ .btn-warn{color:#f59e0b !important}
403
+ .btn-warn:hover{background:rgba(245,158,11,.15) !important}
404
+ .btn-danger{color:#ef4444 !important}
405
+ .btn-danger:hover{background:rgba(239,68,68,.15) !important}
406
+ .btn-success{color:#10b981 !important}
407
+ .btn-success:hover{background:rgba(16,185,129,.15) !important}
408
+ .skill-card.archived{opacity:0.55;border-style:dashed}
395
409
  .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}
396
410
  .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}
397
411
  .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}
@@ -573,32 +587,39 @@ input,textarea,select{font-family:inherit;font-size:inherit}
573
587
  ::-webkit-scrollbar-thumb{background:rgba(255,255,255,.15);border-radius:3px}
574
588
  ::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.25)}
575
589
 
576
- .filter-sep{width:1px;height:20px;background:var(--border);margin:0 4px}
577
- .filter-select{padding:6px 12px;border:1px solid var(--border);border-radius:999px;background:var(--bg-card);color:var(--text-sec);font-size:13px;outline:none;cursor:pointer}
578
- .filter-select:focus{border-color:var(--pri)}
590
+ .filter-sep{width:1px;height:20px;background:var(--border);margin:0 2px}
591
+ .filter-select{padding:5px 14px;border:1px solid var(--border);border-radius:8px;background:transparent;color:var(--text-sec);font-size:12px;font-weight:500;outline:none;cursor:pointer;transition:all .15s;-webkit-appearance:none;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%236b7280'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;padding-right:28px}
592
+ .filter-select:hover{border-color:var(--pri);color:var(--pri)}
593
+ .filter-select:focus{border-color:var(--pri);color:var(--pri);background-color:rgba(99,102,241,.04)}
579
594
  .date-filter{display:flex;align-items:center;gap:10px;margin-bottom:18px;font-size:13px;color:var(--text-sec)}
580
- .date-filter input[type="datetime-local"]{padding:6px 10px;border:1px solid var(--border);border-radius:8px;font-size:12px;outline:none;background:var(--bg-card);color:var(--text)}
595
+ .date-filter input[type="datetime-local"]{padding:5px 12px;border:1px solid var(--border);border-radius:8px;font-size:12px;outline:none;background:transparent;color:var(--text-sec);transition:all .15s}
581
596
  .date-filter input[type="datetime-local"]:focus{border-color:var(--pri)}
582
597
  .date-filter label{font-weight:500}
583
-
584
- .pagination{display:flex;align-items:center;justify-content:center;gap:6px;padding:28px 0;flex-wrap:wrap}
585
- .pagination .pg-btn{min-width:38px;height:38px;display:flex;align-items:center;justify-content:center;border:1px solid var(--border);border-radius:10px;background:var(--bg-card);color:var(--text-sec);font-size:13px;font-weight:500;cursor:pointer;transition:all .15s}
586
- .pagination .pg-btn:hover{border-color:var(--pri);color:var(--pri)}
587
- .pagination .pg-btn.active{background:var(--pri);color:#000;border-color:var(--pri)}
588
- .pagination .pg-btn.disabled{opacity:.4;pointer-events:none}
589
- .pagination .pg-info{font-size:12px;color:var(--text-sec);padding:0 12px}
598
+ .compact-filter-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
599
+ .compact-date{padding:5px 12px;border:1px solid var(--border);border-radius:8px;font-size:12px;font-weight:500;outline:none;background:transparent;color:var(--text-sec);max-width:180px;transition:all .15s}
600
+ .compact-date:hover{border-color:var(--pri);color:var(--pri)}
601
+ .compact-date:focus{border-color:var(--pri);color:var(--pri);background:rgba(99,102,241,.04)}
602
+
603
+ .pagination-row{display:flex;align-items:center;justify-content:center;gap:10px;flex-wrap:wrap;padding:16px 0 8px}
604
+ .pagination{display:flex;align-items:center;justify-content:center;gap:4px;padding:0;flex-wrap:wrap}
605
+ .pagination .pg-btn{min-width:32px;height:32px;display:flex;align-items:center;justify-content:center;border:1px solid transparent;border-radius:8px;background:transparent;color:var(--text-sec);font-size:12px;font-weight:500;cursor:pointer;transition:all .15s}
606
+ .pagination .pg-btn:hover{background:rgba(99,102,241,.06);color:var(--pri);border-color:rgba(99,102,241,.15)}
607
+ .pagination .pg-btn.active{background:var(--pri-grad);color:#fff;border-color:transparent;box-shadow:0 2px 8px rgba(99,102,241,.3)}
608
+ .pagination .pg-btn.disabled{opacity:.3;pointer-events:none}
609
+ .pagination .pg-info{font-size:11px;color:var(--text-muted);padding:0 8px}
590
610
 
591
611
  /* ─── Tasks 视图 ─── */
592
612
  .view-container{flex:1;min-width:0}
593
613
  .view-container>.vp{display:none;flex-direction:column}
594
614
  .view-container>.vp.show{display:flex}
595
- .tasks-view{flex:1;min-width:0;flex-direction:column;gap:16px}
596
- .tasks-header{display:flex;flex-direction:column;gap:14px}
597
- .tasks-stats{display:flex;gap:16px}
598
- .tasks-stat{display:flex;align-items:center;gap:8px;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:12px 18px;flex:1;transition:all .2s}
615
+ .tasks-view{flex:1;min-width:0;flex-direction:column}
616
+ .tasks-header{display:flex;flex-direction:column;gap:0}
617
+ .tasks-stats{display:flex;gap:10px}
618
+ .tasks-stat{display:flex;align-items:center;gap:8px;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:10px 16px;flex:1;transition:all .2s;position:relative;overflow:hidden}
619
+ .tasks-stat::before{content:'';position:absolute;top:0;left:0;bottom:0;width:3px;border-radius:3px 0 0 3px}
599
620
  .tasks-stat:hover{border-color:var(--border-glow)}
600
- .tasks-stat-value{font-size:22px;font-weight:700;color:var(--text)}
601
- .tasks-stat-label{font-size:12px;color:var(--text-sec);font-weight:500}
621
+ .tasks-stat-value{font-size:18px;font-weight:700;color:var(--text)}
622
+ .tasks-stat-label{font-size:11px;color:var(--text-sec);font-weight:500}
602
623
  .tasks-filters{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
603
624
  .tasks-list{display:flex;flex-direction:column;gap:10px}
604
625
  .task-card{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-lg);padding:18px 20px;cursor:pointer;transition:all .25s;position:relative;overflow:hidden}
@@ -670,7 +691,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
670
691
  [data-theme="light"] .tasks-stat{background:#fff}
671
692
 
672
693
  /* ─── Skills ─── */
673
- .skills-view{flex:1;min-width:0;flex-direction:column;gap:16px}
694
+ .skills-view{flex:1;min-width:0;flex-direction:column}
674
695
  .skill-card{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-lg);padding:18px 20px;cursor:pointer;transition:all .25s;position:relative;overflow:hidden}
675
696
  .skill-card:hover{border-color:var(--border-glow);background:var(--bg-card-hover);transform:translateY(-1px);box-shadow:var(--shadow)}
676
697
  .skill-card::before{content:'';position:absolute;top:0;left:0;bottom:0;width:3px;border-radius:3px 0 0 3px;background:var(--violet)}
@@ -697,6 +718,10 @@ input,textarea,select{font-family:inherit;font-size:inherit}
697
718
  .skill-card-bottom .tag{display:flex;align-items:center;gap:4px}
698
719
  .skill-card-tags{display:flex;gap:4px;flex-wrap:wrap}
699
720
  .skill-tag{font-size:10px;padding:2px 8px;border-radius:10px;background:rgba(139,92,246,.1);color:var(--violet);font-weight:500}
721
+ .selection-toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
722
+ #memorySelectionToolbar{margin-bottom:16px}
723
+ .item-select-box{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:5px;border:1px solid var(--border);background:var(--bg-card);cursor:pointer;margin-right:8px;vertical-align:middle}
724
+ .item-select-box input{width:14px;height:14px;cursor:pointer}
700
725
  .skill-detail-desc{font-size:13px;color:var(--text-sec);line-height:1.6;margin-bottom:16px;padding:12px 16px;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius)}
701
726
  .skill-version-item{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:12px 16px}
702
727
  .skill-version-header{display:flex;align-items:center;gap:10px;margin-bottom:6px}
@@ -1225,8 +1250,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
1225
1250
  <div class="sidebar" id="sidebar">
1226
1251
  <div class="stats-grid" id="statsGrid">
1227
1252
  <div class="stat-card pri"><div class="stat-value" id="statTotal">-</div><div class="stat-label" data-i18n="stat.memories">Memories</div></div>
1228
- <div class="stat-card green"><div class="stat-value" id="statSessions">-</div><div class="stat-label" data-i18n="stat.sessions">Sessions</div></div>
1229
- <div class="stat-card amber"><div class="stat-value" id="statEmbeddings">-</div><div class="stat-label" data-i18n="stat.embeddings">Embeddings</div></div>
1253
+ <div class="stat-card green"><div class="stat-value" id="statTasks">-</div><div class="stat-label" data-i18n="stat.tasks">Tasks</div></div>
1254
+ <div class="stat-card amber"><div class="stat-value" id="statSkills">-</div><div class="stat-label" data-i18n="stat.skills">Skills</div></div>
1230
1255
  <div class="stat-card rose"><div class="stat-value" id="statAgents">-</div><div class="stat-label" data-i18n="stat.agents">Agents</div></div>
1231
1256
  </div>
1232
1257
  <div id="sidebarSharingSection" style="display:none">
@@ -1237,8 +1262,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
1237
1262
  </div>
1238
1263
  </div>
1239
1264
  <div id="embeddingStatus"></div>
1265
+ <button class="btn btn-sm btn-ghost" style="width:100%;margin:20px 0 12px;justify-content:center;color:var(--text-muted);font-size:11px" onclick="clearAll()" data-i18n="sidebar.clear">\u{1F5D1} Clear All Data</button>
1240
1266
  <div class="session-list" id="sessionList" style="display:none"></div>
1241
- <button class="btn btn-sm btn-ghost" style="width:100%;margin-top:20px;justify-content:center;color:var(--text-muted);font-size:11px" onclick="clearAll()" data-i18n="sidebar.clear">\u{1F5D1} Clear All Data</button>
1242
1267
  </div>
1243
1268
 
1244
1269
  <div class="view-container">
@@ -1251,14 +1276,13 @@ input,textarea,select{font-family:inherit;font-size:inherit}
1251
1276
  <option value="" data-i18n="filter.allagents">All agents</option>
1252
1277
  </select>
1253
1278
  <select id="memorySearchScope" class="filter-select" onchange="onMemoryScopeChange()" style="display:none">
1254
- <option value="local" data-i18n="scope.thisAgent">This Agent</option>
1255
1279
  <option value="allLocal" data-i18n="scope.thisDevice">This Device</option>
1256
1280
  <option value="hub" data-i18n="scope.hub">Team</option>
1257
1281
  </select>
1258
1282
  </div>
1259
- <div class="search-meta" id="searchMeta"></div>
1260
- <div class="search-meta" id="sharingSearchMeta"></div>
1261
- <div class="filter-bar" id="filterBar">
1283
+ <div id="searchMeta" style="display:none"></div>
1284
+ <div id="sharingSearchMeta" style="display:none"></div>
1285
+ <div class="filter-bar compact-filter-row" id="filterBar" style="flex-wrap:nowrap">
1262
1286
  <button class="filter-chip active" data-role="" onclick="setRoleFilter(this,'')" data-i18n="filter.all">All</button>
1263
1287
  <button class="filter-chip" data-role="user" onclick="setRoleFilter(this,'user')">User</button>
1264
1288
  <button class="filter-chip" data-role="assistant" onclick="setRoleFilter(this,'assistant')">Assistant</button>
@@ -1267,42 +1291,48 @@ input,textarea,select{font-family:inherit;font-size:inherit}
1267
1291
  <option value="newest" data-i18n="filter.newest">Newest first</option>
1268
1292
  <option value="oldest" data-i18n="filter.oldest">Oldest first</option>
1269
1293
  </select>
1270
- <span class="filter-sep"></span>
1271
- <select id="filterSession" class="filter-select" onchange="filterSession(this.value||null)">
1272
- <option value="" data-i18n="filter.allsessions">All sessions</option>
1273
- </select>
1294
+ <span style="flex:1"></span>
1295
+ <button class="filter-chip" id="memorySelectAllBtn" onclick="toggleSelectAllMemories()" data-i18n="skills.selectAll">Select All</button>
1296
+ <button class="filter-chip" id="memoryBulkDeleteBtn" onclick="deleteSelectedMemories()" style="display:none;color:#ef4444;border-color:rgba(239,68,68,.3)" data-i18n="memory.deleteSelected">Delete Selected</button>
1274
1297
  </div>
1275
- <div class="date-filter">
1276
- <label data-i18n="filter.from">From</label><input type="datetime-local" id="dateFrom" step="1" onchange="applyFilters()">
1277
- <label data-i18n="filter.to">To</label><input type="datetime-local" id="dateTo" step="1" onchange="applyFilters()">
1278
- <button class="btn btn-sm btn-text" onclick="clearDateFilter()" data-i18n="filter.clear">Clear</button>
1298
+ <div class="filter-bar compact-filter-row" style="flex-wrap:nowrap;margin-bottom:12px">
1299
+ <input type="datetime-local" id="dateFrom" step="1" onchange="applyFilters()" class="compact-date" data-i18n-ph="filter.from" placeholder="From">
1300
+ <span style="color:var(--text-muted);font-size:12px">—</span>
1301
+ <input type="datetime-local" id="dateTo" step="1" onchange="applyFilters()" class="compact-date" data-i18n-ph="filter.to" placeholder="To">
1302
+ <button class="filter-chip" onclick="clearDateFilter()" data-i18n="filter.clear" style="font-size:11px;padding:4px 10px">Clear</button>
1279
1303
  </div>
1280
1304
  <div class="memory-list" id="memoryList"><div class="spinner"></div></div>
1281
- <div class="pagination" id="pagination"></div>
1305
+ <div class="pagination-row">
1306
+ <div class="pagination" id="pagination"></div>
1307
+ </div>
1282
1308
  </div>
1283
1309
  </div>
1284
1310
  <div class="tasks-view vp" id="tasksView">
1285
- <div class="tasks-header">
1286
- <div class="tasks-stats">
1287
- <div class="tasks-stat"><span class="tasks-stat-value" id="tasksTotalCount">-</span><span class="tasks-stat-label" data-i18n="tasks.total">Total Tasks</span></div>
1288
- <div class="tasks-stat"><span class="tasks-stat-value" id="tasksActiveCount">-</span><span class="tasks-stat-label" data-i18n="tasks.active">Active</span></div>
1289
- <div class="tasks-stat"><span class="tasks-stat-value" id="tasksCompletedCount">-</span><span class="tasks-stat-label" data-i18n="tasks.completed">Completed</span></div>
1290
- <div class="tasks-stat"><span class="tasks-stat-value" id="tasksSkippedCount">-</span><span class="tasks-stat-label" data-i18n="tasks.status.skipped">Skipped</span></div>
1291
- </div>
1292
- <div class="tasks-filters">
1293
- <button class="filter-chip active" data-task-status="" onclick="setTaskStatusFilter(this,'')" data-i18n="filter.all">All</button>
1294
- <button class="filter-chip" data-task-status="active" onclick="setTaskStatusFilter(this,'active')" data-i18n="tasks.status.active">Active</button>
1295
- <button class="filter-chip" data-task-status="completed" onclick="setTaskStatusFilter(this,'completed')" data-i18n="tasks.status.completed">Completed</button>
1296
- <button class="filter-chip" data-task-status="skipped" onclick="setTaskStatusFilter(this,'skipped')" data-i18n="tasks.status.skipped">Skipped</button>
1297
- <select id="taskSearchScope" class="scope-select" onchange="onTaskScopeChange()" style="display:none">
1298
- <option value="local" data-i18n="scope.thisAgent">This Agent</option>
1299
- <option value="allLocal" data-i18n="scope.thisDevice">This Device</option>
1300
- <option value="hub" data-i18n="scope.hub">Team</option>
1301
- </select>
1302
- </div>
1311
+ <div class="search-bar">
1312
+ <span class="search-icon">\u{1F50D}</span>
1313
+ <input type="text" id="taskSearchInput" data-i18n-ph="tasks.search.placeholder" placeholder="Search tasks..." oninput="debounceTaskSearch()">
1314
+ <select id="taskFilterOwner" class="filter-select" onchange="onOwnerFilterChange()">
1315
+ <option value="" data-i18n="filter.allagents">All agents</option>
1316
+ </select>
1317
+ <select id="taskSearchScope" class="scope-select" onchange="onTaskScopeChange()" style="display:none">
1318
+ <option value="allLocal" data-i18n="scope.thisDevice">This Device</option>
1319
+ <option value="hub" data-i18n="scope.hub">Team</option>
1320
+ </select>
1321
+ </div>
1322
+ <div id="taskSearchMeta" style="display:none"></div>
1323
+ <div class="filter-bar compact-filter-row" style="flex-wrap:nowrap">
1324
+ <button class="filter-chip active" data-task-status="" onclick="setTaskStatusFilter(this,'')" data-i18n="filter.all">All</button>
1325
+ <button class="filter-chip" data-task-status="active" onclick="setTaskStatusFilter(this,'active')" data-i18n="tasks.status.active">Active</button>
1326
+ <button class="filter-chip" data-task-status="completed" onclick="setTaskStatusFilter(this,'completed')" data-i18n="tasks.status.completed">Completed</button>
1327
+ <button class="filter-chip" data-task-status="skipped" onclick="setTaskStatusFilter(this,'skipped')" data-i18n="tasks.status.skipped">Skipped</button>
1328
+ <span style="flex:1"></span>
1329
+ <button class="filter-chip" id="taskSelectAllBtn" onclick="toggleSelectAllTasks()" data-i18n="skills.selectAll">Select All</button>
1330
+ <button class="filter-chip" id="taskBulkDeleteBtn" onclick="deleteSelectedTasks()" style="display:none;color:#ef4444;border-color:rgba(239,68,68,.3)" data-i18n="task.deleteSelected">Delete Selected</button>
1303
1331
  </div>
1304
1332
  <div class="tasks-list" id="tasksList"><div class="spinner"></div></div>
1305
- <div class="pagination" id="tasksPagination"></div>
1333
+ <div class="pagination-row">
1334
+ <div class="pagination" id="tasksPagination"></div>
1335
+ </div>
1306
1336
  <div class="task-detail-overlay" id="taskDetailOverlay" onclick="closeTaskDetail(event)">
1307
1337
  <div class="task-detail-panel" onclick="event.stopPropagation()">
1308
1338
  <div class="task-detail-header">
@@ -1336,34 +1366,33 @@ input,textarea,select{font-family:inherit;font-size:inherit}
1336
1366
  <div class="search-bar">
1337
1367
  <span class="search-icon">🔍</span>
1338
1368
  <input type="text" id="skillSearchInput" placeholder="Search skills..." data-i18n-ph="skills.search.placeholder" oninput="debounceSkillSearch()">
1369
+ <select id="skillFilterOwner" class="filter-select" onchange="onOwnerFilterChange()">
1370
+ <option value="" data-i18n="filter.allagents">All agents</option>
1371
+ </select>
1339
1372
  <select id="skillSearchScope" class="scope-select" onchange="onSkillScopeChange()" style="display:none">
1340
- <option value="local" data-i18n="scope.thisAgent">This Agent</option>
1341
1373
  <option value="allLocal" data-i18n="scope.thisDevice">This Device</option>
1342
1374
  <option value="hub" data-i18n="scope.hub">Team</option>
1343
1375
  </select>
1344
1376
  </div>
1345
- <div class="search-meta" id="skillSearchMeta" style="display:none"></div>
1346
- <div class="tasks-header">
1347
- <div class="tasks-stats">
1348
- <div class="tasks-stat"><span class="tasks-stat-value" id="skillsTotalCount">-</span><span class="tasks-stat-label" data-i18n="skills.total">Total Skills</span></div>
1349
- <div class="tasks-stat" style="border-left:3px solid var(--green)"><span class="tasks-stat-value" id="skillsActiveCount">-</span><span class="tasks-stat-label" data-i18n="skills.active">Active</span></div>
1350
- <div class="tasks-stat" style="border-left:3px solid var(--amber)"><span class="tasks-stat-value" id="skillsDraftCount">-</span><span class="tasks-stat-label" data-i18n="skills.draft">Draft</span></div>
1351
- <div class="tasks-stat" style="border-left:3px solid var(--violet)"><span class="tasks-stat-value" id="skillsInstalledCount">-</span><span class="tasks-stat-label" data-i18n="skills.installed">Installed</span></div>
1352
- <div class="tasks-stat" style="border-left:3px solid var(--cyan)"><span class="tasks-stat-value" id="skillsPublicCount">-</span><span class="tasks-stat-label" data-i18n="skills.public">Public</span></div>
1353
- </div>
1354
- <div class="tasks-filters">
1355
- <button class="filter-chip active" data-skill-status="" onclick="setSkillStatusFilter(this,'')" data-i18n="filter.all">All</button>
1356
- <button class="filter-chip" data-skill-status="active" onclick="setSkillStatusFilter(this,'active')" data-i18n="skills.filter.active">Active</button>
1357
- <button class="filter-chip" data-skill-status="draft" onclick="setSkillStatusFilter(this,'draft')" data-i18n="skills.filter.draft">Draft</button>
1358
- <button class="filter-chip" data-skill-status="archived" onclick="setSkillStatusFilter(this,'archived')" data-i18n="skills.filter.archived">Archived</button>
1359
- <select id="skillVisibilityFilter" class="filter-select" onchange="loadSkills()" style="display:none">
1360
- <option value="" data-i18n="filter.allvisibility">All visibility</option>
1361
- <option value="public" data-i18n="filter.public">Public</option>
1362
- <option value="private" data-i18n="filter.private">Private</option>
1363
- </select>
1364
- </div>
1377
+ <div id="skillSearchMeta" style="display:none"></div>
1378
+ <div class="filter-bar compact-filter-row" style="flex-wrap:nowrap">
1379
+ <button class="filter-chip active" data-skill-status="" onclick="setSkillStatusFilter(this,'')" data-i18n="filter.all">All</button>
1380
+ <button class="filter-chip" data-skill-status="active" onclick="setSkillStatusFilter(this,'active')" data-i18n="skills.filter.active">Active</button>
1381
+ <button class="filter-chip" data-skill-status="draft" onclick="setSkillStatusFilter(this,'draft')" data-i18n="skills.filter.draft">Draft</button>
1382
+ <button class="filter-chip" data-skill-status="archived" onclick="setSkillStatusFilter(this,'archived')" data-i18n="skills.filter.archived">Archived</button>
1383
+ <select id="skillVisibilityFilter" class="filter-select" onchange="loadSkills()" style="display:none">
1384
+ <option value="" data-i18n="filter.allvisibility">All visibility</option>
1385
+ <option value="public" data-i18n="filter.public">Public</option>
1386
+ <option value="private" data-i18n="filter.private">Private</option>
1387
+ </select>
1388
+ <span style="flex:1"></span>
1389
+ <button class="filter-chip" id="skillSelectAllBtn" onclick="toggleSelectAllSkills()" data-i18n="skills.selectAll">Select All</button>
1390
+ <button class="filter-chip" id="skillBulkDeleteBtn" onclick="deleteSelectedSkills()" style="display:none;color:#ef4444;border-color:rgba(239,68,68,.3)" data-i18n="skills.deleteSelected">Delete Selected</button>
1365
1391
  </div>
1366
1392
  <div class="tasks-list" id="skillsList"><div class="spinner"></div></div>
1393
+ <div class="pagination-row">
1394
+ <div class="pagination" id="skillsPagination"></div>
1395
+ </div>
1367
1396
  <div id="hubSkillsSection" style="display:none;margin-top:16px">
1368
1397
  <div class="section-title" style="margin-bottom:12px" data-i18n="skills.hub.title">\u{1F310} Team Skills</div>
1369
1398
  <div class="tasks-list" id="hubSkillsList"></div>
@@ -2029,11 +2058,21 @@ input,textarea,select{font-family:inherit;font-size:inherit}
2029
2058
  <div class="toast-container" id="toasts"></div>
2030
2059
 
2031
2060
  <script>
2032
- let activeSession=null,activeRole='',editingId=null,searchTimer=null,memoryCache={},currentPage=1,totalPages=1,totalCount=0,PAGE_SIZE=40,metricsDays=30;
2033
- let memorySearchScope='local',skillSearchScope='local',taskSearchScope='local';
2061
+ let activeSession=null,activeRole='',editingId=null,searchTimer=null,memoryCache={},currentPage=1,totalPages=1,totalCount=0,PAGE_SIZE=20,metricsDays=30;
2062
+ let memorySearchScope='allLocal',skillSearchScope='allLocal',taskSearchScope='allLocal';
2034
2063
  let _lastMemoriesFingerprint='',_lastTasksFingerprint='',_lastSkillsFingerprint='';
2064
+ let selectedMemoryIds=new Set(),currentMemoryIds=[];
2065
+ let selectedTaskIds=new Set(),currentTaskIds=[];
2035
2066
  let _embeddingWarningShown=false;
2036
2067
  let _currentAgentOwner='agent:main';
2068
+ try {
2069
+ const urlParams = new URLSearchParams(window.location.search);
2070
+ const agentId = urlParams.get('agentId');
2071
+ if (agentId) {
2072
+ _currentAgentOwner = 'agent:' + agentId;
2073
+ }
2074
+ } catch(e) {}
2075
+
2037
2076
 
2038
2077
  /* ─── i18n ─── */
2039
2078
  const I18N={
@@ -2098,6 +2137,8 @@ const I18N={
2098
2137
  'tasks.status.active':'Active',
2099
2138
  'tasks.status.completed':'Completed',
2100
2139
  'tasks.status.skipped':'Skipped',
2140
+ 'tasks.search.placeholder':'Search tasks...',
2141
+ 'tasks.search.meta':'Found {0} tasks',
2101
2142
  'tasks.empty':'No tasks yet. Tasks are automatically created as you converse.',
2102
2143
  'tasks.loading':'Loading...',
2103
2144
  'tasks.untitled':'Untitled Task',
@@ -2138,8 +2179,8 @@ const I18N={
2138
2179
  'notif.timeAgo.hour':'{n}h ago',
2139
2180
  'notif.timeAgo.day':'{n}d ago',
2140
2181
  'stat.memories':'Memories',
2141
- 'stat.sessions':'Sessions',
2142
- 'stat.embeddings':'Embeddings',
2182
+ 'stat.tasks':'Tasks',
2183
+ 'stat.skills':'Skills',
2143
2184
  'stat.agents':'Agents',
2144
2185
  'stat.active':'active',
2145
2186
  'stat.deduped':'deduped',
@@ -2179,6 +2220,7 @@ const I18N={
2179
2220
  'card.dedupTarget':'Target: ',
2180
2221
  'card.dedupReason':'Reason: ',
2181
2222
  'card.newSummary':'New',
2223
+ 'pagination.pageSize':'Items per page',
2182
2224
  'pagination.total':' total',
2183
2225
  'range':'Range',
2184
2226
  'range.days':'days',
@@ -2385,9 +2427,18 @@ const I18N={
2385
2427
  'skills.nochangelog':'No changelog',
2386
2428
  'skills.status.active':'Active',
2387
2429
  'skills.status.draft':'Draft',
2388
- 'skills.status.archived':'Archived',
2430
+ 'skills.status.archived':'Disabled',
2431
+ 'skills.action.disable':'Disable',
2432
+ 'skills.action.enable':'Enable',
2433
+ 'skills.action.delete':'Delete',
2434
+ 'skills.disable.confirm':'Are you sure you want to disable this skill? It will no longer be used in search or auto-recall, but can be re-enabled later.',
2435
+ 'skills.disable.error':'Failed to disable skill: ',
2436
+ 'skills.enable.error':'Failed to enable skill: ',
2389
2437
  'skills.updated':'Updated: ',
2390
2438
  'skills.task.prefix':'Task: ',
2439
+ 'skills.selectAll':'Select All',
2440
+ 'skills.unselectAll':'Unselect All',
2441
+ 'skills.deleteSelected':'Delete Selected',
2391
2442
  'tasks.chunks.label':'chunks',
2392
2443
  'tasks.taskid':'Task ID: ',
2393
2444
  'tasks.role.user':'You',
@@ -2728,6 +2779,10 @@ const I18N={
2728
2779
  'task.cancel':'Cancel',
2729
2780
  'task.delete.confirm':'Are you sure you want to delete this task? This cannot be undone.',
2730
2781
  'task.delete.error':'Failed to delete task: ',
2782
+ 'task.deleteSelected':'Delete Selected',
2783
+ 'task.delete.selected.confirm':'Delete {count} selected tasks? This action cannot be undone.',
2784
+ 'task.delete.success':'Deleted {count} tasks.',
2785
+ 'task.delete.partial':'Deleted {ok} tasks, failed {fail}.',
2731
2786
  'task.save.error':'Failed to save task: ',
2732
2787
  'task.retrySkill':'Retry Skill Generation',
2733
2788
  'task.retrySkill.short':'Retry Skill',
@@ -2738,7 +2793,14 @@ const I18N={
2738
2793
  'skill.save':'Save',
2739
2794
  'skill.cancel':'Cancel',
2740
2795
  'skill.delete.confirm':'Are you sure you want to delete this skill? This will also remove all associated files and cannot be undone.',
2796
+ 'skill.delete.selected.confirm':'Delete {count} selected skills? This action cannot be undone.',
2741
2797
  'skill.delete.error':'Failed to delete skill: ',
2798
+ 'skill.delete.partial':'Deleted {ok} skills, failed {fail}.',
2799
+ 'skill.delete.success':'Deleted {count} skills.',
2800
+ 'memory.deleteSelected':'Delete Selected',
2801
+ 'memory.delete.selected.confirm':'Delete {count} selected memories? This action cannot be undone.',
2802
+ 'memory.delete.success':'Deleted {count} memories.',
2803
+ 'memory.delete.partial':'Deleted {ok} memories, failed {fail}.',
2742
2804
  'skill.save.error':'Failed to save skill: ',
2743
2805
  'update.available':'New version available',
2744
2806
  'update.run':'Run',
@@ -2842,6 +2904,8 @@ const I18N={
2842
2904
  'tasks.status.active':'进行中',
2843
2905
  'tasks.status.completed':'已完成',
2844
2906
  'tasks.status.skipped':'已跳过',
2907
+ 'tasks.search.placeholder':'搜索任务...',
2908
+ 'tasks.search.meta':'找到 {0} 个任务',
2845
2909
  'tasks.empty':'暂无任务。任务会随着对话自动创建。',
2846
2910
  'tasks.loading':'加载中...',
2847
2911
  'tasks.untitled':'未命名任务',
@@ -2882,8 +2946,8 @@ const I18N={
2882
2946
  'notif.timeAgo.hour':'{n}小时前',
2883
2947
  'notif.timeAgo.day':'{n}天前',
2884
2948
  'stat.memories':'记忆',
2885
- 'stat.sessions':'会话',
2886
- 'stat.embeddings':'嵌入',
2949
+ 'stat.tasks':'任务',
2950
+ 'stat.skills':'技能',
2887
2951
  'stat.agents':'智能体',
2888
2952
  'stat.active':'活跃',
2889
2953
  'stat.deduped':'已去重',
@@ -2923,6 +2987,7 @@ const I18N={
2923
2987
  'card.dedupTarget':'关联: ',
2924
2988
  'card.dedupReason':'原因: ',
2925
2989
  'card.newSummary':'新摘要',
2990
+ 'pagination.pageSize':'每页数量',
2926
2991
  'pagination.total':' 条',
2927
2992
  'range':'范围',
2928
2993
  'range.days':'天',
@@ -3129,9 +3194,18 @@ const I18N={
3129
3194
  'skills.nochangelog':'暂无变更记录',
3130
3195
  'skills.status.active':'生效中',
3131
3196
  'skills.status.draft':'草稿',
3132
- 'skills.status.archived':'已归档',
3197
+ 'skills.status.archived':'已禁用',
3198
+ 'skills.action.disable':'禁用',
3199
+ 'skills.action.enable':'启用',
3200
+ 'skills.action.delete':'删除',
3201
+ 'skills.disable.confirm':'确定要禁用此技能吗?禁用后不再参与检索和自动召回,但可以随时重新启用。',
3202
+ 'skills.disable.error':'禁用技能失败:',
3203
+ 'skills.enable.error':'启用技能失败:',
3133
3204
  'skills.updated':'更新于:',
3134
3205
  'skills.task.prefix':'任务:',
3206
+ 'skills.selectAll':'全选',
3207
+ 'skills.unselectAll':'取消全选',
3208
+ 'skills.deleteSelected':'删除选中',
3135
3209
  'tasks.chunks.label':'条记忆',
3136
3210
  'tasks.taskid':'任务 ID:',
3137
3211
  'tasks.role.user':'你',
@@ -3472,6 +3546,10 @@ const I18N={
3472
3546
  'task.cancel':'取消',
3473
3547
  'task.delete.confirm':'确定要删除此任务吗?此操作不可撤销。',
3474
3548
  'task.delete.error':'删除任务失败:',
3549
+ 'task.deleteSelected':'删除选中',
3550
+ 'task.delete.selected.confirm':'确定删除选中的 {count} 个任务吗?此操作不可撤销。',
3551
+ 'task.delete.success':'已删除 {count} 个任务。',
3552
+ 'task.delete.partial':'已删除 {ok} 个任务,失败 {fail} 个。',
3475
3553
  'task.save.error':'保存任务失败:',
3476
3554
  'task.retrySkill':'重新生成技能',
3477
3555
  'task.retrySkill.short':'重试技能',
@@ -3482,7 +3560,14 @@ const I18N={
3482
3560
  'skill.save':'保存',
3483
3561
  'skill.cancel':'取消',
3484
3562
  'skill.delete.confirm':'确定要删除此技能吗?关联的文件也会被删除,此操作不可撤销。',
3563
+ 'skill.delete.selected.confirm':'确定删除选中的 {count} 个技能吗?此操作不可撤销。',
3485
3564
  'skill.delete.error':'删除技能失败:',
3565
+ 'skill.delete.partial':'已删除 {ok} 个技能,失败 {fail} 个。',
3566
+ 'skill.delete.success':'已删除 {count} 个技能。',
3567
+ 'memory.deleteSelected':'删除选中',
3568
+ 'memory.delete.selected.confirm':'确定删除选中的 {count} 条记忆吗?此操作不可撤销。',
3569
+ 'memory.delete.success':'已删除 {count} 条记忆。',
3570
+ 'memory.delete.partial':'已删除 {ok} 条记忆,失败 {fail} 条。',
3486
3571
  'skill.save.error':'保存技能失败:',
3487
3572
  'update.available':'发现新版本',
3488
3573
  'update.run':'执行命令',
@@ -3767,9 +3852,12 @@ function switchView(view){
3767
3852
  }
3768
3853
  var sessionSection=document.getElementById('sidebarSessionSection');
3769
3854
  if(sessionSection){
3770
- if(view==='memories'){sessionSection.style.visibility='';sessionSection.style.pointerEvents='';}
3855
+ if(view==='memories'||view==='tasks'||view==='skills'){sessionSection.style.visibility='';sessionSection.style.pointerEvents='';}
3771
3856
  else{sessionSection.style.visibility='hidden';sessionSection.style.pointerEvents='none';}
3772
3857
  }
3858
+ if(view==='memories'||view==='tasks'||view==='skills'){
3859
+ loadStats();
3860
+ }
3773
3861
  if(view==='tasks') loadTasks();
3774
3862
  else if(view==='skills') loadSkills();
3775
3863
  else if(view==='analytics') loadMetrics();
@@ -3786,36 +3874,84 @@ function switchView(view){
3786
3874
  }
3787
3875
 
3788
3876
  function onMemoryScopeChange(){
3789
- memorySearchScope=document.getElementById('memorySearchScope')?.value||'local';
3877
+ memorySearchScope=document.getElementById('memorySearchScope')?.value||'allLocal';
3790
3878
  try{localStorage.setItem('memos_memorySearchScope',memorySearchScope);}catch(e){}
3791
3879
  currentPage=1;
3792
3880
  activeSession=null;activeRole='';
3793
3881
  _lastMemoriesFingerprint='';
3882
+ if(memorySearchScope==='hub') selectedMemoryIds.clear();
3794
3883
  var isHub=memorySearchScope==='hub';
3795
- var isLocal=memorySearchScope==='local';
3796
3884
  var ownerSel=document.getElementById('filterOwner');
3797
3885
  var filterBar=document.getElementById('filterBar');
3798
3886
  var dateFilter=document.querySelector('.date-filter');
3799
- if(ownerSel){ownerSel.style.display=(isHub||isLocal)?'none':'';if(isHub||isLocal)ownerSel.value='';}
3887
+ if(ownerSel){ownerSel.style.display=isHub?'none':'';if(isHub)ownerSel.value='';}
3800
3888
  if(filterBar) filterBar.style.display=isHub?'none':'';
3801
3889
  if(dateFilter) dateFilter.style.display=isHub?'none':'';
3890
+ updateMemorySelectionToolbar();
3802
3891
  if(document.getElementById('searchInput').value.trim()) doSearch(document.getElementById('searchInput').value);
3803
3892
  else if(isHub) { document.getElementById('sharingSearchMeta').textContent=''; loadHubMemories(); }
3804
3893
  else {
3805
3894
  document.getElementById('sharingSearchMeta').textContent='';
3806
- var ownerArg=isLocal?_currentAgentOwner:undefined;
3895
+ var ownerArg=undefined;
3807
3896
  loadStats(ownerArg); loadMemories();
3808
3897
  }
3809
3898
  }
3810
3899
 
3900
+ function normalizePageSize(value,fallback){
3901
+ const v=Number(value);
3902
+ return v===10||v===20||v===40?v:fallback;
3903
+ }
3904
+
3905
+ function applyPageSizeFromSelect(selectId,storageKey,fallback,onApply){
3906
+ const el=document.getElementById(selectId);
3907
+ const next=normalizePageSize(el?.value,fallback);
3908
+ onApply(next);
3909
+ try{localStorage.setItem(storageKey,String(next));}catch(e){}
3910
+ return next;
3911
+ }
3912
+
3913
+ function restorePageSizeSetting(storageKey,selectId,fallback,onApply){
3914
+ let next=fallback;
3915
+ try{
3916
+ const raw=localStorage.getItem(storageKey);
3917
+ next=normalizePageSize(raw||String(fallback),fallback);
3918
+ }catch(e){}
3919
+ onApply(next);
3920
+ const el=document.getElementById(selectId);
3921
+ if(el) el.value=String(next);
3922
+ return next;
3923
+ }
3924
+
3925
+ function onMemoryPageSizeChange(){
3926
+ applyPageSizeFromSelect('memoryPageSize','memos_memoryPageSize',20,function(next){PAGE_SIZE=next;});
3927
+ currentPage=1;
3928
+ if(memorySearchScope==='hub') loadHubMemories();
3929
+ else loadMemories();
3930
+ }
3931
+
3811
3932
  function onSkillScopeChange(){
3812
- skillSearchScope=document.getElementById('skillSearchScope')?.value||'local';
3933
+ skillSearchScope=document.getElementById('skillSearchScope')?.value||'allLocal';
3934
+ skillsPage=0;
3935
+ loadSkills();
3936
+ }
3937
+
3938
+ function onSkillsPageSizeChange(){
3939
+ applyPageSizeFromSelect('skillsPageSize','memos_skillsPageSize',20,function(next){skillsPageSize=next;});
3940
+ skillsPage=0;
3813
3941
  loadSkills();
3814
3942
  }
3815
3943
 
3944
+ function onTasksPageSizeChange(){
3945
+ applyPageSizeFromSelect('tasksPageSize','memos_tasksPageSize',20,function(next){tasksPageSize=next;});
3946
+ tasksPage=0;
3947
+ loadTasks();
3948
+ }
3949
+
3816
3950
  function onTaskScopeChange(){
3817
- taskSearchScope=document.getElementById('taskSearchScope')?.value||'local';
3951
+ taskSearchScope=document.getElementById('taskSearchScope')?.value||'allLocal';
3952
+ if(taskSearchScope==='hub') selectedTaskIds.clear();
3818
3953
  tasksPage=0;
3954
+ updateTaskSelectionToolbar();
3819
3955
  loadTasks();
3820
3956
  }
3821
3957
 
@@ -5040,6 +5176,7 @@ function renderSharingMemorySearchResults(data,query){
5040
5176
  const list=document.getElementById('memoryList');
5041
5177
  const localHits=(data&&data.local&&Array.isArray(data.local.hits))?data.local.hits:[];
5042
5178
  const hubHits=(data&&data.hub&&Array.isArray(data.hub.hits))?data.hub.hits:[];
5179
+ setPageSizeVisible('memoryPageSize',(localHits.length+hubHits.length)>0);
5043
5180
  document.getElementById('searchMeta').textContent='Search results for "'+query+'"';
5044
5181
  document.getElementById('sharingSearchMeta').textContent=t('scope.local')+' '+localHits.length+' · '+t('scope.hub')+' '+hubHits.length;
5045
5182
  document.getElementById('pagination').innerHTML='';
@@ -5459,6 +5596,7 @@ function localMemoryErrorMessage(err){
5459
5596
 
5460
5597
  function debounceSkillSearch(){
5461
5598
  clearTimeout(skillSearchTimer);
5599
+ skillsPage=0;
5462
5600
  skillSearchTimer=setTimeout(function(){loadSkills();},300);
5463
5601
  }
5464
5602
 
@@ -5827,6 +5965,11 @@ function escapeHtml(s){
5827
5965
  return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
5828
5966
  }
5829
5967
 
5968
+ function setPageSizeVisible(selectId,visible){
5969
+ var sel=document.getElementById(selectId);
5970
+ if(sel&&sel.parentElement){ sel.parentElement.style.display=visible?'inline-flex':'none'; }
5971
+ }
5972
+
5830
5973
  function setMetricsDays(d){
5831
5974
  metricsDays=d;
5832
5975
  document.querySelectorAll('.metrics-toolbar .range-btn').forEach(btn=>btn.classList.toggle('active',Number(btn.dataset.days)===d));
@@ -5846,40 +5989,179 @@ async function loadMetrics(){
5846
5989
  }catch(e){console.error('loadMetrics',e)}
5847
5990
  }
5848
5991
 
5992
+ function _st(id,v){var e=document.getElementById(id);if(e)e.textContent=v;}
5849
5993
  function formatNum(n){return n>=1e6?(n/1e6).toFixed(1)+'M':n>=1e3?(n/1e3).toFixed(1)+'k':String(n);}
5850
5994
  function dateLoc(){return curLang==='zh'?'zh-CN':'en-US';}
5851
5995
 
5852
5996
  /* ─── Tasks View Logic ─── */
5853
5997
  let tasksStatusFilter='';
5854
5998
  let tasksPage=0;
5855
- const TASKS_PER_PAGE=20;
5999
+ let tasksPageSize=20;
5856
6000
 
5857
6001
  function setTaskStatusFilter(btn,status){
5858
- document.querySelectorAll('.tasks-filters .filter-chip').forEach(c=>c.classList.remove('active'));
6002
+ document.querySelectorAll('.tasks-view .filter-bar .filter-chip[data-task-status]').forEach(c=>c.classList.remove('active'));
5859
6003
  btn.classList.add('active');
5860
6004
  tasksStatusFilter=status;
5861
6005
  tasksPage=0;
5862
6006
  loadTasks();
5863
6007
  }
5864
6008
 
6009
+ function updateTaskSelectionToolbar(){
6010
+ var toolbar=document.getElementById('taskSelectionToolbar');
6011
+ var selectAllBtn=document.getElementById('taskSelectAllBtn');
6012
+ var bulkDeleteBtn=document.getElementById('taskBulkDeleteBtn');
6013
+ var isHub=taskSearchScope==='hub';
6014
+ var total=document.querySelectorAll('#tasksList .task-select-check').length;
6015
+ var selected=selectedTaskIds.size;
6016
+ if(toolbar) toolbar.style.display=isHub?'none':'flex';
6017
+ if(selectAllBtn){
6018
+ selectAllBtn.textContent=t(selected>0&&selected===total&&total>0?'skills.unselectAll':'skills.selectAll');
6019
+ selectAllBtn.disabled=total===0;
6020
+ selectAllBtn.style.display=total===0?'none':'';
6021
+ }
6022
+ if(bulkDeleteBtn){
6023
+ var base=t('task.deleteSelected');
6024
+ bulkDeleteBtn.style.display=selected>0?'':'none';
6025
+ bulkDeleteBtn.textContent=selected>0?(base+' ('+selected+')'):base;
6026
+ }
6027
+ }
6028
+
6029
+ function toggleTaskSelection(taskId,checked){
6030
+ if(checked) selectedTaskIds.add(taskId);
6031
+ else selectedTaskIds.delete(taskId);
6032
+ updateTaskSelectionToolbar();
6033
+ }
6034
+
6035
+ function toggleSelectAllTasks(){
6036
+ var total=currentTaskIds.length;
6037
+ if(total===0) return;
6038
+ if(selectedTaskIds.size===total){
6039
+ selectedTaskIds.clear();
6040
+ }else{
6041
+ selectedTaskIds=new Set(currentTaskIds);
6042
+ }
6043
+ var checks=document.querySelectorAll('#tasksList .task-select-check');
6044
+ checks.forEach(function(cb){cb.checked=selectedTaskIds.has(cb.value);});
6045
+ updateTaskSelectionToolbar();
6046
+ }
6047
+
6048
+ async function deleteSelectedTasks(){
6049
+ var ids=Array.from(selectedTaskIds);
6050
+ if(ids.length===0) return;
6051
+ var msg=t('task.delete.selected.confirm').replace('{count}',String(ids.length));
6052
+ if(!(await confirmModal(msg,{danger:true}))) return;
6053
+ var ok=0;
6054
+ var fail=0;
6055
+ for(var i=0;i<ids.length;i++){
6056
+ try{
6057
+ var r=await fetch('/api/task/'+ids[i],{method:'DELETE'});
6058
+ var d=await r.json();
6059
+ if(!r.ok) throw new Error(d.error||'unknown');
6060
+ ok++;
6061
+ }catch(e){
6062
+ fail++;
6063
+ }
6064
+ }
6065
+ selectedTaskIds.clear();
6066
+ updateTaskSelectionToolbar();
6067
+ loadTasks();
6068
+ if(fail>0) toast(t('task.delete.partial').replace('{ok}',String(ok)).replace('{fail}',String(fail)),'warn');
6069
+ else toast(t('task.delete.success').replace('{count}',String(ok)),'success');
6070
+ }
6071
+
6072
+ var taskSearchQuery='';
6073
+ var _taskSearchTimer=null;
6074
+ function debounceTaskSearch(){
6075
+ clearTimeout(_taskSearchTimer);
6076
+ _taskSearchTimer=setTimeout(function(){
6077
+ taskSearchQuery=(document.getElementById('taskSearchInput')||{}).value||'';
6078
+ tasksPage=0;
6079
+ loadTasks();
6080
+ },350);
6081
+ }
6082
+
6083
+ function renderTaskCards(tasks,container){
6084
+ currentTaskIds=tasks.map(function(task){return task.id;});
6085
+ selectedTaskIds=new Set(Array.from(selectedTaskIds).filter(function(id){return currentTaskIds.includes(id);}));
6086
+ if(!tasks||tasks.length===0){
6087
+ container.innerHTML='<div style="text-align:center;padding:48px;color:var(--text-muted);font-size:14px" data-i18n="tasks.empty">'+t('tasks.empty')+'</div>';
6088
+ updateTaskSelectionToolbar();
6089
+ return;
6090
+ }
6091
+ container.innerHTML=tasks.map(function(task){
6092
+ var timeStr=formatTime(task.startedAt);
6093
+ var durationStr=task.endedAt?formatDuration(task.endedAt-task.startedAt):'';
6094
+ var taskIsLocalShared=task.owner==='public';
6095
+ var taskIsTeamShared=!!task.sharingVisibility;
6096
+ var taskScope=taskIsTeamShared?'team':taskIsLocalShared?'local':'private';
6097
+ var selectedAttr=selectedTaskIds.has(task.id)?' checked':'';
6098
+ return '<div class="task-card status-'+task.status+'" onclick="openTaskDetail(\\''+task.id+'\\')">'+
6099
+ '<div class="task-card-top">'+
6100
+ '<div class="task-card-title"><label class="item-select-box" onclick="event.stopPropagation()"><input class="task-select-check" type="checkbox" value="'+escAttr(task.id)+'"'+selectedAttr+' onchange="event.stopPropagation();toggleTaskSelection(&quot;'+escAttr(task.id)+'&quot;,this.checked)"></label>'+esc(task.title)+'</div>'+
6101
+ '<div class="task-card-badges">'+renderScopeBadge(taskScope)+'<span class="task-status-badge '+task.status+'">'+t('tasks.status.'+task.status)+'</span></div>'+
6102
+ '</div>'+
6103
+ (task.summary?'<div class="task-card-summary'+(task.status==='skipped'?' skipped-reason':'')+'">'+esc(task.summary)+'</div>':'')+
6104
+ '<div class="task-card-bottom">'+
6105
+ '<span class="tag"><span class="icon">\\u{1F4C5}</span> '+timeStr+'</span>'+
6106
+ (durationStr?'<span class="tag"><span class="icon">\\u23F1</span> '+durationStr+'</span>':'')+
6107
+ '<span class="tag"><span class="icon">\\u{1F4DD}</span> '+(task.chunkCount||0)+' '+t('tasks.chunks.label')+'</span>'+
6108
+ '<span class="tag"><span class="icon">\\u{1F4C2}</span> '+(task.sessionKey||'').slice(0,12)+'</span>'+
6109
+ '</div>'+
6110
+ '<div class="card-actions" onclick="event.stopPropagation()">'+
6111
+ '<button class="btn btn-sm btn-ghost" onclick="openTaskDetail(\\''+task.id+'\\')">'+t('card.expand')+'</button>'+
6112
+ (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>':'')+
6113
+ (task.status==='completed'
6114
+ ?'<button class="btn btn-sm btn-ghost" onclick="openTaskScopeModalFromList(\\''+task.id+'\\',\\''+taskScope+'\\')">\\u270F '+t('share.shareBtn')+'</button>'
6115
+ :'<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>')+
6116
+ '<button class="btn btn-sm btn-ghost" style="color:var(--accent)" onclick="deleteTask(\\''+task.id+'\\')">'+t('task.delete')+'</button>'+
6117
+ '</div>'+
6118
+ '</div>';
6119
+ }).join('');
6120
+ updateTaskSelectionToolbar();
6121
+ }
6122
+
5865
6123
  async function loadTasks(silent){
5866
6124
  const scope=document.getElementById('taskSearchScope')?document.getElementById('taskSearchScope').value:taskSearchScope;
5867
- taskSearchScope=scope||'local';
6125
+ taskSearchScope=scope||'allLocal';
5868
6126
  if(taskSearchScope==='hub'){ return loadHubTasks(); }
5869
6127
  const list=document.getElementById('tasksList');
5870
6128
  if(!silent) list.innerHTML='<div class="spinner"></div>';
5871
6129
  try{
5872
- const params=new URLSearchParams({limit:String(TASKS_PER_PAGE),offset:String(tasksPage*TASKS_PER_PAGE)});
6130
+ var ownerSel=document.getElementById('taskFilterOwner')||document.getElementById('filterOwner');
6131
+ var ownerVal=ownerSel?ownerSel.value:'';
6132
+
6133
+ if(taskSearchQuery&&taskSearchQuery.trim()){
6134
+ var sp=new URLSearchParams({q:taskSearchQuery.trim(),limit:String(tasksPageSize)});
6135
+ if(ownerVal) sp.set('owner',ownerVal);
6136
+ var sr=await fetch('/api/task-search?'+sp).then(function(r){return r.json()});
6137
+ var tasks=sr.tasks||[];
6138
+ document.getElementById('taskSearchMeta').style.display='block';
6139
+ document.getElementById('taskSearchMeta').textContent=t('tasks.search.meta').replace('{0}',tasks.length);
6140
+ _st('tasksTotalCount',tasks.length);
6141
+ _st('tasksActiveCount',tasks.filter(function(tk){return tk.status==='active'}).length);
6142
+ _st('tasksCompletedCount',tasks.filter(function(tk){return tk.status==='completed'}).length);
6143
+ _st('tasksSkippedCount',tasks.filter(function(tk){return tk.status==='skipped'}).length);
6144
+ if(tasksStatusFilter) tasks=tasks.filter(function(tk){return tk.status===tasksStatusFilter});
6145
+ renderTaskCards(tasks,list);
6146
+ document.getElementById('tasksPagination').innerHTML='';
6147
+ return;
6148
+ }
6149
+ document.getElementById('taskSearchMeta').style.display='none';
6150
+
6151
+ const params=new URLSearchParams({limit:String(tasksPageSize),offset:String(tasksPage*tasksPageSize)});
5873
6152
  if(tasksStatusFilter) params.set('status',tasksStatusFilter);
5874
- if(taskSearchScope==='local') params.set('owner','agent:main');
6153
+ if(activeSession) params.set('session',activeSession);
6154
+ if(ownerVal) params.set('owner',ownerVal);
5875
6155
  var baseP=new URLSearchParams();
5876
- if(taskSearchScope==='local') baseP.set('owner','agent:main');
6156
+ if(activeSession) baseP.set('session',activeSession);
6157
+ if(ownerVal) baseP.set('owner',ownerVal);
6158
+ var baseQuery=baseP.toString();
5877
6159
  const [data,allD,activeD,compD,skipD]=await Promise.all([
5878
6160
  fetch('/api/tasks?'+params).then(r=>r.json()),
5879
- fetch('/api/tasks?limit=1&offset=0&'+baseP).then(r=>r.json()),
5880
- fetch('/api/tasks?status=active&limit=1&offset=0&'+baseP).then(r=>r.json()),
5881
- fetch('/api/tasks?status=completed&limit=1&offset=0&'+baseP).then(r=>r.json()),
5882
- fetch('/api/tasks?status=skipped&limit=1&offset=0&'+baseP).then(r=>r.json())
6161
+ fetch('/api/tasks?limit=1&offset=0'+(baseQuery?'&'+baseQuery:'')).then(r=>r.json()),
6162
+ fetch('/api/tasks?status=active&limit=1&offset=0'+(baseQuery?'&'+baseQuery:'')).then(r=>r.json()),
6163
+ fetch('/api/tasks?status=completed&limit=1&offset=0'+(baseQuery?'&'+baseQuery:'')).then(r=>r.json()),
6164
+ fetch('/api/tasks?status=skipped&limit=1&offset=0'+(baseQuery?'&'+baseQuery:'')) .then(r=>r.json())
5883
6165
  ]);
5884
6166
  if(silent){
5885
6167
  var fp=JSON.stringify((data.tasks||[]).map(function(tk){return tk.id+'|'+tk.status+'|'+(tk.updatedAt||tk.startedAt)}));
@@ -5889,50 +6171,24 @@ async function loadTasks(silent){
5889
6171
  }else{
5890
6172
  _lastTasksFingerprint='';
5891
6173
  }
5892
- document.getElementById('tasksTotalCount').textContent=formatNum(allD.total);
5893
- document.getElementById('tasksActiveCount').textContent=formatNum(activeD.total);
5894
- document.getElementById('tasksCompletedCount').textContent=formatNum(compD.total);
5895
- document.getElementById('tasksSkippedCount').textContent=formatNum(skipD.total);
6174
+ _st('tasksTotalCount',formatNum(allD.total));
6175
+ _st('tasksActiveCount',formatNum(activeD.total));
6176
+ _st('tasksCompletedCount',formatNum(compD.total));
6177
+ _st('tasksSkippedCount',formatNum(skipD.total));
6178
+
6179
+ renderTaskCards(data.tasks||[],list);
5896
6180
 
5897
6181
  if(!data.tasks||data.tasks.length===0){
5898
- list.innerHTML='<div style="text-align:center;padding:48px;color:var(--text-muted);font-size:14px" data-i18n="tasks.empty">'+t('tasks.empty')+'</div>';
5899
6182
  document.getElementById('tasksPagination').innerHTML='';
5900
- return;
6183
+ } else {
6184
+ renderTasksPagination(data.total);
5901
6185
  }
5902
-
5903
- list.innerHTML=data.tasks.map(task=>{
5904
- const timeStr=formatTime(task.startedAt);
5905
- const endStr=task.endedAt?formatTime(task.endedAt):'';
5906
- const durationStr=task.endedAt?formatDuration(task.endedAt-task.startedAt):'';
5907
- var taskIsLocalShared=task.owner==='public';
5908
- var taskIsTeamShared=!!task.sharingVisibility;
5909
- var taskScope=taskIsTeamShared?'team':taskIsLocalShared?'local':'private';
5910
- return '<div class="task-card status-'+task.status+'" onclick="openTaskDetail(\\''+task.id+'\\')">'+
5911
- '<div class="task-card-top">'+
5912
- '<div class="task-card-title">'+esc(task.title)+'</div>'+
5913
- '<div class="task-card-badges">'+renderScopeBadge(taskScope)+'<span class="task-status-badge '+task.status+'">'+t('tasks.status.'+task.status)+'</span></div>'+
5914
- '</div>'+
5915
- (task.summary?'<div class="task-card-summary'+(task.status==='skipped'?' skipped-reason':'')+'">'+esc(task.summary)+'</div>':'')+
5916
- '<div class="task-card-bottom">'+
5917
- '<span class="tag"><span class="icon">\\u{1F4C5}</span> '+timeStr+'</span>'+
5918
- (durationStr?'<span class="tag"><span class="icon">\\u23F1</span> '+durationStr+'</span>':'')+
5919
- '<span class="tag"><span class="icon">\\u{1F4DD}</span> '+task.chunkCount+' '+t('tasks.chunks.label')+'</span>'+
5920
- '<span class="tag"><span class="icon">\\u{1F4C2}</span> '+(task.sessionKey||'').slice(0,12)+'</span>'+
5921
- '</div>'+
5922
- '<div class="card-actions" onclick="event.stopPropagation()">'+
5923
- '<button class="btn btn-sm btn-ghost" onclick="openTaskDetail(\\''+task.id+'\\')">'+t('card.expand')+'</button>'+
5924
- (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>':'')+
5925
- (task.status==='completed'
5926
- ?'<button class="btn btn-sm btn-ghost" onclick="openTaskScopeModalFromList(\\''+task.id+'\\',\\''+taskScope+'\\')">\\u270F '+t('share.shareBtn')+'</button>'
5927
- :'<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>')+
5928
- '<button class="btn btn-sm btn-ghost" style="color:var(--accent)" onclick="deleteTask(\\''+task.id+'\\')">'+t('task.delete')+'</button>'+
5929
- '</div>'+
5930
- '</div>';
5931
- }).join('');
5932
-
5933
- renderTasksPagination(data.total);
5934
6186
  }catch(e){
5935
6187
  console.error('loadTasks error:',e);
6188
+ currentTaskIds=[];
6189
+ selectedTaskIds.clear();
6190
+ setPageSizeVisible('tasksPageSize',false);
6191
+ updateTaskSelectionToolbar();
5936
6192
  list.innerHTML='<div style="text-align:center;padding:24px;color:var(--rose)">Failed to load tasks: '+String(e)+'</div>';
5937
6193
  }
5938
6194
  }
@@ -5953,7 +6209,7 @@ function updateTaskCardBadge(taskId,newScope){
5953
6209
 
5954
6210
  function renderTasksPagination(total){
5955
6211
  const el=document.getElementById('tasksPagination');
5956
- const pages=Math.ceil(total/TASKS_PER_PAGE);
6212
+ const pages=Math.ceil(total/tasksPageSize);
5957
6213
  if(pages<=1){el.innerHTML='';return;}
5958
6214
  let html='<button class="pg-btn'+(tasksPage===0?' disabled':'')+'" onclick="tasksPage=Math.max(0,tasksPage-1);loadTasks()">\\u2190</button>';
5959
6215
  const start=Math.max(0,tasksPage-2),end=Math.min(pages,tasksPage+3);
@@ -6141,6 +6397,8 @@ async function deleteTask(taskId){
6141
6397
  const r=await fetch('/api/task/'+taskId,{method:'DELETE'});
6142
6398
  const d=await r.json();
6143
6399
  if(!r.ok) throw new Error(d.error||'unknown');
6400
+ selectedTaskIds.delete(taskId);
6401
+ updateTaskSelectionToolbar();
6144
6402
  closeTaskDetail();
6145
6403
  document.getElementById('taskDetailOverlay').classList.remove('show');
6146
6404
  loadTasks();
@@ -6150,14 +6408,55 @@ async function deleteTask(taskId){
6150
6408
 
6151
6409
  /* ─── Skills View Logic ─── */
6152
6410
  let skillsStatusFilter='';
6411
+ let skillsPage=0;
6412
+ let skillsPageSize=20;
6413
+ let selectedSkillIds=new Set();
6414
+ let currentLocalSkills=[];
6415
+ let skillsFilterSignature='';
6153
6416
 
6154
6417
  function setSkillStatusFilter(btn,status){
6155
- document.querySelectorAll('.skills-view .tasks-filters .filter-chip').forEach(c=>c.classList.remove('active'));
6418
+ document.querySelectorAll('.skills-view .filter-bar .filter-chip[data-skill-status]').forEach(c=>c.classList.remove('active'));
6156
6419
  btn.classList.add('active');
6157
6420
  skillsStatusFilter=status;
6421
+ skillsPage=0;
6158
6422
  loadSkills();
6159
6423
  }
6160
6424
 
6425
+ function updateSkillSelectionToolbar(){
6426
+ var selectAllBtn=document.getElementById('skillSelectAllBtn');
6427
+ var bulkDeleteBtn=document.getElementById('skillBulkDeleteBtn');
6428
+ var total=document.querySelectorAll('#skillsList .skill-select-check').length;
6429
+ var selected=selectedSkillIds.size;
6430
+ if(selectAllBtn){
6431
+ selectAllBtn.textContent=t(selected>0&&selected===total&&total>0?'skills.unselectAll':'skills.selectAll');
6432
+ selectAllBtn.style.display=total===0?'none':'';
6433
+ }
6434
+ if(bulkDeleteBtn){
6435
+ var base=t('skills.deleteSelected');
6436
+ bulkDeleteBtn.style.display=selected>0?'':'none';
6437
+ bulkDeleteBtn.textContent=selected>0?(base+' ('+selected+')'):base;
6438
+ }
6439
+ }
6440
+
6441
+ function toggleSkillSelection(skillId,checked){
6442
+ if(checked) selectedSkillIds.add(skillId);
6443
+ else selectedSkillIds.delete(skillId);
6444
+ updateSkillSelectionToolbar();
6445
+ }
6446
+
6447
+ function toggleSelectAllSkills(){
6448
+ var total=currentLocalSkills.length;
6449
+ if(total===0) return;
6450
+ if(selectedSkillIds.size===total){
6451
+ selectedSkillIds.clear();
6452
+ }else{
6453
+ selectedSkillIds=new Set(currentLocalSkills.map(function(s){return s.id;}));
6454
+ }
6455
+ var checks=document.querySelectorAll('#skillsList .skill-select-check');
6456
+ checks.forEach(function(cb){cb.checked=selectedSkillIds.has(cb.value);});
6457
+ updateSkillSelectionToolbar();
6458
+ }
6459
+
6161
6460
  function updateSkillCardBadge(skillId,newScope){
6162
6461
  var cards=document.querySelectorAll('.skill-card');
6163
6462
  for(var i=0;i<cards.length;i++){
@@ -6174,13 +6473,28 @@ function updateSkillCardBadge(skillId,newScope){
6174
6473
  }
6175
6474
  }
6176
6475
 
6476
+ function renderSkillsPagination(total){
6477
+ const el=document.getElementById('skillsPagination');
6478
+ if(!el) return;
6479
+ const pages=Math.ceil(total/skillsPageSize);
6480
+ if(pages<=1){el.innerHTML='';return;}
6481
+ let html='<button class="pg-btn'+(skillsPage===0?' disabled':'')+'" onclick="skillsPage=Math.max(0,skillsPage-1);loadSkills()">\\u2190</button>';
6482
+ const start=Math.max(0,skillsPage-2),end=Math.min(pages,skillsPage+3);
6483
+ for(let i=start;i<end;i++){
6484
+ html+='<button class="pg-btn'+(i===skillsPage?' active':'')+'" onclick="skillsPage='+i+';loadSkills()">'+(i+1)+'</button>';
6485
+ }
6486
+ html+='<button class="pg-btn'+(skillsPage>=pages-1?' disabled':'')+'" onclick="skillsPage=Math.min('+(pages-1)+',skillsPage+1);loadSkills()">\\u2192</button>';
6487
+ html+='<span class="pg-info">'+total+' '+t('pagination.total')+'</span>';
6488
+ el.innerHTML=html;
6489
+ }
6490
+
6177
6491
  async function loadSkills(silent){
6178
6492
  const list=document.getElementById('skillsList');
6179
6493
  const hubList=document.getElementById('hubSkillsList');
6180
6494
  if(!silent) list.innerHTML='<div class="spinner"></div>';
6181
6495
  var hubSection=document.getElementById('hubSkillsSection');
6182
6496
  if(hubList){
6183
- if(skillSearchScope==='local'||skillSearchScope==='allLocal'){
6497
+ if(skillSearchScope==='allLocal'){
6184
6498
  if(hubSection) hubSection.style.display='none';
6185
6499
  }else{
6186
6500
  if(hubSection) hubSection.style.display='block';
@@ -6190,24 +6504,37 @@ async function loadSkills(silent){
6190
6504
 
6191
6505
  const query=(document.getElementById('skillSearchInput')?.value||'').trim();
6192
6506
  const scope=document.getElementById('skillSearchScope') ? document.getElementById('skillSearchScope').value : skillSearchScope;
6193
- skillSearchScope=scope||'local';
6507
+ skillSearchScope=scope||'allLocal';
6194
6508
 
6195
6509
  try{
6196
6510
  const params=new URLSearchParams();
6197
6511
  if(skillsStatusFilter) params.set('status',skillsStatusFilter);
6512
+ if(activeSession) params.set('session',activeSession);
6513
+ var skillOwnerSel=document.getElementById('skillFilterOwner')||document.getElementById('filterOwner');
6514
+ var skillOwnerVal=skillOwnerSel?skillOwnerSel.value:'';
6515
+ if(skillOwnerVal) params.set('owner',skillOwnerVal);
6198
6516
  const visFilter=document.getElementById('skillVisibilityFilter')?.value;
6199
6517
  if(visFilter) params.set('visibility',visFilter);
6518
+ const filterSignature=[query,skillSearchScope,skillsStatusFilter,visFilter||'',skillOwnerVal].join('|');
6519
+ if(!silent&&filterSignature!==skillsFilterSignature){
6520
+ skillsPage=0;
6521
+ }
6522
+ skillsFilterSignature=filterSignature;
6200
6523
 
6201
6524
  const localRes=await fetch('/api/skills?'+params.toString());
6202
6525
  const localData=await localRes.json();
6203
6526
  let localSkills=Array.isArray(localData.skills)?localData.skills:[];
6527
+ currentLocalSkills=localSkills.slice();
6204
6528
  if(query){
6205
6529
  const q=query.toLowerCase();
6206
6530
  localSkills=localSkills.filter(skill=>{
6207
6531
  const haystack=[skill.name,skill.description,skill.tags].filter(Boolean).join(' ').toLowerCase();
6208
6532
  return haystack.includes(q);
6209
6533
  });
6534
+ currentLocalSkills=localSkills.slice();
6210
6535
  }
6536
+ var localIdSet=new Set(localSkills.map(function(s){return s.id;}));
6537
+ selectedSkillIds=new Set(Array.from(selectedSkillIds).filter(function(id){return localIdSet.has(id);}));
6211
6538
  if(silent){
6212
6539
  var fp=JSON.stringify(localSkills.map(function(s){return s.id+'|'+s.status+'|'+s.version+'|'+(s.visibility||'')}));
6213
6540
  if(fp===_lastSkillsFingerprint) return;
@@ -6231,9 +6558,10 @@ async function loadSkills(silent){
6231
6558
  const skillIsLocalShared=skill.visibility==='public';
6232
6559
  const skillIsTeamShared=!!skill.sharingVisibility;
6233
6560
  const skillScope=skillIsTeamShared?'team':skillIsLocalShared?'local':'private';
6561
+ const selectedAttr=selectedSkillIds.has(skill.id)?' checked':'';
6234
6562
  return '<div class="skill-card '+installedClass+' '+statusClass+'" onclick="openSkillDetail(&quot;'+escAttr(skill.id)+'&quot;)">'+
6235
6563
  '<div class="skill-card-top">'+
6236
- '<div class="skill-card-name">🧠 '+esc(skill.name)+'</div>'+
6564
+ '<div class="skill-card-name"><label class="item-select-box" onclick="event.stopPropagation()"><input class="skill-select-check" type="checkbox" value="'+escAttr(skill.id)+'"'+selectedAttr+' onchange="event.stopPropagation();toggleSkillSelection(&quot;'+escAttr(skill.id)+'&quot;,this.checked)"></label>🧠 '+esc(skill.name)+'</div>'+
6237
6565
  '<div class="skill-card-badges">'+
6238
6566
  qsBadge+
6239
6567
  '<span class="skill-badge version">v'+skill.version+'</span>'+
@@ -6249,25 +6577,40 @@ async function loadSkills(silent){
6249
6577
  (tags.length>0?'<div class="skill-card-tags">'+tags.map(tg=>'<span class="skill-tag">'+esc(tg)+'</span>').join('')+'</div>':'')+
6250
6578
  '<span class="card-actions-inline" onclick="event.stopPropagation()">'+
6251
6579
  '<button class="btn btn-sm btn-ghost" onclick="openSkillDetail(&quot;'+escAttr(skill.id)+'&quot;)">'+t('card.expand')+'</button>'+
6580
+ '<button class="btn btn-sm btn-danger" onclick="deleteSkill(&quot;'+escAttr(skill.id)+'&quot;)">'+t('skill.delete')+'</button>'+
6252
6581
  (skill.status==='active'
6253
6582
  ?'<button class="btn btn-sm btn-ghost" onclick="openSkillScopeModalFromList(&quot;'+escAttr(skill.id)+'&quot;,&quot;'+skillScope+'&quot;)">\\u270F '+t('share.shareBtn')+'</button>'
6254
6583
  :'<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>')+
6584
+ (skill.status==='active'
6585
+ ?'<button class="btn btn-sm btn-ghost btn-warn" onclick="disableSkill(&quot;'+escAttr(skill.id)+'&quot;)">'+t('skills.action.disable')+'</button>'
6586
+ :'')+
6587
+ (skill.status==='archived'
6588
+ ?'<button class="btn btn-sm btn-ghost btn-success" onclick="enableSkill(&quot;'+escAttr(skill.id)+'&quot;)">'+t('skills.action.enable')+'</button>'
6589
+ :'')+
6590
+ '<button class="btn btn-sm btn-ghost btn-danger" onclick="deleteSkill(&quot;'+escAttr(skill.id)+'&quot;)">'+t('skills.action.delete')+'</button>'+
6255
6591
  '</span>'+
6256
6592
  '</div>'+
6257
6593
  '</div>';
6258
6594
  }).join('');
6259
6595
  };
6260
6596
 
6261
- list.innerHTML=renderLocalCards(localSkills);
6262
-
6263
- if(skillSearchScope==='local'||skillSearchScope==='allLocal'){
6597
+ const totalLocalSkills=localSkills.length;
6598
+ const localPages=Math.ceil(totalLocalSkills/skillsPageSize)||1;
6599
+ if(skillsPage>=localPages) skillsPage=Math.max(0,localPages-1);
6600
+ const startIndex=skillsPage*skillsPageSize;
6601
+ const pageSkills=localSkills.slice(startIndex,startIndex+skillsPageSize);
6602
+ list.innerHTML=renderLocalCards(pageSkills);
6603
+ renderSkillsPagination(totalLocalSkills);
6604
+ setPageSizeVisible('skillsPageSize',totalLocalSkills>0);
6605
+ updateSkillSelectionToolbar();
6606
+
6607
+ if(skillSearchScope==='allLocal'){
6264
6608
  if(hubSection) hubSection.style.display='none';
6265
- document.getElementById('skillSearchMeta').textContent=query?(t('skills.search.local')+' '+localSkills.length):'';
6266
- document.getElementById('skillsTotalCount').textContent=formatNum(localSkills.length);
6267
- document.getElementById('skillsActiveCount').textContent=formatNum(localSkills.filter(s=>s.status==='active').length);
6268
- document.getElementById('skillsDraftCount').textContent=formatNum(localSkills.filter(s=>s.status==='draft').length);
6269
- document.getElementById('skillsInstalledCount').textContent=formatNum(localSkills.filter(s=>s.installed).length);
6270
- document.getElementById('skillsPublicCount').textContent=formatNum(localSkills.filter(s=>s.visibility==='public').length);
6609
+ _st('skillsTotalCount',formatNum(localSkills.length));
6610
+ _st('skillsActiveCount',formatNum(localSkills.filter(s=>s.status==='active').length));
6611
+ _st('skillsDraftCount',formatNum(localSkills.filter(s=>s.status==='draft').length));
6612
+ _st('skillsInstalledCount',formatNum(localSkills.filter(s=>s.installed).length));
6613
+ _st('skillsPublicCount',formatNum(localSkills.filter(s=>s.visibility==='public').length));
6271
6614
  return;
6272
6615
  }
6273
6616
 
@@ -6275,12 +6618,11 @@ async function loadSkills(silent){
6275
6618
  if(hubSection) hubSection.style.display='block';
6276
6619
  var localIds=new Set(localSkills.map(function(s){return s.id;}));
6277
6620
  if(hubList){ loadHubSkills(hubList, localIds); }
6278
- document.getElementById('skillSearchMeta').textContent=t('skills.search.local')+' '+localSkills.length;
6279
- document.getElementById('skillsTotalCount').textContent=formatNum(localSkills.length);
6280
- document.getElementById('skillsActiveCount').textContent=formatNum(localSkills.filter(s=>s.status==='active').length);
6281
- document.getElementById('skillsDraftCount').textContent=formatNum(localSkills.filter(s=>s.status==='draft').length);
6282
- document.getElementById('skillsInstalledCount').textContent=formatNum(localSkills.filter(s=>s.installed).length);
6283
- document.getElementById('skillsPublicCount').textContent=formatNum(localSkills.filter(s=>s.visibility==='public').length);
6621
+ _st('skillsTotalCount',formatNum(localSkills.length));
6622
+ _st('skillsActiveCount',formatNum(localSkills.filter(s=>s.status==='active').length));
6623
+ _st('skillsDraftCount',formatNum(localSkills.filter(s=>s.status==='draft').length));
6624
+ _st('skillsInstalledCount',formatNum(localSkills.filter(s=>s.installed).length));
6625
+ _st('skillsPublicCount',formatNum(localSkills.filter(s=>s.visibility==='public').length));
6284
6626
  return;
6285
6627
  }
6286
6628
 
@@ -6290,9 +6632,13 @@ async function loadSkills(silent){
6290
6632
  sharingParams.set('maxResults','20');
6291
6633
  const r=await fetch('/api/sharing/search/skills?'+sharingParams.toString());
6292
6634
  const data=await r.json();
6293
- const localHits=(data.local&&Array.isArray(data.local.hits))?data.local.hits:[];
6635
+ const localSkillIdSet=new Set(currentLocalSkills.map(function(skill){return skill.id;}));
6636
+ const localHitsRaw=(data.local&&Array.isArray(data.local.hits))?data.local.hits:[];
6637
+ const localHits=localHitsRaw.filter(function(skill){return localSkillIdSet.has(skill.skillId);});
6294
6638
  const hubHits=(data.hub&&Array.isArray(data.hub.hits))?data.hub.hits:[];
6295
6639
 
6640
+ const sp=document.getElementById('skillsPagination');
6641
+ if(sp) sp.innerHTML='';
6296
6642
  list.innerHTML=localHits.length?localHits.map(function(skill){
6297
6643
  return '<div class="hub-skill-card" onclick="openSkillDetail(&quot;'+escAttr(skill.skillId)+'&quot;)">'+
6298
6644
  '<div class="summary">'+esc(skill.name)+'</div>'+
@@ -6318,14 +6664,18 @@ async function loadSkills(silent){
6318
6664
  }).join(''):'';
6319
6665
  }
6320
6666
 
6321
- document.getElementById('skillSearchMeta').textContent=t('skills.search.local')+' '+localHits.length+(hubHits.length?' · '+t('scope.hub')+' '+hubHits.length:'');
6322
- document.getElementById('skillsTotalCount').textContent=formatNum(localHits.length+hubHits.length);
6323
- document.getElementById('skillsActiveCount').textContent=formatNum(localHits.length);
6324
- document.getElementById('skillsDraftCount').textContent='0';
6325
- document.getElementById('skillsInstalledCount').textContent='-';
6326
- document.getElementById('skillsPublicCount').textContent=formatNum(hubHits.filter(function(s){return s.visibility==='public';}).length);
6667
+ _st('skillsTotalCount',formatNum(localHits.length+hubHits.length));
6668
+ _st('skillsActiveCount',formatNum(localHits.length));
6669
+ _st('skillsDraftCount','0');
6670
+ _st('skillsInstalledCount','-');
6671
+ _st('skillsPublicCount',formatNum(hubHits.filter(function(s){return s.visibility==='public';}).length));
6672
+ setPageSizeVisible('skillsPageSize',(localHits.length+hubHits.length)>0);
6673
+ updateSkillSelectionToolbar();
6327
6674
  }catch(e){
6328
6675
  list.innerHTML='<div style="text-align:center;padding:24px;color:var(--rose)">'+t('skills.load.error')+': '+esc(String(e))+'</div>';
6676
+ const sp=document.getElementById('skillsPagination');
6677
+ if(sp) sp.innerHTML='';
6678
+ setPageSizeVisible('skillsPageSize',false);
6329
6679
  if(hubList){
6330
6680
  hubList.innerHTML='<div style="text-align:center;padding:24px;color:var(--rose)">'+t('skills.load.error')+'</div>';
6331
6681
  }
@@ -6335,19 +6685,28 @@ async function loadSkills(silent){
6335
6685
  async function loadHubTasks(){
6336
6686
  var list=document.getElementById('tasksList');
6337
6687
  if(!list) return;
6688
+ currentTaskIds=[];
6689
+ selectedTaskIds.clear();
6690
+ updateTaskSelectionToolbar();
6338
6691
  list.innerHTML='<div class="spinner"></div>';
6339
6692
  try{
6340
- var r=await fetch('/api/sharing/tasks/list?limit=40');
6693
+ var r=await fetch('/api/sharing/tasks/list?limit='+tasksPageSize);
6341
6694
  var d=await r.json();
6342
6695
  var tasks=Array.isArray(d.tasks)?d.tasks:[];
6696
+ if(activeSession){
6697
+ tasks=tasks.filter(function(task){
6698
+ return (task.sessionKey||task.session_key||'')===activeSession;
6699
+ });
6700
+ }
6343
6701
  hubTasksCache=tasks;
6344
- document.getElementById('tasksTotalCount').textContent=formatNum(tasks.length);
6345
- document.getElementById('tasksActiveCount').textContent='-';
6346
- document.getElementById('tasksCompletedCount').textContent='-';
6347
- document.getElementById('tasksSkippedCount').textContent='-';
6702
+ _st('tasksTotalCount',formatNum(tasks.length));
6703
+ _st('tasksActiveCount','-');
6704
+ _st('tasksCompletedCount','-');
6705
+ _st('tasksSkippedCount','-');
6348
6706
  if(!tasks.length){
6349
6707
  list.innerHTML='<div style="text-align:center;padding:48px;color:var(--text-muted);font-size:14px">'+t('tasks.empty')+'</div>';
6350
6708
  document.getElementById('tasksPagination').innerHTML='';
6709
+ setPageSizeVisible('tasksPageSize',false);
6351
6710
  return;
6352
6711
  }
6353
6712
  list.innerHTML=tasks.map(function(task,idx){
@@ -6366,9 +6725,11 @@ async function loadHubTasks(){
6366
6725
  '</div>';
6367
6726
  }).join('');
6368
6727
  document.getElementById('tasksPagination').innerHTML='';
6728
+ setPageSizeVisible('tasksPageSize',true);
6369
6729
  }catch(e){
6370
6730
  list.innerHTML='<div style="text-align:center;padding:48px;color:var(--text-muted);font-size:14px">'+t('tasks.empty')+'</div>';
6371
6731
  document.getElementById('tasksPagination').innerHTML='';
6732
+ setPageSizeVisible('tasksPageSize',false);
6372
6733
  }
6373
6734
  }
6374
6735
 
@@ -6524,7 +6885,14 @@ async function openSkillDetail(skillId){
6524
6885
  }
6525
6886
 
6526
6887
  window._currentSkillData=skill;
6527
- document.getElementById('skillDetailActions').innerHTML='';
6888
+ var detailActionsHtml='';
6889
+ if(skill.status==='active'){
6890
+ detailActionsHtml+='<button class="btn btn-sm btn-warn" onclick="disableSkill(&quot;'+escAttr(skill.id)+'&quot;)">'+t('skills.action.disable')+'</button>';
6891
+ }else if(skill.status==='archived'){
6892
+ detailActionsHtml+='<button class="btn btn-sm btn-success" onclick="enableSkill(&quot;'+escAttr(skill.id)+'&quot;)">'+t('skills.action.enable')+'</button>';
6893
+ }
6894
+ detailActionsHtml+='<button class="btn btn-sm btn-danger" onclick="deleteSkill(&quot;'+escAttr(skill.id)+'&quot;)">'+t('skills.action.delete')+'</button>';
6895
+ document.getElementById('skillDetailActions').innerHTML=detailActionsHtml;
6528
6896
 
6529
6897
  }catch(e){
6530
6898
  document.getElementById('skillDetailTitle').textContent=t('skills.error');
@@ -7106,11 +7474,60 @@ async function deleteSkill(skillId){
7106
7474
  const r=await fetch('/api/skill/'+skillId,{method:'DELETE'});
7107
7475
  const d=await r.json();
7108
7476
  if(!r.ok) throw new Error(d.error||'unknown');
7477
+ selectedSkillIds.delete(skillId);
7478
+ updateSkillSelectionToolbar();
7109
7479
  closeSkillDetail();
7110
7480
  document.getElementById('skillDetailOverlay').classList.remove('show');
7111
7481
  loadSkills();
7112
7482
  }catch(e){ alert(t('skill.delete.error')+e.message); }
7113
7483
  }
7484
+ async function disableSkill(skillId){
7485
+ if(!(await confirmModal(t('skills.disable.confirm')))) return;
7486
+ try{
7487
+ const r=await fetch('/api/skill/'+skillId+'/disable',{method:'PUT'});
7488
+ const d=await r.json();
7489
+ if(!r.ok) throw new Error(d.error||'unknown');
7490
+ toast(t('skills.action.disable')+' ✓','ok');
7491
+ closeSkillDetail();
7492
+ document.getElementById('skillDetailOverlay').classList.remove('show');
7493
+ loadSkills();
7494
+ }catch(e){ alert(t('skills.disable.error')+e.message); }
7495
+ }
7496
+ async function enableSkill(skillId){
7497
+ try{
7498
+ const r=await fetch('/api/skill/'+skillId+'/enable',{method:'PUT'});
7499
+ const d=await r.json();
7500
+ if(!r.ok) throw new Error(d.error||'unknown');
7501
+ toast(t('skills.action.enable')+' ✓','ok');
7502
+ closeSkillDetail();
7503
+ document.getElementById('skillDetailOverlay').classList.remove('show');
7504
+ loadSkills();
7505
+ }catch(e){ alert(t('skills.enable.error')+e.message); }
7506
+ }
7507
+
7508
+ async function deleteSelectedSkills(){
7509
+ var ids=Array.from(selectedSkillIds);
7510
+ if(ids.length===0) return;
7511
+ var msg=t('skill.delete.selected.confirm').replace('{count}',String(ids.length));
7512
+ if(!(await confirmModal(msg,{danger:true}))) return;
7513
+ var ok=0;
7514
+ var fail=0;
7515
+ for(var i=0;i<ids.length;i++){
7516
+ try{
7517
+ var r=await fetch('/api/skill/'+ids[i],{method:'DELETE'});
7518
+ var d=await r.json();
7519
+ if(!r.ok) throw new Error(d.error||'unknown');
7520
+ ok++;
7521
+ }catch(e){
7522
+ fail++;
7523
+ }
7524
+ }
7525
+ selectedSkillIds.clear();
7526
+ updateSkillSelectionToolbar();
7527
+ loadSkills();
7528
+ if(fail>0) toast(t('skill.delete.partial').replace('{ok}',String(ok)).replace('{fail}',String(fail)),'warn');
7529
+ else toast(t('skill.delete.success').replace('{count}',String(ok)),'success');
7530
+ }
7114
7531
 
7115
7532
 
7116
7533
  function formatDuration(ms){
@@ -7445,7 +7862,7 @@ async function _livePollTick(){
7445
7862
  var _searchVal=(document.getElementById('searchInput')||{}).value||'';
7446
7863
  if(!_searchVal.trim()){
7447
7864
  if(memorySearchScope==='hub') await loadHubMemories(true);
7448
- else{var _pollOwner=memorySearchScope==='local'?_currentAgentOwner:undefined;await loadStats(_pollOwner);await loadMemories(null,true);}
7865
+ else{var _pollOwner=undefined;await loadStats(_pollOwner);await loadMemories(null,true);}
7449
7866
  }
7450
7867
  }
7451
7868
  else if(_activeView==='tasks') await loadTasks(true);
@@ -7718,7 +8135,7 @@ function stopNotifPoll(){ }
7718
8135
  /* ─── Data loading ─── */
7719
8136
  async function loadAll(){
7720
8137
  await loadStats();
7721
- var initOwner=memorySearchScope==='local'?_currentAgentOwner:undefined;
8138
+ var initOwner=undefined;
7722
8139
  if(initOwner) await loadStats(initOwner);
7723
8140
  await Promise.all([loadMemories(),loadSharingStatus(false)]);
7724
8141
  checkMigrateStatus();
@@ -7738,21 +8155,23 @@ async function loadStats(ownerFilter){
7738
8155
  d=await r.json();
7739
8156
  }catch(e){ d={}; }
7740
8157
  if(!d||typeof d!=='object') d={};
7741
- if(d.currentAgentOwner) _currentAgentOwner=d.currentAgentOwner;
8158
+ if(d.currentAgentOwner && !new URLSearchParams(window.location.search).get('agentId')) _currentAgentOwner=d.currentAgentOwner;
7742
8159
  const tm=d.totalMemories||0;
7743
8160
  const dedupB=d.dedupBreakdown||{};
7744
8161
  const activeCount=dedupB.active||tm;
7745
8162
  const inactiveCount=(dedupB.duplicate||0)+(dedupB.merged||0);
7746
8163
  var agentCount=(d.owners&&d.owners.length)?d.owners.length:1;
7747
- var sfp=tm+':'+(d.totalSessions||0)+':'+(d.totalEmbeddings||0)+':'+agentCount+':'+(d.embeddingProvider||'none')+':'+(ownerFilter||'');
8164
+ var tvc=(d.taskSessions||[]).length;
8165
+ var svc=(d.skillSessions||[]).length;
8166
+ var sfp=tm+':'+(d.totalSessions||0)+':'+(d.totalEmbeddings||0)+':'+agentCount+':'+(d.embeddingProvider||'none')+':'+(ownerFilter||'')+':'+(_activeView||'memories')+':'+tvc+':'+svc;
7748
8167
  if(sfp===_lastStatsFp) return;
7749
8168
  _lastStatsFp=sfp;
7750
8169
  document.getElementById('statTotal').textContent=tm;
7751
8170
  if(inactiveCount>0){
7752
8171
  document.getElementById('statTotal').title=activeCount+' '+t('stat.active')+', '+inactiveCount+' '+t('stat.deduped');
7753
8172
  }
7754
- document.getElementById('statSessions').textContent=d.totalSessions||0;
7755
- document.getElementById('statEmbeddings').textContent=d.totalEmbeddings||0;
8173
+ document.getElementById('statTasks').textContent=d.totalTasks||0;
8174
+ document.getElementById('statSkills').textContent=d.totalSkills||0;
7756
8175
  document.getElementById('statAgents').textContent=agentCount;
7757
8176
 
7758
8177
  const provEl=document.getElementById('embeddingStatus');
@@ -7776,45 +8195,76 @@ async function loadStats(ownerFilter){
7776
8195
  }).catch(()=>{});
7777
8196
  }
7778
8197
 
8198
+ const memorySessions=d.sessions||[];
8199
+ const taskSessions=d.taskSessions||[];
8200
+ const skillSessions=d.skillSessions||[];
8201
+ const sessionMap={memories:memorySessions,tasks:taskSessions,skills:skillSessions};
8202
+ function getSessionsForView(view){
8203
+ return sessionMap[view]||memorySessions;
8204
+ }
8205
+ function countBadgeHtml(cnt){
8206
+ return cnt&&cnt>0?'<span class="count">'+cnt+'</span>':'';
8207
+ }
8208
+
8209
+ const sidebarSessions=getSessionsForView(_activeView);
7779
8210
  const sl=document.getElementById('sessionList');
7780
- sl.innerHTML='<div class="session-item'+(activeSession===null?' active':'')+'" onclick="filterSession(null)"><span>'+t('sidebar.allsessions')+'</span><span class="count">'+tm+'</span></div>';
7781
- (d.sessions||[]).forEach(s=>{
8211
+ if(sl) sl.style.display=sidebarSessions.length>0?'':'none';
8212
+ sl.innerHTML='<div class="session-item'+(activeSession===null?' active':'')+'" onclick="filterSession(null)"><span>'+t('sidebar.allsessions')+'</span>'+countBadgeHtml(sidebarSessions.reduce(function(sum,s){return sum+(s.count||0);},0))+'</div>';
8213
+ sidebarSessions.forEach(s=>{
7782
8214
  const isActive=activeSession===s.session_key;
7783
8215
  const name=s.session_key.length>20?s.session_key.slice(0,8)+'...'+s.session_key.slice(-8):s.session_key;
7784
- sl.innerHTML+='<div class="session-item'+(isActive?' active':'')+'" onclick="filterSession(\\''+s.session_key.replace(/'/g,"\\\\'")+'\\')"><span title="'+s.session_key+'">'+name+'</span><span class="count">'+s.count+'</span></div>';
8216
+ sl.innerHTML+='<div class="session-item'+(isActive?' active':'')+'" onclick="filterSession(\\''+s.session_key.replace(/'/g,"\\\\'")+'\\')"><span title="'+s.session_key+'">'+name+'</span>'+countBadgeHtml(s.count||0)+'</div>';
7785
8217
  });
7786
8218
 
7787
- const fSel=document.getElementById('filterSession');
7788
- if(fSel){
8219
+ [['filterSession','memories'],['taskFilterSession','tasks'],['skillFilterSession','skills']].forEach(function(pair){
8220
+ var selId=pair[0];
8221
+ var viewKey=pair[1];
8222
+ var viewSessions=getSessionsForView(viewKey);
8223
+ const fSel=document.getElementById(selId);
8224
+ if(!fSel) return;
7789
8225
  const curVal=activeSession||'';
7790
- var sessionCount=(d.sessions||[]).length;
8226
+ var sessionCount=viewSessions.length;
8227
+ fSel.style.display=sessionCount>0?'':'none';
7791
8228
  fSel.innerHTML='<option value="">'+t('filter.allsessions')+' ('+sessionCount+')</option>';
7792
- (d.sessions||[]).forEach(s=>{
8229
+ viewSessions.forEach(s=>{
7793
8230
  const sName=s.session_key.length>30?s.session_key.slice(0,12)+'...'+s.session_key.slice(-10):s.session_key;
7794
- fSel.innerHTML+='<option value="'+s.session_key.replace(/"/g,'&quot;')+'"'+(s.session_key===curVal?' selected':'')+'>'+sName+' ('+s.count+')</option>';
8231
+ const countLabel=(s.count&&s.count>0)?' ('+s.count+')':'';
8232
+ fSel.innerHTML+='<option value="'+s.session_key.replace(/"/g,'&quot;')+'"'+(s.session_key===curVal?' selected':'')+'>'+sName+countLabel+'</option>';
7795
8233
  });
7796
- }
8234
+ });
7797
8235
 
7798
- const ownerSel=document.getElementById('filterOwner');
7799
- if(ownerSel && d.owners && d.owners.length>0){
7800
- const curVal=ownerSel.value;
8236
+ if(d.owners && d.owners.length>0){
7801
8237
  var agents=d.owners.filter(function(o){return o && o.indexOf('agent:')===0;});
7802
- ownerSel.innerHTML='<option value="">'+t('filter.allagents')+'</option>';
7803
- agents.forEach(function(o){
7804
- var label=o.replace('agent:','');
7805
- ownerSel.innerHTML+='<option value="'+o+'"'+(o===curVal?' selected':'')+'>'+label+'</option>';
8238
+ ['filterOwner','taskFilterOwner','skillFilterOwner'].forEach(function(selId){
8239
+ var sel=document.getElementById(selId);
8240
+ if(!sel) return;
8241
+ var curVal=sel.value;
8242
+ sel.innerHTML='<option value="">'+t('filter.allagents')+'</option>';
8243
+ agents.forEach(function(o){
8244
+ var label=o.replace('agent:','');
8245
+ sel.innerHTML+='<option value="'+o+'"'+(o===curVal?' selected':'')+'>'+label+'</option>';
8246
+ });
8247
+ if(agents.length<=1) sel.style.display='none';
8248
+ else sel.style.display='';
7806
8249
  });
7807
- if(agents.length<=1) ownerSel.style.display='none';
7808
- else ownerSel.style.display='';
7809
8250
  }
7810
8251
  }
7811
8252
 
7812
8253
  function onOwnerFilterChange(){
7813
- var owner=document.getElementById('filterOwner').value;
8254
+ var src=event&&event.target?event.target:document.getElementById('filterOwner');
8255
+ var owner=src.value;
8256
+ ['filterOwner','taskFilterOwner','skillFilterOwner'].forEach(function(id){
8257
+ var el=document.getElementById(id);
8258
+ if(el&&el!==src) el.value=owner;
8259
+ });
7814
8260
  activeSession=null;
7815
8261
  currentPage=1;
8262
+ taskSearchQuery='';
8263
+ var tsi=document.getElementById('taskSearchInput');if(tsi)tsi.value='';
7816
8264
  refreshSessionDropdown(owner);
7817
8265
  applyFilters();
8266
+ loadTasks();
8267
+ loadSkills();
7818
8268
  }
7819
8269
 
7820
8270
  async function refreshSessionDropdown(ownerFilter){
@@ -7845,12 +8295,13 @@ function getFilterParams(){
7845
8295
  if(dt) p.set('dateTo',dt);
7846
8296
  const sort=document.getElementById('filterSort').value;
7847
8297
  if(sort==='oldest') p.set('sort','oldest');
7848
- const scope=memorySearchScope||'local';
7849
- if(scope==='local'){
7850
- p.set('owner',_currentAgentOwner);
7851
- }else if(scope==='allLocal'){
7852
- const owner=document.getElementById('filterOwner').value;
7853
- if(owner) p.set('owner',owner);
8298
+ const scope=memorySearchScope||'allLocal';
8299
+ if(scope==='allLocal'){
8300
+ const owner=document.getElementById('filterOwner').value;
8301
+ if(owner) {
8302
+ p.set('owner',owner);
8303
+ _currentAgentOwner = owner;
8304
+ }
7854
8305
  }
7855
8306
  return p;
7856
8307
  }
@@ -7911,6 +8362,8 @@ async function loadMemories(page,silent){
7911
8362
 
7912
8363
  async function loadHubMemories(silent){
7913
8364
  const list=document.getElementById('memoryList');
8365
+ selectedMemoryIds.clear();
8366
+ updateMemorySelectionToolbar();
7914
8367
  if(!silent) list.innerHTML='<div class="spinner"></div>';
7915
8368
  try{
7916
8369
  const r=await fetch('/api/sharing/memories/list?limit='+PAGE_SIZE);
@@ -7947,7 +8400,7 @@ async function doSearch(query){
7947
8400
  return;
7948
8401
  }
7949
8402
  currentPage=1;
7950
- var scope=document.getElementById('memorySearchScope')?.value||memorySearchScope||'local';
8403
+ var scope=document.getElementById('memorySearchScope')?.value||memorySearchScope||'allLocal';
7951
8404
  var list=document.getElementById('memoryList');
7952
8405
  list.innerHTML='<div class="spinner"></div>';
7953
8406
  if(scope==='hub'){
@@ -7999,12 +8452,26 @@ function debounceSearch(){
7999
8452
  function filterSession(key){
8000
8453
  activeSession=key;
8001
8454
  currentPage=1;
8002
- var fSel=document.getElementById('filterSession');
8003
- if(fSel) fSel.value=key||'';
8455
+ tasksPage=0;
8456
+ skillsPage=0;
8457
+ ['filterSession','taskFilterSession','skillFilterSession'].forEach(function(selId){
8458
+ var fSel=document.getElementById(selId);
8459
+ if(fSel) fSel.value=key||'';
8460
+ });
8004
8461
  document.querySelectorAll('#sessionList .session-item').forEach(function(el,i){
8005
8462
  if(i===0) el.classList.toggle('active',!key);
8006
8463
  else el.classList.toggle('active',el.querySelector('span')?.title===key);
8007
8464
  });
8465
+ if(_activeView==='tasks'){
8466
+ loadStats();
8467
+ loadTasks();
8468
+ return;
8469
+ }
8470
+ if(_activeView==='skills'){
8471
+ loadStats();
8472
+ loadSkills();
8473
+ return;
8474
+ }
8008
8475
  loadAll();
8009
8476
  }
8010
8477
 
@@ -8031,14 +8498,88 @@ function clearDateFilter(){
8031
8498
  applyFilters();
8032
8499
  }
8033
8500
 
8501
+ function updateMemorySelectionToolbar(){
8502
+ var toolbar=document.getElementById('memorySelectionToolbar');
8503
+ var selectAllBtn=document.getElementById('memorySelectAllBtn');
8504
+ var bulkDeleteBtn=document.getElementById('memoryBulkDeleteBtn');
8505
+ var isHub=memorySearchScope==='hub';
8506
+ var total=document.querySelectorAll('#memoryList .memory-select-check').length;
8507
+ var selected=selectedMemoryIds.size;
8508
+ if(toolbar) toolbar.style.display=isHub?'none':'flex';
8509
+ if(selectAllBtn){
8510
+ selectAllBtn.textContent=t(selected>0&&selected===total&&total>0?'skills.unselectAll':'skills.selectAll');
8511
+ selectAllBtn.disabled=total===0;
8512
+ selectAllBtn.style.display=total===0?'none':'';
8513
+ }
8514
+ if(bulkDeleteBtn){
8515
+ var base=t('memory.deleteSelected');
8516
+ bulkDeleteBtn.style.display=selected>0?'':'none';
8517
+ bulkDeleteBtn.textContent=selected>0?(base+' ('+selected+')'):base;
8518
+ }
8519
+ }
8520
+
8521
+ function toggleMemorySelection(memoryId,checked){
8522
+ if(checked) selectedMemoryIds.add(memoryId);
8523
+ else selectedMemoryIds.delete(memoryId);
8524
+ updateMemorySelectionToolbar();
8525
+ }
8526
+
8527
+ function toggleSelectAllMemories(){
8528
+ var total=currentMemoryIds.length;
8529
+ if(total===0) return;
8530
+ if(selectedMemoryIds.size===total){
8531
+ selectedMemoryIds.clear();
8532
+ }else{
8533
+ selectedMemoryIds=new Set(currentMemoryIds);
8534
+ }
8535
+ var checks=document.querySelectorAll('#memoryList .memory-select-check');
8536
+ checks.forEach(function(cb){cb.checked=selectedMemoryIds.has(cb.value);});
8537
+ updateMemorySelectionToolbar();
8538
+ }
8539
+
8540
+ async function deleteSelectedMemories(){
8541
+ var ids=Array.from(selectedMemoryIds);
8542
+ if(ids.length===0) return;
8543
+ var msg=t('memory.delete.selected.confirm').replace('{count}',String(ids.length));
8544
+ if(!(await confirmModal(msg,{danger:true}))) return;
8545
+ var ok=0;
8546
+ var fail=0;
8547
+ for(var i=0;i<ids.length;i++){
8548
+ try{
8549
+ var r=await fetch('/api/memory/'+ids[i],{method:'DELETE'});
8550
+ var d=await r.json();
8551
+ if(!d.ok) throw new Error(d.error||'unknown');
8552
+ ok++;
8553
+ }catch(e){
8554
+ fail++;
8555
+ }
8556
+ }
8557
+ selectedMemoryIds.clear();
8558
+ updateMemorySelectionToolbar();
8559
+ if(document.getElementById('searchInput').value.trim()){
8560
+ doSearch(document.getElementById('searchInput').value);
8561
+ }else if(memorySearchScope==='hub'){
8562
+ loadHubMemories();
8563
+ }else{
8564
+ loadMemories();
8565
+ }
8566
+ if(fail>0) toast(t('memory.delete.partial').replace('{ok}',String(ok)).replace('{fail}',String(fail)),'warn');
8567
+ else toast(t('memory.delete.success').replace('{count}',String(ok)),'success');
8568
+ }
8569
+
8034
8570
  /* ─── Rendering ─── */
8035
8571
  function renderMemories(items){
8036
8572
  const list=document.getElementById('memoryList');
8573
+ setPageSizeVisible('memoryPageSize',items.length>0);
8037
8574
  if(!items.length){
8038
8575
  list.innerHTML='<div class="empty"><div class="icon">\\u{1F4ED}</div><p>'+t('empty.text')+'</p></div>';
8576
+ currentMemoryIds=[];
8577
+ updateMemorySelectionToolbar();
8039
8578
  return;
8040
8579
  }
8041
8580
  items.forEach(m=>{memoryCache[m.id]=m});
8581
+ currentMemoryIds=items.map(function(m){return m.id;});
8582
+ selectedMemoryIds=new Set(Array.from(selectedMemoryIds).filter(function(id){return currentMemoryIds.includes(id);}));
8042
8583
  list.innerHTML=items.map(m=>{
8043
8584
  const time=m.created_at?new Date(typeof m.created_at==='number'?m.created_at:m.created_at).toLocaleString(dateLoc()):'';
8044
8585
  const role=m.role||'user';
@@ -8065,6 +8606,8 @@ function renderMemories(items){
8065
8606
  const isHubScope=memorySearchScope==='hub';
8066
8607
  const memScope=memShared?'team':isPublicMem?'local':'private';
8067
8608
  const memScopeBadge=isHubScope?renderScopeBadge('team'):renderScopeBadge(memScope);
8609
+ const selectedAttr=selectedMemoryIds.has(id)?' checked':'';
8610
+ const selectBoxHtml=isHubScope?'':'<label class="item-select-box" onclick="event.stopPropagation()"><input class="memory-select-check" type="checkbox" value="'+escAttr(id)+'"'+selectedAttr+' onchange="event.stopPropagation();toggleMemorySelection(&quot;'+escAttr(id)+'&quot;,this.checked)"></label>';
8068
8611
  let dedupInfo='';
8069
8612
  if(ds==='duplicate'||ds==='merged'){
8070
8613
  const reason=m.dedup_reason?'<span style="font-size:11px;color:var(--text-muted)">'+t('card.dedupReason')+esc(m.dedup_reason)+'</span>':'';
@@ -8090,7 +8633,7 @@ function renderMemories(items){
8090
8633
  }
8091
8634
  return '<div class="memory-card'+(isInactive?' dedup-inactive':'')+'">'+
8092
8635
  '<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>'+
8093
- '<div class="card-summary">'+cardTitle+'</div>'+
8636
+ '<div class="card-summary">'+selectBoxHtml+cardTitle+'</div>'+
8094
8637
  (function(){
8095
8638
  if(mc<=0) return '';
8096
8639
  var mergeHtml='<div class="card-merged-info">';
@@ -8120,6 +8663,7 @@ function renderMemories(items){
8120
8663
  vscore+
8121
8664
  '</div></div>';
8122
8665
  }).join('');
8666
+ updateMemorySelectionToolbar();
8123
8667
  }
8124
8668
 
8125
8669
  function updateMemoryCardBadge(chunkId,newScope){
@@ -8322,7 +8866,18 @@ async function deleteMemory(id){
8322
8866
  if(!(await confirmModal(t('confirm.delete'),{danger:true})))return;
8323
8867
  const r=await fetch('/api/memory/'+id,{method:'DELETE'});
8324
8868
  const d=await r.json();
8325
- if(d.ok){toast(t('toast.deleted'),'success');loadAll();}
8869
+ if(d.ok){
8870
+ selectedMemoryIds.delete(id);
8871
+ updateMemorySelectionToolbar();
8872
+ toast(t('toast.deleted'),'success');
8873
+ if(document.getElementById('searchInput').value.trim()){
8874
+ doSearch(document.getElementById('searchInput').value);
8875
+ }else if(memorySearchScope==='hub'){
8876
+ loadHubMemories();
8877
+ }else{
8878
+ loadMemories();
8879
+ }
8880
+ }
8326
8881
  else{toast(t('toast.delfail'),'error')}
8327
8882
  }
8328
8883
 
@@ -9126,12 +9681,16 @@ async function checkForUpdate(){
9126
9681
  /* ─── Init ─── */
9127
9682
  try{
9128
9683
  var savedScope=localStorage.getItem('memos_memorySearchScope');
9129
- if(savedScope&&(savedScope==='local'||savedScope==='allLocal'||savedScope==='hub')){
9684
+ if(savedScope==='local') savedScope='allLocal';
9685
+ if(savedScope&&(savedScope==='allLocal'||savedScope==='hub')){
9130
9686
  memorySearchScope=savedScope;
9131
9687
  var scopeEl=document.getElementById('memorySearchScope');
9132
9688
  if(scopeEl) scopeEl.value=savedScope;
9133
9689
  }
9134
9690
  }catch(e){}
9691
+ restorePageSizeSetting('memos_memoryPageSize','memoryPageSize',20,function(next){PAGE_SIZE=next;});
9692
+ restorePageSizeSetting('memos_skillsPageSize','skillsPageSize',20,function(next){skillsPageSize=next;});
9693
+ restorePageSizeSetting('memos_tasksPageSize','tasksPageSize',20,function(next){tasksPageSize=next;});
9135
9694
  document.getElementById('modalOverlay').addEventListener('click',e=>{if(e.target.id==='modalOverlay')closeModal()});
9136
9695
  document.getElementById('searchInput').addEventListener('keydown',e=>{if(e.key==='Escape'){e.target.value='';currentPage=1;if(memorySearchScope==='hub')loadHubMemories();else loadMemories();}});
9137
9696
  applyI18n();