@memtensor/memos-local-openclaw-plugin 1.0.4-beta.0 → 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/dist/client/connector.d.ts +4 -0
- package/dist/client/connector.d.ts.map +1 -1
- package/dist/client/connector.js +92 -0
- package/dist/client/connector.js.map +1 -1
- package/dist/config.js +9 -9
- package/dist/config.js.map +1 -1
- package/dist/hub/server.d.ts.map +1 -1
- package/dist/hub/server.js +27 -22
- package/dist/hub/server.js.map +1 -1
- package/dist/hub/user-manager.d.ts +1 -0
- package/dist/hub/user-manager.d.ts.map +1 -1
- package/dist/hub/user-manager.js +13 -0
- package/dist/hub/user-manager.js.map +1 -1
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +20 -94
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +386 -207
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts +2 -0
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +104 -15
- package/dist/viewer/server.js.map +1 -1
- package/package.json +1 -1
- package/src/client/connector.ts +94 -0
- package/src/config.ts +9 -9
- package/src/hub/server.ts +28 -22
- package/src/hub/user-manager.ts +13 -0
- package/src/storage/sqlite.ts +20 -94
- package/src/viewer/html.ts +386 -207
- package/src/viewer/server.ts +98 -15
package/src/viewer/html.ts
CHANGED
|
@@ -5,7 +5,8 @@ return `<!DOCTYPE html>
|
|
|
5
5
|
<head>
|
|
6
6
|
<meta charset="UTF-8">
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
8
|
-
<
|
|
8
|
+
<link rel="icon" href="https://statics.memtensor.com.cn/logo/color-m.svg" type="image/svg+xml">
|
|
9
|
+
<title>MemOS 记忆</title>
|
|
9
10
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
11
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
12
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
@@ -58,8 +59,9 @@ return `<!DOCTYPE html>
|
|
|
58
59
|
[data-theme="light"] .analytics-card.amber .ac-value{color:#d97706}
|
|
59
60
|
[data-theme="light"] .analytics-section{background:#fff;border-color:var(--border);box-shadow:0 1px 3px rgba(0,0,0,.04)}
|
|
60
61
|
[data-theme="light"] .analytics-section::before{background:none}
|
|
61
|
-
[data-theme="light"] .chart-bar{box-shadow:
|
|
62
|
-
[data-theme="light"] .chart-bar:hover{box-shadow:0
|
|
62
|
+
[data-theme="light"] .chart-bar{box-shadow:0 1px 4px rgba(99,102,241,.12)}
|
|
63
|
+
[data-theme="light"] .chart-bar:hover{box-shadow:0 3px 12px rgba(79,70,229,.2)}
|
|
64
|
+
[data-theme="light"] .chart-bars::before{opacity:.3}
|
|
63
65
|
[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)}
|
|
64
66
|
[data-theme="light"] .tool-chart-tooltip .tt-time{color:#a5b4fc}
|
|
65
67
|
[data-theme="light"] .tool-chart-tooltip .tt-val{color:#e8eaed}
|
|
@@ -105,9 +107,15 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
105
107
|
/* ─── App Layout (dark dashboard, same as www) ─── */
|
|
106
108
|
.app{display:none;flex-direction:column;min-height:100vh}
|
|
107
109
|
.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)}
|
|
108
|
-
.topbar .brand{display:flex;align-items:center;gap:
|
|
109
|
-
.topbar .brand .icon{width:
|
|
110
|
+
.topbar .brand{display:flex;align-items:center;gap:8px;font-weight:700;font-size:15px;color:var(--text);letter-spacing:-.02em;flex-shrink:0}
|
|
111
|
+
.topbar .brand .icon{width:24px;height:24px;display:flex;align-items:center;justify-content:center;font-size:18px;background:none;border-radius:0}
|
|
112
|
+
.topbar .brand .brand-title{font-size:13px;font-weight:500;opacity:.7}
|
|
113
|
+
.topbar .brand .brand-col{display:flex;flex-direction:column;gap:0}
|
|
114
|
+
.topbar .brand .brand-powered{font-size:9px;font-weight:500;color:var(--text-sec);opacity:.55;letter-spacing:.02em;line-height:1}
|
|
110
115
|
.topbar .brand .sub{font-weight:400;color:var(--text-muted);font-size:11px}
|
|
116
|
+
.memos-logo{display:inline-flex;align-items:center}
|
|
117
|
+
.memos-logo svg{height:28px;width:28px}
|
|
118
|
+
.brand-sep{width:1px;height:20px;background:var(--border);opacity:.5;margin:0 2px}
|
|
111
119
|
.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}
|
|
112
120
|
[data-theme="light"] .version-badge{background:rgba(0,0,0,.05);border-color:rgba(0,0,0,.08);color:var(--text-sec)}
|
|
113
121
|
.topbar-center{flex:1;display:flex;justify-content:center}
|
|
@@ -286,20 +294,67 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
286
294
|
.memory-card.dedup-inactive{opacity:.55;border-style:dashed}
|
|
287
295
|
.memory-card.dedup-inactive:hover{opacity:.85}
|
|
288
296
|
.dedup-target-link{font-size:11px;color:var(--pri);cursor:pointer;text-decoration:underline;margin-left:4px}
|
|
289
|
-
.memory-modal-overlay{position:fixed;inset:0;background:rgba(
|
|
297
|
+
.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)}
|
|
290
298
|
.memory-modal-overlay.show{display:flex}
|
|
291
|
-
.memory-modal{background:
|
|
292
|
-
|
|
293
|
-
.memory-modal
|
|
294
|
-
|
|
295
|
-
.
|
|
296
|
-
|
|
297
|
-
.modal
|
|
298
|
-
.modal
|
|
299
|
-
.modal-
|
|
300
|
-
.modal-
|
|
301
|
-
|
|
302
|
-
.modal-
|
|
299
|
+
[data-theme="light"] .memory-modal-overlay{background:rgba(0,0,0,.45)}
|
|
300
|
+
.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}
|
|
301
|
+
[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)}
|
|
302
|
+
@keyframes mmSlideIn{from{opacity:0;transform:scale(.92) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}
|
|
303
|
+
.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}
|
|
304
|
+
@keyframes mmGradientMove{0%{background-position:0% 50%}100%{background-position:200% 50%}}
|
|
305
|
+
.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}
|
|
306
|
+
[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%)}
|
|
307
|
+
.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}
|
|
308
|
+
.memory-modal-title .mm-tl{display:flex;align-items:center;gap:8px}
|
|
309
|
+
.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)}
|
|
310
|
+
[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)}
|
|
311
|
+
.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}
|
|
312
|
+
.memory-modal-title .mm-close:hover{background:rgba(255,77,77,.12);border-color:rgba(255,77,77,.2);color:#f87171;transform:rotate(90deg)}
|
|
313
|
+
[data-theme="light"] .memory-modal-title .mm-close{border-color:rgba(0,0,0,.06);background:rgba(0,0,0,.03)}
|
|
314
|
+
[data-theme="light"] .memory-modal-title .mm-close:hover{background:rgba(255,77,77,.08);border-color:rgba(255,77,77,.15)}
|
|
315
|
+
.memory-modal-body{position:relative;z-index:2;padding:0;overflow-y:auto;flex:1}
|
|
316
|
+
.mm-hero{padding:0 24px 20px}
|
|
317
|
+
.mm-hero-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:14px}
|
|
318
|
+
.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)}
|
|
319
|
+
.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)}
|
|
320
|
+
.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)}
|
|
321
|
+
.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)}
|
|
322
|
+
[data-theme="light"] .mm-role-chip{color:#6366f1}
|
|
323
|
+
[data-theme="light"] .mm-role-chip.user{color:#059669}
|
|
324
|
+
[data-theme="light"] .mm-role-chip.assistant{color:#6366f1}
|
|
325
|
+
[data-theme="light"] .mm-role-chip.system{color:#d97706}
|
|
326
|
+
.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}
|
|
327
|
+
.mm-dedup-chip.duplicate{background:rgba(239,68,68,.12);color:#fca5a5;border:1px solid rgba(239,68,68,.2)}
|
|
328
|
+
.mm-dedup-chip.merged{background:rgba(251,191,36,.12);color:#fcd34d;border:1px solid rgba(251,191,36,.2)}
|
|
329
|
+
[data-theme="light"] .mm-dedup-chip.duplicate{color:#dc2626}
|
|
330
|
+
[data-theme="light"] .mm-dedup-chip.merged{color:#d97706}
|
|
331
|
+
.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}
|
|
332
|
+
.mm-id::before{content:'\u{1F517}';font-size:9px}
|
|
333
|
+
.mm-id:hover{background:rgba(99,102,241,.12);border-color:rgba(99,102,241,.25);color:var(--text)}
|
|
334
|
+
[data-theme="light"] .mm-id{background:rgba(0,0,0,.03);border-color:rgba(0,0,0,.06)}
|
|
335
|
+
[data-theme="light"] .mm-id:hover{background:rgba(99,102,241,.08);border-color:rgba(99,102,241,.15)}
|
|
336
|
+
.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}
|
|
337
|
+
.mm-summary::before{content:'';position:absolute;left:0;top:12px;bottom:12px;width:3px;border-radius:2px;background:linear-gradient(180deg,#6366f1,#8b5cf6)}
|
|
338
|
+
[data-theme="light"] .mm-summary{background:rgba(99,102,241,.02);border-color:rgba(99,102,241,.06)}
|
|
339
|
+
.mm-section{padding:0 24px 16px}
|
|
340
|
+
.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}
|
|
341
|
+
.mm-section-label::before{content:'';width:6px;height:6px;border-radius:50%;background:linear-gradient(135deg,#6366f1,#8b5cf6);flex-shrink:0}
|
|
342
|
+
.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}
|
|
343
|
+
[data-theme="light"] .mm-content{background:rgba(0,0,0,.03);border-color:rgba(0,0,0,.06)}
|
|
344
|
+
.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)}
|
|
345
|
+
[data-theme="light"] .mm-meta{border-top-color:rgba(0,0,0,.04);background:rgba(0,0,0,.015)}
|
|
346
|
+
.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}
|
|
347
|
+
.mm-meta-chip:hover{background:rgba(255,255,255,.06);border-color:rgba(99,102,241,.15)}
|
|
348
|
+
[data-theme="light"] .mm-meta-chip{background:rgba(0,0,0,.02);border-color:rgba(0,0,0,.04)}
|
|
349
|
+
[data-theme="light"] .mm-meta-chip:hover{background:rgba(99,102,241,.04);border-color:rgba(99,102,241,.1)}
|
|
350
|
+
.mm-meta-chip strong{color:var(--text-muted);font-weight:600;font-size:9px;text-transform:uppercase;letter-spacing:.06em}
|
|
351
|
+
.mm-meta-chip span{color:var(--text);font-weight:500;font-size:12px;word-break:break-all}
|
|
352
|
+
.mm-dedup{padding:0 24px 16px}
|
|
353
|
+
.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}
|
|
354
|
+
.mm-dedup-box::before{content:'\u26A0\uFE0F';flex-shrink:0;font-size:14px}
|
|
355
|
+
.mm-footer{padding:12px 24px 16px;display:flex;align-items:center;justify-content:center}
|
|
356
|
+
.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}
|
|
357
|
+
.mm-footer .dedup-target-link:hover{background:rgba(99,102,241,.12);border-color:rgba(99,102,241,.3)}
|
|
303
358
|
[data-theme="light"] .merge-history{background:rgba(0,0,0,.04)}
|
|
304
359
|
[data-theme="light"] .merge-history-item{border-bottom-color:rgba(0,0,0,.06)}
|
|
305
360
|
.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)}
|
|
@@ -739,8 +794,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
739
794
|
.test-result.ok{color:#22c55e}
|
|
740
795
|
.test-result.fail{color:var(--rose)}
|
|
741
796
|
.test-result.loading{color:var(--text-muted)}
|
|
742
|
-
.settings-actions{display:flex;gap:12px;justify-content:flex-end;align-items:center;margin-top:
|
|
743
|
-
.settings-actions .btn{min-width:
|
|
797
|
+
.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}
|
|
798
|
+
.settings-actions .btn{flex:0 0 auto;min-width:0;padding:8px 24px;font-size:13px}
|
|
744
799
|
.settings-actions .btn-primary{background:rgba(99,102,241,.08);color:var(--pri);border:1px solid rgba(99,102,241,.25);font-weight:600}
|
|
745
800
|
.settings-actions .btn-primary:hover{background:rgba(99,102,241,.14);border-color:var(--pri)}
|
|
746
801
|
[data-theme="light"] .settings-actions .btn-primary{background:rgba(79,70,229,.06);color:#4f46e5;border:1px solid rgba(79,70,229,.2)}
|
|
@@ -806,18 +861,20 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
806
861
|
.analytics-section::before{display:none}
|
|
807
862
|
.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}
|
|
808
863
|
.analytics-section h3 .icon{font-size:14px;opacity:.6}
|
|
809
|
-
.chart-bars{display:flex;align-items:flex-end;gap:
|
|
810
|
-
.chart-
|
|
811
|
-
.chart-bar-
|
|
812
|
-
.chart-bar-
|
|
864
|
+
.chart-bars{display:flex;align-items:flex-end;gap:1px;padding:12px 4px 4px;min-height:200px;position:relative;width:100%}
|
|
865
|
+
.chart-bars::before{content:'';position:absolute;left:0;right:0;bottom:24px;height:1px;background:var(--border);opacity:.4}
|
|
866
|
+
.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}
|
|
867
|
+
.chart-bar-col{width:100%;height:160px;display:flex;flex-direction:column;justify-content:flex-end;align-items:center}
|
|
868
|
+
.chart-bar-wrap:hover .chart-bar{opacity:1;filter:brightness(1.2);transform:scaleY(1.02);transform-origin:bottom}
|
|
813
869
|
.chart-bar-wrap:hover .chart-bar-label{color:var(--text)}
|
|
814
870
|
.chart-bar-wrap:hover .chart-tip{opacity:1;transform:translateX(-50%) translateY(0)}
|
|
815
|
-
.chart-tip{position:absolute;top:-
|
|
816
|
-
.chart-
|
|
817
|
-
.chart-bar
|
|
818
|
-
.chart-bar.
|
|
819
|
-
.chart-bar.
|
|
820
|
-
.chart-bar
|
|
871
|
+
.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)}
|
|
872
|
+
[data-theme="light"] .chart-tip{background:rgba(17,24,39,.9)}
|
|
873
|
+
.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)}
|
|
874
|
+
.chart-bar.violet{background:linear-gradient(180deg,#8b5cf6 0%,#6366f1 100%)}
|
|
875
|
+
.chart-bar.green{background:linear-gradient(180deg,#34d399 0%,#10b981 100%);box-shadow:0 1px 4px rgba(16,185,129,.12)}
|
|
876
|
+
.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}
|
|
877
|
+
.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}
|
|
821
878
|
.chart-legend{display:flex;gap:14px;margin-top:12px;flex-wrap:wrap;font-size:11px;color:var(--text-sec);font-weight:500}
|
|
822
879
|
.chart-legend span{display:inline-flex;align-items:center;gap:5px}
|
|
823
880
|
.chart-legend .dot{width:8px;height:8px;border-radius:2px}
|
|
@@ -871,7 +928,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
871
928
|
[data-theme="light"] .auth-theme-toggle .theme-icon-dark{display:none}
|
|
872
929
|
|
|
873
930
|
@media(max-width:1100px){.analytics-cards{grid-template-columns:repeat(3,1fr)}}
|
|
874
|
-
@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
|
|
931
|
+
@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}}
|
|
875
932
|
</style>
|
|
876
933
|
</head>
|
|
877
934
|
<body>
|
|
@@ -883,7 +940,9 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
883
940
|
<button class="auth-theme-toggle" onclick="toggleLang()" aria-label="Switch language"><span data-i18n="lang.switch">EN</span></button>
|
|
884
941
|
</div>
|
|
885
942
|
<div class="auth-card">
|
|
886
|
-
<div class="logo"
|
|
943
|
+
<div class="logo" style="display:flex;flex-direction:column;align-items:center;gap:10px">
|
|
944
|
+
<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>
|
|
945
|
+
</div>
|
|
887
946
|
<h1 data-i18n="title">OpenClaw Memory</h1>
|
|
888
947
|
<p style="font-size:12px;color:var(--text-sec);margin-bottom:6px" data-i18n="subtitle">Powered by MemOS</p>
|
|
889
948
|
<p data-i18n="setup.desc">Set a password to protect your memories</p>
|
|
@@ -901,7 +960,9 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
901
960
|
<button class="auth-theme-toggle" onclick="toggleLang()" aria-label="Switch language"><span data-i18n="lang.switch">EN</span></button>
|
|
902
961
|
</div>
|
|
903
962
|
<div class="auth-card">
|
|
904
|
-
<div class="logo"
|
|
963
|
+
<div class="logo" style="display:flex;flex-direction:column;align-items:center;gap:10px">
|
|
964
|
+
<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>
|
|
965
|
+
</div>
|
|
905
966
|
<h1 data-i18n="title">OpenClaw Memory</h1>
|
|
906
967
|
<p style="font-size:12px;color:var(--text-sec);margin-bottom:6px" data-i18n="subtitle">Powered by MemOS</p>
|
|
907
968
|
<p data-i18n="login.desc">Enter your password to access memories</p>
|
|
@@ -953,8 +1014,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
953
1014
|
<div class="app" id="app">
|
|
954
1015
|
<div class="topbar">
|
|
955
1016
|
<div class="brand">
|
|
956
|
-
<
|
|
957
|
-
<span data-i18n="title">
|
|
1017
|
+
<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>
|
|
1018
|
+
<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}
|
|
958
1019
|
</div>
|
|
959
1020
|
<div class="topbar-center">
|
|
960
1021
|
<nav class="nav-tabs">
|
|
@@ -1006,9 +1067,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1006
1067
|
<option value="public" data-i18n="filter.public">Public</option>
|
|
1007
1068
|
</select>
|
|
1008
1069
|
<select id="memorySearchScope" class="filter-select" onchange="onMemoryScopeChange()">
|
|
1009
|
-
<option value="local">Local</option>
|
|
1010
|
-
<option value="
|
|
1011
|
-
<option value="all">All</option>
|
|
1070
|
+
<option value="local" data-i18n="scope.local">Local</option>
|
|
1071
|
+
<option value="all" data-i18n="scope.hub">Hub</option>
|
|
1012
1072
|
</select>
|
|
1013
1073
|
</div>
|
|
1014
1074
|
<div class="search-meta" id="searchMeta"></div>
|
|
@@ -1051,9 +1111,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1051
1111
|
<button class="filter-chip" data-task-status="completed" onclick="setTaskStatusFilter(this,'completed')" data-i18n="tasks.status.completed">Completed</button>
|
|
1052
1112
|
<button class="filter-chip" data-task-status="skipped" onclick="setTaskStatusFilter(this,'skipped')" data-i18n="tasks.status.skipped">Skipped</button>
|
|
1053
1113
|
<select id="taskSearchScope" class="scope-select" onchange="onTaskScopeChange()">
|
|
1054
|
-
<option value="local">Local</option>
|
|
1055
|
-
<option value="
|
|
1056
|
-
<option value="all">All</option>
|
|
1114
|
+
<option value="local" data-i18n="scope.local">Local</option>
|
|
1115
|
+
<option value="all" data-i18n="scope.hub">Hub</option>
|
|
1057
1116
|
</select>
|
|
1058
1117
|
<button class="btn btn-sm btn-ghost" onclick="loadTasks()" style="margin-left:auto" data-i18n="refresh">\u21BB Refresh</button>
|
|
1059
1118
|
</div>
|
|
@@ -1095,8 +1154,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1095
1154
|
<input type="text" id="skillSearchInput" placeholder="Search skills..." data-i18n-ph="skills.search.placeholder" oninput="debounceSkillSearch()">
|
|
1096
1155
|
<select id="skillSearchScope" class="scope-select" onchange="onSkillScopeChange()">
|
|
1097
1156
|
<option value="local" data-i18n="scope.local">Local</option>
|
|
1098
|
-
<option value="
|
|
1099
|
-
<option value="all" data-i18n="scope.all">All</option>
|
|
1157
|
+
<option value="all" data-i18n="scope.hub">Hub</option>
|
|
1100
1158
|
</select>
|
|
1101
1159
|
</div>
|
|
1102
1160
|
<div class="search-meta" id="skillSearchMeta"></div>
|
|
@@ -1411,16 +1469,11 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1411
1469
|
<button class="btn btn-sm" id="btnRoleHub" onclick="selectSharingRole('hub')" data-i18n="settings.hub.role.hub">Hub (Server)</button>
|
|
1412
1470
|
<button class="btn btn-sm" id="btnRoleClient" onclick="selectSharingRole('client')" data-i18n="settings.hub.role.client">Client (Connect to Hub)</button>
|
|
1413
1471
|
</div>
|
|
1414
|
-
<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.</div>
|
|
1472
|
+
<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>
|
|
1415
1473
|
</div>
|
|
1416
1474
|
|
|
1417
1475
|
<div id="hubModeConfig" style="display:none">
|
|
1418
|
-
<
|
|
1419
|
-
<div style="font-weight:700;color:var(--text);margin-bottom:4px" data-i18n="settings.hub.hubSteps.title">Quick Setup (3 steps)</div>
|
|
1420
|
-
<div><span style="color:var(--accent)">1.</span> <span data-i18n="settings.hub.hubSteps.s1">Fill in Team Name below (or keep default)</span></div>
|
|
1421
|
-
<div><span style="color:var(--accent)">2.</span> <span data-i18n="settings.hub.hubSteps.s2">Click "Save Settings", then restart OpenClaw gateway</span></div>
|
|
1422
|
-
<div><span style="color:var(--accent)">3.</span> <span data-i18n="settings.hub.hubSteps.s3">Share the Hub Address and Team Token below with your team members</span></div>
|
|
1423
|
-
</div>
|
|
1476
|
+
<input type="hidden" id="cfgHubTeamToken" value="">
|
|
1424
1477
|
<div class="settings-grid">
|
|
1425
1478
|
<div class="settings-field">
|
|
1426
1479
|
<label data-i18n="settings.hub.port">Hub Port</label>
|
|
@@ -1432,15 +1485,13 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1432
1485
|
<input type="text" id="cfgHubTeamName" placeholder="My Team">
|
|
1433
1486
|
<div class="field-hint" data-i18n="settings.hub.teamName.hint">Your team display name</div>
|
|
1434
1487
|
</div>
|
|
1435
|
-
<div class="settings-field full-width">
|
|
1436
|
-
<label data-i18n="settings.hub.teamToken">Team Token</label>
|
|
1437
|
-
<input type="text" id="cfgHubTeamToken" readonly style="background:var(--bg);cursor:pointer" onclick="this.select();document.execCommand('copy');toast(t('settings.hub.tokenCopied'),'success')">
|
|
1438
|
-
<div class="field-hint" data-i18n="settings.hub.teamToken.hint">Auto-generated secret for clients to join. Click to copy. Share this with your team members.</div>
|
|
1439
|
-
</div>
|
|
1440
1488
|
</div>
|
|
1441
|
-
<div id="hubShareInfo"
|
|
1442
|
-
<div
|
|
1443
|
-
<div id="hubShareInfoContent"
|
|
1489
|
+
<div id="hubShareInfo" style="display:none;margin-top:14px;background:var(--bg);border:1px solid var(--border);border-radius:10px;padding:14px 18px">
|
|
1490
|
+
<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>
|
|
1491
|
+
<div id="hubShareInfoContent" style="display:grid;grid-template-columns:auto 1fr;gap:6px 12px;font-size:12px;align-items:center"></div>
|
|
1492
|
+
</div>
|
|
1493
|
+
<div style="margin-top:16px;display:flex;align-items:center;gap:12px">
|
|
1494
|
+
<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>
|
|
1444
1495
|
</div>
|
|
1445
1496
|
</div>
|
|
1446
1497
|
|
|
@@ -1462,11 +1513,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1462
1513
|
<input type="text" id="cfgClientTeamToken" placeholder="">
|
|
1463
1514
|
<div class="field-hint" data-i18n="settings.hub.teamTokenClient.hint">Get this from your Hub admin to join the team</div>
|
|
1464
1515
|
</div>
|
|
1465
|
-
<
|
|
1466
|
-
<label data-i18n="settings.hub.userToken">User Token</label>
|
|
1467
|
-
<input type="text" id="cfgClientUserToken" placeholder="">
|
|
1468
|
-
<div class="field-hint" data-i18n="settings.hub.userToken.hint">Usually auto-obtained after joining. Leave blank in most cases.</div>
|
|
1469
|
-
</div>
|
|
1516
|
+
<input type="hidden" id="cfgClientUserToken" value="">
|
|
1470
1517
|
</div>
|
|
1471
1518
|
<div style="margin-top:12px">
|
|
1472
1519
|
<button class="btn btn-sm btn-primary" id="btnTestHubConn" onclick="testHubConnection()" data-i18n="settings.hub.testConnection">Test Connection</button>
|
|
@@ -1535,13 +1582,11 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1535
1582
|
</div>
|
|
1536
1583
|
<div class="admin-tabs" id="adminTabsBar">
|
|
1537
1584
|
<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>
|
|
1538
|
-
<button class="admin-tab" onclick="switchAdminTab('groups',this)"><span class="at-icon">\u{1F4C2}</span> <span data-i18n="admin.tab.groups">Groups</span> <span class="at-count" id="adminTabCountGroups">0</span></button>
|
|
1539
1585
|
<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>
|
|
1540
1586
|
<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>
|
|
1541
1587
|
<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>
|
|
1542
1588
|
</div>
|
|
1543
1589
|
<div class="admin-panel active" id="adminUsersPanel"></div>
|
|
1544
|
-
<div class="admin-panel" id="adminGroupsPanel"></div>
|
|
1545
1590
|
<div class="admin-panel" id="adminSharedMemoriesPanel"></div>
|
|
1546
1591
|
<div class="admin-panel" id="adminMemoriesPanel"></div>
|
|
1547
1592
|
<div class="admin-panel" id="adminSkillsPanel"></div>
|
|
@@ -1746,7 +1791,7 @@ let _embeddingWarningShown=false;
|
|
|
1746
1791
|
/* ─── i18n ─── */
|
|
1747
1792
|
const I18N={
|
|
1748
1793
|
en:{
|
|
1749
|
-
'title':'
|
|
1794
|
+
'title':'MemOS',
|
|
1750
1795
|
'subtitle':'Powered by MemOS',
|
|
1751
1796
|
'setup.desc':'Set a password to protect your memories',
|
|
1752
1797
|
'setup.pw':'Enter a password (4+ characters)',
|
|
@@ -2066,7 +2111,7 @@ const I18N={
|
|
|
2066
2111
|
'settings.hub.role':'Role',
|
|
2067
2112
|
'settings.hub.role.hub':'Hub (Server)',
|
|
2068
2113
|
'settings.hub.role.client':'Client (Connect to Hub)',
|
|
2069
|
-
'settings.hub.role.hint':'Hub = run the server; Client = connect to one.',
|
|
2114
|
+
'settings.hub.role.hint':'Hub = run the server; Client = connect to one. These two modes are mutually exclusive.',
|
|
2070
2115
|
'settings.hub.port':'Hub Port',
|
|
2071
2116
|
'settings.hub.port.hint':'Port for Hub service. Default: 18800',
|
|
2072
2117
|
'settings.hub.teamName':'Team Name',
|
|
@@ -2103,13 +2148,31 @@ const I18N={
|
|
|
2103
2148
|
'sidebar.hub':'\u{1F310} Team Sharing',
|
|
2104
2149
|
'sharing.sidebar.connected':'Connected',
|
|
2105
2150
|
'sharing.sidebar.disconnected':'Disconnected',
|
|
2151
|
+
'sharing.sidebar.pending':'Pending Approval',
|
|
2152
|
+
'sharing.sidebar.rejected':'Rejected',
|
|
2106
2153
|
'sharing.sidebar.starting':'Starting...',
|
|
2107
2154
|
'sharing.sidebar.notConfigured':'Not configured',
|
|
2108
2155
|
'sharing.sidebar.identity':'Identity:',
|
|
2109
2156
|
'sharing.sidebar.admin':'Admin',
|
|
2110
2157
|
'sharing.sidebar.targetHub':'Target Hub:',
|
|
2158
|
+
'sharing.pendingApproval.hint':'Your join request has been submitted. Please wait for the Hub admin to approve.',
|
|
2159
|
+
'sharing.rejected.hint':'Your join request was rejected by the Hub admin. Please contact the admin or retry.',
|
|
2160
|
+
'sharing.retryJoin':'Retry Join',
|
|
2161
|
+
'sharing.retryJoin.hint':'Clears local data and re-submits the join request',
|
|
2162
|
+
'sharing.retryJoin.confirm':'This will clear your current connection and re-submit a join request. Continue?',
|
|
2163
|
+
'sharing.retryJoin.success':'Join request re-submitted. Waiting for admin approval.',
|
|
2164
|
+
'sharing.retryJoin.fail':'Failed to retry join',
|
|
2165
|
+
'sharing.cannotJoinSelf':'Cannot join your own Hub. Please enter a remote Hub address.',
|
|
2166
|
+
'scope.hub':'Hub',
|
|
2167
|
+
'memory.detail.title':'Memory Detail',
|
|
2168
|
+
'memory.detail.loading':'Loading...',
|
|
2169
|
+
'memory.detail.notFound':'Memory not found',
|
|
2170
|
+
'memory.detail.copyId':'Click to copy ID',
|
|
2171
|
+
'memory.detail.created':'Created ',
|
|
2172
|
+
'memory.detail.updated':'Updated ',
|
|
2173
|
+
'memory.detail.viewTarget':'View target: ',
|
|
2111
2174
|
'admin.title':'Hub Admin Panel',
|
|
2112
|
-
'admin.subtitle':'Manage team members
|
|
2175
|
+
'admin.subtitle':'Manage team members and shared resources',
|
|
2113
2176
|
'admin.refresh':'\u21BB Refresh',
|
|
2114
2177
|
'admin.tab.users':'Users',
|
|
2115
2178
|
'admin.tab.groups':'Groups',
|
|
@@ -2155,6 +2218,7 @@ const I18N={
|
|
|
2155
2218
|
'admin.membersCount':'Members ({n}):',
|
|
2156
2219
|
'admin.noMembersYet':'No members yet.',
|
|
2157
2220
|
'admin.loadFailed':'Failed to load admin data: ',
|
|
2221
|
+
'admin.noPermission':'You do not have admin permissions to access this panel.',
|
|
2158
2222
|
'admin.groupsFailed':'Failed to load groups: ',
|
|
2159
2223
|
'toast.userApproved':'User approved',
|
|
2160
2224
|
'toast.userRejected':'User rejected',
|
|
@@ -2283,7 +2347,7 @@ const I18N={
|
|
|
2283
2347
|
'update.dismiss':'Dismiss'
|
|
2284
2348
|
},
|
|
2285
2349
|
zh:{
|
|
2286
|
-
'title':'
|
|
2350
|
+
'title':'MemOS 记忆',
|
|
2287
2351
|
'subtitle':'由 MemOS 驱动',
|
|
2288
2352
|
'setup.desc':'设置密码以保护你的记忆数据',
|
|
2289
2353
|
'setup.pw':'输入密码(至少4位)',
|
|
@@ -2603,7 +2667,7 @@ const I18N={
|
|
|
2603
2667
|
'settings.hub.role':'角色',
|
|
2604
2668
|
'settings.hub.role.hub':'Hub(服务端)',
|
|
2605
2669
|
'settings.hub.role.client':'Client(连接到 Hub)',
|
|
2606
|
-
'settings.hub.role.hint':'Hub = 本机做服务端;Client =
|
|
2670
|
+
'settings.hub.role.hint':'Hub = 本机做服务端;Client = 连接别人的服务端。两种模式互斥,不能同时使用。',
|
|
2607
2671
|
'settings.hub.port':'Hub 端口',
|
|
2608
2672
|
'settings.hub.port.hint':'Hub 服务端口,默认 18800',
|
|
2609
2673
|
'settings.hub.teamName':'团队名称',
|
|
@@ -2640,13 +2704,31 @@ const I18N={
|
|
|
2640
2704
|
'sidebar.hub':'\u{1F310} 团队共享',
|
|
2641
2705
|
'sharing.sidebar.connected':'已连接',
|
|
2642
2706
|
'sharing.sidebar.disconnected':'已断开',
|
|
2707
|
+
'sharing.sidebar.pending':'等待审核',
|
|
2708
|
+
'sharing.sidebar.rejected':'已拒绝',
|
|
2643
2709
|
'sharing.sidebar.starting':'启动中...',
|
|
2644
2710
|
'sharing.sidebar.notConfigured':'未配置',
|
|
2645
2711
|
'sharing.sidebar.identity':'身份:',
|
|
2646
2712
|
'sharing.sidebar.admin':'管理员',
|
|
2647
2713
|
'sharing.sidebar.targetHub':'目标 Hub:',
|
|
2714
|
+
'sharing.pendingApproval.hint':'加入申请已提交,请等待 Hub 管理员审核通过。',
|
|
2715
|
+
'sharing.rejected.hint':'您的加入申请已被 Hub 管理员拒绝,请联系管理员或重新申请。',
|
|
2716
|
+
'sharing.retryJoin':'重新申请',
|
|
2717
|
+
'sharing.retryJoin.hint':'清除本地连接数据并重新提交加入申请',
|
|
2718
|
+
'sharing.retryJoin.confirm':'这将清除当前连接数据并重新提交加入申请,是否继续?',
|
|
2719
|
+
'sharing.retryJoin.success':'加入申请已重新提交,请等待管理员审核。',
|
|
2720
|
+
'sharing.retryJoin.fail':'重新申请失败',
|
|
2721
|
+
'sharing.cannotJoinSelf':'不能加入自己的 Hub,请输入远程 Hub 地址。',
|
|
2722
|
+
'scope.hub':'Hub',
|
|
2723
|
+
'memory.detail.title':'记忆详情',
|
|
2724
|
+
'memory.detail.loading':'加载中...',
|
|
2725
|
+
'memory.detail.notFound':'未找到该记忆',
|
|
2726
|
+
'memory.detail.copyId':'点击复制 ID',
|
|
2727
|
+
'memory.detail.created':'创建于 ',
|
|
2728
|
+
'memory.detail.updated':'更新于 ',
|
|
2729
|
+
'memory.detail.viewTarget':'查看目标: ',
|
|
2648
2730
|
'admin.title':'Hub 管理面板',
|
|
2649
|
-
'admin.subtitle':'
|
|
2731
|
+
'admin.subtitle':'管理团队成员和共享资源',
|
|
2650
2732
|
'admin.refresh':'\u21BB 刷新',
|
|
2651
2733
|
'admin.tab.users':'用户',
|
|
2652
2734
|
'admin.tab.groups':'分组',
|
|
@@ -2692,6 +2774,7 @@ const I18N={
|
|
|
2692
2774
|
'admin.membersCount':'成员({n}):',
|
|
2693
2775
|
'admin.noMembersYet':'暂无成员。',
|
|
2694
2776
|
'admin.loadFailed':'加载管理数据失败:',
|
|
2777
|
+
'admin.noPermission':'您没有管理员权限,无法访问此面板。',
|
|
2695
2778
|
'admin.groupsFailed':'加载分组失败:',
|
|
2696
2779
|
'toast.userApproved':'用户已批准',
|
|
2697
2780
|
'toast.userRejected':'用户已拒绝',
|
|
@@ -2935,6 +3018,20 @@ function selectSharingRole(role){
|
|
|
2935
3018
|
document.getElementById('btnRoleClient').className='btn btn-sm'+(role==='client'?' btn-primary':'');
|
|
2936
3019
|
document.getElementById('hubModeConfig').style.display=role==='hub'?'block':'none';
|
|
2937
3020
|
document.getElementById('clientModeConfig').style.display=role==='client'?'block':'none';
|
|
3021
|
+
var sp=document.getElementById('sharingStatusPanel');
|
|
3022
|
+
var tp=document.getElementById('sharingTeamPanel');
|
|
3023
|
+
var ap=document.getElementById('sharingAdminPanel');
|
|
3024
|
+
if(role==='client'){
|
|
3025
|
+
if(sp) sp.style.display='none';
|
|
3026
|
+
if(tp) tp.style.display='none';
|
|
3027
|
+
if(ap) ap.style.display='none';
|
|
3028
|
+
var adminTab=document.querySelector('.tab[data-view="admin"]');
|
|
3029
|
+
if(adminTab) adminTab.style.display='none';
|
|
3030
|
+
}else{
|
|
3031
|
+
if(sp) sp.style.display='';
|
|
3032
|
+
if(tp) tp.style.display='';
|
|
3033
|
+
if(ap) ap.style.display='';
|
|
3034
|
+
}
|
|
2938
3035
|
if(role==='hub'){
|
|
2939
3036
|
var tk=document.getElementById('cfgHubTeamToken');
|
|
2940
3037
|
if(!tk.value.trim()) tk.value=_genToken(18);
|
|
@@ -2947,16 +3044,20 @@ function updateHubShareInfo(){
|
|
|
2947
3044
|
var panel=document.getElementById('hubShareInfo');
|
|
2948
3045
|
var content=document.getElementById('hubShareInfoContent');
|
|
2949
3046
|
if(!panel||!content) return;
|
|
2950
|
-
var
|
|
3047
|
+
var tokenEl=document.getElementById('cfgHubTeamToken');
|
|
3048
|
+
var token=tokenEl?tokenEl.value.trim():'';
|
|
2951
3049
|
var port=document.getElementById('cfgHubPort').value.trim()||'18800';
|
|
2952
3050
|
if(!token||_sharingRole!=='hub'){panel.style.display='none';return;}
|
|
2953
3051
|
panel.style.display='block';
|
|
3052
|
+
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';
|
|
2954
3053
|
var renderShare=function(ip){
|
|
2955
3054
|
var addr=ip?(ip+':'+esc(port)):('<'+t('settings.hub.shareInfo.yourIP')+'>:'+esc(port));
|
|
2956
3055
|
var tip=t('settings.hub.shareInfo.clickCopy');
|
|
2957
3056
|
content.innerHTML=
|
|
2958
|
-
'<span
|
|
2959
|
-
'<span
|
|
3057
|
+
'<span style="font-size:11px;color:var(--text-muted);font-weight:500">Hub '+t('settings.hub.hubAddress')+'</span>'+
|
|
3058
|
+
'<span style="'+cpStyle+'" onclick="navigator.clipboard.writeText(this.textContent);toast('Copied!','success')" title="'+tip+'">'+addr+'</span>'+
|
|
3059
|
+
'<span style="font-size:11px;color:var(--text-muted);font-weight:500">Team Token</span>'+
|
|
3060
|
+
'<span style="'+cpStyle+'" onclick="navigator.clipboard.writeText(this.textContent);toast('Copied!','success')" title="'+tip+'">'+esc(token)+'</span>';
|
|
2960
3061
|
};
|
|
2961
3062
|
if(_cachedLocalIP){renderShare(_cachedLocalIP);return;}
|
|
2962
3063
|
renderShare('');
|
|
@@ -2970,6 +3071,15 @@ async function testHubConnection(){
|
|
|
2970
3071
|
var addr=document.getElementById('cfgClientHubAddress').value.trim();
|
|
2971
3072
|
if(!addr){result.innerHTML='<span style="color:var(--rose)">\u274C '+t('settings.hub.test.noAddr')+'</span>';return;}
|
|
2972
3073
|
btn.disabled=true;result.innerHTML=t('settings.hub.test.testing');
|
|
3074
|
+
try{
|
|
3075
|
+
var ipsData=await fetch('/api/local-ips').then(function(r){return r.json();});
|
|
3076
|
+
var localAddrs=['127.0.0.1','localhost','0.0.0.0'].concat(ipsData.ips||[]);
|
|
3077
|
+
var parsed=new URL(addr.indexOf('://')>-1?addr:'http://'+addr);
|
|
3078
|
+
if(localAddrs.indexOf(parsed.hostname)>=0){
|
|
3079
|
+
result.innerHTML='<span style="color:var(--rose)">\u274C '+t('sharing.cannotJoinSelf')+'</span>';
|
|
3080
|
+
btn.disabled=false;return;
|
|
3081
|
+
}
|
|
3082
|
+
}catch(e){}
|
|
2973
3083
|
try{
|
|
2974
3084
|
var url=addr.match(/^https?:\\/\\//)?addr:'http://'+addr;
|
|
2975
3085
|
url=url.replace(/\\/+$/,'');
|
|
@@ -2978,7 +3088,8 @@ async function testHubConnection(){
|
|
|
2978
3088
|
if(d.ok){
|
|
2979
3089
|
result.innerHTML='<span style="color:var(--green)">\u2705 '+t('settings.hub.test.ok')+(d.teamName?' — '+esc(d.teamName):'')+'</span>';
|
|
2980
3090
|
}else{
|
|
2981
|
-
|
|
3091
|
+
var errMsg=d.error==='cannot_join_self'?t('sharing.cannotJoinSelf'):(d.error||t('settings.hub.test.fail'));
|
|
3092
|
+
result.innerHTML='<span style="color:var(--rose)">\u274C '+errMsg+'</span>';
|
|
2982
3093
|
}
|
|
2983
3094
|
}catch(e){
|
|
2984
3095
|
result.innerHTML='<span style="color:var(--rose)">\u274C '+esc(String(e))+'</span>';
|
|
@@ -3087,6 +3198,7 @@ function renderSharingSidebar(data){
|
|
|
3087
3198
|
if(!statusEl||!hintEl) return;
|
|
3088
3199
|
if(!data||!data.enabled){
|
|
3089
3200
|
if(section) section.style.display='none';
|
|
3201
|
+
window._isHubAdmin=false;
|
|
3090
3202
|
return;
|
|
3091
3203
|
}
|
|
3092
3204
|
if(section) section.style.display='block';
|
|
@@ -3095,25 +3207,43 @@ function renderSharingSidebar(data){
|
|
|
3095
3207
|
if(!badgeEl)return;
|
|
3096
3208
|
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>';
|
|
3097
3209
|
}
|
|
3098
|
-
if(
|
|
3099
|
-
|
|
3210
|
+
if(data.role==='hub'){
|
|
3211
|
+
setBadge('#34d399',t('sharing.sidebar.connected'),true);
|
|
3212
|
+
statusEl.innerHTML='';
|
|
3213
|
+
hintEl.textContent='';
|
|
3214
|
+
var adminTab=document.querySelector('.tab[data-view="admin"]');
|
|
3215
|
+
if(adminTab) adminTab.style.display='';
|
|
3216
|
+
}else if(conn.pendingApproval&&conn.user){
|
|
3217
|
+
setBadge('#fbbf24',t('sharing.sidebar.pending'),false);
|
|
3218
|
+
var html='<div class="info-grid">';
|
|
3219
|
+
html+='<span class="label">'+t('sharing.sidebar.identity')+'</span><span class="value">'+esc(conn.user.username)+'</span>';
|
|
3220
|
+
if(conn.teamName) html+='<span class="label">'+t('sharing.team')+'</span><span class="value">'+esc(conn.teamName)+'</span>';
|
|
3221
|
+
html+='</div>';
|
|
3222
|
+
statusEl.innerHTML=html;
|
|
3223
|
+
hintEl.textContent=t('sharing.pendingApproval.hint');
|
|
3224
|
+
var adminTab=document.querySelector('.tab[data-view="admin"]');
|
|
3225
|
+
if(adminTab) adminTab.style.display='none';
|
|
3226
|
+
}else if(conn.rejected&&conn.user){
|
|
3227
|
+
setBadge('#ef4444',t('sharing.sidebar.rejected'),false);
|
|
3228
|
+
var html='<div class="info-grid">';
|
|
3229
|
+
html+='<span class="label">'+t('sharing.sidebar.identity')+'</span><span class="value">'+esc(conn.user.username||'-')+'</span>';
|
|
3230
|
+
if(conn.teamName) html+='<span class="label">'+t('sharing.team')+'</span><span class="value">'+esc(conn.teamName)+'</span>';
|
|
3231
|
+
html+='</div>';
|
|
3232
|
+
statusEl.innerHTML=html;
|
|
3233
|
+
hintEl.textContent=t('sharing.rejected.hint');
|
|
3234
|
+
var adminTab=document.querySelector('.tab[data-view="admin"]');
|
|
3235
|
+
if(adminTab) adminTab.style.display='none';
|
|
3236
|
+
}else if(conn.connected&&conn.user){
|
|
3100
3237
|
var isAdmin=conn.user.role==='admin';
|
|
3101
3238
|
setBadge('#34d399',t('sharing.sidebar.connected'),true);
|
|
3102
3239
|
var html='<div class="info-grid">';
|
|
3103
3240
|
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>';
|
|
3104
3241
|
html+='<span class="label">'+t('sharing.team')+'</span><span class="value">'+esc(conn.teamName||'-')+'</span>';
|
|
3105
|
-
if(groups) html+='<span class="label">'+t('sharing.groups')+'</span><span class="value">'+esc(groups)+'</span>';
|
|
3106
3242
|
html+='</div>';
|
|
3107
3243
|
statusEl.innerHTML=html;
|
|
3108
3244
|
hintEl.innerHTML='';
|
|
3109
3245
|
var adminTab=document.querySelector('.tab[data-view="admin"]');
|
|
3110
3246
|
if(adminTab) adminTab.style.display=isAdmin?'':'none';
|
|
3111
|
-
}else if(data.role==='hub'){
|
|
3112
|
-
setBadge('#fbbf24',t('sharing.sidebar.starting'),false);
|
|
3113
|
-
statusEl.innerHTML='';
|
|
3114
|
-
hintEl.textContent=t('sharing.hubMode.hint');
|
|
3115
|
-
var adminTab=document.querySelector('.tab[data-view="admin"]');
|
|
3116
|
-
if(adminTab) adminTab.style.display=(data.admin&&data.admin.canManageUsers)?'':'none';
|
|
3117
3247
|
}else if(data.clientConfigured){
|
|
3118
3248
|
setBadge('#ef4444',t('sharing.sidebar.disconnected'),false);
|
|
3119
3249
|
statusEl.innerHTML='<div style="font-size:11px;color:var(--text-muted)">'+t('sharing.sidebar.targetHub')+' '+esc(data.hubUrl||'')+'</div>';
|
|
@@ -3136,49 +3266,75 @@ function renderSharingSettings(data){
|
|
|
3136
3266
|
}
|
|
3137
3267
|
var conn=data.connection||{};
|
|
3138
3268
|
var user=conn.user||{};
|
|
3139
|
-
var
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
var
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
'</span>';
|
|
3269
|
+
var actualRole=data.role||_sharingRole||'client';
|
|
3270
|
+
if(data.role) _sharingRole=data.role;
|
|
3271
|
+
var isAdmin=(data.admin&&data.admin.canManageUsers)||(conn.connected&&user.role==='admin')||(actualRole==='hub');
|
|
3272
|
+
window._isHubAdmin=isAdmin;
|
|
3273
|
+
var adminTab=document.querySelector('.tab[data-view="admin"]');
|
|
3274
|
+
if(adminTab) adminTab.style.display=isAdmin?'':'none';
|
|
3275
|
+
var hubAdminBtn=document.getElementById('hubAdminEntryBtn');
|
|
3276
|
+
|
|
3277
|
+
if(actualRole==='hub'){
|
|
3278
|
+
statusEl.innerHTML='';teamEl.innerHTML='';adminEl.innerHTML='';
|
|
3279
|
+
if(hubAdminBtn) hubAdminBtn.style.display=isAdmin?'':'none';
|
|
3280
|
+
return;
|
|
3152
3281
|
}
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
if(
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3282
|
+
|
|
3283
|
+
if(actualRole==='client'){
|
|
3284
|
+
statusEl.style.display='none';teamEl.style.display='none';adminEl.style.display='none';
|
|
3285
|
+
if(adminTab) adminTab.style.display='none';
|
|
3286
|
+
if(hubAdminBtn) hubAdminBtn.style.display='none';
|
|
3287
|
+
|
|
3288
|
+
var connBadge;
|
|
3289
|
+
if(conn.pendingApproval){
|
|
3290
|
+
connBadge='<span class="hic-badge pending"><span class="hic-dot amber"></span>'+t('sharing.sidebar.pending')+'</span>';
|
|
3291
|
+
}else if(conn.rejected){
|
|
3292
|
+
connBadge='<span class="hic-badge disconnected"><span class="hic-dot red"></span>'+t('sharing.sidebar.rejected')+'</span>';
|
|
3293
|
+
}else if(conn.connected){
|
|
3294
|
+
connBadge='<span class="hic-badge connected"><span class="hic-dot green"></span>'+t('sharing.sidebar.connected')+'</span>';
|
|
3295
|
+
}else{
|
|
3296
|
+
connBadge='<span class="hic-badge disconnected"><span class="hic-dot red"></span>'+t('sharing.sidebar.disconnected')+'</span>';
|
|
3166
3297
|
}
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3298
|
+
statusEl.style.display='';
|
|
3299
|
+
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">';
|
|
3300
|
+
if(conn.pendingApproval&&user.username){
|
|
3301
|
+
sh+='<span class="hic-label">'+t('sharing.user')+'</span><span class="hic-value">'+esc(user.username)+'</span>';
|
|
3302
|
+
sh+='</div><div class="hic-empty" style="color:#f59e0b">'+t('sharing.pendingApproval.hint')+'</div></div>';
|
|
3303
|
+
}else if(conn.rejected){
|
|
3304
|
+
if(user.username) 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:#ef4444">'+t('sharing.rejected.hint')+'</div>'+
|
|
3306
|
+
'<div style="margin-top:10px;padding:0 16px 14px"><button class="btn btn-sm btn-primary" onclick="retryHubJoin()">'+t('sharing.retryJoin')+'</button>'+
|
|
3307
|
+
'<span style="font-size:11px;color:var(--text-muted);margin-left:8px">'+t('sharing.retryJoin.hint')+'</span></div></div>';
|
|
3308
|
+
}else if(conn.connected&&user.username){
|
|
3309
|
+
sh+='<span class="hic-label">'+t('sharing.user')+'</span><span class="hic-value" style="display:flex;align-items:center;gap:6px">'+
|
|
3310
|
+
'<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" />'+
|
|
3311
|
+
'<button class="btn btn-sm" onclick="updateHubUsername()" style="padding:2px 10px;font-size:11px">'+t('sharing.saveUsername')+'</button>'+
|
|
3312
|
+
'</span>';
|
|
3313
|
+
sh+='<span class="hic-label">'+t('sharing.team')+'</span><span class="hic-value">'+esc(conn.teamName||'-')+'</span>';
|
|
3314
|
+
sh+='</div></div>';
|
|
3315
|
+
}else{
|
|
3316
|
+
sh+='</div></div>';
|
|
3317
|
+
}
|
|
3318
|
+
statusEl.innerHTML=sh;
|
|
3319
|
+
teamEl.innerHTML='';adminEl.innerHTML='';
|
|
3320
|
+
return;
|
|
3171
3321
|
}
|
|
3172
|
-
teamEl.innerHTML=th;
|
|
3173
3322
|
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3323
|
+
statusEl.innerHTML='';teamEl.innerHTML='';adminEl.innerHTML='';
|
|
3324
|
+
}
|
|
3325
|
+
|
|
3326
|
+
async function retryHubJoin(){
|
|
3327
|
+
if(!confirm(t('sharing.retryJoin.confirm'))) return;
|
|
3328
|
+
try{
|
|
3329
|
+
var r=await fetch('/api/sharing/retry-join',{method:'POST',headers:{'Content-Type':'application/json'}});
|
|
3330
|
+
var d=await r.json();
|
|
3331
|
+
if(d.ok){
|
|
3332
|
+
toast(t('sharing.retryJoin.success'),'success');
|
|
3333
|
+
setTimeout(function(){location.reload();},1500);
|
|
3334
|
+
}else{
|
|
3335
|
+
toast(d.error||t('sharing.retryJoin.fail'),'error');
|
|
3336
|
+
}
|
|
3337
|
+
}catch(e){toast(t('sharing.retryJoin.fail')+': '+e.message,'error');}
|
|
3182
3338
|
}
|
|
3183
3339
|
|
|
3184
3340
|
async function updateHubUsername(){
|
|
@@ -3212,6 +3368,7 @@ async function updateHubUsername(){
|
|
|
3212
3368
|
}
|
|
3213
3369
|
|
|
3214
3370
|
async function loadSharingPendingUsers(){
|
|
3371
|
+
if(_sharingRole==='client') return;
|
|
3215
3372
|
var el=document.getElementById('sharingAdminPanel');
|
|
3216
3373
|
if(!el) return;
|
|
3217
3374
|
el.innerHTML=t('sharing.loading');
|
|
@@ -3419,24 +3576,27 @@ function switchAdminTab(tab,btn){
|
|
|
3419
3576
|
}
|
|
3420
3577
|
|
|
3421
3578
|
async function loadAdminData(){
|
|
3579
|
+
if(!window._isHubAdmin){
|
|
3580
|
+
var statsEl=document.getElementById('adminStats');
|
|
3581
|
+
if(statsEl) statsEl.innerHTML='<div class="admin-empty">'+t('admin.noPermission')+'</div>';
|
|
3582
|
+
return;
|
|
3583
|
+
}
|
|
3422
3584
|
try{
|
|
3423
|
-
var [usersR,
|
|
3585
|
+
var [usersR,tasksR,skillsR,pendingR,memoriesR]=await Promise.all([
|
|
3424
3586
|
fetch('/api/sharing/users').then(function(r){return r.json();}),
|
|
3425
|
-
fetch('/api/sharing/groups').then(function(r){return r.json();}),
|
|
3426
3587
|
fetch('/api/admin/shared-tasks').then(function(r){return r.json();}),
|
|
3427
3588
|
fetch('/api/admin/shared-skills').then(function(r){return r.json();}),
|
|
3428
3589
|
fetch('/api/sharing/pending-users').then(function(r){return r.json();}),
|
|
3429
3590
|
fetch('/api/admin/shared-memories').then(function(r){return r.json();})
|
|
3430
3591
|
]);
|
|
3431
3592
|
adminDataCache.users=Array.isArray(usersR.users)?usersR.users:[];
|
|
3432
|
-
adminDataCache.groups=
|
|
3593
|
+
adminDataCache.groups=[];
|
|
3433
3594
|
adminDataCache.tasks=Array.isArray(tasksR.tasks)?tasksR.tasks:[];
|
|
3434
3595
|
adminDataCache.skills=Array.isArray(skillsR.skills)?skillsR.skills:[];
|
|
3435
3596
|
adminDataCache.memories=Array.isArray(memoriesR.memories)?memoriesR.memories:[];
|
|
3436
3597
|
var pending=Array.isArray(pendingR.users)?pendingR.users:[];
|
|
3437
3598
|
renderAdminStats(pending.length);
|
|
3438
3599
|
renderAdminUsers(adminDataCache.users, pending);
|
|
3439
|
-
renderAdminGroups(adminDataCache.groups);
|
|
3440
3600
|
renderAdminMemories(adminDataCache.tasks);
|
|
3441
3601
|
renderAdminSkills(adminDataCache.skills);
|
|
3442
3602
|
renderAdminSharedMemories(adminDataCache.memories);
|
|
@@ -3452,12 +3612,10 @@ function renderAdminStats(pendingCount){
|
|
|
3452
3612
|
el.innerHTML=
|
|
3453
3613
|
'<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>'+
|
|
3454
3614
|
'<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>'+
|
|
3455
|
-
'<div class="admin-stat-box"><span class="as-icon">\u{1F4C2}</span><div class="val">'+adminDataCache.groups.length+'</div><div class="lbl">'+t('admin.stat.groups')+'</div></div>'+
|
|
3456
3615
|
'<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>'+
|
|
3457
3616
|
'<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>'+
|
|
3458
3617
|
'<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>';
|
|
3459
3618
|
var tc=document.getElementById('adminTabCountUsers');if(tc)tc.textContent=adminDataCache.users.length+pendingCount;
|
|
3460
|
-
tc=document.getElementById('adminTabCountGroups');if(tc)tc.textContent=adminDataCache.groups.length;
|
|
3461
3619
|
tc=document.getElementById('adminTabCountMemories');if(tc)tc.textContent=(adminDataCache.memories||[]).length;
|
|
3462
3620
|
tc=document.getElementById('adminTabCountTasks');if(tc)tc.textContent=adminDataCache.tasks.length;
|
|
3463
3621
|
tc=document.getElementById('adminTabCountSkills');if(tc)tc.textContent=adminDataCache.skills.length;
|
|
@@ -3620,10 +3778,9 @@ function renderAdminMemories(tasks){
|
|
|
3620
3778
|
for(var i=0;i<tasks.length;i++){
|
|
3621
3779
|
var tk=tasks[i];
|
|
3622
3780
|
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>'+
|
|
3623
|
-
'
|
|
3781
|
+
'</div>'+
|
|
3624
3782
|
'<div class="admin-card-meta">'+
|
|
3625
3783
|
t('admin.owner')+esc(tk.ownerName||tk.sourceUserId||'unknown')+
|
|
3626
|
-
(tk.groupName?' \u00B7 '+t('admin.group')+esc(tk.groupName):'')+
|
|
3627
3784
|
(tk.chunkCount!=null?' \u00B7 '+t('admin.chunks')+tk.chunkCount:'')+
|
|
3628
3785
|
' \u00B7 '+t('admin.updated')+new Date(tk.updatedAt||tk.createdAt).toLocaleDateString()+
|
|
3629
3786
|
'</div>'+
|
|
@@ -3655,11 +3812,10 @@ function renderAdminSkills(skills){
|
|
|
3655
3812
|
for(var i=0;i<skills.length;i++){
|
|
3656
3813
|
var s=skills[i];
|
|
3657
3814
|
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>'+
|
|
3658
|
-
'
|
|
3815
|
+
'</div>'+
|
|
3659
3816
|
'<div class="admin-card-meta">'+
|
|
3660
3817
|
(s.description?esc(s.description)+'<br>':'')+
|
|
3661
3818
|
t('admin.owner')+esc(s.ownerName||s.sourceUserId||'unknown')+
|
|
3662
|
-
(s.groupName?' \u00B7 '+t('admin.group')+esc(s.groupName):'')+
|
|
3663
3819
|
(s.version!=null?' \u00B7 '+t('admin.version')+s.version:'')+
|
|
3664
3820
|
(s.qualityScore!=null?' \u00B7 '+t('admin.quality')+s.qualityScore:'')+
|
|
3665
3821
|
'</div>'+
|
|
@@ -3691,10 +3847,9 @@ function renderAdminSharedMemories(memories){
|
|
|
3691
3847
|
for(var i=0;i<memories.length;i++){
|
|
3692
3848
|
var m=memories[i];
|
|
3693
3849
|
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>'+
|
|
3694
|
-
'
|
|
3850
|
+
'</div>'+
|
|
3695
3851
|
'<div class="admin-card-meta">'+
|
|
3696
3852
|
t('admin.owner')+esc(m.ownerName||m.sourceUserId||'unknown')+
|
|
3697
|
-
(m.groupName?' \u00B7 '+t('admin.group')+esc(m.groupName):'')+
|
|
3698
3853
|
(m.kind?' \u00B7 Kind: '+esc(m.kind):'')+
|
|
3699
3854
|
(m.role?' \u00B7 Role: '+esc(m.role):'')+
|
|
3700
3855
|
' \u00B7 '+t('admin.updated')+new Date(m.updatedAt||m.createdAt).toLocaleDateString()+
|
|
@@ -3883,21 +4038,16 @@ function renderTaskShareActions(task){
|
|
|
3883
4038
|
const isShared=!!current;
|
|
3884
4039
|
var statusHtml='';
|
|
3885
4040
|
if(isShared){
|
|
3886
|
-
|
|
3887
|
-
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')+' ('+scopeLabel+')</span>';
|
|
4041
|
+
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>';
|
|
3888
4042
|
}
|
|
3889
4043
|
el.innerHTML=statusHtml+
|
|
3890
|
-
'<select id="taskShareScope" class="filter-select" style="min-width:120px">'+
|
|
3891
|
-
'<option value="group"'+(current==='group'?' selected':'')+'>Share to Group</option>'+
|
|
3892
|
-
'<option value="public"'+(current==='public'?' selected':'')+'>Share to Public</option>'+
|
|
3893
|
-
'</select>'+
|
|
3894
4044
|
'<button class="btn btn-sm" onclick="shareCurrentTask()">'+(isShared?t('share.updateBtn'):t('share.shareBtn'))+'</button>'+
|
|
3895
4045
|
(isShared?'<button class="btn btn-sm btn-ghost" onclick="unshareCurrentTask()">'+t('share.unshareBtn')+'</button>':'');
|
|
3896
4046
|
}
|
|
3897
4047
|
|
|
3898
4048
|
async function shareCurrentTask(){
|
|
3899
4049
|
if(!currentTaskDetail) return;
|
|
3900
|
-
const visibility=
|
|
4050
|
+
const visibility='public';
|
|
3901
4051
|
try{
|
|
3902
4052
|
const r=await fetch('/api/sharing/tasks/share',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({taskId:currentTaskDetail.id,visibility:visibility})});
|
|
3903
4053
|
const d=await r.json();
|
|
@@ -3922,21 +4072,16 @@ function renderSkillShareActions(skill){
|
|
|
3922
4072
|
const isShared=!!current;
|
|
3923
4073
|
var statusHtml='';
|
|
3924
4074
|
if(isShared){
|
|
3925
|
-
|
|
3926
|
-
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')+' ('+scopeLabel+')</span>';
|
|
4075
|
+
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>';
|
|
3927
4076
|
}
|
|
3928
4077
|
el.innerHTML=statusHtml+
|
|
3929
|
-
'<select id="skillShareScope" class="filter-select" style="min-width:120px">'+
|
|
3930
|
-
'<option value="group"'+(current==='group'?' selected':'')+'>Share to Group</option>'+
|
|
3931
|
-
'<option value="public"'+(current==='public'?' selected':'')+'>Share to Public</option>'+
|
|
3932
|
-
'</select>'+
|
|
3933
4078
|
'<button class="btn btn-sm" onclick="shareCurrentSkill()">'+(isShared?t('share.updateBtn'):t('share.shareBtn'))+'</button>'+
|
|
3934
4079
|
(isShared?'<button class="btn btn-sm btn-ghost" onclick="unshareCurrentSkill()">'+t('share.unshareBtn')+'</button>':'');
|
|
3935
4080
|
}
|
|
3936
4081
|
|
|
3937
4082
|
async function shareCurrentSkill(){
|
|
3938
4083
|
if(!currentSkillDetail) return;
|
|
3939
|
-
const visibility=
|
|
4084
|
+
const visibility='public';
|
|
3940
4085
|
try{
|
|
3941
4086
|
const r=await fetch('/api/sharing/skills/share',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({skillId:currentSkillDetail.id,visibility:visibility})});
|
|
3942
4087
|
const d=await r.json();
|
|
@@ -5188,6 +5333,7 @@ async function saveConfig(){
|
|
|
5188
5333
|
cfg.sharing.hub={port:hubPort?Number(hubPort):18800};
|
|
5189
5334
|
if(hubTeamName) cfg.sharing.hub.teamName=hubTeamName;
|
|
5190
5335
|
if(hubTeamToken) cfg.sharing.hub.teamToken=hubTeamToken;
|
|
5336
|
+
cfg.sharing.client={hubAddress:'',userToken:'',teamToken:''};
|
|
5191
5337
|
}
|
|
5192
5338
|
if(sharingEnabled&&_sharingRole==='client'){
|
|
5193
5339
|
var clientAddr=document.getElementById('cfgClientHubAddress').value.trim();
|
|
@@ -5197,6 +5343,17 @@ async function saveConfig(){
|
|
|
5197
5343
|
if(clientAddr) cfg.sharing.client.hubAddress=clientAddr;
|
|
5198
5344
|
if(clientTeamToken) cfg.sharing.client.teamToken=clientTeamToken;
|
|
5199
5345
|
if(clientUserToken) cfg.sharing.client.userToken=clientUserToken;
|
|
5346
|
+
cfg.sharing.hub={port:18800,teamName:'',teamToken:''};
|
|
5347
|
+
if(clientAddr){
|
|
5348
|
+
try{
|
|
5349
|
+
var ips=await fetch('/api/local-ips').then(function(r){return r.json();});
|
|
5350
|
+
var localAddrs=['127.0.0.1','localhost','0.0.0.0'].concat(ips.ips||[]);
|
|
5351
|
+
var parsed=new URL(clientAddr.indexOf('://')>-1?clientAddr:'http://'+clientAddr);
|
|
5352
|
+
if(localAddrs.indexOf(parsed.hostname)>=0){
|
|
5353
|
+
done();toast(t('sharing.cannotJoinSelf'),'error');return;
|
|
5354
|
+
}
|
|
5355
|
+
}catch(e){}
|
|
5356
|
+
}
|
|
5200
5357
|
}
|
|
5201
5358
|
|
|
5202
5359
|
// 1) Embedding model is required
|
|
@@ -5374,7 +5531,7 @@ function fillDays(rows,days){
|
|
|
5374
5531
|
const row=map.get(dateStr)||{};
|
|
5375
5532
|
out.push({date:dateStr,count:row.count??0,list:row.list??0,search:row.search??0,total:(row.list??0)+(row.search??0)});
|
|
5376
5533
|
}
|
|
5377
|
-
if(days>
|
|
5534
|
+
if(days>60){
|
|
5378
5535
|
const weeks=[];let i=0;
|
|
5379
5536
|
while(i<out.length){
|
|
5380
5537
|
const chunk=out.slice(i,i+7);
|
|
@@ -5392,19 +5549,23 @@ function fillDays(rows,days){
|
|
|
5392
5549
|
}
|
|
5393
5550
|
|
|
5394
5551
|
function renderBars(el,data,valueKey,H){
|
|
5395
|
-
|
|
5396
|
-
if(vals.every(v
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
el.innerHTML=data.map(r
|
|
5401
|
-
|
|
5402
|
-
|
|
5552
|
+
var vals=data.map(function(d){return d[valueKey]??0;});
|
|
5553
|
+
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;}
|
|
5554
|
+
var max=Math.max(1,Math.max.apply(null,vals));
|
|
5555
|
+
var n=data.length;
|
|
5556
|
+
var labelStep=n<=7?1:(n<=14?2:5);
|
|
5557
|
+
el.innerHTML=data.map(function(r,idx){
|
|
5558
|
+
var v=r[valueKey]??0;
|
|
5559
|
+
var rawDate=r.date.includes('~')?r.date:(r.date.length>5?r.date.slice(5):'');
|
|
5560
|
+
var showLabel=(idx%labelStep===0)||(idx===n-1);
|
|
5561
|
+
var label=showLabel?rawDate:'';
|
|
5562
|
+
var tipDate=r.date.length>=10?r.date.slice(5,10):'';
|
|
5563
|
+
var tipText=tipDate?(tipDate+': '+v):(''+v);
|
|
5403
5564
|
if(v===0){
|
|
5404
|
-
return '<div class="chart-bar-wrap"
|
|
5565
|
+
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>';
|
|
5405
5566
|
}
|
|
5406
|
-
|
|
5407
|
-
return '<div class="chart-bar-wrap"
|
|
5567
|
+
var h=Math.max(8,Math.round((v/max)*H));
|
|
5568
|
+
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>';
|
|
5408
5569
|
}).join('');
|
|
5409
5570
|
}
|
|
5410
5571
|
|
|
@@ -5415,25 +5576,31 @@ function renderChartWrites(rows){
|
|
|
5415
5576
|
}
|
|
5416
5577
|
|
|
5417
5578
|
function renderChartCalls(rows){
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
if(vals.every(v
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5579
|
+
var el=document.getElementById('chartCalls');
|
|
5580
|
+
var filled=fillDays(rows?.map(function(r){return {date:r.date,list:r.list,search:r.search};}),metricsDays);
|
|
5581
|
+
var vals=filled.map(function(f){return f.total;});
|
|
5582
|
+
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;}
|
|
5583
|
+
var max=Math.max(1,Math.max.apply(null,vals));
|
|
5584
|
+
var H=160;
|
|
5585
|
+
var n=filled.length;
|
|
5586
|
+
var labelStep=n<=7?1:(n<=14?2:5);
|
|
5587
|
+
el.innerHTML=filled.map(function(r,idx){
|
|
5588
|
+
var rawDate=r.date.includes('~')?r.date:(r.date.length>5?r.date.slice(5):'');
|
|
5589
|
+
var showLabel=(idx%labelStep===0)||(idx===n-1);
|
|
5590
|
+
var label=showLabel?rawDate:'';
|
|
5591
|
+
var tipDate=r.date.length>=10?r.date.slice(5,10):'';
|
|
5426
5592
|
if(r.total===0){
|
|
5427
|
-
|
|
5593
|
+
var tipZero=tipDate?(tipDate+': 0'):'0';
|
|
5594
|
+
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>';
|
|
5428
5595
|
}
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5596
|
+
var totalH=Math.max(8,Math.round((r.total/max)*H));
|
|
5597
|
+
var listH=r.list?Math.max(4,Math.round((r.list/r.total)*totalH)):0;
|
|
5598
|
+
var searchH=r.search?totalH-listH:0;
|
|
5599
|
+
var tip=(tipDate?tipDate+' - ':'')+'List: '+r.list+', Search: '+r.search;
|
|
5600
|
+
var bars='';
|
|
5434
5601
|
if(searchH>0) bars+='<div class="chart-bar violet" style="height:'+searchH+'px"></div>';
|
|
5435
5602
|
if(listH>0) bars+='<div class="chart-bar" style="height:'+listH+'px"></div>';
|
|
5436
|
-
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>';
|
|
5603
|
+
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>';
|
|
5437
5604
|
}).join('');
|
|
5438
5605
|
}
|
|
5439
5606
|
|
|
@@ -6011,38 +6178,50 @@ function scrollToMemory(targetId){
|
|
|
6011
6178
|
}
|
|
6012
6179
|
showMemoryModal(targetId);
|
|
6013
6180
|
}
|
|
6181
|
+
function fmtModalDate(v){
|
|
6182
|
+
if(!v) return '-';
|
|
6183
|
+
var d=new Date(v);
|
|
6184
|
+
if(isNaN(d.getTime())) return '-';
|
|
6185
|
+
return d.toLocaleString(dateLoc());
|
|
6186
|
+
}
|
|
6014
6187
|
async function showMemoryModal(chunkId){
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
body.innerHTML='<div style="text-align:center;padding:
|
|
6188
|
+
var overlay=document.getElementById('memoryModal');
|
|
6189
|
+
var body=document.getElementById('memoryModalBody');
|
|
6190
|
+
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>';
|
|
6018
6191
|
overlay.classList.add('show');
|
|
6019
6192
|
try{
|
|
6020
|
-
|
|
6021
|
-
if(!res.ok){body.innerHTML='<div style="text-align:center;padding:
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
if(
|
|
6040
|
-
|
|
6041
|
-
if(m.
|
|
6042
|
-
if(m.
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6193
|
+
var res=await fetch('/api/memory/'+encodeURIComponent(chunkId));
|
|
6194
|
+
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;}
|
|
6195
|
+
var data=await res.json();
|
|
6196
|
+
var m=data.memory;
|
|
6197
|
+
var role=(m.role||'unknown').toUpperCase();
|
|
6198
|
+
var roleCls=(m.role||'').toLowerCase();
|
|
6199
|
+
var ds=m.dedup_status||'active';
|
|
6200
|
+
var h='<div class="mm-hero">';
|
|
6201
|
+
h+='<div class="mm-hero-row">';
|
|
6202
|
+
h+='<span class="mm-role-chip '+roleCls+'">'+role+'</span>';
|
|
6203
|
+
if(ds!=='active') h+='<span class="mm-dedup-chip '+(ds==='duplicate'?'duplicate':'merged')+'">'+(ds==='duplicate'?'\u274C':'\u{1F504}')+' '+ds+'</span>';
|
|
6204
|
+
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>';
|
|
6205
|
+
h+='</div>';
|
|
6206
|
+
if(m.summary) h+='<div class="mm-summary">'+esc(m.summary)+'</div>';
|
|
6207
|
+
h+='</div>';
|
|
6208
|
+
if(m.content){
|
|
6209
|
+
h+='<div class="mm-section"><div class="mm-section-label">Content</div><pre class="mm-content">'+esc(m.content)+'</pre></div>';
|
|
6210
|
+
}
|
|
6211
|
+
h+='<div class="mm-meta">';
|
|
6212
|
+
if(m.session_key) h+='<div class="mm-meta-chip"><strong>Session</strong><span>'+esc(m.session_key.slice(0,12))+'</span></div>';
|
|
6213
|
+
h+='<div class="mm-meta-chip"><strong>'+t('memory.detail.created')+'</strong><span>'+fmtModalDate(m.created_at)+'</span></div>';
|
|
6214
|
+
if(m.updated_at) h+='<div class="mm-meta-chip"><strong>'+t('memory.detail.updated')+'</strong><span>'+fmtModalDate(m.updated_at)+'</span></div>';
|
|
6215
|
+
if(m.kind) h+='<div class="mm-meta-chip"><strong>Kind</strong><span>'+esc(m.kind)+'</span></div>';
|
|
6216
|
+
h+='</div>';
|
|
6217
|
+
if(m.dedup_reason){
|
|
6218
|
+
h+='<div class="mm-dedup"><div class="mm-dedup-box">'+esc(m.dedup_reason)+'</div></div>';
|
|
6219
|
+
}
|
|
6220
|
+
if(m.dedup_target&&m.dedup_target!==chunkId){
|
|
6221
|
+
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>';
|
|
6222
|
+
}
|
|
6223
|
+
body.innerHTML=h;
|
|
6224
|
+
}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>';}
|
|
6046
6225
|
}
|
|
6047
6226
|
function closeMemoryModal(){document.getElementById('memoryModal').classList.remove('show');}
|
|
6048
6227
|
|
|
@@ -6840,8 +7019,8 @@ checkAuth();
|
|
|
6840
7019
|
<div class="memory-modal-overlay" id="memoryModal" onclick="if(event.target===this)closeMemoryModal()">
|
|
6841
7020
|
<div class="memory-modal">
|
|
6842
7021
|
<div class="memory-modal-title">
|
|
6843
|
-
<span>Memory Detail</span>
|
|
6844
|
-
<button class="
|
|
7022
|
+
<div class="mm-tl"><div class="mm-tl-icon">\u{1F9E0}</div><span data-i18n="memory.detail.title">Memory Detail</span></div>
|
|
7023
|
+
<button class="mm-close" onclick="closeMemoryModal()" title="Close">×</button>
|
|
6845
7024
|
</div>
|
|
6846
7025
|
<div class="memory-modal-body" id="memoryModalBody"></div>
|
|
6847
7026
|
</div>
|