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/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import fs15 from "fs/promises";
|
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "chattercatcher",
|
|
11
|
-
version: "0.
|
|
11
|
+
version: "0.2.2",
|
|
12
12
|
description: "\u672C\u5730\u4F18\u5148\u7684\u98DE\u4E66/Lark \u5BB6\u5EAD\u7FA4\u77E5\u8BC6\u5E93\u673A\u5668\u4EBA",
|
|
13
13
|
type: "module",
|
|
14
14
|
main: "dist/index.js",
|
|
@@ -4463,8 +4463,8 @@ function findMarkdownLinkEnd(text, start) {
|
|
|
4463
4463
|
}
|
|
4464
4464
|
return -1;
|
|
4465
4465
|
}
|
|
4466
|
-
function
|
|
4467
|
-
|
|
4466
|
+
function stripInlineMarkdown(text) {
|
|
4467
|
+
let output = "";
|
|
4468
4468
|
let index2 = 0;
|
|
4469
4469
|
while (index2 < text.length) {
|
|
4470
4470
|
const linkStart = text.indexOf("[", index2);
|
|
@@ -4473,12 +4473,10 @@ function parseInline(text) {
|
|
|
4473
4473
|
const candidates = [linkStart, boldStarStart, boldUnderscoreStart].filter((value) => value >= 0);
|
|
4474
4474
|
const next = candidates.length ? Math.min(...candidates) : -1;
|
|
4475
4475
|
if (next < 0) {
|
|
4476
|
-
|
|
4476
|
+
output += text.slice(index2);
|
|
4477
4477
|
break;
|
|
4478
4478
|
}
|
|
4479
|
-
|
|
4480
|
-
elements.push({ tag: "text", text: text.slice(index2, next) });
|
|
4481
|
-
}
|
|
4479
|
+
output += text.slice(index2, next);
|
|
4482
4480
|
if (next === linkStart) {
|
|
4483
4481
|
const labelEnd = text.indexOf("](", next);
|
|
4484
4482
|
if (labelEnd > next) {
|
|
@@ -4486,27 +4484,29 @@ function parseInline(text) {
|
|
|
4486
4484
|
const hrefEnd = findMarkdownLinkEnd(text, hrefStart);
|
|
4487
4485
|
const href = hrefEnd >= 0 ? text.slice(hrefStart, hrefEnd) : "";
|
|
4488
4486
|
if (hrefEnd >= 0 && /^https?:\/\/\S+$/.test(href)) {
|
|
4489
|
-
|
|
4487
|
+
output += `${text.slice(next + 1, labelEnd)} ${href}`;
|
|
4490
4488
|
index2 = hrefEnd + 1;
|
|
4491
4489
|
continue;
|
|
4492
4490
|
}
|
|
4493
4491
|
}
|
|
4494
|
-
|
|
4492
|
+
output += text[next];
|
|
4495
4493
|
index2 = next + 1;
|
|
4496
4494
|
continue;
|
|
4497
4495
|
}
|
|
4498
4496
|
const marker = next === boldStarStart ? "**" : "__";
|
|
4499
4497
|
const close = text.indexOf(marker, next + marker.length);
|
|
4500
4498
|
if (close > next + marker.length) {
|
|
4501
|
-
|
|
4499
|
+
output += text.slice(next + marker.length, close);
|
|
4502
4500
|
index2 = close + marker.length;
|
|
4503
4501
|
continue;
|
|
4504
4502
|
}
|
|
4505
|
-
|
|
4503
|
+
output += marker;
|
|
4506
4504
|
index2 = next + marker.length;
|
|
4507
4505
|
}
|
|
4508
|
-
|
|
4509
|
-
|
|
4506
|
+
return output;
|
|
4507
|
+
}
|
|
4508
|
+
function parseInline(text) {
|
|
4509
|
+
return [{ tag: "text", text: stripInlineMarkdown(text) || " " }];
|
|
4510
4510
|
}
|
|
4511
4511
|
function pushParagraph(content, lines) {
|
|
4512
4512
|
if (lines.length === 0) return;
|
|
@@ -4547,7 +4547,7 @@ ${code.join("\n")}
|
|
|
4547
4547
|
const heading = line.match(/^#{1,6}\s+(.+)$/);
|
|
4548
4548
|
if (heading) {
|
|
4549
4549
|
pushParagraph(content, paragraph);
|
|
4550
|
-
content.push([{ tag: "text", text: heading[1]
|
|
4550
|
+
content.push([{ tag: "text", text: stripInlineMarkdown(heading[1]) || " " }]);
|
|
4551
4551
|
continue;
|
|
4552
4552
|
}
|
|
4553
4553
|
const unordered = line.match(/^[-*]\s+(.+)$/);
|
|
@@ -4575,17 +4575,13 @@ function buildFeishuPostContent(markdown, options) {
|
|
|
4575
4575
|
const content = parseMarkdownBlocks(markdown);
|
|
4576
4576
|
const mentions = options?.mentions ?? [];
|
|
4577
4577
|
if (mentions.length) {
|
|
4578
|
-
const mentionElements = mentions.map((mention) => ({
|
|
4579
|
-
tag: "at",
|
|
4580
|
-
user_id: mention.openId,
|
|
4581
|
-
user_name: mention.name
|
|
4582
|
-
}));
|
|
4583
4578
|
const firstLine = content[0] ?? [];
|
|
4584
4579
|
const firstText = firstLine[0];
|
|
4580
|
+
const prefix = mentions.map((mention) => `@${mention.name}`).join(" ");
|
|
4585
4581
|
if (firstText?.tag === "text") {
|
|
4586
|
-
content[0] = [
|
|
4582
|
+
content[0] = [{ tag: "text", text: `${prefix} ${firstText.text}` }, ...firstLine.slice(1)];
|
|
4587
4583
|
} else {
|
|
4588
|
-
content[0] = [
|
|
4584
|
+
content[0] = [{ tag: "text", text: `${prefix} ` }, ...firstLine];
|
|
4589
4585
|
}
|
|
4590
4586
|
}
|
|
4591
4587
|
return {
|
|
@@ -4614,11 +4610,23 @@ function extractImageKey(response) {
|
|
|
4614
4610
|
}
|
|
4615
4611
|
throw new Error("\u98DE\u4E66\u56FE\u7247\u4E0A\u4F20\u54CD\u5E94\u7F3A\u5C11 image_key\u3002");
|
|
4616
4612
|
}
|
|
4617
|
-
function
|
|
4613
|
+
function collectErrorFields(error) {
|
|
4614
|
+
const fields = [error];
|
|
4618
4615
|
const value = error && typeof error === "object" ? error : {};
|
|
4619
|
-
|
|
4620
|
-
const
|
|
4621
|
-
|
|
4616
|
+
fields.push(value.code, value.errorCode, value.msg, value.message);
|
|
4617
|
+
const response = value.response && typeof value.response === "object" ? value.response : {};
|
|
4618
|
+
const data2 = response.data && typeof response.data === "object" ? response.data : {};
|
|
4619
|
+
fields.push(data2.code, data2.errorCode, data2.msg, data2.message);
|
|
4620
|
+
return fields;
|
|
4621
|
+
}
|
|
4622
|
+
function isRichTextCompatibilityError(error) {
|
|
4623
|
+
return collectErrorFields(error).some((field) => {
|
|
4624
|
+
if (field === 230001) return true;
|
|
4625
|
+
if (typeof field === "string") {
|
|
4626
|
+
return /post|msg_type|content|unsupported|invalid/i.test(field);
|
|
4627
|
+
}
|
|
4628
|
+
return false;
|
|
4629
|
+
});
|
|
4622
4630
|
}
|
|
4623
4631
|
async function sendWithTextFallback(input2) {
|
|
4624
4632
|
try {
|
|
@@ -5926,516 +5934,867 @@ import Fastify from "fastify";
|
|
|
5926
5934
|
function buildHtml() {
|
|
5927
5935
|
return `<!doctype html>
|
|
5928
5936
|
<html lang="zh-CN">
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
.
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
.
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
.
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
5937
|
+
<head>
|
|
5938
|
+
<meta charset="utf-8" />
|
|
5939
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
5940
|
+
<meta name="color-scheme" content="dark" />
|
|
5941
|
+
<title>ChatterCatcher</title>
|
|
5942
|
+
<style>
|
|
5943
|
+
:root {
|
|
5944
|
+
--bg-primary: #0a0a0f;
|
|
5945
|
+
--bg-secondary: #12121a;
|
|
5946
|
+
--bg-tertiary: #1a1a28;
|
|
5947
|
+
--glass-bg: rgba(255,255,255,0.05);
|
|
5948
|
+
--glass-border: rgba(255,255,255,0.1);
|
|
5949
|
+
--glass-border-hover: rgba(255,255,255,0.2);
|
|
5950
|
+
--glass-shadow: 0 8px 32px rgba(0,0,0,0.3);
|
|
5951
|
+
--text-primary: #f0f0f5;
|
|
5952
|
+
--text-secondary: #a0a0b0;
|
|
5953
|
+
--text-muted: #6e6e80;
|
|
5954
|
+
--accent: #64d2ff;
|
|
5955
|
+
--accent-hover: #7dd8ff;
|
|
5956
|
+
--success: #30d158;
|
|
5957
|
+
--warning: #ff9f0a;
|
|
5958
|
+
--danger: #ff453a;
|
|
5959
|
+
--radius-sm: 8px;
|
|
5960
|
+
--radius-md: 12px;
|
|
5961
|
+
--radius-lg: 16px;
|
|
5962
|
+
--radius-xl: 24px;
|
|
5963
|
+
--space-xs: 4px;
|
|
5964
|
+
--space-sm: 8px;
|
|
5965
|
+
--space-md: 16px;
|
|
5966
|
+
--space-lg: 24px;
|
|
5967
|
+
--space-xl: 32px;
|
|
5968
|
+
--space-2xl: 48px;
|
|
5969
|
+
--font-sans: -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;
|
|
5970
|
+
--font-mono: "SF Mono","Menlo","Consolas",monospace;
|
|
5971
|
+
}
|
|
5972
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
5973
|
+
body {
|
|
5974
|
+
font-family: var(--font-sans);
|
|
5975
|
+
background: var(--bg-primary);
|
|
5976
|
+
color: var(--text-primary);
|
|
5977
|
+
line-height: 1.6;
|
|
5978
|
+
-webkit-font-smoothing: antialiased;
|
|
5979
|
+
overflow-x: hidden;
|
|
5980
|
+
min-height: 100vh;
|
|
5981
|
+
}
|
|
5982
|
+
.glass {
|
|
5983
|
+
background: var(--glass-bg);
|
|
5984
|
+
backdrop-filter: blur(20px) saturate(180%);
|
|
5985
|
+
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
|
5986
|
+
border: 1px solid var(--glass-border);
|
|
5987
|
+
border-radius: var(--radius-lg);
|
|
5988
|
+
box-shadow: var(--glass-shadow);
|
|
5989
|
+
transition: all 0.3s ease;
|
|
5990
|
+
}
|
|
5991
|
+
.glass:hover { border-color: var(--glass-border-hover); box-shadow: 0 12px 40px rgba(0,0,0,0.4); }
|
|
5992
|
+
.gradient-bg {
|
|
5993
|
+
background: linear-gradient(135deg,#0a0a0f 0%,#12121a 50%,#1a1a28 100%);
|
|
5994
|
+
min-height: 100vh;
|
|
5995
|
+
}
|
|
5996
|
+
.sidebar {
|
|
5997
|
+
position: fixed; left: 0; top: 0; width: 260px; height: 100vh;
|
|
5998
|
+
padding: var(--space-lg); display: flex; flex-direction: column; gap: var(--space-md); z-index: 100;
|
|
5999
|
+
background: linear-gradient(180deg,rgba(255,255,255,0.08) 0%,rgba(255,255,255,0.02) 100%);
|
|
6000
|
+
backdrop-filter: blur(40px) saturate(200%);
|
|
6001
|
+
-webkit-backdrop-filter: blur(40px) saturate(200%);
|
|
6002
|
+
border-right: 1px solid var(--glass-border);
|
|
6003
|
+
}
|
|
6004
|
+
.sidebar-logo {
|
|
6005
|
+
display: flex; align-items: center; gap: var(--space-sm);
|
|
6006
|
+
padding: var(--space-md); font-size: 20px; font-weight: 700;
|
|
6007
|
+
color: var(--text-primary); margin-bottom: var(--space-md);
|
|
6008
|
+
}
|
|
6009
|
+
.logo-icon {
|
|
6010
|
+
width: 36px; height: 36px;
|
|
6011
|
+
background: linear-gradient(135deg,var(--accent),#5e60ce);
|
|
6012
|
+
border-radius: var(--radius-md);
|
|
6013
|
+
display: flex; align-items: center; justify-content: center;
|
|
6014
|
+
box-shadow: 0 4px 16px rgba(100,210,255,0.3);
|
|
6015
|
+
}
|
|
6016
|
+
.sidebar-nav { display: flex; flex-direction: column; gap: var(--space-xs); }
|
|
6017
|
+
.nav-item {
|
|
6018
|
+
display: flex; align-items: center; gap: var(--space-sm);
|
|
6019
|
+
padding: var(--space-sm) var(--space-md); border-radius: var(--radius-md);
|
|
6020
|
+
color: var(--text-secondary); text-decoration: none; cursor: pointer;
|
|
6021
|
+
transition: all 0.2s ease; border: none; background: none;
|
|
6022
|
+
font-size: 14px; font-family: inherit; width: 100%; text-align: left;
|
|
6023
|
+
}
|
|
6024
|
+
.nav-item:hover { background: rgba(255,255,255,0.06); color: var(--text-primary); }
|
|
6025
|
+
.nav-item.active {
|
|
6026
|
+
background: rgba(100,210,255,0.15); color: var(--accent);
|
|
6027
|
+
box-shadow: 0 0 20px rgba(100,210,255,0.1);
|
|
6028
|
+
}
|
|
6029
|
+
.nav-icon { width: 20px; height: 20px; flex-shrink: 0; }
|
|
6030
|
+
.main-content { margin-left: 260px; min-height: 100vh; padding: var(--space-xl); }
|
|
6031
|
+
.page-header { margin-bottom: var(--space-xl); }
|
|
6032
|
+
.page-title {
|
|
6033
|
+
font-size: 36px; font-weight: 700; letter-spacing: -0.03em;
|
|
6034
|
+
margin-bottom: var(--space-sm);
|
|
6035
|
+
background: linear-gradient(135deg,var(--text-primary),var(--accent));
|
|
6036
|
+
-webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
|
|
6037
|
+
}
|
|
6038
|
+
.page-subtitle { color: var(--text-secondary); font-size: 15px; }
|
|
6039
|
+
.metrics-grid {
|
|
6040
|
+
display: grid; grid-template-columns: repeat(auto-fit,minmax(200px,1fr));
|
|
6041
|
+
gap: var(--space-md); margin-bottom: var(--space-xl);
|
|
6042
|
+
}
|
|
6043
|
+
.metric-card {
|
|
6044
|
+
padding: var(--space-lg); display: flex; flex-direction: column; gap: var(--space-sm);
|
|
6045
|
+
position: relative; overflow: hidden;
|
|
6046
|
+
}
|
|
6047
|
+
.metric-card::before {
|
|
6048
|
+
content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
|
|
6049
|
+
background: linear-gradient(90deg,var(--accent),transparent); opacity: 0.5;
|
|
6050
|
+
}
|
|
6051
|
+
.metric-value { font-size: 40px; font-weight: 700; color: var(--text-primary); line-height: 1; font-variant-numeric: tabular-nums; }
|
|
6052
|
+
.metric-label { font-size: 12px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; }
|
|
6053
|
+
.metric-note { font-size: 13px; color: var(--text-secondary); margin-top: var(--space-xs); }
|
|
6054
|
+
.content-grid { display: grid; grid-template-columns: 2fr 1fr; gap: var(--space-lg); }
|
|
6055
|
+
.content-panel { padding: var(--space-lg); }
|
|
6056
|
+
.panel-header {
|
|
6057
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
6058
|
+
margin-bottom: var(--space-lg); padding-bottom: var(--space-md);
|
|
6059
|
+
border-bottom: 1px solid var(--glass-border);
|
|
6060
|
+
}
|
|
6061
|
+
.panel-title { font-size: 18px; font-weight: 600; }
|
|
6062
|
+
.message-list { display: flex; flex-direction: column; gap: var(--space-sm); }
|
|
6063
|
+
.message-card {
|
|
6064
|
+
padding: var(--space-md); border-radius: var(--radius-md);
|
|
6065
|
+
background: rgba(255,255,255,0.03); border: 1px solid transparent;
|
|
6066
|
+
transition: all 0.25s ease; cursor: pointer;
|
|
6067
|
+
}
|
|
6068
|
+
.message-card:hover { background: rgba(255,255,255,0.06); border-color: var(--glass-border); transform: translateX(4px); }
|
|
6069
|
+
.message-meta {
|
|
6070
|
+
display: flex; align-items: center; gap: var(--space-md);
|
|
6071
|
+
color: var(--text-muted); font-size: 12px; margin-bottom: var(--space-xs); flex-wrap: wrap;
|
|
6072
|
+
}
|
|
6073
|
+
.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; }
|
|
6074
|
+
.status-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
|
|
6075
|
+
.status-dot.online { background: var(--success); box-shadow: 0 0 8px var(--success); }
|
|
6076
|
+
.status-dot.offline { background: var(--danger); }
|
|
6077
|
+
.status-dot.warning { background: var(--warning); box-shadow: 0 0 8px var(--warning); }
|
|
6078
|
+
.status-dot.pending { background: var(--text-muted); }
|
|
6079
|
+
.btn {
|
|
6080
|
+
display: inline-flex; align-items: center; justify-content: center; gap: var(--space-sm);
|
|
6081
|
+
padding: 10px var(--space-md); border-radius: var(--radius-md);
|
|
6082
|
+
border: 1px solid var(--glass-border); background: var(--glass-bg);
|
|
6083
|
+
color: var(--text-primary); font-family: inherit; font-size: 14px;
|
|
6084
|
+
cursor: pointer; transition: all 0.2s ease; text-decoration: none;
|
|
6085
|
+
}
|
|
6086
|
+
.btn:hover { background: rgba(255,255,255,0.1); border-color: var(--glass-border-hover); transform: translateY(-1px); }
|
|
6087
|
+
.btn-primary {
|
|
6088
|
+
background: linear-gradient(135deg,var(--accent),#5e60ce); color: white; border: none;
|
|
6089
|
+
font-weight: 600; box-shadow: 0 4px 16px rgba(100,210,255,0.3);
|
|
6090
|
+
}
|
|
6091
|
+
.btn-primary:hover {
|
|
6092
|
+
background: linear-gradient(135deg,var(--accent-hover),#6b6dd8);
|
|
6093
|
+
box-shadow: 0 6px 20px rgba(100,210,255,0.4); transform: translateY(-1px);
|
|
6094
|
+
}
|
|
6095
|
+
.btn-danger { background: rgba(255,69,58,0.15); color: var(--danger); border-color: rgba(255,69,58,0.3); }
|
|
6096
|
+
.btn-danger:hover { background: rgba(255,69,58,0.25); }
|
|
6097
|
+
.btn-sm { padding: 6px var(--space-sm); font-size: 13px; }
|
|
6098
|
+
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
|
|
6099
|
+
.tag {
|
|
6100
|
+
display: inline-flex; align-items: center; padding: 2px 10px;
|
|
6101
|
+
border-radius: 20px; font-size: 12px; font-weight: 500;
|
|
6102
|
+
background: rgba(255,255,255,0.06); color: var(--text-secondary);
|
|
6103
|
+
}
|
|
6104
|
+
.tag-success { background: rgba(48,209,88,0.15); color: var(--success); }
|
|
6105
|
+
.tag-warning { background: rgba(255,159,10,0.15); color: var(--warning); }
|
|
6106
|
+
.tag-error { background: rgba(255,69,58,0.15); color: var(--danger); }
|
|
6107
|
+
.tag-info { background: rgba(100,210,255,0.15); color: var(--accent); }
|
|
6108
|
+
.empty-state { text-align: center; padding: var(--space-2xl); color: var(--text-muted); }
|
|
6109
|
+
.empty-state svg { width: 48px; height: 48px; margin: 0 auto var(--space-md); opacity: 0.3; }
|
|
6110
|
+
.skeleton {
|
|
6111
|
+
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%);
|
|
6112
|
+
background-size: 200% 100%; animation: shimmer 1.5s infinite; border-radius: var(--radius-sm);
|
|
6113
|
+
}
|
|
6114
|
+
@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
|
|
6115
|
+
@keyframes fadeIn { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
|
|
6116
|
+
@keyframes slideIn { from { opacity: 0; transform: translateX(-10px); } to { opacity: 1; transform: translateX(0); } }
|
|
6117
|
+
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
|
|
6118
|
+
.view { display: none; animation: fadeIn 0.35s ease; }
|
|
6119
|
+
.view.active { display: block; }
|
|
6120
|
+
.search-box { position: relative; width: 100%; max-width: 400px; }
|
|
6121
|
+
.search-box input {
|
|
6122
|
+
width: 100%; padding: var(--space-sm) var(--space-md) var(--space-sm) 40px;
|
|
6123
|
+
border-radius: var(--radius-md); border: 1px solid var(--glass-border);
|
|
6124
|
+
background: var(--glass-bg); color: var(--text-primary); font-family: inherit;
|
|
6125
|
+
font-size: 14px; outline: none; transition: all 0.2s ease;
|
|
6126
|
+
}
|
|
6127
|
+
.search-box input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(100,210,255,0.1); }
|
|
6128
|
+
.search-box .search-icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--text-muted); }
|
|
6129
|
+
.data-table { width: 100%; border-collapse: collapse; }
|
|
6130
|
+
.data-table th {
|
|
6131
|
+
text-align: left; padding: var(--space-sm) var(--space-md); color: var(--text-muted);
|
|
6132
|
+
font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em;
|
|
6133
|
+
border-bottom: 1px solid var(--glass-border);
|
|
6134
|
+
}
|
|
6135
|
+
.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; }
|
|
6136
|
+
.data-table tr:hover td { background: rgba(255,255,255,0.02); }
|
|
6137
|
+
.data-table tr:last-child td { border-bottom: none; }
|
|
6138
|
+
.truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
|
|
6139
|
+
.truncate-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
|
6140
|
+
.truncate-3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
|
|
6141
|
+
.flex { display: flex; } .flex-col { flex-direction: column; }
|
|
6142
|
+
.items-center { align-items: center; } .justify-between { justify-content: space-between; }
|
|
6143
|
+
.gap-sm { gap: var(--space-sm); } .gap-md { gap: var(--space-md); }
|
|
6144
|
+
.mt-md { margin-top: var(--space-md); } .mt-lg { margin-top: var(--space-lg); }
|
|
6145
|
+
.mb-md { margin-bottom: var(--space-md); }
|
|
6146
|
+
.toast {
|
|
6147
|
+
padding: var(--space-md) var(--space-lg); border-radius: var(--radius-md);
|
|
6148
|
+
background: var(--glass-bg); backdrop-filter: blur(20px); border: 1px solid var(--glass-border);
|
|
6149
|
+
box-shadow: 0 8px 32px rgba(0,0,0,0.4); color: var(--text-primary); font-size: 14px;
|
|
6150
|
+
max-width: 400px; animation: slideIn 0.3s ease;
|
|
6151
|
+
display: flex; align-items: center; gap: var(--space-sm);
|
|
6152
|
+
}
|
|
6153
|
+
.toast-success { border-color: rgba(48,209,88,0.3); background: rgba(48,209,88,0.1); }
|
|
6154
|
+
.toast-error { border-color: rgba(255,69,58,0.3); background: rgba(255,69,58,0.1); }
|
|
6155
|
+
.toast-warning { border-color: rgba(255,159,10,0.3); background: rgba(255,159,10,0.1); }
|
|
6156
|
+
.episode-card {
|
|
6157
|
+
padding: var(--space-md); border-radius: var(--radius-md);
|
|
6158
|
+
background: rgba(255,255,255,0.03); border: 1px solid transparent; transition: all 0.25s ease;
|
|
6159
|
+
}
|
|
6160
|
+
.episode-card:hover { background: rgba(255,255,255,0.06); border-color: var(--glass-border); }
|
|
6161
|
+
.qa-card {
|
|
6162
|
+
padding: var(--space-md); border-radius: var(--radius-md);
|
|
6163
|
+
background: rgba(255,255,255,0.03); border-left: 3px solid var(--accent); margin-bottom: var(--space-sm);
|
|
6164
|
+
}
|
|
6165
|
+
.qa-question { font-weight: 600; color: var(--text-primary); margin-bottom: var(--space-xs); font-size: 14px; }
|
|
6166
|
+
.qa-answer { color: var(--text-secondary); font-size: 14px; line-height: 1.6; }
|
|
6167
|
+
.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--space-lg); }
|
|
6168
|
+
.section-title { font-size: 24px; font-weight: 700; }
|
|
6169
|
+
.tabs {
|
|
6170
|
+
display: flex; gap: var(--space-xs); padding: 4px;
|
|
6171
|
+
background: rgba(255,255,255,0.03); border-radius: var(--radius-md); border: 1px solid var(--glass-border);
|
|
6172
|
+
}
|
|
6173
|
+
.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; }
|
|
6174
|
+
.tab:hover { color: var(--text-primary); }
|
|
6175
|
+
.tab.active { background: rgba(255,255,255,0.08); color: var(--text-primary); font-weight: 500; }
|
|
6176
|
+
.file-card {
|
|
6177
|
+
padding: var(--space-md); border-radius: var(--radius-md);
|
|
6178
|
+
background: rgba(255,255,255,0.03); border: 1px solid transparent; transition: all 0.25s ease; cursor: pointer;
|
|
6179
|
+
}
|
|
6180
|
+
.file-card:hover { background: rgba(255,255,255,0.06); border-color: var(--glass-border); }
|
|
6181
|
+
.file-icon {
|
|
6182
|
+
width: 40px; height: 40px; border-radius: var(--radius-sm);
|
|
6183
|
+
background: linear-gradient(135deg,var(--accent),#5e60ce);
|
|
6184
|
+
display: flex; align-items: center; justify-content: center; margin-bottom: var(--space-sm);
|
|
6185
|
+
}
|
|
6186
|
+
.timeline { position: relative; padding-left: 28px; }
|
|
6187
|
+
.timeline::before { content: ''; position: absolute; left: 8px; top: 0; bottom: 0; width: 2px; background: linear-gradient(180deg,var(--accent),transparent); opacity: 0.3; }
|
|
6188
|
+
.timeline-item { position: relative; padding-bottom: var(--space-lg); }
|
|
6189
|
+
.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); }
|
|
6190
|
+
.timeline-date { font-size: 12px; color: var(--text-muted); margin-bottom: var(--space-xs); }
|
|
6191
|
+
.timeline-content { color: var(--text-secondary); font-size: 14px; }
|
|
6192
|
+
.status-bar { display: flex; align-items: center; gap: var(--space-md); padding: var(--space-md); margin-bottom: var(--space-lg); }
|
|
6193
|
+
.status-item { display: flex; align-items: center; gap: var(--space-sm); }
|
|
6194
|
+
.status-label { font-size: 13px; color: var(--text-muted); }
|
|
6195
|
+
.status-value { font-size: 14px; font-weight: 600; color: var(--text-primary); }
|
|
6196
|
+
.grid-2 { display: grid; grid-template-columns: repeat(2,1fr); gap: var(--space-md); }
|
|
6197
|
+
.grid-3 { display: grid; grid-template-columns: repeat(3,1fr); gap: var(--space-md); }
|
|
6198
|
+
.settings-group { padding: var(--space-lg); margin-bottom: var(--space-lg); }
|
|
6199
|
+
.settings-item { display: flex; justify-content: space-between; align-items: center; padding: var(--space-md) 0; border-bottom: 1px solid var(--glass-border); }
|
|
6200
|
+
.settings-item:last-child { border-bottom: none; }
|
|
6201
|
+
.settings-label { font-size: 14px; font-weight: 500; color: var(--text-primary); }
|
|
6202
|
+
.settings-value { font-size: 14px; color: var(--text-secondary); font-family: var(--font-mono); }
|
|
6203
|
+
.settings-desc { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
|
|
6204
|
+
.mobile-nav {
|
|
6205
|
+
display: none; position: fixed; bottom: 0; left: 0; right: 0;
|
|
6206
|
+
padding: var(--space-sm); z-index: 100; flex-direction: row; justify-content: space-around;
|
|
6207
|
+
border-top: 1px solid var(--glass-border);
|
|
6208
|
+
background: linear-gradient(180deg,rgba(255,255,255,0.08) 0%,rgba(255,255,255,0.02) 100%);
|
|
6209
|
+
backdrop-filter: blur(40px) saturate(200%);
|
|
6210
|
+
}
|
|
6211
|
+
.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; }
|
|
6212
|
+
.mobile-nav-item.active { color: var(--accent); }
|
|
6213
|
+
.pulse { animation: pulse 2s cubic-bezier(0.4,0,0.6,1) infinite; }
|
|
6214
|
+
@media (max-width: 1024px) {
|
|
6215
|
+
.sidebar { width: 72px; padding: var(--space-sm); }
|
|
6216
|
+
.sidebar-logo span, .nav-item span { display: none; }
|
|
6217
|
+
.nav-item { justify-content: center; padding: var(--space-sm); }
|
|
6218
|
+
.main-content { margin-left: 72px; padding: var(--space-lg); }
|
|
6219
|
+
.content-grid { grid-template-columns: 1fr; }
|
|
6220
|
+
.metrics-grid { grid-template-columns: repeat(2,1fr); }
|
|
6221
|
+
}
|
|
6222
|
+
@media (max-width: 768px) {
|
|
6223
|
+
.sidebar { display: none; }
|
|
6224
|
+
.mobile-nav { display: flex; }
|
|
6225
|
+
.main-content { margin-left: 0; margin-bottom: 80px; padding: var(--space-md); }
|
|
6226
|
+
.page-title { font-size: 28px; }
|
|
6227
|
+
.metrics-grid { grid-template-columns: repeat(2,1fr); }
|
|
6228
|
+
.grid-2, .grid-3 { grid-template-columns: 1fr; }
|
|
6229
|
+
.section-header { flex-direction: column; align-items: flex-start; gap: var(--space-sm); }
|
|
6230
|
+
}
|
|
6231
|
+
@media (prefers-reduced-motion: reduce) {
|
|
6232
|
+
*, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
|
|
6233
|
+
}
|
|
6234
|
+
::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
6235
|
+
::-webkit-scrollbar-track { background: transparent; }
|
|
6236
|
+
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 4px; }
|
|
6237
|
+
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.2); }
|
|
6238
|
+
.highlight-text { background: rgba(100,210,255,0.15); padding: 0 4px; border-radius: 3px; color: var(--accent); }
|
|
6239
|
+
</style>
|
|
6240
|
+
</head>
|
|
6241
|
+
<body class="gradient-bg">
|
|
6242
|
+
<aside class="sidebar">
|
|
6243
|
+
<div class="sidebar-logo">
|
|
6244
|
+
<div class="logo-icon">
|
|
6245
|
+
<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>
|
|
6246
|
+
</div>
|
|
6247
|
+
<span>ChatterCatcher</span>
|
|
6248
|
+
</div>
|
|
6249
|
+
<nav class="sidebar-nav">
|
|
6250
|
+
<button class="nav-item active" data-view="overview">
|
|
6251
|
+
<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>
|
|
6252
|
+
<span>\u6982\u89C8</span>
|
|
6253
|
+
</button>
|
|
6254
|
+
<button class="nav-item" data-view="messages">
|
|
6255
|
+
<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>
|
|
6256
|
+
<span>\u6D88\u606F</span>
|
|
6257
|
+
</button>
|
|
6258
|
+
<button class="nav-item" data-view="episodes">
|
|
6259
|
+
<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>
|
|
6260
|
+
<span>\u4F1A\u8BDD\u8BB0\u5FC6</span>
|
|
6261
|
+
</button>
|
|
6262
|
+
<button class="nav-item" data-view="files">
|
|
6263
|
+
<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>
|
|
6264
|
+
<span>\u6587\u4EF6\u5E93</span>
|
|
6265
|
+
</button>
|
|
6266
|
+
<button class="nav-item" data-view="tasks">
|
|
6267
|
+
<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>
|
|
6268
|
+
<span>\u4EFB\u52A1</span>
|
|
6269
|
+
</button>
|
|
6270
|
+
<button class="nav-item" data-view="qa-logs">
|
|
6271
|
+
<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>
|
|
6272
|
+
<span>\u95EE\u7B54\u65E5\u5FD7</span>
|
|
6273
|
+
</button>
|
|
6274
|
+
<button class="nav-item" data-view="settings">
|
|
6275
|
+
<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>
|
|
6276
|
+
<span>\u8BBE\u7F6E</span>
|
|
6277
|
+
</button>
|
|
6278
|
+
</nav>
|
|
6279
|
+
<div style="margin-top: auto; padding: var(--space-md);">
|
|
6280
|
+
<div style="display: flex; align-items: center; gap: var(--space-sm); font-size: 12px; color: var(--text-muted);">
|
|
6281
|
+
<span class="status-dot online" id="gateway-indicator"></span>
|
|
6282
|
+
<span id="gateway-status-text">Gateway \u8FD0\u884C\u4E2D</span>
|
|
6283
|
+
</div>
|
|
6284
|
+
<div style="font-size: 11px; color: var(--text-muted); margin-top: var(--space-xs); opacity: 0.7;" id="version-text">v0.0.0</div>
|
|
6285
|
+
</div>
|
|
6286
|
+
</aside>
|
|
6287
|
+
|
|
6288
|
+
<nav class="mobile-nav glass">
|
|
6289
|
+
<button class="mobile-nav-item active" data-view="overview">
|
|
6290
|
+
<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>
|
|
6291
|
+
<span>\u6982\u89C8</span>
|
|
6292
|
+
</button>
|
|
6293
|
+
<button class="mobile-nav-item" data-view="messages">
|
|
6294
|
+
<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>
|
|
6295
|
+
<span>\u6D88\u606F</span>
|
|
6296
|
+
</button>
|
|
6297
|
+
<button class="mobile-nav-item" data-view="files">
|
|
6298
|
+
<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>
|
|
6299
|
+
<span>\u6587\u4EF6</span>
|
|
6300
|
+
</button>
|
|
6301
|
+
<button class="mobile-nav-item" data-view="tasks">
|
|
6302
|
+
<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>
|
|
6303
|
+
<span>\u4EFB\u52A1</span>
|
|
6304
|
+
</button>
|
|
6305
|
+
<button class="mobile-nav-item" data-view="settings">
|
|
6306
|
+
<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>
|
|
6307
|
+
<span>\u8BBE\u7F6E</span>
|
|
6308
|
+
</button>
|
|
6309
|
+
</nav>
|
|
6310
|
+
|
|
6311
|
+
<main class="main-content">
|
|
6312
|
+
<div class="view active" id="view-overview">
|
|
6313
|
+
<div class="page-header">
|
|
6314
|
+
<h1 class="page-title">Dashboard</h1>
|
|
6315
|
+
<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>
|
|
6316
|
+
</div>
|
|
6317
|
+
<div class="metrics-grid" id="metrics"></div>
|
|
6318
|
+
<div class="content-grid">
|
|
6030
6319
|
<div>
|
|
6031
|
-
<
|
|
6032
|
-
|
|
6320
|
+
<div class="content-panel glass">
|
|
6321
|
+
<div class="panel-header">
|
|
6322
|
+
<h2 class="panel-title">\u6700\u8FD1\u6D88\u606F</h2>
|
|
6323
|
+
<button class="btn btn-sm" onclick="navigateTo('messages')">\u67E5\u770B\u5168\u90E8</button>
|
|
6324
|
+
</div>
|
|
6325
|
+
<div id="recent-messages"></div>
|
|
6326
|
+
</div>
|
|
6327
|
+
<div class="content-panel glass mt-lg">
|
|
6328
|
+
<div class="panel-header">
|
|
6329
|
+
<h2 class="panel-title">\u4F1A\u8BDD\u8BB0\u5FC6</h2>
|
|
6330
|
+
<button class="btn btn-sm" onclick="navigateTo('episodes')">\u67E5\u770B\u5168\u90E8</button>
|
|
6331
|
+
</div>
|
|
6332
|
+
<div id="recent-episodes"></div>
|
|
6333
|
+
</div>
|
|
6033
6334
|
</div>
|
|
6034
6335
|
<div>
|
|
6035
|
-
<div class="
|
|
6036
|
-
<
|
|
6336
|
+
<div class="content-panel glass">
|
|
6337
|
+
<div class="panel-header"><h2 class="panel-title">\u7CFB\u7EDF\u72B6\u6001</h2></div>
|
|
6338
|
+
<div id="system-status"></div>
|
|
6339
|
+
</div>
|
|
6340
|
+
<div class="content-panel glass mt-lg">
|
|
6341
|
+
<div class="panel-header"><h2 class="panel-title">\u5FEB\u6377\u64CD\u4F5C</h2></div>
|
|
6342
|
+
<div style="display: flex; flex-direction: column; gap: var(--space-sm);">
|
|
6343
|
+
<button class="btn btn-primary" id="btn-process-messages" onclick="processNow()">
|
|
6344
|
+
<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>
|
|
6345
|
+
\u7ACB\u5373\u5904\u7406\u6D88\u606F
|
|
6346
|
+
</button>
|
|
6347
|
+
<button class="btn" onclick="navigateTo('settings')">
|
|
6348
|
+
<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>
|
|
6349
|
+
\u7CFB\u7EDF\u8BBE\u7F6E
|
|
6350
|
+
</button>
|
|
6351
|
+
</div>
|
|
6352
|
+
</div>
|
|
6353
|
+
<div class="content-panel glass mt-lg">
|
|
6354
|
+
<div class="panel-header"><h2 class="panel-title">RAG \u68C0\u7D22</h2></div>
|
|
6355
|
+
<div style="font-size: 13px; color: var(--text-secondary); line-height: 1.8;">
|
|
6356
|
+
<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>
|
|
6357
|
+
<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>
|
|
6358
|
+
<div style="display: flex; align-items: center; gap: var(--space-sm);"><span class="tag tag-success">\u6DF7\u5408</span><span>Hybrid RAG</span></div>
|
|
6359
|
+
</div>
|
|
6037
6360
|
</div>
|
|
6038
|
-
<div id="action-status" class="status-line"></div>
|
|
6039
6361
|
</div>
|
|
6040
|
-
</
|
|
6362
|
+
</div>
|
|
6363
|
+
</div>
|
|
6041
6364
|
|
|
6042
|
-
|
|
6365
|
+
<div class="view" id="view-messages">
|
|
6366
|
+
<div class="section-header">
|
|
6367
|
+
<div><h1 class="section-title">\u6D88\u606F</h1><p class="page-subtitle">\u7FA4\u804A\u6D88\u606F\u5386\u53F2</p></div>
|
|
6368
|
+
<div class="search-box">
|
|
6369
|
+
<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>
|
|
6370
|
+
<input type="text" id="message-search" placeholder="\u641C\u7D22\u6D88\u606F..." oninput="filterMessages()" />
|
|
6371
|
+
</div>
|
|
6372
|
+
</div>
|
|
6373
|
+
<div class="content-panel glass"><div id="messages-list"></div></div>
|
|
6374
|
+
</div>
|
|
6043
6375
|
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6376
|
+
<div class="view" id="view-episodes">
|
|
6377
|
+
<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>
|
|
6378
|
+
<div class="content-panel glass"><div id="episodes-list"></div></div>
|
|
6379
|
+
</div>
|
|
6380
|
+
|
|
6381
|
+
<div class="view" id="view-files">
|
|
6382
|
+
<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>
|
|
6383
|
+
<div id="files-list"></div>
|
|
6384
|
+
</div>
|
|
6385
|
+
|
|
6386
|
+
<div class="view" id="view-tasks">
|
|
6387
|
+
<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>
|
|
6388
|
+
<div class="tabs" style="margin-bottom: var(--space-lg);">
|
|
6389
|
+
<button class="tab active" data-tab="file-jobs" onclick="switchTab('file-jobs')">\u6587\u4EF6\u89E3\u6790</button>
|
|
6390
|
+
<button class="tab" data-tab="cron-jobs" onclick="switchTab('cron-jobs')">\u5B9A\u65F6\u4EFB\u52A1</button>
|
|
6391
|
+
</div>
|
|
6392
|
+
<div class="content-panel glass" id="tab-file-jobs"><div id="file-jobs-list"></div></div>
|
|
6393
|
+
<div class="content-panel glass" id="tab-cron-jobs" style="display: none;"><div id="cron-jobs-list"></div></div>
|
|
6394
|
+
</div>
|
|
6395
|
+
|
|
6396
|
+
<div class="view" id="view-qa-logs">
|
|
6397
|
+
<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>
|
|
6398
|
+
<div class="content-panel glass"><div id="qa-logs-list"></div></div>
|
|
6399
|
+
</div>
|
|
6400
|
+
|
|
6401
|
+
<div class="view" id="view-settings">
|
|
6402
|
+
<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>
|
|
6403
|
+
<div class="settings-group glass" id="settings-config"></div>
|
|
6404
|
+
<div class="settings-group glass">
|
|
6405
|
+
<h3 style="font-size: 16px; font-weight: 600; margin-bottom: var(--space-md);">\u64CD\u4F5C</h3>
|
|
6406
|
+
<div style="display: flex; flex-direction: column; gap: var(--space-sm);">
|
|
6407
|
+
<button class="btn btn-primary" onclick="processNow()">
|
|
6408
|
+
<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>
|
|
6409
|
+
\u7ACB\u5373\u5904\u7406\u6D88\u606F\u7D22\u5F15
|
|
6410
|
+
</button>
|
|
6411
|
+
<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);">
|
|
6412
|
+
\u8FD0\u884C CLI \u547D\u4EE4\u8FDB\u884C\u66F4\u591A\u64CD\u4F5C\uFF1A
|
|
6413
|
+
<div style="font-family: var(--font-mono); margin-top: var(--space-xs); line-height: 1.8;">
|
|
6414
|
+
chattercatcher settings<br/>
|
|
6415
|
+
chattercatcher doctor<br/>
|
|
6416
|
+
chattercatcher index rebuild<br/>
|
|
6417
|
+
chattercatcher files add <path...><br/>
|
|
6418
|
+
chattercatcher export
|
|
6419
|
+
</div>
|
|
6420
|
+
</div>
|
|
6058
6421
|
</div>
|
|
6059
|
-
<aside>
|
|
6060
|
-
<section>
|
|
6061
|
-
<h2>\u7FA4\u804A</h2>
|
|
6062
|
-
<div id="chats" class="empty">\u6B63\u5728\u8BFB\u53D6...</div>
|
|
6063
|
-
</section>
|
|
6064
|
-
<section>
|
|
6065
|
-
<h2>\u6587\u4EF6\u5E93</h2>
|
|
6066
|
-
<div id="files" class="empty">\u6B63\u5728\u8BFB\u53D6...</div>
|
|
6067
|
-
</section>
|
|
6068
|
-
<section>
|
|
6069
|
-
<h2>\u89E3\u6790\u4EFB\u52A1</h2>
|
|
6070
|
-
<div id="file-jobs" class="empty">\u6B63\u5728\u8BFB\u53D6...</div>
|
|
6071
|
-
</section>
|
|
6072
|
-
<section>
|
|
6073
|
-
<h2>\u5B9A\u65F6\u4EFB\u52A1</h2>
|
|
6074
|
-
<div id="cron-jobs" class="empty">\u6B63\u5728\u8BFB\u53D6...</div>
|
|
6075
|
-
</section>
|
|
6076
|
-
<section>
|
|
6077
|
-
<h2>\u672C\u5730\u64CD\u4F5C</h2>
|
|
6078
|
-
<p><code>chattercatcher settings</code> \u4FEE\u6539\u914D\u7F6E\u3002</p>
|
|
6079
|
-
<p><code>chattercatcher files add <path...></code> \u5BFC\u5165\u6587\u672C\u3001DOCX \u6216 PDF \u6587\u4EF6\u3002</p>
|
|
6080
|
-
<p><code>chattercatcher doctor</code> \u68C0\u67E5\u98DE\u4E66\u3001\u6A21\u578B\u3001RAG \u548C\u672C\u5730\u5B58\u50A8\u3002</p>
|
|
6081
|
-
</section>
|
|
6082
|
-
</aside>
|
|
6083
6422
|
</div>
|
|
6084
|
-
</
|
|
6085
|
-
|
|
6086
|
-
const metrics = document.querySelector("#metrics");
|
|
6087
|
-
const messages = document.querySelector("#messages");
|
|
6088
|
-
const episodes = document.querySelector("#episodes");
|
|
6089
|
-
const chats = document.querySelector("#chats");
|
|
6090
|
-
const files = document.querySelector("#files");
|
|
6091
|
-
const fileJobs = document.querySelector("#file-jobs");
|
|
6092
|
-
const cronJobs = document.querySelector("#cron-jobs");
|
|
6093
|
-
const qaLogs = document.querySelector("#qa-logs");
|
|
6094
|
-
const processMessages = document.querySelector("#process-messages");
|
|
6095
|
-
const actionStatus = document.querySelector("#action-status");
|
|
6096
|
-
|
|
6097
|
-
let webActionToken = "__WEB_ACTION_TOKEN__";
|
|
6098
|
-
|
|
6099
|
-
function fmt(value) {
|
|
6100
|
-
return value == null || value === "" ? "-" : String(value);
|
|
6101
|
-
}
|
|
6423
|
+
</div>
|
|
6424
|
+
</main>
|
|
6102
6425
|
|
|
6103
|
-
|
|
6104
|
-
return fmt(value)
|
|
6105
|
-
.replaceAll("&", "&")
|
|
6106
|
-
.replaceAll("<", "<")
|
|
6107
|
-
.replaceAll(">", ">")
|
|
6108
|
-
.replaceAll('"', """);
|
|
6109
|
-
}
|
|
6426
|
+
<div id="toast-container" style="position: fixed; top: 24px; right: 24px; z-index: 1001; display: flex; flex-direction: column; gap: 12px;"></div>
|
|
6110
6427
|
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6428
|
+
<script>
|
|
6429
|
+
let currentView = "overview";
|
|
6430
|
+
let allMessages = [];
|
|
6431
|
+
let allEpisodes = [];
|
|
6432
|
+
let allFiles = [];
|
|
6433
|
+
let allFileJobs = [];
|
|
6434
|
+
let allCronJobs = [];
|
|
6435
|
+
let allQaLogs = [];
|
|
6436
|
+
let statusData = null;
|
|
6114
6437
|
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
}
|
|
6438
|
+
function fmt(value) { return value == null || value === "" ? "-" : String(value); }
|
|
6439
|
+
function escapeHtml(value) {
|
|
6440
|
+
return fmt(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
6441
|
+
}
|
|
6442
|
+
function isOpaqueId(value) { return /^(ou|oc|om|cli|on|un|uid)_?[a-z0-9]+/i.test(fmt(value)); }
|
|
6443
|
+
function formatDateTime(value) {
|
|
6444
|
+
var date = new Date(value);
|
|
6445
|
+
if (Number.isNaN(date.getTime())) return fmt(value);
|
|
6446
|
+
var pad = function(n) { return String(n).padStart(2, "0"); };
|
|
6447
|
+
return date.getFullYear() + "/" + pad(date.getMonth()+1) + "/" + pad(date.getDate()) + " " + pad(date.getHours()) + ":" + pad(date.getMinutes());
|
|
6448
|
+
}
|
|
6449
|
+
function displaySender(value) { return isOpaqueId(value) ? "\u7FA4\u6210\u5458" : fmt(value); }
|
|
6450
|
+
function displayChatName(value, platform) { return !isOpaqueId(value) ? fmt(value) : (platform === "feishu" ? "\u98DE\u4E66\u7FA4\u804A" : "\u7FA4\u804A"); }
|
|
6129
6451
|
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6452
|
+
function showToast(message, type) {
|
|
6453
|
+
type = type || "info";
|
|
6454
|
+
var container = document.getElementById("toast-container");
|
|
6455
|
+
var toast = document.createElement("div");
|
|
6456
|
+
toast.className = "toast toast-" + type;
|
|
6457
|
+
toast.textContent = message;
|
|
6458
|
+
container.appendChild(toast);
|
|
6459
|
+
setTimeout(function() {
|
|
6460
|
+
toast.style.opacity = "0"; toast.style.transform = "translateX(10px)";
|
|
6461
|
+
setTimeout(function() { toast.remove(); }, 300);
|
|
6462
|
+
}, 3000);
|
|
6463
|
+
}
|
|
6133
6464
|
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6465
|
+
function navigateTo(view) {
|
|
6466
|
+
document.querySelectorAll(".view").forEach(function(el) { el.classList.remove("active"); });
|
|
6467
|
+
document.querySelectorAll(".nav-item, .mobile-nav-item").forEach(function(el) { el.classList.remove("active"); });
|
|
6468
|
+
document.getElementById("view-" + view).classList.add("active");
|
|
6469
|
+
document.querySelectorAll('[data-view="' + view + '"]').forEach(function(el) { el.classList.add("active"); });
|
|
6470
|
+
currentView = view;
|
|
6471
|
+
window.scrollTo(0, 0);
|
|
6472
|
+
if (view === "messages") renderMessagesView();
|
|
6473
|
+
if (view === "episodes") renderEpisodesView();
|
|
6474
|
+
if (view === "files") renderFilesView();
|
|
6475
|
+
if (view === "tasks") renderTasksView();
|
|
6476
|
+
if (view === "qa-logs") renderQaLogsView();
|
|
6477
|
+
}
|
|
6138
6478
|
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
return "\u5F85\u542F\u52A8";
|
|
6143
|
-
}
|
|
6479
|
+
document.querySelectorAll(".nav-item, .mobile-nav-item").forEach(function(el) {
|
|
6480
|
+
el.addEventListener("click", function() { navigateTo(el.dataset.view); });
|
|
6481
|
+
});
|
|
6144
6482
|
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6483
|
+
function switchTab(tab) {
|
|
6484
|
+
document.querySelectorAll(".tab").forEach(function(el) { el.classList.remove("active"); });
|
|
6485
|
+
document.querySelector('[data-tab="' + tab + '"]').classList.add("active");
|
|
6486
|
+
document.getElementById("tab-file-jobs").style.display = tab === "file-jobs" ? "block" : "none";
|
|
6487
|
+
document.getElementById("tab-cron-jobs").style.display = tab === "cron-jobs" ? "block" : "none";
|
|
6488
|
+
if (tab === "file-jobs") renderFileJobs();
|
|
6489
|
+
if (tab === "cron-jobs") renderCronJobs();
|
|
6490
|
+
}
|
|
6149
6491
|
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
["\u7FA4\u804A", status.data.chats, "\u672C\u5730\u7FA4\u804A\u6570", ""],
|
|
6156
|
-
["\u6D88\u606F", status.data.messages, "\u5DF2\u5165\u5E93\u6D88\u606F", ""],
|
|
6157
|
-
["\u4F1A\u8BDD\u8BB0\u5FC6", status.data.episodes, "\u5DF2\u751F\u6210\u6458\u8981", ""],
|
|
6158
|
-
["\u6587\u4EF6", status.data.files, "\u6587\u4EF6\u77E5\u8BC6\u6E90", ""],
|
|
6159
|
-
].map(([label, value, note, extra]) => \`
|
|
6160
|
-
<div class="metric">
|
|
6161
|
-
<div class="label">\${escapeHtml(label)}</div>
|
|
6162
|
-
<div class="value \${extra}">\${escapeHtml(value)}</div>
|
|
6163
|
-
<div class="note">\${escapeHtml(note)}</div>
|
|
6164
|
-
</div>
|
|
6165
|
-
\`).join("");
|
|
6492
|
+
async function fetchJson(path) {
|
|
6493
|
+
var response = await fetch(path);
|
|
6494
|
+
if (!response.ok) {
|
|
6495
|
+
var body = await response.text();
|
|
6496
|
+
throw new Error(path + " " + response.status + " " + body);
|
|
6166
6497
|
}
|
|
6498
|
+
return response.json();
|
|
6499
|
+
}
|
|
6167
6500
|
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
}
|
|
6174
|
-
messages.className = "";
|
|
6175
|
-
messages.innerHTML = \`
|
|
6176
|
-
<div class="message-list">
|
|
6177
|
-
\${items.map((item) => \`
|
|
6178
|
-
<article class="message-item">
|
|
6179
|
-
<div class="message-meta">
|
|
6180
|
-
<span>\${escapeHtml(formatDateTime(item.sentAt))}</span>
|
|
6181
|
-
<span>\${escapeHtml(displaySender(item.senderName))}</span>
|
|
6182
|
-
<span>\${escapeHtml(displayChatName(item.chatName, item.platform))}</span>
|
|
6183
|
-
</div>
|
|
6184
|
-
<div class="message-body">\${escapeHtml(item.text)}</div>
|
|
6185
|
-
</article>
|
|
6186
|
-
\`).join("")}
|
|
6187
|
-
</div>
|
|
6188
|
-
\`;
|
|
6501
|
+
async function postJson(path, options) {
|
|
6502
|
+
var response = await fetch(path, Object.assign({ method: "POST" }, options || {}));
|
|
6503
|
+
var result = await response.json();
|
|
6504
|
+
if (!response.ok) {
|
|
6505
|
+
throw new Error(result.message || result.reason || "\u8BF7\u6C42\u5931\u8D25");
|
|
6189
6506
|
}
|
|
6507
|
+
return result;
|
|
6508
|
+
}
|
|
6190
6509
|
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
}
|
|
6197
|
-
episodes.className = "";
|
|
6198
|
-
episodes.innerHTML = \`
|
|
6199
|
-
<div class="message-list">
|
|
6200
|
-
\${items.map((item) => \`
|
|
6201
|
-
<article class="message-item">
|
|
6202
|
-
<div class="message-meta">
|
|
6203
|
-
<span>\${escapeHtml(formatDateTime(item.startedAt))} - \${escapeHtml(formatDateTime(item.endedAt))}</span>
|
|
6204
|
-
<span>\${escapeHtml(displayChatName(item.chatName, "feishu"))}</span>
|
|
6205
|
-
<span>\${escapeHtml(item.messageCount)} \u6761\u6D88\u606F</span>
|
|
6206
|
-
</div>
|
|
6207
|
-
<div class="message-body">\${escapeHtml(item.summary)}</div>
|
|
6208
|
-
</article>
|
|
6209
|
-
\`).join("")}
|
|
6210
|
-
</div>
|
|
6211
|
-
\`;
|
|
6510
|
+
async function deleteJson(path) {
|
|
6511
|
+
var response = await fetch(path, { method: "DELETE" });
|
|
6512
|
+
var result = await response.json();
|
|
6513
|
+
if (!response.ok) {
|
|
6514
|
+
throw new Error(result.message || result.reason || "\u8BF7\u6C42\u5931\u8D25");
|
|
6212
6515
|
}
|
|
6516
|
+
return result;
|
|
6517
|
+
}
|
|
6213
6518
|
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
chats
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6519
|
+
function renderMetrics(status) {
|
|
6520
|
+
var gatewayClass = status.gateway.configured ? "status-dot online" : "status-dot offline";
|
|
6521
|
+
var gatewayText = status.gateway.connection === "running" ? "\u8FD0\u884C\u4E2D" : (!status.gateway.configured ? "\u672A\u914D\u7F6E" : "\u5F85\u542F\u52A8");
|
|
6522
|
+
var metricsHtml = [
|
|
6523
|
+
["Gateway", gatewayText, "\u98DE\u4E66\u957F\u8FDE\u63A5", gatewayClass],
|
|
6524
|
+
["\u7248\u672C", status.version || "unknown", "\u5F53\u524D\u8FD0\u884C\u7248\u672C", ""],
|
|
6525
|
+
["\u7FA4\u804A", status.data.chats, "\u672C\u5730\u7FA4\u804A\u6570", ""],
|
|
6526
|
+
["\u6D88\u606F", status.data.messages, "\u5DF2\u5165\u5E93\u6D88\u606F", ""],
|
|
6527
|
+
["\u4F1A\u8BDD\u8BB0\u5FC6", status.data.episodes, "\u5DF2\u751F\u6210\u6458\u8981", ""],
|
|
6528
|
+
["\u6587\u4EF6", status.data.files, "\u6587\u4EF6\u77E5\u8BC6\u6E90", ""],
|
|
6529
|
+
["\u95EE\u7B54", status.data.qaLogs, "\u95EE\u7B54\u8BB0\u5F55", ""],
|
|
6530
|
+
["\u4EFB\u52A1", status.data.cronJobs, "\u5B9A\u65F6\u4EFB\u52A1", ""]
|
|
6531
|
+
].map(function(item) {
|
|
6532
|
+
var label = item[0], value = item[1], note = item[2], dotClass = item[3];
|
|
6533
|
+
return '<div class="metric-card glass"><div class="metric-label">' + escapeHtml(label) + '</div>' +
|
|
6534
|
+
'<div class="metric-value">' + (dotClass ? '<span class="' + dotClass + '" style="margin-right:8px;"></span>' : '') + escapeHtml(value) + '</div>' +
|
|
6535
|
+
'<div class="metric-note">' + escapeHtml(note) + '</div></div>';
|
|
6536
|
+
}).join("");
|
|
6537
|
+
document.getElementById("metrics").innerHTML = metricsHtml;
|
|
6538
|
+
document.getElementById("gateway-indicator").className = gatewayClass;
|
|
6539
|
+
document.getElementById("gateway-status-text").textContent = "Gateway " + gatewayText;
|
|
6540
|
+
document.getElementById("version-text").textContent = "v" + (status.version || "unknown");
|
|
6541
|
+
}
|
|
6235
6542
|
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
</
|
|
6259
|
-
|
|
6543
|
+
function renderSystemStatus(status) {
|
|
6544
|
+
var gateway = status.gateway;
|
|
6545
|
+
var html = '<div style="display:flex;flex-direction:column;gap:var(--space-md);">';
|
|
6546
|
+
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>';
|
|
6547
|
+
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>';
|
|
6548
|
+
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>';
|
|
6549
|
+
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>';
|
|
6550
|
+
html += '<div class="settings-item"><div><div class="settings-label">\u5411\u91CF\u68C0\u7D22</div></div><div class="settings-value">SQLite embedding</div></div>';
|
|
6551
|
+
html += '</div>';
|
|
6552
|
+
document.getElementById("system-status").innerHTML = html;
|
|
6553
|
+
}
|
|
6554
|
+
|
|
6555
|
+
function renderRecentMessages(items) {
|
|
6556
|
+
var el = document.getElementById("recent-messages");
|
|
6557
|
+
if (!items || items.length === 0) {
|
|
6558
|
+
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>';
|
|
6559
|
+
return;
|
|
6560
|
+
}
|
|
6561
|
+
var html = '<div class="message-list">';
|
|
6562
|
+
for (var i = 0; i < Math.min(items.length, 5); i++) {
|
|
6563
|
+
var item = items[i];
|
|
6564
|
+
html += '<div class="message-card"><div class="message-meta">' +
|
|
6565
|
+
'<span>' + escapeHtml(formatDateTime(item.sentAt)) + '</span>' +
|
|
6566
|
+
'<span>' + escapeHtml(displaySender(item.senderName)) + '</span>' +
|
|
6567
|
+
'<span>' + escapeHtml(displayChatName(item.chatName, item.platform)) + '</span>' +
|
|
6568
|
+
'</div><div class="message-text">' + escapeHtml(item.text) + '</div></div>';
|
|
6260
6569
|
}
|
|
6570
|
+
html += '</div>';
|
|
6571
|
+
el.innerHTML = html;
|
|
6572
|
+
}
|
|
6261
6573
|
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
<td>
|
|
6276
|
-
<div>\${escapeHtml(item.fileName)}</div>
|
|
6277
|
-
<div class="path" title="\${escapeHtml(item.id)}">ID: \${escapeHtml(item.id)}</div>
|
|
6278
|
-
<div class="path" title="\${escapeHtml(item.error || item.storedPath)}">\${escapeHtml(item.error || item.storedPath)}</div>
|
|
6279
|
-
</td>
|
|
6280
|
-
<td>\${escapeHtml(item.status)}</td>
|
|
6281
|
-
</tr>
|
|
6282
|
-
\`).join("")}
|
|
6283
|
-
</tbody>
|
|
6284
|
-
</table>
|
|
6285
|
-
\`;
|
|
6574
|
+
function renderRecentEpisodes(items) {
|
|
6575
|
+
var el = document.getElementById("recent-episodes");
|
|
6576
|
+
if (!items || items.length === 0) {
|
|
6577
|
+
el.innerHTML = '<div class="empty-state">\u8FD8\u6CA1\u6709\u4F1A\u8BDD\u8BB0\u5FC6\u3002</div>';
|
|
6578
|
+
return;
|
|
6579
|
+
}
|
|
6580
|
+
var html = '<div class="message-list">';
|
|
6581
|
+
for (var i = 0; i < Math.min(items.length, 3); i++) {
|
|
6582
|
+
var item = items[i];
|
|
6583
|
+
html += '<div class="episode-card"><div class="message-meta">' +
|
|
6584
|
+
'<span>' + escapeHtml(formatDateTime(item.startedAt)) + " - " + escapeHtml(formatDateTime(item.endedAt)) + '</span>' +
|
|
6585
|
+
'<span>' + escapeHtml(item.messageCount) + ' \u6761\u6D88\u606F</span>' +
|
|
6586
|
+
'</div><div class="message-text">' + escapeHtml(item.summary) + '</div></div>';
|
|
6286
6587
|
}
|
|
6588
|
+
html += '</div>';
|
|
6589
|
+
el.innerHTML = html;
|
|
6590
|
+
}
|
|
6287
6591
|
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
</td>
|
|
6310
|
-
<td>\${escapeHtml(item.status)}</td>
|
|
6311
|
-
</tr>
|
|
6312
|
-
\`).join("")}
|
|
6313
|
-
</tbody>
|
|
6314
|
-
</table>
|
|
6315
|
-
\`;
|
|
6592
|
+
function renderMessagesView() {
|
|
6593
|
+
var el = document.getElementById("messages-list");
|
|
6594
|
+
if (!allMessages || allMessages.length === 0) {
|
|
6595
|
+
el.innerHTML = '<div class="empty-state">\u8FD8\u6CA1\u6709\u6D88\u606F\u3002</div>';
|
|
6596
|
+
return;
|
|
6597
|
+
}
|
|
6598
|
+
var searchInput = document.getElementById("message-search");
|
|
6599
|
+
var searchTerm = searchInput ? searchInput.value.toLowerCase() : "";
|
|
6600
|
+
var filtered = searchTerm ? allMessages.filter(function(m) { return (m.text || "").toLowerCase().indexOf(searchTerm) !== -1; }) : allMessages;
|
|
6601
|
+
if (filtered.length === 0) {
|
|
6602
|
+
el.innerHTML = '<div class="empty-state">\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6D88\u606F\u3002</div>';
|
|
6603
|
+
return;
|
|
6604
|
+
}
|
|
6605
|
+
var html = '<div class="message-list">';
|
|
6606
|
+
for (var i = 0; i < Math.min(filtered.length, 50); i++) {
|
|
6607
|
+
var item = filtered[i];
|
|
6608
|
+
html += '<div class="message-card"><div class="message-meta">' +
|
|
6609
|
+
'<span>' + escapeHtml(formatDateTime(item.sentAt)) + '</span>' +
|
|
6610
|
+
'<span>' + escapeHtml(displaySender(item.senderName)) + '</span>' +
|
|
6611
|
+
'<span>' + escapeHtml(displayChatName(item.chatName, item.platform)) + '</span>' +
|
|
6612
|
+
'</div><div class="message-text" style="-webkit-line-clamp:4;">' + escapeHtml(item.text) + '</div></div>';
|
|
6316
6613
|
}
|
|
6614
|
+
html += '</div>';
|
|
6615
|
+
if (filtered.length > 50) {
|
|
6616
|
+
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>';
|
|
6617
|
+
}
|
|
6618
|
+
el.innerHTML = html;
|
|
6619
|
+
}
|
|
6317
6620
|
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
const rows = items.map((item) => {
|
|
6326
|
-
const citationCount = Array.isArray(item.citations) ? item.citations.length : 0;
|
|
6327
|
-
return [
|
|
6328
|
-
'<article class="message-item">',
|
|
6329
|
-
' <div class="message-meta">',
|
|
6330
|
-
" <span>" + escapeHtml(formatDateTime(item.createdAt)) + "</span>",
|
|
6331
|
-
" <span>" + escapeHtml(item.status) + "</span>",
|
|
6332
|
-
" <span>" + escapeHtml(citationCount) + " \u6761\u5F15\u7528</span>",
|
|
6333
|
-
" </div>",
|
|
6334
|
-
" <div class=\\"message-body\\"><strong>\u95EE\uFF1A</strong>" + escapeHtml(item.question) + "</div>",
|
|
6335
|
-
" <div class=\\"message-body\\"><strong>\u7B54\uFF1A</strong>" + escapeHtml(item.answer) + "</div>",
|
|
6336
|
-
"</article>",
|
|
6337
|
-
].join("\\n");
|
|
6338
|
-
});
|
|
6339
|
-
qaLogs.innerHTML = [
|
|
6340
|
-
'<div class="message-list">',
|
|
6341
|
-
rows.join(""),
|
|
6342
|
-
"</div>",
|
|
6343
|
-
].join("\\n");
|
|
6621
|
+
function filterMessages() { renderMessagesView(); }
|
|
6622
|
+
|
|
6623
|
+
function renderEpisodesView() {
|
|
6624
|
+
var el = document.getElementById("episodes-list");
|
|
6625
|
+
if (!allEpisodes || allEpisodes.length === 0) {
|
|
6626
|
+
el.innerHTML = '<div class="empty-state">\u8FD8\u6CA1\u6709\u4F1A\u8BDD\u8BB0\u5FC6\u3002</div>';
|
|
6627
|
+
return;
|
|
6344
6628
|
}
|
|
6629
|
+
var html = '<div class="timeline">';
|
|
6630
|
+
for (var i = 0; i < allEpisodes.length; i++) {
|
|
6631
|
+
var item = allEpisodes[i];
|
|
6632
|
+
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>';
|
|
6633
|
+
}
|
|
6634
|
+
html += '</div>';
|
|
6635
|
+
el.innerHTML = html;
|
|
6636
|
+
}
|
|
6345
6637
|
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
}
|
|
6352
|
-
return response.json();
|
|
6638
|
+
function renderFilesView() {
|
|
6639
|
+
var el = document.getElementById("files-list");
|
|
6640
|
+
if (!allFiles || allFiles.length === 0) {
|
|
6641
|
+
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>';
|
|
6642
|
+
return;
|
|
6353
6643
|
}
|
|
6644
|
+
var html = '<div class="grid-2">';
|
|
6645
|
+
for (var i = 0; i < allFiles.length; i++) {
|
|
6646
|
+
var item = allFiles[i];
|
|
6647
|
+
html += '<div class="file-card glass"><div class="file-icon">' +
|
|
6648
|
+
'<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>' +
|
|
6649
|
+
'</div><div style="font-weight:600;margin-bottom:4px;">' + escapeHtml(item.fileName) + '</div>' +
|
|
6650
|
+
'<div style="font-size:13px;color:var(--text-muted);margin-bottom:4px;" class="truncate">' + escapeHtml(item.storedPath) + '</div>' +
|
|
6651
|
+
'<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>';
|
|
6652
|
+
}
|
|
6653
|
+
html += '</div>';
|
|
6654
|
+
el.innerHTML = html;
|
|
6655
|
+
}
|
|
6354
6656
|
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6657
|
+
function renderTasksView() {
|
|
6658
|
+
var activeTab = document.querySelector(".tab.active");
|
|
6659
|
+
var tab = activeTab ? activeTab.dataset.tab : "file-jobs";
|
|
6660
|
+
if (tab === "file-jobs") renderFileJobs();
|
|
6661
|
+
else renderCronJobs();
|
|
6662
|
+
}
|
|
6663
|
+
|
|
6664
|
+
function renderFileJobs() {
|
|
6665
|
+
var el = document.getElementById("file-jobs-list");
|
|
6666
|
+
if (!allFileJobs || allFileJobs.length === 0) {
|
|
6667
|
+
el.innerHTML = '<div class="empty-state">\u8FD8\u6CA1\u6709\u6587\u4EF6\u89E3\u6790\u4EFB\u52A1\u3002</div>';
|
|
6668
|
+
return;
|
|
6358
6669
|
}
|
|
6670
|
+
var html = '<table class="data-table"><thead><tr><th>\u6587\u4EF6</th><th>\u72B6\u6001</th><th>\u4FE1\u606F</th></tr></thead><tbody>';
|
|
6671
|
+
for (var i = 0; i < allFileJobs.length; i++) {
|
|
6672
|
+
var item = allFileJobs[i];
|
|
6673
|
+
var tagClass = item.status === 'indexed' ? 'tag-success' : item.status === 'failed' ? 'tag-error' : 'tag-warning';
|
|
6674
|
+
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>' +
|
|
6675
|
+
'<td><span class="tag ' + tagClass + '">' + escapeHtml(item.status) + '</span></td>' +
|
|
6676
|
+
'<td style="font-size:13px;color:var(--text-muted);">' + escapeHtml(item.error || "") + '</td></tr>';
|
|
6677
|
+
}
|
|
6678
|
+
html += '</tbody></table>';
|
|
6679
|
+
el.innerHTML = html;
|
|
6680
|
+
}
|
|
6359
6681
|
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
}
|
|
6682
|
+
function renderCronJobs() {
|
|
6683
|
+
var el = document.getElementById("cron-jobs-list");
|
|
6684
|
+
if (!allCronJobs || allCronJobs.length === 0) {
|
|
6685
|
+
el.innerHTML = '<div class="empty-state">\u8FD8\u6CA1\u6709\u5B9A\u65F6\u4EFB\u52A1\u3002</div>';
|
|
6686
|
+
return;
|
|
6366
6687
|
}
|
|
6688
|
+
var html = '<table class="data-table"><thead><tr><th>\u4EFB\u52A1</th><th>\u72B6\u6001</th><th>\u64CD\u4F5C</th></tr></thead><tbody>';
|
|
6689
|
+
for (var i = 0; i < allCronJobs.length; i++) {
|
|
6690
|
+
var item = allCronJobs[i];
|
|
6691
|
+
var tagClass = item.status === 'active' ? 'tag-success' : 'tag-warning';
|
|
6692
|
+
html += '<tr><td><div style="font-weight:500;">' + escapeHtml(item.schedule) + '</div>' +
|
|
6693
|
+
'<div style="font-size:13px;color:var(--text-muted);" class="truncate-2">' + escapeHtml(item.prompt) + '</div>' +
|
|
6694
|
+
'<div style="font-size:12px;color:var(--text-muted);">\u4E0B\u6B21: ' + escapeHtml(formatDateTime(item.nextRunAt)) + '</div>' +
|
|
6695
|
+
(item.lastError ? '<div style="font-size:12px;color:var(--danger);margin-top:4px;">' + escapeHtml(item.lastError) + '</div>' : '') +
|
|
6696
|
+
'</td><td><span class="tag ' + tagClass + '">' + escapeHtml(item.status) + '</span></td><td>' +
|
|
6697
|
+
(item.status === "active" ? '<button class="btn btn-sm btn-danger" data-delete-cron-job="' + escapeHtml(item.id) + '">\u5220\u9664</button>' : '-') +
|
|
6698
|
+
'</td></tr>';
|
|
6699
|
+
}
|
|
6700
|
+
html += '</tbody></table>';
|
|
6701
|
+
el.innerHTML = html;
|
|
6702
|
+
}
|
|
6367
6703
|
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6704
|
+
function renderQaLogsView() {
|
|
6705
|
+
var el = document.getElementById("qa-logs-list");
|
|
6706
|
+
if (!allQaLogs || allQaLogs.length === 0) {
|
|
6707
|
+
el.innerHTML = '<div class="empty-state">\u8FD8\u6CA1\u6709\u95EE\u7B54\u65E5\u5FD7\u3002</div>';
|
|
6708
|
+
return;
|
|
6709
|
+
}
|
|
6710
|
+
var html = '';
|
|
6711
|
+
for (var i = 0; i < allQaLogs.length; i++) {
|
|
6712
|
+
var item = allQaLogs[i];
|
|
6713
|
+
var citationCount = Array.isArray(item.citations) ? item.citations.length : 0;
|
|
6714
|
+
var statusClass = item.status === 'success' ? 'tag-success' : 'tag-warning';
|
|
6715
|
+
html += '<div class="qa-card"><div class="message-meta" style="margin-bottom:var(--space-sm);">' +
|
|
6716
|
+
'<span>' + escapeHtml(formatDateTime(item.createdAt)) + '</span>' +
|
|
6717
|
+
'<span class="tag ' + statusClass + '">' + escapeHtml(item.status) + '</span>' +
|
|
6718
|
+
'<span>' + citationCount + ' \u6761\u5F15\u7528</span></div>' +
|
|
6719
|
+
'<div class="qa-question">' + escapeHtml(item.question) + '</div>' +
|
|
6720
|
+
'<div class="qa-answer">' + escapeHtml(item.answer) + '</div></div>';
|
|
6379
6721
|
}
|
|
6722
|
+
el.innerHTML = html;
|
|
6723
|
+
}
|
|
6380
6724
|
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
return;
|
|
6393
|
-
}
|
|
6725
|
+
function renderSettings(status) {
|
|
6726
|
+
var el = document.getElementById("settings-config");
|
|
6727
|
+
var html = '<h3 style="font-size:16px;font-weight:600;margin-bottom:var(--space-md);">\u7CFB\u7EDF\u914D\u7F6E</h3>';
|
|
6728
|
+
html += '<div style="display:flex;flex-direction:column;">';
|
|
6729
|
+
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>';
|
|
6730
|
+
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>';
|
|
6731
|
+
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>';
|
|
6732
|
+
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>';
|
|
6733
|
+
html += '</div>';
|
|
6734
|
+
el.innerHTML = html;
|
|
6735
|
+
}
|
|
6394
6736
|
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
}
|
|
6400
|
-
await load();
|
|
6401
|
-
} catch (error) {
|
|
6402
|
-
actionStatus.textContent = error instanceof Error ? error.message : String(error);
|
|
6403
|
-
} finally {
|
|
6404
|
-
processMessages.disabled = false;
|
|
6405
|
-
}
|
|
6406
|
-
}
|
|
6737
|
+
async function loadSection(path, setter) {
|
|
6738
|
+
try { setter(await fetchJson(path)); }
|
|
6739
|
+
catch (error) { console.error("\u52A0\u8F7D\u5931\u8D25:", path, error); }
|
|
6740
|
+
}
|
|
6407
6741
|
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
actionStatus.textContent = "\u6B63\u5728\u5220\u9664\u5B9A\u65F6\u4EFB\u52A1...";
|
|
6415
|
-
try {
|
|
6416
|
-
const response = await fetch(\`/api/cron-jobs/\${encodeURIComponent(id)}\`, {
|
|
6417
|
-
method: "DELETE",
|
|
6418
|
-
headers: { "x-chattercatcher-web-token": webActionToken },
|
|
6419
|
-
});
|
|
6420
|
-
const result = await response.json();
|
|
6421
|
-
actionStatus.textContent = result.ok ? "\u5B9A\u65F6\u4EFB\u52A1\u5DF2\u5220\u9664\u3002" : result.message || "\u5220\u9664\u5931\u8D25\u3002";
|
|
6422
|
-
await load();
|
|
6423
|
-
} catch (error) {
|
|
6424
|
-
actionStatus.textContent = error instanceof Error ? error.message : String(error);
|
|
6425
|
-
} finally {
|
|
6426
|
-
target.removeAttribute("disabled");
|
|
6427
|
-
}
|
|
6742
|
+
async function load() {
|
|
6743
|
+
await loadSection("/api/status", function(data) {
|
|
6744
|
+
statusData = data;
|
|
6745
|
+
renderMetrics(data);
|
|
6746
|
+
renderSystemStatus(data);
|
|
6747
|
+
renderSettings(data);
|
|
6428
6748
|
});
|
|
6749
|
+
await loadSection("/api/messages/recent?limit=50", function(data) { allMessages = data.items || []; renderRecentMessages(allMessages); });
|
|
6750
|
+
await loadSection("/api/episodes?limit=20", function(data) { allEpisodes = data.items || []; renderRecentEpisodes(allEpisodes); });
|
|
6751
|
+
await loadSection("/api/files", function(data) { allFiles = data.items || []; });
|
|
6752
|
+
await loadSection("/api/file-jobs", function(data) { allFileJobs = data.items || []; });
|
|
6753
|
+
await loadSection("/api/qa-logs?limit=20", function(data) { allQaLogs = data.items || []; });
|
|
6754
|
+
await loadSection("/api/cron-jobs", function(data) { allCronJobs = data.items || []; });
|
|
6755
|
+
if (currentView === "messages") renderMessagesView();
|
|
6756
|
+
if (currentView === "episodes") renderEpisodesView();
|
|
6757
|
+
if (currentView === "files") renderFilesView();
|
|
6758
|
+
if (currentView === "tasks") renderTasksView();
|
|
6759
|
+
if (currentView === "qa-logs") renderQaLogsView();
|
|
6760
|
+
}
|
|
6429
6761
|
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6762
|
+
async function processNow() {
|
|
6763
|
+
var btn = document.getElementById("btn-process-messages");
|
|
6764
|
+
if (btn) { btn.disabled = true; }
|
|
6765
|
+
showToast("\u6B63\u5728\u5904\u7406\u6D88\u606F\u7D22\u5F15...", "info");
|
|
6766
|
+
try {
|
|
6767
|
+
var result = await postJson("/api/process/messages");
|
|
6768
|
+
if (result.status === "skipped") { showToast(result.reason, "warning"); }
|
|
6769
|
+
else { showToast("\u5904\u7406\u5B8C\u6210\uFF1Achunks=" + result.chunks + ", vectors=" + result.vectors, "success"); }
|
|
6770
|
+
await load();
|
|
6771
|
+
} catch (error) {
|
|
6772
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
6773
|
+
} finally {
|
|
6774
|
+
if (btn) { btn.disabled = false; }
|
|
6775
|
+
}
|
|
6776
|
+
}
|
|
6777
|
+
|
|
6778
|
+
document.addEventListener("click", async function(event) {
|
|
6779
|
+
var target = event.target;
|
|
6780
|
+
if (!(target instanceof HTMLElement)) return;
|
|
6781
|
+
var id = target.dataset.deleteCronJob;
|
|
6782
|
+
if (!id) return;
|
|
6783
|
+
target.disabled = true;
|
|
6784
|
+
showToast("\u6B63\u5728\u5220\u9664\u5B9A\u65F6\u4EFB\u52A1...", "info");
|
|
6785
|
+
try {
|
|
6786
|
+
var result = await deleteJson("/api/cron-jobs/" + encodeURIComponent(id));
|
|
6787
|
+
showToast(result.ok ? "\u5B9A\u65F6\u4EFB\u52A1\u5DF2\u5220\u9664" : (result.message || "\u5220\u9664\u5931\u8D25"), result.ok ? "success" : "error");
|
|
6788
|
+
await load();
|
|
6789
|
+
} catch (error) {
|
|
6790
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
6791
|
+
}
|
|
6792
|
+
});
|
|
6793
|
+
|
|
6794
|
+
void load();
|
|
6795
|
+
setInterval(function() { if (document.visibilityState === "visible") void load(); }, 5000);
|
|
6796
|
+
</script>
|
|
6797
|
+
</body>
|
|
6439
6798
|
</html>`;
|
|
6440
6799
|
}
|
|
6441
6800
|
function parseLimit(value, fallback, max) {
|
|
@@ -6445,12 +6804,22 @@ function parseLimit(value, fallback, max) {
|
|
|
6445
6804
|
function getWebActionToken(secrets) {
|
|
6446
6805
|
return secrets.web.actionToken;
|
|
6447
6806
|
}
|
|
6448
|
-
function
|
|
6449
|
-
return
|
|
6807
|
+
function getWebActionCookie(token) {
|
|
6808
|
+
return `chattercatcher_web_token=${encodeURIComponent(token)}; Path=/; HttpOnly; SameSite=Strict`;
|
|
6809
|
+
}
|
|
6810
|
+
function parseCookies(header) {
|
|
6811
|
+
const value = Array.isArray(header) ? header.join("; ") : header;
|
|
6812
|
+
if (!value) return {};
|
|
6813
|
+
const cookies = {};
|
|
6814
|
+
for (const part of value.split(";")) {
|
|
6815
|
+
const [rawName, ...rawValue] = part.trim().split("=");
|
|
6816
|
+
if (!rawName || rawValue.length === 0) continue;
|
|
6817
|
+
cookies[rawName] = decodeURIComponent(rawValue.join("="));
|
|
6818
|
+
}
|
|
6819
|
+
return cookies;
|
|
6450
6820
|
}
|
|
6451
6821
|
function isAuthorizedWebAction(request, token) {
|
|
6452
|
-
|
|
6453
|
-
return provided === token;
|
|
6822
|
+
return parseCookies(request.headers.cookie).chattercatcher_web_token === token;
|
|
6454
6823
|
}
|
|
6455
6824
|
function createWebApp(config, options = {}) {
|
|
6456
6825
|
const app = Fastify({ logger: false });
|
|
@@ -6578,7 +6947,8 @@ function createWebApp(config, options = {}) {
|
|
|
6578
6947
|
app.get("/", async (_request, reply) => {
|
|
6579
6948
|
await tokenReady;
|
|
6580
6949
|
reply.type("text/html; charset=utf-8");
|
|
6581
|
-
|
|
6950
|
+
reply.header("set-cookie", getWebActionCookie(webActionToken));
|
|
6951
|
+
return buildHtml();
|
|
6582
6952
|
});
|
|
6583
6953
|
return app;
|
|
6584
6954
|
}
|