chattercatcher 0.1.31 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +866 -496
- package/dist/cli.js.map +1 -1
- package/dist/index.js +863 -493
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4318,8 +4318,8 @@ function findMarkdownLinkEnd(text, start) {
|
|
|
4318
4318
|
}
|
|
4319
4319
|
return -1;
|
|
4320
4320
|
}
|
|
4321
|
-
function
|
|
4322
|
-
|
|
4321
|
+
function stripInlineMarkdown(text) {
|
|
4322
|
+
let output = "";
|
|
4323
4323
|
let index = 0;
|
|
4324
4324
|
while (index < text.length) {
|
|
4325
4325
|
const linkStart = text.indexOf("[", index);
|
|
@@ -4328,12 +4328,10 @@ function parseInline(text) {
|
|
|
4328
4328
|
const candidates = [linkStart, boldStarStart, boldUnderscoreStart].filter((value) => value >= 0);
|
|
4329
4329
|
const next = candidates.length ? Math.min(...candidates) : -1;
|
|
4330
4330
|
if (next < 0) {
|
|
4331
|
-
|
|
4331
|
+
output += text.slice(index);
|
|
4332
4332
|
break;
|
|
4333
4333
|
}
|
|
4334
|
-
|
|
4335
|
-
elements.push({ tag: "text", text: text.slice(index, next) });
|
|
4336
|
-
}
|
|
4334
|
+
output += text.slice(index, next);
|
|
4337
4335
|
if (next === linkStart) {
|
|
4338
4336
|
const labelEnd = text.indexOf("](", next);
|
|
4339
4337
|
if (labelEnd > next) {
|
|
@@ -4341,27 +4339,29 @@ function parseInline(text) {
|
|
|
4341
4339
|
const hrefEnd = findMarkdownLinkEnd(text, hrefStart);
|
|
4342
4340
|
const href = hrefEnd >= 0 ? text.slice(hrefStart, hrefEnd) : "";
|
|
4343
4341
|
if (hrefEnd >= 0 && /^https?:\/\/\S+$/.test(href)) {
|
|
4344
|
-
|
|
4342
|
+
output += `${text.slice(next + 1, labelEnd)} ${href}`;
|
|
4345
4343
|
index = hrefEnd + 1;
|
|
4346
4344
|
continue;
|
|
4347
4345
|
}
|
|
4348
4346
|
}
|
|
4349
|
-
|
|
4347
|
+
output += text[next];
|
|
4350
4348
|
index = next + 1;
|
|
4351
4349
|
continue;
|
|
4352
4350
|
}
|
|
4353
4351
|
const marker = next === boldStarStart ? "**" : "__";
|
|
4354
4352
|
const close = text.indexOf(marker, next + marker.length);
|
|
4355
4353
|
if (close > next + marker.length) {
|
|
4356
|
-
|
|
4354
|
+
output += text.slice(next + marker.length, close);
|
|
4357
4355
|
index = close + marker.length;
|
|
4358
4356
|
continue;
|
|
4359
4357
|
}
|
|
4360
|
-
|
|
4358
|
+
output += marker;
|
|
4361
4359
|
index = next + marker.length;
|
|
4362
4360
|
}
|
|
4363
|
-
|
|
4364
|
-
|
|
4361
|
+
return output;
|
|
4362
|
+
}
|
|
4363
|
+
function parseInline(text) {
|
|
4364
|
+
return [{ tag: "text", text: stripInlineMarkdown(text) || " " }];
|
|
4365
4365
|
}
|
|
4366
4366
|
function pushParagraph(content, lines) {
|
|
4367
4367
|
if (lines.length === 0) return;
|
|
@@ -4402,7 +4402,7 @@ ${code.join("\n")}
|
|
|
4402
4402
|
const heading = line.match(/^#{1,6}\s+(.+)$/);
|
|
4403
4403
|
if (heading) {
|
|
4404
4404
|
pushParagraph(content, paragraph);
|
|
4405
|
-
content.push([{ tag: "text", text: heading[1]
|
|
4405
|
+
content.push([{ tag: "text", text: stripInlineMarkdown(heading[1]) || " " }]);
|
|
4406
4406
|
continue;
|
|
4407
4407
|
}
|
|
4408
4408
|
const unordered = line.match(/^[-*]\s+(.+)$/);
|
|
@@ -4430,17 +4430,13 @@ function buildFeishuPostContent(markdown, options) {
|
|
|
4430
4430
|
const content = parseMarkdownBlocks(markdown);
|
|
4431
4431
|
const mentions = options?.mentions ?? [];
|
|
4432
4432
|
if (mentions.length) {
|
|
4433
|
-
const mentionElements = mentions.map((mention) => ({
|
|
4434
|
-
tag: "at",
|
|
4435
|
-
user_id: mention.openId,
|
|
4436
|
-
user_name: mention.name
|
|
4437
|
-
}));
|
|
4438
4433
|
const firstLine = content[0] ?? [];
|
|
4439
4434
|
const firstText = firstLine[0];
|
|
4435
|
+
const prefix = mentions.map((mention) => `@${mention.name}`).join(" ");
|
|
4440
4436
|
if (firstText?.tag === "text") {
|
|
4441
|
-
content[0] = [
|
|
4437
|
+
content[0] = [{ tag: "text", text: `${prefix} ${firstText.text}` }, ...firstLine.slice(1)];
|
|
4442
4438
|
} else {
|
|
4443
|
-
content[0] = [
|
|
4439
|
+
content[0] = [{ tag: "text", text: `${prefix} ` }, ...firstLine];
|
|
4444
4440
|
}
|
|
4445
4441
|
}
|
|
4446
4442
|
return {
|
|
@@ -4469,11 +4465,23 @@ function extractImageKey(response) {
|
|
|
4469
4465
|
}
|
|
4470
4466
|
throw new Error("\u98DE\u4E66\u56FE\u7247\u4E0A\u4F20\u54CD\u5E94\u7F3A\u5C11 image_key\u3002");
|
|
4471
4467
|
}
|
|
4472
|
-
function
|
|
4468
|
+
function collectErrorFields(error) {
|
|
4469
|
+
const fields = [error];
|
|
4473
4470
|
const value = error && typeof error === "object" ? error : {};
|
|
4474
|
-
|
|
4475
|
-
const
|
|
4476
|
-
|
|
4471
|
+
fields.push(value.code, value.errorCode, value.msg, value.message);
|
|
4472
|
+
const response = value.response && typeof value.response === "object" ? value.response : {};
|
|
4473
|
+
const data = response.data && typeof response.data === "object" ? response.data : {};
|
|
4474
|
+
fields.push(data.code, data.errorCode, data.msg, data.message);
|
|
4475
|
+
return fields;
|
|
4476
|
+
}
|
|
4477
|
+
function isRichTextCompatibilityError(error) {
|
|
4478
|
+
return collectErrorFields(error).some((field) => {
|
|
4479
|
+
if (field === 230001) return true;
|
|
4480
|
+
if (typeof field === "string") {
|
|
4481
|
+
return /post|msg_type|content|unsupported|invalid/i.test(field);
|
|
4482
|
+
}
|
|
4483
|
+
return false;
|
|
4484
|
+
});
|
|
4477
4485
|
}
|
|
4478
4486
|
async function sendWithTextFallback(input) {
|
|
4479
4487
|
try {
|
|
@@ -5500,516 +5508,867 @@ import Fastify from "fastify";
|
|
|
5500
5508
|
function buildHtml() {
|
|
5501
5509
|
return `<!doctype html>
|
|
5502
5510
|
<html lang="zh-CN">
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
.
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
.
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
.
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5511
|
+
<head>
|
|
5512
|
+
<meta charset="utf-8" />
|
|
5513
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
5514
|
+
<meta name="color-scheme" content="dark" />
|
|
5515
|
+
<title>ChatterCatcher</title>
|
|
5516
|
+
<style>
|
|
5517
|
+
:root {
|
|
5518
|
+
--bg-primary: #0a0a0f;
|
|
5519
|
+
--bg-secondary: #12121a;
|
|
5520
|
+
--bg-tertiary: #1a1a28;
|
|
5521
|
+
--glass-bg: rgba(255,255,255,0.05);
|
|
5522
|
+
--glass-border: rgba(255,255,255,0.1);
|
|
5523
|
+
--glass-border-hover: rgba(255,255,255,0.2);
|
|
5524
|
+
--glass-shadow: 0 8px 32px rgba(0,0,0,0.3);
|
|
5525
|
+
--text-primary: #f0f0f5;
|
|
5526
|
+
--text-secondary: #a0a0b0;
|
|
5527
|
+
--text-muted: #6e6e80;
|
|
5528
|
+
--accent: #64d2ff;
|
|
5529
|
+
--accent-hover: #7dd8ff;
|
|
5530
|
+
--success: #30d158;
|
|
5531
|
+
--warning: #ff9f0a;
|
|
5532
|
+
--danger: #ff453a;
|
|
5533
|
+
--radius-sm: 8px;
|
|
5534
|
+
--radius-md: 12px;
|
|
5535
|
+
--radius-lg: 16px;
|
|
5536
|
+
--radius-xl: 24px;
|
|
5537
|
+
--space-xs: 4px;
|
|
5538
|
+
--space-sm: 8px;
|
|
5539
|
+
--space-md: 16px;
|
|
5540
|
+
--space-lg: 24px;
|
|
5541
|
+
--space-xl: 32px;
|
|
5542
|
+
--space-2xl: 48px;
|
|
5543
|
+
--font-sans: -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;
|
|
5544
|
+
--font-mono: "SF Mono","Menlo","Consolas",monospace;
|
|
5545
|
+
}
|
|
5546
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
5547
|
+
body {
|
|
5548
|
+
font-family: var(--font-sans);
|
|
5549
|
+
background: var(--bg-primary);
|
|
5550
|
+
color: var(--text-primary);
|
|
5551
|
+
line-height: 1.6;
|
|
5552
|
+
-webkit-font-smoothing: antialiased;
|
|
5553
|
+
overflow-x: hidden;
|
|
5554
|
+
min-height: 100vh;
|
|
5555
|
+
}
|
|
5556
|
+
.glass {
|
|
5557
|
+
background: var(--glass-bg);
|
|
5558
|
+
backdrop-filter: blur(20px) saturate(180%);
|
|
5559
|
+
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
|
5560
|
+
border: 1px solid var(--glass-border);
|
|
5561
|
+
border-radius: var(--radius-lg);
|
|
5562
|
+
box-shadow: var(--glass-shadow);
|
|
5563
|
+
transition: all 0.3s ease;
|
|
5564
|
+
}
|
|
5565
|
+
.glass:hover { border-color: var(--glass-border-hover); box-shadow: 0 12px 40px rgba(0,0,0,0.4); }
|
|
5566
|
+
.gradient-bg {
|
|
5567
|
+
background: linear-gradient(135deg,#0a0a0f 0%,#12121a 50%,#1a1a28 100%);
|
|
5568
|
+
min-height: 100vh;
|
|
5569
|
+
}
|
|
5570
|
+
.sidebar {
|
|
5571
|
+
position: fixed; left: 0; top: 0; width: 260px; height: 100vh;
|
|
5572
|
+
padding: var(--space-lg); display: flex; flex-direction: column; gap: var(--space-md); z-index: 100;
|
|
5573
|
+
background: linear-gradient(180deg,rgba(255,255,255,0.08) 0%,rgba(255,255,255,0.02) 100%);
|
|
5574
|
+
backdrop-filter: blur(40px) saturate(200%);
|
|
5575
|
+
-webkit-backdrop-filter: blur(40px) saturate(200%);
|
|
5576
|
+
border-right: 1px solid var(--glass-border);
|
|
5577
|
+
}
|
|
5578
|
+
.sidebar-logo {
|
|
5579
|
+
display: flex; align-items: center; gap: var(--space-sm);
|
|
5580
|
+
padding: var(--space-md); font-size: 20px; font-weight: 700;
|
|
5581
|
+
color: var(--text-primary); margin-bottom: var(--space-md);
|
|
5582
|
+
}
|
|
5583
|
+
.logo-icon {
|
|
5584
|
+
width: 36px; height: 36px;
|
|
5585
|
+
background: linear-gradient(135deg,var(--accent),#5e60ce);
|
|
5586
|
+
border-radius: var(--radius-md);
|
|
5587
|
+
display: flex; align-items: center; justify-content: center;
|
|
5588
|
+
box-shadow: 0 4px 16px rgba(100,210,255,0.3);
|
|
5589
|
+
}
|
|
5590
|
+
.sidebar-nav { display: flex; flex-direction: column; gap: var(--space-xs); }
|
|
5591
|
+
.nav-item {
|
|
5592
|
+
display: flex; align-items: center; gap: var(--space-sm);
|
|
5593
|
+
padding: var(--space-sm) var(--space-md); border-radius: var(--radius-md);
|
|
5594
|
+
color: var(--text-secondary); text-decoration: none; cursor: pointer;
|
|
5595
|
+
transition: all 0.2s ease; border: none; background: none;
|
|
5596
|
+
font-size: 14px; font-family: inherit; width: 100%; text-align: left;
|
|
5597
|
+
}
|
|
5598
|
+
.nav-item:hover { background: rgba(255,255,255,0.06); color: var(--text-primary); }
|
|
5599
|
+
.nav-item.active {
|
|
5600
|
+
background: rgba(100,210,255,0.15); color: var(--accent);
|
|
5601
|
+
box-shadow: 0 0 20px rgba(100,210,255,0.1);
|
|
5602
|
+
}
|
|
5603
|
+
.nav-icon { width: 20px; height: 20px; flex-shrink: 0; }
|
|
5604
|
+
.main-content { margin-left: 260px; min-height: 100vh; padding: var(--space-xl); }
|
|
5605
|
+
.page-header { margin-bottom: var(--space-xl); }
|
|
5606
|
+
.page-title {
|
|
5607
|
+
font-size: 36px; font-weight: 700; letter-spacing: -0.03em;
|
|
5608
|
+
margin-bottom: var(--space-sm);
|
|
5609
|
+
background: linear-gradient(135deg,var(--text-primary),var(--accent));
|
|
5610
|
+
-webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
|
|
5611
|
+
}
|
|
5612
|
+
.page-subtitle { color: var(--text-secondary); font-size: 15px; }
|
|
5613
|
+
.metrics-grid {
|
|
5614
|
+
display: grid; grid-template-columns: repeat(auto-fit,minmax(200px,1fr));
|
|
5615
|
+
gap: var(--space-md); margin-bottom: var(--space-xl);
|
|
5616
|
+
}
|
|
5617
|
+
.metric-card {
|
|
5618
|
+
padding: var(--space-lg); display: flex; flex-direction: column; gap: var(--space-sm);
|
|
5619
|
+
position: relative; overflow: hidden;
|
|
5620
|
+
}
|
|
5621
|
+
.metric-card::before {
|
|
5622
|
+
content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
|
|
5623
|
+
background: linear-gradient(90deg,var(--accent),transparent); opacity: 0.5;
|
|
5624
|
+
}
|
|
5625
|
+
.metric-value { font-size: 40px; font-weight: 700; color: var(--text-primary); line-height: 1; font-variant-numeric: tabular-nums; }
|
|
5626
|
+
.metric-label { font-size: 12px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; }
|
|
5627
|
+
.metric-note { font-size: 13px; color: var(--text-secondary); margin-top: var(--space-xs); }
|
|
5628
|
+
.content-grid { display: grid; grid-template-columns: 2fr 1fr; gap: var(--space-lg); }
|
|
5629
|
+
.content-panel { padding: var(--space-lg); }
|
|
5630
|
+
.panel-header {
|
|
5631
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
5632
|
+
margin-bottom: var(--space-lg); padding-bottom: var(--space-md);
|
|
5633
|
+
border-bottom: 1px solid var(--glass-border);
|
|
5634
|
+
}
|
|
5635
|
+
.panel-title { font-size: 18px; font-weight: 600; }
|
|
5636
|
+
.message-list { display: flex; flex-direction: column; gap: var(--space-sm); }
|
|
5637
|
+
.message-card {
|
|
5638
|
+
padding: var(--space-md); border-radius: var(--radius-md);
|
|
5639
|
+
background: rgba(255,255,255,0.03); border: 1px solid transparent;
|
|
5640
|
+
transition: all 0.25s ease; cursor: pointer;
|
|
5641
|
+
}
|
|
5642
|
+
.message-card:hover { background: rgba(255,255,255,0.06); border-color: var(--glass-border); transform: translateX(4px); }
|
|
5643
|
+
.message-meta {
|
|
5644
|
+
display: flex; align-items: center; gap: var(--space-md);
|
|
5645
|
+
color: var(--text-muted); font-size: 12px; margin-bottom: var(--space-xs); flex-wrap: wrap;
|
|
5646
|
+
}
|
|
5647
|
+
.message-text { color: var(--text-secondary); font-size: 14px; line-height: 1.6; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
|
5648
|
+
.status-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
|
|
5649
|
+
.status-dot.online { background: var(--success); box-shadow: 0 0 8px var(--success); }
|
|
5650
|
+
.status-dot.offline { background: var(--danger); }
|
|
5651
|
+
.status-dot.warning { background: var(--warning); box-shadow: 0 0 8px var(--warning); }
|
|
5652
|
+
.status-dot.pending { background: var(--text-muted); }
|
|
5653
|
+
.btn {
|
|
5654
|
+
display: inline-flex; align-items: center; justify-content: center; gap: var(--space-sm);
|
|
5655
|
+
padding: 10px var(--space-md); border-radius: var(--radius-md);
|
|
5656
|
+
border: 1px solid var(--glass-border); background: var(--glass-bg);
|
|
5657
|
+
color: var(--text-primary); font-family: inherit; font-size: 14px;
|
|
5658
|
+
cursor: pointer; transition: all 0.2s ease; text-decoration: none;
|
|
5659
|
+
}
|
|
5660
|
+
.btn:hover { background: rgba(255,255,255,0.1); border-color: var(--glass-border-hover); transform: translateY(-1px); }
|
|
5661
|
+
.btn-primary {
|
|
5662
|
+
background: linear-gradient(135deg,var(--accent),#5e60ce); color: white; border: none;
|
|
5663
|
+
font-weight: 600; box-shadow: 0 4px 16px rgba(100,210,255,0.3);
|
|
5664
|
+
}
|
|
5665
|
+
.btn-primary:hover {
|
|
5666
|
+
background: linear-gradient(135deg,var(--accent-hover),#6b6dd8);
|
|
5667
|
+
box-shadow: 0 6px 20px rgba(100,210,255,0.4); transform: translateY(-1px);
|
|
5668
|
+
}
|
|
5669
|
+
.btn-danger { background: rgba(255,69,58,0.15); color: var(--danger); border-color: rgba(255,69,58,0.3); }
|
|
5670
|
+
.btn-danger:hover { background: rgba(255,69,58,0.25); }
|
|
5671
|
+
.btn-sm { padding: 6px var(--space-sm); font-size: 13px; }
|
|
5672
|
+
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
|
|
5673
|
+
.tag {
|
|
5674
|
+
display: inline-flex; align-items: center; padding: 2px 10px;
|
|
5675
|
+
border-radius: 20px; font-size: 12px; font-weight: 500;
|
|
5676
|
+
background: rgba(255,255,255,0.06); color: var(--text-secondary);
|
|
5677
|
+
}
|
|
5678
|
+
.tag-success { background: rgba(48,209,88,0.15); color: var(--success); }
|
|
5679
|
+
.tag-warning { background: rgba(255,159,10,0.15); color: var(--warning); }
|
|
5680
|
+
.tag-error { background: rgba(255,69,58,0.15); color: var(--danger); }
|
|
5681
|
+
.tag-info { background: rgba(100,210,255,0.15); color: var(--accent); }
|
|
5682
|
+
.empty-state { text-align: center; padding: var(--space-2xl); color: var(--text-muted); }
|
|
5683
|
+
.empty-state svg { width: 48px; height: 48px; margin: 0 auto var(--space-md); opacity: 0.3; }
|
|
5684
|
+
.skeleton {
|
|
5685
|
+
background: linear-gradient(90deg,rgba(255,255,255,0.03) 25%,rgba(255,255,255,0.08) 50%,rgba(255,255,255,0.03) 75%);
|
|
5686
|
+
background-size: 200% 100%; animation: shimmer 1.5s infinite; border-radius: var(--radius-sm);
|
|
5687
|
+
}
|
|
5688
|
+
@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
|
|
5689
|
+
@keyframes fadeIn { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
|
|
5690
|
+
@keyframes slideIn { from { opacity: 0; transform: translateX(-10px); } to { opacity: 1; transform: translateX(0); } }
|
|
5691
|
+
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
|
|
5692
|
+
.view { display: none; animation: fadeIn 0.35s ease; }
|
|
5693
|
+
.view.active { display: block; }
|
|
5694
|
+
.search-box { position: relative; width: 100%; max-width: 400px; }
|
|
5695
|
+
.search-box input {
|
|
5696
|
+
width: 100%; padding: var(--space-sm) var(--space-md) var(--space-sm) 40px;
|
|
5697
|
+
border-radius: var(--radius-md); border: 1px solid var(--glass-border);
|
|
5698
|
+
background: var(--glass-bg); color: var(--text-primary); font-family: inherit;
|
|
5699
|
+
font-size: 14px; outline: none; transition: all 0.2s ease;
|
|
5700
|
+
}
|
|
5701
|
+
.search-box input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(100,210,255,0.1); }
|
|
5702
|
+
.search-box .search-icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--text-muted); }
|
|
5703
|
+
.data-table { width: 100%; border-collapse: collapse; }
|
|
5704
|
+
.data-table th {
|
|
5705
|
+
text-align: left; padding: var(--space-sm) var(--space-md); color: var(--text-muted);
|
|
5706
|
+
font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em;
|
|
5707
|
+
border-bottom: 1px solid var(--glass-border);
|
|
5708
|
+
}
|
|
5709
|
+
.data-table td { padding: var(--space-sm) var(--space-md); color: var(--text-secondary); font-size: 14px; border-bottom: 1px solid rgba(255,255,255,0.03); vertical-align: top; }
|
|
5710
|
+
.data-table tr:hover td { background: rgba(255,255,255,0.02); }
|
|
5711
|
+
.data-table tr:last-child td { border-bottom: none; }
|
|
5712
|
+
.truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
|
|
5713
|
+
.truncate-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
|
5714
|
+
.truncate-3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
|
|
5715
|
+
.flex { display: flex; } .flex-col { flex-direction: column; }
|
|
5716
|
+
.items-center { align-items: center; } .justify-between { justify-content: space-between; }
|
|
5717
|
+
.gap-sm { gap: var(--space-sm); } .gap-md { gap: var(--space-md); }
|
|
5718
|
+
.mt-md { margin-top: var(--space-md); } .mt-lg { margin-top: var(--space-lg); }
|
|
5719
|
+
.mb-md { margin-bottom: var(--space-md); }
|
|
5720
|
+
.toast {
|
|
5721
|
+
padding: var(--space-md) var(--space-lg); border-radius: var(--radius-md);
|
|
5722
|
+
background: var(--glass-bg); backdrop-filter: blur(20px); border: 1px solid var(--glass-border);
|
|
5723
|
+
box-shadow: 0 8px 32px rgba(0,0,0,0.4); color: var(--text-primary); font-size: 14px;
|
|
5724
|
+
max-width: 400px; animation: slideIn 0.3s ease;
|
|
5725
|
+
display: flex; align-items: center; gap: var(--space-sm);
|
|
5726
|
+
}
|
|
5727
|
+
.toast-success { border-color: rgba(48,209,88,0.3); background: rgba(48,209,88,0.1); }
|
|
5728
|
+
.toast-error { border-color: rgba(255,69,58,0.3); background: rgba(255,69,58,0.1); }
|
|
5729
|
+
.toast-warning { border-color: rgba(255,159,10,0.3); background: rgba(255,159,10,0.1); }
|
|
5730
|
+
.episode-card {
|
|
5731
|
+
padding: var(--space-md); border-radius: var(--radius-md);
|
|
5732
|
+
background: rgba(255,255,255,0.03); border: 1px solid transparent; transition: all 0.25s ease;
|
|
5733
|
+
}
|
|
5734
|
+
.episode-card:hover { background: rgba(255,255,255,0.06); border-color: var(--glass-border); }
|
|
5735
|
+
.qa-card {
|
|
5736
|
+
padding: var(--space-md); border-radius: var(--radius-md);
|
|
5737
|
+
background: rgba(255,255,255,0.03); border-left: 3px solid var(--accent); margin-bottom: var(--space-sm);
|
|
5738
|
+
}
|
|
5739
|
+
.qa-question { font-weight: 600; color: var(--text-primary); margin-bottom: var(--space-xs); font-size: 14px; }
|
|
5740
|
+
.qa-answer { color: var(--text-secondary); font-size: 14px; line-height: 1.6; }
|
|
5741
|
+
.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--space-lg); }
|
|
5742
|
+
.section-title { font-size: 24px; font-weight: 700; }
|
|
5743
|
+
.tabs {
|
|
5744
|
+
display: flex; gap: var(--space-xs); padding: 4px;
|
|
5745
|
+
background: rgba(255,255,255,0.03); border-radius: var(--radius-md); border: 1px solid var(--glass-border);
|
|
5746
|
+
}
|
|
5747
|
+
.tab { padding: 8px 16px; border-radius: var(--radius-sm); border: none; background: none; color: var(--text-secondary); font-family: inherit; font-size: 14px; cursor: pointer; transition: all 0.2s ease; }
|
|
5748
|
+
.tab:hover { color: var(--text-primary); }
|
|
5749
|
+
.tab.active { background: rgba(255,255,255,0.08); color: var(--text-primary); font-weight: 500; }
|
|
5750
|
+
.file-card {
|
|
5751
|
+
padding: var(--space-md); border-radius: var(--radius-md);
|
|
5752
|
+
background: rgba(255,255,255,0.03); border: 1px solid transparent; transition: all 0.25s ease; cursor: pointer;
|
|
5753
|
+
}
|
|
5754
|
+
.file-card:hover { background: rgba(255,255,255,0.06); border-color: var(--glass-border); }
|
|
5755
|
+
.file-icon {
|
|
5756
|
+
width: 40px; height: 40px; border-radius: var(--radius-sm);
|
|
5757
|
+
background: linear-gradient(135deg,var(--accent),#5e60ce);
|
|
5758
|
+
display: flex; align-items: center; justify-content: center; margin-bottom: var(--space-sm);
|
|
5759
|
+
}
|
|
5760
|
+
.timeline { position: relative; padding-left: 28px; }
|
|
5761
|
+
.timeline::before { content: ''; position: absolute; left: 8px; top: 0; bottom: 0; width: 2px; background: linear-gradient(180deg,var(--accent),transparent); opacity: 0.3; }
|
|
5762
|
+
.timeline-item { position: relative; padding-bottom: var(--space-lg); }
|
|
5763
|
+
.timeline-item::before { content: ''; position: absolute; left: -24px; top: 4px; width: 10px; height: 10px; border-radius: 50%; background: var(--accent); border: 2px solid var(--bg-primary); box-shadow: 0 0 0 2px var(--accent); }
|
|
5764
|
+
.timeline-date { font-size: 12px; color: var(--text-muted); margin-bottom: var(--space-xs); }
|
|
5765
|
+
.timeline-content { color: var(--text-secondary); font-size: 14px; }
|
|
5766
|
+
.status-bar { display: flex; align-items: center; gap: var(--space-md); padding: var(--space-md); margin-bottom: var(--space-lg); }
|
|
5767
|
+
.status-item { display: flex; align-items: center; gap: var(--space-sm); }
|
|
5768
|
+
.status-label { font-size: 13px; color: var(--text-muted); }
|
|
5769
|
+
.status-value { font-size: 14px; font-weight: 600; color: var(--text-primary); }
|
|
5770
|
+
.grid-2 { display: grid; grid-template-columns: repeat(2,1fr); gap: var(--space-md); }
|
|
5771
|
+
.grid-3 { display: grid; grid-template-columns: repeat(3,1fr); gap: var(--space-md); }
|
|
5772
|
+
.settings-group { padding: var(--space-lg); margin-bottom: var(--space-lg); }
|
|
5773
|
+
.settings-item { display: flex; justify-content: space-between; align-items: center; padding: var(--space-md) 0; border-bottom: 1px solid var(--glass-border); }
|
|
5774
|
+
.settings-item:last-child { border-bottom: none; }
|
|
5775
|
+
.settings-label { font-size: 14px; font-weight: 500; color: var(--text-primary); }
|
|
5776
|
+
.settings-value { font-size: 14px; color: var(--text-secondary); font-family: var(--font-mono); }
|
|
5777
|
+
.settings-desc { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
|
|
5778
|
+
.mobile-nav {
|
|
5779
|
+
display: none; position: fixed; bottom: 0; left: 0; right: 0;
|
|
5780
|
+
padding: var(--space-sm); z-index: 100; flex-direction: row; justify-content: space-around;
|
|
5781
|
+
border-top: 1px solid var(--glass-border);
|
|
5782
|
+
background: linear-gradient(180deg,rgba(255,255,255,0.08) 0%,rgba(255,255,255,0.02) 100%);
|
|
5783
|
+
backdrop-filter: blur(40px) saturate(200%);
|
|
5784
|
+
}
|
|
5785
|
+
.mobile-nav-item { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: var(--space-xs); color: var(--text-secondary); text-decoration: none; cursor: pointer; border: none; background: none; font-size: 10px; font-family: inherit; }
|
|
5786
|
+
.mobile-nav-item.active { color: var(--accent); }
|
|
5787
|
+
.pulse { animation: pulse 2s cubic-bezier(0.4,0,0.6,1) infinite; }
|
|
5788
|
+
@media (max-width: 1024px) {
|
|
5789
|
+
.sidebar { width: 72px; padding: var(--space-sm); }
|
|
5790
|
+
.sidebar-logo span, .nav-item span { display: none; }
|
|
5791
|
+
.nav-item { justify-content: center; padding: var(--space-sm); }
|
|
5792
|
+
.main-content { margin-left: 72px; padding: var(--space-lg); }
|
|
5793
|
+
.content-grid { grid-template-columns: 1fr; }
|
|
5794
|
+
.metrics-grid { grid-template-columns: repeat(2,1fr); }
|
|
5795
|
+
}
|
|
5796
|
+
@media (max-width: 768px) {
|
|
5797
|
+
.sidebar { display: none; }
|
|
5798
|
+
.mobile-nav { display: flex; }
|
|
5799
|
+
.main-content { margin-left: 0; margin-bottom: 80px; padding: var(--space-md); }
|
|
5800
|
+
.page-title { font-size: 28px; }
|
|
5801
|
+
.metrics-grid { grid-template-columns: repeat(2,1fr); }
|
|
5802
|
+
.grid-2, .grid-3 { grid-template-columns: 1fr; }
|
|
5803
|
+
.section-header { flex-direction: column; align-items: flex-start; gap: var(--space-sm); }
|
|
5804
|
+
}
|
|
5805
|
+
@media (prefers-reduced-motion: reduce) {
|
|
5806
|
+
*, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
|
|
5807
|
+
}
|
|
5808
|
+
::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
5809
|
+
::-webkit-scrollbar-track { background: transparent; }
|
|
5810
|
+
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 4px; }
|
|
5811
|
+
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.2); }
|
|
5812
|
+
.highlight-text { background: rgba(100,210,255,0.15); padding: 0 4px; border-radius: 3px; color: var(--accent); }
|
|
5813
|
+
</style>
|
|
5814
|
+
</head>
|
|
5815
|
+
<body class="gradient-bg">
|
|
5816
|
+
<aside class="sidebar">
|
|
5817
|
+
<div class="sidebar-logo">
|
|
5818
|
+
<div class="logo-icon">
|
|
5819
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
|
5820
|
+
</div>
|
|
5821
|
+
<span>ChatterCatcher</span>
|
|
5822
|
+
</div>
|
|
5823
|
+
<nav class="sidebar-nav">
|
|
5824
|
+
<button class="nav-item active" data-view="overview">
|
|
5825
|
+
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
|
|
5826
|
+
<span>\u6982\u89C8</span>
|
|
5827
|
+
</button>
|
|
5828
|
+
<button class="nav-item" data-view="messages">
|
|
5829
|
+
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
|
5830
|
+
<span>\u6D88\u606F</span>
|
|
5831
|
+
</button>
|
|
5832
|
+
<button class="nav-item" data-view="episodes">
|
|
5833
|
+
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
|
|
5834
|
+
<span>\u4F1A\u8BDD\u8BB0\u5FC6</span>
|
|
5835
|
+
</button>
|
|
5836
|
+
<button class="nav-item" data-view="files">
|
|
5837
|
+
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"/><polyline points="14 2 14 8 20 8"/></svg>
|
|
5838
|
+
<span>\u6587\u4EF6\u5E93</span>
|
|
5839
|
+
</button>
|
|
5840
|
+
<button class="nav-item" data-view="tasks">
|
|
5841
|
+
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
|
|
5842
|
+
<span>\u4EFB\u52A1</span>
|
|
5843
|
+
</button>
|
|
5844
|
+
<button class="nav-item" data-view="qa-logs">
|
|
5845
|
+
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
|
|
5846
|
+
<span>\u95EE\u7B54\u65E5\u5FD7</span>
|
|
5847
|
+
</button>
|
|
5848
|
+
<button class="nav-item" data-view="settings">
|
|
5849
|
+
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.6 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
|
5850
|
+
<span>\u8BBE\u7F6E</span>
|
|
5851
|
+
</button>
|
|
5852
|
+
</nav>
|
|
5853
|
+
<div style="margin-top: auto; padding: var(--space-md);">
|
|
5854
|
+
<div style="display: flex; align-items: center; gap: var(--space-sm); font-size: 12px; color: var(--text-muted);">
|
|
5855
|
+
<span class="status-dot online" id="gateway-indicator"></span>
|
|
5856
|
+
<span id="gateway-status-text">Gateway \u8FD0\u884C\u4E2D</span>
|
|
5857
|
+
</div>
|
|
5858
|
+
<div style="font-size: 11px; color: var(--text-muted); margin-top: var(--space-xs); opacity: 0.7;" id="version-text">v0.0.0</div>
|
|
5859
|
+
</div>
|
|
5860
|
+
</aside>
|
|
5861
|
+
|
|
5862
|
+
<nav class="mobile-nav glass">
|
|
5863
|
+
<button class="mobile-nav-item active" data-view="overview">
|
|
5864
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
|
|
5865
|
+
<span>\u6982\u89C8</span>
|
|
5866
|
+
</button>
|
|
5867
|
+
<button class="mobile-nav-item" data-view="messages">
|
|
5868
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
|
5869
|
+
<span>\u6D88\u606F</span>
|
|
5870
|
+
</button>
|
|
5871
|
+
<button class="mobile-nav-item" data-view="files">
|
|
5872
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"/><polyline points="14 2 14 8 20 8"/></svg>
|
|
5873
|
+
<span>\u6587\u4EF6</span>
|
|
5874
|
+
</button>
|
|
5875
|
+
<button class="mobile-nav-item" data-view="tasks">
|
|
5876
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
|
|
5877
|
+
<span>\u4EFB\u52A1</span>
|
|
5878
|
+
</button>
|
|
5879
|
+
<button class="mobile-nav-item" data-view="settings">
|
|
5880
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.6 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
|
5881
|
+
<span>\u8BBE\u7F6E</span>
|
|
5882
|
+
</button>
|
|
5883
|
+
</nav>
|
|
5884
|
+
|
|
5885
|
+
<main class="main-content">
|
|
5886
|
+
<div class="view active" id="view-overview">
|
|
5887
|
+
<div class="page-header">
|
|
5888
|
+
<h1 class="page-title">Dashboard</h1>
|
|
5889
|
+
<p class="page-subtitle">\u672C\u5730\u4F18\u5148\u7684\u5BB6\u5EAD\u7FA4\u77E5\u8BC6\u5E93 \xB7 \u95EE\u7B54\u5FC5\u987B\u5148\u68C0\u7D22 RAG \u8BC1\u636E\uFF0C\u4E0D\u5806\u53E0\u5168\u91CF\u4E0A\u4E0B\u6587</p>
|
|
5890
|
+
</div>
|
|
5891
|
+
<div class="metrics-grid" id="metrics"></div>
|
|
5892
|
+
<div class="content-grid">
|
|
5604
5893
|
<div>
|
|
5605
|
-
<
|
|
5606
|
-
|
|
5894
|
+
<div class="content-panel glass">
|
|
5895
|
+
<div class="panel-header">
|
|
5896
|
+
<h2 class="panel-title">\u6700\u8FD1\u6D88\u606F</h2>
|
|
5897
|
+
<button class="btn btn-sm" onclick="navigateTo('messages')">\u67E5\u770B\u5168\u90E8</button>
|
|
5898
|
+
</div>
|
|
5899
|
+
<div id="recent-messages"></div>
|
|
5900
|
+
</div>
|
|
5901
|
+
<div class="content-panel glass mt-lg">
|
|
5902
|
+
<div class="panel-header">
|
|
5903
|
+
<h2 class="panel-title">\u4F1A\u8BDD\u8BB0\u5FC6</h2>
|
|
5904
|
+
<button class="btn btn-sm" onclick="navigateTo('episodes')">\u67E5\u770B\u5168\u90E8</button>
|
|
5905
|
+
</div>
|
|
5906
|
+
<div id="recent-episodes"></div>
|
|
5907
|
+
</div>
|
|
5607
5908
|
</div>
|
|
5608
5909
|
<div>
|
|
5609
|
-
<div class="
|
|
5610
|
-
<
|
|
5910
|
+
<div class="content-panel glass">
|
|
5911
|
+
<div class="panel-header"><h2 class="panel-title">\u7CFB\u7EDF\u72B6\u6001</h2></div>
|
|
5912
|
+
<div id="system-status"></div>
|
|
5913
|
+
</div>
|
|
5914
|
+
<div class="content-panel glass mt-lg">
|
|
5915
|
+
<div class="panel-header"><h2 class="panel-title">\u5FEB\u6377\u64CD\u4F5C</h2></div>
|
|
5916
|
+
<div style="display: flex; flex-direction: column; gap: var(--space-sm);">
|
|
5917
|
+
<button class="btn btn-primary" id="btn-process-messages" onclick="processNow()">
|
|
5918
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg>
|
|
5919
|
+
\u7ACB\u5373\u5904\u7406\u6D88\u606F
|
|
5920
|
+
</button>
|
|
5921
|
+
<button class="btn" onclick="navigateTo('settings')">
|
|
5922
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.6 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
|
5923
|
+
\u7CFB\u7EDF\u8BBE\u7F6E
|
|
5924
|
+
</button>
|
|
5925
|
+
</div>
|
|
5926
|
+
</div>
|
|
5927
|
+
<div class="content-panel glass mt-lg">
|
|
5928
|
+
<div class="panel-header"><h2 class="panel-title">RAG \u68C0\u7D22</h2></div>
|
|
5929
|
+
<div style="font-size: 13px; color: var(--text-secondary); line-height: 1.8;">
|
|
5930
|
+
<div style="display: flex; align-items: center; gap: var(--space-sm); margin-bottom: var(--space-sm);"><span class="tag tag-success">FTS5</span><span>\u5173\u952E\u8BCD\u68C0\u7D22</span></div>
|
|
5931
|
+
<div style="display: flex; align-items: center; gap: var(--space-sm); margin-bottom: var(--space-sm);"><span class="tag tag-info">\u5411\u91CF</span><span>\u8BED\u4E49\u68C0\u7D22</span></div>
|
|
5932
|
+
<div style="display: flex; align-items: center; gap: var(--space-sm);"><span class="tag tag-success">\u6DF7\u5408</span><span>Hybrid RAG</span></div>
|
|
5933
|
+
</div>
|
|
5611
5934
|
</div>
|
|
5612
|
-
<div id="action-status" class="status-line"></div>
|
|
5613
5935
|
</div>
|
|
5614
|
-
</
|
|
5936
|
+
</div>
|
|
5937
|
+
</div>
|
|
5615
5938
|
|
|
5616
|
-
|
|
5939
|
+
<div class="view" id="view-messages">
|
|
5940
|
+
<div class="section-header">
|
|
5941
|
+
<div><h1 class="section-title">\u6D88\u606F</h1><p class="page-subtitle">\u7FA4\u804A\u6D88\u606F\u5386\u53F2</p></div>
|
|
5942
|
+
<div class="search-box">
|
|
5943
|
+
<svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
|
5944
|
+
<input type="text" id="message-search" placeholder="\u641C\u7D22\u6D88\u606F..." oninput="filterMessages()" />
|
|
5945
|
+
</div>
|
|
5946
|
+
</div>
|
|
5947
|
+
<div class="content-panel glass"><div id="messages-list"></div></div>
|
|
5948
|
+
</div>
|
|
5617
5949
|
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5950
|
+
<div class="view" id="view-episodes">
|
|
5951
|
+
<div class="section-header"><div><h1 class="section-title">\u4F1A\u8BDD\u8BB0\u5FC6</h1><p class="page-subtitle">\u81EA\u52A8\u805A\u5408\u7684\u804A\u5929\u7247\u6BB5</p></div></div>
|
|
5952
|
+
<div class="content-panel glass"><div id="episodes-list"></div></div>
|
|
5953
|
+
</div>
|
|
5954
|
+
|
|
5955
|
+
<div class="view" id="view-files">
|
|
5956
|
+
<div class="section-header"><div><h1 class="section-title">\u6587\u4EF6\u5E93</h1><p class="page-subtitle">\u5DF2\u5BFC\u5165\u7684\u6587\u4EF6\u77E5\u8BC6\u6E90</p></div></div>
|
|
5957
|
+
<div id="files-list"></div>
|
|
5958
|
+
</div>
|
|
5959
|
+
|
|
5960
|
+
<div class="view" id="view-tasks">
|
|
5961
|
+
<div class="section-header"><div><h1 class="section-title">\u4EFB\u52A1</h1><p class="page-subtitle">\u6587\u4EF6\u89E3\u6790\u4E0E\u5B9A\u65F6\u4EFB\u52A1</p></div></div>
|
|
5962
|
+
<div class="tabs" style="margin-bottom: var(--space-lg);">
|
|
5963
|
+
<button class="tab active" data-tab="file-jobs" onclick="switchTab('file-jobs')">\u6587\u4EF6\u89E3\u6790</button>
|
|
5964
|
+
<button class="tab" data-tab="cron-jobs" onclick="switchTab('cron-jobs')">\u5B9A\u65F6\u4EFB\u52A1</button>
|
|
5965
|
+
</div>
|
|
5966
|
+
<div class="content-panel glass" id="tab-file-jobs"><div id="file-jobs-list"></div></div>
|
|
5967
|
+
<div class="content-panel glass" id="tab-cron-jobs" style="display: none;"><div id="cron-jobs-list"></div></div>
|
|
5968
|
+
</div>
|
|
5969
|
+
|
|
5970
|
+
<div class="view" id="view-qa-logs">
|
|
5971
|
+
<div class="section-header"><div><h1 class="section-title">\u95EE\u7B54\u65E5\u5FD7</h1><p class="page-subtitle">\u95EE\u7B54\u5386\u53F2\u8BB0\u5F55</p></div></div>
|
|
5972
|
+
<div class="content-panel glass"><div id="qa-logs-list"></div></div>
|
|
5973
|
+
</div>
|
|
5974
|
+
|
|
5975
|
+
<div class="view" id="view-settings">
|
|
5976
|
+
<div class="section-header"><div><h1 class="section-title">\u8BBE\u7F6E</h1><p class="page-subtitle">\u7CFB\u7EDF\u914D\u7F6E\u4E0E\u64CD\u4F5C</p></div></div>
|
|
5977
|
+
<div class="settings-group glass" id="settings-config"></div>
|
|
5978
|
+
<div class="settings-group glass">
|
|
5979
|
+
<h3 style="font-size: 16px; font-weight: 600; margin-bottom: var(--space-md);">\u64CD\u4F5C</h3>
|
|
5980
|
+
<div style="display: flex; flex-direction: column; gap: var(--space-sm);">
|
|
5981
|
+
<button class="btn btn-primary" onclick="processNow()">
|
|
5982
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg>
|
|
5983
|
+
\u7ACB\u5373\u5904\u7406\u6D88\u606F\u7D22\u5F15
|
|
5984
|
+
</button>
|
|
5985
|
+
<div style="font-size: 12px; color: var(--text-muted); padding: var(--space-sm); background: rgba(255,255,255,0.03); border-radius: var(--radius-sm);">
|
|
5986
|
+
\u8FD0\u884C CLI \u547D\u4EE4\u8FDB\u884C\u66F4\u591A\u64CD\u4F5C\uFF1A
|
|
5987
|
+
<div style="font-family: var(--font-mono); margin-top: var(--space-xs); line-height: 1.8;">
|
|
5988
|
+
chattercatcher settings<br/>
|
|
5989
|
+
chattercatcher doctor<br/>
|
|
5990
|
+
chattercatcher index rebuild<br/>
|
|
5991
|
+
chattercatcher files add <path...><br/>
|
|
5992
|
+
chattercatcher export
|
|
5993
|
+
</div>
|
|
5994
|
+
</div>
|
|
5632
5995
|
</div>
|
|
5633
|
-
<aside>
|
|
5634
|
-
<section>
|
|
5635
|
-
<h2>\u7FA4\u804A</h2>
|
|
5636
|
-
<div id="chats" class="empty">\u6B63\u5728\u8BFB\u53D6...</div>
|
|
5637
|
-
</section>
|
|
5638
|
-
<section>
|
|
5639
|
-
<h2>\u6587\u4EF6\u5E93</h2>
|
|
5640
|
-
<div id="files" class="empty">\u6B63\u5728\u8BFB\u53D6...</div>
|
|
5641
|
-
</section>
|
|
5642
|
-
<section>
|
|
5643
|
-
<h2>\u89E3\u6790\u4EFB\u52A1</h2>
|
|
5644
|
-
<div id="file-jobs" class="empty">\u6B63\u5728\u8BFB\u53D6...</div>
|
|
5645
|
-
</section>
|
|
5646
|
-
<section>
|
|
5647
|
-
<h2>\u5B9A\u65F6\u4EFB\u52A1</h2>
|
|
5648
|
-
<div id="cron-jobs" class="empty">\u6B63\u5728\u8BFB\u53D6...</div>
|
|
5649
|
-
</section>
|
|
5650
|
-
<section>
|
|
5651
|
-
<h2>\u672C\u5730\u64CD\u4F5C</h2>
|
|
5652
|
-
<p><code>chattercatcher settings</code> \u4FEE\u6539\u914D\u7F6E\u3002</p>
|
|
5653
|
-
<p><code>chattercatcher files add <path...></code> \u5BFC\u5165\u6587\u672C\u3001DOCX \u6216 PDF \u6587\u4EF6\u3002</p>
|
|
5654
|
-
<p><code>chattercatcher doctor</code> \u68C0\u67E5\u98DE\u4E66\u3001\u6A21\u578B\u3001RAG \u548C\u672C\u5730\u5B58\u50A8\u3002</p>
|
|
5655
|
-
</section>
|
|
5656
|
-
</aside>
|
|
5657
5996
|
</div>
|
|
5658
|
-
</
|
|
5659
|
-
|
|
5660
|
-
const metrics = document.querySelector("#metrics");
|
|
5661
|
-
const messages = document.querySelector("#messages");
|
|
5662
|
-
const episodes = document.querySelector("#episodes");
|
|
5663
|
-
const chats = document.querySelector("#chats");
|
|
5664
|
-
const files = document.querySelector("#files");
|
|
5665
|
-
const fileJobs = document.querySelector("#file-jobs");
|
|
5666
|
-
const cronJobs = document.querySelector("#cron-jobs");
|
|
5667
|
-
const qaLogs = document.querySelector("#qa-logs");
|
|
5668
|
-
const processMessages = document.querySelector("#process-messages");
|
|
5669
|
-
const actionStatus = document.querySelector("#action-status");
|
|
5997
|
+
</div>
|
|
5998
|
+
</main>
|
|
5670
5999
|
|
|
5671
|
-
|
|
6000
|
+
<div id="toast-container" style="position: fixed; top: 24px; right: 24px; z-index: 1001; display: flex; flex-direction: column; gap: 12px;"></div>
|
|
5672
6001
|
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
6002
|
+
<script>
|
|
6003
|
+
let currentView = "overview";
|
|
6004
|
+
let allMessages = [];
|
|
6005
|
+
let allEpisodes = [];
|
|
6006
|
+
let allFiles = [];
|
|
6007
|
+
let allFileJobs = [];
|
|
6008
|
+
let allCronJobs = [];
|
|
6009
|
+
let allQaLogs = [];
|
|
6010
|
+
let statusData = null;
|
|
5676
6011
|
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
6012
|
+
function fmt(value) { return value == null || value === "" ? "-" : String(value); }
|
|
6013
|
+
function escapeHtml(value) {
|
|
6014
|
+
return fmt(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
6015
|
+
}
|
|
6016
|
+
function isOpaqueId(value) { return /^(ou|oc|om|cli|on|un|uid)_?[a-z0-9]+/i.test(fmt(value)); }
|
|
6017
|
+
function formatDateTime(value) {
|
|
6018
|
+
var date = new Date(value);
|
|
6019
|
+
if (Number.isNaN(date.getTime())) return fmt(value);
|
|
6020
|
+
var pad = function(n) { return String(n).padStart(2, "0"); };
|
|
6021
|
+
return date.getFullYear() + "/" + pad(date.getMonth()+1) + "/" + pad(date.getDate()) + " " + pad(date.getHours()) + ":" + pad(date.getMinutes());
|
|
6022
|
+
}
|
|
6023
|
+
function displaySender(value) { return isOpaqueId(value) ? "\u7FA4\u6210\u5458" : fmt(value); }
|
|
6024
|
+
function displayChatName(value, platform) { return !isOpaqueId(value) ? fmt(value) : (platform === "feishu" ? "\u98DE\u4E66\u7FA4\u804A" : "\u7FA4\u804A"); }
|
|
5684
6025
|
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
6026
|
+
function showToast(message, type) {
|
|
6027
|
+
type = type || "info";
|
|
6028
|
+
var container = document.getElementById("toast-container");
|
|
6029
|
+
var toast = document.createElement("div");
|
|
6030
|
+
toast.className = "toast toast-" + type;
|
|
6031
|
+
toast.textContent = message;
|
|
6032
|
+
container.appendChild(toast);
|
|
6033
|
+
setTimeout(function() {
|
|
6034
|
+
toast.style.opacity = "0"; toast.style.transform = "translateX(10px)";
|
|
6035
|
+
setTimeout(function() { toast.remove(); }, 300);
|
|
6036
|
+
}, 3000);
|
|
6037
|
+
}
|
|
5688
6038
|
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
}
|
|
6039
|
+
function navigateTo(view) {
|
|
6040
|
+
document.querySelectorAll(".view").forEach(function(el) { el.classList.remove("active"); });
|
|
6041
|
+
document.querySelectorAll(".nav-item, .mobile-nav-item").forEach(function(el) { el.classList.remove("active"); });
|
|
6042
|
+
document.getElementById("view-" + view).classList.add("active");
|
|
6043
|
+
document.querySelectorAll('[data-view="' + view + '"]').forEach(function(el) { el.classList.add("active"); });
|
|
6044
|
+
currentView = view;
|
|
6045
|
+
window.scrollTo(0, 0);
|
|
6046
|
+
if (view === "messages") renderMessagesView();
|
|
6047
|
+
if (view === "episodes") renderEpisodesView();
|
|
6048
|
+
if (view === "files") renderFilesView();
|
|
6049
|
+
if (view === "tasks") renderTasksView();
|
|
6050
|
+
if (view === "qa-logs") renderQaLogsView();
|
|
6051
|
+
}
|
|
5703
6052
|
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
6053
|
+
document.querySelectorAll(".nav-item, .mobile-nav-item").forEach(function(el) {
|
|
6054
|
+
el.addEventListener("click", function() { navigateTo(el.dataset.view); });
|
|
6055
|
+
});
|
|
5707
6056
|
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
6057
|
+
function switchTab(tab) {
|
|
6058
|
+
document.querySelectorAll(".tab").forEach(function(el) { el.classList.remove("active"); });
|
|
6059
|
+
document.querySelector('[data-tab="' + tab + '"]').classList.add("active");
|
|
6060
|
+
document.getElementById("tab-file-jobs").style.display = tab === "file-jobs" ? "block" : "none";
|
|
6061
|
+
document.getElementById("tab-cron-jobs").style.display = tab === "cron-jobs" ? "block" : "none";
|
|
6062
|
+
if (tab === "file-jobs") renderFileJobs();
|
|
6063
|
+
if (tab === "cron-jobs") renderCronJobs();
|
|
6064
|
+
}
|
|
5712
6065
|
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
6066
|
+
async function fetchJson(path) {
|
|
6067
|
+
var response = await fetch(path);
|
|
6068
|
+
if (!response.ok) {
|
|
6069
|
+
var body = await response.text();
|
|
6070
|
+
throw new Error(path + " " + response.status + " " + body);
|
|
5717
6071
|
}
|
|
6072
|
+
return response.json();
|
|
6073
|
+
}
|
|
5718
6074
|
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
6075
|
+
async function postJson(path, options) {
|
|
6076
|
+
var response = await fetch(path, Object.assign({ method: "POST" }, options || {}));
|
|
6077
|
+
var result = await response.json();
|
|
6078
|
+
if (!response.ok) {
|
|
6079
|
+
throw new Error(result.message || result.reason || "\u8BF7\u6C42\u5931\u8D25");
|
|
5722
6080
|
}
|
|
6081
|
+
return result;
|
|
6082
|
+
}
|
|
5723
6083
|
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
["\u7FA4\u804A", status.data.chats, "\u672C\u5730\u7FA4\u804A\u6570", ""],
|
|
5730
|
-
["\u6D88\u606F", status.data.messages, "\u5DF2\u5165\u5E93\u6D88\u606F", ""],
|
|
5731
|
-
["\u4F1A\u8BDD\u8BB0\u5FC6", status.data.episodes, "\u5DF2\u751F\u6210\u6458\u8981", ""],
|
|
5732
|
-
["\u6587\u4EF6", status.data.files, "\u6587\u4EF6\u77E5\u8BC6\u6E90", ""],
|
|
5733
|
-
].map(([label, value, note, extra]) => \`
|
|
5734
|
-
<div class="metric">
|
|
5735
|
-
<div class="label">\${escapeHtml(label)}</div>
|
|
5736
|
-
<div class="value \${extra}">\${escapeHtml(value)}</div>
|
|
5737
|
-
<div class="note">\${escapeHtml(note)}</div>
|
|
5738
|
-
</div>
|
|
5739
|
-
\`).join("");
|
|
6084
|
+
async function deleteJson(path) {
|
|
6085
|
+
var response = await fetch(path, { method: "DELETE" });
|
|
6086
|
+
var result = await response.json();
|
|
6087
|
+
if (!response.ok) {
|
|
6088
|
+
throw new Error(result.message || result.reason || "\u8BF7\u6C42\u5931\u8D25");
|
|
5740
6089
|
}
|
|
6090
|
+
return result;
|
|
6091
|
+
}
|
|
5741
6092
|
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
messages
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
6093
|
+
function renderMetrics(status) {
|
|
6094
|
+
var gatewayClass = status.gateway.configured ? "status-dot online" : "status-dot offline";
|
|
6095
|
+
var gatewayText = status.gateway.connection === "running" ? "\u8FD0\u884C\u4E2D" : (!status.gateway.configured ? "\u672A\u914D\u7F6E" : "\u5F85\u542F\u52A8");
|
|
6096
|
+
var metricsHtml = [
|
|
6097
|
+
["Gateway", gatewayText, "\u98DE\u4E66\u957F\u8FDE\u63A5", gatewayClass],
|
|
6098
|
+
["\u7248\u672C", status.version || "unknown", "\u5F53\u524D\u8FD0\u884C\u7248\u672C", ""],
|
|
6099
|
+
["\u7FA4\u804A", status.data.chats, "\u672C\u5730\u7FA4\u804A\u6570", ""],
|
|
6100
|
+
["\u6D88\u606F", status.data.messages, "\u5DF2\u5165\u5E93\u6D88\u606F", ""],
|
|
6101
|
+
["\u4F1A\u8BDD\u8BB0\u5FC6", status.data.episodes, "\u5DF2\u751F\u6210\u6458\u8981", ""],
|
|
6102
|
+
["\u6587\u4EF6", status.data.files, "\u6587\u4EF6\u77E5\u8BC6\u6E90", ""],
|
|
6103
|
+
["\u95EE\u7B54", status.data.qaLogs, "\u95EE\u7B54\u8BB0\u5F55", ""],
|
|
6104
|
+
["\u4EFB\u52A1", status.data.cronJobs, "\u5B9A\u65F6\u4EFB\u52A1", ""]
|
|
6105
|
+
].map(function(item) {
|
|
6106
|
+
var label = item[0], value = item[1], note = item[2], dotClass = item[3];
|
|
6107
|
+
return '<div class="metric-card glass"><div class="metric-label">' + escapeHtml(label) + '</div>' +
|
|
6108
|
+
'<div class="metric-value">' + (dotClass ? '<span class="' + dotClass + '" style="margin-right:8px;"></span>' : '') + escapeHtml(value) + '</div>' +
|
|
6109
|
+
'<div class="metric-note">' + escapeHtml(note) + '</div></div>';
|
|
6110
|
+
}).join("");
|
|
6111
|
+
document.getElementById("metrics").innerHTML = metricsHtml;
|
|
6112
|
+
document.getElementById("gateway-indicator").className = gatewayClass;
|
|
6113
|
+
document.getElementById("gateway-status-text").textContent = "Gateway " + gatewayText;
|
|
6114
|
+
document.getElementById("version-text").textContent = "v" + (status.version || "unknown");
|
|
6115
|
+
}
|
|
5764
6116
|
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
<div class="message-meta">
|
|
5777
|
-
<span>\${escapeHtml(formatDateTime(item.startedAt))} - \${escapeHtml(formatDateTime(item.endedAt))}</span>
|
|
5778
|
-
<span>\${escapeHtml(displayChatName(item.chatName, "feishu"))}</span>
|
|
5779
|
-
<span>\${escapeHtml(item.messageCount)} \u6761\u6D88\u606F</span>
|
|
5780
|
-
</div>
|
|
5781
|
-
<div class="message-body">\${escapeHtml(item.summary)}</div>
|
|
5782
|
-
</article>
|
|
5783
|
-
\`).join("")}
|
|
5784
|
-
</div>
|
|
5785
|
-
\`;
|
|
5786
|
-
}
|
|
6117
|
+
function renderSystemStatus(status) {
|
|
6118
|
+
var gateway = status.gateway;
|
|
6119
|
+
var html = '<div style="display:flex;flex-direction:column;gap:var(--space-md);">';
|
|
6120
|
+
html += '<div class="settings-item"><div><div class="settings-label">Gateway</div></div><div class="settings-value">' + (gateway.connection === "running" ? '<span class="tag tag-success">\u8FD0\u884C\u4E2D</span>' : '<span class="tag tag-warning">\u672A\u8FD0\u884C</span>') + '</div></div>';
|
|
6121
|
+
html += '<div class="settings-item"><div><div class="settings-label">Web UI</div></div><div class="settings-value">' + escapeHtml((status.web && status.web.host ? status.web.host : "127.0.0.1") + ":" + (status.web && status.web.port ? status.web.port : "3878")) + '</div></div>';
|
|
6122
|
+
html += '<div class="settings-item"><div><div class="settings-label">RAG \u6A21\u5F0F</div></div><div class="settings-value"><span class="tag tag-success">\u5F3A\u5236\u68C0\u7D22</span></div></div>';
|
|
6123
|
+
html += '<div class="settings-item"><div><div class="settings-label">\u5173\u952E\u8BCD\u68C0\u7D22</div></div><div class="settings-value">SQLite FTS5</div></div>';
|
|
6124
|
+
html += '<div class="settings-item"><div><div class="settings-label">\u5411\u91CF\u68C0\u7D22</div></div><div class="settings-value">SQLite embedding</div></div>';
|
|
6125
|
+
html += '</div>';
|
|
6126
|
+
document.getElementById("system-status").innerHTML = html;
|
|
6127
|
+
}
|
|
5787
6128
|
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
}
|
|
5794
|
-
chats.className = "";
|
|
5795
|
-
chats.innerHTML = \`
|
|
5796
|
-
<table>
|
|
5797
|
-
<thead><tr><th>\u540D\u79F0</th><th>\u5E73\u53F0</th></tr></thead>
|
|
5798
|
-
<tbody>
|
|
5799
|
-
\${items.map((item) => \`
|
|
5800
|
-
<tr>
|
|
5801
|
-
<td><span class="id-text" title="\${escapeHtml(item.name)}">\${escapeHtml(displayChatName(item.name, item.platform))}</span></td>
|
|
5802
|
-
<td>\${escapeHtml(item.platform)}</td>
|
|
5803
|
-
</tr>
|
|
5804
|
-
\`).join("")}
|
|
5805
|
-
</tbody>
|
|
5806
|
-
</table>
|
|
5807
|
-
\`;
|
|
6129
|
+
function renderRecentMessages(items) {
|
|
6130
|
+
var el = document.getElementById("recent-messages");
|
|
6131
|
+
if (!items || items.length === 0) {
|
|
6132
|
+
el.innerHTML = '<div class="empty-state">\u8FD8\u6CA1\u6709\u6D88\u606F\u3002\u542F\u52A8 Gateway \u540E\uFF0C\u7FA4\u804A\u6587\u672C\u4F1A\u8FDB\u5165\u672C\u5730 RAG \u7D22\u5F15\u3002</div>';
|
|
6133
|
+
return;
|
|
5808
6134
|
}
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
files.innerHTML = \`
|
|
5818
|
-
<table>
|
|
5819
|
-
<thead><tr><th>\u6587\u4EF6</th><th>\u89E3\u6790\u5668</th><th>\u5B57\u7B26</th></tr></thead>
|
|
5820
|
-
<tbody>
|
|
5821
|
-
\${items.map((item) => \`
|
|
5822
|
-
<tr>
|
|
5823
|
-
<td>
|
|
5824
|
-
<div>\${escapeHtml(item.fileName)}</div>
|
|
5825
|
-
<div class="path" title="\${escapeHtml(item.storedPath)}">\${escapeHtml(item.storedPath)}</div>
|
|
5826
|
-
</td>
|
|
5827
|
-
<td>\${escapeHtml(item.parser || "unknown")}</td>
|
|
5828
|
-
<td>\${escapeHtml(item.characters)}</td>
|
|
5829
|
-
</tr>
|
|
5830
|
-
\`).join("")}
|
|
5831
|
-
</tbody>
|
|
5832
|
-
</table>
|
|
5833
|
-
\`;
|
|
6135
|
+
var html = '<div class="message-list">';
|
|
6136
|
+
for (var i = 0; i < Math.min(items.length, 5); i++) {
|
|
6137
|
+
var item = items[i];
|
|
6138
|
+
html += '<div class="message-card"><div class="message-meta">' +
|
|
6139
|
+
'<span>' + escapeHtml(formatDateTime(item.sentAt)) + '</span>' +
|
|
6140
|
+
'<span>' + escapeHtml(displaySender(item.senderName)) + '</span>' +
|
|
6141
|
+
'<span>' + escapeHtml(displayChatName(item.chatName, item.platform)) + '</span>' +
|
|
6142
|
+
'</div><div class="message-text">' + escapeHtml(item.text) + '</div></div>';
|
|
5834
6143
|
}
|
|
6144
|
+
html += '</div>';
|
|
6145
|
+
el.innerHTML = html;
|
|
6146
|
+
}
|
|
5835
6147
|
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
}
|
|
5842
|
-
fileJobs.className = "";
|
|
5843
|
-
fileJobs.innerHTML = \`
|
|
5844
|
-
<table>
|
|
5845
|
-
<thead><tr><th>\u4EFB\u52A1</th><th>\u72B6\u6001</th></tr></thead>
|
|
5846
|
-
<tbody>
|
|
5847
|
-
\${items.map((item) => \`
|
|
5848
|
-
<tr>
|
|
5849
|
-
<td>
|
|
5850
|
-
<div>\${escapeHtml(item.fileName)}</div>
|
|
5851
|
-
<div class="path" title="\${escapeHtml(item.id)}">ID: \${escapeHtml(item.id)}</div>
|
|
5852
|
-
<div class="path" title="\${escapeHtml(item.error || item.storedPath)}">\${escapeHtml(item.error || item.storedPath)}</div>
|
|
5853
|
-
</td>
|
|
5854
|
-
<td>\${escapeHtml(item.status)}</td>
|
|
5855
|
-
</tr>
|
|
5856
|
-
\`).join("")}
|
|
5857
|
-
</tbody>
|
|
5858
|
-
</table>
|
|
5859
|
-
\`;
|
|
6148
|
+
function renderRecentEpisodes(items) {
|
|
6149
|
+
var el = document.getElementById("recent-episodes");
|
|
6150
|
+
if (!items || items.length === 0) {
|
|
6151
|
+
el.innerHTML = '<div class="empty-state">\u8FD8\u6CA1\u6709\u4F1A\u8BDD\u8BB0\u5FC6\u3002</div>';
|
|
6152
|
+
return;
|
|
5860
6153
|
}
|
|
6154
|
+
var html = '<div class="message-list">';
|
|
6155
|
+
for (var i = 0; i < Math.min(items.length, 3); i++) {
|
|
6156
|
+
var item = items[i];
|
|
6157
|
+
html += '<div class="episode-card"><div class="message-meta">' +
|
|
6158
|
+
'<span>' + escapeHtml(formatDateTime(item.startedAt)) + " - " + escapeHtml(formatDateTime(item.endedAt)) + '</span>' +
|
|
6159
|
+
'<span>' + escapeHtml(item.messageCount) + ' \u6761\u6D88\u606F</span>' +
|
|
6160
|
+
'</div><div class="message-text">' + escapeHtml(item.summary) + '</div></div>';
|
|
6161
|
+
}
|
|
6162
|
+
html += '</div>';
|
|
6163
|
+
el.innerHTML = html;
|
|
6164
|
+
}
|
|
5861
6165
|
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
</tbody>
|
|
5888
|
-
</table>
|
|
5889
|
-
\`;
|
|
6166
|
+
function renderMessagesView() {
|
|
6167
|
+
var el = document.getElementById("messages-list");
|
|
6168
|
+
if (!allMessages || allMessages.length === 0) {
|
|
6169
|
+
el.innerHTML = '<div class="empty-state">\u8FD8\u6CA1\u6709\u6D88\u606F\u3002</div>';
|
|
6170
|
+
return;
|
|
6171
|
+
}
|
|
6172
|
+
var searchInput = document.getElementById("message-search");
|
|
6173
|
+
var searchTerm = searchInput ? searchInput.value.toLowerCase() : "";
|
|
6174
|
+
var filtered = searchTerm ? allMessages.filter(function(m) { return (m.text || "").toLowerCase().indexOf(searchTerm) !== -1; }) : allMessages;
|
|
6175
|
+
if (filtered.length === 0) {
|
|
6176
|
+
el.innerHTML = '<div class="empty-state">\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6D88\u606F\u3002</div>';
|
|
6177
|
+
return;
|
|
6178
|
+
}
|
|
6179
|
+
var html = '<div class="message-list">';
|
|
6180
|
+
for (var i = 0; i < Math.min(filtered.length, 50); i++) {
|
|
6181
|
+
var item = filtered[i];
|
|
6182
|
+
html += '<div class="message-card"><div class="message-meta">' +
|
|
6183
|
+
'<span>' + escapeHtml(formatDateTime(item.sentAt)) + '</span>' +
|
|
6184
|
+
'<span>' + escapeHtml(displaySender(item.senderName)) + '</span>' +
|
|
6185
|
+
'<span>' + escapeHtml(displayChatName(item.chatName, item.platform)) + '</span>' +
|
|
6186
|
+
'</div><div class="message-text" style="-webkit-line-clamp:4;">' + escapeHtml(item.text) + '</div></div>';
|
|
6187
|
+
}
|
|
6188
|
+
html += '</div>';
|
|
6189
|
+
if (filtered.length > 50) {
|
|
6190
|
+
html += '<div style="text-align:center;padding:var(--space-md);color:var(--text-muted);font-size:13px;">\u8FD8\u6709 ' + (filtered.length - 50) + ' \u6761\u6D88\u606F...</div>';
|
|
5890
6191
|
}
|
|
6192
|
+
el.innerHTML = html;
|
|
6193
|
+
}
|
|
5891
6194
|
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
" <span>" + escapeHtml(formatDateTime(item.createdAt)) + "</span>",
|
|
5905
|
-
" <span>" + escapeHtml(item.status) + "</span>",
|
|
5906
|
-
" <span>" + escapeHtml(citationCount) + " \u6761\u5F15\u7528</span>",
|
|
5907
|
-
" </div>",
|
|
5908
|
-
" <div class=\\"message-body\\"><strong>\u95EE\uFF1A</strong>" + escapeHtml(item.question) + "</div>",
|
|
5909
|
-
" <div class=\\"message-body\\"><strong>\u7B54\uFF1A</strong>" + escapeHtml(item.answer) + "</div>",
|
|
5910
|
-
"</article>",
|
|
5911
|
-
].join("\\n");
|
|
5912
|
-
});
|
|
5913
|
-
qaLogs.innerHTML = [
|
|
5914
|
-
'<div class="message-list">',
|
|
5915
|
-
rows.join(""),
|
|
5916
|
-
"</div>",
|
|
5917
|
-
].join("\\n");
|
|
6195
|
+
function filterMessages() { renderMessagesView(); }
|
|
6196
|
+
|
|
6197
|
+
function renderEpisodesView() {
|
|
6198
|
+
var el = document.getElementById("episodes-list");
|
|
6199
|
+
if (!allEpisodes || allEpisodes.length === 0) {
|
|
6200
|
+
el.innerHTML = '<div class="empty-state">\u8FD8\u6CA1\u6709\u4F1A\u8BDD\u8BB0\u5FC6\u3002</div>';
|
|
6201
|
+
return;
|
|
6202
|
+
}
|
|
6203
|
+
var html = '<div class="timeline">';
|
|
6204
|
+
for (var i = 0; i < allEpisodes.length; i++) {
|
|
6205
|
+
var item = allEpisodes[i];
|
|
6206
|
+
html += '<div class="timeline-item"><div class="timeline-date">' + escapeHtml(formatDateTime(item.startedAt)) + " - " + escapeHtml(formatDateTime(item.endedAt)) + " \xB7 " + escapeHtml(item.messageCount) + ' \u6761\u6D88\u606F</div><div class="timeline-content">' + escapeHtml(item.summary) + '</div></div>';
|
|
5918
6207
|
}
|
|
6208
|
+
html += '</div>';
|
|
6209
|
+
el.innerHTML = html;
|
|
6210
|
+
}
|
|
5919
6211
|
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
6212
|
+
function renderFilesView() {
|
|
6213
|
+
var el = document.getElementById("files-list");
|
|
6214
|
+
if (!allFiles || allFiles.length === 0) {
|
|
6215
|
+
el.innerHTML = '<div class="content-panel glass"><div class="empty-state">\u8FD8\u6CA1\u6709\u6587\u4EF6\u3002\u8FD0\u884C <code>chattercatcher files add <path...></code> \u5BFC\u5165\u6587\u4EF6\u3002</div></div>';
|
|
6216
|
+
return;
|
|
6217
|
+
}
|
|
6218
|
+
var html = '<div class="grid-2">';
|
|
6219
|
+
for (var i = 0; i < allFiles.length; i++) {
|
|
6220
|
+
var item = allFiles[i];
|
|
6221
|
+
html += '<div class="file-card glass"><div class="file-icon">' +
|
|
6222
|
+
'<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"/><polyline points="14 2 14 8 20 8"/></svg>' +
|
|
6223
|
+
'</div><div style="font-weight:600;margin-bottom:4px;">' + escapeHtml(item.fileName) + '</div>' +
|
|
6224
|
+
'<div style="font-size:13px;color:var(--text-muted);margin-bottom:4px;" class="truncate">' + escapeHtml(item.storedPath) + '</div>' +
|
|
6225
|
+
'<div style="display:flex;gap:var(--space-sm);"><span class="tag">' + escapeHtml(item.parser || "unknown") + '</span><span class="tag">' + escapeHtml(item.characters) + ' \u5B57\u7B26</span></div></div>';
|
|
5927
6226
|
}
|
|
6227
|
+
html += '</div>';
|
|
6228
|
+
el.innerHTML = html;
|
|
6229
|
+
}
|
|
5928
6230
|
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
6231
|
+
function renderTasksView() {
|
|
6232
|
+
var activeTab = document.querySelector(".tab.active");
|
|
6233
|
+
var tab = activeTab ? activeTab.dataset.tab : "file-jobs";
|
|
6234
|
+
if (tab === "file-jobs") renderFileJobs();
|
|
6235
|
+
else renderCronJobs();
|
|
6236
|
+
}
|
|
6237
|
+
|
|
6238
|
+
function renderFileJobs() {
|
|
6239
|
+
var el = document.getElementById("file-jobs-list");
|
|
6240
|
+
if (!allFileJobs || allFileJobs.length === 0) {
|
|
6241
|
+
el.innerHTML = '<div class="empty-state">\u8FD8\u6CA1\u6709\u6587\u4EF6\u89E3\u6790\u4EFB\u52A1\u3002</div>';
|
|
6242
|
+
return;
|
|
5932
6243
|
}
|
|
6244
|
+
var html = '<table class="data-table"><thead><tr><th>\u6587\u4EF6</th><th>\u72B6\u6001</th><th>\u4FE1\u606F</th></tr></thead><tbody>';
|
|
6245
|
+
for (var i = 0; i < allFileJobs.length; i++) {
|
|
6246
|
+
var item = allFileJobs[i];
|
|
6247
|
+
var tagClass = item.status === 'indexed' ? 'tag-success' : item.status === 'failed' ? 'tag-error' : 'tag-warning';
|
|
6248
|
+
html += '<tr><td><div style="font-weight:500;">' + escapeHtml(item.fileName) + '</div><div style="font-size:12px;color:var(--text-muted);" class="truncate">' + escapeHtml(item.storedPath || item.id) + '</div></td>' +
|
|
6249
|
+
'<td><span class="tag ' + tagClass + '">' + escapeHtml(item.status) + '</span></td>' +
|
|
6250
|
+
'<td style="font-size:13px;color:var(--text-muted);">' + escapeHtml(item.error || "") + '</td></tr>';
|
|
6251
|
+
}
|
|
6252
|
+
html += '</tbody></table>';
|
|
6253
|
+
el.innerHTML = html;
|
|
6254
|
+
}
|
|
5933
6255
|
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
}
|
|
6256
|
+
function renderCronJobs() {
|
|
6257
|
+
var el = document.getElementById("cron-jobs-list");
|
|
6258
|
+
if (!allCronJobs || allCronJobs.length === 0) {
|
|
6259
|
+
el.innerHTML = '<div class="empty-state">\u8FD8\u6CA1\u6709\u5B9A\u65F6\u4EFB\u52A1\u3002</div>';
|
|
6260
|
+
return;
|
|
5940
6261
|
}
|
|
6262
|
+
var html = '<table class="data-table"><thead><tr><th>\u4EFB\u52A1</th><th>\u72B6\u6001</th><th>\u64CD\u4F5C</th></tr></thead><tbody>';
|
|
6263
|
+
for (var i = 0; i < allCronJobs.length; i++) {
|
|
6264
|
+
var item = allCronJobs[i];
|
|
6265
|
+
var tagClass = item.status === 'active' ? 'tag-success' : 'tag-warning';
|
|
6266
|
+
html += '<tr><td><div style="font-weight:500;">' + escapeHtml(item.schedule) + '</div>' +
|
|
6267
|
+
'<div style="font-size:13px;color:var(--text-muted);" class="truncate-2">' + escapeHtml(item.prompt) + '</div>' +
|
|
6268
|
+
'<div style="font-size:12px;color:var(--text-muted);">\u4E0B\u6B21: ' + escapeHtml(formatDateTime(item.nextRunAt)) + '</div>' +
|
|
6269
|
+
(item.lastError ? '<div style="font-size:12px;color:var(--danger);margin-top:4px;">' + escapeHtml(item.lastError) + '</div>' : '') +
|
|
6270
|
+
'</td><td><span class="tag ' + tagClass + '">' + escapeHtml(item.status) + '</span></td><td>' +
|
|
6271
|
+
(item.status === "active" ? '<button class="btn btn-sm btn-danger" data-delete-cron-job="' + escapeHtml(item.id) + '">\u5220\u9664</button>' : '-') +
|
|
6272
|
+
'</td></tr>';
|
|
6273
|
+
}
|
|
6274
|
+
html += '</tbody></table>';
|
|
6275
|
+
el.innerHTML = html;
|
|
6276
|
+
}
|
|
5941
6277
|
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
6278
|
+
function renderQaLogsView() {
|
|
6279
|
+
var el = document.getElementById("qa-logs-list");
|
|
6280
|
+
if (!allQaLogs || allQaLogs.length === 0) {
|
|
6281
|
+
el.innerHTML = '<div class="empty-state">\u8FD8\u6CA1\u6709\u95EE\u7B54\u65E5\u5FD7\u3002</div>';
|
|
6282
|
+
return;
|
|
6283
|
+
}
|
|
6284
|
+
var html = '';
|
|
6285
|
+
for (var i = 0; i < allQaLogs.length; i++) {
|
|
6286
|
+
var item = allQaLogs[i];
|
|
6287
|
+
var citationCount = Array.isArray(item.citations) ? item.citations.length : 0;
|
|
6288
|
+
var statusClass = item.status === 'success' ? 'tag-success' : 'tag-warning';
|
|
6289
|
+
html += '<div class="qa-card"><div class="message-meta" style="margin-bottom:var(--space-sm);">' +
|
|
6290
|
+
'<span>' + escapeHtml(formatDateTime(item.createdAt)) + '</span>' +
|
|
6291
|
+
'<span class="tag ' + statusClass + '">' + escapeHtml(item.status) + '</span>' +
|
|
6292
|
+
'<span>' + citationCount + ' \u6761\u5F15\u7528</span></div>' +
|
|
6293
|
+
'<div class="qa-question">' + escapeHtml(item.question) + '</div>' +
|
|
6294
|
+
'<div class="qa-answer">' + escapeHtml(item.answer) + '</div></div>';
|
|
5953
6295
|
}
|
|
6296
|
+
el.innerHTML = html;
|
|
6297
|
+
}
|
|
5954
6298
|
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
return;
|
|
5967
|
-
}
|
|
6299
|
+
function renderSettings(status) {
|
|
6300
|
+
var el = document.getElementById("settings-config");
|
|
6301
|
+
var html = '<h3 style="font-size:16px;font-weight:600;margin-bottom:var(--space-md);">\u7CFB\u7EDF\u914D\u7F6E</h3>';
|
|
6302
|
+
html += '<div style="display:flex;flex-direction:column;">';
|
|
6303
|
+
html += '<div class="settings-item"><div><div class="settings-label">Web UI</div><div class="settings-desc">' + escapeHtml((status.web && status.web.host ? status.web.host : "127.0.0.1") + ":" + (status.web && status.web.port ? status.web.port : "3878")) + '</div></div></div>';
|
|
6304
|
+
html += '<div class="settings-item"><div><div class="settings-label">Gateway</div><div class="settings-desc">' + (status.gateway.configured ? "\u5DF2\u914D\u7F6E" : "\u672A\u914D\u7F6E") + '</div></div></div>';
|
|
6305
|
+
html += '<div class="settings-item"><div><div class="settings-label">RAG \u6A21\u5F0F</div><div class="settings-desc">\u5F3A\u5236\u5148\u68C0\u7D22\u8BC1\u636E\uFF0C\u7981\u6B62\u5168\u91CF\u4E0A\u4E0B\u6587\u5806\u53E0</div></div></div>';
|
|
6306
|
+
html += '<div class="settings-item"><div><div class="settings-label">\u6570\u636E\u76EE\u5F55</div><div class="settings-desc">SQLite + \u672C\u5730\u6587\u4EF6</div></div></div>';
|
|
6307
|
+
html += '</div>';
|
|
6308
|
+
el.innerHTML = html;
|
|
6309
|
+
}
|
|
5968
6310
|
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
}
|
|
5974
|
-
await load();
|
|
5975
|
-
} catch (error) {
|
|
5976
|
-
actionStatus.textContent = error instanceof Error ? error.message : String(error);
|
|
5977
|
-
} finally {
|
|
5978
|
-
processMessages.disabled = false;
|
|
5979
|
-
}
|
|
5980
|
-
}
|
|
6311
|
+
async function loadSection(path, setter) {
|
|
6312
|
+
try { setter(await fetchJson(path)); }
|
|
6313
|
+
catch (error) { console.error("\u52A0\u8F7D\u5931\u8D25:", path, error); }
|
|
6314
|
+
}
|
|
5981
6315
|
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
actionStatus.textContent = "\u6B63\u5728\u5220\u9664\u5B9A\u65F6\u4EFB\u52A1...";
|
|
5989
|
-
try {
|
|
5990
|
-
const response = await fetch(\`/api/cron-jobs/\${encodeURIComponent(id)}\`, {
|
|
5991
|
-
method: "DELETE",
|
|
5992
|
-
headers: { "x-chattercatcher-web-token": webActionToken },
|
|
5993
|
-
});
|
|
5994
|
-
const result = await response.json();
|
|
5995
|
-
actionStatus.textContent = result.ok ? "\u5B9A\u65F6\u4EFB\u52A1\u5DF2\u5220\u9664\u3002" : result.message || "\u5220\u9664\u5931\u8D25\u3002";
|
|
5996
|
-
await load();
|
|
5997
|
-
} catch (error) {
|
|
5998
|
-
actionStatus.textContent = error instanceof Error ? error.message : String(error);
|
|
5999
|
-
} finally {
|
|
6000
|
-
target.removeAttribute("disabled");
|
|
6001
|
-
}
|
|
6316
|
+
async function load() {
|
|
6317
|
+
await loadSection("/api/status", function(data) {
|
|
6318
|
+
statusData = data;
|
|
6319
|
+
renderMetrics(data);
|
|
6320
|
+
renderSystemStatus(data);
|
|
6321
|
+
renderSettings(data);
|
|
6002
6322
|
});
|
|
6323
|
+
await loadSection("/api/messages/recent?limit=50", function(data) { allMessages = data.items || []; renderRecentMessages(allMessages); });
|
|
6324
|
+
await loadSection("/api/episodes?limit=20", function(data) { allEpisodes = data.items || []; renderRecentEpisodes(allEpisodes); });
|
|
6325
|
+
await loadSection("/api/files", function(data) { allFiles = data.items || []; });
|
|
6326
|
+
await loadSection("/api/file-jobs", function(data) { allFileJobs = data.items || []; });
|
|
6327
|
+
await loadSection("/api/qa-logs?limit=20", function(data) { allQaLogs = data.items || []; });
|
|
6328
|
+
await loadSection("/api/cron-jobs", function(data) { allCronJobs = data.items || []; });
|
|
6329
|
+
if (currentView === "messages") renderMessagesView();
|
|
6330
|
+
if (currentView === "episodes") renderEpisodesView();
|
|
6331
|
+
if (currentView === "files") renderFilesView();
|
|
6332
|
+
if (currentView === "tasks") renderTasksView();
|
|
6333
|
+
if (currentView === "qa-logs") renderQaLogsView();
|
|
6334
|
+
}
|
|
6003
6335
|
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6336
|
+
async function processNow() {
|
|
6337
|
+
var btn = document.getElementById("btn-process-messages");
|
|
6338
|
+
if (btn) { btn.disabled = true; }
|
|
6339
|
+
showToast("\u6B63\u5728\u5904\u7406\u6D88\u606F\u7D22\u5F15...", "info");
|
|
6340
|
+
try {
|
|
6341
|
+
var result = await postJson("/api/process/messages");
|
|
6342
|
+
if (result.status === "skipped") { showToast(result.reason, "warning"); }
|
|
6343
|
+
else { showToast("\u5904\u7406\u5B8C\u6210\uFF1Achunks=" + result.chunks + ", vectors=" + result.vectors, "success"); }
|
|
6344
|
+
await load();
|
|
6345
|
+
} catch (error) {
|
|
6346
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
6347
|
+
} finally {
|
|
6348
|
+
if (btn) { btn.disabled = false; }
|
|
6349
|
+
}
|
|
6350
|
+
}
|
|
6351
|
+
|
|
6352
|
+
document.addEventListener("click", async function(event) {
|
|
6353
|
+
var target = event.target;
|
|
6354
|
+
if (!(target instanceof HTMLElement)) return;
|
|
6355
|
+
var id = target.dataset.deleteCronJob;
|
|
6356
|
+
if (!id) return;
|
|
6357
|
+
target.disabled = true;
|
|
6358
|
+
showToast("\u6B63\u5728\u5220\u9664\u5B9A\u65F6\u4EFB\u52A1...", "info");
|
|
6359
|
+
try {
|
|
6360
|
+
var result = await deleteJson("/api/cron-jobs/" + encodeURIComponent(id));
|
|
6361
|
+
showToast(result.ok ? "\u5B9A\u65F6\u4EFB\u52A1\u5DF2\u5220\u9664" : (result.message || "\u5220\u9664\u5931\u8D25"), result.ok ? "success" : "error");
|
|
6362
|
+
await load();
|
|
6363
|
+
} catch (error) {
|
|
6364
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
6365
|
+
}
|
|
6366
|
+
});
|
|
6367
|
+
|
|
6368
|
+
void load();
|
|
6369
|
+
setInterval(function() { if (document.visibilityState === "visible") void load(); }, 5000);
|
|
6370
|
+
</script>
|
|
6371
|
+
</body>
|
|
6013
6372
|
</html>`;
|
|
6014
6373
|
}
|
|
6015
6374
|
function parseLimit(value, fallback, max) {
|
|
@@ -6019,12 +6378,22 @@ function parseLimit(value, fallback, max) {
|
|
|
6019
6378
|
function getWebActionToken(secrets) {
|
|
6020
6379
|
return secrets.web.actionToken;
|
|
6021
6380
|
}
|
|
6022
|
-
function
|
|
6023
|
-
return
|
|
6381
|
+
function getWebActionCookie(token) {
|
|
6382
|
+
return `chattercatcher_web_token=${encodeURIComponent(token)}; Path=/; HttpOnly; SameSite=Strict`;
|
|
6383
|
+
}
|
|
6384
|
+
function parseCookies(header) {
|
|
6385
|
+
const value = Array.isArray(header) ? header.join("; ") : header;
|
|
6386
|
+
if (!value) return {};
|
|
6387
|
+
const cookies = {};
|
|
6388
|
+
for (const part of value.split(";")) {
|
|
6389
|
+
const [rawName, ...rawValue] = part.trim().split("=");
|
|
6390
|
+
if (!rawName || rawValue.length === 0) continue;
|
|
6391
|
+
cookies[rawName] = decodeURIComponent(rawValue.join("="));
|
|
6392
|
+
}
|
|
6393
|
+
return cookies;
|
|
6024
6394
|
}
|
|
6025
6395
|
function isAuthorizedWebAction(request, token) {
|
|
6026
|
-
|
|
6027
|
-
return provided === token;
|
|
6396
|
+
return parseCookies(request.headers.cookie).chattercatcher_web_token === token;
|
|
6028
6397
|
}
|
|
6029
6398
|
function createWebApp(config, options = {}) {
|
|
6030
6399
|
const app = Fastify({ logger: false });
|
|
@@ -6152,7 +6521,8 @@ function createWebApp(config, options = {}) {
|
|
|
6152
6521
|
app.get("/", async (_request, reply) => {
|
|
6153
6522
|
await tokenReady;
|
|
6154
6523
|
reply.type("text/html; charset=utf-8");
|
|
6155
|
-
|
|
6524
|
+
reply.header("set-cookie", getWebActionCookie(webActionToken));
|
|
6525
|
+
return buildHtml();
|
|
6156
6526
|
});
|
|
6157
6527
|
return app;
|
|
6158
6528
|
}
|