@memtensor/memos-local-openclaw-plugin 1.0.3 → 1.0.4-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -21
- package/dist/client/connector.d.ts +30 -0
- package/dist/client/connector.d.ts.map +1 -0
- package/dist/client/connector.js +219 -0
- package/dist/client/connector.js.map +1 -0
- package/dist/client/hub.d.ts +61 -0
- package/dist/client/hub.d.ts.map +1 -0
- package/dist/client/hub.js +148 -0
- package/dist/client/hub.js.map +1 -0
- package/dist/client/skill-sync.d.ts +29 -0
- package/dist/client/skill-sync.d.ts.map +1 -0
- package/dist/client/skill-sync.js +216 -0
- package/dist/client/skill-sync.js.map +1 -0
- package/dist/config.d.ts +2 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +70 -3
- package/dist/config.js.map +1 -1
- package/dist/embedding/index.d.ts +4 -2
- package/dist/embedding/index.d.ts.map +1 -1
- package/dist/embedding/index.js +17 -1
- package/dist/embedding/index.js.map +1 -1
- package/dist/hub/auth.d.ts +19 -0
- package/dist/hub/auth.d.ts.map +1 -0
- package/dist/hub/auth.js +70 -0
- package/dist/hub/auth.js.map +1 -0
- package/dist/hub/server.d.ts +41 -0
- package/dist/hub/server.d.ts.map +1 -0
- package/dist/hub/server.js +747 -0
- package/dist/hub/server.js.map +1 -0
- package/dist/hub/user-manager.d.ts +29 -0
- package/dist/hub/user-manager.d.ts.map +1 -0
- package/dist/hub/user-manager.js +125 -0
- package/dist/hub/user-manager.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/ingest/providers/index.d.ts +10 -2
- package/dist/ingest/providers/index.d.ts.map +1 -1
- package/dist/ingest/providers/index.js +203 -6
- package/dist/ingest/providers/index.js.map +1 -1
- package/dist/ingest/providers/openai.d.ts +1 -0
- package/dist/ingest/providers/openai.d.ts.map +1 -1
- package/dist/ingest/providers/openai.js +1 -0
- package/dist/ingest/providers/openai.js.map +1 -1
- package/dist/ingest/task-processor.js +1 -1
- package/dist/ingest/task-processor.js.map +1 -1
- package/dist/openclaw-api.d.ts +53 -0
- package/dist/openclaw-api.d.ts.map +1 -0
- package/dist/openclaw-api.js +189 -0
- package/dist/openclaw-api.js.map +1 -0
- package/dist/recall/engine.js +1 -1
- package/dist/recall/engine.js.map +1 -1
- package/dist/shared/llm-call.d.ts +4 -1
- package/dist/shared/llm-call.d.ts.map +1 -1
- package/dist/shared/llm-call.js +15 -0
- package/dist/shared/llm-call.js.map +1 -1
- package/dist/sharing/types.contract.d.ts +2 -0
- package/dist/sharing/types.contract.d.ts.map +1 -0
- package/dist/sharing/types.contract.js +3 -0
- package/dist/sharing/types.contract.js.map +1 -0
- package/dist/sharing/types.d.ts +80 -0
- package/dist/sharing/types.d.ts.map +1 -0
- package/dist/sharing/types.js +3 -0
- package/dist/sharing/types.js.map +1 -0
- package/dist/skill/evaluator.d.ts.map +1 -1
- package/dist/skill/evaluator.js +2 -2
- package/dist/skill/evaluator.js.map +1 -1
- package/dist/skill/generator.d.ts.map +1 -1
- package/dist/skill/generator.js +4 -4
- package/dist/skill/generator.js.map +1 -1
- package/dist/skill/upgrader.js +1 -1
- package/dist/skill/upgrader.js.map +1 -1
- package/dist/skill/validator.js +1 -1
- package/dist/skill/validator.js.map +1 -1
- package/dist/storage/sqlite.d.ts +294 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +828 -8
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +3 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/memory-search.d.ts +3 -2
- package/dist/tools/memory-search.d.ts.map +1 -1
- package/dist/tools/memory-search.js +48 -7
- package/dist/tools/memory-search.js.map +1 -1
- package/dist/tools/network-memory-detail.d.ts +4 -0
- package/dist/tools/network-memory-detail.d.ts.map +1 -0
- package/dist/tools/network-memory-detail.js +34 -0
- package/dist/tools/network-memory-detail.js.map +1 -0
- package/dist/types.d.ts +47 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +2595 -345
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts +45 -0
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +1153 -15
- package/dist/viewer/server.js.map +1 -1
- package/index.ts +428 -16
- package/openclaw.plugin.json +2 -1
- package/package.json +3 -3
- package/scripts/postinstall.cjs +282 -45
- package/skill/memos-memory-guide/SKILL.md +26 -2
- package/src/client/connector.ts +218 -0
- package/src/client/hub.ts +189 -0
- package/src/client/skill-sync.ts +202 -0
- package/src/config.ts +92 -3
- package/src/embedding/index.ts +21 -1
- package/src/hub/auth.ts +78 -0
- package/src/hub/server.ts +740 -0
- package/src/hub/user-manager.ts +139 -0
- package/src/index.ts +7 -4
- package/src/ingest/providers/index.ts +240 -6
- package/src/ingest/providers/openai.ts +1 -1
- package/src/ingest/task-processor.ts +1 -1
- package/src/openclaw-api.ts +287 -0
- package/src/recall/engine.ts +1 -1
- package/src/shared/llm-call.ts +19 -1
- package/src/sharing/types.contract.ts +40 -0
- package/src/sharing/types.ts +102 -0
- package/src/skill/evaluator.ts +3 -2
- package/src/skill/generator.ts +6 -4
- package/src/skill/upgrader.ts +1 -1
- package/src/skill/validator.ts +1 -1
- package/src/storage/sqlite.ts +1093 -7
- package/src/tools/index.ts +1 -0
- package/src/tools/memory-search.ts +57 -8
- package/src/tools/network-memory-detail.ts +34 -0
- package/src/types.ts +42 -2
- package/src/viewer/html.ts +2595 -345
- package/src/viewer/server.ts +1068 -18
- package/dist/ingest/extra-paths.d.ts +0 -13
- package/dist/ingest/extra-paths.d.ts.map +0 -1
- package/dist/ingest/extra-paths.js +0 -173
- package/dist/ingest/extra-paths.js.map +0 -1
package/dist/viewer/html.js
CHANGED
|
@@ -8,7 +8,8 @@ function viewerHTML(pluginVersion) {
|
|
|
8
8
|
<head>
|
|
9
9
|
<meta charset="UTF-8">
|
|
10
10
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
11
|
-
<
|
|
11
|
+
<link rel="icon" href="https://statics.memtensor.com.cn/logo/color-m.svg" type="image/svg+xml">
|
|
12
|
+
<title>MemOS 记忆</title>
|
|
12
13
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
13
14
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
14
15
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
@@ -61,8 +62,9 @@ function viewerHTML(pluginVersion) {
|
|
|
61
62
|
[data-theme="light"] .analytics-card.amber .ac-value{color:#d97706}
|
|
62
63
|
[data-theme="light"] .analytics-section{background:#fff;border-color:var(--border);box-shadow:0 1px 3px rgba(0,0,0,.04)}
|
|
63
64
|
[data-theme="light"] .analytics-section::before{background:none}
|
|
64
|
-
[data-theme="light"] .chart-bar{box-shadow:
|
|
65
|
-
[data-theme="light"] .chart-bar:hover{box-shadow:0
|
|
65
|
+
[data-theme="light"] .chart-bar{box-shadow:0 1px 4px rgba(99,102,241,.12)}
|
|
66
|
+
[data-theme="light"] .chart-bar:hover{box-shadow:0 3px 12px rgba(79,70,229,.2)}
|
|
67
|
+
[data-theme="light"] .chart-bars::before{opacity:.3}
|
|
66
68
|
[data-theme="light"] .tool-chart-tooltip{background:rgba(17,24,39,.92);color:#e8eaed;border-color:rgba(99,102,241,.3);box-shadow:0 8px 24px rgba(0,0,0,.2)}
|
|
67
69
|
[data-theme="light"] .tool-chart-tooltip .tt-time{color:#a5b4fc}
|
|
68
70
|
[data-theme="light"] .tool-chart-tooltip .tt-val{color:#e8eaed}
|
|
@@ -108,9 +110,15 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
108
110
|
/* ─── App Layout (dark dashboard, same as www) ─── */
|
|
109
111
|
.app{display:none;flex-direction:column;min-height:100vh}
|
|
110
112
|
.topbar{background:rgba(11,13,17,.88);border-bottom:1px solid var(--border);padding:0 28px;height:56px;display:flex;align-items:center;position:sticky;top:0;z-index:100;backdrop-filter:blur(12px)}
|
|
111
|
-
.topbar .brand{display:flex;align-items:center;gap:
|
|
112
|
-
.topbar .brand .icon{width:
|
|
113
|
+
.topbar .brand{display:flex;align-items:center;gap:8px;font-weight:700;font-size:15px;color:var(--text);letter-spacing:-.02em;flex-shrink:0}
|
|
114
|
+
.topbar .brand .icon{width:24px;height:24px;display:flex;align-items:center;justify-content:center;font-size:18px;background:none;border-radius:0}
|
|
115
|
+
.topbar .brand .brand-title{font-size:13px;font-weight:500;opacity:.7}
|
|
116
|
+
.topbar .brand .brand-col{display:flex;flex-direction:column;gap:0}
|
|
117
|
+
.topbar .brand .brand-powered{font-size:9px;font-weight:500;color:var(--text-sec);opacity:.55;letter-spacing:.02em;line-height:1}
|
|
113
118
|
.topbar .brand .sub{font-weight:400;color:var(--text-muted);font-size:11px}
|
|
119
|
+
.memos-logo{display:inline-flex;align-items:center}
|
|
120
|
+
.memos-logo svg{height:28px;width:28px}
|
|
121
|
+
.brand-sep{width:1px;height:20px;background:var(--border);opacity:.5;margin:0 2px}
|
|
114
122
|
.version-badge{font-size:10px;font-weight:600;color:var(--text-muted);background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.1);padding:1px 7px;border-radius:6px;margin-left:6px;letter-spacing:.02em;user-select:all}
|
|
115
123
|
[data-theme="light"] .version-badge{background:rgba(0,0,0,.05);border-color:rgba(0,0,0,.08);color:var(--text-sec)}
|
|
116
124
|
.topbar-center{flex:1;display:flex;justify-content:center}
|
|
@@ -148,6 +156,101 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
148
156
|
.search-bar input:focus{border-color:var(--pri);box-shadow:0 0 0 3px var(--pri-glow)}
|
|
149
157
|
.search-bar .search-icon{position:absolute;left:14px;top:50%;transform:translateY(-50%);color:var(--text-muted);font-size:14px;pointer-events:none}
|
|
150
158
|
.search-meta{font-size:12px;color:var(--text-sec);margin-bottom:14px;padding:0 2px}
|
|
159
|
+
.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}
|
|
160
|
+
.sharing-inline-meta{font-size:12px;color:var(--text-muted);margin:-8px 0 14px 2px}
|
|
161
|
+
.sharing-sidebar-card{margin:14px 0 18px;border:1px solid var(--border);background:var(--bg-card);border-radius:12px;padding:12px;box-shadow:var(--shadow-sm)}
|
|
162
|
+
.sharing-sidebar-card .title{font-size:12px;font-weight:700;color:var(--text);margin-bottom:8px;text-transform:uppercase;letter-spacing:.04em}
|
|
163
|
+
.sharing-sidebar-card .status{font-size:13px;color:var(--text-sec);line-height:1.5}
|
|
164
|
+
.sharing-sidebar-card .status strong{color:var(--text)}
|
|
165
|
+
.sharing-sidebar-card .hint{margin-top:8px;font-size:11px;color:var(--text-muted)}
|
|
166
|
+
.sharing-sidebar-card .user-row{display:flex;align-items:center;gap:8px;margin-bottom:10px}
|
|
167
|
+
.sharing-sidebar-card .user-row .username{font-size:13px;font-weight:600;color:var(--text)}
|
|
168
|
+
.sharing-sidebar-card .role-badge{display:inline-block;font-size:10px;font-weight:600;padding:2px 8px;border-radius:9999px;line-height:1.4;letter-spacing:.02em}
|
|
169
|
+
.sharing-sidebar-card .role-badge.admin{background:rgba(52,199,89,.15);color:#34c759}
|
|
170
|
+
.sharing-sidebar-card .role-badge.client{background:rgba(175,82,222,.15);color:#af52de}
|
|
171
|
+
.sharing-sidebar-card .info-grid{display:grid;grid-template-columns:auto 1fr;gap:4px 10px;font-size:12px;margin-bottom:8px}
|
|
172
|
+
.sharing-sidebar-card .info-grid .label{color:var(--text-muted);font-weight:500;white-space:nowrap}
|
|
173
|
+
.sharing-sidebar-card .info-grid .value{color:var(--text-sec);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
174
|
+
.sharing-sidebar-card .api-badge{display:inline-block;font-size:10px;font-weight:600;padding:2px 8px;border-radius:9999px;background:rgba(142,142,147,.12);color:var(--text-muted);letter-spacing:.02em}
|
|
175
|
+
[data-theme="light"] .sharing-sidebar-card .role-badge.admin{background:rgba(5,150,105,.1);color:#059669}
|
|
176
|
+
[data-theme="light"] .sharing-sidebar-card .role-badge.client{background:rgba(124,58,237,.1);color:#7c3aed}
|
|
177
|
+
.result-section{margin-bottom:18px;border:1px solid var(--border);border-radius:14px;background:var(--bg-card);overflow:hidden}
|
|
178
|
+
.result-section-header{display:flex;justify-content:space-between;align-items:center;padding:12px 14px;border-bottom:1px solid var(--border);background:rgba(255,255,255,.02)}
|
|
179
|
+
.result-section-title{font-size:14px;font-weight:700;color:var(--text)}
|
|
180
|
+
.result-section-sub{font-size:12px;color:var(--text-muted)}
|
|
181
|
+
.search-hit-list{padding:12px;display:flex;flex-direction:column;gap:10px}
|
|
182
|
+
.search-hit-card,.hub-hit-card,.hub-skill-card{border:1px solid var(--border);border-radius:12px;background:var(--bg);padding:12px;box-shadow:var(--shadow-sm)}
|
|
183
|
+
.search-hit-card .summary,.hub-hit-card .summary,.hub-skill-card .summary{font-size:14px;font-weight:600;color:var(--text);margin-bottom:6px}
|
|
184
|
+
.search-hit-card .excerpt,.hub-hit-card .excerpt,.hub-skill-card .excerpt{font-size:12px;color:var(--text-sec);line-height:1.55;white-space:pre-wrap}
|
|
185
|
+
.search-hit-meta,.hub-hit-meta,.hub-skill-meta{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px;font-size:11px;color:var(--text-muted)}
|
|
186
|
+
.meta-chip{display:inline-flex;align-items:center;gap:5px;padding:4px 8px;border:1px solid var(--border);border-radius:999px;background:var(--bg-card)}
|
|
187
|
+
.hub-hit-actions,.hub-skill-actions,.task-share-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
|
|
188
|
+
.sharing-settings-grid{display:grid;grid-template-columns:1.1fr .9fr;gap:18px}
|
|
189
|
+
.sharing-panel{border:1px solid var(--border);border-radius:14px;background:var(--bg-card);padding:14px;box-shadow:var(--shadow-sm)}
|
|
190
|
+
.sharing-panel h4{font-size:14px;font-weight:700;color:var(--text);margin:0 0 10px 0}
|
|
191
|
+
.sharing-panel .line{font-size:13px;color:var(--text-sec);margin-bottom:8px;line-height:1.55}
|
|
192
|
+
.sharing-panel .line strong{color:var(--text)}
|
|
193
|
+
.pending-user-list{display:flex;flex-direction:column;gap:10px}
|
|
194
|
+
.pending-user-card{border:1px solid var(--border);border-radius:12px;padding:12px;background:var(--bg)}
|
|
195
|
+
.pending-user-name{font-size:14px;font-weight:700;color:var(--text)}
|
|
196
|
+
.pending-user-meta{font-size:12px;color:var(--text-sec);margin-top:4px}
|
|
197
|
+
.pending-user-actions{display:flex;gap:8px;margin-top:10px}
|
|
198
|
+
/* ─── Admin Panel ─── */
|
|
199
|
+
.admin-header{position:relative;padding:28px 28px 20px;background:linear-gradient(135deg,rgba(99,102,241,.08) 0%,rgba(139,92,246,.06) 50%,rgba(6,182,212,.05) 100%);border:1px solid rgba(99,102,241,.12);border-radius:16px;margin-bottom:20px;overflow:hidden}
|
|
200
|
+
.admin-header::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--violet),var(--pri),var(--cyan));border-radius:16px 16px 0 0}
|
|
201
|
+
.admin-header-top{display:flex;justify-content:space-between;align-items:center}
|
|
202
|
+
.admin-header h2{font-size:20px;font-weight:800;color:var(--text);display:flex;align-items:center;gap:10px;margin:0}
|
|
203
|
+
.admin-header h2 .ah-icon{width:36px;height:36px;border-radius:10px;background:linear-gradient(135deg,var(--pri),var(--violet));display:flex;align-items:center;justify-content:center;font-size:18px;box-shadow:0 4px 12px rgba(99,102,241,.25)}
|
|
204
|
+
.admin-header-sub{font-size:12px;color:var(--text-muted);margin-top:6px}
|
|
205
|
+
.admin-stat-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:10px;margin-top:18px}
|
|
206
|
+
.admin-stat-box{position:relative;text-align:center;padding:16px 8px 14px;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;overflow:hidden;transition:all .2s}
|
|
207
|
+
.admin-stat-box:hover{border-color:rgba(99,102,241,.25);transform:translateY(-1px);box-shadow:0 4px 12px rgba(0,0,0,.06)}
|
|
208
|
+
.admin-stat-box::before{content:'';position:absolute;top:0;left:50%;transform:translateX(-50%);width:40px;height:2px;border-radius:0 0 4px 4px}
|
|
209
|
+
.admin-stat-box:nth-child(1)::before{background:var(--green)}
|
|
210
|
+
.admin-stat-box:nth-child(2)::before{background:var(--amber)}
|
|
211
|
+
.admin-stat-box:nth-child(3)::before{background:var(--violet)}
|
|
212
|
+
.admin-stat-box:nth-child(4)::before{background:var(--cyan)}
|
|
213
|
+
.admin-stat-box:nth-child(5)::before{background:var(--pri)}
|
|
214
|
+
.admin-stat-box:nth-child(6)::before{background:var(--rose)}
|
|
215
|
+
.admin-stat-box .as-icon{font-size:16px;margin-bottom:4px;display:block}
|
|
216
|
+
.admin-stat-box .val{font-size:22px;font-weight:800;color:var(--text);font-variant-numeric:tabular-nums}
|
|
217
|
+
.admin-stat-box .lbl{font-size:10px;color:var(--text-muted);margin-top:3px;letter-spacing:.03em}
|
|
218
|
+
.admin-tabs{display:flex;gap:4px;padding:4px;background:var(--bg);border:1px solid var(--border);border-radius:14px;overflow-x:auto;-webkit-overflow-scrolling:touch;margin-bottom:20px}
|
|
219
|
+
.admin-tab{position:relative;display:flex;align-items:center;gap:6px;padding:9px 16px;border:none;background:transparent;color:var(--text-muted);font-size:13px;font-weight:500;cursor:pointer;border-radius:10px;transition:all .2s;white-space:nowrap;font-family:inherit}
|
|
220
|
+
.admin-tab:hover{background:rgba(99,102,241,.06);color:var(--text)}
|
|
221
|
+
.admin-tab.active{background:var(--bg-card);color:var(--text);font-weight:600;box-shadow:0 2px 8px rgba(0,0,0,.06),0 0 0 1px rgba(99,102,241,.1)}
|
|
222
|
+
.admin-tab .at-icon{font-size:14px;line-height:1}
|
|
223
|
+
.admin-tab .at-count{font-size:10px;font-weight:700;padding:1px 6px;border-radius:8px;background:rgba(99,102,241,.1);color:var(--pri);min-width:18px;text-align:center}
|
|
224
|
+
.admin-tab.active .at-count{background:rgba(99,102,241,.15)}
|
|
225
|
+
.admin-panel{display:none}
|
|
226
|
+
.admin-panel.active{display:block}
|
|
227
|
+
.admin-card{border:1px solid var(--border);border-radius:12px;padding:14px 16px;background:var(--bg-card);margin-bottom:10px;transition:all .2s;position:relative;overflow:hidden}
|
|
228
|
+
.admin-card::before{content:'';position:absolute;top:0;left:0;bottom:0;width:3px;border-radius:3px 0 0 3px;background:var(--pri);opacity:.5}
|
|
229
|
+
.admin-card:hover{border-color:rgba(99,102,241,.25);box-shadow:0 2px 12px rgba(0,0,0,.04);transform:translateY(-1px)}
|
|
230
|
+
.admin-card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}
|
|
231
|
+
.admin-card-title{font-size:14px;font-weight:700;color:var(--text)}
|
|
232
|
+
.admin-card-meta{font-size:12px;color:var(--text-muted);line-height:1.5}
|
|
233
|
+
.admin-card-actions{display:flex;gap:8px;margin-top:10px}
|
|
234
|
+
.admin-badge{display:inline-flex;align-items:center;gap:4px;font-size:10px;font-weight:700;padding:3px 10px;border-radius:999px;letter-spacing:.02em}
|
|
235
|
+
.admin-badge.admin{background:rgba(52,199,89,.15);color:#34c759}
|
|
236
|
+
.admin-badge.member{background:rgba(142,142,147,.12);color:var(--text-muted)}
|
|
237
|
+
.admin-badge.pending{background:rgba(255,159,10,.15);color:#ff9f0a}
|
|
238
|
+
.admin-badge.public{background:rgba(99,102,241,.1);color:var(--pri)}
|
|
239
|
+
.admin-badge.group{background:rgba(139,92,246,.1);color:var(--violet)}
|
|
240
|
+
.admin-empty{font-size:13px;color:var(--text-muted);padding:40px 20px;text-align:center;border:1px dashed var(--border);border-radius:12px;background:var(--bg)}
|
|
241
|
+
.admin-empty .ae-icon{font-size:28px;display:block;margin-bottom:8px;opacity:.5}
|
|
242
|
+
[data-theme="light"] .admin-badge.admin{background:rgba(5,150,105,.1);color:#059669}
|
|
243
|
+
[data-theme="light"] .admin-badge.member{background:rgba(0,0,0,.06);color:#6b7280}
|
|
244
|
+
[data-theme="light"] .admin-badge.pending{background:rgba(245,158,11,.1);color:#d97706}
|
|
245
|
+
[data-theme="light"] .admin-header{background:linear-gradient(135deg,rgba(99,102,241,.05) 0%,rgba(139,92,246,.04) 50%,rgba(6,182,212,.03) 100%)}
|
|
246
|
+
.task-detail-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
247
|
+
.shared-memory-overlay,.shared-memory-overlay.show{display:none}
|
|
248
|
+
.shared-memory-overlay.show{display:flex;position:fixed;inset:0;align-items:center;justify-content:center;background:rgba(0,0,0,.55);z-index:1200;padding:24px}
|
|
249
|
+
.shared-memory-panel{width:min(860px,95vw);max-height:85vh;overflow:auto;border:1px solid var(--border);border-radius:18px;background:var(--bg-card);box-shadow:var(--shadow-lg);padding:20px}
|
|
250
|
+
.shared-memory-panel h3{font-size:18px;color:var(--text);margin-bottom:10px}
|
|
251
|
+
.shared-memory-panel .content{font-size:13px;color:var(--text-sec);line-height:1.7;white-space:pre-wrap;background:var(--bg);border:1px solid var(--border);border-radius:12px;padding:14px;margin-top:12px}
|
|
252
|
+
.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)}
|
|
253
|
+
@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}}
|
|
151
254
|
|
|
152
255
|
.filter-bar{display:flex;gap:8px;margin-bottom:16px;flex-wrap:wrap}
|
|
153
256
|
.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}
|
|
@@ -194,20 +297,67 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
194
297
|
.memory-card.dedup-inactive{opacity:.55;border-style:dashed}
|
|
195
298
|
.memory-card.dedup-inactive:hover{opacity:.85}
|
|
196
299
|
.dedup-target-link{font-size:11px;color:var(--pri);cursor:pointer;text-decoration:underline;margin-left:4px}
|
|
197
|
-
.memory-modal-overlay{position:fixed;inset:0;background:rgba(
|
|
300
|
+
.memory-modal-overlay{position:fixed;inset:0;background:rgba(5,8,16,.75);z-index:9999;display:none;align-items:center;justify-content:center;backdrop-filter:blur(12px) saturate(1.2)}
|
|
198
301
|
.memory-modal-overlay.show{display:flex}
|
|
199
|
-
.memory-modal{background:
|
|
200
|
-
|
|
201
|
-
.memory-modal
|
|
202
|
-
|
|
203
|
-
.
|
|
204
|
-
|
|
205
|
-
.modal
|
|
206
|
-
.modal
|
|
207
|
-
.modal-
|
|
208
|
-
.modal-
|
|
209
|
-
|
|
210
|
-
.modal-
|
|
302
|
+
[data-theme="light"] .memory-modal-overlay{background:rgba(0,0,0,.45)}
|
|
303
|
+
.memory-modal{position:relative;background:linear-gradient(160deg,#0d1117 0%,#161b22 50%,#0d1117 100%);border:1px solid rgba(99,102,241,.2);border-radius:20px;width:min(600px,92vw);max-height:82vh;display:flex;flex-direction:column;box-shadow:0 32px 100px rgba(0,0,0,.6),0 0 60px rgba(99,102,241,.08),inset 0 1px 0 rgba(255,255,255,.06);animation:mmSlideIn .35s cubic-bezier(.22,1,.36,1);overflow:hidden}
|
|
304
|
+
[data-theme="light"] .memory-modal{background:linear-gradient(160deg,#ffffff 0%,#f8f9fc 50%,#ffffff 100%);border-color:rgba(99,102,241,.15);box-shadow:0 32px 100px rgba(0,0,0,.12),0 0 40px rgba(99,102,241,.06)}
|
|
305
|
+
@keyframes mmSlideIn{from{opacity:0;transform:scale(.92) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}
|
|
306
|
+
.memory-modal::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,#6366f1,#8b5cf6,#ec4899,#f43f5e,#6366f1);background-size:200% 100%;animation:mmGradientMove 3s linear infinite;border-radius:20px 20px 0 0;z-index:1}
|
|
307
|
+
@keyframes mmGradientMove{0%{background-position:0% 50%}100%{background-position:200% 50%}}
|
|
308
|
+
.memory-modal::after{content:'';position:absolute;inset:0;background:radial-gradient(ellipse at 20% 0%,rgba(99,102,241,.06) 0%,transparent 60%),radial-gradient(ellipse at 80% 100%,rgba(236,72,153,.04) 0%,transparent 60%);pointer-events:none;border-radius:20px}
|
|
309
|
+
[data-theme="light"] .memory-modal::after{background:radial-gradient(ellipse at 20% 0%,rgba(99,102,241,.04) 0%,transparent 60%),radial-gradient(ellipse at 80% 100%,rgba(236,72,153,.03) 0%,transparent 60%)}
|
|
310
|
+
.memory-modal-title{position:relative;z-index:2;display:flex;align-items:center;justify-content:space-between;padding:18px 24px 14px;font-size:12px;font-weight:700;color:var(--text-sec);letter-spacing:.06em;text-transform:uppercase}
|
|
311
|
+
.memory-modal-title .mm-tl{display:flex;align-items:center;gap:8px}
|
|
312
|
+
.memory-modal-title .mm-tl-icon{width:28px;height:28px;border-radius:8px;background:linear-gradient(135deg,rgba(99,102,241,.15),rgba(139,92,246,.1));display:flex;align-items:center;justify-content:center;font-size:14px;border:1px solid rgba(99,102,241,.2)}
|
|
313
|
+
[data-theme="light"] .memory-modal-title .mm-tl-icon{background:linear-gradient(135deg,rgba(99,102,241,.1),rgba(139,92,246,.06));border-color:rgba(99,102,241,.12)}
|
|
314
|
+
.memory-modal-title .mm-close{width:30px;height:30px;display:flex;align-items:center;justify-content:center;border-radius:10px;border:1px solid rgba(255,255,255,.06);background:rgba(255,255,255,.04);color:var(--text-sec);cursor:pointer;font-size:16px;transition:all .2s}
|
|
315
|
+
.memory-modal-title .mm-close:hover{background:rgba(255,77,77,.12);border-color:rgba(255,77,77,.2);color:#f87171;transform:rotate(90deg)}
|
|
316
|
+
[data-theme="light"] .memory-modal-title .mm-close{border-color:rgba(0,0,0,.06);background:rgba(0,0,0,.03)}
|
|
317
|
+
[data-theme="light"] .memory-modal-title .mm-close:hover{background:rgba(255,77,77,.08);border-color:rgba(255,77,77,.15)}
|
|
318
|
+
.memory-modal-body{position:relative;z-index:2;padding:0;overflow-y:auto;flex:1}
|
|
319
|
+
.mm-hero{padding:0 24px 20px}
|
|
320
|
+
.mm-hero-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:14px}
|
|
321
|
+
.mm-role-chip{display:inline-flex;align-items:center;gap:5px;font-size:10px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;padding:4px 12px;border-radius:20px;background:linear-gradient(135deg,rgba(99,102,241,.15),rgba(139,92,246,.1));color:#a5b4fc;border:1px solid rgba(99,102,241,.2)}
|
|
322
|
+
.mm-role-chip.user{background:linear-gradient(135deg,rgba(16,185,129,.15),rgba(52,211,153,.1));color:#6ee7b7;border-color:rgba(16,185,129,.2)}
|
|
323
|
+
.mm-role-chip.assistant{background:linear-gradient(135deg,rgba(99,102,241,.15),rgba(139,92,246,.1));color:#a5b4fc;border-color:rgba(99,102,241,.2)}
|
|
324
|
+
.mm-role-chip.system{background:linear-gradient(135deg,rgba(251,191,36,.15),rgba(245,158,11,.1));color:#fcd34d;border-color:rgba(251,191,36,.2)}
|
|
325
|
+
[data-theme="light"] .mm-role-chip{color:#6366f1}
|
|
326
|
+
[data-theme="light"] .mm-role-chip.user{color:#059669}
|
|
327
|
+
[data-theme="light"] .mm-role-chip.assistant{color:#6366f1}
|
|
328
|
+
[data-theme="light"] .mm-role-chip.system{color:#d97706}
|
|
329
|
+
.mm-dedup-chip{display:inline-flex;align-items:center;gap:4px;font-size:10px;font-weight:600;padding:3px 10px;border-radius:20px;text-transform:uppercase;letter-spacing:.04em}
|
|
330
|
+
.mm-dedup-chip.duplicate{background:rgba(239,68,68,.12);color:#fca5a5;border:1px solid rgba(239,68,68,.2)}
|
|
331
|
+
.mm-dedup-chip.merged{background:rgba(251,191,36,.12);color:#fcd34d;border:1px solid rgba(251,191,36,.2)}
|
|
332
|
+
[data-theme="light"] .mm-dedup-chip.duplicate{color:#dc2626}
|
|
333
|
+
[data-theme="light"] .mm-dedup-chip.merged{color:#d97706}
|
|
334
|
+
.mm-id{font-family:'SF Mono',Consolas,'Courier New',monospace;font-size:10px;color:var(--text-muted);background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.06);padding:3px 10px;border-radius:20px;cursor:pointer;transition:all .2s;display:inline-flex;align-items:center;gap:4px}
|
|
335
|
+
.mm-id::before{content:'\u{1F517}';font-size:9px}
|
|
336
|
+
.mm-id:hover{background:rgba(99,102,241,.12);border-color:rgba(99,102,241,.25);color:var(--text)}
|
|
337
|
+
[data-theme="light"] .mm-id{background:rgba(0,0,0,.03);border-color:rgba(0,0,0,.06)}
|
|
338
|
+
[data-theme="light"] .mm-id:hover{background:rgba(99,102,241,.08);border-color:rgba(99,102,241,.15)}
|
|
339
|
+
.mm-summary{font-size:15px;font-weight:600;color:var(--text);line-height:1.6;letter-spacing:-.01em;padding:16px 20px;background:rgba(255,255,255,.02);border-radius:12px;border:1px solid rgba(255,255,255,.04);position:relative}
|
|
340
|
+
.mm-summary::before{content:'';position:absolute;left:0;top:12px;bottom:12px;width:3px;border-radius:2px;background:linear-gradient(180deg,#6366f1,#8b5cf6)}
|
|
341
|
+
[data-theme="light"] .mm-summary{background:rgba(99,102,241,.02);border-color:rgba(99,102,241,.06)}
|
|
342
|
+
.mm-section{padding:0 24px 16px}
|
|
343
|
+
.mm-section-label{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--text-muted);margin-bottom:8px;display:flex;align-items:center;gap:6px}
|
|
344
|
+
.mm-section-label::before{content:'';width:6px;height:6px;border-radius:50%;background:linear-gradient(135deg,#6366f1,#8b5cf6);flex-shrink:0}
|
|
345
|
+
.mm-content{font-family:'SF Mono',Consolas,'Courier New',monospace;font-size:12px;line-height:1.75;color:var(--text);white-space:pre-wrap;word-break:break-all;background:rgba(0,0,0,.3);border-radius:12px;padding:16px 18px;max-height:240px;overflow-y:auto;margin:0;border:1px solid rgba(255,255,255,.04);position:relative}
|
|
346
|
+
[data-theme="light"] .mm-content{background:rgba(0,0,0,.03);border-color:rgba(0,0,0,.06)}
|
|
347
|
+
.mm-meta{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:8px;padding:16px 24px;border-top:1px solid rgba(255,255,255,.04);background:rgba(0,0,0,.1)}
|
|
348
|
+
[data-theme="light"] .mm-meta{border-top-color:rgba(0,0,0,.04);background:rgba(0,0,0,.015)}
|
|
349
|
+
.mm-meta-chip{display:flex;flex-direction:column;gap:3px;font-size:11px;color:var(--text-sec);background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.04);padding:10px 12px;border-radius:10px;transition:all .2s}
|
|
350
|
+
.mm-meta-chip:hover{background:rgba(255,255,255,.06);border-color:rgba(99,102,241,.15)}
|
|
351
|
+
[data-theme="light"] .mm-meta-chip{background:rgba(0,0,0,.02);border-color:rgba(0,0,0,.04)}
|
|
352
|
+
[data-theme="light"] .mm-meta-chip:hover{background:rgba(99,102,241,.04);border-color:rgba(99,102,241,.1)}
|
|
353
|
+
.mm-meta-chip strong{color:var(--text-muted);font-weight:600;font-size:9px;text-transform:uppercase;letter-spacing:.06em}
|
|
354
|
+
.mm-meta-chip span{color:var(--text);font-weight:500;font-size:12px;word-break:break-all}
|
|
355
|
+
.mm-dedup{padding:0 24px 16px}
|
|
356
|
+
.mm-dedup-box{background:linear-gradient(135deg,rgba(251,191,36,.06),rgba(245,158,11,.03));border:1px solid rgba(251,191,36,.12);border-radius:12px;padding:14px 16px;font-size:12px;color:var(--text-sec);line-height:1.6;display:flex;align-items:flex-start;gap:8px}
|
|
357
|
+
.mm-dedup-box::before{content:'\u26A0\uFE0F';flex-shrink:0;font-size:14px}
|
|
358
|
+
.mm-footer{padding:12px 24px 16px;display:flex;align-items:center;justify-content:center}
|
|
359
|
+
.mm-footer .dedup-target-link{font-size:11px;color:#818cf8;cursor:pointer;padding:6px 16px;border-radius:8px;border:1px solid rgba(99,102,241,.2);background:rgba(99,102,241,.06);transition:all .2s;font-weight:500}
|
|
360
|
+
.mm-footer .dedup-target-link:hover{background:rgba(99,102,241,.12);border-color:rgba(99,102,241,.3)}
|
|
211
361
|
[data-theme="light"] .merge-history{background:rgba(0,0,0,.04)}
|
|
212
362
|
[data-theme="light"] .merge-history-item{border-bottom-color:rgba(0,0,0,.06)}
|
|
213
363
|
.card-merged-info{margin-top:8px;padding:8px 12px;background:rgba(16,185,129,.06);border:1px dashed rgba(16,185,129,.2);border-radius:8px;font-size:12px;line-height:1.6;color:var(--text-sec)}
|
|
@@ -245,6 +395,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
245
395
|
.form-group textarea{min-height:100px;resize:vertical}
|
|
246
396
|
.modal-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:28px}
|
|
247
397
|
|
|
398
|
+
|
|
248
399
|
/* ─── Toast ─── */
|
|
249
400
|
.emb-banner{display:flex;align-items:center;gap:10px;padding:12px 20px;font-size:13px;font-weight:500;border-radius:10px;margin:0 32px 0;animation:slideIn .3s ease}
|
|
250
401
|
.emb-banner.warning{background:rgba(245,158,11,.1);color:#d97706;border:1px solid rgba(245,158,11,.25)}
|
|
@@ -438,9 +589,9 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
438
589
|
.nav-tabs .tab.active{color:var(--text);background:rgba(255,255,255,.1);border-color:var(--border);box-shadow:0 1px 4px rgba(0,0,0,.15)}
|
|
439
590
|
[data-theme="light"] .nav-tabs{background:rgba(0,0,0,.05)}
|
|
440
591
|
[data-theme="light"] .nav-tabs .tab.active{background:#fff;border-color:rgba(0,0,0,.1);box-shadow:0 1px 3px rgba(0,0,0,.08);color:var(--text)}
|
|
441
|
-
.analytics-view,.settings-view,.logs-view,.migrate-view{display:none;flex:1;min-width:0;flex-direction:column;gap:20px}
|
|
442
|
-
.analytics-view.show,.settings-view.show,.logs-view.show,.migrate-view.show{display:flex}
|
|
443
|
-
.feed-wrap,.tasks-view,.skills-view,.analytics-view,.settings-view,.logs-view,.migrate-view{max-width:960px}
|
|
592
|
+
.analytics-view,.settings-view,.logs-view,.migrate-view,.admin-view{display:none;flex:1;min-width:0;flex-direction:column;gap:20px}
|
|
593
|
+
.analytics-view.show,.settings-view.show,.logs-view.show,.migrate-view.show,.admin-view.show{display:flex}
|
|
594
|
+
.feed-wrap,.tasks-view,.skills-view,.analytics-view,.settings-view,.logs-view,.migrate-view,.admin-view{max-width:960px}
|
|
444
595
|
|
|
445
596
|
/* ─── Logs ─── */
|
|
446
597
|
.logs-toolbar{display:flex;align-items:center;justify-content:space-between;padding:8px 0}
|
|
@@ -558,6 +709,70 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
558
709
|
.settings-section{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-lg);padding:24px 28px}
|
|
559
710
|
.settings-section h3{font-size:13px;font-weight:700;color:var(--text);margin-bottom:16px;display:flex;align-items:center;gap:8px}
|
|
560
711
|
.settings-section h3 .icon{font-size:16px;opacity:.8}
|
|
712
|
+
.settings-tabs-bar{display:flex;gap:4px;margin-bottom:24px;padding:4px;background:var(--bg);border:1px solid var(--border);border-radius:14px;overflow-x:auto;-webkit-overflow-scrolling:touch}
|
|
713
|
+
.settings-tab-btn{position:relative;display:flex;align-items:center;gap:7px;padding:10px 16px;border:none;background:transparent;color:var(--text-muted);font-size:13px;font-weight:500;cursor:pointer;border-radius:10px;transition:all .25s ease;white-space:nowrap;font-family:inherit}
|
|
714
|
+
.settings-tab-btn:hover{background:rgba(99,102,241,.06);color:var(--text)}
|
|
715
|
+
.settings-tab-btn.active{background:var(--bg-card);color:var(--text);font-weight:600;box-shadow:0 2px 8px rgba(0,0,0,.06),0 0 0 1px rgba(99,102,241,.1)}
|
|
716
|
+
.settings-tab-btn .stab-icon{font-size:15px;line-height:1}
|
|
717
|
+
.settings-tab-btn .stab-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}
|
|
718
|
+
.settings-tab-btn[data-tab="embedding"] .stab-dot{background:#6366f1}
|
|
719
|
+
.settings-tab-btn[data-tab="summarizer"] .stab-dot{background:#8b5cf6}
|
|
720
|
+
.settings-tab-btn[data-tab="skill"] .stab-dot{background:#f59e0b}
|
|
721
|
+
.settings-tab-btn[data-tab="hub"] .stab-dot{background:#06b6d4}
|
|
722
|
+
.settings-tab-btn[data-tab="general"] .stab-dot{background:#10b981}
|
|
723
|
+
.settings-tab-btn.active .stab-dot{box-shadow:0 0 6px currentColor}
|
|
724
|
+
[data-theme="light"] .settings-tab-btn.active{box-shadow:0 2px 8px rgba(0,0,0,.05),0 0 0 1px rgba(99,102,241,.08)}
|
|
725
|
+
.settings-cards-grid{display:flex;flex-direction:column;gap:24px}
|
|
726
|
+
.settings-card[data-stab]{display:none}
|
|
727
|
+
.settings-card[data-stab].stab-active{display:block}
|
|
728
|
+
.settings-card{position:relative;background:var(--bg-card);border:1px solid var(--border);border-radius:16px;overflow:hidden;transition:border-color .3s,box-shadow .3s}
|
|
729
|
+
.settings-card::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;opacity:.5;transition:opacity .3s}
|
|
730
|
+
.settings-card:hover{border-color:rgba(99,102,241,.25);box-shadow:0 0 24px rgba(99,102,241,.06),0 8px 32px rgba(0,0,0,.1)}
|
|
731
|
+
.settings-card:hover::before{opacity:1}
|
|
732
|
+
.settings-card.card-embedding::before{background:linear-gradient(90deg,#6366f1,#818cf8,#a5b4fc)}
|
|
733
|
+
.settings-card.card-summarizer::before{background:linear-gradient(90deg,#8b5cf6,#a78bfa,#c4b5fd)}
|
|
734
|
+
.settings-card.card-skill::before{background:linear-gradient(90deg,#f59e0b,#fbbf24,#fcd34d)}
|
|
735
|
+
.settings-card.card-hub::before{background:linear-gradient(90deg,#06b6d4,#22d3ee,#67e8f9)}
|
|
736
|
+
.settings-card.card-general::before{background:linear-gradient(90deg,#10b981,#34d399,#6ee7b7)}
|
|
737
|
+
.settings-card-header{display:flex;align-items:center;gap:14px;padding:22px 28px 0}
|
|
738
|
+
.settings-card-icon{width:44px;height:44px;display:flex;align-items:center;justify-content:center;border-radius:12px;font-size:22px;flex-shrink:0;border:1px solid transparent}
|
|
739
|
+
.settings-card-title-wrap{flex:1;min-width:0}
|
|
740
|
+
.settings-card-title{font-size:16px;font-weight:700;color:var(--text);letter-spacing:.01em}
|
|
741
|
+
.settings-card-desc{font-size:11px;color:var(--text-muted);margin-top:3px;font-weight:400;line-height:1.4}
|
|
742
|
+
.settings-card-body{padding:18px 28px 24px}
|
|
743
|
+
.settings-card-divider{height:1px;background:var(--border);margin:18px 0;opacity:.6}
|
|
744
|
+
.settings-card-subtitle{font-size:12px;font-weight:700;color:var(--text-sec);margin-bottom:10px;letter-spacing:.01em}
|
|
745
|
+
.hub-info-card{border:1px solid var(--border);border-radius:12px;padding:14px 16px;background:var(--bg);position:relative;overflow:hidden;margin-bottom:12px}
|
|
746
|
+
.hub-info-card::before{content:'';position:absolute;top:0;left:0;bottom:0;width:3px;border-radius:3px 0 0 3px}
|
|
747
|
+
.hub-info-card.hic-share{border-color:rgba(99,102,241,.2);background:linear-gradient(135deg,rgba(99,102,241,.04),rgba(139,92,246,.03))}
|
|
748
|
+
.hub-info-card.hic-share::before{background:linear-gradient(180deg,var(--pri),var(--violet))}
|
|
749
|
+
.hub-info-card.hic-status::before{background:var(--green)}
|
|
750
|
+
.hub-info-card.hic-team::before{background:var(--violet)}
|
|
751
|
+
.hub-info-card.hic-pending::before{background:var(--amber)}
|
|
752
|
+
.hub-info-card .hic-title{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:700;color:var(--text);margin-bottom:10px}
|
|
753
|
+
.hub-info-card .hic-title .hic-icon{font-size:14px}
|
|
754
|
+
.hub-info-card .hic-grid{display:grid;grid-template-columns:auto 1fr;gap:4px 12px;font-size:12px;align-items:baseline}
|
|
755
|
+
.hub-info-card .hic-grid .hic-label{color:var(--text-muted);white-space:nowrap}
|
|
756
|
+
.hub-info-card .hic-grid .hic-value{color:var(--text);font-weight:500;word-break:break-all}
|
|
757
|
+
.hub-info-card .hic-grid .hic-value.mono{font-family:monospace;font-size:11px;cursor:pointer;user-select:all}
|
|
758
|
+
.hub-info-card .hic-grid .hic-value.mono:hover{color:var(--pri)}
|
|
759
|
+
.hub-info-card .hic-badge{display:inline-flex;align-items:center;gap:4px;font-size:10px;font-weight:600;padding:2px 8px;border-radius:9999px}
|
|
760
|
+
.hub-info-card .hic-badge.connected{background:rgba(52,211,153,.12);color:#34d399}
|
|
761
|
+
.hub-info-card .hic-badge.disconnected{background:rgba(239,68,68,.1);color:#ef4444}
|
|
762
|
+
.hub-info-card .hic-badge.admin{background:rgba(52,199,89,.12);color:#34c759}
|
|
763
|
+
.hub-info-card .hic-badge.pending{background:rgba(251,191,36,.12);color:#fbbf24}
|
|
764
|
+
.hub-info-card .hic-dot{width:6px;height:6px;border-radius:50%;display:inline-block}
|
|
765
|
+
.hub-info-card .hic-dot.green{background:#34d399;box-shadow:0 0 6px #34d399}
|
|
766
|
+
.hub-info-card .hic-dot.red{background:#ef4444}
|
|
767
|
+
.hub-info-card .hic-dot.amber{background:#fbbf24;box-shadow:0 0 4px #fbbf24}
|
|
768
|
+
.hub-info-card .hic-empty{font-size:12px;color:var(--text-muted);text-align:center;padding:12px 0}
|
|
769
|
+
.hub-info-card .hic-actions{display:flex;gap:8px;margin-top:10px}
|
|
770
|
+
[data-theme="light"] .hub-info-card.hic-share{background:linear-gradient(135deg,rgba(99,102,241,.03),rgba(139,92,246,.02))}
|
|
771
|
+
.settings-card .settings-section{background:none;border:none;padding:0;border-radius:0;margin-bottom:16px}
|
|
772
|
+
.settings-card .settings-section:last-child{margin-bottom:0}
|
|
773
|
+
.settings-card .settings-section h3{margin-bottom:12px}
|
|
774
|
+
[data-theme="light"] .settings-card{box-shadow:0 1px 3px rgba(0,0,0,.04)}
|
|
775
|
+
[data-theme="light"] .settings-card:hover{box-shadow:0 0 24px rgba(79,70,229,.05),0 8px 32px rgba(0,0,0,.06)}
|
|
561
776
|
.settings-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
|
|
562
777
|
@media(max-width:800px){.settings-grid{grid-template-columns:1fr}}
|
|
563
778
|
.settings-field{display:flex;flex-direction:column;gap:4px}
|
|
@@ -565,7 +780,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
565
780
|
.settings-field input,.settings-field select{background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:8px 12px;color:var(--text);font-size:13px;font-family:inherit;transition:border-color .15s}
|
|
566
781
|
.settings-field input:focus,.settings-field select:focus{outline:none;border-color:var(--pri)}
|
|
567
782
|
.settings-field input[type="password"]{font-family:'Courier New',monospace;letter-spacing:.05em}
|
|
568
|
-
.
|
|
783
|
+
.field-hint{font-size:10px;color:var(--text-muted);line-height:1.5}
|
|
784
|
+
.settings-field .field-hint{margin-top:2px}
|
|
569
785
|
.settings-field.full-width{grid-column:1/-1}
|
|
570
786
|
.settings-toggle{display:flex;align-items:center;gap:10px;padding:4px 0}
|
|
571
787
|
.settings-toggle label{font-size:12px;font-weight:500;color:var(--text-sec);text-transform:none;letter-spacing:0}
|
|
@@ -581,8 +797,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
581
797
|
.test-result.ok{color:#22c55e}
|
|
582
798
|
.test-result.fail{color:var(--rose)}
|
|
583
799
|
.test-result.loading{color:var(--text-muted)}
|
|
584
|
-
.settings-actions{display:flex;gap:12px;justify-content:flex-end;align-items:center;margin-top:
|
|
585
|
-
.settings-actions .btn{min-width:
|
|
800
|
+
.settings-actions{display:flex;gap:12px;justify-content:flex-end;align-items:center;margin-top:20px;padding-top:16px;border-top:1px solid var(--border);flex-wrap:nowrap}
|
|
801
|
+
.settings-actions .btn{flex:0 0 auto;min-width:0;padding:8px 24px;font-size:13px}
|
|
586
802
|
.settings-actions .btn-primary{background:rgba(99,102,241,.08);color:var(--pri);border:1px solid rgba(99,102,241,.25);font-weight:600}
|
|
587
803
|
.settings-actions .btn-primary:hover{background:rgba(99,102,241,.14);border-color:var(--pri)}
|
|
588
804
|
[data-theme="light"] .settings-actions .btn-primary{background:rgba(79,70,229,.06);color:#4f46e5;border:1px solid rgba(79,70,229,.2)}
|
|
@@ -648,18 +864,20 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
648
864
|
.analytics-section::before{display:none}
|
|
649
865
|
.analytics-section h3{font-size:11px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:16px;display:flex;align-items:center;gap:8px}
|
|
650
866
|
.analytics-section h3 .icon{font-size:14px;opacity:.6}
|
|
651
|
-
.chart-bars{display:flex;align-items:flex-end;gap:
|
|
652
|
-
.chart-
|
|
653
|
-
.chart-bar-
|
|
654
|
-
.chart-bar-
|
|
867
|
+
.chart-bars{display:flex;align-items:flex-end;gap:1px;padding:12px 4px 4px;min-height:200px;position:relative;width:100%}
|
|
868
|
+
.chart-bars::before{content:'';position:absolute;left:0;right:0;bottom:24px;height:1px;background:var(--border);opacity:.4}
|
|
869
|
+
.chart-bar-wrap{flex:1 1 0;min-width:0;max-width:32px;display:flex;flex-direction:column;align-items:center;gap:4px;position:relative;cursor:pointer}
|
|
870
|
+
.chart-bar-col{width:100%;height:160px;display:flex;flex-direction:column;justify-content:flex-end;align-items:center}
|
|
871
|
+
.chart-bar-wrap:hover .chart-bar{opacity:1;filter:brightness(1.2);transform:scaleY(1.02);transform-origin:bottom}
|
|
655
872
|
.chart-bar-wrap:hover .chart-bar-label{color:var(--text)}
|
|
656
873
|
.chart-bar-wrap:hover .chart-tip{opacity:1;transform:translateX(-50%) translateY(0)}
|
|
657
|
-
.chart-tip{position:absolute;top:-
|
|
658
|
-
.chart-
|
|
659
|
-
.chart-bar
|
|
660
|
-
.chart-bar.
|
|
661
|
-
.chart-bar.
|
|
662
|
-
.chart-bar
|
|
874
|
+
.chart-tip{position:absolute;top:-8px;left:50%;transform:translateX(-50%) translateY(6px);background:rgba(15,18,25,.95);border:1px solid rgba(99,102,241,.3);color:#e8eaed;padding:3px 10px;border-radius:8px;font-size:10px;font-weight:600;white-space:nowrap;z-index:5;pointer-events:none;box-shadow:0 4px 16px rgba(0,0,0,.3);opacity:0;transition:all .2s cubic-bezier(.22,1,.36,1)}
|
|
875
|
+
[data-theme="light"] .chart-tip{background:rgba(17,24,39,.9)}
|
|
876
|
+
.chart-bar{width:100%;max-width:20px;min-width:4px;border-radius:3px 3px 1px 1px;background:linear-gradient(180deg,#818cf8 0%,#6366f1 100%);opacity:.85;transition:all .25s cubic-bezier(.22,1,.36,1);box-shadow:0 1px 4px rgba(99,102,241,.12)}
|
|
877
|
+
.chart-bar.violet{background:linear-gradient(180deg,#8b5cf6 0%,#6366f1 100%)}
|
|
878
|
+
.chart-bar.green{background:linear-gradient(180deg,#34d399 0%,#10b981 100%);box-shadow:0 1px 4px rgba(16,185,129,.12)}
|
|
879
|
+
.chart-bar.zero{width:100%;max-width:20px;min-width:4px;height:2px!important;background:var(--border);opacity:.25;border-radius:1px;box-shadow:none}
|
|
880
|
+
.chart-bar-label{font-size:8px;color:var(--text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:40px;text-align:center;transition:color .15s;letter-spacing:0}
|
|
663
881
|
.chart-legend{display:flex;gap:14px;margin-top:12px;flex-wrap:wrap;font-size:11px;color:var(--text-sec);font-weight:500}
|
|
664
882
|
.chart-legend span{display:inline-flex;align-items:center;gap:5px}
|
|
665
883
|
.chart-legend .dot{width:8px;height:8px;border-radius:2px}
|
|
@@ -713,7 +931,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
713
931
|
[data-theme="light"] .auth-theme-toggle .theme-icon-dark{display:none}
|
|
714
932
|
|
|
715
933
|
@media(max-width:1100px){.analytics-cards{grid-template-columns:repeat(3,1fr)}}
|
|
716
|
-
@media(max-width:900px){.main-content{flex-direction:column;padding:20px}.sidebar{width:100%}.sidebar .stats-grid{grid-template-columns:repeat(4,1fr)}.analytics-cards{grid-template-columns:repeat(2,1fr)}.topbar{padding:0 16px;gap:8px}.topbar .brand
|
|
934
|
+
@media(max-width:900px){.main-content{flex-direction:column;padding:20px}.sidebar{width:100%}.sidebar .stats-grid{grid-template-columns:repeat(4,1fr)}.analytics-cards{grid-template-columns:repeat(2,1fr)}.topbar{padding:0 16px;gap:8px}.topbar .brand .brand-title{display:none}.topbar .brand .brand-powered{display:none}.topbar-center{justify-content:flex-start}}
|
|
717
935
|
</style>
|
|
718
936
|
</head>
|
|
719
937
|
<body>
|
|
@@ -725,7 +943,9 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
725
943
|
<button class="auth-theme-toggle" onclick="toggleLang()" aria-label="Switch language"><span data-i18n="lang.switch">EN</span></button>
|
|
726
944
|
</div>
|
|
727
945
|
<div class="auth-card">
|
|
728
|
-
<div class="logo"
|
|
946
|
+
<div class="logo" style="display:flex;flex-direction:column;align-items:center;gap:10px">
|
|
947
|
+
<svg width="56" height="56" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="aLG" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#ff4d4d"/><stop offset="100%" stop-color="#991b1b"/></linearGradient></defs><path d="M60 10C30 10 15 35 15 55C15 75 30 95 45 100L45 110L55 110L55 100C55 100 60 102 65 100L65 110L75 110L75 100C90 95 105 75 105 55C105 35 90 10 60 10Z" fill="url(#aLG)"/><path d="M20 45C5 40 0 50 5 60C10 70 20 65 25 55C28 48 25 45 20 45Z" fill="url(#aLG)"/><path d="M100 45C115 40 120 50 115 60C110 70 100 65 95 55C92 48 95 45 100 45Z" fill="url(#aLG)"/><path d="M45 15Q35 5 30 8" stroke="#ff4d4d" stroke-width="2" stroke-linecap="round"/><path d="M75 15Q85 5 90 8" stroke="#ff4d4d" stroke-width="2" stroke-linecap="round"/><circle cx="45" cy="35" r="6" fill="#050810"/><circle cx="75" cy="35" r="6" fill="#050810"/><circle cx="46" cy="34" r="2" fill="#00e5cc"/><circle cx="76" cy="34" r="2" fill="#00e5cc"/></svg>
|
|
948
|
+
</div>
|
|
729
949
|
<h1 data-i18n="title">OpenClaw Memory</h1>
|
|
730
950
|
<p style="font-size:12px;color:var(--text-sec);margin-bottom:6px" data-i18n="subtitle">Powered by MemOS</p>
|
|
731
951
|
<p data-i18n="setup.desc">Set a password to protect your memories</p>
|
|
@@ -743,7 +963,9 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
743
963
|
<button class="auth-theme-toggle" onclick="toggleLang()" aria-label="Switch language"><span data-i18n="lang.switch">EN</span></button>
|
|
744
964
|
</div>
|
|
745
965
|
<div class="auth-card">
|
|
746
|
-
<div class="logo"
|
|
966
|
+
<div class="logo" style="display:flex;flex-direction:column;align-items:center;gap:10px">
|
|
967
|
+
<svg width="56" height="56" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="bLG" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#ff4d4d"/><stop offset="100%" stop-color="#991b1b"/></linearGradient></defs><path d="M60 10C30 10 15 35 15 55C15 75 30 95 45 100L45 110L55 110L55 100C55 100 60 102 65 100L65 110L75 110L75 100C90 95 105 75 105 55C105 35 90 10 60 10Z" fill="url(#bLG)"/><path d="M20 45C5 40 0 50 5 60C10 70 20 65 25 55C28 48 25 45 20 45Z" fill="url(#bLG)"/><path d="M100 45C115 40 120 50 115 60C110 70 100 65 95 55C92 48 95 45 100 45Z" fill="url(#bLG)"/><path d="M45 15Q35 5 30 8" stroke="#ff4d4d" stroke-width="2" stroke-linecap="round"/><path d="M75 15Q85 5 90 8" stroke="#ff4d4d" stroke-width="2" stroke-linecap="round"/><circle cx="45" cy="35" r="6" fill="#050810"/><circle cx="75" cy="35" r="6" fill="#050810"/><circle cx="46" cy="34" r="2" fill="#00e5cc"/><circle cx="76" cy="34" r="2" fill="#00e5cc"/></svg>
|
|
968
|
+
</div>
|
|
747
969
|
<h1 data-i18n="title">OpenClaw Memory</h1>
|
|
748
970
|
<p style="font-size:12px;color:var(--text-sec);margin-bottom:6px" data-i18n="subtitle">Powered by MemOS</p>
|
|
749
971
|
<p data-i18n="login.desc">Enter your password to access memories</p>
|
|
@@ -795,8 +1017,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
795
1017
|
<div class="app" id="app">
|
|
796
1018
|
<div class="topbar">
|
|
797
1019
|
<div class="brand">
|
|
798
|
-
<
|
|
799
|
-
<span data-i18n="title">
|
|
1020
|
+
<span class="memos-logo"><svg width="28" height="28" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="topLG" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#ff4d4d"/><stop offset="100%" stop-color="#991b1b"/></linearGradient></defs><path d="M60 10C30 10 15 35 15 55C15 75 30 95 45 100L45 110L55 110L55 100C55 100 60 102 65 100L65 110L75 110L75 100C90 95 105 75 105 55C105 35 90 10 60 10Z" fill="url(#topLG)"/><path d="M20 45C5 40 0 50 5 60C10 70 20 65 25 55C28 48 25 45 20 45Z" fill="url(#topLG)"/><path d="M100 45C115 40 120 50 115 60C110 70 100 65 95 55C92 48 95 45 100 45Z" fill="url(#topLG)"/><path d="M45 15Q35 5 30 8" stroke="#ff4d4d" stroke-width="2" stroke-linecap="round"/><path d="M75 15Q85 5 90 8" stroke="#ff4d4d" stroke-width="2" stroke-linecap="round"/><circle cx="45" cy="35" r="6" fill="#050810"/><circle cx="75" cy="35" r="6" fill="#050810"/><circle cx="46" cy="34" r="2" fill="#00e5cc"/><circle cx="76" cy="34" r="2" fill="#00e5cc"/></svg></span>
|
|
1021
|
+
<div class="brand-col"><span data-i18n="title" class="brand-title">MemOS</span><span data-i18n="subtitle" class="brand-powered">Powered by MemOS</span></div>${vBadge}
|
|
800
1022
|
</div>
|
|
801
1023
|
<div class="topbar-center">
|
|
802
1024
|
<nav class="nav-tabs">
|
|
@@ -806,6 +1028,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
806
1028
|
<button class="tab" data-view="analytics" onclick="switchView('analytics')" data-i18n="tab.analytics">\u{1F4CA} Analytics</button>
|
|
807
1029
|
<button class="tab" data-view="logs" onclick="switchView('logs')" data-i18n="tab.logs">\u{1F4DD} Logs</button>
|
|
808
1030
|
<button class="tab" data-view="import" onclick="switchView('import')" data-i18n="tab.import">\u{1F4E5} Import</button>
|
|
1031
|
+
<button class="tab" data-view="admin" onclick="switchView('admin')" style="display:none" data-i18n="tab.admin">\u{1F6E1} Admin</button>
|
|
809
1032
|
<button class="tab" data-view="settings" onclick="switchView('settings')" data-i18n="tab.settings">\u2699 Settings</button>
|
|
810
1033
|
</nav>
|
|
811
1034
|
</div>
|
|
@@ -825,12 +1048,16 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
825
1048
|
<div class="stat-card amber"><div class="stat-value" id="statEmbeddings">-</div><div class="stat-label" data-i18n="stat.embeddings">Embeddings</div></div>
|
|
826
1049
|
<div class="stat-card rose"><div class="stat-value" id="statTimeSpan">-</div><div class="stat-label" data-i18n="stat.days">Days</div></div>
|
|
827
1050
|
</div>
|
|
828
|
-
<div id="
|
|
829
|
-
<div
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
1051
|
+
<div id="sidebarSharingSection" style="display:none">
|
|
1052
|
+
<div class="sharing-sidebar-card">
|
|
1053
|
+
<div class="title" style="display:flex;align-items:center;gap:8px"><span data-i18n="sidebar.hub">\u{1F310} Team Sharing</span><span id="sharingSidebarConnBadge"></span></div>
|
|
1054
|
+
<div class="status" id="sharingSidebarStatus"></div>
|
|
1055
|
+
<div class="hint" id="sharingSidebarHint"></div>
|
|
1056
|
+
</div>
|
|
833
1057
|
</div>
|
|
1058
|
+
<div id="embeddingStatus"></div>
|
|
1059
|
+
<div class="session-list" id="sessionList" style="display:none"></div>
|
|
1060
|
+
<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>
|
|
834
1061
|
</div>
|
|
835
1062
|
|
|
836
1063
|
<div class="feed-wrap" id="feedWrap">
|
|
@@ -838,8 +1065,17 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
838
1065
|
<div class="search-bar">
|
|
839
1066
|
<span class="search-icon">\u{1F50D}</span>
|
|
840
1067
|
<input type="text" id="searchInput" data-i18n-ph="search.placeholder" placeholder="Search memories (supports semantic search)..." oninput="debounceSearch()">
|
|
1068
|
+
<select id="filterOwner" class="filter-select" onchange="onOwnerFilterChange()">
|
|
1069
|
+
<option value="" data-i18n="filter.allowners">All owners</option>
|
|
1070
|
+
<option value="public" data-i18n="filter.public">Public</option>
|
|
1071
|
+
</select>
|
|
1072
|
+
<select id="memorySearchScope" class="filter-select" onchange="onMemoryScopeChange()">
|
|
1073
|
+
<option value="local" data-i18n="scope.local">Local</option>
|
|
1074
|
+
<option value="all" data-i18n="scope.hub">Hub</option>
|
|
1075
|
+
</select>
|
|
841
1076
|
</div>
|
|
842
1077
|
<div class="search-meta" id="searchMeta"></div>
|
|
1078
|
+
<div class="search-meta" id="sharingSearchMeta"></div>
|
|
843
1079
|
<div class="filter-bar" id="filterBar">
|
|
844
1080
|
<button class="filter-chip active" data-role="" onclick="setRoleFilter(this,'')" data-i18n="filter.all">All</button>
|
|
845
1081
|
<button class="filter-chip" data-role="user" onclick="setRoleFilter(this,'user')">User</button>
|
|
@@ -851,9 +1087,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
851
1087
|
<option value="oldest" data-i18n="filter.oldest">Oldest first</option>
|
|
852
1088
|
</select>
|
|
853
1089
|
<span class="filter-sep"></span>
|
|
854
|
-
<select id="
|
|
855
|
-
<option value="" data-i18n="filter.
|
|
856
|
-
<option value="public" data-i18n="filter.public">Public</option>
|
|
1090
|
+
<select id="filterSession" class="filter-select" onchange="filterSession(this.value||null)">
|
|
1091
|
+
<option value="" data-i18n="filter.allsessions">All sessions</option>
|
|
857
1092
|
</select>
|
|
858
1093
|
</div>
|
|
859
1094
|
<div class="date-filter">
|
|
@@ -878,6 +1113,10 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
878
1113
|
<button class="filter-chip" data-task-status="active" onclick="setTaskStatusFilter(this,'active')" data-i18n="tasks.status.active">Active</button>
|
|
879
1114
|
<button class="filter-chip" data-task-status="completed" onclick="setTaskStatusFilter(this,'completed')" data-i18n="tasks.status.completed">Completed</button>
|
|
880
1115
|
<button class="filter-chip" data-task-status="skipped" onclick="setTaskStatusFilter(this,'skipped')" data-i18n="tasks.status.skipped">Skipped</button>
|
|
1116
|
+
<select id="taskSearchScope" class="scope-select" onchange="onTaskScopeChange()">
|
|
1117
|
+
<option value="local" data-i18n="scope.local">Local</option>
|
|
1118
|
+
<option value="all" data-i18n="scope.hub">Hub</option>
|
|
1119
|
+
</select>
|
|
881
1120
|
<button class="btn btn-sm btn-ghost" onclick="loadTasks()" style="margin-left:auto" data-i18n="refresh">\u21BB Refresh</button>
|
|
882
1121
|
</div>
|
|
883
1122
|
</div>
|
|
@@ -887,7 +1126,10 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
887
1126
|
<div class="task-detail-panel" onclick="event.stopPropagation()">
|
|
888
1127
|
<div class="task-detail-header">
|
|
889
1128
|
<h2 id="taskDetailTitle"></h2>
|
|
890
|
-
<
|
|
1129
|
+
<div style="display:flex;gap:8px;align-items:center">
|
|
1130
|
+
<div id="taskShareActions" style="display:flex;gap:8px;align-items:center"></div>
|
|
1131
|
+
<button class="btn btn-icon" onclick="closeTaskDetail()" title="Close">\u2715</button>
|
|
1132
|
+
</div>
|
|
891
1133
|
</div>
|
|
892
1134
|
<div class="task-detail-meta" id="taskDetailMeta"></div>
|
|
893
1135
|
<div class="task-skill-section" id="taskSkillSection"></div>
|
|
@@ -898,7 +1140,27 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
898
1140
|
</div>
|
|
899
1141
|
</div>
|
|
900
1142
|
</div>
|
|
1143
|
+
<div class="shared-memory-overlay" id="sharedMemoryOverlay" onclick="closeSharedMemoryDetail(event)">
|
|
1144
|
+
<div class="shared-memory-panel" onclick="event.stopPropagation()">
|
|
1145
|
+
<div class="task-detail-header">
|
|
1146
|
+
<h3 id="sharedMemoryTitle">Shared Memory</h3>
|
|
1147
|
+
<button class="btn btn-icon" onclick="closeSharedMemoryDetail()" title="Close">✕</button>
|
|
1148
|
+
</div>
|
|
1149
|
+
<div class="task-detail-meta" id="sharedMemoryMeta"></div>
|
|
1150
|
+
<div class="task-detail-summary" id="sharedMemorySummary"></div>
|
|
1151
|
+
<div class="content" id="sharedMemoryContent"></div>
|
|
1152
|
+
</div>
|
|
1153
|
+
</div>
|
|
901
1154
|
<div class="skills-view" id="skillsView">
|
|
1155
|
+
<div class="search-bar">
|
|
1156
|
+
<span class="search-icon">🔍</span>
|
|
1157
|
+
<input type="text" id="skillSearchInput" placeholder="Search skills..." data-i18n-ph="skills.search.placeholder" oninput="debounceSkillSearch()">
|
|
1158
|
+
<select id="skillSearchScope" class="scope-select" onchange="onSkillScopeChange()">
|
|
1159
|
+
<option value="local" data-i18n="scope.local">Local</option>
|
|
1160
|
+
<option value="all" data-i18n="scope.hub">Hub</option>
|
|
1161
|
+
</select>
|
|
1162
|
+
</div>
|
|
1163
|
+
<div class="search-meta" id="skillSearchMeta"></div>
|
|
902
1164
|
<div class="tasks-header">
|
|
903
1165
|
<div class="tasks-stats">
|
|
904
1166
|
<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>
|
|
@@ -922,6 +1184,10 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
922
1184
|
</div>
|
|
923
1185
|
</div>
|
|
924
1186
|
<div class="tasks-list" id="skillsList"><div class="spinner"></div></div>
|
|
1187
|
+
<div id="hubSkillsSection" style="display:none;margin-top:16px">
|
|
1188
|
+
<div class="section-title" style="margin-bottom:12px" data-i18n="skills.hub.title">\u{1F310} Hub Skills</div>
|
|
1189
|
+
<div class="tasks-list" id="hubSkillsList"></div>
|
|
1190
|
+
</div>
|
|
925
1191
|
</div>
|
|
926
1192
|
<div class="task-detail-overlay" id="skillDetailOverlay" onclick="closeSkillDetail(event)">
|
|
927
1193
|
<div class="task-detail-panel" onclick="event.stopPropagation()">
|
|
@@ -935,6 +1201,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
935
1201
|
</div>
|
|
936
1202
|
<div class="task-detail-meta" id="skillDetailMeta"></div>
|
|
937
1203
|
<div class="skill-detail-desc" id="skillDetailDesc"></div>
|
|
1204
|
+
<div id="skillShareActions" style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:8px 0"></div>
|
|
938
1205
|
<div class="task-detail-chunks-title" data-i18n="skills.files">Skill Files</div>
|
|
939
1206
|
<div class="skill-files-list" id="skillFilesList"></div>
|
|
940
1207
|
<div class="task-detail-chunks-title" id="skillContentTitle" data-i18n="skills.content">SKILL.md Content</div>
|
|
@@ -1000,178 +1267,302 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1000
1267
|
|
|
1001
1268
|
<!-- ─── Settings View ─── -->
|
|
1002
1269
|
<div class="settings-view" id="settingsView">
|
|
1003
|
-
<div class="settings-
|
|
1004
|
-
<
|
|
1005
|
-
<
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
</div>
|
|
1010
|
-
</div>
|
|
1011
|
-
<div class="settings-section">
|
|
1012
|
-
<h3><span class="icon">\u{1F4E1}</span> <span data-i18n="settings.embedding">Embedding Model</span></h3>
|
|
1013
|
-
<div class="settings-grid">
|
|
1014
|
-
<div class="settings-field">
|
|
1015
|
-
<label data-i18n="settings.provider">Provider</label>
|
|
1016
|
-
<select id="cfgEmbProvider" onchange="onProviderChange('embedding')">
|
|
1017
|
-
<option value="openai_compatible">OpenAI Compatible</option>
|
|
1018
|
-
<option value="openai">OpenAI</option>
|
|
1019
|
-
<option value="siliconflow">SiliconFlow (\u7845\u57FA\u6D41\u52A8)</option>
|
|
1020
|
-
<option value="zhipu">Zhipu AI (\u667A\u8C31)</option>
|
|
1021
|
-
<option value="bailian">Alibaba Bailian (\u767E\u70BC)</option>
|
|
1022
|
-
<option value="gemini">Gemini</option>
|
|
1023
|
-
<option value="azure_openai">Azure OpenAI</option>
|
|
1024
|
-
<option value="cohere">Cohere</option>
|
|
1025
|
-
<option value="mistral">Mistral</option>
|
|
1026
|
-
<option value="voyage">Voyage</option>
|
|
1027
|
-
<option value="local">Local</option>
|
|
1028
|
-
</select>
|
|
1029
|
-
</div>
|
|
1030
|
-
<div class="settings-field">
|
|
1031
|
-
<label data-i18n="settings.model">Model</label>
|
|
1032
|
-
<input type="text" id="cfgEmbModel" placeholder="e.g. bge-m3">
|
|
1033
|
-
</div>
|
|
1034
|
-
<div class="settings-field full-width">
|
|
1035
|
-
<label>Endpoint</label>
|
|
1036
|
-
<input type="text" id="cfgEmbEndpoint" placeholder="https://...">
|
|
1037
|
-
</div>
|
|
1038
|
-
<div class="settings-field">
|
|
1039
|
-
<label>API Key</label>
|
|
1040
|
-
<input type="password" id="cfgEmbApiKey" placeholder="\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022">
|
|
1041
|
-
</div>
|
|
1042
|
-
</div>
|
|
1043
|
-
<div class="test-conn-row">
|
|
1044
|
-
<button class="btn btn-sm btn-ghost" onclick="testModel('embedding')" id="testEmbBtn" data-i18n="settings.test">Test Connection</button>
|
|
1045
|
-
<span class="test-result" id="testEmbResult"></span>
|
|
1046
|
-
</div>
|
|
1270
|
+
<div class="settings-tabs-bar">
|
|
1271
|
+
<button class="settings-tab-btn active" data-tab="embedding" onclick="switchSettingsTab('embedding',this)"><span class="stab-dot"></span><span data-i18n="settings.embedding">Embedding Model</span></button>
|
|
1272
|
+
<button class="settings-tab-btn" data-tab="summarizer" onclick="switchSettingsTab('summarizer',this)"><span class="stab-dot"></span><span data-i18n="settings.summarizer">Summarizer Model</span></button>
|
|
1273
|
+
<button class="settings-tab-btn" data-tab="skill" onclick="switchSettingsTab('skill',this)"><span class="stab-dot"></span><span data-i18n="settings.skill">Skill Evolution</span></button>
|
|
1274
|
+
<button class="settings-tab-btn" data-tab="hub" onclick="switchSettingsTab('hub',this)"><span class="stab-dot"></span><span data-i18n="settings.hub">Hub & Team</span></button>
|
|
1275
|
+
<button class="settings-tab-btn" data-tab="general" onclick="switchSettingsTab('general',this)"><span class="stab-dot"></span><span data-i18n="settings.general">General</span></button>
|
|
1047
1276
|
</div>
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
<div class="settings-
|
|
1052
|
-
<div class="settings-
|
|
1053
|
-
<
|
|
1054
|
-
<
|
|
1055
|
-
<
|
|
1056
|
-
<
|
|
1057
|
-
|
|
1058
|
-
<option value="zhipu">Zhipu AI (\u667A\u8C31)</option>
|
|
1059
|
-
<option value="deepseek">DeepSeek</option>
|
|
1060
|
-
<option value="bailian">Alibaba Bailian (\u767E\u70BC)</option>
|
|
1061
|
-
<option value="moonshot">Moonshot (Kimi)</option>
|
|
1062
|
-
<option value="anthropic">Anthropic</option>
|
|
1063
|
-
<option value="gemini">Gemini</option>
|
|
1064
|
-
<option value="azure_openai">Azure OpenAI</option>
|
|
1065
|
-
<option value="bedrock">Bedrock</option>
|
|
1066
|
-
</select>
|
|
1067
|
-
</div>
|
|
1068
|
-
<div class="settings-field">
|
|
1069
|
-
<label data-i18n="settings.model">Model</label>
|
|
1070
|
-
<input type="text" id="cfgSumModel" placeholder="e.g. gpt-4o-mini">
|
|
1071
|
-
</div>
|
|
1072
|
-
<div class="settings-field full-width">
|
|
1073
|
-
<label>Endpoint</label>
|
|
1074
|
-
<input type="text" id="cfgSumEndpoint" placeholder="https://...">
|
|
1075
|
-
</div>
|
|
1076
|
-
<div class="settings-field">
|
|
1077
|
-
<label>API Key</label>
|
|
1078
|
-
<input type="password" id="cfgSumApiKey" placeholder="\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022">
|
|
1277
|
+
<div class="settings-cards-grid">
|
|
1278
|
+
|
|
1279
|
+
<!-- ══ Card: Embedding Model ══ -->
|
|
1280
|
+
<div class="settings-card card-embedding stab-active" data-stab="embedding">
|
|
1281
|
+
<div class="settings-card-header">
|
|
1282
|
+
<div class="settings-card-icon" style="background:rgba(99,102,241,.1);border-color:rgba(99,102,241,.15)">\u{1F4E1}</div>
|
|
1283
|
+
<div class="settings-card-title-wrap">
|
|
1284
|
+
<div class="settings-card-title" data-i18n="settings.embedding">Embedding Model</div>
|
|
1285
|
+
<div class="settings-card-desc" data-i18n="settings.embedding.desc">Vector embedding model for memory search and retrieval</div>
|
|
1286
|
+
</div>
|
|
1079
1287
|
</div>
|
|
1080
|
-
<div class="settings-
|
|
1081
|
-
<
|
|
1082
|
-
|
|
1288
|
+
<div class="settings-card-body">
|
|
1289
|
+
<div class="settings-grid">
|
|
1290
|
+
<div class="settings-field">
|
|
1291
|
+
<label data-i18n="settings.provider">Provider</label>
|
|
1292
|
+
<select id="cfgEmbProvider" onchange="onProviderChange('embedding')">
|
|
1293
|
+
<option value="openai_compatible">OpenAI Compatible</option>
|
|
1294
|
+
<option value="openai">OpenAI</option>
|
|
1295
|
+
<option value="siliconflow">SiliconFlow (\u7845\u57FA\u6D41\u52A8)</option>
|
|
1296
|
+
<option value="zhipu">Zhipu AI (\u667A\u8C31)</option>
|
|
1297
|
+
<option value="bailian">Alibaba Bailian (\u767E\u70BC)</option>
|
|
1298
|
+
<option value="gemini">Gemini</option>
|
|
1299
|
+
<option value="azure_openai">Azure OpenAI</option>
|
|
1300
|
+
<option value="cohere">Cohere</option>
|
|
1301
|
+
<option value="mistral">Mistral</option>
|
|
1302
|
+
<option value="voyage">Voyage</option>
|
|
1303
|
+
<option value="local">Local</option>
|
|
1304
|
+
<option value="openclaw">OpenClaw Host</option>
|
|
1305
|
+
</select>
|
|
1306
|
+
</div>
|
|
1307
|
+
<div class="settings-field">
|
|
1308
|
+
<label data-i18n="settings.model">Model</label>
|
|
1309
|
+
<input type="text" id="cfgEmbModel" placeholder="e.g. bge-m3">
|
|
1310
|
+
</div>
|
|
1311
|
+
<div class="settings-field full-width">
|
|
1312
|
+
<label>Endpoint</label>
|
|
1313
|
+
<input type="text" id="cfgEmbEndpoint" placeholder="https://...">
|
|
1314
|
+
</div>
|
|
1315
|
+
<div class="settings-field">
|
|
1316
|
+
<label>API Key</label>
|
|
1317
|
+
<input type="password" id="cfgEmbApiKey" placeholder="\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022">
|
|
1318
|
+
</div>
|
|
1319
|
+
</div>
|
|
1320
|
+
<div class="test-conn-row">
|
|
1321
|
+
<button class="btn btn-sm btn-ghost" onclick="testModel('embedding')" id="testEmbBtn" data-i18n="settings.test">Test Connection</button>
|
|
1322
|
+
<span class="test-result" id="testEmbResult"></span>
|
|
1323
|
+
</div>
|
|
1083
1324
|
</div>
|
|
1084
1325
|
</div>
|
|
1085
|
-
<div class="test-conn-row">
|
|
1086
|
-
<button class="btn btn-sm btn-ghost" onclick="testModel('summarizer')" id="testSumBtn" data-i18n="settings.test">Test Connection</button>
|
|
1087
|
-
<span class="test-result" id="testSumResult"></span>
|
|
1088
|
-
</div>
|
|
1089
|
-
</div>
|
|
1090
|
-
</div>
|
|
1091
1326
|
|
|
1092
|
-
|
|
1093
|
-
<
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
<
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
<label class="toggle-switch"><input type="checkbox" id="cfgSkillAutoInstall"><span class="toggle-slider"></span></label>
|
|
1101
|
-
<label data-i18n="settings.skill.autoinstall">Auto Install Skills</label>
|
|
1102
|
-
</div>
|
|
1103
|
-
<div class="settings-field">
|
|
1104
|
-
<label data-i18n="settings.skill.confidence">Min Confidence</label>
|
|
1105
|
-
<input type="number" id="cfgSkillConfidence" step="0.1" min="0" max="1" placeholder="0.7">
|
|
1327
|
+
<!-- ══ Card: Summarizer Model ══ -->
|
|
1328
|
+
<div class="settings-card card-summarizer" data-stab="summarizer">
|
|
1329
|
+
<div class="settings-card-header">
|
|
1330
|
+
<div class="settings-card-icon" style="background:rgba(139,92,246,.1);border-color:rgba(139,92,246,.15)">\u{1F9E0}</div>
|
|
1331
|
+
<div class="settings-card-title-wrap">
|
|
1332
|
+
<div class="settings-card-title" data-i18n="settings.summarizer">Summarizer Model</div>
|
|
1333
|
+
<div class="settings-card-desc" data-i18n="settings.summarizer.desc">LLM for memory summarization, deduplication and analysis</div>
|
|
1334
|
+
</div>
|
|
1106
1335
|
</div>
|
|
1107
|
-
<div class="settings-
|
|
1108
|
-
<
|
|
1109
|
-
|
|
1336
|
+
<div class="settings-card-body">
|
|
1337
|
+
<div class="settings-grid">
|
|
1338
|
+
<div class="settings-field">
|
|
1339
|
+
<label data-i18n="settings.provider">Provider</label>
|
|
1340
|
+
<select id="cfgSumProvider" onchange="onProviderChange('summarizer')">
|
|
1341
|
+
<option value="openai_compatible">OpenAI Compatible</option>
|
|
1342
|
+
<option value="openai">OpenAI</option>
|
|
1343
|
+
<option value="siliconflow">SiliconFlow (\u7845\u57FA\u6D41\u52A8)</option>
|
|
1344
|
+
<option value="zhipu">Zhipu AI (\u667A\u8C31)</option>
|
|
1345
|
+
<option value="deepseek">DeepSeek</option>
|
|
1346
|
+
<option value="bailian">Alibaba Bailian (\u767E\u70BC)</option>
|
|
1347
|
+
<option value="moonshot">Moonshot (Kimi)</option>
|
|
1348
|
+
<option value="anthropic">Anthropic</option>
|
|
1349
|
+
<option value="gemini">Gemini</option>
|
|
1350
|
+
<option value="azure_openai">Azure OpenAI</option>
|
|
1351
|
+
<option value="bedrock">Bedrock</option>
|
|
1352
|
+
<option value="openclaw">OpenClaw Host</option>
|
|
1353
|
+
</select>
|
|
1354
|
+
</div>
|
|
1355
|
+
<div class="settings-field">
|
|
1356
|
+
<label data-i18n="settings.model">Model</label>
|
|
1357
|
+
<input type="text" id="cfgSumModel" placeholder="e.g. gpt-4o-mini">
|
|
1358
|
+
</div>
|
|
1359
|
+
<div class="settings-field full-width">
|
|
1360
|
+
<label>Endpoint</label>
|
|
1361
|
+
<input type="text" id="cfgSumEndpoint" placeholder="https://...">
|
|
1362
|
+
</div>
|
|
1363
|
+
<div class="settings-field">
|
|
1364
|
+
<label>API Key</label>
|
|
1365
|
+
<input type="password" id="cfgSumApiKey" placeholder="\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022">
|
|
1366
|
+
</div>
|
|
1367
|
+
<div class="settings-field">
|
|
1368
|
+
<label data-i18n="settings.temperature">Temperature</label>
|
|
1369
|
+
<input type="number" id="cfgSumTemp" step="0.1" min="0" max="2" placeholder="0">
|
|
1370
|
+
</div>
|
|
1371
|
+
</div>
|
|
1372
|
+
<div class="test-conn-row">
|
|
1373
|
+
<button class="btn btn-sm btn-ghost" onclick="testModel('summarizer')" id="testSumBtn" data-i18n="settings.test">Test Connection</button>
|
|
1374
|
+
<span class="test-result" id="testSumResult"></span>
|
|
1375
|
+
</div>
|
|
1110
1376
|
</div>
|
|
1111
1377
|
</div>
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
<div class="settings-
|
|
1116
|
-
<div class="settings-
|
|
1117
|
-
|
|
1118
|
-
<
|
|
1119
|
-
|
|
1120
|
-
<option value="openai_compatible">OpenAI Compatible</option>
|
|
1121
|
-
<option value="openai">OpenAI</option>
|
|
1122
|
-
<option value="siliconflow">SiliconFlow (\u7845\u57FA\u6D41\u52A8)</option>
|
|
1123
|
-
<option value="zhipu">Zhipu AI (\u667A\u8C31)</option>
|
|
1124
|
-
<option value="deepseek">DeepSeek</option>
|
|
1125
|
-
<option value="bailian">Alibaba Bailian (\u767E\u70BC)</option>
|
|
1126
|
-
<option value="moonshot">Moonshot (Kimi)</option>
|
|
1127
|
-
<option value="anthropic">Anthropic</option>
|
|
1128
|
-
<option value="gemini">Gemini</option>
|
|
1129
|
-
<option value="azure_openai">Azure OpenAI</option>
|
|
1130
|
-
<option value="bedrock">Bedrock</option>
|
|
1131
|
-
</select>
|
|
1378
|
+
|
|
1379
|
+
<!-- ══ Card: Skill Evolution ══ -->
|
|
1380
|
+
<div class="settings-card card-skill" data-stab="skill">
|
|
1381
|
+
<div class="settings-card-header">
|
|
1382
|
+
<div class="settings-card-icon" style="background:rgba(245,158,11,.1);border-color:rgba(245,158,11,.15)">\u{1F527}</div>
|
|
1383
|
+
<div class="settings-card-title-wrap">
|
|
1384
|
+
<div class="settings-card-title" data-i18n="settings.skill">Skill Evolution</div>
|
|
1385
|
+
<div class="settings-card-desc" data-i18n="settings.skill.desc">Auto-extract reusable skills from conversation patterns</div>
|
|
1132
1386
|
</div>
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1387
|
+
</div>
|
|
1388
|
+
<div class="settings-card-body">
|
|
1389
|
+
<div class="settings-grid">
|
|
1390
|
+
<div class="settings-toggle">
|
|
1391
|
+
<label class="toggle-switch"><input type="checkbox" id="cfgSkillEnabled"><span class="toggle-slider"></span></label>
|
|
1392
|
+
<label data-i18n="settings.skill.enabled">Enable Skill Evolution</label>
|
|
1393
|
+
</div>
|
|
1394
|
+
<div class="settings-toggle">
|
|
1395
|
+
<label class="toggle-switch"><input type="checkbox" id="cfgSkillAutoInstall"><span class="toggle-slider"></span></label>
|
|
1396
|
+
<label data-i18n="settings.skill.autoinstall">Auto Install Skills</label>
|
|
1397
|
+
</div>
|
|
1398
|
+
<div class="settings-field">
|
|
1399
|
+
<label data-i18n="settings.skill.confidence">Min Confidence</label>
|
|
1400
|
+
<input type="number" id="cfgSkillConfidence" step="0.1" min="0" max="1" placeholder="0.7">
|
|
1401
|
+
</div>
|
|
1402
|
+
<div class="settings-field">
|
|
1403
|
+
<label data-i18n="settings.skill.minchunks">Min Chunks</label>
|
|
1404
|
+
<input type="number" id="cfgSkillMinChunks" placeholder="6">
|
|
1405
|
+
</div>
|
|
1136
1406
|
</div>
|
|
1137
|
-
<div class="settings-
|
|
1138
|
-
|
|
1139
|
-
|
|
1407
|
+
<div class="settings-card-divider"></div>
|
|
1408
|
+
<div class="settings-card-subtitle" data-i18n="settings.skill.model">Skill Dedicated Model</div>
|
|
1409
|
+
<div class="field-hint" style="margin-bottom:12px" data-i18n="settings.skill.model.hint">If not configured, the main Summarizer Model above will be used for skill generation. Configure a dedicated model here for higher quality skill output.</div>
|
|
1410
|
+
<div class="settings-grid">
|
|
1411
|
+
<div class="settings-field">
|
|
1412
|
+
<label data-i18n="settings.provider">Provider</label>
|
|
1413
|
+
<select id="cfgSkillProvider" onchange="onProviderChange('skill')">
|
|
1414
|
+
<option value="">\u2014 <span data-i18n="settings.skill.usemain">Use main summarizer</span> \u2014</option>
|
|
1415
|
+
<option value="openai_compatible">OpenAI Compatible</option>
|
|
1416
|
+
<option value="openai">OpenAI</option>
|
|
1417
|
+
<option value="siliconflow">SiliconFlow (\u7845\u57FA\u6D41\u52A8)</option>
|
|
1418
|
+
<option value="zhipu">Zhipu AI (\u667A\u8C31)</option>
|
|
1419
|
+
<option value="deepseek">DeepSeek</option>
|
|
1420
|
+
<option value="bailian">Alibaba Bailian (\u767E\u70BC)</option>
|
|
1421
|
+
<option value="moonshot">Moonshot (Kimi)</option>
|
|
1422
|
+
<option value="anthropic">Anthropic</option>
|
|
1423
|
+
<option value="gemini">Gemini</option>
|
|
1424
|
+
<option value="azure_openai">Azure OpenAI</option>
|
|
1425
|
+
<option value="bedrock">Bedrock</option>
|
|
1426
|
+
<option value="openclaw">OpenClaw Host</option>
|
|
1427
|
+
</select>
|
|
1428
|
+
</div>
|
|
1429
|
+
<div class="settings-field">
|
|
1430
|
+
<label data-i18n="settings.model">Model</label>
|
|
1431
|
+
<input type="text" id="cfgSkillModel" placeholder="e.g. claude-4.6-opus">
|
|
1432
|
+
</div>
|
|
1433
|
+
<div class="settings-field full-width">
|
|
1434
|
+
<label>Endpoint</label>
|
|
1435
|
+
<input type="text" id="cfgSkillEndpoint" placeholder="https://...">
|
|
1436
|
+
</div>
|
|
1437
|
+
<div class="settings-field">
|
|
1438
|
+
<label>API Key</label>
|
|
1439
|
+
<input type="password" id="cfgSkillApiKey" placeholder="\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022">
|
|
1440
|
+
</div>
|
|
1140
1441
|
</div>
|
|
1141
|
-
<div class="
|
|
1142
|
-
<
|
|
1143
|
-
<
|
|
1442
|
+
<div class="test-conn-row">
|
|
1443
|
+
<button class="btn btn-sm btn-ghost" onclick="testModel('skill')" id="testSkillBtn" data-i18n="settings.test">Test Connection</button>
|
|
1444
|
+
<span class="test-result" id="testSkillResult"></span>
|
|
1144
1445
|
</div>
|
|
1145
1446
|
</div>
|
|
1146
|
-
<div class="test-conn-row">
|
|
1147
|
-
<button class="btn btn-sm btn-ghost" onclick="testModel('skill')" id="testSkillBtn" data-i18n="settings.test">Test Connection</button>
|
|
1148
|
-
<span class="test-result" id="testSkillResult"></span>
|
|
1149
|
-
</div>
|
|
1150
1447
|
</div>
|
|
1151
|
-
</div>
|
|
1152
1448
|
|
|
1153
|
-
|
|
1154
|
-
<
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
<
|
|
1158
|
-
|
|
1449
|
+
<!-- ══ Card: Hub & Team ══ -->
|
|
1450
|
+
<div class="settings-card card-hub" id="settingsSharingConfig" data-stab="hub">
|
|
1451
|
+
<div class="settings-card-header">
|
|
1452
|
+
<div class="settings-card-icon" style="background:rgba(6,182,212,.1);border-color:rgba(6,182,212,.15)">\u{1F310}</div>
|
|
1453
|
+
<div class="settings-card-title-wrap">
|
|
1454
|
+
<div class="settings-card-title" data-i18n="settings.hub">Hub & Team</div>
|
|
1455
|
+
<div class="settings-card-desc" data-i18n="settings.hub.desc">Share memories, tasks and skills with your team</div>
|
|
1456
|
+
</div>
|
|
1159
1457
|
</div>
|
|
1160
|
-
<div class="settings-
|
|
1161
|
-
<div class="field-hint" data-i18n="settings.
|
|
1458
|
+
<div class="settings-card-body">
|
|
1459
|
+
<div class="field-hint" style="margin-bottom:12px" data-i18n="settings.hub.enable.hint">Enable to share memories, tasks and skills with your team. When disabled, all features work normally in local-only mode.</div>
|
|
1460
|
+
<div class="settings-toggle" style="margin-bottom:16px">
|
|
1461
|
+
<label class="toggle-switch">
|
|
1462
|
+
<input type="checkbox" id="cfgSharingEnabled" onchange="onSharingToggle()">
|
|
1463
|
+
<span class="toggle-slider"></span>
|
|
1464
|
+
</label>
|
|
1465
|
+
<label data-i18n="settings.hub.enable.label">Enable Hub Sharing</label>
|
|
1466
|
+
</div>
|
|
1467
|
+
|
|
1468
|
+
<div id="sharingConfigPanel" style="display:none">
|
|
1469
|
+
<div style="margin-bottom:14px">
|
|
1470
|
+
<label style="font-size:11px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.04em;display:block;margin-bottom:6px" data-i18n="settings.hub.role">Role</label>
|
|
1471
|
+
<div style="display:flex;gap:8px">
|
|
1472
|
+
<button class="btn btn-sm" id="btnRoleHub" onclick="selectSharingRole('hub')" data-i18n="settings.hub.role.hub">Hub (Server)</button>
|
|
1473
|
+
<button class="btn btn-sm" id="btnRoleClient" onclick="selectSharingRole('client')" data-i18n="settings.hub.role.client">Client (Connect to Hub)</button>
|
|
1474
|
+
</div>
|
|
1475
|
+
<div class="field-hint" style="margin-top:6px" data-i18n="settings.hub.role.hint">Hub: this device runs the central server. Client: connect to an existing Hub. The two modes are mutually exclusive.</div>
|
|
1476
|
+
</div>
|
|
1477
|
+
|
|
1478
|
+
<div id="hubModeConfig" style="display:none">
|
|
1479
|
+
<input type="hidden" id="cfgHubTeamToken" value="">
|
|
1480
|
+
<div class="settings-grid">
|
|
1481
|
+
<div class="settings-field">
|
|
1482
|
+
<label data-i18n="settings.hub.port">Hub Port</label>
|
|
1483
|
+
<input type="number" id="cfgHubPort" placeholder="18800" value="18800">
|
|
1484
|
+
<div class="field-hint" data-i18n="settings.hub.port.hint">Port for Hub service. Default: 18800</div>
|
|
1485
|
+
</div>
|
|
1486
|
+
<div class="settings-field">
|
|
1487
|
+
<label data-i18n="settings.hub.teamName">Team Name</label>
|
|
1488
|
+
<input type="text" id="cfgHubTeamName" placeholder="My Team">
|
|
1489
|
+
<div class="field-hint" data-i18n="settings.hub.teamName.hint">Your team display name</div>
|
|
1490
|
+
</div>
|
|
1491
|
+
</div>
|
|
1492
|
+
<div id="hubShareInfo" style="display:none;margin-top:14px;background:var(--bg);border:1px solid var(--border);border-radius:10px;padding:14px 18px">
|
|
1493
|
+
<div style="font-size:11px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.04em;margin-bottom:10px" data-i18n="settings.hub.shareInfo.title">Share this info with your team members:</div>
|
|
1494
|
+
<div id="hubShareInfoContent" style="display:grid;grid-template-columns:auto 1fr;gap:6px 12px;font-size:12px;align-items:center"></div>
|
|
1495
|
+
</div>
|
|
1496
|
+
<div style="margin-top:16px;display:flex;align-items:center;gap:12px">
|
|
1497
|
+
<button class="btn btn-sm btn-primary" onclick="switchView('admin')" id="hubAdminEntryBtn" style="display:none" data-i18n="sharing.openAdmin">\u{1F6E1} Open Admin Panel</button>
|
|
1498
|
+
</div>
|
|
1499
|
+
</div>
|
|
1500
|
+
|
|
1501
|
+
<div id="clientModeConfig" style="display:none">
|
|
1502
|
+
<div style="background:rgba(99,102,241,.08);border:1px solid rgba(99,102,241,.2);border-radius:10px;padding:14px 18px;margin-bottom:14px;font-size:12px;line-height:1.7;color:var(--text-sec)">
|
|
1503
|
+
<div style="font-weight:700;color:var(--text);margin-bottom:4px" data-i18n="settings.hub.clientSteps.title">Quick Setup (3 steps)</div>
|
|
1504
|
+
<div><span style="color:var(--accent)">1.</span> <span data-i18n="settings.hub.clientSteps.s1">Ask your Hub admin for Hub Address and Team Token</span></div>
|
|
1505
|
+
<div><span style="color:var(--accent)">2.</span> <span data-i18n="settings.hub.clientSteps.s2">Fill them in below, click "Test Connection" to verify</span></div>
|
|
1506
|
+
<div><span style="color:var(--accent)">3.</span> <span data-i18n="settings.hub.clientSteps.s3">Click "Save Settings", then restart OpenClaw gateway to connect</span></div>
|
|
1507
|
+
</div>
|
|
1508
|
+
<div class="settings-grid">
|
|
1509
|
+
<div class="settings-field full-width">
|
|
1510
|
+
<label data-i18n="settings.hub.hubAddress">Hub Address</label>
|
|
1511
|
+
<input type="text" id="cfgClientHubAddress" placeholder="e.g. 192.168.1.100:18800">
|
|
1512
|
+
<div class="field-hint" data-i18n="settings.hub.hubAddress.hint">Hub server address, e.g. 192.168.1.100:18800 or hub.example.com:18800</div>
|
|
1513
|
+
</div>
|
|
1514
|
+
<div class="settings-field">
|
|
1515
|
+
<label data-i18n="settings.hub.teamTokenClient">Team Token</label>
|
|
1516
|
+
<input type="text" id="cfgClientTeamToken" placeholder="">
|
|
1517
|
+
<div class="field-hint" data-i18n="settings.hub.teamTokenClient.hint">Get this from your Hub admin to join the team</div>
|
|
1518
|
+
</div>
|
|
1519
|
+
<input type="hidden" id="cfgClientUserToken" value="">
|
|
1520
|
+
</div>
|
|
1521
|
+
<div style="margin-top:12px">
|
|
1522
|
+
<button class="btn btn-sm btn-primary" id="btnTestHubConn" onclick="testHubConnection()" data-i18n="settings.hub.testConnection">Test Connection</button>
|
|
1523
|
+
<span id="hubConnTestResult" style="margin-left:10px;font-size:12px"></span>
|
|
1524
|
+
</div>
|
|
1525
|
+
</div>
|
|
1526
|
+
</div>
|
|
1527
|
+
|
|
1528
|
+
<div class="settings-card-divider"></div>
|
|
1529
|
+
<div id="sharingStatusPanel"></div>
|
|
1530
|
+
<div id="sharingTeamPanel"></div>
|
|
1531
|
+
<div id="sharingAdminPanel"></div>
|
|
1162
1532
|
</div>
|
|
1163
1533
|
</div>
|
|
1164
|
-
</div>
|
|
1165
1534
|
|
|
1166
|
-
|
|
1167
|
-
<
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
<
|
|
1171
|
-
|
|
1172
|
-
|
|
1535
|
+
<!-- ══ Card: General ══ -->
|
|
1536
|
+
<div class="settings-card card-general" id="settingsModelConfig" data-stab="general">
|
|
1537
|
+
<div class="settings-card-header">
|
|
1538
|
+
<div class="settings-card-icon" style="background:rgba(16,185,129,.1);border-color:rgba(16,185,129,.15)">\u2699\uFE0F</div>
|
|
1539
|
+
<div class="settings-card-title-wrap">
|
|
1540
|
+
<div class="settings-card-title" data-i18n="settings.general">General</div>
|
|
1541
|
+
<div class="settings-card-desc" data-i18n="settings.general.desc">System status, ports and telemetry</div>
|
|
1542
|
+
</div>
|
|
1543
|
+
</div>
|
|
1544
|
+
<div class="settings-card-body">
|
|
1545
|
+
<div class="settings-card-subtitle" data-i18n="settings.modelhealth">\u{1F4CA} Model Health</div>
|
|
1546
|
+
<div class="model-health-bar" id="modelHealthBar">
|
|
1547
|
+
<div style="font-size:12px;color:var(--text-muted);width:100%">Loading model status...</div>
|
|
1548
|
+
</div>
|
|
1549
|
+
<div class="settings-card-divider"></div>
|
|
1550
|
+
<div class="settings-grid">
|
|
1551
|
+
<div class="settings-field">
|
|
1552
|
+
<label data-i18n="settings.viewerport">Viewer Port</label>
|
|
1553
|
+
<input type="number" id="cfgViewerPort" placeholder="18799">
|
|
1554
|
+
<div class="field-hint" data-i18n="settings.viewerport.hint">Requires restart to take effect</div>
|
|
1555
|
+
</div>
|
|
1556
|
+
</div>
|
|
1557
|
+
<div class="settings-card-divider"></div>
|
|
1558
|
+
<div class="settings-toggle">
|
|
1559
|
+
<label class="toggle-switch"><input type="checkbox" id="cfgTelemetryEnabled" checked><span class="toggle-slider"></span></label>
|
|
1560
|
+
<label data-i18n="settings.telemetry.enabled">Enable Anonymous Telemetry</label>
|
|
1561
|
+
</div>
|
|
1562
|
+
<div class="field-hint" style="margin-top:6px" data-i18n="settings.telemetry.hint">Anonymous usage analytics to help improve the plugin. Only sends tool names, latencies, and version info. No memory content, queries, or personal data is ever sent.</div>
|
|
1173
1563
|
</div>
|
|
1174
1564
|
</div>
|
|
1565
|
+
|
|
1175
1566
|
</div>
|
|
1176
1567
|
|
|
1177
1568
|
<div class="settings-actions">
|
|
@@ -1182,6 +1573,28 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1182
1573
|
<div style="font-size:11px;color:var(--text-muted);text-align:right;margin-top:4px" data-i18n="settings.restart.hint">Some changes require restarting the OpenClaw gateway to take effect.</div>
|
|
1183
1574
|
</div>
|
|
1184
1575
|
|
|
1576
|
+
<!-- ─── Admin Page ─── -->
|
|
1577
|
+
<div class="admin-view" id="adminView">
|
|
1578
|
+
<div class="admin-header">
|
|
1579
|
+
<div class="admin-header-top">
|
|
1580
|
+
<h2><span class="ah-icon">\u{1F6E1}</span> <span data-i18n="admin.title">Hub Admin Panel</span></h2>
|
|
1581
|
+
<button class="btn btn-sm btn-ghost" onclick="loadAdminData()" style="backdrop-filter:blur(8px)" data-i18n="admin.refresh">\u21BB Refresh</button>
|
|
1582
|
+
</div>
|
|
1583
|
+
<div class="admin-header-sub" data-i18n="admin.subtitle">Manage team members, groups, and shared resources</div>
|
|
1584
|
+
<div class="admin-stat-row" id="adminStats"></div>
|
|
1585
|
+
</div>
|
|
1586
|
+
<div class="admin-tabs" id="adminTabsBar">
|
|
1587
|
+
<button class="admin-tab active" onclick="switchAdminTab('users',this)"><span class="at-icon">\u{1F465}</span> <span data-i18n="admin.tab.users">Users</span> <span class="at-count" id="adminTabCountUsers">0</span></button>
|
|
1588
|
+
<button class="admin-tab" onclick="switchAdminTab('sharedMemories',this)"><span class="at-icon">\u{1F4AD}</span> <span data-i18n="admin.tab.sharedMemories">Shared Memories</span> <span class="at-count" id="adminTabCountMemories">0</span></button>
|
|
1589
|
+
<button class="admin-tab" onclick="switchAdminTab('memories',this)"><span class="at-icon">\u{1F4CB}</span> <span data-i18n="admin.tab.memories">Shared Tasks</span> <span class="at-count" id="adminTabCountTasks">0</span></button>
|
|
1590
|
+
<button class="admin-tab" onclick="switchAdminTab('skills',this)"><span class="at-icon">\u{1F9E0}</span> <span data-i18n="admin.tab.skills">Shared Skills</span> <span class="at-count" id="adminTabCountSkills">0</span></button>
|
|
1591
|
+
</div>
|
|
1592
|
+
<div class="admin-panel active" id="adminUsersPanel"></div>
|
|
1593
|
+
<div class="admin-panel" id="adminSharedMemoriesPanel"></div>
|
|
1594
|
+
<div class="admin-panel" id="adminMemoriesPanel"></div>
|
|
1595
|
+
<div class="admin-panel" id="adminSkillsPanel"></div>
|
|
1596
|
+
</div>
|
|
1597
|
+
|
|
1185
1598
|
<!-- ─── Import Page ─── -->
|
|
1186
1599
|
<div class="migrate-view" id="migrateView">
|
|
1187
1600
|
<div class="settings-section" style="border:1px solid rgba(99,102,241,.15)">
|
|
@@ -1375,12 +1788,13 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1375
1788
|
|
|
1376
1789
|
<script>
|
|
1377
1790
|
let activeSession=null,activeRole='',editingId=null,searchTimer=null,memoryCache={},currentPage=1,totalPages=1,totalCount=0,PAGE_SIZE=40,metricsDays=30;
|
|
1791
|
+
let memorySearchScope='local',skillSearchScope='local',taskSearchScope='local';
|
|
1378
1792
|
let _embeddingWarningShown=false;
|
|
1379
1793
|
|
|
1380
1794
|
/* ─── i18n ─── */
|
|
1381
1795
|
const I18N={
|
|
1382
1796
|
en:{
|
|
1383
|
-
'title':'
|
|
1797
|
+
'title':'MemOS',
|
|
1384
1798
|
'subtitle':'Powered by MemOS',
|
|
1385
1799
|
'setup.desc':'Set a password to protect your memories',
|
|
1386
1800
|
'setup.pw':'Enter a password (4+ characters)',
|
|
@@ -1420,6 +1834,14 @@ const I18N={
|
|
|
1420
1834
|
'skills.active':'Active',
|
|
1421
1835
|
'skills.installed':'Installed',
|
|
1422
1836
|
'skills.public':'Public',
|
|
1837
|
+
'skills.search.placeholder':'Search skills...',
|
|
1838
|
+
'skills.search.local':'Local',
|
|
1839
|
+
'skills.search.noresult':'No matching skills found',
|
|
1840
|
+
'skills.load.error':'Failed to load skills',
|
|
1841
|
+
'skills.hub.title':'\u{1F310} Hub Skills',
|
|
1842
|
+
'scope.local':'Local',
|
|
1843
|
+
'scope.group':'Group',
|
|
1844
|
+
'scope.all':'All',
|
|
1423
1845
|
'skills.visibility.public':'Public',
|
|
1424
1846
|
'skills.visibility.private':'Private',
|
|
1425
1847
|
'skills.setPublic':'Set Public',
|
|
@@ -1458,6 +1880,7 @@ const I18N={
|
|
|
1458
1880
|
'filter.newest':'Newest first',
|
|
1459
1881
|
'filter.oldest':'Oldest first',
|
|
1460
1882
|
'filter.allowners':'All owners',
|
|
1883
|
+
'filter.allsessions':'All sessions',
|
|
1461
1884
|
'filter.public':'Public',
|
|
1462
1885
|
'filter.private':'Private',
|
|
1463
1886
|
'filter.allvisibility':'All visibility',
|
|
@@ -1543,6 +1966,15 @@ const I18N={
|
|
|
1543
1966
|
'settings.summarizer':'Summarizer Model',
|
|
1544
1967
|
'settings.skill':'Skill Evolution',
|
|
1545
1968
|
'settings.general':'General',
|
|
1969
|
+
'settings.embedding.desc':'Vector embedding model for memory search and retrieval',
|
|
1970
|
+
'settings.summarizer.desc':'LLM for memory summarization, deduplication and analysis',
|
|
1971
|
+
'settings.skill.desc':'Auto-extract reusable skills from conversation patterns',
|
|
1972
|
+
'settings.hub.desc':'Share memories, tasks and skills with your team',
|
|
1973
|
+
'settings.general.desc':'System status, ports and telemetry',
|
|
1974
|
+
'settings.hostproxy.embedding':'Use Gateway Model',
|
|
1975
|
+
'settings.hostproxy.completion':'Use Gateway Model',
|
|
1976
|
+
'settings.hostproxy.skill':'Use Gateway Model',
|
|
1977
|
+
'settings.hostproxy.hint.short':'Reuse gateway model config',
|
|
1546
1978
|
'settings.provider':'Provider',
|
|
1547
1979
|
'settings.model':'Model',
|
|
1548
1980
|
'settings.temperature':'Temperature',
|
|
@@ -1551,12 +1983,12 @@ const I18N={
|
|
|
1551
1983
|
'settings.skill.confidence':'Min Confidence',
|
|
1552
1984
|
'settings.skill.minchunks':'Min Chunks',
|
|
1553
1985
|
'settings.skill.model':'Skill Dedicated Model',
|
|
1554
|
-
'settings.skill.model.hint':'
|
|
1986
|
+
'settings.skill.model.hint':'Leave empty to reuse the Summarizer model. Set a dedicated one for higher quality.',
|
|
1555
1987
|
'settings.optional':'Optional',
|
|
1556
1988
|
'settings.skill.usemain':'Use Main Summarizer',
|
|
1557
1989
|
'settings.telemetry':'Telemetry',
|
|
1558
1990
|
'settings.telemetry.enabled':'Enable Anonymous Telemetry',
|
|
1559
|
-
'settings.telemetry.hint':'
|
|
1991
|
+
'settings.telemetry.hint':'Only collects tool names, latencies and version info. No memory content or personal data.',
|
|
1560
1992
|
'settings.viewerport':'Viewer Port',
|
|
1561
1993
|
'settings.viewerport.hint':'Requires restart to take effect',
|
|
1562
1994
|
'settings.test':'Test Connection',
|
|
@@ -1653,7 +2085,7 @@ const I18N={
|
|
|
1653
2085
|
'skills.related':'Related Tasks',
|
|
1654
2086
|
'skills.download':'\u2B07 Download',
|
|
1655
2087
|
'skills.installed.badge':'Installed',
|
|
1656
|
-
'skills.empty':'No skills yet. Skills are
|
|
2088
|
+
'skills.empty':'No skills yet. Skills are auto-generated from completed tasks with reusable patterns.',
|
|
1657
2089
|
'skills.loading':'Loading...',
|
|
1658
2090
|
'skills.error':'Error loading skill',
|
|
1659
2091
|
'skills.error.detail':'Failed to load skill: ',
|
|
@@ -1674,6 +2106,222 @@ const I18N={
|
|
|
1674
2106
|
'tasks.error':'Error',
|
|
1675
2107
|
'tasks.error.detail':'Failed to load task details',
|
|
1676
2108
|
'tasks.untitled.related':'Untitled',
|
|
2109
|
+
'tab.admin':'\u{1F6E1} Admin',
|
|
2110
|
+
'settings.hub':'Hub & Team',
|
|
2111
|
+
'settings.hub.enable':'Enable Hub Sharing',
|
|
2112
|
+
'settings.hub.enable.hint':'When off, everything works locally as usual.',
|
|
2113
|
+
'settings.hub.enable.label':'Enable Hub Sharing',
|
|
2114
|
+
'settings.hub.role':'Role',
|
|
2115
|
+
'settings.hub.role.hub':'Hub (Server)',
|
|
2116
|
+
'settings.hub.role.client':'Client (Connect to Hub)',
|
|
2117
|
+
'settings.hub.role.hint':'Hub = run the server; Client = connect to one. These two modes are mutually exclusive.',
|
|
2118
|
+
'settings.hub.port':'Hub Port',
|
|
2119
|
+
'settings.hub.port.hint':'Port for Hub service. Default: 18800',
|
|
2120
|
+
'settings.hub.teamName':'Team Name',
|
|
2121
|
+
'settings.hub.teamName.hint':'Display name for your team',
|
|
2122
|
+
'settings.hub.teamToken':'Team Token',
|
|
2123
|
+
'settings.hub.teamToken.hint':'Auto-generated secret for clients to join. Click to copy. Share this with your team members.',
|
|
2124
|
+
'settings.hub.tokenCopied':'Team Token copied!',
|
|
2125
|
+
'settings.hub.hubSteps.title':'Quick Setup (3 steps)',
|
|
2126
|
+
'settings.hub.hubSteps.s1':'Fill in Team Name below (or keep default)',
|
|
2127
|
+
'settings.hub.hubSteps.s2':'Click "Save Settings", then restart OpenClaw gateway',
|
|
2128
|
+
'settings.hub.hubSteps.s3':'Share the Hub Address and Team Token below with your team members',
|
|
2129
|
+
'settings.hub.clientSteps.title':'Quick Setup (3 steps)',
|
|
2130
|
+
'settings.hub.clientSteps.s1':'Ask your Hub admin for Hub Address and Team Token',
|
|
2131
|
+
'settings.hub.clientSteps.s2':'Fill them in below, click "Test Connection" to verify',
|
|
2132
|
+
'settings.hub.clientSteps.s3':'Click "Save Settings", then restart OpenClaw gateway to connect',
|
|
2133
|
+
'settings.hub.shareInfo.title':'Share this info with your team members:',
|
|
2134
|
+
'settings.hub.shareInfo.yourIP':'your-IP',
|
|
2135
|
+
'settings.hub.shareInfo.clickCopy':'Click to copy',
|
|
2136
|
+
'settings.hub.restartAlert':'Hub sharing config saved! Please restart the OpenClaw gateway for changes to take effect.\\n\\nRun: openclaw gateway stop && openclaw gateway',
|
|
2137
|
+
'settings.hub.hubAddress':'Hub Address',
|
|
2138
|
+
'settings.hub.hubAddress.hint':'Hub server address, e.g. 192.168.1.100:18800',
|
|
2139
|
+
'settings.hub.teamTokenClient':'Team Token',
|
|
2140
|
+
'settings.hub.teamTokenClient.hint':'Get this from your Hub admin to join the team',
|
|
2141
|
+
'settings.hub.userToken':'User Token',
|
|
2142
|
+
'settings.hub.userToken.hint':'Usually auto-obtained after joining. Only fill if given by admin.',
|
|
2143
|
+
'settings.hub.testConnection':'Test Connection',
|
|
2144
|
+
'settings.hub.test.noAddr':'Please enter Hub address first',
|
|
2145
|
+
'settings.hub.test.testing':'Testing...',
|
|
2146
|
+
'settings.hub.test.ok':'Connected successfully',
|
|
2147
|
+
'settings.hub.test.fail':'Connection failed',
|
|
2148
|
+
'settings.hub.connection':'Connection Status',
|
|
2149
|
+
'settings.hub.team':'Team & Groups',
|
|
2150
|
+
'settings.hub.adminPending':'Admin Pending Users',
|
|
2151
|
+
'sidebar.hub':'\u{1F310} Team Sharing',
|
|
2152
|
+
'sharing.sidebar.connected':'Connected',
|
|
2153
|
+
'sharing.sidebar.disconnected':'Disconnected',
|
|
2154
|
+
'sharing.sidebar.pending':'Pending Approval',
|
|
2155
|
+
'sharing.sidebar.rejected':'Rejected',
|
|
2156
|
+
'sharing.sidebar.starting':'Starting...',
|
|
2157
|
+
'sharing.sidebar.notConfigured':'Not configured',
|
|
2158
|
+
'sharing.sidebar.identity':'Identity:',
|
|
2159
|
+
'sharing.sidebar.admin':'Admin',
|
|
2160
|
+
'sharing.sidebar.targetHub':'Target Hub:',
|
|
2161
|
+
'sharing.pendingApproval.hint':'Your join request has been submitted. Please wait for the Hub admin to approve.',
|
|
2162
|
+
'sharing.rejected.hint':'Your join request was rejected by the Hub admin. Please contact the admin or retry.',
|
|
2163
|
+
'sharing.retryJoin':'Retry Join',
|
|
2164
|
+
'sharing.retryJoin.hint':'Clears local data and re-submits the join request',
|
|
2165
|
+
'sharing.retryJoin.confirm':'This will clear your current connection and re-submit a join request. Continue?',
|
|
2166
|
+
'sharing.retryJoin.success':'Join request re-submitted. Waiting for admin approval.',
|
|
2167
|
+
'sharing.retryJoin.fail':'Failed to retry join',
|
|
2168
|
+
'sharing.cannotJoinSelf':'Cannot join your own Hub. Please enter a remote Hub address.',
|
|
2169
|
+
'scope.hub':'Hub',
|
|
2170
|
+
'memory.detail.title':'Memory Detail',
|
|
2171
|
+
'memory.detail.loading':'Loading...',
|
|
2172
|
+
'memory.detail.notFound':'Memory not found',
|
|
2173
|
+
'memory.detail.copyId':'Click to copy ID',
|
|
2174
|
+
'memory.detail.created':'Created ',
|
|
2175
|
+
'memory.detail.updated':'Updated ',
|
|
2176
|
+
'memory.detail.viewTarget':'View target: ',
|
|
2177
|
+
'admin.title':'Hub Admin Panel',
|
|
2178
|
+
'admin.subtitle':'Manage team members and shared resources',
|
|
2179
|
+
'admin.refresh':'\u21BB Refresh',
|
|
2180
|
+
'admin.tab.users':'Users',
|
|
2181
|
+
'admin.tab.groups':'Groups',
|
|
2182
|
+
'admin.tab.memories':'Shared Tasks',
|
|
2183
|
+
'admin.tab.skills':'Shared Skills',
|
|
2184
|
+
'admin.stat.activeUsers':'Active Users',
|
|
2185
|
+
'admin.stat.pending':'Pending',
|
|
2186
|
+
'admin.stat.groups':'Groups',
|
|
2187
|
+
'admin.stat.sharedTasks':'Shared Tasks',
|
|
2188
|
+
'admin.stat.sharedSkills':'Shared Skills',
|
|
2189
|
+
'admin.stat.sharedMemories':'Shared Memories',
|
|
2190
|
+
'admin.pendingApproval':'Pending Approval',
|
|
2191
|
+
'admin.activeUsers':'Active Users',
|
|
2192
|
+
'admin.noActiveUsers':'No active users.',
|
|
2193
|
+
'admin.approve':'Approve',
|
|
2194
|
+
'admin.reject':'Reject',
|
|
2195
|
+
'admin.device':'Device: ',
|
|
2196
|
+
'admin.groups':'Groups',
|
|
2197
|
+
'admin.newGroup':'+ New Group',
|
|
2198
|
+
'admin.groupName':'Group name',
|
|
2199
|
+
'admin.groupDesc':'Description (optional)',
|
|
2200
|
+
'admin.create':'Create',
|
|
2201
|
+
'admin.cancel':'Cancel',
|
|
2202
|
+
'admin.delete':'Delete',
|
|
2203
|
+
'admin.members':'Members',
|
|
2204
|
+
'admin.noGroups':'No groups created yet.',
|
|
2205
|
+
'admin.noMembers':'No members.',
|
|
2206
|
+
'admin.add':'Add',
|
|
2207
|
+
'admin.remove':'Remove',
|
|
2208
|
+
'admin.sharedTasks':'Shared Tasks',
|
|
2209
|
+
'admin.noSharedTasks':'No shared tasks on Hub.',
|
|
2210
|
+
'admin.owner':'Owner: ',
|
|
2211
|
+
'admin.group':'Group: ',
|
|
2212
|
+
'admin.chunks':'Chunks: ',
|
|
2213
|
+
'admin.updated':'Updated: ',
|
|
2214
|
+
'admin.sharedSkills':'Shared Skills',
|
|
2215
|
+
'admin.noSharedSkills':'No shared skills on Hub.',
|
|
2216
|
+
'admin.sharedMemories':'Shared Memories',
|
|
2217
|
+
'admin.noSharedMemories':'No shared memories on Hub.',
|
|
2218
|
+
'admin.tab.sharedMemories':'Shared Memories',
|
|
2219
|
+
'admin.version':'v',
|
|
2220
|
+
'admin.quality':'Quality: ',
|
|
2221
|
+
'admin.membersCount':'Members ({n}):',
|
|
2222
|
+
'admin.noMembersYet':'No members yet.',
|
|
2223
|
+
'admin.loadFailed':'Failed to load admin data: ',
|
|
2224
|
+
'admin.noPermission':'You do not have admin permissions to access this panel.',
|
|
2225
|
+
'admin.groupsFailed':'Failed to load groups: ',
|
|
2226
|
+
'toast.userApproved':'User approved',
|
|
2227
|
+
'toast.userRejected':'User rejected',
|
|
2228
|
+
'toast.approveFail':'Approve failed',
|
|
2229
|
+
'toast.rejectFail':'Reject failed',
|
|
2230
|
+
'toast.groupCreated':'Group created',
|
|
2231
|
+
'toast.groupDeleted':'Group deleted',
|
|
2232
|
+
'toast.memberAdded':'Member added',
|
|
2233
|
+
'toast.memberRemoved':'Member removed',
|
|
2234
|
+
'toast.taskRemoved':'Task removed',
|
|
2235
|
+
'toast.skillRemoved':'Skill removed',
|
|
2236
|
+
'toast.memoryRemoved':'Memory removed',
|
|
2237
|
+
'toast.createFail':'Create failed',
|
|
2238
|
+
'toast.deleteFail':'Delete failed',
|
|
2239
|
+
'toast.addFail':'Add failed',
|
|
2240
|
+
'toast.removeFail':'Remove failed',
|
|
2241
|
+
'toast.groupNameRequired':'Group name is required',
|
|
2242
|
+
'confirm.rejectUser':'Reject this user?',
|
|
2243
|
+
'confirm.removeGroupMember':'Remove this member from the group?',
|
|
2244
|
+
'confirm.removeMember':'Remove this member?',
|
|
2245
|
+
'confirm.deleteGroup':'Delete group "{name}"? Members will be removed.',
|
|
2246
|
+
'confirm.deleteGroupShort':'Delete group "{name}"?',
|
|
2247
|
+
'confirm.removeTask':'Remove shared task "{name}" from Hub? This cannot be undone.',
|
|
2248
|
+
'confirm.removeSkill':'Remove shared skill "{name}" from Hub? This cannot be undone.',
|
|
2249
|
+
'confirm.removeMemory':'Remove shared memory "{name}" from Hub? This cannot be undone.',
|
|
2250
|
+
'sharing.disabled':'Sharing disabled',
|
|
2251
|
+
'sharing.disabled.hint':'Enable sharing in plugin config to connect a Hub.',
|
|
2252
|
+
'sharing.hubAdmin':'Hub Admin',
|
|
2253
|
+
'sharing.client':'Client',
|
|
2254
|
+
'sharing.hubMode':'Hub mode',
|
|
2255
|
+
'sharing.hubMode.status':'Status: not connected to self',
|
|
2256
|
+
'sharing.hubMode.hint':'Configure sharing.client with hubAddress and userToken pointing to this Hub to enable admin UI.',
|
|
2257
|
+
'sharing.clientConfigured':'Client configured',
|
|
2258
|
+
'sharing.clientDisconnected':'Status: disconnected',
|
|
2259
|
+
'sharing.clientDisconnected.hint':'Viewer will keep showing local data; Hub actions may fail until the connection is restored.',
|
|
2260
|
+
'sharing.clientNotConfigured':'Client not configured',
|
|
2261
|
+
'sharing.clientNotConfigured.hint':'Set hubAddress and userToken in sharing.client to enable team features.',
|
|
2262
|
+
'sharing.settingsDisabled':'Sharing is disabled.',
|
|
2263
|
+
'sharing.settingsDisabled.hint':'Enable sharing in config to use Hub memory and skill collaboration.',
|
|
2264
|
+
'sharing.noTeam':'No team connection.',
|
|
2265
|
+
'sharing.adminUnavailable':'Admin tools unavailable.',
|
|
2266
|
+
'sharing.adminEnabled':'Admin controls enabled',
|
|
2267
|
+
'sharing.adminPendingHint':'Pending users will appear below.',
|
|
2268
|
+
'sharing.notAdmin':'Current user is not an admin.',
|
|
2269
|
+
'sharing.pendingLoadFail':'Failed to load pending users: ',
|
|
2270
|
+
'sharing.noPending':'No pending users.',
|
|
2271
|
+
'sharing.manageGroups':'Manage Groups',
|
|
2272
|
+
'sharing.openAdmin':'Open Admin Panel',
|
|
2273
|
+
'sharing.saveUsername':'Save',
|
|
2274
|
+
'sharing.username.invalid':'Username must be 2-32 characters',
|
|
2275
|
+
'sharing.username.taken':'Username already taken',
|
|
2276
|
+
'sharing.username.updated':'Username updated',
|
|
2277
|
+
'sharing.username.error':'Failed to update username',
|
|
2278
|
+
'sharing.hubNotConfigured':'Hub is running but client connection is not configured.',
|
|
2279
|
+
'sharing.hubNotConfigured.hint':'Add sharing.client.hubAddress and sharing.client.userToken pointing to this Hub to enable the admin interface.',
|
|
2280
|
+
'sharing.notConnected':'Not connected to Hub.',
|
|
2281
|
+
'sharing.role':'Role:',
|
|
2282
|
+
'sharing.mode':'Mode:',
|
|
2283
|
+
'sharing.role.hub':'Server (hosting the Hub)',
|
|
2284
|
+
'sharing.role.client':'Member (connected to Hub)',
|
|
2285
|
+
'sharing.clientConfiguredLabel':'Client configured:',
|
|
2286
|
+
'sharing.configuredHub':'Configured Hub:',
|
|
2287
|
+
'sharing.connected':'Connected:',
|
|
2288
|
+
'sharing.yes':'yes',
|
|
2289
|
+
'sharing.no':'no',
|
|
2290
|
+
'sharing.user':'User:',
|
|
2291
|
+
'sharing.team':'Team:',
|
|
2292
|
+
'sharing.groups':'Groups:',
|
|
2293
|
+
'sharing.loading':'Loading...',
|
|
2294
|
+
'sharing.loadingGroups':'Loading groups...',
|
|
2295
|
+
'sharing.noGroupsYet':'No groups yet.',
|
|
2296
|
+
'search.localResults':'Local Results',
|
|
2297
|
+
'search.hubResults':'Hub Results',
|
|
2298
|
+
'search.noLocal':'No local results.',
|
|
2299
|
+
'search.noHub':'No Hub results.',
|
|
2300
|
+
'search.viewDetail':'View Detail',
|
|
2301
|
+
'search.sharedMemory':'Shared Memory',
|
|
2302
|
+
'search.loadFailed':'Failed to load shared memory',
|
|
2303
|
+
'share.alreadyShared':'Shared',
|
|
2304
|
+
'share.shareBtn':'Share',
|
|
2305
|
+
'share.updateBtn':'Update',
|
|
2306
|
+
'share.unshareBtn':'Unshare',
|
|
2307
|
+
'toast.taskShared':'Task shared',
|
|
2308
|
+
'toast.taskShareFail':'Task share failed',
|
|
2309
|
+
'toast.taskUnshared':'Task unshared',
|
|
2310
|
+
'toast.taskUnshareFail':'Task unshare failed',
|
|
2311
|
+
'toast.memoryShared':'Memory shared',
|
|
2312
|
+
'toast.memoryShareFail':'Memory share failed',
|
|
2313
|
+
'toast.memoryUnshared':'Memory unshared',
|
|
2314
|
+
'toast.memoryUnshareFail':'Memory unshare failed',
|
|
2315
|
+
'toast.skillShared':'Skill shared',
|
|
2316
|
+
'toast.skillShareFail':'Skill share failed',
|
|
2317
|
+
'toast.skillUnshared':'Skill unshared',
|
|
2318
|
+
'toast.skillUnshareFail':'Skill unshare failed',
|
|
2319
|
+
'share.memoryVisibilityPrompt':'Share visibility (public or group):',
|
|
2320
|
+
'share.memoryUnshareConfirm':'Unshare this memory?',
|
|
2321
|
+
'share.group':'Group',
|
|
2322
|
+
'share.public':'Public',
|
|
2323
|
+
'toast.skillPulled':'Skill pulled to local storage',
|
|
2324
|
+
'toast.skillPullFail':'Skill pull failed',
|
|
1677
2325
|
'task.edit':'Edit',
|
|
1678
2326
|
'task.delete':'Delete',
|
|
1679
2327
|
'task.save':'Save',
|
|
@@ -1702,7 +2350,7 @@ const I18N={
|
|
|
1702
2350
|
'update.dismiss':'Dismiss'
|
|
1703
2351
|
},
|
|
1704
2352
|
zh:{
|
|
1705
|
-
'title':'
|
|
2353
|
+
'title':'MemOS 记忆',
|
|
1706
2354
|
'subtitle':'由 MemOS 驱动',
|
|
1707
2355
|
'setup.desc':'设置密码以保护你的记忆数据',
|
|
1708
2356
|
'setup.pw':'输入密码(至少4位)',
|
|
@@ -1742,6 +2390,14 @@ const I18N={
|
|
|
1742
2390
|
'skills.active':'生效中',
|
|
1743
2391
|
'skills.installed':'已安装',
|
|
1744
2392
|
'skills.public':'公开',
|
|
2393
|
+
'skills.search.placeholder':'搜索技能...',
|
|
2394
|
+
'skills.search.local':'本地',
|
|
2395
|
+
'skills.search.noresult':'未找到匹配的技能',
|
|
2396
|
+
'skills.load.error':'加载技能失败',
|
|
2397
|
+
'skills.hub.title':'\u{1F310} Hub 共享技能',
|
|
2398
|
+
'scope.local':'本地',
|
|
2399
|
+
'scope.group':'团队',
|
|
2400
|
+
'scope.all':'全部',
|
|
1745
2401
|
'skills.visibility.public':'公开',
|
|
1746
2402
|
'skills.visibility.private':'私有',
|
|
1747
2403
|
'skills.setPublic':'设为公开',
|
|
@@ -1780,6 +2436,7 @@ const I18N={
|
|
|
1780
2436
|
'filter.newest':'最新优先',
|
|
1781
2437
|
'filter.oldest':'最早优先',
|
|
1782
2438
|
'filter.allowners':'所有归属',
|
|
2439
|
+
'filter.allsessions':'全部会话',
|
|
1783
2440
|
'filter.public':'公开',
|
|
1784
2441
|
'filter.private':'私有',
|
|
1785
2442
|
'filter.allvisibility':'所有可见性',
|
|
@@ -1865,6 +2522,15 @@ const I18N={
|
|
|
1865
2522
|
'settings.summarizer':'摘要模型',
|
|
1866
2523
|
'settings.skill':'技能进化',
|
|
1867
2524
|
'settings.general':'通用设置',
|
|
2525
|
+
'settings.embedding.desc':'向量嵌入模型,用于记忆检索和语义搜索',
|
|
2526
|
+
'settings.summarizer.desc':'大语言模型,用于记忆摘要、去重和分析',
|
|
2527
|
+
'settings.skill.desc':'从对话模式中自动提取可复用的技能',
|
|
2528
|
+
'settings.hub.desc':'与团队共享记忆、任务和技能',
|
|
2529
|
+
'settings.general.desc':'系统状态、端口和数据统计',
|
|
2530
|
+
'settings.hostproxy.embedding':'复用网关模型',
|
|
2531
|
+
'settings.hostproxy.completion':'复用网关模型',
|
|
2532
|
+
'settings.hostproxy.skill':'复用网关模型',
|
|
2533
|
+
'settings.hostproxy.hint.short':'复用网关已有的模型配置',
|
|
1868
2534
|
'settings.provider':'服务商',
|
|
1869
2535
|
'settings.model':'模型',
|
|
1870
2536
|
'settings.temperature':'温度',
|
|
@@ -1873,12 +2539,12 @@ const I18N={
|
|
|
1873
2539
|
'settings.skill.confidence':'最低置信度',
|
|
1874
2540
|
'settings.skill.minchunks':'最少记忆片段',
|
|
1875
2541
|
'settings.skill.model':'技能专用模型',
|
|
1876
|
-
'settings.skill.model.hint':'
|
|
2542
|
+
'settings.skill.model.hint':'不配置则复用摘要模型。如需更高质量可单独指定。',
|
|
1877
2543
|
'settings.optional':'可选',
|
|
1878
2544
|
'settings.skill.usemain':'使用主摘要模型',
|
|
1879
2545
|
'settings.telemetry':'数据统计',
|
|
1880
2546
|
'settings.telemetry.enabled':'启用匿名数据统计',
|
|
1881
|
-
'settings.telemetry.hint':'
|
|
2547
|
+
'settings.telemetry.hint':'仅收集工具名称、响应时间和版本号,不涉及任何记忆内容或个人数据。',
|
|
1882
2548
|
'settings.viewerport':'Viewer 端口',
|
|
1883
2549
|
'settings.viewerport.hint':'修改后需重启网关生效',
|
|
1884
2550
|
'settings.test':'测试连接',
|
|
@@ -1975,7 +2641,7 @@ const I18N={
|
|
|
1975
2641
|
'skills.related':'关联任务',
|
|
1976
2642
|
'skills.download':'\u2B07 下载',
|
|
1977
2643
|
'skills.installed.badge':'已安装',
|
|
1978
|
-
'skills.empty':'
|
|
2644
|
+
'skills.empty':'暂无技能。技能会从已完成的任务中自动提炼生成。',
|
|
1979
2645
|
'skills.loading':'加载中...',
|
|
1980
2646
|
'skills.error':'加载技能失败',
|
|
1981
2647
|
'skills.error.detail':'加载技能失败:',
|
|
@@ -1996,6 +2662,222 @@ const I18N={
|
|
|
1996
2662
|
'tasks.error':'出错了',
|
|
1997
2663
|
'tasks.error.detail':'加载任务详情失败',
|
|
1998
2664
|
'tasks.untitled.related':'未命名',
|
|
2665
|
+
'tab.admin':'\u{1F6E1} 管理',
|
|
2666
|
+
'settings.hub':'Hub 与团队',
|
|
2667
|
+
'settings.hub.enable':'启用 Hub 共享',
|
|
2668
|
+
'settings.hub.enable.hint':'关闭时仅本地使用,不影响其他功能。',
|
|
2669
|
+
'settings.hub.enable.label':'启用 Hub 共享',
|
|
2670
|
+
'settings.hub.role':'角色',
|
|
2671
|
+
'settings.hub.role.hub':'Hub(服务端)',
|
|
2672
|
+
'settings.hub.role.client':'Client(连接到 Hub)',
|
|
2673
|
+
'settings.hub.role.hint':'Hub = 本机做服务端;Client = 连接别人的服务端。两种模式互斥,不能同时使用。',
|
|
2674
|
+
'settings.hub.port':'Hub 端口',
|
|
2675
|
+
'settings.hub.port.hint':'Hub 服务端口,默认 18800',
|
|
2676
|
+
'settings.hub.teamName':'团队名称',
|
|
2677
|
+
'settings.hub.teamName.hint':'你的团队显示名称',
|
|
2678
|
+
'settings.hub.teamToken':'团队令牌',
|
|
2679
|
+
'settings.hub.teamToken.hint':'自动生成的密钥,点击可复制。请将此令牌分享给团队成员。',
|
|
2680
|
+
'settings.hub.tokenCopied':'团队令牌已复制!',
|
|
2681
|
+
'settings.hub.hubSteps.title':'快速配置(3 步)',
|
|
2682
|
+
'settings.hub.hubSteps.s1':'填写下方团队名称(或保持默认)',
|
|
2683
|
+
'settings.hub.hubSteps.s2':'点击"保存设置",然后重启 OpenClaw 网关',
|
|
2684
|
+
'settings.hub.hubSteps.s3':'将下方的 Hub 地址和团队令牌分享给团队成员',
|
|
2685
|
+
'settings.hub.clientSteps.title':'快速配置(3 步)',
|
|
2686
|
+
'settings.hub.clientSteps.s1':'向 Hub 管理员获取 Hub 地址和团队令牌',
|
|
2687
|
+
'settings.hub.clientSteps.s2':'填入下方,点击"测试连接"验证连通性',
|
|
2688
|
+
'settings.hub.clientSteps.s3':'点击"保存设置",然后重启 OpenClaw 网关即可连接',
|
|
2689
|
+
'settings.hub.shareInfo.title':'请将以下信息分享给团队成员:',
|
|
2690
|
+
'settings.hub.shareInfo.yourIP':'你的IP',
|
|
2691
|
+
'settings.hub.shareInfo.clickCopy':'点击复制',
|
|
2692
|
+
'settings.hub.restartAlert':'Hub 共享配置已保存!请重启 OpenClaw 网关使配置生效。\\n\\n执行命令:openclaw gateway stop && openclaw gateway',
|
|
2693
|
+
'settings.hub.hubAddress':'Hub 地址',
|
|
2694
|
+
'settings.hub.hubAddress.hint':'Hub 服务器地址,如 192.168.1.100:18800',
|
|
2695
|
+
'settings.hub.teamTokenClient':'团队令牌',
|
|
2696
|
+
'settings.hub.teamTokenClient.hint':'向 Hub 管理员获取此令牌以加入团队',
|
|
2697
|
+
'settings.hub.userToken':'用户令牌',
|
|
2698
|
+
'settings.hub.userToken.hint':'通常在加入团队后自动获取,无需手动填写。',
|
|
2699
|
+
'settings.hub.testConnection':'测试连接',
|
|
2700
|
+
'settings.hub.test.noAddr':'请先输入 Hub 地址',
|
|
2701
|
+
'settings.hub.test.testing':'测试中...',
|
|
2702
|
+
'settings.hub.test.ok':'连接成功',
|
|
2703
|
+
'settings.hub.test.fail':'连接失败',
|
|
2704
|
+
'settings.hub.connection':'连接状态',
|
|
2705
|
+
'settings.hub.team':'团队与分组',
|
|
2706
|
+
'settings.hub.adminPending':'管理员待审用户',
|
|
2707
|
+
'sidebar.hub':'\u{1F310} 团队共享',
|
|
2708
|
+
'sharing.sidebar.connected':'已连接',
|
|
2709
|
+
'sharing.sidebar.disconnected':'已断开',
|
|
2710
|
+
'sharing.sidebar.pending':'等待审核',
|
|
2711
|
+
'sharing.sidebar.rejected':'已拒绝',
|
|
2712
|
+
'sharing.sidebar.starting':'启动中...',
|
|
2713
|
+
'sharing.sidebar.notConfigured':'未配置',
|
|
2714
|
+
'sharing.sidebar.identity':'身份:',
|
|
2715
|
+
'sharing.sidebar.admin':'管理员',
|
|
2716
|
+
'sharing.sidebar.targetHub':'目标 Hub:',
|
|
2717
|
+
'sharing.pendingApproval.hint':'加入申请已提交,请等待 Hub 管理员审核通过。',
|
|
2718
|
+
'sharing.rejected.hint':'您的加入申请已被 Hub 管理员拒绝,请联系管理员或重新申请。',
|
|
2719
|
+
'sharing.retryJoin':'重新申请',
|
|
2720
|
+
'sharing.retryJoin.hint':'清除本地连接数据并重新提交加入申请',
|
|
2721
|
+
'sharing.retryJoin.confirm':'这将清除当前连接数据并重新提交加入申请,是否继续?',
|
|
2722
|
+
'sharing.retryJoin.success':'加入申请已重新提交,请等待管理员审核。',
|
|
2723
|
+
'sharing.retryJoin.fail':'重新申请失败',
|
|
2724
|
+
'sharing.cannotJoinSelf':'不能加入自己的 Hub,请输入远程 Hub 地址。',
|
|
2725
|
+
'scope.hub':'Hub',
|
|
2726
|
+
'memory.detail.title':'记忆详情',
|
|
2727
|
+
'memory.detail.loading':'加载中...',
|
|
2728
|
+
'memory.detail.notFound':'未找到该记忆',
|
|
2729
|
+
'memory.detail.copyId':'点击复制 ID',
|
|
2730
|
+
'memory.detail.created':'创建于 ',
|
|
2731
|
+
'memory.detail.updated':'更新于 ',
|
|
2732
|
+
'memory.detail.viewTarget':'查看目标: ',
|
|
2733
|
+
'admin.title':'Hub 管理面板',
|
|
2734
|
+
'admin.subtitle':'管理团队成员和共享资源',
|
|
2735
|
+
'admin.refresh':'\u21BB 刷新',
|
|
2736
|
+
'admin.tab.users':'用户',
|
|
2737
|
+
'admin.tab.groups':'分组',
|
|
2738
|
+
'admin.tab.memories':'共享任务',
|
|
2739
|
+
'admin.tab.skills':'共享技能',
|
|
2740
|
+
'admin.stat.activeUsers':'活跃用户',
|
|
2741
|
+
'admin.stat.pending':'待审核',
|
|
2742
|
+
'admin.stat.groups':'分组',
|
|
2743
|
+
'admin.stat.sharedTasks':'共享任务',
|
|
2744
|
+
'admin.stat.sharedSkills':'共享技能',
|
|
2745
|
+
'admin.stat.sharedMemories':'共享记忆',
|
|
2746
|
+
'admin.pendingApproval':'待审批',
|
|
2747
|
+
'admin.activeUsers':'活跃用户',
|
|
2748
|
+
'admin.noActiveUsers':'暂无活跃用户。',
|
|
2749
|
+
'admin.approve':'批准',
|
|
2750
|
+
'admin.reject':'拒绝',
|
|
2751
|
+
'admin.device':'设备:',
|
|
2752
|
+
'admin.groups':'分组',
|
|
2753
|
+
'admin.newGroup':'+ 新建分组',
|
|
2754
|
+
'admin.groupName':'分组名称',
|
|
2755
|
+
'admin.groupDesc':'描述(可选)',
|
|
2756
|
+
'admin.create':'创建',
|
|
2757
|
+
'admin.cancel':'取消',
|
|
2758
|
+
'admin.delete':'删除',
|
|
2759
|
+
'admin.members':'成员',
|
|
2760
|
+
'admin.noGroups':'暂无分组。',
|
|
2761
|
+
'admin.noMembers':'暂无成员。',
|
|
2762
|
+
'admin.add':'添加',
|
|
2763
|
+
'admin.remove':'移除',
|
|
2764
|
+
'admin.sharedTasks':'共享任务',
|
|
2765
|
+
'admin.noSharedTasks':'Hub 上暂无共享任务。',
|
|
2766
|
+
'admin.owner':'归属:',
|
|
2767
|
+
'admin.group':'分组:',
|
|
2768
|
+
'admin.chunks':'记忆片段:',
|
|
2769
|
+
'admin.updated':'更新于:',
|
|
2770
|
+
'admin.sharedSkills':'共享技能',
|
|
2771
|
+
'admin.noSharedSkills':'Hub 上暂无共享技能。',
|
|
2772
|
+
'admin.sharedMemories':'共享记忆',
|
|
2773
|
+
'admin.noSharedMemories':'Hub 上暂无共享记忆。',
|
|
2774
|
+
'admin.tab.sharedMemories':'共享记忆',
|
|
2775
|
+
'admin.version':'v',
|
|
2776
|
+
'admin.quality':'质量:',
|
|
2777
|
+
'admin.membersCount':'成员({n}):',
|
|
2778
|
+
'admin.noMembersYet':'暂无成员。',
|
|
2779
|
+
'admin.loadFailed':'加载管理数据失败:',
|
|
2780
|
+
'admin.noPermission':'您没有管理员权限,无法访问此面板。',
|
|
2781
|
+
'admin.groupsFailed':'加载分组失败:',
|
|
2782
|
+
'toast.userApproved':'用户已批准',
|
|
2783
|
+
'toast.userRejected':'用户已拒绝',
|
|
2784
|
+
'toast.approveFail':'批准失败',
|
|
2785
|
+
'toast.rejectFail':'拒绝失败',
|
|
2786
|
+
'toast.groupCreated':'分组已创建',
|
|
2787
|
+
'toast.groupDeleted':'分组已删除',
|
|
2788
|
+
'toast.memberAdded':'成员已添加',
|
|
2789
|
+
'toast.memberRemoved':'成员已移除',
|
|
2790
|
+
'toast.taskRemoved':'任务已移除',
|
|
2791
|
+
'toast.skillRemoved':'技能已移除',
|
|
2792
|
+
'toast.memoryRemoved':'记忆已移除',
|
|
2793
|
+
'toast.createFail':'创建失败',
|
|
2794
|
+
'toast.deleteFail':'删除失败',
|
|
2795
|
+
'toast.addFail':'添加失败',
|
|
2796
|
+
'toast.removeFail':'移除失败',
|
|
2797
|
+
'toast.groupNameRequired':'请输入分组名称',
|
|
2798
|
+
'confirm.rejectUser':'确定要拒绝此用户吗?',
|
|
2799
|
+
'confirm.removeGroupMember':'确定要将此成员移出分组吗?',
|
|
2800
|
+
'confirm.removeMember':'确定要移除此成员吗?',
|
|
2801
|
+
'confirm.deleteGroup':'确定要删除分组「{name}」吗?成员将被移除。',
|
|
2802
|
+
'confirm.deleteGroupShort':'确定要删除分组「{name}」吗?',
|
|
2803
|
+
'confirm.removeTask':'确定要从 Hub 移除共享任务「{name}」吗?此操作不可撤销。',
|
|
2804
|
+
'confirm.removeSkill':'确定要从 Hub 移除共享技能「{name}」吗?此操作不可撤销。',
|
|
2805
|
+
'confirm.removeMemory':'确定要从 Hub 移除共享记忆「{name}」吗?此操作不可撤销。',
|
|
2806
|
+
'sharing.disabled':'共享已禁用',
|
|
2807
|
+
'sharing.disabled.hint':'在插件配置中启用共享以连接 Hub。',
|
|
2808
|
+
'sharing.hubAdmin':'Hub 管理员',
|
|
2809
|
+
'sharing.client':'客户端',
|
|
2810
|
+
'sharing.hubMode':'Hub 模式',
|
|
2811
|
+
'sharing.hubMode.status':'状态:未连接到自身',
|
|
2812
|
+
'sharing.hubMode.hint':'配置 sharing.client 的 hubAddress 和 userToken 指向此 Hub 以启用管理界面。',
|
|
2813
|
+
'sharing.clientConfigured':'客户端已配置',
|
|
2814
|
+
'sharing.clientDisconnected':'状态:已断开',
|
|
2815
|
+
'sharing.clientDisconnected.hint':'查看器将继续显示本地数据;Hub 操作可能在连接恢复前失败。',
|
|
2816
|
+
'sharing.clientNotConfigured':'客户端未配置',
|
|
2817
|
+
'sharing.clientNotConfigured.hint':'设置 sharing.client 中的 hubAddress 和 userToken 以启用团队功能。',
|
|
2818
|
+
'sharing.settingsDisabled':'共享已禁用。',
|
|
2819
|
+
'sharing.settingsDisabled.hint':'在配置中启用共享以使用 Hub 记忆和技能协作。',
|
|
2820
|
+
'sharing.noTeam':'无团队连接。',
|
|
2821
|
+
'sharing.adminUnavailable':'管理工具不可用。',
|
|
2822
|
+
'sharing.adminEnabled':'管理控制已启用',
|
|
2823
|
+
'sharing.adminPendingHint':'待审用户将显示在下方。',
|
|
2824
|
+
'sharing.notAdmin':'当前用户不是管理员。',
|
|
2825
|
+
'sharing.pendingLoadFail':'加载待审用户失败:',
|
|
2826
|
+
'sharing.noPending':'暂无待审用户。',
|
|
2827
|
+
'sharing.manageGroups':'管理分组',
|
|
2828
|
+
'sharing.openAdmin':'打开管理面板',
|
|
2829
|
+
'sharing.saveUsername':'保存',
|
|
2830
|
+
'sharing.username.invalid':'用户名需 2-32 个字符',
|
|
2831
|
+
'sharing.username.taken':'用户名已被占用',
|
|
2832
|
+
'sharing.username.updated':'用户名已更新',
|
|
2833
|
+
'sharing.username.error':'更新用户名失败',
|
|
2834
|
+
'sharing.hubNotConfigured':'Hub 正在运行,但客户端连接未配置。',
|
|
2835
|
+
'sharing.hubNotConfigured.hint':'添加 sharing.client.hubAddress 和 sharing.client.userToken 指向此 Hub 以启用管理界面。',
|
|
2836
|
+
'sharing.notConnected':'未连接到 Hub。',
|
|
2837
|
+
'sharing.role':'角色:',
|
|
2838
|
+
'sharing.mode':'身份:',
|
|
2839
|
+
'sharing.role.hub':'服务端(Hub 主机)',
|
|
2840
|
+
'sharing.role.client':'成员(连接到 Hub)',
|
|
2841
|
+
'sharing.clientConfiguredLabel':'客户端已配置:',
|
|
2842
|
+
'sharing.configuredHub':'配置的 Hub:',
|
|
2843
|
+
'sharing.connected':'已连接:',
|
|
2844
|
+
'sharing.yes':'是',
|
|
2845
|
+
'sharing.no':'否',
|
|
2846
|
+
'sharing.user':'用户:',
|
|
2847
|
+
'sharing.team':'团队:',
|
|
2848
|
+
'sharing.groups':'分组:',
|
|
2849
|
+
'sharing.loading':'加载中...',
|
|
2850
|
+
'sharing.loadingGroups':'正在加载分组...',
|
|
2851
|
+
'sharing.noGroupsYet':'暂无分组。',
|
|
2852
|
+
'search.localResults':'本地结果',
|
|
2853
|
+
'search.hubResults':'Hub 结果',
|
|
2854
|
+
'search.noLocal':'无本地结果。',
|
|
2855
|
+
'search.noHub':'无 Hub 结果。',
|
|
2856
|
+
'search.viewDetail':'查看详情',
|
|
2857
|
+
'search.sharedMemory':'共享记忆',
|
|
2858
|
+
'search.loadFailed':'加载共享记忆失败',
|
|
2859
|
+
'share.alreadyShared':'已共享',
|
|
2860
|
+
'share.shareBtn':'共享',
|
|
2861
|
+
'share.updateBtn':'更新共享',
|
|
2862
|
+
'share.unshareBtn':'取消共享',
|
|
2863
|
+
'toast.taskShared':'任务已共享',
|
|
2864
|
+
'toast.taskShareFail':'任务共享失败',
|
|
2865
|
+
'toast.taskUnshared':'任务已取消共享',
|
|
2866
|
+
'toast.taskUnshareFail':'取消共享失败',
|
|
2867
|
+
'toast.memoryShared':'记忆已共享',
|
|
2868
|
+
'toast.memoryShareFail':'记忆共享失败',
|
|
2869
|
+
'toast.memoryUnshared':'记忆已取消共享',
|
|
2870
|
+
'toast.memoryUnshareFail':'记忆取消共享失败',
|
|
2871
|
+
'toast.skillShared':'技能已共享',
|
|
2872
|
+
'toast.skillShareFail':'技能共享失败',
|
|
2873
|
+
'toast.skillUnshared':'技能已取消共享',
|
|
2874
|
+
'toast.skillUnshareFail':'技能取消共享失败',
|
|
2875
|
+
'share.memoryVisibilityPrompt':'共享可见性(public 或 group):',
|
|
2876
|
+
'share.memoryUnshareConfirm':'取消共享此记忆?',
|
|
2877
|
+
'share.group':'团队',
|
|
2878
|
+
'share.public':'公开',
|
|
2879
|
+
'toast.skillPulled':'技能已拉取到本地',
|
|
2880
|
+
'toast.skillPullFail':'技能拉取失败',
|
|
1999
2881
|
'task.edit':'编辑',
|
|
2000
2882
|
'task.delete':'删除',
|
|
2001
2883
|
'task.save':'保存',
|
|
@@ -2123,11 +3005,114 @@ async function doReset(){
|
|
|
2123
3005
|
else{err.textContent=d.error||t('reset.err.fail')}
|
|
2124
3006
|
}
|
|
2125
3007
|
|
|
3008
|
+
var _sharingRole='client';
|
|
3009
|
+
function _genToken(len){
|
|
3010
|
+
var a=new Uint8Array(len||18);crypto.getRandomValues(a);
|
|
3011
|
+
return btoa(String.fromCharCode.apply(null,a)).replace(/\\+/g,'-').replace(/\\//g,'_').replace(/=+$/,'');
|
|
3012
|
+
}
|
|
3013
|
+
function onSharingToggle(){
|
|
3014
|
+
var on=document.getElementById('cfgSharingEnabled').checked;
|
|
3015
|
+
document.getElementById('sharingConfigPanel').style.display=on?'block':'none';
|
|
3016
|
+
if(on) selectSharingRole(_sharingRole);
|
|
3017
|
+
}
|
|
3018
|
+
function selectSharingRole(role){
|
|
3019
|
+
_sharingRole=role;
|
|
3020
|
+
document.getElementById('btnRoleHub').className='btn btn-sm'+(role==='hub'?' btn-primary':'');
|
|
3021
|
+
document.getElementById('btnRoleClient').className='btn btn-sm'+(role==='client'?' btn-primary':'');
|
|
3022
|
+
document.getElementById('hubModeConfig').style.display=role==='hub'?'block':'none';
|
|
3023
|
+
document.getElementById('clientModeConfig').style.display=role==='client'?'block':'none';
|
|
3024
|
+
var sp=document.getElementById('sharingStatusPanel');
|
|
3025
|
+
var tp=document.getElementById('sharingTeamPanel');
|
|
3026
|
+
var ap=document.getElementById('sharingAdminPanel');
|
|
3027
|
+
if(role==='client'){
|
|
3028
|
+
if(sp) sp.style.display='none';
|
|
3029
|
+
if(tp) tp.style.display='none';
|
|
3030
|
+
if(ap) ap.style.display='none';
|
|
3031
|
+
var adminTab=document.querySelector('.tab[data-view="admin"]');
|
|
3032
|
+
if(adminTab) adminTab.style.display='none';
|
|
3033
|
+
}else{
|
|
3034
|
+
if(sp) sp.style.display='';
|
|
3035
|
+
if(tp) tp.style.display='';
|
|
3036
|
+
if(ap) ap.style.display='';
|
|
3037
|
+
}
|
|
3038
|
+
if(role==='hub'){
|
|
3039
|
+
var tk=document.getElementById('cfgHubTeamToken');
|
|
3040
|
+
if(!tk.value.trim()) tk.value=_genToken(18);
|
|
3041
|
+
var tn=document.getElementById('cfgHubTeamName');
|
|
3042
|
+
if(!tn.value.trim()) tn.value='My Team';
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
var _cachedLocalIP='';
|
|
3046
|
+
function updateHubShareInfo(){
|
|
3047
|
+
var panel=document.getElementById('hubShareInfo');
|
|
3048
|
+
var content=document.getElementById('hubShareInfoContent');
|
|
3049
|
+
if(!panel||!content) return;
|
|
3050
|
+
var tokenEl=document.getElementById('cfgHubTeamToken');
|
|
3051
|
+
var token=tokenEl?tokenEl.value.trim():'';
|
|
3052
|
+
var port=document.getElementById('cfgHubPort').value.trim()||'18800';
|
|
3053
|
+
if(!token||_sharingRole!=='hub'){panel.style.display='none';return;}
|
|
3054
|
+
panel.style.display='block';
|
|
3055
|
+
var cpStyle='cursor:pointer;background:rgba(99,102,241,.06);border:1px solid rgba(99,102,241,.15);border-radius:6px;padding:4px 10px;font-family:var(--mono);font-size:12px;color:var(--text);transition:all .15s;user-select:all';
|
|
3056
|
+
var renderShare=function(ip){
|
|
3057
|
+
var addr=ip?(ip+':'+esc(port)):('<'+t('settings.hub.shareInfo.yourIP')+'>:'+esc(port));
|
|
3058
|
+
var tip=t('settings.hub.shareInfo.clickCopy');
|
|
3059
|
+
content.innerHTML=
|
|
3060
|
+
'<span style="font-size:11px;color:var(--text-muted);font-weight:500">Hub '+t('settings.hub.hubAddress')+'</span>'+
|
|
3061
|
+
'<span style="'+cpStyle+'" onclick="navigator.clipboard.writeText(this.textContent);toast('Copied!','success')" title="'+tip+'">'+addr+'</span>'+
|
|
3062
|
+
'<span style="font-size:11px;color:var(--text-muted);font-weight:500">Team Token</span>'+
|
|
3063
|
+
'<span style="'+cpStyle+'" onclick="navigator.clipboard.writeText(this.textContent);toast('Copied!','success')" title="'+tip+'">'+esc(token)+'</span>';
|
|
3064
|
+
};
|
|
3065
|
+
if(_cachedLocalIP){renderShare(_cachedLocalIP);return;}
|
|
3066
|
+
renderShare('');
|
|
3067
|
+
fetch('/api/local-ips').then(function(r){return r.json()}).then(function(d){
|
|
3068
|
+
if(d.ips&&d.ips.length>0){_cachedLocalIP=d.ips[0];renderShare(_cachedLocalIP);}
|
|
3069
|
+
}).catch(function(){});
|
|
3070
|
+
}
|
|
3071
|
+
async function testHubConnection(){
|
|
3072
|
+
var btn=document.getElementById('btnTestHubConn');
|
|
3073
|
+
var result=document.getElementById('hubConnTestResult');
|
|
3074
|
+
var addr=document.getElementById('cfgClientHubAddress').value.trim();
|
|
3075
|
+
if(!addr){result.innerHTML='<span style="color:var(--rose)">\u274C '+t('settings.hub.test.noAddr')+'</span>';return;}
|
|
3076
|
+
btn.disabled=true;result.innerHTML=t('settings.hub.test.testing');
|
|
3077
|
+
try{
|
|
3078
|
+
var ipsData=await fetch('/api/local-ips').then(function(r){return r.json();});
|
|
3079
|
+
var localAddrs=['127.0.0.1','localhost','0.0.0.0'].concat(ipsData.ips||[]);
|
|
3080
|
+
var parsed=new URL(addr.indexOf('://')>-1?addr:'http://'+addr);
|
|
3081
|
+
if(localAddrs.indexOf(parsed.hostname)>=0){
|
|
3082
|
+
result.innerHTML='<span style="color:var(--rose)">\u274C '+t('sharing.cannotJoinSelf')+'</span>';
|
|
3083
|
+
btn.disabled=false;return;
|
|
3084
|
+
}
|
|
3085
|
+
}catch(e){}
|
|
3086
|
+
try{
|
|
3087
|
+
var url=addr.match(/^https?:\\/\\//)?addr:'http://'+addr;
|
|
3088
|
+
url=url.replace(/\\/+$/,'');
|
|
3089
|
+
var r=await fetch('/api/sharing/test-hub',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({hubUrl:url})});
|
|
3090
|
+
var d=await r.json();
|
|
3091
|
+
if(d.ok){
|
|
3092
|
+
result.innerHTML='<span style="color:var(--green)">\u2705 '+t('settings.hub.test.ok')+(d.teamName?' — '+esc(d.teamName):'')+'</span>';
|
|
3093
|
+
}else{
|
|
3094
|
+
var errMsg=d.error==='cannot_join_self'?t('sharing.cannotJoinSelf'):(d.error||t('settings.hub.test.fail'));
|
|
3095
|
+
result.innerHTML='<span style="color:var(--rose)">\u274C '+errMsg+'</span>';
|
|
3096
|
+
}
|
|
3097
|
+
}catch(e){
|
|
3098
|
+
result.innerHTML='<span style="color:var(--rose)">\u274C '+esc(String(e))+'</span>';
|
|
3099
|
+
}finally{btn.disabled=false;}
|
|
3100
|
+
}
|
|
3101
|
+
|
|
2126
3102
|
function enterApp(){
|
|
2127
3103
|
document.getElementById('app').style.display='flex';
|
|
2128
3104
|
loadAll();
|
|
2129
3105
|
}
|
|
2130
3106
|
|
|
3107
|
+
function switchSettingsTab(tab,btn){
|
|
3108
|
+
document.querySelectorAll('.settings-tab-btn').forEach(function(b){b.classList.remove('active')});
|
|
3109
|
+
if(btn){btn.classList.add('active')}
|
|
3110
|
+
else{var b=document.querySelector('.settings-tab-btn[data-tab="'+tab+'"]');if(b)b.classList.add('active')}
|
|
3111
|
+
document.querySelectorAll('.settings-card[data-stab]').forEach(function(c){
|
|
3112
|
+
if(c.dataset.stab===tab){c.classList.add('stab-active')}else{c.classList.remove('stab-active')}
|
|
3113
|
+
});
|
|
3114
|
+
}
|
|
3115
|
+
|
|
2131
3116
|
function switchView(view){
|
|
2132
3117
|
document.querySelectorAll('.nav-tabs .tab').forEach(t=>t.classList.toggle('active',t.dataset.view===view));
|
|
2133
3118
|
const feedWrap=document.getElementById('feedWrap');
|
|
@@ -2137,6 +3122,7 @@ function switchView(view){
|
|
|
2137
3122
|
const logsView=document.getElementById('logsView');
|
|
2138
3123
|
const settingsView=document.getElementById('settingsView');
|
|
2139
3124
|
const migrateView=document.getElementById('migrateView');
|
|
3125
|
+
const adminView=document.getElementById('adminView');
|
|
2140
3126
|
const sidebar=document.getElementById('sidebar');
|
|
2141
3127
|
feedWrap.classList.add('hide');
|
|
2142
3128
|
analyticsView.classList.remove('show');
|
|
@@ -2145,19 +3131,17 @@ function switchView(view){
|
|
|
2145
3131
|
logsView.classList.remove('show');
|
|
2146
3132
|
settingsView.classList.remove('show');
|
|
2147
3133
|
migrateView.classList.remove('show');
|
|
2148
|
-
|
|
3134
|
+
if(adminView) adminView.classList.remove('show');
|
|
3135
|
+
var sessionSection=document.getElementById('sidebarSessionSection');
|
|
2149
3136
|
if(view==='memories'){
|
|
2150
3137
|
feedWrap.classList.remove('hide');
|
|
2151
|
-
sessionSection.style.visibility='';
|
|
2152
|
-
sessionSection.style.pointerEvents='';
|
|
3138
|
+
if(sessionSection){sessionSection.style.visibility='';sessionSection.style.pointerEvents='';}
|
|
2153
3139
|
} else if(view==='tasks'||view==='skills'){
|
|
2154
|
-
sessionSection.style.visibility='hidden';
|
|
2155
|
-
sessionSection.style.pointerEvents='none';
|
|
3140
|
+
if(sessionSection){sessionSection.style.visibility='hidden';sessionSection.style.pointerEvents='none';}
|
|
2156
3141
|
if(view==='tasks'){tasksView.classList.add('show');loadTasks();}
|
|
2157
3142
|
else{skillsView.classList.add('show');loadSkills();}
|
|
2158
3143
|
} else {
|
|
2159
|
-
sessionSection.style.visibility='hidden';
|
|
2160
|
-
sessionSection.style.pointerEvents='none';
|
|
3144
|
+
if(sessionSection){sessionSection.style.visibility='hidden';sessionSection.style.pointerEvents='none';}
|
|
2161
3145
|
if(view==='analytics'){
|
|
2162
3146
|
analyticsView.classList.add('show');
|
|
2163
3147
|
loadMetrics();
|
|
@@ -2171,8 +3155,979 @@ function switchView(view){
|
|
|
2171
3155
|
} else if(view==='import'){
|
|
2172
3156
|
migrateView.classList.add('show');
|
|
2173
3157
|
if(!window._migrateRunning) migrateScan(false);
|
|
3158
|
+
} else if(view==='admin'){
|
|
3159
|
+
if(adminView){adminView.classList.add('show');loadAdminData();}
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
|
|
3164
|
+
function onMemoryScopeChange(){
|
|
3165
|
+
memorySearchScope=document.getElementById('memorySearchScope')?.value||'local';
|
|
3166
|
+
if(document.getElementById('searchInput').value.trim()) doSearch(document.getElementById('searchInput').value);
|
|
3167
|
+
else if(memorySearchScope!=='local') { document.getElementById('sharingSearchMeta').textContent=''; loadHubMemories(); }
|
|
3168
|
+
else { document.getElementById('sharingSearchMeta').textContent=''; loadMemories(); }
|
|
3169
|
+
}
|
|
3170
|
+
|
|
3171
|
+
function onSkillScopeChange(){
|
|
3172
|
+
skillSearchScope=document.getElementById('skillSearchScope')?.value||'local';
|
|
3173
|
+
loadSkills();
|
|
3174
|
+
}
|
|
3175
|
+
|
|
3176
|
+
function onTaskScopeChange(){
|
|
3177
|
+
taskSearchScope=document.getElementById('taskSearchScope')?.value||'local';
|
|
3178
|
+
tasksPage=0;
|
|
3179
|
+
loadTasks();
|
|
3180
|
+
}
|
|
3181
|
+
|
|
3182
|
+
async function loadSharingStatus(forcePending){
|
|
3183
|
+
try{
|
|
3184
|
+
const r=await fetch('/api/sharing/status');
|
|
3185
|
+
const d=await r.json();
|
|
3186
|
+
sharingStatusCache=d;
|
|
3187
|
+
renderSharingSidebar(d);
|
|
3188
|
+
renderSharingSettings(d);
|
|
3189
|
+
if(forcePending && d && d.admin && d.admin.canManageUsers) loadSharingPendingUsers();
|
|
3190
|
+
}catch(e){
|
|
3191
|
+
renderSharingSidebar(null);
|
|
3192
|
+
renderSharingSettings(null);
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
|
|
3196
|
+
function renderSharingSidebar(data){
|
|
3197
|
+
var section=document.getElementById('sidebarSharingSection');
|
|
3198
|
+
var statusEl=document.getElementById('sharingSidebarStatus');
|
|
3199
|
+
var hintEl=document.getElementById('sharingSidebarHint');
|
|
3200
|
+
var badgeEl=document.getElementById('sharingSidebarConnBadge');
|
|
3201
|
+
if(!statusEl||!hintEl) return;
|
|
3202
|
+
if(!data||!data.enabled){
|
|
3203
|
+
if(section) section.style.display='none';
|
|
3204
|
+
window._isHubAdmin=false;
|
|
3205
|
+
return;
|
|
3206
|
+
}
|
|
3207
|
+
if(section) section.style.display='block';
|
|
3208
|
+
var conn=data.connection||{};
|
|
3209
|
+
function setBadge(color,text,glow){
|
|
3210
|
+
if(!badgeEl)return;
|
|
3211
|
+
badgeEl.innerHTML='<span style="display:inline-flex;align-items:center;gap:4px;font-size:10px;font-weight:600;padding:2px 8px;border-radius:9999px;background:'+color+'15;color:'+color+'"><span style="display:inline-block;width:6px;height:6px;border-radius:50%;background:'+color+(glow?';box-shadow:0 0 4px '+color:'')+'"></span>'+esc(text)+'</span>';
|
|
3212
|
+
}
|
|
3213
|
+
if(data.role==='hub'){
|
|
3214
|
+
setBadge('#34d399',t('sharing.sidebar.connected'),true);
|
|
3215
|
+
statusEl.innerHTML='';
|
|
3216
|
+
hintEl.textContent='';
|
|
3217
|
+
var adminTab=document.querySelector('.tab[data-view="admin"]');
|
|
3218
|
+
if(adminTab) adminTab.style.display='';
|
|
3219
|
+
}else if(conn.pendingApproval&&conn.user){
|
|
3220
|
+
setBadge('#fbbf24',t('sharing.sidebar.pending'),false);
|
|
3221
|
+
var html='<div class="info-grid">';
|
|
3222
|
+
html+='<span class="label">'+t('sharing.sidebar.identity')+'</span><span class="value">'+esc(conn.user.username)+'</span>';
|
|
3223
|
+
if(conn.teamName) html+='<span class="label">'+t('sharing.team')+'</span><span class="value">'+esc(conn.teamName)+'</span>';
|
|
3224
|
+
html+='</div>';
|
|
3225
|
+
statusEl.innerHTML=html;
|
|
3226
|
+
hintEl.textContent=t('sharing.pendingApproval.hint');
|
|
3227
|
+
var adminTab=document.querySelector('.tab[data-view="admin"]');
|
|
3228
|
+
if(adminTab) adminTab.style.display='none';
|
|
3229
|
+
}else if(conn.rejected&&conn.user){
|
|
3230
|
+
setBadge('#ef4444',t('sharing.sidebar.rejected'),false);
|
|
3231
|
+
var html='<div class="info-grid">';
|
|
3232
|
+
html+='<span class="label">'+t('sharing.sidebar.identity')+'</span><span class="value">'+esc(conn.user.username||'-')+'</span>';
|
|
3233
|
+
if(conn.teamName) html+='<span class="label">'+t('sharing.team')+'</span><span class="value">'+esc(conn.teamName)+'</span>';
|
|
3234
|
+
html+='</div>';
|
|
3235
|
+
statusEl.innerHTML=html;
|
|
3236
|
+
hintEl.textContent=t('sharing.rejected.hint');
|
|
3237
|
+
var adminTab=document.querySelector('.tab[data-view="admin"]');
|
|
3238
|
+
if(adminTab) adminTab.style.display='none';
|
|
3239
|
+
}else if(conn.connected&&conn.user){
|
|
3240
|
+
var isAdmin=conn.user.role==='admin';
|
|
3241
|
+
setBadge('#34d399',t('sharing.sidebar.connected'),true);
|
|
3242
|
+
var html='<div class="info-grid">';
|
|
3243
|
+
html+='<span class="label">'+t('sharing.sidebar.identity')+'</span><span class="value">'+esc(conn.user.username)+(isAdmin?' <span class="role-badge admin">'+t('sharing.sidebar.admin')+'</span>':'')+'</span>';
|
|
3244
|
+
html+='<span class="label">'+t('sharing.team')+'</span><span class="value">'+esc(conn.teamName||'-')+'</span>';
|
|
3245
|
+
html+='</div>';
|
|
3246
|
+
statusEl.innerHTML=html;
|
|
3247
|
+
hintEl.innerHTML='';
|
|
3248
|
+
var adminTab=document.querySelector('.tab[data-view="admin"]');
|
|
3249
|
+
if(adminTab) adminTab.style.display=isAdmin?'':'none';
|
|
3250
|
+
}else if(data.clientConfigured){
|
|
3251
|
+
setBadge('#ef4444',t('sharing.sidebar.disconnected'),false);
|
|
3252
|
+
statusEl.innerHTML='<div style="font-size:11px;color:var(--text-muted)">'+t('sharing.sidebar.targetHub')+' '+esc(data.hubUrl||'')+'</div>';
|
|
3253
|
+
hintEl.textContent=t('sharing.clientDisconnected.hint');
|
|
3254
|
+
}else{
|
|
3255
|
+
setBadge('#888',t('sharing.sidebar.notConfigured'),false);
|
|
3256
|
+
statusEl.innerHTML='';
|
|
3257
|
+
hintEl.textContent=t('sharing.clientNotConfigured.hint');
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
|
|
3261
|
+
function renderSharingSettings(data){
|
|
3262
|
+
var statusEl=document.getElementById('sharingStatusPanel');
|
|
3263
|
+
var teamEl=document.getElementById('sharingTeamPanel');
|
|
3264
|
+
var adminEl=document.getElementById('sharingAdminPanel');
|
|
3265
|
+
if(!statusEl||!teamEl||!adminEl) return;
|
|
3266
|
+
if(!data||!data.enabled){
|
|
3267
|
+
statusEl.innerHTML='';teamEl.innerHTML='';adminEl.innerHTML='';
|
|
3268
|
+
return;
|
|
3269
|
+
}
|
|
3270
|
+
var conn=data.connection||{};
|
|
3271
|
+
var user=conn.user||{};
|
|
3272
|
+
var actualRole=data.role||_sharingRole||'client';
|
|
3273
|
+
if(data.role) _sharingRole=data.role;
|
|
3274
|
+
var isAdmin=(data.admin&&data.admin.canManageUsers)||(conn.connected&&user.role==='admin')||(actualRole==='hub');
|
|
3275
|
+
window._isHubAdmin=isAdmin;
|
|
3276
|
+
var adminTab=document.querySelector('.tab[data-view="admin"]');
|
|
3277
|
+
if(adminTab) adminTab.style.display=isAdmin?'':'none';
|
|
3278
|
+
var hubAdminBtn=document.getElementById('hubAdminEntryBtn');
|
|
3279
|
+
|
|
3280
|
+
if(actualRole==='hub'){
|
|
3281
|
+
statusEl.innerHTML='';teamEl.innerHTML='';adminEl.innerHTML='';
|
|
3282
|
+
if(hubAdminBtn) hubAdminBtn.style.display=isAdmin?'':'none';
|
|
3283
|
+
return;
|
|
3284
|
+
}
|
|
3285
|
+
|
|
3286
|
+
if(actualRole==='client'){
|
|
3287
|
+
statusEl.style.display='none';teamEl.style.display='none';adminEl.style.display='none';
|
|
3288
|
+
if(adminTab) adminTab.style.display='none';
|
|
3289
|
+
if(hubAdminBtn) hubAdminBtn.style.display='none';
|
|
3290
|
+
|
|
3291
|
+
var connBadge;
|
|
3292
|
+
if(conn.pendingApproval){
|
|
3293
|
+
connBadge='<span class="hic-badge pending"><span class="hic-dot amber"></span>'+t('sharing.sidebar.pending')+'</span>';
|
|
3294
|
+
}else if(conn.rejected){
|
|
3295
|
+
connBadge='<span class="hic-badge disconnected"><span class="hic-dot red"></span>'+t('sharing.sidebar.rejected')+'</span>';
|
|
3296
|
+
}else if(conn.connected){
|
|
3297
|
+
connBadge='<span class="hic-badge connected"><span class="hic-dot green"></span>'+t('sharing.sidebar.connected')+'</span>';
|
|
3298
|
+
}else{
|
|
3299
|
+
connBadge='<span class="hic-badge disconnected"><span class="hic-dot red"></span>'+t('sharing.sidebar.disconnected')+'</span>';
|
|
3300
|
+
}
|
|
3301
|
+
statusEl.style.display='';
|
|
3302
|
+
var sh='<div class="hub-info-card hic-status"><div class="hic-title"><span class="hic-icon">\u{1F517}</span>'+t('settings.hub.connection')+' '+connBadge+'</div><div class="hic-grid">';
|
|
3303
|
+
if(conn.pendingApproval&&user.username){
|
|
3304
|
+
sh+='<span class="hic-label">'+t('sharing.user')+'</span><span class="hic-value">'+esc(user.username)+'</span>';
|
|
3305
|
+
sh+='</div><div class="hic-empty" style="color:#f59e0b">'+t('sharing.pendingApproval.hint')+'</div></div>';
|
|
3306
|
+
}else if(conn.rejected){
|
|
3307
|
+
if(user.username) sh+='<span class="hic-label">'+t('sharing.user')+'</span><span class="hic-value">'+esc(user.username)+'</span>';
|
|
3308
|
+
sh+='</div><div class="hic-empty" style="color:#ef4444">'+t('sharing.rejected.hint')+'</div>'+
|
|
3309
|
+
'<div style="margin-top:10px;padding:0 16px 14px"><button class="btn btn-sm btn-primary" onclick="retryHubJoin()">'+t('sharing.retryJoin')+'</button>'+
|
|
3310
|
+
'<span style="font-size:11px;color:var(--text-muted);margin-left:8px">'+t('sharing.retryJoin.hint')+'</span></div></div>';
|
|
3311
|
+
}else if(conn.connected&&user.username){
|
|
3312
|
+
sh+='<span class="hic-label">'+t('sharing.user')+'</span><span class="hic-value" style="display:flex;align-items:center;gap:6px">'+
|
|
3313
|
+
'<input type="text" id="hubUsernameInput" value="'+esc(user.username)+'" style="border:1px solid var(--border);border-radius:6px;padding:3px 8px;font-size:12px;background:var(--bg);color:var(--text);width:120px;font-family:inherit" />'+
|
|
3314
|
+
'<button class="btn btn-sm" onclick="updateHubUsername()" style="padding:2px 10px;font-size:11px">'+t('sharing.saveUsername')+'</button>'+
|
|
3315
|
+
'</span>';
|
|
3316
|
+
sh+='<span class="hic-label">'+t('sharing.team')+'</span><span class="hic-value">'+esc(conn.teamName||'-')+'</span>';
|
|
3317
|
+
sh+='</div></div>';
|
|
3318
|
+
}else{
|
|
3319
|
+
sh+='</div></div>';
|
|
3320
|
+
}
|
|
3321
|
+
statusEl.innerHTML=sh;
|
|
3322
|
+
teamEl.innerHTML='';adminEl.innerHTML='';
|
|
3323
|
+
return;
|
|
3324
|
+
}
|
|
3325
|
+
|
|
3326
|
+
statusEl.innerHTML='';teamEl.innerHTML='';adminEl.innerHTML='';
|
|
3327
|
+
}
|
|
3328
|
+
|
|
3329
|
+
async function retryHubJoin(){
|
|
3330
|
+
if(!confirm(t('sharing.retryJoin.confirm'))) return;
|
|
3331
|
+
try{
|
|
3332
|
+
var r=await fetch('/api/sharing/retry-join',{method:'POST',headers:{'Content-Type':'application/json'}});
|
|
3333
|
+
var d=await r.json();
|
|
3334
|
+
if(d.ok){
|
|
3335
|
+
toast(t('sharing.retryJoin.success'),'success');
|
|
3336
|
+
setTimeout(function(){location.reload();},1500);
|
|
3337
|
+
}else{
|
|
3338
|
+
toast(d.error||t('sharing.retryJoin.fail'),'error');
|
|
3339
|
+
}
|
|
3340
|
+
}catch(e){toast(t('sharing.retryJoin.fail')+': '+e.message,'error');}
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3343
|
+
async function updateHubUsername(){
|
|
3344
|
+
var input=document.getElementById('hubUsernameInput');
|
|
3345
|
+
if(!input) return;
|
|
3346
|
+
var newName=input.value.trim();
|
|
3347
|
+
if(!newName||newName.length<2||newName.length>32){
|
|
3348
|
+
toast(t('sharing.username.invalid'),'error');
|
|
3349
|
+
return;
|
|
3350
|
+
}
|
|
3351
|
+
try{
|
|
3352
|
+
var r=await fetch('/api/sharing/update-username',{
|
|
3353
|
+
method:'POST',
|
|
3354
|
+
headers:{'Content-Type':'application/json'},
|
|
3355
|
+
body:JSON.stringify({username:newName})
|
|
3356
|
+
});
|
|
3357
|
+
var d=await r.json();
|
|
3358
|
+
if(d.error==='username_taken'){
|
|
3359
|
+
toast(t('sharing.username.taken'),'error');
|
|
3360
|
+
return;
|
|
3361
|
+
}
|
|
3362
|
+
if(d.error){
|
|
3363
|
+
toast(d.error,'error');
|
|
3364
|
+
return;
|
|
3365
|
+
}
|
|
3366
|
+
toast(t('sharing.username.updated'),'success');
|
|
3367
|
+
loadSharingStatus(false);
|
|
3368
|
+
}catch(e){
|
|
3369
|
+
toast(t('sharing.username.error'),'error');
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
async function loadSharingPendingUsers(){
|
|
3374
|
+
if(_sharingRole==='client') return;
|
|
3375
|
+
var el=document.getElementById('sharingAdminPanel');
|
|
3376
|
+
if(!el) return;
|
|
3377
|
+
el.innerHTML=t('sharing.loading');
|
|
3378
|
+
try{
|
|
3379
|
+
const r=await fetch('/api/sharing/pending-users');
|
|
3380
|
+
const d=await r.json();
|
|
3381
|
+
const users=Array.isArray(d.users)?d.users:[];
|
|
3382
|
+
renderSharingPendingUsers(users, d.error, sharingStatusCache&&sharingStatusCache.admin?sharingStatusCache.admin.rejectSupported:false);
|
|
3383
|
+
}catch(e){
|
|
3384
|
+
el.innerHTML='<div class="line">'+t('sharing.pendingLoadFail')+esc(String(e))+'</div>';
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
|
|
3388
|
+
function renderSharingPendingUsers(users, error, rejectSupported){
|
|
3389
|
+
var el=document.getElementById('sharingAdminPanel');
|
|
3390
|
+
if(!el) return;
|
|
3391
|
+
var wrap='<div class="hub-info-card hic-pending"><div class="hic-title"><span class="hic-icon">\u{1F6E1}</span>'+t('settings.hub.adminPending')+' <span class="hic-badge pending"><span class="hic-dot amber"></span>'+(users?users.length:0)+'</span></div>';
|
|
3392
|
+
if(error){
|
|
3393
|
+
el.innerHTML=wrap+'<div class="hic-empty">'+esc(error)+'</div></div>';
|
|
3394
|
+
return;
|
|
3395
|
+
}
|
|
3396
|
+
if(!users||users.length===0){
|
|
3397
|
+
el.innerHTML=wrap+'<div class="hic-empty">'+t('sharing.noPending')+'</div></div>';
|
|
3398
|
+
return;
|
|
3399
|
+
}
|
|
3400
|
+
el.innerHTML=wrap+users.map(function(user){
|
|
3401
|
+
return '<div style="display:flex;align-items:center;justify-content:space-between;padding:8px 0;border-top:1px solid var(--border)">'+
|
|
3402
|
+
'<div><div style="font-size:13px;font-weight:600;color:var(--text)">'+esc(user.username||user.id||'')+'</div>'+
|
|
3403
|
+
'<div style="font-size:11px;color:var(--text-muted)">'+t('admin.device')+esc(user.deviceName||'unknown')+'</div></div>'+
|
|
3404
|
+
'<div class="hic-actions" style="margin:0">'+
|
|
3405
|
+
'<button class="btn btn-sm btn-primary" onclick="approveSharingUser("'+escAttr(user.id)+'","'+escAttr(user.username||'')+'")">'+t('admin.approve')+'</button>'+
|
|
3406
|
+
(rejectSupported?'<button class="btn btn-sm btn-ghost" style="color:var(--rose)" onclick="rejectSharingUser("'+escAttr(user.id)+'","'+escAttr(user.username||'')+'")">'+t('admin.reject')+'</button>':'')+
|
|
3407
|
+
'</div></div>';
|
|
3408
|
+
}).join('')+'</div>';
|
|
3409
|
+
}
|
|
3410
|
+
|
|
3411
|
+
async function approveSharingUser(userId,username){
|
|
3412
|
+
try{
|
|
3413
|
+
const r=await fetch('/api/sharing/approve-user',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:userId,username:username})});
|
|
3414
|
+
const d=await r.json();
|
|
3415
|
+
if(d.ok){toast(t('toast.userApproved'),'success');loadSharingPendingUsers();loadSharingStatus(true);} else {toast(d.error||t('toast.approveFail'),'error');}
|
|
3416
|
+
}catch(e){toast(t('toast.approveFail')+': '+e.message,'error');}
|
|
3417
|
+
}
|
|
3418
|
+
|
|
3419
|
+
async function rejectSharingUser(userId,username){
|
|
3420
|
+
try{
|
|
3421
|
+
const r=await fetch('/api/sharing/reject-user',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:userId,username:username})});
|
|
3422
|
+
const d=await r.json();
|
|
3423
|
+
if(d.ok){toast(t('toast.userRejected'),'success');loadSharingPendingUsers();} else {toast(d.error||t('toast.rejectFail'),'error');}
|
|
3424
|
+
}catch(e){toast(t('toast.rejectFail')+': '+e.message,'error');}
|
|
3425
|
+
}
|
|
3426
|
+
|
|
3427
|
+
/* ─── Group Manager ─── */
|
|
3428
|
+
var groupManagerUsers=[];
|
|
3429
|
+
async function loadGroupManager(){
|
|
3430
|
+
var panel=document.getElementById('groupManagerPanel');
|
|
3431
|
+
if(!panel) return;
|
|
3432
|
+
panel.style.display='block';
|
|
3433
|
+
panel.innerHTML=t('sharing.loadingGroups');
|
|
3434
|
+
try{
|
|
3435
|
+
var [gr,ur]=await Promise.all([
|
|
3436
|
+
fetch('/api/sharing/groups').then(function(r){return r.json();}),
|
|
3437
|
+
fetch('/api/sharing/users').then(function(r){return r.json();})
|
|
3438
|
+
]);
|
|
3439
|
+
var groups=Array.isArray(gr.groups)?gr.groups:[];
|
|
3440
|
+
groupManagerUsers=Array.isArray(ur.users)?ur.users:[];
|
|
3441
|
+
renderGroupManager(panel,groups);
|
|
3442
|
+
}catch(e){panel.innerHTML=t('admin.groupsFailed')+esc(String(e));}
|
|
3443
|
+
}
|
|
3444
|
+
function renderGroupManager(panel,groups){
|
|
3445
|
+
var html='<div style="margin-bottom:8px;display:flex;gap:8px;align-items:center;font-size:12px">'+
|
|
3446
|
+
'<strong>'+t('admin.groups')+' ('+groups.length+')</strong>'+
|
|
3447
|
+
'<button class="btn btn-sm" onclick="showCreateGroupForm()" style="font-size:11px">'+t('admin.newGroup')+'</button>'+
|
|
3448
|
+
'</div>';
|
|
3449
|
+
html+='<div id="createGroupForm" style="display:none;margin-bottom:10px;padding:10px;background:var(--bg);border:1px solid var(--border);border-radius:8px;font-size:12px">'+
|
|
3450
|
+
'<input id="newGroupName" type="text" placeholder="'+t('admin.groupName')+'" style="width:60%;padding:4px 8px;border:1px solid var(--border);border-radius:6px;font-size:12px;margin-right:6px;background:var(--bg);color:var(--text)">'+
|
|
3451
|
+
'<input id="newGroupDesc" type="text" placeholder="'+t('admin.groupDesc')+'" style="width:60%;padding:4px 8px;border:1px solid var(--border);border-radius:6px;font-size:12px;margin-right:6px;margin-top:6px;background:var(--bg);color:var(--text)">'+
|
|
3452
|
+
'<div style="margin-top:6px"><button class="btn btn-sm btn-primary" onclick="createGroup()" style="font-size:11px">'+t('admin.create')+'</button> '+
|
|
3453
|
+
'<button class="btn btn-sm btn-ghost" onclick="hideCreateGroupForm()" style="font-size:11px">'+t('admin.cancel')+'</button></div>'+
|
|
3454
|
+
'</div>';
|
|
3455
|
+
if(groups.length===0){
|
|
3456
|
+
html+='<div style="font-size:12px;color:var(--text-muted);padding:6px 0">'+t('sharing.noGroupsYet')+'</div>';
|
|
3457
|
+
}else{
|
|
3458
|
+
html+='<div style="display:flex;flex-direction:column;gap:6px">';
|
|
3459
|
+
for(var i=0;i<groups.length;i++){
|
|
3460
|
+
var g=groups[i];
|
|
3461
|
+
html+='<div style="padding:8px 12px;background:var(--bg);border:1px solid var(--border);border-radius:8px;font-size:12px">'+
|
|
3462
|
+
'<div style="display:flex;justify-content:space-between;align-items:center">'+
|
|
3463
|
+
'<div><strong style="font-size:12px">'+esc(g.name)+'</strong>'+(g.description?' — <span style="color:var(--text-sec);font-size:11px">'+esc(g.description)+'</span>':'')+'</div>'+
|
|
3464
|
+
'<div style="display:flex;gap:6px">'+
|
|
3465
|
+
'<button class="btn btn-sm" onclick="toggleGroupMembers("'+escAttr(g.id)+'")" style="font-size:11px;padding:2px 8px">'+t('admin.members')+'</button>'+
|
|
3466
|
+
'<button class="btn btn-sm btn-ghost" onclick="deleteGroup("'+escAttr(g.id)+'","'+escAttr(g.name)+'")" style="color:#ef4444;font-size:11px;padding:2px 8px">'+t('admin.delete')+'</button>'+
|
|
3467
|
+
'</div>'+
|
|
3468
|
+
'</div>'+
|
|
3469
|
+
'<div id="groupMembers_'+escAttr(g.id)+'" style="display:none;margin-top:6px;font-size:12px"></div>'+
|
|
3470
|
+
'</div>';
|
|
3471
|
+
}
|
|
3472
|
+
html+='</div>';
|
|
3473
|
+
}
|
|
3474
|
+
panel.innerHTML=html;
|
|
3475
|
+
}
|
|
3476
|
+
function showCreateGroupForm(){var f=document.getElementById('createGroupForm');if(f)f.style.display='block';}
|
|
3477
|
+
function hideCreateGroupForm(){var f=document.getElementById('createGroupForm');if(f)f.style.display='none';}
|
|
3478
|
+
async function createGroup(){
|
|
3479
|
+
var name=(document.getElementById('newGroupName')).value.trim();
|
|
3480
|
+
var desc=(document.getElementById('newGroupDesc')).value.trim();
|
|
3481
|
+
if(!name){toast(t('toast.groupNameRequired'),'error');return;}
|
|
3482
|
+
try{
|
|
3483
|
+
var r=await fetch('/api/sharing/groups',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:name,description:desc})});
|
|
3484
|
+
var d=await r.json();
|
|
3485
|
+
if(d.ok){toast(t('toast.groupCreated'),'success');hideCreateGroupForm();loadGroupManager();}else{toast(d.error||t('toast.createFail'),'error');}
|
|
3486
|
+
}catch(e){toast(t('toast.createFail')+': '+e.message,'error');}
|
|
3487
|
+
}
|
|
3488
|
+
async function deleteGroup(groupId,groupName){
|
|
3489
|
+
if(!confirm(t('confirm.deleteGroup').replace('{name}',groupName))) return;
|
|
3490
|
+
try{
|
|
3491
|
+
var r=await fetch('/api/sharing/groups/'+encodeURIComponent(groupId),{method:'DELETE'});
|
|
3492
|
+
var d=await r.json();
|
|
3493
|
+
if(d.ok){toast(t('toast.groupDeleted'),'success');loadGroupManager();}else{toast(d.error||t('toast.deleteFail'),'error');}
|
|
3494
|
+
}catch(e){toast(t('toast.deleteFail')+': '+e.message,'error');}
|
|
3495
|
+
}
|
|
3496
|
+
async function toggleGroupMembers(groupId){
|
|
3497
|
+
var el=document.getElementById('groupMembers_'+groupId);
|
|
3498
|
+
if(!el) return;
|
|
3499
|
+
if(el.style.display!=='none'){el.style.display='none';return;}
|
|
3500
|
+
el.style.display='block';
|
|
3501
|
+
el.innerHTML=t('sharing.loading');
|
|
3502
|
+
try{
|
|
3503
|
+
var r=await fetch('/api/sharing/groups/'+encodeURIComponent(groupId)+'/members');
|
|
3504
|
+
var d=await r.json();
|
|
3505
|
+
var members=Array.isArray(d.members)?d.members:[];
|
|
3506
|
+
renderGroupMembers(el,groupId,members);
|
|
3507
|
+
}catch(e){el.innerHTML=t('admin.groupsFailed')+esc(String(e));}
|
|
3508
|
+
}
|
|
3509
|
+
function renderGroupMembers(el,groupId,members){
|
|
3510
|
+
var html='<div style="font-size:12px;margin-bottom:6px;color:var(--text-sec)">'+t('admin.membersCount').replace('{n}',members.length)+'</div>';
|
|
3511
|
+
if(members.length>0){
|
|
3512
|
+
html+='<div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px">';
|
|
3513
|
+
for(var i=0;i<members.length;i++){
|
|
3514
|
+
var m=members[i];
|
|
3515
|
+
html+='<span style="display:inline-flex;align-items:center;gap:4px;padding:3px 10px;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;font-size:12px">'+
|
|
3516
|
+
esc(m.username||m.userId)+
|
|
3517
|
+
' <button style="background:none;border:none;color:#ef4444;cursor:pointer;font-size:11px;padding:0 2px" onclick="removeGroupMember("'+escAttr(groupId)+'","'+escAttr(m.userId)+'")">×</button>'+
|
|
3518
|
+
'</span>';
|
|
3519
|
+
}
|
|
3520
|
+
html+='</div>';
|
|
3521
|
+
}else{
|
|
3522
|
+
html+='<div style="font-size:12px;color:var(--text-muted);margin-bottom:8px">'+t('admin.noMembersYet')+'</div>';
|
|
3523
|
+
}
|
|
3524
|
+
var memberIds=new Set(members.map(function(m){return m.userId;}));
|
|
3525
|
+
var available=groupManagerUsers.filter(function(u){return !memberIds.has(u.id);});
|
|
3526
|
+
if(available.length>0){
|
|
3527
|
+
html+='<div style="display:flex;gap:6px;align-items:center">'+
|
|
3528
|
+
'<select id="addMemberSelect_'+escAttr(groupId)+'" style="padding:4px 8px;border:1px solid var(--border);border-radius:6px;font-size:12px">';
|
|
3529
|
+
for(var j=0;j<available.length;j++){
|
|
3530
|
+
html+='<option value="'+escAttr(available[j].id)+'">'+esc(available[j].username)+'</option>';
|
|
3531
|
+
}
|
|
3532
|
+
html+='</select>'+
|
|
3533
|
+
'<button class="btn btn-sm" onclick="addGroupMember("'+escAttr(groupId)+'")">'+t('admin.add')+'</button>'+
|
|
3534
|
+
'</div>';
|
|
3535
|
+
}
|
|
3536
|
+
el.innerHTML=html;
|
|
3537
|
+
}
|
|
3538
|
+
async function addGroupMember(groupId){
|
|
3539
|
+
var sel=document.getElementById('addMemberSelect_'+groupId);
|
|
3540
|
+
if(!sel) return;
|
|
3541
|
+
var userId=sel.value;
|
|
3542
|
+
if(!userId) return;
|
|
3543
|
+
try{
|
|
3544
|
+
var r=await fetch('/api/sharing/groups/'+encodeURIComponent(groupId)+'/members',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:userId})});
|
|
3545
|
+
var d=await r.json();
|
|
3546
|
+
if(d.ok){toast(t('toast.memberAdded'),'success');reloadGroupMembers(groupId);}else{toast(d.error||t('toast.addFail'),'error');}
|
|
3547
|
+
}catch(e){toast(t('toast.addFail')+': '+e.message,'error');}
|
|
3548
|
+
}
|
|
3549
|
+
async function removeGroupMember(groupId,userId){
|
|
3550
|
+
if(!confirm(t('confirm.removeGroupMember'))) return;
|
|
3551
|
+
try{
|
|
3552
|
+
var r=await fetch('/api/sharing/groups/'+encodeURIComponent(groupId)+'/members',{method:'DELETE',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:userId})});
|
|
3553
|
+
var d=await r.json();
|
|
3554
|
+
if(d.ok){toast(t('toast.memberRemoved'),'success');reloadGroupMembers(groupId);}else{toast(d.error||t('toast.removeFail'),'error');}
|
|
3555
|
+
}catch(e){toast(t('toast.removeFail')+': '+e.message,'error');}
|
|
3556
|
+
}
|
|
3557
|
+
async function reloadGroupMembers(groupId){
|
|
3558
|
+
var el=document.getElementById('groupMembers_'+groupId);
|
|
3559
|
+
if(!el) return;
|
|
3560
|
+
el.style.display='block';
|
|
3561
|
+
el.innerHTML=t('sharing.loading');
|
|
3562
|
+
try{
|
|
3563
|
+
var r=await fetch('/api/sharing/groups/'+encodeURIComponent(groupId)+'/members');
|
|
3564
|
+
var d=await r.json();
|
|
3565
|
+
var members=Array.isArray(d.members)?d.members:[];
|
|
3566
|
+
renderGroupMembers(el,groupId,members);
|
|
3567
|
+
}catch(e){el.innerHTML=t('admin.groupsFailed')+esc(String(e));}
|
|
3568
|
+
}
|
|
3569
|
+
|
|
3570
|
+
/* ─── Hub Admin Panel ─── */
|
|
3571
|
+
var adminDataCache={users:[],groups:[],tasks:[],skills:[],memories:[]};
|
|
3572
|
+
|
|
3573
|
+
function switchAdminTab(tab,btn){
|
|
3574
|
+
document.querySelectorAll('.admin-tabs .admin-tab').forEach(function(t){t.classList.remove('active');});
|
|
3575
|
+
btn.classList.add('active');
|
|
3576
|
+
document.querySelectorAll('.admin-panel').forEach(function(p){p.classList.remove('active');});
|
|
3577
|
+
var panel=document.getElementById('admin'+tab.charAt(0).toUpperCase()+tab.slice(1)+'Panel');
|
|
3578
|
+
if(panel) panel.classList.add('active');
|
|
3579
|
+
}
|
|
3580
|
+
|
|
3581
|
+
async function loadAdminData(){
|
|
3582
|
+
if(!window._isHubAdmin){
|
|
3583
|
+
var statsEl=document.getElementById('adminStats');
|
|
3584
|
+
if(statsEl) statsEl.innerHTML='<div class="admin-empty">'+t('admin.noPermission')+'</div>';
|
|
3585
|
+
return;
|
|
3586
|
+
}
|
|
3587
|
+
try{
|
|
3588
|
+
var [usersR,tasksR,skillsR,pendingR,memoriesR]=await Promise.all([
|
|
3589
|
+
fetch('/api/sharing/users').then(function(r){return r.json();}),
|
|
3590
|
+
fetch('/api/admin/shared-tasks').then(function(r){return r.json();}),
|
|
3591
|
+
fetch('/api/admin/shared-skills').then(function(r){return r.json();}),
|
|
3592
|
+
fetch('/api/sharing/pending-users').then(function(r){return r.json();}),
|
|
3593
|
+
fetch('/api/admin/shared-memories').then(function(r){return r.json();})
|
|
3594
|
+
]);
|
|
3595
|
+
adminDataCache.users=Array.isArray(usersR.users)?usersR.users:[];
|
|
3596
|
+
adminDataCache.groups=[];
|
|
3597
|
+
adminDataCache.tasks=Array.isArray(tasksR.tasks)?tasksR.tasks:[];
|
|
3598
|
+
adminDataCache.skills=Array.isArray(skillsR.skills)?skillsR.skills:[];
|
|
3599
|
+
adminDataCache.memories=Array.isArray(memoriesR.memories)?memoriesR.memories:[];
|
|
3600
|
+
var pending=Array.isArray(pendingR.users)?pendingR.users:[];
|
|
3601
|
+
renderAdminStats(pending.length);
|
|
3602
|
+
renderAdminUsers(adminDataCache.users, pending);
|
|
3603
|
+
renderAdminMemories(adminDataCache.tasks);
|
|
3604
|
+
renderAdminSkills(adminDataCache.skills);
|
|
3605
|
+
renderAdminSharedMemories(adminDataCache.memories);
|
|
3606
|
+
}catch(e){
|
|
3607
|
+
var statsEl=document.getElementById('adminStats');
|
|
3608
|
+
if(statsEl) statsEl.innerHTML='<div class="admin-empty">'+t('admin.loadFailed')+esc(String(e))+'</div>';
|
|
3609
|
+
}
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3612
|
+
function renderAdminStats(pendingCount){
|
|
3613
|
+
var el=document.getElementById('adminStats');
|
|
3614
|
+
if(!el) return;
|
|
3615
|
+
el.innerHTML=
|
|
3616
|
+
'<div class="admin-stat-box"><span class="as-icon">\u{1F465}</span><div class="val">'+adminDataCache.users.length+'</div><div class="lbl">'+t('admin.stat.activeUsers')+'</div></div>'+
|
|
3617
|
+
'<div class="admin-stat-box"><span class="as-icon">\u{23F3}</span><div class="val">'+pendingCount+'</div><div class="lbl">'+t('admin.stat.pending')+'</div></div>'+
|
|
3618
|
+
'<div class="admin-stat-box"><span class="as-icon">\u{1F4CB}</span><div class="val">'+adminDataCache.tasks.length+'</div><div class="lbl">'+t('admin.stat.sharedTasks')+'</div></div>'+
|
|
3619
|
+
'<div class="admin-stat-box"><span class="as-icon">\u{1F9E0}</span><div class="val">'+adminDataCache.skills.length+'</div><div class="lbl">'+t('admin.stat.sharedSkills')+'</div></div>'+
|
|
3620
|
+
'<div class="admin-stat-box"><span class="as-icon">\u{1F4AD}</span><div class="val">'+(adminDataCache.memories||[]).length+'</div><div class="lbl">'+t('admin.stat.sharedMemories')+'</div></div>';
|
|
3621
|
+
var tc=document.getElementById('adminTabCountUsers');if(tc)tc.textContent=adminDataCache.users.length+pendingCount;
|
|
3622
|
+
tc=document.getElementById('adminTabCountMemories');if(tc)tc.textContent=(adminDataCache.memories||[]).length;
|
|
3623
|
+
tc=document.getElementById('adminTabCountTasks');if(tc)tc.textContent=adminDataCache.tasks.length;
|
|
3624
|
+
tc=document.getElementById('adminTabCountSkills');if(tc)tc.textContent=adminDataCache.skills.length;
|
|
3625
|
+
}
|
|
3626
|
+
|
|
3627
|
+
function renderAdminUsers(users,pending){
|
|
3628
|
+
var el=document.getElementById('adminUsersPanel');
|
|
3629
|
+
if(!el) return;
|
|
3630
|
+
var html='';
|
|
3631
|
+
if(pending&&pending.length>0){
|
|
3632
|
+
html+='<div style="margin-bottom:16px"><h3 style="font-size:14px;font-weight:600;color:var(--amber);margin-bottom:10px">'+t('admin.pendingApproval')+' ('+pending.length+')</h3>';
|
|
3633
|
+
for(var p=0;p<pending.length;p++){
|
|
3634
|
+
var pu=pending[p];
|
|
3635
|
+
html+='<div class="admin-card"><div class="admin-card-header"><div class="admin-card-title">'+esc(pu.username||pu.id||'Unknown')+'</div><span class="admin-badge pending">pending</span></div>'+
|
|
3636
|
+
'<div class="admin-card-meta">'+t('admin.device')+esc(pu.deviceName||'unknown')+'</div>'+
|
|
3637
|
+
'<div class="admin-card-actions">'+
|
|
3638
|
+
'<button class="btn btn-sm btn-primary" onclick="adminApproveUser("'+escAttr(pu.id)+'","'+escAttr(pu.username||'')+'")">'+t('admin.approve')+'</button>'+
|
|
3639
|
+
'<button class="btn btn-sm btn-ghost" onclick="adminRejectUser("'+escAttr(pu.id)+'")" style="color:var(--rose)">'+t('admin.reject')+'</button>'+
|
|
3640
|
+
'</div></div>';
|
|
3641
|
+
}
|
|
3642
|
+
html+='</div>';
|
|
3643
|
+
}
|
|
3644
|
+
html+='<h3 style="font-size:14px;font-weight:600;color:var(--text);margin-bottom:10px">'+t('admin.activeUsers')+' ('+users.length+')</h3>';
|
|
3645
|
+
if(users.length===0){
|
|
3646
|
+
html+='<div class="admin-empty"><span class="ae-icon">\u{1F465}</span>'+t('admin.noActiveUsers')+'</div>';
|
|
3647
|
+
}else{
|
|
3648
|
+
for(var i=0;i<users.length;i++){
|
|
3649
|
+
var u=users[i];
|
|
3650
|
+
html+='<div class="admin-card"><div class="admin-card-header"><div class="admin-card-title">'+esc(u.username||u.id)+'</div>'+
|
|
3651
|
+
'<span class="admin-badge '+(u.role==='admin'?'admin':'member')+'">'+esc(u.role||'member')+'</span></div>'+
|
|
3652
|
+
'<div class="admin-card-meta">ID: '+esc(u.id)+(u.status?' \u00B7 Status: '+esc(u.status):'')+'</div></div>';
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
el.innerHTML=html;
|
|
3656
|
+
}
|
|
3657
|
+
|
|
3658
|
+
async function adminApproveUser(userId,username){
|
|
3659
|
+
try{
|
|
3660
|
+
var r=await fetch('/api/sharing/approve-user',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:userId,username:username})});
|
|
3661
|
+
var d=await r.json();
|
|
3662
|
+
if(d.ok){toast(t('toast.userApproved'),'success');loadAdminData();}else{toast(d.error||t('toast.approveFail'),'error');}
|
|
3663
|
+
}catch(e){toast(t('toast.approveFail')+': '+e.message,'error');}
|
|
3664
|
+
}
|
|
3665
|
+
async function adminRejectUser(userId){
|
|
3666
|
+
if(!confirm(t('confirm.rejectUser'))) return;
|
|
3667
|
+
try{
|
|
3668
|
+
var r=await fetch('/api/sharing/reject-user',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:userId})});
|
|
3669
|
+
var d=await r.json();
|
|
3670
|
+
if(d.ok){toast(t('toast.userRejected'),'success');loadAdminData();}else{toast(d.error||t('toast.rejectFail'),'error');}
|
|
3671
|
+
}catch(e){toast(t('toast.rejectFail')+': '+e.message,'error');}
|
|
3672
|
+
}
|
|
3673
|
+
|
|
3674
|
+
function renderAdminGroups(groups){
|
|
3675
|
+
var el=document.getElementById('adminGroupsPanel');
|
|
3676
|
+
if(!el) return;
|
|
3677
|
+
var html='<div style="margin-bottom:12px;display:flex;justify-content:space-between;align-items:center">'+
|
|
3678
|
+
'<h3 style="font-size:14px;font-weight:600;color:var(--text)">'+t('admin.groups')+' ('+groups.length+')</h3>'+
|
|
3679
|
+
'<button class="btn btn-sm btn-primary" onclick="showAdminCreateGroup()">'+t('admin.newGroup')+'</button></div>';
|
|
3680
|
+
html+='<div id="adminCreateGroupForm" style="display:none;margin-bottom:14px;padding:14px;background:var(--bg);border:1px solid var(--border);border-radius:10px">'+
|
|
3681
|
+
'<div style="display:flex;flex-direction:column;gap:8px">'+
|
|
3682
|
+
'<input id="adminNewGroupName" type="text" placeholder="'+t('admin.groupName')+'" style="padding:8px 12px;border:1px solid var(--border);border-radius:8px;font-size:13px;background:var(--bg-card);color:var(--text)">'+
|
|
3683
|
+
'<input id="adminNewGroupDesc" type="text" placeholder="'+t('admin.groupDesc')+'" style="padding:8px 12px;border:1px solid var(--border);border-radius:8px;font-size:13px;background:var(--bg-card);color:var(--text)">'+
|
|
3684
|
+
'<div style="display:flex;gap:8px"><button class="btn btn-sm btn-primary" onclick="adminCreateGroup()">'+t('admin.create')+'</button>'+
|
|
3685
|
+
'<button class="btn btn-sm btn-ghost" onclick="hideAdminCreateGroup()">'+t('admin.cancel')+'</button></div></div></div>';
|
|
3686
|
+
if(groups.length===0){
|
|
3687
|
+
html+='<div class="admin-empty"><span class="ae-icon">\u{1F4C2}</span>'+t('admin.noGroups')+'</div>';
|
|
3688
|
+
}else{
|
|
3689
|
+
for(var i=0;i<groups.length;i++){
|
|
3690
|
+
var g=groups[i];
|
|
3691
|
+
html+='<div class="admin-card"><div class="admin-card-header"><div class="admin-card-title">'+esc(g.name)+'</div>'+
|
|
3692
|
+
'<button class="btn btn-sm btn-ghost" onclick="adminDeleteGroup("'+escAttr(g.id)+'","'+escAttr(g.name)+'")" style="color:var(--rose);font-size:11px">'+t('admin.delete')+'</button></div>'+
|
|
3693
|
+
(g.description?'<div class="admin-card-meta">'+esc(g.description)+'</div>':'')+
|
|
3694
|
+
'<div id="adminGroupMembers_'+escAttr(g.id)+'" style="margin-top:10px"></div>'+
|
|
3695
|
+
'</div>';
|
|
3696
|
+
}
|
|
3697
|
+
}
|
|
3698
|
+
el.innerHTML=html;
|
|
3699
|
+
for(var i=0;i<groups.length;i++){adminLoadGroupMembers(groups[i].id);}
|
|
3700
|
+
}
|
|
3701
|
+
function showAdminCreateGroup(){var f=document.getElementById('adminCreateGroupForm');if(f)f.style.display='block';}
|
|
3702
|
+
function hideAdminCreateGroup(){var f=document.getElementById('adminCreateGroupForm');if(f)f.style.display='none';}
|
|
3703
|
+
async function adminCreateGroup(){
|
|
3704
|
+
var name=(document.getElementById('adminNewGroupName')).value.trim();
|
|
3705
|
+
var desc=(document.getElementById('adminNewGroupDesc')).value.trim();
|
|
3706
|
+
if(!name){toast(t('toast.groupNameRequired'),'error');return;}
|
|
3707
|
+
try{
|
|
3708
|
+
var r=await fetch('/api/sharing/groups',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:name,description:desc})});
|
|
3709
|
+
var d=await r.json();
|
|
3710
|
+
if(d.ok||d.id){toast(t('toast.groupCreated'),'success');hideAdminCreateGroup();loadAdminData();}else{toast(d.error||t('toast.createFail'),'error');}
|
|
3711
|
+
}catch(e){toast(t('toast.createFail')+': '+e.message,'error');}
|
|
3712
|
+
}
|
|
3713
|
+
async function adminDeleteGroup(groupId,groupName){
|
|
3714
|
+
if(!confirm(t('confirm.deleteGroupShort').replace('{name}',groupName))) return;
|
|
3715
|
+
try{
|
|
3716
|
+
var r=await fetch('/api/sharing/groups/'+encodeURIComponent(groupId),{method:'DELETE'});
|
|
3717
|
+
var d=await r.json();
|
|
3718
|
+
if(d.ok){toast(t('toast.groupDeleted'),'success');loadAdminData();}else{toast(d.error||t('toast.deleteFail'),'error');}
|
|
3719
|
+
}catch(e){toast(t('toast.deleteFail')+': '+e.message,'error');}
|
|
3720
|
+
}
|
|
3721
|
+
async function adminLoadGroupMembers(groupId){
|
|
3722
|
+
var el=document.getElementById('adminGroupMembers_'+groupId);
|
|
3723
|
+
if(!el) return;
|
|
3724
|
+
el.innerHTML=t('sharing.loading');
|
|
3725
|
+
try{
|
|
3726
|
+
var r=await fetch('/api/sharing/groups/'+encodeURIComponent(groupId)+'/members');
|
|
3727
|
+
var d=await r.json();
|
|
3728
|
+
var members=Array.isArray(d.members)?d.members:[];
|
|
3729
|
+
var html='<div style="font-size:12px;margin-bottom:6px;color:var(--text-sec)">'+t('admin.membersCount').replace('{n}',members.length)+'</div>';
|
|
3730
|
+
if(members.length>0){
|
|
3731
|
+
html+='<div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px">';
|
|
3732
|
+
for(var i=0;i<members.length;i++){
|
|
3733
|
+
var m=members[i];
|
|
3734
|
+
html+='<span style="display:inline-flex;align-items:center;gap:4px;padding:3px 10px;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;font-size:12px">'+
|
|
3735
|
+
esc(m.username||m.userId)+
|
|
3736
|
+
' <button style="background:none;border:none;color:#ef4444;cursor:pointer;font-size:11px;padding:0 2px" onclick="adminRemoveGroupMember("'+escAttr(groupId)+'","'+escAttr(m.userId)+'")">×</button></span>';
|
|
3737
|
+
}
|
|
3738
|
+
html+='</div>';
|
|
3739
|
+
}else{
|
|
3740
|
+
html+='<div style="font-size:12px;color:var(--text-muted);margin-bottom:8px">'+t('admin.noMembers')+'</div>';
|
|
3741
|
+
}
|
|
3742
|
+
var memberIds=new Set(members.map(function(m){return m.userId;}));
|
|
3743
|
+
var available=adminDataCache.users.filter(function(u){return !memberIds.has(u.id);});
|
|
3744
|
+
if(available.length>0){
|
|
3745
|
+
html+='<div style="display:flex;gap:6px;align-items:center">'+
|
|
3746
|
+
'<select id="adminAddMember_'+escAttr(groupId)+'" style="padding:4px 8px;border:1px solid var(--border);border-radius:6px;font-size:12px;background:var(--bg-card);color:var(--text)">';
|
|
3747
|
+
for(var j=0;j<available.length;j++){
|
|
3748
|
+
html+='<option value="'+escAttr(available[j].id)+'">'+esc(available[j].username)+'</option>';
|
|
3749
|
+
}
|
|
3750
|
+
html+='</select><button class="btn btn-sm" onclick="adminAddGroupMember("'+escAttr(groupId)+'")">'+t('admin.add')+'</button></div>';
|
|
3751
|
+
}
|
|
3752
|
+
el.innerHTML=html;
|
|
3753
|
+
}catch(e){el.innerHTML=t('admin.groupsFailed')+esc(String(e));}
|
|
3754
|
+
}
|
|
3755
|
+
async function adminAddGroupMember(groupId){
|
|
3756
|
+
var sel=document.getElementById('adminAddMember_'+groupId);
|
|
3757
|
+
if(!sel) return;
|
|
3758
|
+
try{
|
|
3759
|
+
var r=await fetch('/api/sharing/groups/'+encodeURIComponent(groupId)+'/members',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:sel.value})});
|
|
3760
|
+
var d=await r.json();
|
|
3761
|
+
if(d.ok){toast(t('toast.memberAdded'),'success');adminLoadGroupMembers(groupId);}else{toast(d.error||t('toast.addFail'),'error');}
|
|
3762
|
+
}catch(e){toast(t('toast.addFail')+': '+e.message,'error');}
|
|
3763
|
+
}
|
|
3764
|
+
async function adminRemoveGroupMember(groupId,userId){
|
|
3765
|
+
if(!confirm(t('confirm.removeMember'))) return;
|
|
3766
|
+
try{
|
|
3767
|
+
var r=await fetch('/api/sharing/groups/'+encodeURIComponent(groupId)+'/members',{method:'DELETE',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:userId})});
|
|
3768
|
+
var d=await r.json();
|
|
3769
|
+
if(d.ok){toast(t('toast.memberRemoved'),'success');adminLoadGroupMembers(groupId);}else{toast(d.error||t('toast.removeFail'),'error');}
|
|
3770
|
+
}catch(e){toast(t('toast.removeFail')+': '+e.message,'error');}
|
|
3771
|
+
}
|
|
3772
|
+
|
|
3773
|
+
function renderAdminMemories(tasks){
|
|
3774
|
+
var el=document.getElementById('adminMemoriesPanel');
|
|
3775
|
+
if(!el) return;
|
|
3776
|
+
adminTasksCache=tasks;
|
|
3777
|
+
var html='<h3 style="font-size:14px;font-weight:600;color:var(--text);margin-bottom:10px">'+t('admin.sharedTasks')+' ('+tasks.length+')</h3>';
|
|
3778
|
+
if(tasks.length===0){
|
|
3779
|
+
html+='<div class="admin-empty"><span class="ae-icon">\u{1F4CB}</span>'+t('admin.noSharedTasks')+'</div>';
|
|
3780
|
+
}else{
|
|
3781
|
+
for(var i=0;i<tasks.length;i++){
|
|
3782
|
+
var tk=tasks[i];
|
|
3783
|
+
html+='<div class="admin-card" onclick="openHubTaskDetailFromCache(\\\'admin\\\','+i+')" style="cursor:pointer"><div class="admin-card-header"><div class="admin-card-title">'+esc(tk.title||tk.id)+'</div>'+
|
|
3784
|
+
'</div>'+
|
|
3785
|
+
'<div class="admin-card-meta">'+
|
|
3786
|
+
t('admin.owner')+esc(tk.ownerName||tk.sourceUserId||'unknown')+
|
|
3787
|
+
(tk.chunkCount!=null?' \u00B7 '+t('admin.chunks')+tk.chunkCount:'')+
|
|
3788
|
+
' \u00B7 '+t('admin.updated')+new Date(tk.updatedAt||tk.createdAt).toLocaleDateString()+
|
|
3789
|
+
'</div>'+
|
|
3790
|
+
'<div class="admin-card-actions">'+
|
|
3791
|
+
'<button class="btn btn-sm btn-ghost" onclick="event.stopPropagation();adminDeleteTask("'+escAttr(tk.id)+'","'+escAttr(tk.title||tk.id)+'")" style="color:var(--rose)">'+t('admin.remove')+'</button>'+
|
|
3792
|
+
'</div></div>';
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
el.innerHTML=html;
|
|
3796
|
+
}
|
|
3797
|
+
|
|
3798
|
+
async function adminDeleteTask(taskId,taskTitle){
|
|
3799
|
+
if(!confirm(t('confirm.removeTask').replace('{name}',taskTitle))) return;
|
|
3800
|
+
try{
|
|
3801
|
+
var r=await fetch('/api/admin/shared-tasks/'+encodeURIComponent(taskId),{method:'DELETE'});
|
|
3802
|
+
var d=await r.json();
|
|
3803
|
+
if(d.ok){toast(t('toast.taskRemoved'),'success');loadAdminData();}else{toast(d.error||t('toast.removeFail'),'error');}
|
|
3804
|
+
}catch(e){toast(t('toast.removeFail')+': '+e.message,'error');}
|
|
3805
|
+
}
|
|
3806
|
+
|
|
3807
|
+
function renderAdminSkills(skills){
|
|
3808
|
+
var el=document.getElementById('adminSkillsPanel');
|
|
3809
|
+
if(!el) return;
|
|
3810
|
+
adminSkillsCache=skills;
|
|
3811
|
+
var html='<h3 style="font-size:14px;font-weight:600;color:var(--text);margin-bottom:10px">'+t('admin.sharedSkills')+' ('+skills.length+')</h3>';
|
|
3812
|
+
if(skills.length===0){
|
|
3813
|
+
html+='<div class="admin-empty"><span class="ae-icon">\u{1F9E0}</span>'+t('admin.noSharedSkills')+'</div>';
|
|
3814
|
+
}else{
|
|
3815
|
+
for(var i=0;i<skills.length;i++){
|
|
3816
|
+
var s=skills[i];
|
|
3817
|
+
html+='<div class="admin-card" onclick="openHubSkillDetailFromCache(\\\'admin\\\','+i+')" style="cursor:pointer"><div class="admin-card-header"><div class="admin-card-title">'+esc(s.name||s.id)+'</div>'+
|
|
3818
|
+
'</div>'+
|
|
3819
|
+
'<div class="admin-card-meta">'+
|
|
3820
|
+
(s.description?esc(s.description)+'<br>':'')+
|
|
3821
|
+
t('admin.owner')+esc(s.ownerName||s.sourceUserId||'unknown')+
|
|
3822
|
+
(s.version!=null?' \u00B7 '+t('admin.version')+s.version:'')+
|
|
3823
|
+
(s.qualityScore!=null?' \u00B7 '+t('admin.quality')+s.qualityScore:'')+
|
|
3824
|
+
'</div>'+
|
|
3825
|
+
'<div class="admin-card-actions">'+
|
|
3826
|
+
'<button class="btn btn-sm btn-ghost" onclick="event.stopPropagation();adminDeleteSkill("'+escAttr(s.id)+'","'+escAttr(s.name||s.id)+'")" style="color:var(--rose)">'+t('admin.remove')+'</button>'+
|
|
3827
|
+
'</div></div>';
|
|
2174
3828
|
}
|
|
2175
3829
|
}
|
|
3830
|
+
el.innerHTML=html;
|
|
3831
|
+
}
|
|
3832
|
+
|
|
3833
|
+
async function adminDeleteSkill(skillId,skillName){
|
|
3834
|
+
if(!confirm(t('confirm.removeSkill').replace('{name}',skillName))) return;
|
|
3835
|
+
try{
|
|
3836
|
+
var r=await fetch('/api/admin/shared-skills/'+encodeURIComponent(skillId),{method:'DELETE'});
|
|
3837
|
+
var d=await r.json();
|
|
3838
|
+
if(d.ok){toast(t('toast.skillRemoved'),'success');loadAdminData();}else{toast(d.error||t('toast.removeFail'),'error');}
|
|
3839
|
+
}catch(e){toast(t('toast.removeFail')+': '+e.message,'error');}
|
|
3840
|
+
}
|
|
3841
|
+
|
|
3842
|
+
function renderAdminSharedMemories(memories){
|
|
3843
|
+
var el=document.getElementById('adminSharedMemoriesPanel');
|
|
3844
|
+
if(!el) return;
|
|
3845
|
+
adminMemoriesCache=memories||[];
|
|
3846
|
+
var html='<h3 style="font-size:14px;font-weight:600;color:var(--text);margin-bottom:10px">'+t('admin.sharedMemories')+' ('+(memories||[]).length+')</h3>';
|
|
3847
|
+
if(!memories||memories.length===0){
|
|
3848
|
+
html+='<div class="admin-empty"><span class="ae-icon">\u{1F4AD}</span>'+t('admin.noSharedMemories')+'</div>';
|
|
3849
|
+
}else{
|
|
3850
|
+
for(var i=0;i<memories.length;i++){
|
|
3851
|
+
var m=memories[i];
|
|
3852
|
+
html+='<div class="admin-card" onclick="openHubMemoryDetail(\\\'admin\\\','+i+')" style="cursor:pointer"><div class="admin-card-header"><div class="admin-card-title">'+esc(m.summary||m.content?.slice(0,80)||m.id)+'</div>'+
|
|
3853
|
+
'</div>'+
|
|
3854
|
+
'<div class="admin-card-meta">'+
|
|
3855
|
+
t('admin.owner')+esc(m.ownerName||m.sourceUserId||'unknown')+
|
|
3856
|
+
(m.kind?' \u00B7 Kind: '+esc(m.kind):'')+
|
|
3857
|
+
(m.role?' \u00B7 Role: '+esc(m.role):'')+
|
|
3858
|
+
' \u00B7 '+t('admin.updated')+new Date(m.updatedAt||m.createdAt).toLocaleDateString()+
|
|
3859
|
+
'</div>'+
|
|
3860
|
+
'<div class="admin-card-actions">'+
|
|
3861
|
+
'<button class="btn btn-sm btn-ghost" onclick="event.stopPropagation();adminDeleteMemory("'+escAttr(m.id)+'","'+escAttr(m.summary||m.id)+'")" style="color:var(--rose)">'+t('admin.remove')+'</button>'+
|
|
3862
|
+
'</div></div>';
|
|
3863
|
+
}
|
|
3864
|
+
}
|
|
3865
|
+
el.innerHTML=html;
|
|
3866
|
+
}
|
|
3867
|
+
|
|
3868
|
+
async function adminDeleteMemory(memoryId,memoryTitle){
|
|
3869
|
+
if(!confirm(t('confirm.removeMemory').replace('{name}',memoryTitle))) return;
|
|
3870
|
+
try{
|
|
3871
|
+
var r=await fetch('/api/admin/shared-memories/'+encodeURIComponent(memoryId),{method:'DELETE'});
|
|
3872
|
+
var d=await r.json();
|
|
3873
|
+
if(d.ok){toast(t('toast.memoryRemoved'),'success');loadAdminData();}else{toast(d.error||t('toast.removeFail'),'error');}
|
|
3874
|
+
}catch(e){toast(t('toast.removeFail')+': '+e.message,'error');}
|
|
3875
|
+
}
|
|
3876
|
+
|
|
3877
|
+
function renderSharingMemorySearchResults(data,query){
|
|
3878
|
+
const list=document.getElementById('memoryList');
|
|
3879
|
+
const localHits=(data&&data.local&&Array.isArray(data.local.hits))?data.local.hits:[];
|
|
3880
|
+
const hubHits=(data&&data.hub&&Array.isArray(data.hub.hits))?data.hub.hits:[];
|
|
3881
|
+
document.getElementById('searchMeta').textContent='Search results for "'+query+'"';
|
|
3882
|
+
document.getElementById('sharingSearchMeta').textContent='Local '+localHits.length+' · Hub '+hubHits.length;
|
|
3883
|
+
document.getElementById('pagination').innerHTML='';
|
|
3884
|
+
list.innerHTML=''+
|
|
3885
|
+
'<div class="result-section">'+
|
|
3886
|
+
'<div class="result-section-header"><div class="result-section-title">'+t('search.localResults')+'</div><div class="result-section-sub">'+localHits.length+' hit(s)</div></div>'+
|
|
3887
|
+
'<div class="search-hit-list">'+(localHits.length?localHits.map(function(hit,idx){
|
|
3888
|
+
return '<div class="search-hit-card">'+
|
|
3889
|
+
'<div class="summary">'+(idx+1)+'. '+esc(hit.summary||'(no summary)')+'</div>'+
|
|
3890
|
+
'<div class="excerpt">'+esc(hit.excerpt||'')+'</div>'+
|
|
3891
|
+
'<div class="search-hit-meta">'+
|
|
3892
|
+
'<span class="meta-chip">role: '+esc(hit.role||'unknown')+'</span>'+
|
|
3893
|
+
(hit.score!=null?'<span class="meta-chip">score: '+Math.round(hit.score*100)+'%</span>':'')+
|
|
3894
|
+
(hit.taskId?'<span class="meta-chip">task: '+esc(hit.taskId)+'</span>':'')+
|
|
3895
|
+
'</div>'+
|
|
3896
|
+
'</div>';
|
|
3897
|
+
}).join(''):'<div class="search-hit-card"><div class="excerpt">'+t('search.noLocal')+'</div></div>')+'</div>'+
|
|
3898
|
+
'</div>'+
|
|
3899
|
+
'<div class="result-section">'+
|
|
3900
|
+
'<div class="result-section-header"><div class="result-section-title">'+t('search.hubResults')+'</div><div class="result-section-sub">'+hubHits.length+' hit(s)</div></div>'+
|
|
3901
|
+
'<div class="search-hit-list">'+(hubHits.length?hubHits.map(function(hit,idx){
|
|
3902
|
+
return '<div class="hub-hit-card">'+
|
|
3903
|
+
'<div class="summary">'+(idx+1)+'. '+esc(hit.summary||'(no summary)')+'</div>'+
|
|
3904
|
+
'<div class="excerpt">'+esc(hit.excerpt||'')+'</div>'+
|
|
3905
|
+
'<div class="hub-hit-meta">'+
|
|
3906
|
+
'<span class="meta-chip">owner: '+esc(hit.ownerName||'unknown')+'</span>'+
|
|
3907
|
+
(hit.groupName?'<span class="meta-chip">group: '+esc(hit.groupName)+'</span>':'')+
|
|
3908
|
+
'<span class="meta-chip">visibility: '+esc(hit.visibility||'hub')+'</span>'+
|
|
3909
|
+
'</div>'+
|
|
3910
|
+
'<div class="hub-hit-actions">'+
|
|
3911
|
+
'<button class="btn btn-sm" onclick="openSharedMemoryDetail("'+escAttr(hit.remoteHitId)+'","'+escAttr(hit.summary||t('search.sharedMemory'))+'","'+escAttr(hit.ownerName||'')+'","'+escAttr(hit.groupName||'')+'")">'+t('search.viewDetail')+'</button>'+
|
|
3912
|
+
'</div>'+
|
|
3913
|
+
'</div>';
|
|
3914
|
+
}).join(''):'<div class="hub-hit-card"><div class="excerpt">'+t('search.noHub')+'</div></div>')+'</div>'+
|
|
3915
|
+
'</div>';
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3918
|
+
async function openSharedMemoryDetail(remoteHitId,title,owner,groupName){
|
|
3919
|
+
currentSharedMemoryHitId=remoteHitId;
|
|
3920
|
+
document.getElementById('sharedMemoryOverlay').classList.add('show');
|
|
3921
|
+
document.getElementById('sharedMemoryTitle').textContent=title||t('search.sharedMemory');
|
|
3922
|
+
document.getElementById('sharedMemoryMeta').innerHTML='<span class="meta-item">Hub</span>'+(owner?'<span class="meta-item">'+t('admin.owner')+esc(owner)+'</span>':'')+(groupName?'<span class="meta-item">'+t('admin.group')+esc(groupName)+'</span>':'');
|
|
3923
|
+
document.getElementById('sharedMemorySummary').textContent=t('sharing.loading');
|
|
3924
|
+
document.getElementById('sharedMemoryContent').textContent='';
|
|
3925
|
+
try{
|
|
3926
|
+
const r=await fetch('/api/sharing/memory-detail',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({remoteHitId:remoteHitId})});
|
|
3927
|
+
const d=await r.json();
|
|
3928
|
+
if(d.error) throw new Error(d.error);
|
|
3929
|
+
document.getElementById('sharedMemorySummary').textContent=d.summary||'';
|
|
3930
|
+
document.getElementById('sharedMemoryContent').textContent=d.content||'';
|
|
3931
|
+
}catch(e){
|
|
3932
|
+
document.getElementById('sharedMemorySummary').textContent=t('search.loadFailed');
|
|
3933
|
+
document.getElementById('sharedMemoryContent').textContent=String(e.message||e);
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
|
|
3937
|
+
function closeSharedMemoryDetail(event){
|
|
3938
|
+
if(event && event.target!==document.getElementById('sharedMemoryOverlay')) return;
|
|
3939
|
+
document.getElementById('sharedMemoryOverlay').classList.remove('show');
|
|
3940
|
+
}
|
|
3941
|
+
|
|
3942
|
+
async function openHubMemoryDetail(cacheKey,idx){
|
|
3943
|
+
var arr=cacheKey==='admin'?adminMemoriesCache:hubMemoriesCache;
|
|
3944
|
+
var m=arr[idx];
|
|
3945
|
+
if(!m) return;
|
|
3946
|
+
var overlay=document.getElementById('sharedMemoryOverlay');
|
|
3947
|
+
overlay.classList.add('show');
|
|
3948
|
+
document.getElementById('sharedMemoryTitle').textContent=m.summary||m.content?.slice(0,80)||'(no summary)';
|
|
3949
|
+
var metaHtml='<span class="meta-item">\\u{1F310} Hub</span>'+
|
|
3950
|
+
(m.ownerName?'<span class="meta-item">'+t('admin.owner')+esc(m.ownerName)+'</span>':'')+
|
|
3951
|
+
(m.groupName?'<span class="meta-item">'+t('admin.group')+esc(m.groupName)+'</span>':'')+
|
|
3952
|
+
(m.kind?'<span class="meta-item">Kind: '+esc(m.kind)+'</span>':'')+
|
|
3953
|
+
(m.role?'<span class="meta-item">Role: '+esc(m.role)+'</span>':'')+
|
|
3954
|
+
'<span class="meta-item">visibility: '+esc(m.visibility||'hub')+'</span>'+
|
|
3955
|
+
'<span class="meta-item">'+new Date(m.updatedAt||m.createdAt||0).toLocaleString(dateLoc())+'</span>';
|
|
3956
|
+
document.getElementById('sharedMemoryMeta').innerHTML=metaHtml;
|
|
3957
|
+
document.getElementById('sharedMemorySummary').textContent=m.summary||'';
|
|
3958
|
+
document.getElementById('sharedMemoryContent').textContent=m.content||t('sharing.loading');
|
|
3959
|
+
// try to fetch full content from Hub API
|
|
3960
|
+
var remoteId=m.remoteHitId||m.id;
|
|
3961
|
+
if(remoteId){
|
|
3962
|
+
try{
|
|
3963
|
+
var r=await fetch('/api/sharing/memory-detail',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({remoteHitId:remoteId})});
|
|
3964
|
+
var d=await r.json();
|
|
3965
|
+
if(!d.error&&(d.content||d.summary)){
|
|
3966
|
+
if(d.summary) document.getElementById('sharedMemorySummary').textContent=d.summary;
|
|
3967
|
+
document.getElementById('sharedMemoryContent').textContent=d.content||m.content||'';
|
|
3968
|
+
}
|
|
3969
|
+
}catch(e){}
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
|
|
3973
|
+
function openHubTaskDetailFromCache(cacheKey,idx){
|
|
3974
|
+
var arr=cacheKey==='admin'?adminTasksCache:hubTasksCache;
|
|
3975
|
+
var task=arr[idx];
|
|
3976
|
+
if(!task) return;
|
|
3977
|
+
var overlay=document.getElementById('taskDetailOverlay');
|
|
3978
|
+
overlay.classList.add('show');
|
|
3979
|
+
document.getElementById('taskDetailTitle').textContent=task.title||'(no title)';
|
|
3980
|
+
document.getElementById('taskShareActions').innerHTML='';
|
|
3981
|
+
var meta=[
|
|
3982
|
+
'<span class="meta-item">\\u{1F310} Hub</span>',
|
|
3983
|
+
task.status?'<span class="meta-item"><span class="task-status-badge '+task.status+'">'+esc(task.status)+'</span></span>':'',
|
|
3984
|
+
'<span class="meta-item">'+t('admin.owner')+esc(task.ownerName||'unknown')+'</span>',
|
|
3985
|
+
task.groupName?'<span class="meta-item">'+t('admin.group')+esc(task.groupName)+'</span>':'',
|
|
3986
|
+
'<span class="meta-item">visibility: '+esc(task.visibility||'hub')+'</span>',
|
|
3987
|
+
task.chunkCount!=null?'<span class="meta-item">\\u{1F4DD} '+esc(String(task.chunkCount))+' '+t('tasks.chunks.label')+'</span>':'',
|
|
3988
|
+
task.startedAt?'<span class="meta-item">\\u{1F4C5} '+formatTime(task.startedAt)+'</span>':'',
|
|
3989
|
+
task.endedAt?'<span class="meta-item">\\u2192 '+formatTime(task.endedAt)+'</span>':'',
|
|
3990
|
+
(task.updatedAt||task.createdAt)?'<span class="meta-item">'+t('admin.updated')+new Date(task.updatedAt||task.createdAt).toLocaleString(dateLoc())+'</span>':'',
|
|
3991
|
+
task.sourceTaskId?'<div style="width:100%;margin-top:4px"><span class="meta-item" style="width:100%">'+t('tasks.taskid')+'<span class="task-id-full">'+esc(task.sourceTaskId)+'</span></span></div>':'',
|
|
3992
|
+
].filter(Boolean);
|
|
3993
|
+
document.getElementById('taskDetailMeta').innerHTML=meta.join('');
|
|
3994
|
+
document.getElementById('taskSkillSection').innerHTML='';
|
|
3995
|
+
document.getElementById('taskSkillSection').className='task-skill-section';
|
|
3996
|
+
document.getElementById('taskDetailSummary').innerHTML=task.summary?renderSummaryHtml(task.summary):'<div style="color:var(--text-muted);font-size:13px">'+t('tasks.nochunks')+'</div>';
|
|
3997
|
+
document.getElementById('taskDetailChunks').innerHTML='<div style="color:var(--text-muted);padding:12px;font-size:13px">'+t('tasks.nochunks')+'</div>';
|
|
3998
|
+
}
|
|
3999
|
+
|
|
4000
|
+
function openHubSkillDetailFromCache(cacheKey,idx){
|
|
4001
|
+
var arr=cacheKey==='admin'?adminSkillsCache:hubSkillsCache;
|
|
4002
|
+
var skill=arr[idx];
|
|
4003
|
+
if(!skill) return;
|
|
4004
|
+
var overlay=document.getElementById('skillDetailOverlay');
|
|
4005
|
+
overlay.classList.add('show');
|
|
4006
|
+
document.getElementById('skillDetailTitle').textContent='\\u{1F9E0} '+(skill.name||'(no name)');
|
|
4007
|
+
var qs=skill.qualityScore;
|
|
4008
|
+
var qsBadge=(qs!==null&&qs!==undefined)?'<span class="meta-item"><span class="skill-badge quality '+(qs>=7?'high':qs>=5?'mid':'low')+'">\\u2605 '+(+qs).toFixed(1)+'/10</span></span>':'';
|
|
4009
|
+
var meta=[
|
|
4010
|
+
'<span class="meta-item">\\u{1F310} Hub</span>',
|
|
4011
|
+
skill.version!=null?'<span class="meta-item"><span class="skill-badge version">v'+skill.version+'</span></span>':'',
|
|
4012
|
+
skill.status?'<span class="meta-item"><span class="skill-badge status-'+skill.status+'">'+esc(skill.status)+'</span></span>':'',
|
|
4013
|
+
'<span class="meta-item">visibility: '+esc(skill.visibility||'hub')+'</span>',
|
|
4014
|
+
qsBadge,
|
|
4015
|
+
'<span class="meta-item">'+t('admin.owner')+esc(skill.ownerName||'unknown')+'</span>',
|
|
4016
|
+
skill.groupName?'<span class="meta-item">'+t('admin.group')+esc(skill.groupName)+'</span>':'',
|
|
4017
|
+
(skill.updatedAt||skill.createdAt)?'<span class="meta-item">'+t('admin.updated')+new Date(skill.updatedAt||skill.createdAt).toLocaleString(dateLoc())+'</span>':'',
|
|
4018
|
+
].filter(Boolean);
|
|
4019
|
+
document.getElementById('skillDetailMeta').innerHTML=meta.join('');
|
|
4020
|
+
document.getElementById('skillDetailDesc').textContent=skill.description||'';
|
|
4021
|
+
document.getElementById('skillFilesList').innerHTML='';
|
|
4022
|
+
document.getElementById('skillDetailContent').innerHTML=skill.content?'<pre>'+esc(skill.content)+'</pre>':'';
|
|
4023
|
+
document.getElementById('skillVersionsList').innerHTML='';
|
|
4024
|
+
document.getElementById('skillRelatedTasks').innerHTML='';
|
|
4025
|
+
var visBtn=document.getElementById('skillVisibilityBtn');
|
|
4026
|
+
if(visBtn) visBtn.style.display='none';
|
|
4027
|
+
var dlBtn=document.getElementById('skillDownloadBtn');
|
|
4028
|
+
if(dlBtn) dlBtn.style.display='none';
|
|
4029
|
+
var shareBtn=document.getElementById('skillShareActions');
|
|
4030
|
+
if(shareBtn) shareBtn.innerHTML='';
|
|
4031
|
+
}
|
|
4032
|
+
|
|
4033
|
+
function escAttr(s){return String(s||'').replace(/&/g,'&').replace(/'/g,''').replace(/"/g,'"').replace(/</g,'<').replace(/>/g,'>');}
|
|
4034
|
+
|
|
4035
|
+
function renderTaskShareActions(task){
|
|
4036
|
+
currentTaskDetail=task||null;
|
|
4037
|
+
const el=document.getElementById('taskShareActions');
|
|
4038
|
+
if(!el){return;}
|
|
4039
|
+
if(!task||!task.id){el.innerHTML='';return;}
|
|
4040
|
+
const current=(task.sharingVisibility||task.visibility||null);
|
|
4041
|
+
const isShared=!!current;
|
|
4042
|
+
var statusHtml='';
|
|
4043
|
+
if(isShared){
|
|
4044
|
+
statusHtml='<span style="display:inline-flex;align-items:center;gap:4px;padding:3px 10px;background:#22c55e22;border:1px solid #22c55e44;border-radius:12px;font-size:12px;color:#22c55e">\u2713 '+t('share.alreadyShared')+'</span>';
|
|
4045
|
+
}
|
|
4046
|
+
el.innerHTML=statusHtml+
|
|
4047
|
+
'<button class="btn btn-sm" onclick="shareCurrentTask()">'+(isShared?t('share.updateBtn'):t('share.shareBtn'))+'</button>'+
|
|
4048
|
+
(isShared?'<button class="btn btn-sm btn-ghost" onclick="unshareCurrentTask()">'+t('share.unshareBtn')+'</button>':'');
|
|
4049
|
+
}
|
|
4050
|
+
|
|
4051
|
+
async function shareCurrentTask(){
|
|
4052
|
+
if(!currentTaskDetail) return;
|
|
4053
|
+
const visibility='public';
|
|
4054
|
+
try{
|
|
4055
|
+
const r=await fetch('/api/sharing/tasks/share',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({taskId:currentTaskDetail.id,visibility:visibility})});
|
|
4056
|
+
const d=await r.json();
|
|
4057
|
+
if(d.ok||d.shared){toast(t('toast.taskShared'),'success');currentTaskDetail.sharingVisibility=visibility;renderTaskShareActions(currentTaskDetail);} else {toast(d.error||t('toast.taskShareFail'),'error');}
|
|
4058
|
+
}catch(e){toast(t('toast.taskShareFail')+': '+e.message,'error');}
|
|
4059
|
+
}
|
|
4060
|
+
|
|
4061
|
+
async function unshareCurrentTask(){
|
|
4062
|
+
if(!currentTaskDetail) return;
|
|
4063
|
+
try{
|
|
4064
|
+
const r=await fetch('/api/sharing/tasks/unshare',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({taskId:currentTaskDetail.id})});
|
|
4065
|
+
const d=await r.json();
|
|
4066
|
+
if(d.ok||d.unshared){toast(t('toast.taskUnshared'),'success');currentTaskDetail.sharingVisibility=null;renderTaskShareActions(currentTaskDetail);} else {toast(d.error||t('toast.taskUnshareFail'),'error');}
|
|
4067
|
+
}catch(e){toast(t('toast.taskUnshareFail')+': '+e.message,'error');}
|
|
4068
|
+
}
|
|
4069
|
+
|
|
4070
|
+
function renderSkillShareActions(skill){
|
|
4071
|
+
const el=document.getElementById('skillShareActions');
|
|
4072
|
+
if(!el){return;}
|
|
4073
|
+
if(!skill||!skill.id){el.innerHTML='';return;}
|
|
4074
|
+
const current=(skill.sharingVisibility||null);
|
|
4075
|
+
const isShared=!!current;
|
|
4076
|
+
var statusHtml='';
|
|
4077
|
+
if(isShared){
|
|
4078
|
+
statusHtml='<span style="display:inline-flex;align-items:center;gap:4px;padding:3px 10px;background:#22c55e22;border:1px solid #22c55e44;border-radius:12px;font-size:12px;color:#22c55e">\u2713 '+t('share.alreadyShared')+'</span>';
|
|
4079
|
+
}
|
|
4080
|
+
el.innerHTML=statusHtml+
|
|
4081
|
+
'<button class="btn btn-sm" onclick="shareCurrentSkill()">'+(isShared?t('share.updateBtn'):t('share.shareBtn'))+'</button>'+
|
|
4082
|
+
(isShared?'<button class="btn btn-sm btn-ghost" onclick="unshareCurrentSkill()">'+t('share.unshareBtn')+'</button>':'');
|
|
4083
|
+
}
|
|
4084
|
+
|
|
4085
|
+
async function shareCurrentSkill(){
|
|
4086
|
+
if(!currentSkillDetail) return;
|
|
4087
|
+
const visibility='public';
|
|
4088
|
+
try{
|
|
4089
|
+
const r=await fetch('/api/sharing/skills/share',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({skillId:currentSkillDetail.id,visibility:visibility})});
|
|
4090
|
+
const d=await r.json();
|
|
4091
|
+
if(d.ok){toast(t('toast.skillShared'),'success');currentSkillDetail.sharingVisibility=visibility;renderSkillShareActions(currentSkillDetail);} else {toast(d.error||t('toast.skillShareFail'),'error');}
|
|
4092
|
+
}catch(e){toast(t('toast.skillShareFail')+': '+e.message,'error');}
|
|
4093
|
+
}
|
|
4094
|
+
|
|
4095
|
+
async function unshareCurrentSkill(){
|
|
4096
|
+
if(!currentSkillDetail) return;
|
|
4097
|
+
try{
|
|
4098
|
+
const r=await fetch('/api/sharing/skills/unshare',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({skillId:currentSkillDetail.id})});
|
|
4099
|
+
const d=await r.json();
|
|
4100
|
+
if(d.ok){toast(t('toast.skillUnshared'),'success');currentSkillDetail.sharingVisibility=null;renderSkillShareActions(currentSkillDetail);} else {toast(d.error||t('toast.skillUnshareFail'),'error');}
|
|
4101
|
+
}catch(e){toast(t('toast.skillUnshareFail')+': '+e.message,'error');}
|
|
4102
|
+
}
|
|
4103
|
+
|
|
4104
|
+
async function shareMemoryPrompt(chunkId){
|
|
4105
|
+
try{
|
|
4106
|
+
const r=await fetch('/api/sharing/memories/share',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({chunkId:chunkId,visibility:'public'})});
|
|
4107
|
+
const d=await r.json();
|
|
4108
|
+
if(d.ok){toast(t('toast.memoryShared'),'success');loadMemories();} else {toast(d.error||t('toast.memoryShareFail'),'error');}
|
|
4109
|
+
}catch(e){toast(t('toast.memoryShareFail')+': '+e.message,'error');}
|
|
4110
|
+
}
|
|
4111
|
+
|
|
4112
|
+
async function unshareMemory(chunkId){
|
|
4113
|
+
try{
|
|
4114
|
+
const r=await fetch('/api/sharing/memories/unshare',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({chunkId:chunkId})});
|
|
4115
|
+
const d=await r.json();
|
|
4116
|
+
if(d.ok){toast(t('toast.memoryUnshared'),'success');loadMemories();} else {toast(d.error||t('toast.memoryUnshareFail'),'error');}
|
|
4117
|
+
}catch(e){toast(t('toast.memoryUnshareFail')+': '+e.message,'error');}
|
|
4118
|
+
}
|
|
4119
|
+
|
|
4120
|
+
function debounceSkillSearch(){
|
|
4121
|
+
clearTimeout(skillSearchTimer);
|
|
4122
|
+
skillSearchTimer=setTimeout(function(){loadSkills();},300);
|
|
4123
|
+
}
|
|
4124
|
+
|
|
4125
|
+
async function pullHubSkill(skillId){
|
|
4126
|
+
try{
|
|
4127
|
+
const r=await fetch('/api/sharing/skills/pull',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({skillId:skillId})});
|
|
4128
|
+
const d=await r.json();
|
|
4129
|
+
if(d.ok||d.pulled||d.details){toast(t('toast.skillPulled'),'success');loadSkills();} else {toast(d.error||t('toast.skillPullFail'),'error');}
|
|
4130
|
+
}catch(e){toast(t('toast.skillPullFail')+': '+e.message,'error');}
|
|
2176
4131
|
}
|
|
2177
4132
|
|
|
2178
4133
|
// ─── Logs ───
|
|
@@ -2231,7 +4186,7 @@ function renderLogToolFilter(tools,current){
|
|
|
2231
4186
|
|
|
2232
4187
|
function formatLogTime(ts){
|
|
2233
4188
|
const d=new Date(ts);
|
|
2234
|
-
const time=d.toLocaleTimeString(
|
|
4189
|
+
const time=d.toLocaleTimeString(dateLoc(),{hour:'2-digit',minute:'2-digit',second:'2-digit',hour12:false});
|
|
2235
4190
|
const y=d.getFullYear();
|
|
2236
4191
|
const m=String(d.getMonth()+1).padStart(2,'0');
|
|
2237
4192
|
const day=String(d.getDate()).padStart(2,'0');
|
|
@@ -2492,6 +4447,7 @@ function setMetricsDays(d){
|
|
|
2492
4447
|
}
|
|
2493
4448
|
|
|
2494
4449
|
async function loadMetrics(){
|
|
4450
|
+
try{
|
|
2495
4451
|
const r=await fetch('/api/metrics?days='+metricsDays);
|
|
2496
4452
|
const d=await r.json();
|
|
2497
4453
|
document.getElementById('mTotal').textContent=formatNum(d.totals.memories);
|
|
@@ -2500,9 +4456,11 @@ async function loadMetrics(){
|
|
|
2500
4456
|
document.getElementById('mEmbeddings').textContent=formatNum(d.totals.embeddings);
|
|
2501
4457
|
renderChartWrites(d.writesPerDay);
|
|
2502
4458
|
loadToolMetrics();
|
|
4459
|
+
}catch(e){console.error('loadMetrics',e)}
|
|
2503
4460
|
}
|
|
2504
4461
|
|
|
2505
4462
|
function formatNum(n){return n>=1e6?(n/1e6).toFixed(1)+'M':n>=1e3?(n/1e3).toFixed(1)+'k':String(n);}
|
|
4463
|
+
function dateLoc(){return curLang==='zh'?'zh-CN':'en-US';}
|
|
2506
4464
|
|
|
2507
4465
|
/* ─── Tasks View Logic ─── */
|
|
2508
4466
|
let tasksStatusFilter='';
|
|
@@ -2518,29 +4476,24 @@ function setTaskStatusFilter(btn,status){
|
|
|
2518
4476
|
}
|
|
2519
4477
|
|
|
2520
4478
|
async function loadTasks(){
|
|
4479
|
+
const scope=document.getElementById('taskSearchScope')?document.getElementById('taskSearchScope').value:taskSearchScope;
|
|
4480
|
+
taskSearchScope=scope||'local';
|
|
4481
|
+
if(taskSearchScope!=='local'){ return loadHubTasks(); }
|
|
2521
4482
|
const list=document.getElementById('tasksList');
|
|
2522
4483
|
list.innerHTML='<div class="spinner"></div>';
|
|
2523
4484
|
try{
|
|
2524
4485
|
const params=new URLSearchParams({limit:String(TASKS_PER_PAGE),offset:String(tasksPage*TASKS_PER_PAGE)});
|
|
2525
4486
|
if(tasksStatusFilter) params.set('status',tasksStatusFilter);
|
|
2526
|
-
const
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
4487
|
+
const [data,allD,activeD,compD,skipD]=await Promise.all([
|
|
4488
|
+
fetch('/api/tasks?'+params).then(r=>r.json()),
|
|
4489
|
+
fetch('/api/tasks?limit=1&offset=0').then(r=>r.json()),
|
|
4490
|
+
fetch('/api/tasks?status=active&limit=1&offset=0').then(r=>r.json()),
|
|
4491
|
+
fetch('/api/tasks?status=completed&limit=1&offset=0').then(r=>r.json()),
|
|
4492
|
+
fetch('/api/tasks?status=skipped&limit=1&offset=0').then(r=>r.json())
|
|
4493
|
+
]);
|
|
2532
4494
|
document.getElementById('tasksTotalCount').textContent=formatNum(allD.total);
|
|
2533
|
-
|
|
2534
|
-
const activeR=await fetch('/api/tasks?status=active&limit=1&offset=0');
|
|
2535
|
-
const activeD=await activeR.json();
|
|
2536
4495
|
document.getElementById('tasksActiveCount').textContent=formatNum(activeD.total);
|
|
2537
|
-
|
|
2538
|
-
const compR=await fetch('/api/tasks?status=completed&limit=1&offset=0');
|
|
2539
|
-
const compD=await compR.json();
|
|
2540
4496
|
document.getElementById('tasksCompletedCount').textContent=formatNum(compD.total);
|
|
2541
|
-
|
|
2542
|
-
const skipR=await fetch('/api/tasks?status=skipped&limit=1&offset=0');
|
|
2543
|
-
const skipD=await skipR.json();
|
|
2544
4497
|
document.getElementById('tasksSkippedCount').textContent=formatNum(skipD.total);
|
|
2545
4498
|
|
|
2546
4499
|
if(!data.tasks||data.tasks.length===0){
|
|
@@ -2606,13 +4559,16 @@ async function openTaskDetail(taskId){
|
|
|
2606
4559
|
document.getElementById('taskSkillSection').className='task-skill-section';
|
|
2607
4560
|
document.getElementById('taskDetailSummary').textContent='';
|
|
2608
4561
|
document.getElementById('taskDetailChunks').innerHTML='<div class="spinner"></div>';
|
|
4562
|
+
document.getElementById('taskShareActions').innerHTML='';
|
|
2609
4563
|
document.getElementById('taskDetailActions').innerHTML='';
|
|
2610
4564
|
|
|
2611
4565
|
try{
|
|
2612
4566
|
const r=await fetch('/api/task/'+taskId);
|
|
2613
4567
|
const task=await r.json();
|
|
4568
|
+
currentTaskDetail=task;
|
|
2614
4569
|
|
|
2615
4570
|
document.getElementById('taskDetailTitle').textContent=task.title||t('tasks.untitled');
|
|
4571
|
+
renderTaskShareActions(task);
|
|
2616
4572
|
|
|
2617
4573
|
const meta=[
|
|
2618
4574
|
'<span class="meta-item"><span class="task-status-badge '+task.status+'">'+t('tasks.status.'+task.status)+'</span></span>',
|
|
@@ -2781,60 +4737,177 @@ function setSkillStatusFilter(btn,status){
|
|
|
2781
4737
|
|
|
2782
4738
|
async function loadSkills(){
|
|
2783
4739
|
const list=document.getElementById('skillsList');
|
|
4740
|
+
const hubList=document.getElementById('hubSkillsList');
|
|
2784
4741
|
list.innerHTML='<div class="spinner"></div>';
|
|
4742
|
+
var hubSection=document.getElementById('hubSkillsSection');
|
|
4743
|
+
if(hubList){
|
|
4744
|
+
if(skillSearchScope==='local'){
|
|
4745
|
+
if(hubSection) hubSection.style.display='none';
|
|
4746
|
+
}else{
|
|
4747
|
+
if(hubSection) hubSection.style.display='block';
|
|
4748
|
+
hubList.innerHTML='<div class="spinner"></div>';
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
|
|
4752
|
+
const query=(document.getElementById('skillSearchInput')?.value||'').trim();
|
|
4753
|
+
const scope=document.getElementById('skillSearchScope') ? document.getElementById('skillSearchScope').value : skillSearchScope;
|
|
4754
|
+
skillSearchScope=scope||'local';
|
|
4755
|
+
|
|
2785
4756
|
try{
|
|
2786
4757
|
const params=new URLSearchParams();
|
|
2787
4758
|
if(skillsStatusFilter) params.set('status',skillsStatusFilter);
|
|
2788
4759
|
const visFilter=document.getElementById('skillVisibilityFilter')?.value;
|
|
2789
4760
|
if(visFilter) params.set('visibility',visFilter);
|
|
2790
|
-
const r=await fetch('/api/skills?'+params);
|
|
2791
|
-
const data=await r.json();
|
|
2792
4761
|
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
4762
|
+
const localRes=await fetch('/api/skills?'+params.toString());
|
|
4763
|
+
const localData=await localRes.json();
|
|
4764
|
+
let localSkills=Array.isArray(localData.skills)?localData.skills:[];
|
|
4765
|
+
if(query){
|
|
4766
|
+
const q=query.toLowerCase();
|
|
4767
|
+
localSkills=localSkills.filter(skill=>{
|
|
4768
|
+
const haystack=[skill.name,skill.description,skill.tags].filter(Boolean).join(' ').toLowerCase();
|
|
4769
|
+
return haystack.includes(q);
|
|
4770
|
+
});
|
|
4771
|
+
}
|
|
2798
4772
|
|
|
2799
|
-
|
|
2800
|
-
|
|
4773
|
+
const renderLocalCards=function(skills){
|
|
4774
|
+
if(!skills||skills.length===0){
|
|
4775
|
+
return '<div style="text-align:center;padding:48px;color:var(--text-muted);font-size:14px">'+(query?t('skills.search.noresult'):t('skills.empty'))+'</div>';
|
|
4776
|
+
}
|
|
4777
|
+
return skills.map(skill=>{
|
|
4778
|
+
const timeStr=formatTime(skill.createdAt);
|
|
4779
|
+
const tags=parseTags(skill.tags);
|
|
4780
|
+
const installedClass=skill.installed?'installed':'';
|
|
4781
|
+
const statusClass=skill.status==='archived'?'archived':(skill.status==='draft'?'draft':'');
|
|
4782
|
+
const sourceLabel=tags.includes('hub-import')?'hub-import':skill.sourceType;
|
|
4783
|
+
const qs=skill.qualityScore;
|
|
4784
|
+
const qsBadge=qs!==null&&qs!==undefined?'<span class="skill-badge quality '+(qs>=7?'high':qs>=5?'mid':'low')+'">★ '+qs.toFixed(1)+'</span>':'';
|
|
4785
|
+
const visBadge=skill.visibility==='public'?'<span class="skill-badge visibility-public">🌐 '+t('skills.visibility.public')+'</span>':'';
|
|
4786
|
+
return '<div class="skill-card '+installedClass+' '+statusClass+'" onclick="openSkillDetail("'+escAttr(skill.id)+'")">'+
|
|
4787
|
+
'<div class="skill-card-top">'+
|
|
4788
|
+
'<div class="skill-card-name">🧠 '+esc(skill.name)+'</div>'+
|
|
4789
|
+
'<div class="skill-card-badges">'+
|
|
4790
|
+
qsBadge+
|
|
4791
|
+
'<span class="skill-badge version">v'+skill.version+'</span>'+
|
|
4792
|
+
visBadge+
|
|
4793
|
+
(skill.installed?'<span class="skill-badge installed">'+t('skills.installed.badge')+'</span>':'')+
|
|
4794
|
+
'<span class="skill-badge status-'+skill.status+'">'+t('skills.status.'+skill.status)+'</span>'+
|
|
4795
|
+
'</div>'+
|
|
4796
|
+
'</div>'+
|
|
4797
|
+
'<div class="skill-card-desc">'+esc(skill.description)+'</div>'+
|
|
4798
|
+
'<div class="skill-card-bottom">'+
|
|
4799
|
+
'<span class="tag"><span class="icon">📅</span> '+timeStr+'</span>'+
|
|
4800
|
+
'<span class="tag"><span class="icon">📦</span> '+sourceLabel+'</span>'+
|
|
4801
|
+
(tags.length>0?'<div class="skill-card-tags">'+tags.map(t=>'<span class="skill-tag">'+esc(t)+'</span>').join('')+'</div>':'')+
|
|
4802
|
+
'</div>'+
|
|
4803
|
+
'</div>';
|
|
4804
|
+
}).join('');
|
|
4805
|
+
};
|
|
4806
|
+
|
|
4807
|
+
list.innerHTML=renderLocalCards(localSkills);
|
|
4808
|
+
|
|
4809
|
+
if(skillSearchScope==='local'){
|
|
4810
|
+
if(hubSection) hubSection.style.display='none';
|
|
4811
|
+
document.getElementById('skillSearchMeta').textContent=query?(t('skills.search.local')+' '+localSkills.length):'';
|
|
4812
|
+
document.getElementById('skillsTotalCount').textContent=formatNum(localSkills.length);
|
|
4813
|
+
document.getElementById('skillsActiveCount').textContent=formatNum(localSkills.filter(s=>s.status==='active').length);
|
|
4814
|
+
document.getElementById('skillsDraftCount').textContent=formatNum(localSkills.filter(s=>s.status==='draft').length);
|
|
4815
|
+
document.getElementById('skillsInstalledCount').textContent=formatNum(localSkills.filter(s=>s.installed).length);
|
|
4816
|
+
document.getElementById('skillsPublicCount').textContent=formatNum(localSkills.filter(s=>s.visibility==='public').length);
|
|
2801
4817
|
return;
|
|
2802
4818
|
}
|
|
2803
4819
|
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
4820
|
+
if(!query){
|
|
4821
|
+
if(hubSection) hubSection.style.display='block';
|
|
4822
|
+
if(hubList){ loadHubSkills(hubList); }
|
|
4823
|
+
document.getElementById('skillSearchMeta').textContent=t('skills.search.local')+' '+localSkills.length;
|
|
4824
|
+
document.getElementById('skillsTotalCount').textContent=formatNum(localSkills.length);
|
|
4825
|
+
document.getElementById('skillsActiveCount').textContent=formatNum(localSkills.filter(s=>s.status==='active').length);
|
|
4826
|
+
document.getElementById('skillsDraftCount').textContent=formatNum(localSkills.filter(s=>s.status==='draft').length);
|
|
4827
|
+
document.getElementById('skillsInstalledCount').textContent=formatNum(localSkills.filter(s=>s.installed).length);
|
|
4828
|
+
document.getElementById('skillsPublicCount').textContent=formatNum(localSkills.filter(s=>s.visibility==='public').length);
|
|
4829
|
+
return;
|
|
4830
|
+
}
|
|
4831
|
+
|
|
4832
|
+
const sharingParams=new URLSearchParams();
|
|
4833
|
+
sharingParams.set('query',query);
|
|
4834
|
+
sharingParams.set('scope',skillSearchScope);
|
|
4835
|
+
sharingParams.set('maxResults','20');
|
|
4836
|
+
const r=await fetch('/api/sharing/search/skills?'+sharingParams.toString());
|
|
4837
|
+
const data=await r.json();
|
|
4838
|
+
const localHits=(data.local&&Array.isArray(data.local.hits))?data.local.hits:[];
|
|
4839
|
+
const hubHits=(data.hub&&Array.isArray(data.hub.hits))?data.hub.hits:[];
|
|
4840
|
+
|
|
4841
|
+
list.innerHTML=localHits.length?localHits.map(function(skill){
|
|
4842
|
+
return '<div class="hub-skill-card" onclick="openSkillDetail("'+escAttr(skill.skillId)+'")">'+
|
|
4843
|
+
'<div class="summary">'+esc(skill.name)+'</div>'+
|
|
4844
|
+
'<div class="excerpt">'+esc(skill.description||'')+'</div>'+
|
|
4845
|
+
'<div class="hub-skill-meta"><span class="meta-chip">visibility: '+esc(skill.visibility||'private')+'</span><span class="meta-chip">owner: '+esc(skill.owner||'agent:main')+'</span></div>'+
|
|
4846
|
+
'</div>';
|
|
4847
|
+
}).join(''):'<div style="text-align:center;padding:48px;color:var(--text-muted);font-size:14px">'+t('skills.search.noresult')+'</div>';
|
|
4848
|
+
|
|
4849
|
+
if(hubList){
|
|
4850
|
+
if(hubSection) hubSection.style.display=hubHits.length?'block':'none';
|
|
4851
|
+
hubList.innerHTML=hubHits.length?hubHits.map(function(skill){
|
|
4852
|
+
return '<div class="hub-skill-card">'+
|
|
4853
|
+
'<div class="summary">'+esc(skill.name)+'</div>'+
|
|
4854
|
+
'<div class="excerpt">'+esc(skill.description||'')+'</div>'+
|
|
4855
|
+
'<div class="hub-skill-meta">'+
|
|
4856
|
+
'<span class="meta-chip">owner: '+esc(skill.ownerName||'unknown')+'</span>'+
|
|
4857
|
+
(skill.groupName?'<span class="meta-chip">group: '+esc(skill.groupName)+'</span>':'')+
|
|
4858
|
+
'<span class="meta-chip">visibility: '+esc(skill.visibility||'hub')+'</span>'+
|
|
4859
|
+
(skill.version!=null?'<span class="meta-chip">v'+skill.version+'</span>':'')+
|
|
2821
4860
|
'</div>'+
|
|
4861
|
+
'<div class="hub-skill-actions"><button class="btn btn-sm" onclick="event.stopPropagation();pullHubSkill("'+escAttr(skill.skillId)+'")">Pull to Local</button></div>'+
|
|
4862
|
+
'</div>';
|
|
4863
|
+
}).join(''):'';
|
|
4864
|
+
}
|
|
4865
|
+
|
|
4866
|
+
document.getElementById('skillSearchMeta').textContent=t('skills.search.local')+' '+localHits.length+(hubHits.length?' · Hub '+hubHits.length:'');
|
|
4867
|
+
document.getElementById('skillsTotalCount').textContent=formatNum(localHits.length+hubHits.length);
|
|
4868
|
+
document.getElementById('skillsActiveCount').textContent=formatNum(localHits.length);
|
|
4869
|
+
document.getElementById('skillsDraftCount').textContent='0';
|
|
4870
|
+
document.getElementById('skillsInstalledCount').textContent='-';
|
|
4871
|
+
document.getElementById('skillsPublicCount').textContent=formatNum(hubHits.filter(function(s){return s.visibility==='public';}).length);
|
|
4872
|
+
}catch(e){
|
|
4873
|
+
list.innerHTML='<div style="text-align:center;padding:24px;color:var(--rose)">'+t('skills.load.error')+': '+esc(String(e))+'</div>';
|
|
4874
|
+
if(hubList){
|
|
4875
|
+
hubList.innerHTML='<div style="text-align:center;padding:24px;color:var(--rose)">'+t('skills.load.error')+'</div>';
|
|
4876
|
+
}
|
|
4877
|
+
}
|
|
4878
|
+
}
|
|
4879
|
+
|
|
4880
|
+
async function loadHubSkills(hubList){
|
|
4881
|
+
if(!hubList) hubList=document.getElementById('hubSkillsList');
|
|
4882
|
+
if(!hubList) return;
|
|
4883
|
+
var hubSection=document.getElementById('hubSkillsSection');
|
|
4884
|
+
hubList.innerHTML='<div class="spinner"></div>';
|
|
4885
|
+
try{
|
|
4886
|
+
const r=await fetch('/api/sharing/skills/list?limit=40');
|
|
4887
|
+
const d=await r.json();
|
|
4888
|
+
const skills=Array.isArray(d.skills)?d.skills:[];
|
|
4889
|
+
hubSkillsCache=skills;
|
|
4890
|
+
if(!skills.length){
|
|
4891
|
+
if(hubSection) hubSection.style.display='none';
|
|
4892
|
+
return;
|
|
4893
|
+
}
|
|
4894
|
+
if(hubSection) hubSection.style.display='block';
|
|
4895
|
+
hubList.innerHTML=skills.map(function(skill,idx){
|
|
4896
|
+
return '<div class="hub-skill-card" onclick="openHubSkillDetailFromCache(\\\'hub\\\',' +idx+')" style="cursor:pointer">'+
|
|
4897
|
+
'<div class="summary">'+esc(skill.name)+'</div>'+
|
|
4898
|
+
'<div class="excerpt">'+esc(skill.description||'')+'</div>'+
|
|
4899
|
+
'<div class="hub-skill-meta">'+
|
|
4900
|
+
'<span class="meta-chip">owner: '+esc(skill.ownerName||'unknown')+'</span>'+
|
|
4901
|
+
(skill.groupName?'<span class="meta-chip">group: '+esc(skill.groupName)+'</span>':'')+
|
|
4902
|
+
'<span class="meta-chip">visibility: '+esc(skill.visibility||'hub')+'</span>'+
|
|
4903
|
+
(skill.version!=null?'<span class="meta-chip">v'+skill.version+'</span>':'')+
|
|
2822
4904
|
'</div>'+
|
|
2823
|
-
'<div class="skill-
|
|
2824
|
-
'<div class="skill-card-bottom">'+
|
|
2825
|
-
'<span class="tag"><span class="icon">\\u{1F4C5}</span> '+timeStr+'</span>'+
|
|
2826
|
-
'<span class="tag"><span class="icon">\\u{1F4E6}</span> '+skill.sourceType+'</span>'+
|
|
2827
|
-
(tags.length>0?'<div class="skill-card-tags">'+tags.map(t=>'<span class="skill-tag">'+esc(t)+'</span>').join('')+'</div>':'')+
|
|
2828
|
-
'</div>'+
|
|
2829
|
-
'<div class="card-actions" onclick="event.stopPropagation()">'+
|
|
2830
|
-
'<button class="btn btn-sm btn-ghost" onclick="openSkillDetail(\\''+skill.id+'\\')">'+t('card.expand')+'</button>'+
|
|
2831
|
-
(skill.visibility==='public'?'<button class="btn btn-sm btn-ghost" onclick="toggleSkillPublic(\\''+skill.id+'\\',false)">\\u{1F512} '+t('skills.setPrivate')+'</button>':'<button class="btn btn-sm btn-ghost" onclick="toggleSkillPublic(\\''+skill.id+'\\',true)">\\u{1F310} '+t('skills.setPublic')+'</button>')+
|
|
2832
|
-
'<button class="btn btn-sm btn-ghost" style="color:var(--accent)" onclick="deleteSkill(\\''+skill.id+'\\')">'+t('skill.delete')+'</button>'+
|
|
2833
|
-
'</div>'+
|
|
4905
|
+
'<div class="hub-skill-actions"><button class="btn btn-sm" onclick="event.stopPropagation();pullHubSkill(\\''+escAttr(skill.sourceSkillId)+'\\')">Pull to Local</button></div>'+
|
|
2834
4906
|
'</div>';
|
|
2835
4907
|
}).join('');
|
|
2836
4908
|
}catch(e){
|
|
2837
|
-
|
|
4909
|
+
if(hubSection) hubSection.style.display='none';
|
|
4910
|
+
hubList.innerHTML='';
|
|
2838
4911
|
}
|
|
2839
4912
|
}
|
|
2840
4913
|
|
|
@@ -2843,6 +4916,7 @@ function parseTags(tagsStr){
|
|
|
2843
4916
|
}
|
|
2844
4917
|
|
|
2845
4918
|
let currentSkillId='';
|
|
4919
|
+
let currentSkillDetail=null;
|
|
2846
4920
|
|
|
2847
4921
|
async function openSkillDetail(skillId){
|
|
2848
4922
|
currentSkillId=skillId;
|
|
@@ -2855,6 +4929,8 @@ async function openSkillDetail(skillId){
|
|
|
2855
4929
|
document.getElementById('skillDetailContent').innerHTML='<div class="spinner"></div>';
|
|
2856
4930
|
document.getElementById('skillVersionsList').innerHTML='<div class="spinner"></div>';
|
|
2857
4931
|
document.getElementById('skillRelatedTasks').innerHTML='';
|
|
4932
|
+
var vb=document.getElementById('skillVisibilityBtn');if(vb)vb.style.display='';
|
|
4933
|
+
var db=document.getElementById('skillDownloadBtn');if(db)db.style.display='';
|
|
2858
4934
|
document.getElementById('skillDetailActions').innerHTML='';
|
|
2859
4935
|
|
|
2860
4936
|
try{
|
|
@@ -2900,6 +4976,8 @@ async function openSkillDetail(skillId){
|
|
|
2900
4976
|
}
|
|
2901
4977
|
|
|
2902
4978
|
document.getElementById('skillDetailDesc').textContent=skill.description;
|
|
4979
|
+
currentSkillDetail=skill;
|
|
4980
|
+
renderSkillShareActions(skill);
|
|
2903
4981
|
|
|
2904
4982
|
if(files.length>0){
|
|
2905
4983
|
const fileIcons={'skill':'\\u{1F4D6}','script':'\\u{2699}','reference':'\\u{1F4CE}','file':'\\u{1F4C4}'};
|
|
@@ -3107,6 +5185,9 @@ function timeAgo(ts){
|
|
|
3107
5185
|
}
|
|
3108
5186
|
|
|
3109
5187
|
/* ─── Settings / Config ─── */
|
|
5188
|
+
function syncHostToggles(){}
|
|
5189
|
+
function onProviderChange(){}
|
|
5190
|
+
|
|
3110
5191
|
async function loadConfig(){
|
|
3111
5192
|
try{
|
|
3112
5193
|
const r=await fetch('/api/config');
|
|
@@ -3141,6 +5222,26 @@ async function loadConfig(){
|
|
|
3141
5222
|
|
|
3142
5223
|
const tel=cfg.telemetry||{};
|
|
3143
5224
|
document.getElementById('cfgTelemetryEnabled').checked=tel.enabled!==false;
|
|
5225
|
+
|
|
5226
|
+
const sharing=cfg.sharing||{};
|
|
5227
|
+
const caps=sharing.capabilities||{};
|
|
5228
|
+
const embProv=(cfg.embedding||{}).provider;
|
|
5229
|
+
const sumProv=(cfg.summarizer||{}).provider;
|
|
5230
|
+
const skProv=((cfg.skillEvolution||{}).summarizer||{}).provider;
|
|
5231
|
+
|
|
5232
|
+
|
|
5233
|
+
document.getElementById('cfgSharingEnabled').checked=!!sharing.enabled;
|
|
5234
|
+
_sharingRole=sharing.role||'client';
|
|
5235
|
+
var hub=sharing.hub||{};
|
|
5236
|
+
var client=sharing.client||{};
|
|
5237
|
+
document.getElementById('cfgHubPort').value=hub.port||18800;
|
|
5238
|
+
document.getElementById('cfgHubTeamName').value=hub.teamName||'';
|
|
5239
|
+
document.getElementById('cfgHubTeamToken').value=hub.teamToken||'';
|
|
5240
|
+
document.getElementById('cfgClientHubAddress').value=client.hubAddress||'';
|
|
5241
|
+
document.getElementById('cfgClientTeamToken').value=client.teamToken||'';
|
|
5242
|
+
document.getElementById('cfgClientUserToken').value=client.userToken||'';
|
|
5243
|
+
onSharingToggle();
|
|
5244
|
+
updateHubShareInfo();
|
|
3144
5245
|
}catch(e){
|
|
3145
5246
|
console.error('loadConfig error',e);
|
|
3146
5247
|
}
|
|
@@ -3222,6 +5323,42 @@ async function saveConfig(){
|
|
|
3222
5323
|
|
|
3223
5324
|
function done(){saveBtn.disabled=false;saveBtn.textContent=t('settings.save');}
|
|
3224
5325
|
|
|
5326
|
+
var sharingEnabled=document.getElementById('cfgSharingEnabled').checked;
|
|
5327
|
+
cfg.sharing={
|
|
5328
|
+
enabled:sharingEnabled,
|
|
5329
|
+
role:_sharingRole,
|
|
5330
|
+
capabilities:{}
|
|
5331
|
+
};
|
|
5332
|
+
if(sharingEnabled&&_sharingRole==='hub'){
|
|
5333
|
+
var hubPort=document.getElementById('cfgHubPort').value.trim();
|
|
5334
|
+
var hubTeamName=document.getElementById('cfgHubTeamName').value.trim();
|
|
5335
|
+
var hubTeamToken=document.getElementById('cfgHubTeamToken').value.trim();
|
|
5336
|
+
cfg.sharing.hub={port:hubPort?Number(hubPort):18800};
|
|
5337
|
+
if(hubTeamName) cfg.sharing.hub.teamName=hubTeamName;
|
|
5338
|
+
if(hubTeamToken) cfg.sharing.hub.teamToken=hubTeamToken;
|
|
5339
|
+
cfg.sharing.client={hubAddress:'',userToken:'',teamToken:''};
|
|
5340
|
+
}
|
|
5341
|
+
if(sharingEnabled&&_sharingRole==='client'){
|
|
5342
|
+
var clientAddr=document.getElementById('cfgClientHubAddress').value.trim();
|
|
5343
|
+
var clientTeamToken=document.getElementById('cfgClientTeamToken').value.trim();
|
|
5344
|
+
var clientUserToken=document.getElementById('cfgClientUserToken').value.trim();
|
|
5345
|
+
cfg.sharing.client={};
|
|
5346
|
+
if(clientAddr) cfg.sharing.client.hubAddress=clientAddr;
|
|
5347
|
+
if(clientTeamToken) cfg.sharing.client.teamToken=clientTeamToken;
|
|
5348
|
+
if(clientUserToken) cfg.sharing.client.userToken=clientUserToken;
|
|
5349
|
+
cfg.sharing.hub={port:18800,teamName:'',teamToken:''};
|
|
5350
|
+
if(clientAddr){
|
|
5351
|
+
try{
|
|
5352
|
+
var ips=await fetch('/api/local-ips').then(function(r){return r.json();});
|
|
5353
|
+
var localAddrs=['127.0.0.1','localhost','0.0.0.0'].concat(ips.ips||[]);
|
|
5354
|
+
var parsed=new URL(clientAddr.indexOf('://')>-1?clientAddr:'http://'+clientAddr);
|
|
5355
|
+
if(localAddrs.indexOf(parsed.hostname)>=0){
|
|
5356
|
+
done();toast(t('sharing.cannotJoinSelf'),'error');return;
|
|
5357
|
+
}
|
|
5358
|
+
}catch(e){}
|
|
5359
|
+
}
|
|
5360
|
+
}
|
|
5361
|
+
|
|
3225
5362
|
// 1) Embedding model is required
|
|
3226
5363
|
if(!embP||embP===''){done();toast(t('settings.save.emb.required'),'error');return;}
|
|
3227
5364
|
|
|
@@ -3278,6 +5415,10 @@ async function saveConfig(){
|
|
|
3278
5415
|
el.classList.add('show');
|
|
3279
5416
|
setTimeout(()=>el.classList.remove('show'),2500);
|
|
3280
5417
|
toast(t('settings.saved'),'success');
|
|
5418
|
+
if(sharingEnabled){
|
|
5419
|
+
updateHubShareInfo();
|
|
5420
|
+
setTimeout(function(){alert(t('settings.hub.restartAlert'));},300);
|
|
5421
|
+
}
|
|
3281
5422
|
}catch(e){
|
|
3282
5423
|
toast(t('settings.save.fail')+': '+e.message,'error');
|
|
3283
5424
|
}finally{done();}
|
|
@@ -3381,7 +5522,7 @@ function formatDuration(ms){
|
|
|
3381
5522
|
|
|
3382
5523
|
function formatTime(ts){
|
|
3383
5524
|
if(!ts) return '-';
|
|
3384
|
-
return new Date(ts).toLocaleString(
|
|
5525
|
+
return new Date(ts).toLocaleString(dateLoc(),{month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit'});
|
|
3385
5526
|
}
|
|
3386
5527
|
|
|
3387
5528
|
function fillDays(rows,days){
|
|
@@ -3393,7 +5534,7 @@ function fillDays(rows,days){
|
|
|
3393
5534
|
const row=map.get(dateStr)||{};
|
|
3394
5535
|
out.push({date:dateStr,count:row.count??0,list:row.list??0,search:row.search??0,total:(row.list??0)+(row.search??0)});
|
|
3395
5536
|
}
|
|
3396
|
-
if(days>
|
|
5537
|
+
if(days>60){
|
|
3397
5538
|
const weeks=[];let i=0;
|
|
3398
5539
|
while(i<out.length){
|
|
3399
5540
|
const chunk=out.slice(i,i+7);
|
|
@@ -3411,19 +5552,23 @@ function fillDays(rows,days){
|
|
|
3411
5552
|
}
|
|
3412
5553
|
|
|
3413
5554
|
function renderBars(el,data,valueKey,H){
|
|
3414
|
-
|
|
3415
|
-
if(vals.every(v
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
el.innerHTML=data.map(r
|
|
3420
|
-
|
|
3421
|
-
|
|
5555
|
+
var vals=data.map(function(d){return d[valueKey]??0;});
|
|
5556
|
+
if(vals.every(function(v){return v===0;})){el.innerHTML='<div style="color:var(--text-muted);font-size:13px;padding:40px;text-align:center">'+t('chart.nodata')+'</div>';return;}
|
|
5557
|
+
var max=Math.max(1,Math.max.apply(null,vals));
|
|
5558
|
+
var n=data.length;
|
|
5559
|
+
var labelStep=n<=7?1:(n<=14?2:5);
|
|
5560
|
+
el.innerHTML=data.map(function(r,idx){
|
|
5561
|
+
var v=r[valueKey]??0;
|
|
5562
|
+
var rawDate=r.date.includes('~')?r.date:(r.date.length>5?r.date.slice(5):'');
|
|
5563
|
+
var showLabel=(idx%labelStep===0)||(idx===n-1);
|
|
5564
|
+
var label=showLabel?rawDate:'';
|
|
5565
|
+
var tipDate=r.date.length>=10?r.date.slice(5,10):'';
|
|
5566
|
+
var tipText=tipDate?(tipDate+': '+v):(''+v);
|
|
3422
5567
|
if(v===0){
|
|
3423
|
-
return '<div class="chart-bar-wrap"
|
|
5568
|
+
return '<div class="chart-bar-wrap"><div class="chart-tip">'+tipText+'</div><div class="chart-bar-col"><div class="chart-bar zero" style="height:3px"></div></div><div class="chart-bar-label">'+label+'</div></div>';
|
|
3424
5569
|
}
|
|
3425
|
-
|
|
3426
|
-
return '<div class="chart-bar-wrap"
|
|
5570
|
+
var h=Math.max(8,Math.round((v/max)*H));
|
|
5571
|
+
return '<div class="chart-bar-wrap"><div class="chart-tip">'+tipText+'</div><div class="chart-bar-col"><div class="chart-bar" style="height:'+h+'px"></div></div><div class="chart-bar-label">'+label+'</div></div>';
|
|
3427
5572
|
}).join('');
|
|
3428
5573
|
}
|
|
3429
5574
|
|
|
@@ -3434,25 +5579,31 @@ function renderChartWrites(rows){
|
|
|
3434
5579
|
}
|
|
3435
5580
|
|
|
3436
5581
|
function renderChartCalls(rows){
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
if(vals.every(v
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
5582
|
+
var el=document.getElementById('chartCalls');
|
|
5583
|
+
var filled=fillDays(rows?.map(function(r){return {date:r.date,list:r.list,search:r.search};}),metricsDays);
|
|
5584
|
+
var vals=filled.map(function(f){return f.total;});
|
|
5585
|
+
if(vals.every(function(v){return v===0;})){el.innerHTML='<div style="color:var(--text-muted);font-size:13px;padding:40px;text-align:center">'+t('chart.nocalls')+'</div>';return;}
|
|
5586
|
+
var max=Math.max(1,Math.max.apply(null,vals));
|
|
5587
|
+
var H=160;
|
|
5588
|
+
var n=filled.length;
|
|
5589
|
+
var labelStep=n<=7?1:(n<=14?2:5);
|
|
5590
|
+
el.innerHTML=filled.map(function(r,idx){
|
|
5591
|
+
var rawDate=r.date.includes('~')?r.date:(r.date.length>5?r.date.slice(5):'');
|
|
5592
|
+
var showLabel=(idx%labelStep===0)||(idx===n-1);
|
|
5593
|
+
var label=showLabel?rawDate:'';
|
|
5594
|
+
var tipDate=r.date.length>=10?r.date.slice(5,10):'';
|
|
3445
5595
|
if(r.total===0){
|
|
3446
|
-
|
|
5596
|
+
var tipZero=tipDate?(tipDate+': 0'):'0';
|
|
5597
|
+
return '<div class="chart-bar-wrap"><div class="chart-tip">'+tipZero+'</div><div class="chart-bar-col"><div class="chart-bar zero" style="height:3px"></div></div><div class="chart-bar-label">'+label+'</div></div>';
|
|
3447
5598
|
}
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
5599
|
+
var totalH=Math.max(8,Math.round((r.total/max)*H));
|
|
5600
|
+
var listH=r.list?Math.max(4,Math.round((r.list/r.total)*totalH)):0;
|
|
5601
|
+
var searchH=r.search?totalH-listH:0;
|
|
5602
|
+
var tip=(tipDate?tipDate+' - ':'')+'List: '+r.list+', Search: '+r.search;
|
|
5603
|
+
var bars='';
|
|
3453
5604
|
if(searchH>0) bars+='<div class="chart-bar violet" style="height:'+searchH+'px"></div>';
|
|
3454
5605
|
if(listH>0) bars+='<div class="chart-bar" style="height:'+listH+'px"></div>';
|
|
3455
|
-
return '<div class="chart-bar-wrap"><div class="chart-tip">'+tip+'</div><div class="chart-bar-col"><div style="display:flex;flex-direction:column;gap:1px">'+bars+'</div></div><div class="chart-bar-label">'+label+'</div></div>';
|
|
5606
|
+
return '<div class="chart-bar-wrap"><div class="chart-tip">'+tip+'</div><div class="chart-bar-col"><div style="display:flex;flex-direction:column;gap:1px;align-items:center;width:100%">'+bars+'</div></div><div class="chart-bar-label">'+label+'</div></div>';
|
|
3456
5607
|
}).join('');
|
|
3457
5608
|
}
|
|
3458
5609
|
|
|
@@ -3634,16 +5785,18 @@ function renderToolAgg(data){
|
|
|
3634
5785
|
|
|
3635
5786
|
/* ─── Data loading ─── */
|
|
3636
5787
|
async function loadAll(){
|
|
3637
|
-
await Promise.all([loadStats(),loadMemories()]);
|
|
5788
|
+
await Promise.all([loadStats(),loadMemories(),loadSharingStatus(false)]);
|
|
3638
5789
|
checkMigrateStatus();
|
|
3639
5790
|
connectPPSSE();
|
|
3640
5791
|
checkForUpdate();
|
|
3641
5792
|
}
|
|
3642
5793
|
|
|
3643
|
-
async function loadStats(){
|
|
5794
|
+
async function loadStats(ownerFilter){
|
|
3644
5795
|
let d;
|
|
3645
5796
|
try{
|
|
3646
|
-
|
|
5797
|
+
var statsUrl='/api/stats';
|
|
5798
|
+
if(ownerFilter) statsUrl+='?owner='+encodeURIComponent(ownerFilter);
|
|
5799
|
+
const r=await fetch(statsUrl);
|
|
3647
5800
|
d=await r.json();
|
|
3648
5801
|
}catch(e){ d={}; }
|
|
3649
5802
|
if(!d||typeof d!=='object') d={};
|
|
@@ -3699,6 +5852,17 @@ async function loadStats(){
|
|
|
3699
5852
|
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>';
|
|
3700
5853
|
});
|
|
3701
5854
|
|
|
5855
|
+
const fSel=document.getElementById('filterSession');
|
|
5856
|
+
if(fSel){
|
|
5857
|
+
const curVal=activeSession||'';
|
|
5858
|
+
var sessionCount=(d.sessions||[]).length;
|
|
5859
|
+
fSel.innerHTML='<option value="">'+t('filter.allsessions')+' ('+sessionCount+')</option>';
|
|
5860
|
+
(d.sessions||[]).forEach(s=>{
|
|
5861
|
+
const sName=s.session_key.length>30?s.session_key.slice(0,12)+'...'+s.session_key.slice(-10):s.session_key;
|
|
5862
|
+
fSel.innerHTML+='<option value="'+s.session_key.replace(/"/g,'"')+'"'+(s.session_key===curVal?' selected':'')+'>'+sName+' ('+s.count+')</option>';
|
|
5863
|
+
});
|
|
5864
|
+
}
|
|
5865
|
+
|
|
3702
5866
|
const ownerSel=document.getElementById('filterOwner');
|
|
3703
5867
|
if(ownerSel && d.owners && d.owners.length>0){
|
|
3704
5868
|
const curVal=ownerSel.value;
|
|
@@ -3710,6 +5874,32 @@ async function loadStats(){
|
|
|
3710
5874
|
}
|
|
3711
5875
|
}
|
|
3712
5876
|
|
|
5877
|
+
function onOwnerFilterChange(){
|
|
5878
|
+
var owner=document.getElementById('filterOwner').value;
|
|
5879
|
+
activeSession=null;
|
|
5880
|
+
currentPage=1;
|
|
5881
|
+
refreshSessionDropdown(owner);
|
|
5882
|
+
applyFilters();
|
|
5883
|
+
}
|
|
5884
|
+
|
|
5885
|
+
async function refreshSessionDropdown(ownerFilter){
|
|
5886
|
+
try{
|
|
5887
|
+
var statsUrl='/api/stats';
|
|
5888
|
+
if(ownerFilter) statsUrl+='?owner='+encodeURIComponent(ownerFilter);
|
|
5889
|
+
var r=await fetch(statsUrl);
|
|
5890
|
+
var d=await r.json();
|
|
5891
|
+
var sessions=d.sessions||[];
|
|
5892
|
+
var fSel=document.getElementById('filterSession');
|
|
5893
|
+
if(fSel){
|
|
5894
|
+
fSel.innerHTML='<option value="">'+t('filter.allsessions')+' ('+sessions.length+')</option>';
|
|
5895
|
+
sessions.forEach(function(s){
|
|
5896
|
+
var sName=s.session_key.length>30?s.session_key.slice(0,12)+'...'+s.session_key.slice(-10):s.session_key;
|
|
5897
|
+
fSel.innerHTML+='<option value="'+s.session_key.replace(/"/g,'"')+'">'+sName+' ('+s.count+')</option>';
|
|
5898
|
+
});
|
|
5899
|
+
}
|
|
5900
|
+
}catch(e){}
|
|
5901
|
+
}
|
|
5902
|
+
|
|
3713
5903
|
function getFilterParams(){
|
|
3714
5904
|
const p=new URLSearchParams();
|
|
3715
5905
|
if(activeSession) p.set('session',activeSession);
|
|
@@ -3748,22 +5938,16 @@ async function loadMemories(page){
|
|
|
3748
5938
|
}
|
|
3749
5939
|
}
|
|
3750
5940
|
|
|
3751
|
-
async function
|
|
3752
|
-
if(!q.trim()){currentPage=1;loadMemories();return}
|
|
5941
|
+
async function loadHubMemories(){
|
|
3753
5942
|
const list=document.getElementById('memoryList');
|
|
3754
5943
|
list.innerHTML='<div class="spinner"></div>';
|
|
3755
5944
|
try{
|
|
3756
|
-
const
|
|
3757
|
-
p.set('q',q);
|
|
3758
|
-
const r=await fetch('/api/search?'+p.toString());
|
|
5945
|
+
const r=await fetch('/api/sharing/memories/list?limit='+PAGE_SIZE);
|
|
3759
5946
|
const d=await r.json();
|
|
3760
|
-
const
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
meta.push(total+t('search.meta.results'));
|
|
3765
|
-
document.getElementById('searchMeta').textContent=meta.join(' \\u00B7 ');
|
|
3766
|
-
renderMemories(d.results||[]);
|
|
5947
|
+
const items=d.memories||[];
|
|
5948
|
+
document.getElementById('searchMeta').textContent=items.length+t('search.meta.total');
|
|
5949
|
+
document.getElementById('sharingSearchMeta').textContent='';
|
|
5950
|
+
renderMemories(items);
|
|
3767
5951
|
document.getElementById('pagination').innerHTML='';
|
|
3768
5952
|
}catch(e){
|
|
3769
5953
|
document.getElementById('searchMeta').textContent='0'+t('search.meta.results');
|
|
@@ -3772,6 +5956,51 @@ async function doSearch(q){
|
|
|
3772
5956
|
}
|
|
3773
5957
|
}
|
|
3774
5958
|
|
|
5959
|
+
async function doSearch(query){
|
|
5960
|
+
query=(query||'').trim();
|
|
5961
|
+
if(!query){loadMemories();return;}
|
|
5962
|
+
var scope=document.getElementById('memorySearchScope')?.value||memorySearchScope||'local';
|
|
5963
|
+
var list=document.getElementById('memoryList');
|
|
5964
|
+
list.innerHTML='<div class="spinner"></div>';
|
|
5965
|
+
if(scope!=='local'){
|
|
5966
|
+
try{
|
|
5967
|
+
var r=await fetch('/api/sharing/search/memories',{
|
|
5968
|
+
method:'POST',
|
|
5969
|
+
headers:{'Content-Type':'application/json'},
|
|
5970
|
+
body:JSON.stringify({query:query,scope:scope,maxResults:20,role:activeRole||undefined})
|
|
5971
|
+
});
|
|
5972
|
+
var data=await r.json();
|
|
5973
|
+
renderSharingMemorySearchResults(data,query);
|
|
5974
|
+
}catch(e){
|
|
5975
|
+
document.getElementById('searchMeta').textContent='0'+t('search.meta.results');
|
|
5976
|
+
document.getElementById('sharingSearchMeta').textContent='';
|
|
5977
|
+
renderMemories([]);
|
|
5978
|
+
document.getElementById('pagination').innerHTML='';
|
|
5979
|
+
}
|
|
5980
|
+
} else {
|
|
5981
|
+
try{
|
|
5982
|
+
var p=getFilterParams();
|
|
5983
|
+
p.set('q',query);
|
|
5984
|
+
var r=await fetch('/api/search?'+p.toString());
|
|
5985
|
+
var d=await r.json();
|
|
5986
|
+
var total=d.total||0;
|
|
5987
|
+
var meta=[];
|
|
5988
|
+
if(d.vectorCount>0) meta.push(d.vectorCount+t('search.meta.semantic'));
|
|
5989
|
+
if(d.ftsCount>0) meta.push(d.ftsCount+t('search.meta.text'));
|
|
5990
|
+
meta.push(total+t('search.meta.results'));
|
|
5991
|
+
document.getElementById('searchMeta').textContent=meta.join(' \u00B7 ');
|
|
5992
|
+
document.getElementById('sharingSearchMeta').textContent='';
|
|
5993
|
+
renderMemories(d.results||[]);
|
|
5994
|
+
document.getElementById('pagination').innerHTML='';
|
|
5995
|
+
}catch(e){
|
|
5996
|
+
document.getElementById('searchMeta').textContent='0'+t('search.meta.results');
|
|
5997
|
+
document.getElementById('sharingSearchMeta').textContent='';
|
|
5998
|
+
renderMemories([]);
|
|
5999
|
+
document.getElementById('pagination').innerHTML='';
|
|
6000
|
+
}
|
|
6001
|
+
}
|
|
6002
|
+
}
|
|
6003
|
+
|
|
3775
6004
|
function debounceSearch(){
|
|
3776
6005
|
clearTimeout(searchTimer);
|
|
3777
6006
|
searchTimer=setTimeout(()=>doSearch(document.getElementById('searchInput').value),350);
|
|
@@ -3780,6 +6009,12 @@ function debounceSearch(){
|
|
|
3780
6009
|
function filterSession(key){
|
|
3781
6010
|
activeSession=key;
|
|
3782
6011
|
currentPage=1;
|
|
6012
|
+
var fSel=document.getElementById('filterSession');
|
|
6013
|
+
if(fSel) fSel.value=key||'';
|
|
6014
|
+
document.querySelectorAll('#sessionList .session-item').forEach(function(el,i){
|
|
6015
|
+
if(i===0) el.classList.toggle('active',!key);
|
|
6016
|
+
else el.classList.toggle('active',el.querySelector('span')?.title===key);
|
|
6017
|
+
});
|
|
3783
6018
|
loadAll();
|
|
3784
6019
|
}
|
|
3785
6020
|
|
|
@@ -3815,7 +6050,7 @@ function renderMemories(items){
|
|
|
3815
6050
|
}
|
|
3816
6051
|
items.forEach(m=>{memoryCache[m.id]=m});
|
|
3817
6052
|
list.innerHTML=items.map(m=>{
|
|
3818
|
-
const time=m.created_at?new Date(typeof m.created_at==='number'?m.created_at:m.created_at).toLocaleString(
|
|
6053
|
+
const time=m.created_at?new Date(typeof m.created_at==='number'?m.created_at:m.created_at).toLocaleString(dateLoc()):'';
|
|
3819
6054
|
const role=m.role||'user';
|
|
3820
6055
|
const rawSummary=m.summary||'';
|
|
3821
6056
|
const rawContent=m.content||'';
|
|
@@ -3827,7 +6062,7 @@ function renderMemories(items){
|
|
|
3827
6062
|
const mc=m.merge_count||0;
|
|
3828
6063
|
const cardTitle=esc(rawSummary||rawContent||'');
|
|
3829
6064
|
const mergeBadge=mc>0?'<span class="merge-badge">\\u{1F504} '+t('card.evolved')+' '+mc+t('card.times')+'</span>':'';
|
|
3830
|
-
const updatedAt=(m.updated_at&&m.updated_at>m.created_at)?'<span class="card-updated">'+t('card.updated')+' '+new Date(m.updated_at).toLocaleString(
|
|
6065
|
+
const updatedAt=(m.updated_at&&m.updated_at>m.created_at)?'<span class="card-updated">'+t('card.updated')+' '+new Date(m.updated_at).toLocaleString(dateLoc())+'</span>':'';
|
|
3831
6066
|
const ds=m.dedup_status||'active';
|
|
3832
6067
|
const isInactive=ds==='merged';
|
|
3833
6068
|
const dedupBadge=ds==='duplicate'?'<span class="dedup-badge duplicate">'+t('card.dedupDuplicate')+'</span>':ds==='merged'?'<span class="dedup-badge merged">'+t('card.dedupMerged')+'</span>':'';
|
|
@@ -3836,6 +6071,8 @@ function renderMemories(items){
|
|
|
3836
6071
|
const ownerVal=m.owner||'agent:main';
|
|
3837
6072
|
const isPublicMem=ownerVal==='public';
|
|
3838
6073
|
const ownerBadge=isPublicMem?'<span class="owner-badge public">\\u{1F310} '+t('filter.public')+'</span>':'<span class="owner-badge agent">\\u{1F512} '+t('filter.private')+'</span>';
|
|
6074
|
+
const memShared=m.sharingVisibility||null;
|
|
6075
|
+
const memShareBadge=memShared?'<span style="display:inline-flex;align-items:center;gap:3px;padding:2px 8px;background:#22c55e22;border:1px solid #22c55e44;border-radius:10px;font-size:11px;color:#22c55e">\\u2713 '+(memShared==='group'?t('share.group'):t('share.public'))+'</span>':'';
|
|
3839
6076
|
let dedupInfo='';
|
|
3840
6077
|
if(ds==='duplicate'||ds==='merged'){
|
|
3841
6078
|
const reason=m.dedup_reason?'<span style="font-size:11px;color:var(--text-muted)">'+t('card.dedupReason')+esc(m.dedup_reason)+'</span>':'';
|
|
@@ -3849,7 +6086,7 @@ function renderMemories(items){
|
|
|
3849
6086
|
if(hist.length>0){
|
|
3850
6087
|
historyHtml='<div class="merge-history" id="history-'+id+'" style="display:none"><div style="font-weight:600;margin-bottom:8px;font-size:12px">'+t('card.evolveHistory')+' ('+hist.length+')</div>';
|
|
3851
6088
|
hist.forEach(function(h){
|
|
3852
|
-
const ht=h.at?new Date(h.at).toLocaleString(
|
|
6089
|
+
const ht=h.at?new Date(h.at).toLocaleString(dateLoc()):'';
|
|
3853
6090
|
historyHtml+='<div class="merge-history-item"><span class="merge-action '+h.action+'">'+h.action+'</span> <span style="color:var(--text-muted)">'+ht+'</span><br>'+esc(h.reason||'');
|
|
3854
6091
|
if(h.from) historyHtml+='<br><span style="opacity:.6">'+t('card.oldSummary')+':</span> '+esc(h.from);
|
|
3855
6092
|
if(h.to) historyHtml+='<br><span style="opacity:.6">'+t('card.newSummary')+':</span> '+esc(h.to);
|
|
@@ -3885,6 +6122,7 @@ function renderMemories(items){
|
|
|
3885
6122
|
(mc>0?'<button class="btn btn-sm btn-ghost" onclick="toggleHistory(\\''+id+'\\')">'+t('card.evolveHistory')+'</button>':'')+
|
|
3886
6123
|
'<button class="btn btn-sm btn-ghost" onclick="openEditModal(\\''+id+'\\')">'+t('card.edit')+'</button>'+
|
|
3887
6124
|
(isPublicMem?'<button class="btn btn-sm btn-ghost" onclick="toggleMemoryPublic(\\''+id+'\\',false)">\\u{1F512} '+t('skills.setPrivate')+'</button>':'<button class="btn btn-sm btn-ghost mem-public-btn" onclick="toggleMemoryPublic(\\''+id+'\\',true)">\\u{1F310} '+t('skills.setPublic')+'</button>')+
|
|
6125
|
+
(memShared?'<button class="btn btn-sm btn-ghost" onclick="unshareMemory(\\''+id+'\\')">'+t('share.unshareBtn')+'</button>':'<button class="btn btn-sm btn-ghost" onclick="shareMemoryPrompt(\\''+id+'\\')">'+t('share.shareBtn')+'</button>')+
|
|
3888
6126
|
'<button class="btn btn-sm btn-ghost" style="color:var(--accent)" onclick="deleteMemory(\\''+id+'\\')">'+t('card.delete')+'</button>'+
|
|
3889
6127
|
vscore+
|
|
3890
6128
|
'</div></div>';
|
|
@@ -3943,38 +6181,50 @@ function scrollToMemory(targetId){
|
|
|
3943
6181
|
}
|
|
3944
6182
|
showMemoryModal(targetId);
|
|
3945
6183
|
}
|
|
6184
|
+
function fmtModalDate(v){
|
|
6185
|
+
if(!v) return '-';
|
|
6186
|
+
var d=new Date(v);
|
|
6187
|
+
if(isNaN(d.getTime())) return '-';
|
|
6188
|
+
return d.toLocaleString(dateLoc());
|
|
6189
|
+
}
|
|
3946
6190
|
async function showMemoryModal(chunkId){
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
body.innerHTML='<div style="text-align:center;padding:
|
|
6191
|
+
var overlay=document.getElementById('memoryModal');
|
|
6192
|
+
var body=document.getElementById('memoryModalBody');
|
|
6193
|
+
body.innerHTML='<div style="text-align:center;padding:56px;color:var(--text-sec)"><div class="spinner" style="margin:0 auto 14px"></div><div style="font-size:12px;letter-spacing:.04em">'+t('memory.detail.loading')+'</div></div>';
|
|
3950
6194
|
overlay.classList.add('show');
|
|
3951
6195
|
try{
|
|
3952
|
-
|
|
3953
|
-
if(!res.ok){body.innerHTML='<div style="text-align:center;padding:
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
if(
|
|
3972
|
-
|
|
3973
|
-
if(m.
|
|
3974
|
-
if(m.
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
6196
|
+
var res=await fetch('/api/memory/'+encodeURIComponent(chunkId));
|
|
6197
|
+
if(!res.ok){body.innerHTML='<div style="text-align:center;padding:56px"><div style="font-size:32px;margin-bottom:12px">\u{1F50D}</div><div style="color:#f87171;font-size:13px">'+t('memory.detail.notFound')+'</div></div>';return;}
|
|
6198
|
+
var data=await res.json();
|
|
6199
|
+
var m=data.memory;
|
|
6200
|
+
var role=(m.role||'unknown').toUpperCase();
|
|
6201
|
+
var roleCls=(m.role||'').toLowerCase();
|
|
6202
|
+
var ds=m.dedup_status||'active';
|
|
6203
|
+
var h='<div class="mm-hero">';
|
|
6204
|
+
h+='<div class="mm-hero-row">';
|
|
6205
|
+
h+='<span class="mm-role-chip '+roleCls+'">'+role+'</span>';
|
|
6206
|
+
if(ds!=='active') h+='<span class="mm-dedup-chip '+(ds==='duplicate'?'duplicate':'merged')+'">'+(ds==='duplicate'?'\u274C':'\u{1F504}')+' '+ds+'</span>';
|
|
6207
|
+
h+='<span class="mm-id" onclick="navigator.clipboard.writeText(\\''+esc(m.id)+'\\');toast(\\'ID copied\\',\\'success\\')" title="'+t('memory.detail.copyId')+'">'+esc(m.id.slice(0,12))+'</span>';
|
|
6208
|
+
h+='</div>';
|
|
6209
|
+
if(m.summary) h+='<div class="mm-summary">'+esc(m.summary)+'</div>';
|
|
6210
|
+
h+='</div>';
|
|
6211
|
+
if(m.content){
|
|
6212
|
+
h+='<div class="mm-section"><div class="mm-section-label">Content</div><pre class="mm-content">'+esc(m.content)+'</pre></div>';
|
|
6213
|
+
}
|
|
6214
|
+
h+='<div class="mm-meta">';
|
|
6215
|
+
if(m.session_key) h+='<div class="mm-meta-chip"><strong>Session</strong><span>'+esc(m.session_key.slice(0,12))+'</span></div>';
|
|
6216
|
+
h+='<div class="mm-meta-chip"><strong>'+t('memory.detail.created')+'</strong><span>'+fmtModalDate(m.created_at)+'</span></div>';
|
|
6217
|
+
if(m.updated_at) h+='<div class="mm-meta-chip"><strong>'+t('memory.detail.updated')+'</strong><span>'+fmtModalDate(m.updated_at)+'</span></div>';
|
|
6218
|
+
if(m.kind) h+='<div class="mm-meta-chip"><strong>Kind</strong><span>'+esc(m.kind)+'</span></div>';
|
|
6219
|
+
h+='</div>';
|
|
6220
|
+
if(m.dedup_reason){
|
|
6221
|
+
h+='<div class="mm-dedup"><div class="mm-dedup-box">'+esc(m.dedup_reason)+'</div></div>';
|
|
6222
|
+
}
|
|
6223
|
+
if(m.dedup_target&&m.dedup_target!==chunkId){
|
|
6224
|
+
h+='<div class="mm-footer"><span class="dedup-target-link" onclick="closeMemoryModal();scrollToMemory(\\''+m.dedup_target+'\\')">'+t('memory.detail.viewTarget')+' '+m.dedup_target.slice(0,8)+'...</span></div>';
|
|
6225
|
+
}
|
|
6226
|
+
body.innerHTML=h;
|
|
6227
|
+
}catch(e){body.innerHTML='<div style="text-align:center;padding:56px"><div style="font-size:32px;margin-bottom:12px">\u26A0\uFE0F</div><div style="color:#f87171;font-size:13px">'+esc(String(e))+'</div></div>';}
|
|
3978
6228
|
}
|
|
3979
6229
|
function closeMemoryModal(){document.getElementById('memoryModal').classList.remove('show');}
|
|
3980
6230
|
|
|
@@ -4772,8 +7022,8 @@ checkAuth();
|
|
|
4772
7022
|
<div class="memory-modal-overlay" id="memoryModal" onclick="if(event.target===this)closeMemoryModal()">
|
|
4773
7023
|
<div class="memory-modal">
|
|
4774
7024
|
<div class="memory-modal-title">
|
|
4775
|
-
<span>Memory Detail</span>
|
|
4776
|
-
<button class="
|
|
7025
|
+
<div class="mm-tl"><div class="mm-tl-icon">\u{1F9E0}</div><span data-i18n="memory.detail.title">Memory Detail</span></div>
|
|
7026
|
+
<button class="mm-close" onclick="closeMemoryModal()" title="Close">×</button>
|
|
4777
7027
|
</div>
|
|
4778
7028
|
<div class="memory-modal-body" id="memoryModalBody"></div>
|
|
4779
7029
|
</div>
|