@memtensor/memos-local-openclaw-plugin 1.0.8-beta → 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,9 +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}
700
- .skill-selection-toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:10px}
701
- .skill-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}
702
- .skill-select-box input{width:14px;height:14px;cursor:pointer}
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}
703
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)}
704
726
  .skill-version-item{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:12px 16px}
705
727
  .skill-version-header{display:flex;align-items:center;gap:10px;margin-bottom:6px}
@@ -1228,8 +1250,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
1228
1250
  <div class="sidebar" id="sidebar">
1229
1251
  <div class="stats-grid" id="statsGrid">
1230
1252
  <div class="stat-card pri"><div class="stat-value" id="statTotal">-</div><div class="stat-label" data-i18n="stat.memories">Memories</div></div>
1231
- <div class="stat-card green"><div class="stat-value" id="statSessions">-</div><div class="stat-label" data-i18n="stat.sessions">Sessions</div></div>
1232
- <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>
1233
1255
  <div class="stat-card rose"><div class="stat-value" id="statAgents">-</div><div class="stat-label" data-i18n="stat.agents">Agents</div></div>
1234
1256
  </div>
1235
1257
  <div id="sidebarSharingSection" style="display:none">
@@ -1240,8 +1262,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
1240
1262
  </div>
1241
1263
  </div>
1242
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>
1243
1266
  <div class="session-list" id="sessionList" style="display:none"></div>
1244
- <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>
1245
1267
  </div>
1246
1268
 
1247
1269
  <div class="view-container">
@@ -1257,15 +1279,10 @@ input,textarea,select{font-family:inherit;font-size:inherit}
1257
1279
  <option value="allLocal" data-i18n="scope.thisDevice">This Device</option>
1258
1280
  <option value="hub" data-i18n="scope.hub">Team</option>
1259
1281
  </select>
1260
- <select id="memoryPageSize" class="filter-select" onchange="onMemoryPageSizeChange()">
1261
- <option value="10">10 / page</option>
1262
- <option value="20" selected>20 / page</option>
1263
- <option value="40">40 / page</option>
1264
- </select>
1265
1282
  </div>
1266
- <div class="search-meta" id="searchMeta"></div>
1267
- <div class="search-meta" id="sharingSearchMeta"></div>
1268
- <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">
1269
1286
  <button class="filter-chip active" data-role="" onclick="setRoleFilter(this,'')" data-i18n="filter.all">All</button>
1270
1287
  <button class="filter-chip" data-role="user" onclick="setRoleFilter(this,'user')">User</button>
1271
1288
  <button class="filter-chip" data-role="assistant" onclick="setRoleFilter(this,'assistant')">Assistant</button>
@@ -1274,46 +1291,48 @@ input,textarea,select{font-family:inherit;font-size:inherit}
1274
1291
  <option value="newest" data-i18n="filter.newest">Newest first</option>
1275
1292
  <option value="oldest" data-i18n="filter.oldest">Oldest first</option>
1276
1293
  </select>
1277
- <span class="filter-sep"></span>
1278
- <select id="filterSession" class="filter-select" onchange="filterSession(this.value||null)">
1279
- <option value="" data-i18n="filter.allsessions">All sessions</option>
1280
- </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>
1281
1297
  </div>
1282
- <div class="date-filter">
1283
- <label data-i18n="filter.from">From</label><input type="datetime-local" id="dateFrom" step="1" onchange="applyFilters()">
1284
- <label data-i18n="filter.to">To</label><input type="datetime-local" id="dateTo" step="1" onchange="applyFilters()">
1285
- <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>
1286
1303
  </div>
1287
1304
  <div class="memory-list" id="memoryList"><div class="spinner"></div></div>
1288
- <div class="pagination" id="pagination"></div>
1305
+ <div class="pagination-row">
1306
+ <div class="pagination" id="pagination"></div>
1307
+ </div>
1289
1308
  </div>
1290
1309
  </div>
1291
1310
  <div class="tasks-view vp" id="tasksView">
1292
- <div class="tasks-header">
1293
- <div class="tasks-stats">
1294
- <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>
1295
- <div class="tasks-stat"><span class="tasks-stat-value" id="tasksActiveCount">-</span><span class="tasks-stat-label" data-i18n="tasks.active">Active</span></div>
1296
- <div class="tasks-stat"><span class="tasks-stat-value" id="tasksCompletedCount">-</span><span class="tasks-stat-label" data-i18n="tasks.completed">Completed</span></div>
1297
- <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>
1298
- </div>
1299
- <div class="tasks-filters">
1300
- <button class="filter-chip active" data-task-status="" onclick="setTaskStatusFilter(this,'')" data-i18n="filter.all">All</button>
1301
- <button class="filter-chip" data-task-status="active" onclick="setTaskStatusFilter(this,'active')" data-i18n="tasks.status.active">Active</button>
1302
- <button class="filter-chip" data-task-status="completed" onclick="setTaskStatusFilter(this,'completed')" data-i18n="tasks.status.completed">Completed</button>
1303
- <button class="filter-chip" data-task-status="skipped" onclick="setTaskStatusFilter(this,'skipped')" data-i18n="tasks.status.skipped">Skipped</button>
1304
- <select id="tasksPageSize" class="filter-select" onchange="onTasksPageSizeChange()">
1305
- <option value="10">10 / page</option>
1306
- <option value="20" selected>20 / page</option>
1307
- <option value="40">40 / page</option>
1308
- </select>
1309
- <select id="taskSearchScope" class="scope-select" onchange="onTaskScopeChange()" style="display:none">
1310
- <option value="allLocal" data-i18n="scope.thisDevice">This Device</option>
1311
- <option value="hub" data-i18n="scope.hub">Team</option>
1312
- </select>
1313
- </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>
1314
1331
  </div>
1315
1332
  <div class="tasks-list" id="tasksList"><div class="spinner"></div></div>
1316
- <div class="pagination" id="tasksPagination"></div>
1333
+ <div class="pagination-row">
1334
+ <div class="pagination" id="tasksPagination"></div>
1335
+ </div>
1317
1336
  <div class="task-detail-overlay" id="taskDetailOverlay" onclick="closeTaskDetail(event)">
1318
1337
  <div class="task-detail-panel" onclick="event.stopPropagation()">
1319
1338
  <div class="task-detail-header">
@@ -1347,43 +1366,33 @@ input,textarea,select{font-family:inherit;font-size:inherit}
1347
1366
  <div class="search-bar">
1348
1367
  <span class="search-icon">🔍</span>
1349
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>
1350
1372
  <select id="skillSearchScope" class="scope-select" onchange="onSkillScopeChange()" style="display:none">
1351
1373
  <option value="allLocal" data-i18n="scope.thisDevice">This Device</option>
1352
1374
  <option value="hub" data-i18n="scope.hub">Team</option>
1353
1375
  </select>
1354
- <select id="skillsPageSize" class="filter-select" onchange="onSkillsPageSizeChange()">
1355
- <option value="10">10 / page</option>
1356
- <option value="20" selected>20 / page</option>
1357
- <option value="40">40 / page</option>
1358
- </select>
1359
1376
  </div>
1360
- <div class="search-meta" id="skillSearchMeta" style="display:none"></div>
1361
- <div class="tasks-header">
1362
- <div class="tasks-stats">
1363
- <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>
1364
- <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>
1365
- <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>
1366
- <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>
1367
- <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>
1368
- </div>
1369
- <div class="tasks-filters">
1370
- <button class="filter-chip active" data-skill-status="" onclick="setSkillStatusFilter(this,'')" data-i18n="filter.all">All</button>
1371
- <button class="filter-chip" data-skill-status="active" onclick="setSkillStatusFilter(this,'active')" data-i18n="skills.filter.active">Active</button>
1372
- <button class="filter-chip" data-skill-status="draft" onclick="setSkillStatusFilter(this,'draft')" data-i18n="skills.filter.draft">Draft</button>
1373
- <button class="filter-chip" data-skill-status="archived" onclick="setSkillStatusFilter(this,'archived')" data-i18n="skills.filter.archived">Archived</button>
1374
- <select id="skillVisibilityFilter" class="filter-select" onchange="loadSkills()" style="display:none">
1375
- <option value="" data-i18n="filter.allvisibility">All visibility</option>
1376
- <option value="public" data-i18n="filter.public">Public</option>
1377
- <option value="private" data-i18n="filter.private">Private</option>
1378
- </select>
1379
- </div>
1380
- <div class="skill-selection-toolbar">
1381
- <button class="btn btn-sm btn-ghost" id="skillSelectAllBtn" onclick="toggleSelectAllSkills()" data-i18n="skills.selectAll">Select All</button>
1382
- <button class="btn btn-sm btn-danger" id="skillBulkDeleteBtn" onclick="deleteSelectedSkills()" disabled data-i18n="skills.deleteSelected">Delete Selected</button>
1383
- </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>
1384
1391
  </div>
1385
1392
  <div class="tasks-list" id="skillsList"><div class="spinner"></div></div>
1386
- <div class="pagination" id="skillsPagination"></div>
1393
+ <div class="pagination-row">
1394
+ <div class="pagination" id="skillsPagination"></div>
1395
+ </div>
1387
1396
  <div id="hubSkillsSection" style="display:none;margin-top:16px">
1388
1397
  <div class="section-title" style="margin-bottom:12px" data-i18n="skills.hub.title">\u{1F310} Team Skills</div>
1389
1398
  <div class="tasks-list" id="hubSkillsList"></div>
@@ -2052,6 +2061,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
2052
2061
  let activeSession=null,activeRole='',editingId=null,searchTimer=null,memoryCache={},currentPage=1,totalPages=1,totalCount=0,PAGE_SIZE=20,metricsDays=30;
2053
2062
  let memorySearchScope='allLocal',skillSearchScope='allLocal',taskSearchScope='allLocal';
2054
2063
  let _lastMemoriesFingerprint='',_lastTasksFingerprint='',_lastSkillsFingerprint='';
2064
+ let selectedMemoryIds=new Set(),currentMemoryIds=[];
2065
+ let selectedTaskIds=new Set(),currentTaskIds=[];
2055
2066
  let _embeddingWarningShown=false;
2056
2067
  let _currentAgentOwner='agent:main';
2057
2068
  try {
@@ -2126,6 +2137,8 @@ const I18N={
2126
2137
  'tasks.status.active':'Active',
2127
2138
  'tasks.status.completed':'Completed',
2128
2139
  'tasks.status.skipped':'Skipped',
2140
+ 'tasks.search.placeholder':'Search tasks...',
2141
+ 'tasks.search.meta':'Found {0} tasks',
2129
2142
  'tasks.empty':'No tasks yet. Tasks are automatically created as you converse.',
2130
2143
  'tasks.loading':'Loading...',
2131
2144
  'tasks.untitled':'Untitled Task',
@@ -2166,8 +2179,8 @@ const I18N={
2166
2179
  'notif.timeAgo.hour':'{n}h ago',
2167
2180
  'notif.timeAgo.day':'{n}d ago',
2168
2181
  'stat.memories':'Memories',
2169
- 'stat.sessions':'Sessions',
2170
- 'stat.embeddings':'Embeddings',
2182
+ 'stat.tasks':'Tasks',
2183
+ 'stat.skills':'Skills',
2171
2184
  'stat.agents':'Agents',
2172
2185
  'stat.active':'active',
2173
2186
  'stat.deduped':'deduped',
@@ -2207,6 +2220,7 @@ const I18N={
2207
2220
  'card.dedupTarget':'Target: ',
2208
2221
  'card.dedupReason':'Reason: ',
2209
2222
  'card.newSummary':'New',
2223
+ 'pagination.pageSize':'Items per page',
2210
2224
  'pagination.total':' total',
2211
2225
  'range':'Range',
2212
2226
  'range.days':'days',
@@ -2413,7 +2427,13 @@ const I18N={
2413
2427
  'skills.nochangelog':'No changelog',
2414
2428
  'skills.status.active':'Active',
2415
2429
  'skills.status.draft':'Draft',
2416
- '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: ',
2417
2437
  'skills.updated':'Updated: ',
2418
2438
  'skills.task.prefix':'Task: ',
2419
2439
  'skills.selectAll':'Select All',
@@ -2759,6 +2779,10 @@ const I18N={
2759
2779
  'task.cancel':'Cancel',
2760
2780
  'task.delete.confirm':'Are you sure you want to delete this task? This cannot be undone.',
2761
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}.',
2762
2786
  'task.save.error':'Failed to save task: ',
2763
2787
  'task.retrySkill':'Retry Skill Generation',
2764
2788
  'task.retrySkill.short':'Retry Skill',
@@ -2773,6 +2797,10 @@ const I18N={
2773
2797
  'skill.delete.error':'Failed to delete skill: ',
2774
2798
  'skill.delete.partial':'Deleted {ok} skills, failed {fail}.',
2775
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}.',
2776
2804
  'skill.save.error':'Failed to save skill: ',
2777
2805
  'update.available':'New version available',
2778
2806
  'update.run':'Run',
@@ -2876,6 +2904,8 @@ const I18N={
2876
2904
  'tasks.status.active':'进行中',
2877
2905
  'tasks.status.completed':'已完成',
2878
2906
  'tasks.status.skipped':'已跳过',
2907
+ 'tasks.search.placeholder':'搜索任务...',
2908
+ 'tasks.search.meta':'找到 {0} 个任务',
2879
2909
  'tasks.empty':'暂无任务。任务会随着对话自动创建。',
2880
2910
  'tasks.loading':'加载中...',
2881
2911
  'tasks.untitled':'未命名任务',
@@ -2916,8 +2946,8 @@ const I18N={
2916
2946
  'notif.timeAgo.hour':'{n}小时前',
2917
2947
  'notif.timeAgo.day':'{n}天前',
2918
2948
  'stat.memories':'记忆',
2919
- 'stat.sessions':'会话',
2920
- 'stat.embeddings':'嵌入',
2949
+ 'stat.tasks':'任务',
2950
+ 'stat.skills':'技能',
2921
2951
  'stat.agents':'智能体',
2922
2952
  'stat.active':'活跃',
2923
2953
  'stat.deduped':'已去重',
@@ -2957,6 +2987,7 @@ const I18N={
2957
2987
  'card.dedupTarget':'关联: ',
2958
2988
  'card.dedupReason':'原因: ',
2959
2989
  'card.newSummary':'新摘要',
2990
+ 'pagination.pageSize':'每页数量',
2960
2991
  'pagination.total':' 条',
2961
2992
  'range':'范围',
2962
2993
  'range.days':'天',
@@ -3163,7 +3194,13 @@ const I18N={
3163
3194
  'skills.nochangelog':'暂无变更记录',
3164
3195
  'skills.status.active':'生效中',
3165
3196
  'skills.status.draft':'草稿',
3166
- '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':'启用技能失败:',
3167
3204
  'skills.updated':'更新于:',
3168
3205
  'skills.task.prefix':'任务:',
3169
3206
  'skills.selectAll':'全选',
@@ -3509,6 +3546,10 @@ const I18N={
3509
3546
  'task.cancel':'取消',
3510
3547
  'task.delete.confirm':'确定要删除此任务吗?此操作不可撤销。',
3511
3548
  'task.delete.error':'删除任务失败:',
3549
+ 'task.deleteSelected':'删除选中',
3550
+ 'task.delete.selected.confirm':'确定删除选中的 {count} 个任务吗?此操作不可撤销。',
3551
+ 'task.delete.success':'已删除 {count} 个任务。',
3552
+ 'task.delete.partial':'已删除 {ok} 个任务,失败 {fail} 个。',
3512
3553
  'task.save.error':'保存任务失败:',
3513
3554
  'task.retrySkill':'重新生成技能',
3514
3555
  'task.retrySkill.short':'重试技能',
@@ -3523,6 +3564,10 @@ const I18N={
3523
3564
  'skill.delete.error':'删除技能失败:',
3524
3565
  'skill.delete.partial':'已删除 {ok} 个技能,失败 {fail} 个。',
3525
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} 条。',
3526
3571
  'skill.save.error':'保存技能失败:',
3527
3572
  'update.available':'发现新版本',
3528
3573
  'update.run':'执行命令',
@@ -3807,9 +3852,12 @@ function switchView(view){
3807
3852
  }
3808
3853
  var sessionSection=document.getElementById('sidebarSessionSection');
3809
3854
  if(sessionSection){
3810
- if(view==='memories'){sessionSection.style.visibility='';sessionSection.style.pointerEvents='';}
3855
+ if(view==='memories'||view==='tasks'||view==='skills'){sessionSection.style.visibility='';sessionSection.style.pointerEvents='';}
3811
3856
  else{sessionSection.style.visibility='hidden';sessionSection.style.pointerEvents='none';}
3812
3857
  }
3858
+ if(view==='memories'||view==='tasks'||view==='skills'){
3859
+ loadStats();
3860
+ }
3813
3861
  if(view==='tasks') loadTasks();
3814
3862
  else if(view==='skills') loadSkills();
3815
3863
  else if(view==='analytics') loadMetrics();
@@ -3831,6 +3879,7 @@ function onMemoryScopeChange(){
3831
3879
  currentPage=1;
3832
3880
  activeSession=null;activeRole='';
3833
3881
  _lastMemoriesFingerprint='';
3882
+ if(memorySearchScope==='hub') selectedMemoryIds.clear();
3834
3883
  var isHub=memorySearchScope==='hub';
3835
3884
  var ownerSel=document.getElementById('filterOwner');
3836
3885
  var filterBar=document.getElementById('filterBar');
@@ -3838,6 +3887,7 @@ function onMemoryScopeChange(){
3838
3887
  if(ownerSel){ownerSel.style.display=isHub?'none':'';if(isHub)ownerSel.value='';}
3839
3888
  if(filterBar) filterBar.style.display=isHub?'none':'';
3840
3889
  if(dateFilter) dateFilter.style.display=isHub?'none':'';
3890
+ updateMemorySelectionToolbar();
3841
3891
  if(document.getElementById('searchInput').value.trim()) doSearch(document.getElementById('searchInput').value);
3842
3892
  else if(isHub) { document.getElementById('sharingSearchMeta').textContent=''; loadHubMemories(); }
3843
3893
  else {
@@ -3899,7 +3949,9 @@ function onTasksPageSizeChange(){
3899
3949
 
3900
3950
  function onTaskScopeChange(){
3901
3951
  taskSearchScope=document.getElementById('taskSearchScope')?.value||'allLocal';
3952
+ if(taskSearchScope==='hub') selectedTaskIds.clear();
3902
3953
  tasksPage=0;
3954
+ updateTaskSelectionToolbar();
3903
3955
  loadTasks();
3904
3956
  }
3905
3957
 
@@ -5124,6 +5176,7 @@ function renderSharingMemorySearchResults(data,query){
5124
5176
  const list=document.getElementById('memoryList');
5125
5177
  const localHits=(data&&data.local&&Array.isArray(data.local.hits))?data.local.hits:[];
5126
5178
  const hubHits=(data&&data.hub&&Array.isArray(data.hub.hits))?data.hub.hits:[];
5179
+ setPageSizeVisible('memoryPageSize',(localHits.length+hubHits.length)>0);
5127
5180
  document.getElementById('searchMeta').textContent='Search results for "'+query+'"';
5128
5181
  document.getElementById('sharingSearchMeta').textContent=t('scope.local')+' '+localHits.length+' · '+t('scope.hub')+' '+hubHits.length;
5129
5182
  document.getElementById('pagination').innerHTML='';
@@ -5912,6 +5965,11 @@ function escapeHtml(s){
5912
5965
  return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
5913
5966
  }
5914
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
+
5915
5973
  function setMetricsDays(d){
5916
5974
  metricsDays=d;
5917
5975
  document.querySelectorAll('.metrics-toolbar .range-btn').forEach(btn=>btn.classList.toggle('active',Number(btn.dataset.days)===d));
@@ -5931,6 +5989,7 @@ async function loadMetrics(){
5931
5989
  }catch(e){console.error('loadMetrics',e)}
5932
5990
  }
5933
5991
 
5992
+ function _st(id,v){var e=document.getElementById(id);if(e)e.textContent=v;}
5934
5993
  function formatNum(n){return n>=1e6?(n/1e6).toFixed(1)+'M':n>=1e3?(n/1e3).toFixed(1)+'k':String(n);}
5935
5994
  function dateLoc(){return curLang==='zh'?'zh-CN':'en-US';}
5936
5995
 
@@ -5940,13 +5999,127 @@ let tasksPage=0;
5940
5999
  let tasksPageSize=20;
5941
6000
 
5942
6001
  function setTaskStatusFilter(btn,status){
5943
- 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'));
5944
6003
  btn.classList.add('active');
5945
6004
  tasksStatusFilter=status;
5946
6005
  tasksPage=0;
5947
6006
  loadTasks();
5948
6007
  }
5949
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
+
5950
6123
  async function loadTasks(silent){
5951
6124
  const scope=document.getElementById('taskSearchScope')?document.getElementById('taskSearchScope').value:taskSearchScope;
5952
6125
  taskSearchScope=scope||'allLocal';
@@ -5954,15 +6127,41 @@ async function loadTasks(silent){
5954
6127
  const list=document.getElementById('tasksList');
5955
6128
  if(!silent) list.innerHTML='<div class="spinner"></div>';
5956
6129
  try{
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
+
5957
6151
  const params=new URLSearchParams({limit:String(tasksPageSize),offset:String(tasksPage*tasksPageSize)});
5958
6152
  if(tasksStatusFilter) params.set('status',tasksStatusFilter);
6153
+ if(activeSession) params.set('session',activeSession);
6154
+ if(ownerVal) params.set('owner',ownerVal);
5959
6155
  var baseP=new URLSearchParams();
6156
+ if(activeSession) baseP.set('session',activeSession);
6157
+ if(ownerVal) baseP.set('owner',ownerVal);
6158
+ var baseQuery=baseP.toString();
5960
6159
  const [data,allD,activeD,compD,skipD]=await Promise.all([
5961
6160
  fetch('/api/tasks?'+params).then(r=>r.json()),
5962
- fetch('/api/tasks?limit=1&offset=0&'+baseP).then(r=>r.json()),
5963
- fetch('/api/tasks?status=active&limit=1&offset=0&'+baseP).then(r=>r.json()),
5964
- fetch('/api/tasks?status=completed&limit=1&offset=0&'+baseP).then(r=>r.json()),
5965
- 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())
5966
6165
  ]);
5967
6166
  if(silent){
5968
6167
  var fp=JSON.stringify((data.tasks||[]).map(function(tk){return tk.id+'|'+tk.status+'|'+(tk.updatedAt||tk.startedAt)}));
@@ -5972,50 +6171,24 @@ async function loadTasks(silent){
5972
6171
  }else{
5973
6172
  _lastTasksFingerprint='';
5974
6173
  }
5975
- document.getElementById('tasksTotalCount').textContent=formatNum(allD.total);
5976
- document.getElementById('tasksActiveCount').textContent=formatNum(activeD.total);
5977
- document.getElementById('tasksCompletedCount').textContent=formatNum(compD.total);
5978
- 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);
5979
6180
 
5980
6181
  if(!data.tasks||data.tasks.length===0){
5981
- list.innerHTML='<div style="text-align:center;padding:48px;color:var(--text-muted);font-size:14px" data-i18n="tasks.empty">'+t('tasks.empty')+'</div>';
5982
6182
  document.getElementById('tasksPagination').innerHTML='';
5983
- return;
6183
+ } else {
6184
+ renderTasksPagination(data.total);
5984
6185
  }
5985
-
5986
- list.innerHTML=data.tasks.map(task=>{
5987
- const timeStr=formatTime(task.startedAt);
5988
- const endStr=task.endedAt?formatTime(task.endedAt):'';
5989
- const durationStr=task.endedAt?formatDuration(task.endedAt-task.startedAt):'';
5990
- var taskIsLocalShared=task.owner==='public';
5991
- var taskIsTeamShared=!!task.sharingVisibility;
5992
- var taskScope=taskIsTeamShared?'team':taskIsLocalShared?'local':'private';
5993
- return '<div class="task-card status-'+task.status+'" onclick="openTaskDetail(\\''+task.id+'\\')">'+
5994
- '<div class="task-card-top">'+
5995
- '<div class="task-card-title">'+esc(task.title)+'</div>'+
5996
- '<div class="task-card-badges">'+renderScopeBadge(taskScope)+'<span class="task-status-badge '+task.status+'">'+t('tasks.status.'+task.status)+'</span></div>'+
5997
- '</div>'+
5998
- (task.summary?'<div class="task-card-summary'+(task.status==='skipped'?' skipped-reason':'')+'">'+esc(task.summary)+'</div>':'')+
5999
- '<div class="task-card-bottom">'+
6000
- '<span class="tag"><span class="icon">\\u{1F4C5}</span> '+timeStr+'</span>'+
6001
- (durationStr?'<span class="tag"><span class="icon">\\u23F1</span> '+durationStr+'</span>':'')+
6002
- '<span class="tag"><span class="icon">\\u{1F4DD}</span> '+task.chunkCount+' '+t('tasks.chunks.label')+'</span>'+
6003
- '<span class="tag"><span class="icon">\\u{1F4C2}</span> '+(task.sessionKey||'').slice(0,12)+'</span>'+
6004
- '</div>'+
6005
- '<div class="card-actions" onclick="event.stopPropagation()">'+
6006
- '<button class="btn btn-sm btn-ghost" onclick="openTaskDetail(\\''+task.id+'\\')">'+t('card.expand')+'</button>'+
6007
- (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>':'')+
6008
- (task.status==='completed'
6009
- ?'<button class="btn btn-sm btn-ghost" onclick="openTaskScopeModalFromList(\\''+task.id+'\\',\\''+taskScope+'\\')">\\u270F '+t('share.shareBtn')+'</button>'
6010
- :'<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>')+
6011
- '<button class="btn btn-sm btn-ghost" style="color:var(--accent)" onclick="deleteTask(\\''+task.id+'\\')">'+t('task.delete')+'</button>'+
6012
- '</div>'+
6013
- '</div>';
6014
- }).join('');
6015
-
6016
- renderTasksPagination(data.total);
6017
6186
  }catch(e){
6018
6187
  console.error('loadTasks error:',e);
6188
+ currentTaskIds=[];
6189
+ selectedTaskIds.clear();
6190
+ setPageSizeVisible('tasksPageSize',false);
6191
+ updateTaskSelectionToolbar();
6019
6192
  list.innerHTML='<div style="text-align:center;padding:24px;color:var(--rose)">Failed to load tasks: '+String(e)+'</div>';
6020
6193
  }
6021
6194
  }
@@ -6224,6 +6397,8 @@ async function deleteTask(taskId){
6224
6397
  const r=await fetch('/api/task/'+taskId,{method:'DELETE'});
6225
6398
  const d=await r.json();
6226
6399
  if(!r.ok) throw new Error(d.error||'unknown');
6400
+ selectedTaskIds.delete(taskId);
6401
+ updateTaskSelectionToolbar();
6227
6402
  closeTaskDetail();
6228
6403
  document.getElementById('taskDetailOverlay').classList.remove('show');
6229
6404
  loadTasks();
@@ -6240,7 +6415,7 @@ let currentLocalSkills=[];
6240
6415
  let skillsFilterSignature='';
6241
6416
 
6242
6417
  function setSkillStatusFilter(btn,status){
6243
- 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'));
6244
6419
  btn.classList.add('active');
6245
6420
  skillsStatusFilter=status;
6246
6421
  skillsPage=0;
@@ -6250,14 +6425,15 @@ function setSkillStatusFilter(btn,status){
6250
6425
  function updateSkillSelectionToolbar(){
6251
6426
  var selectAllBtn=document.getElementById('skillSelectAllBtn');
6252
6427
  var bulkDeleteBtn=document.getElementById('skillBulkDeleteBtn');
6253
- var total=currentLocalSkills.length;
6428
+ var total=document.querySelectorAll('#skillsList .skill-select-check').length;
6254
6429
  var selected=selectedSkillIds.size;
6255
6430
  if(selectAllBtn){
6256
6431
  selectAllBtn.textContent=t(selected>0&&selected===total&&total>0?'skills.unselectAll':'skills.selectAll');
6432
+ selectAllBtn.style.display=total===0?'none':'';
6257
6433
  }
6258
6434
  if(bulkDeleteBtn){
6259
- bulkDeleteBtn.disabled=selected===0;
6260
6435
  var base=t('skills.deleteSelected');
6436
+ bulkDeleteBtn.style.display=selected>0?'':'none';
6261
6437
  bulkDeleteBtn.textContent=selected>0?(base+' ('+selected+')'):base;
6262
6438
  }
6263
6439
  }
@@ -6333,9 +6509,13 @@ async function loadSkills(silent){
6333
6509
  try{
6334
6510
  const params=new URLSearchParams();
6335
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);
6336
6516
  const visFilter=document.getElementById('skillVisibilityFilter')?.value;
6337
6517
  if(visFilter) params.set('visibility',visFilter);
6338
- const filterSignature=[query,skillSearchScope,skillsStatusFilter,visFilter||''].join('|');
6518
+ const filterSignature=[query,skillSearchScope,skillsStatusFilter,visFilter||'',skillOwnerVal].join('|');
6339
6519
  if(!silent&&filterSignature!==skillsFilterSignature){
6340
6520
  skillsPage=0;
6341
6521
  }
@@ -6381,7 +6561,7 @@ async function loadSkills(silent){
6381
6561
  const selectedAttr=selectedSkillIds.has(skill.id)?' checked':'';
6382
6562
  return '<div class="skill-card '+installedClass+' '+statusClass+'" onclick="openSkillDetail(&quot;'+escAttr(skill.id)+'&quot;)">'+
6383
6563
  '<div class="skill-card-top">'+
6384
- '<div class="skill-card-name"><label class="skill-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>'+
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>'+
6385
6565
  '<div class="skill-card-badges">'+
6386
6566
  qsBadge+
6387
6567
  '<span class="skill-badge version">v'+skill.version+'</span>'+
@@ -6401,6 +6581,13 @@ async function loadSkills(silent){
6401
6581
  (skill.status==='active'
6402
6582
  ?'<button class="btn btn-sm btn-ghost" onclick="openSkillScopeModalFromList(&quot;'+escAttr(skill.id)+'&quot;,&quot;'+skillScope+'&quot;)">\\u270F '+t('share.shareBtn')+'</button>'
6403
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>'+
6404
6591
  '</span>'+
6405
6592
  '</div>'+
6406
6593
  '</div>';
@@ -6414,16 +6601,16 @@ async function loadSkills(silent){
6414
6601
  const pageSkills=localSkills.slice(startIndex,startIndex+skillsPageSize);
6415
6602
  list.innerHTML=renderLocalCards(pageSkills);
6416
6603
  renderSkillsPagination(totalLocalSkills);
6604
+ setPageSizeVisible('skillsPageSize',totalLocalSkills>0);
6417
6605
  updateSkillSelectionToolbar();
6418
6606
 
6419
6607
  if(skillSearchScope==='allLocal'){
6420
6608
  if(hubSection) hubSection.style.display='none';
6421
- document.getElementById('skillSearchMeta').textContent=query?(t('skills.search.local')+' '+localSkills.length):'';
6422
- document.getElementById('skillsTotalCount').textContent=formatNum(localSkills.length);
6423
- document.getElementById('skillsActiveCount').textContent=formatNum(localSkills.filter(s=>s.status==='active').length);
6424
- document.getElementById('skillsDraftCount').textContent=formatNum(localSkills.filter(s=>s.status==='draft').length);
6425
- document.getElementById('skillsInstalledCount').textContent=formatNum(localSkills.filter(s=>s.installed).length);
6426
- 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));
6427
6614
  return;
6428
6615
  }
6429
6616
 
@@ -6431,12 +6618,11 @@ async function loadSkills(silent){
6431
6618
  if(hubSection) hubSection.style.display='block';
6432
6619
  var localIds=new Set(localSkills.map(function(s){return s.id;}));
6433
6620
  if(hubList){ loadHubSkills(hubList, localIds); }
6434
- document.getElementById('skillSearchMeta').textContent=t('skills.search.local')+' '+localSkills.length;
6435
- document.getElementById('skillsTotalCount').textContent=formatNum(localSkills.length);
6436
- document.getElementById('skillsActiveCount').textContent=formatNum(localSkills.filter(s=>s.status==='active').length);
6437
- document.getElementById('skillsDraftCount').textContent=formatNum(localSkills.filter(s=>s.status==='draft').length);
6438
- document.getElementById('skillsInstalledCount').textContent=formatNum(localSkills.filter(s=>s.installed).length);
6439
- 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));
6440
6626
  return;
6441
6627
  }
6442
6628
 
@@ -6446,7 +6632,9 @@ async function loadSkills(silent){
6446
6632
  sharingParams.set('maxResults','20');
6447
6633
  const r=await fetch('/api/sharing/search/skills?'+sharingParams.toString());
6448
6634
  const data=await r.json();
6449
- 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);});
6450
6638
  const hubHits=(data.hub&&Array.isArray(data.hub.hits))?data.hub.hits:[];
6451
6639
 
6452
6640
  const sp=document.getElementById('skillsPagination');
@@ -6476,17 +6664,18 @@ async function loadSkills(silent){
6476
6664
  }).join(''):'';
6477
6665
  }
6478
6666
 
6479
- document.getElementById('skillSearchMeta').textContent=t('skills.search.local')+' '+localHits.length+(hubHits.length?' · '+t('scope.hub')+' '+hubHits.length:'');
6480
- document.getElementById('skillsTotalCount').textContent=formatNum(localHits.length+hubHits.length);
6481
- document.getElementById('skillsActiveCount').textContent=formatNum(localHits.length);
6482
- document.getElementById('skillsDraftCount').textContent='0';
6483
- document.getElementById('skillsInstalledCount').textContent='-';
6484
- 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);
6485
6673
  updateSkillSelectionToolbar();
6486
6674
  }catch(e){
6487
6675
  list.innerHTML='<div style="text-align:center;padding:24px;color:var(--rose)">'+t('skills.load.error')+': '+esc(String(e))+'</div>';
6488
6676
  const sp=document.getElementById('skillsPagination');
6489
6677
  if(sp) sp.innerHTML='';
6678
+ setPageSizeVisible('skillsPageSize',false);
6490
6679
  if(hubList){
6491
6680
  hubList.innerHTML='<div style="text-align:center;padding:24px;color:var(--rose)">'+t('skills.load.error')+'</div>';
6492
6681
  }
@@ -6496,19 +6685,28 @@ async function loadSkills(silent){
6496
6685
  async function loadHubTasks(){
6497
6686
  var list=document.getElementById('tasksList');
6498
6687
  if(!list) return;
6688
+ currentTaskIds=[];
6689
+ selectedTaskIds.clear();
6690
+ updateTaskSelectionToolbar();
6499
6691
  list.innerHTML='<div class="spinner"></div>';
6500
6692
  try{
6501
6693
  var r=await fetch('/api/sharing/tasks/list?limit='+tasksPageSize);
6502
6694
  var d=await r.json();
6503
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
+ }
6504
6701
  hubTasksCache=tasks;
6505
- document.getElementById('tasksTotalCount').textContent=formatNum(tasks.length);
6506
- document.getElementById('tasksActiveCount').textContent='-';
6507
- document.getElementById('tasksCompletedCount').textContent='-';
6508
- document.getElementById('tasksSkippedCount').textContent='-';
6702
+ _st('tasksTotalCount',formatNum(tasks.length));
6703
+ _st('tasksActiveCount','-');
6704
+ _st('tasksCompletedCount','-');
6705
+ _st('tasksSkippedCount','-');
6509
6706
  if(!tasks.length){
6510
6707
  list.innerHTML='<div style="text-align:center;padding:48px;color:var(--text-muted);font-size:14px">'+t('tasks.empty')+'</div>';
6511
6708
  document.getElementById('tasksPagination').innerHTML='';
6709
+ setPageSizeVisible('tasksPageSize',false);
6512
6710
  return;
6513
6711
  }
6514
6712
  list.innerHTML=tasks.map(function(task,idx){
@@ -6527,9 +6725,11 @@ async function loadHubTasks(){
6527
6725
  '</div>';
6528
6726
  }).join('');
6529
6727
  document.getElementById('tasksPagination').innerHTML='';
6728
+ setPageSizeVisible('tasksPageSize',true);
6530
6729
  }catch(e){
6531
6730
  list.innerHTML='<div style="text-align:center;padding:48px;color:var(--text-muted);font-size:14px">'+t('tasks.empty')+'</div>';
6532
6731
  document.getElementById('tasksPagination').innerHTML='';
6732
+ setPageSizeVisible('tasksPageSize',false);
6533
6733
  }
6534
6734
  }
6535
6735
 
@@ -6685,7 +6885,14 @@ async function openSkillDetail(skillId){
6685
6885
  }
6686
6886
 
6687
6887
  window._currentSkillData=skill;
6688
- 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;
6689
6896
 
6690
6897
  }catch(e){
6691
6898
  document.getElementById('skillDetailTitle').textContent=t('skills.error');
@@ -7274,6 +7481,29 @@ async function deleteSkill(skillId){
7274
7481
  loadSkills();
7275
7482
  }catch(e){ alert(t('skill.delete.error')+e.message); }
7276
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
+ }
7277
7507
 
7278
7508
  async function deleteSelectedSkills(){
7279
7509
  var ids=Array.from(selectedSkillIds);
@@ -7931,15 +8161,17 @@ async function loadStats(ownerFilter){
7931
8161
  const activeCount=dedupB.active||tm;
7932
8162
  const inactiveCount=(dedupB.duplicate||0)+(dedupB.merged||0);
7933
8163
  var agentCount=(d.owners&&d.owners.length)?d.owners.length:1;
7934
- 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;
7935
8167
  if(sfp===_lastStatsFp) return;
7936
8168
  _lastStatsFp=sfp;
7937
8169
  document.getElementById('statTotal').textContent=tm;
7938
8170
  if(inactiveCount>0){
7939
8171
  document.getElementById('statTotal').title=activeCount+' '+t('stat.active')+', '+inactiveCount+' '+t('stat.deduped');
7940
8172
  }
7941
- document.getElementById('statSessions').textContent=d.totalSessions||0;
7942
- document.getElementById('statEmbeddings').textContent=d.totalEmbeddings||0;
8173
+ document.getElementById('statTasks').textContent=d.totalTasks||0;
8174
+ document.getElementById('statSkills').textContent=d.totalSkills||0;
7943
8175
  document.getElementById('statAgents').textContent=agentCount;
7944
8176
 
7945
8177
  const provEl=document.getElementById('embeddingStatus');
@@ -7963,45 +8195,76 @@ async function loadStats(ownerFilter){
7963
8195
  }).catch(()=>{});
7964
8196
  }
7965
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);
7966
8210
  const sl=document.getElementById('sessionList');
7967
- sl.innerHTML='<div class="session-item'+(activeSession===null?' active':'')+'" onclick="filterSession(null)"><span>'+t('sidebar.allsessions')+'</span><span class="count">'+tm+'</span></div>';
7968
- (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=>{
7969
8214
  const isActive=activeSession===s.session_key;
7970
8215
  const name=s.session_key.length>20?s.session_key.slice(0,8)+'...'+s.session_key.slice(-8):s.session_key;
7971
- 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>';
7972
8217
  });
7973
8218
 
7974
- const fSel=document.getElementById('filterSession');
7975
- 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;
7976
8225
  const curVal=activeSession||'';
7977
- var sessionCount=(d.sessions||[]).length;
8226
+ var sessionCount=viewSessions.length;
8227
+ fSel.style.display=sessionCount>0?'':'none';
7978
8228
  fSel.innerHTML='<option value="">'+t('filter.allsessions')+' ('+sessionCount+')</option>';
7979
- (d.sessions||[]).forEach(s=>{
8229
+ viewSessions.forEach(s=>{
7980
8230
  const sName=s.session_key.length>30?s.session_key.slice(0,12)+'...'+s.session_key.slice(-10):s.session_key;
7981
- 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>';
7982
8233
  });
7983
- }
8234
+ });
7984
8235
 
7985
- const ownerSel=document.getElementById('filterOwner');
7986
- if(ownerSel && d.owners && d.owners.length>0){
7987
- const curVal=ownerSel.value;
8236
+ if(d.owners && d.owners.length>0){
7988
8237
  var agents=d.owners.filter(function(o){return o && o.indexOf('agent:')===0;});
7989
- ownerSel.innerHTML='<option value="">'+t('filter.allagents')+'</option>';
7990
- agents.forEach(function(o){
7991
- var label=o.replace('agent:','');
7992
- 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='';
7993
8249
  });
7994
- if(agents.length<=1) ownerSel.style.display='none';
7995
- else ownerSel.style.display='';
7996
8250
  }
7997
8251
  }
7998
8252
 
7999
8253
  function onOwnerFilterChange(){
8000
- 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
+ });
8001
8260
  activeSession=null;
8002
8261
  currentPage=1;
8262
+ taskSearchQuery='';
8263
+ var tsi=document.getElementById('taskSearchInput');if(tsi)tsi.value='';
8003
8264
  refreshSessionDropdown(owner);
8004
8265
  applyFilters();
8266
+ loadTasks();
8267
+ loadSkills();
8005
8268
  }
8006
8269
 
8007
8270
  async function refreshSessionDropdown(ownerFilter){
@@ -8099,6 +8362,8 @@ async function loadMemories(page,silent){
8099
8362
 
8100
8363
  async function loadHubMemories(silent){
8101
8364
  const list=document.getElementById('memoryList');
8365
+ selectedMemoryIds.clear();
8366
+ updateMemorySelectionToolbar();
8102
8367
  if(!silent) list.innerHTML='<div class="spinner"></div>';
8103
8368
  try{
8104
8369
  const r=await fetch('/api/sharing/memories/list?limit='+PAGE_SIZE);
@@ -8187,12 +8452,26 @@ function debounceSearch(){
8187
8452
  function filterSession(key){
8188
8453
  activeSession=key;
8189
8454
  currentPage=1;
8190
- var fSel=document.getElementById('filterSession');
8191
- 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
+ });
8192
8461
  document.querySelectorAll('#sessionList .session-item').forEach(function(el,i){
8193
8462
  if(i===0) el.classList.toggle('active',!key);
8194
8463
  else el.classList.toggle('active',el.querySelector('span')?.title===key);
8195
8464
  });
8465
+ if(_activeView==='tasks'){
8466
+ loadStats();
8467
+ loadTasks();
8468
+ return;
8469
+ }
8470
+ if(_activeView==='skills'){
8471
+ loadStats();
8472
+ loadSkills();
8473
+ return;
8474
+ }
8196
8475
  loadAll();
8197
8476
  }
8198
8477
 
@@ -8219,14 +8498,88 @@ function clearDateFilter(){
8219
8498
  applyFilters();
8220
8499
  }
8221
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
+
8222
8570
  /* ─── Rendering ─── */
8223
8571
  function renderMemories(items){
8224
8572
  const list=document.getElementById('memoryList');
8573
+ setPageSizeVisible('memoryPageSize',items.length>0);
8225
8574
  if(!items.length){
8226
8575
  list.innerHTML='<div class="empty"><div class="icon">\\u{1F4ED}</div><p>'+t('empty.text')+'</p></div>';
8576
+ currentMemoryIds=[];
8577
+ updateMemorySelectionToolbar();
8227
8578
  return;
8228
8579
  }
8229
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);}));
8230
8583
  list.innerHTML=items.map(m=>{
8231
8584
  const time=m.created_at?new Date(typeof m.created_at==='number'?m.created_at:m.created_at).toLocaleString(dateLoc()):'';
8232
8585
  const role=m.role||'user';
@@ -8253,6 +8606,8 @@ function renderMemories(items){
8253
8606
  const isHubScope=memorySearchScope==='hub';
8254
8607
  const memScope=memShared?'team':isPublicMem?'local':'private';
8255
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>';
8256
8611
  let dedupInfo='';
8257
8612
  if(ds==='duplicate'||ds==='merged'){
8258
8613
  const reason=m.dedup_reason?'<span style="font-size:11px;color:var(--text-muted)">'+t('card.dedupReason')+esc(m.dedup_reason)+'</span>':'';
@@ -8278,7 +8633,7 @@ function renderMemories(items){
8278
8633
  }
8279
8634
  return '<div class="memory-card'+(isInactive?' dedup-inactive':'')+'">'+
8280
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>'+
8281
- '<div class="card-summary">'+cardTitle+'</div>'+
8636
+ '<div class="card-summary">'+selectBoxHtml+cardTitle+'</div>'+
8282
8637
  (function(){
8283
8638
  if(mc<=0) return '';
8284
8639
  var mergeHtml='<div class="card-merged-info">';
@@ -8308,6 +8663,7 @@ function renderMemories(items){
8308
8663
  vscore+
8309
8664
  '</div></div>';
8310
8665
  }).join('');
8666
+ updateMemorySelectionToolbar();
8311
8667
  }
8312
8668
 
8313
8669
  function updateMemoryCardBadge(chunkId,newScope){
@@ -8510,7 +8866,18 @@ async function deleteMemory(id){
8510
8866
  if(!(await confirmModal(t('confirm.delete'),{danger:true})))return;
8511
8867
  const r=await fetch('/api/memory/'+id,{method:'DELETE'});
8512
8868
  const d=await r.json();
8513
- 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
+ }
8514
8881
  else{toast(t('toast.delfail'),'error')}
8515
8882
  }
8516
8883