@cicctencent/agent-server 0.2.41 → 0.2.42

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.
Files changed (112) hide show
  1. package/README.md +17 -1
  2. package/dist/cjs/agent/agent-server.d.ts.map +1 -1
  3. package/dist/cjs/agent/agent-server.js +6 -10
  4. package/dist/cjs/agent/agent-server.js.map +1 -1
  5. package/dist/cjs/doc-generator/config-loader.d.ts +27 -0
  6. package/dist/cjs/doc-generator/config-loader.d.ts.map +1 -0
  7. package/dist/cjs/doc-generator/config-loader.js +200 -0
  8. package/dist/cjs/doc-generator/config-loader.js.map +1 -0
  9. package/dist/cjs/doc-generator/docx-renderer.d.ts +14 -0
  10. package/dist/cjs/doc-generator/docx-renderer.d.ts.map +1 -0
  11. package/dist/cjs/doc-generator/docx-renderer.js +354 -0
  12. package/dist/cjs/doc-generator/docx-renderer.js.map +1 -0
  13. package/dist/cjs/doc-generator/index.d.ts +23 -0
  14. package/dist/cjs/doc-generator/index.d.ts.map +1 -0
  15. package/dist/cjs/doc-generator/index.js +251 -0
  16. package/dist/cjs/doc-generator/index.js.map +1 -0
  17. package/dist/cjs/doc-generator/pdf-renderer.d.ts +14 -0
  18. package/dist/cjs/doc-generator/pdf-renderer.d.ts.map +1 -0
  19. package/dist/cjs/doc-generator/pdf-renderer.js +378 -0
  20. package/dist/cjs/doc-generator/pdf-renderer.js.map +1 -0
  21. package/dist/cjs/doc-generator/pptx-renderer.d.ts +14 -0
  22. package/dist/cjs/doc-generator/pptx-renderer.d.ts.map +1 -0
  23. package/dist/cjs/doc-generator/pptx-renderer.js +649 -0
  24. package/dist/cjs/doc-generator/pptx-renderer.js.map +1 -0
  25. package/dist/cjs/doc-generator/report-renderer.d.ts +88 -0
  26. package/dist/cjs/doc-generator/report-renderer.d.ts.map +1 -0
  27. package/dist/cjs/doc-generator/report-renderer.js +699 -0
  28. package/dist/cjs/doc-generator/report-renderer.js.map +1 -0
  29. package/dist/cjs/doc-generator/types.d.ts +193 -0
  30. package/dist/cjs/doc-generator/types.d.ts.map +1 -0
  31. package/dist/cjs/doc-generator/types.js +7 -0
  32. package/dist/cjs/doc-generator/types.js.map +1 -0
  33. package/dist/cjs/index.d.ts +4 -0
  34. package/dist/cjs/index.d.ts.map +1 -1
  35. package/dist/cjs/index.js +45 -36
  36. package/dist/cjs/index.js.map +1 -1
  37. package/dist/cjs/prompt/prompt-builder.d.ts +1 -1
  38. package/dist/cjs/prompt/prompt-builder.d.ts.map +1 -1
  39. package/dist/cjs/prompt/prompt-builder.js +14 -1
  40. package/dist/cjs/prompt/prompt-builder.js.map +1 -1
  41. package/dist/cjs/prompt/shared-fragments.d.ts +3 -3
  42. package/dist/cjs/prompt/shared-fragments.d.ts.map +1 -1
  43. package/dist/cjs/prompt/shared-fragments.js +5 -3
  44. package/dist/cjs/prompt/shared-fragments.js.map +1 -1
  45. package/dist/cjs/tool/builtin.d.ts +10 -3
  46. package/dist/cjs/tool/builtin.d.ts.map +1 -1
  47. package/dist/cjs/tool/builtin.js +10 -3
  48. package/dist/cjs/tool/builtin.js.map +1 -1
  49. package/dist/cjs/tool/tools/docgen.d.ts +5 -0
  50. package/dist/cjs/tool/tools/docgen.d.ts.map +1 -0
  51. package/dist/cjs/tool/tools/docgen.js +388 -0
  52. package/dist/cjs/tool/tools/docgen.js.map +1 -0
  53. package/dist/cjs/tool/tools/sandbox.js +2 -2
  54. package/dist/cjs/tool/tools/sandbox.js.map +1 -1
  55. package/dist/cjs/tool/tools/web.js +2 -2
  56. package/dist/cjs/tool/tools/web.js.map +1 -1
  57. package/dist/esm/agent/agent-server.d.ts.map +1 -1
  58. package/dist/esm/agent/agent-server.js +6 -10
  59. package/dist/esm/agent/agent-server.js.map +1 -1
  60. package/dist/esm/doc-generator/config-loader.d.ts +27 -0
  61. package/dist/esm/doc-generator/config-loader.d.ts.map +1 -0
  62. package/dist/esm/doc-generator/config-loader.js +160 -0
  63. package/dist/esm/doc-generator/config-loader.js.map +1 -0
  64. package/dist/esm/doc-generator/docx-renderer.d.ts +14 -0
  65. package/dist/esm/doc-generator/docx-renderer.d.ts.map +1 -0
  66. package/dist/esm/doc-generator/docx-renderer.js +318 -0
  67. package/dist/esm/doc-generator/docx-renderer.js.map +1 -0
  68. package/dist/esm/doc-generator/index.d.ts +23 -0
  69. package/dist/esm/doc-generator/index.d.ts.map +1 -0
  70. package/dist/esm/doc-generator/index.js +212 -0
  71. package/dist/esm/doc-generator/index.js.map +1 -0
  72. package/dist/esm/doc-generator/pdf-renderer.d.ts +14 -0
  73. package/dist/esm/doc-generator/pdf-renderer.d.ts.map +1 -0
  74. package/dist/esm/doc-generator/pdf-renderer.js +339 -0
  75. package/dist/esm/doc-generator/pdf-renderer.js.map +1 -0
  76. package/dist/esm/doc-generator/pptx-renderer.d.ts +14 -0
  77. package/dist/esm/doc-generator/pptx-renderer.d.ts.map +1 -0
  78. package/dist/esm/doc-generator/pptx-renderer.js +643 -0
  79. package/dist/esm/doc-generator/pptx-renderer.js.map +1 -0
  80. package/dist/esm/doc-generator/report-renderer.d.ts +88 -0
  81. package/dist/esm/doc-generator/report-renderer.d.ts.map +1 -0
  82. package/dist/esm/doc-generator/report-renderer.js +663 -0
  83. package/dist/esm/doc-generator/report-renderer.js.map +1 -0
  84. package/dist/esm/doc-generator/types.d.ts +193 -0
  85. package/dist/esm/doc-generator/types.d.ts.map +1 -0
  86. package/dist/esm/doc-generator/types.js +6 -0
  87. package/dist/esm/doc-generator/types.js.map +1 -0
  88. package/dist/esm/index.d.ts +4 -0
  89. package/dist/esm/index.d.ts.map +1 -1
  90. package/dist/esm/index.js +3 -0
  91. package/dist/esm/index.js.map +1 -1
  92. package/dist/esm/prompt/prompt-builder.d.ts +1 -1
  93. package/dist/esm/prompt/prompt-builder.d.ts.map +1 -1
  94. package/dist/esm/prompt/prompt-builder.js +14 -1
  95. package/dist/esm/prompt/prompt-builder.js.map +1 -1
  96. package/dist/esm/prompt/shared-fragments.d.ts +3 -3
  97. package/dist/esm/prompt/shared-fragments.d.ts.map +1 -1
  98. package/dist/esm/prompt/shared-fragments.js +5 -3
  99. package/dist/esm/prompt/shared-fragments.js.map +1 -1
  100. package/dist/esm/tool/builtin.d.ts +10 -3
  101. package/dist/esm/tool/builtin.d.ts.map +1 -1
  102. package/dist/esm/tool/builtin.js +10 -3
  103. package/dist/esm/tool/builtin.js.map +1 -1
  104. package/dist/esm/tool/tools/docgen.d.ts +5 -0
  105. package/dist/esm/tool/tools/docgen.d.ts.map +1 -0
  106. package/dist/esm/tool/tools/docgen.js +352 -0
  107. package/dist/esm/tool/tools/docgen.js.map +1 -0
  108. package/dist/esm/tool/tools/sandbox.js +2 -2
  109. package/dist/esm/tool/tools/sandbox.js.map +1 -1
  110. package/dist/esm/tool/tools/web.js +2 -2
  111. package/dist/esm/tool/tools/web.js.map +1 -1
  112. package/package.json +7 -2
@@ -0,0 +1,699 @@
1
+ "use strict";
2
+ /**
3
+ * @file report-renderer.ts — HTML 报告模板渲染器
4
+ * @description 将结构化 ReportData 渲染为完整 HTML 报表(ECharts + 指标卡 + 表格 + 标签)
5
+ * 移植自 html-renderer.cjs,新增 radar / gauge 图表类型和 kv-list block 类型
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.renderReport = renderReport;
42
+ const path = __importStar(require("path"));
43
+ const fs = __importStar(require("fs"));
44
+ const marked_1 = require("marked");
45
+ const DARK_THEME = {
46
+ css: {
47
+ bgPage: '#0a0e17', bgCard: '#111827', bgSubtle: '#1a2235', bgHover: '#1f2a3f',
48
+ textPrimary: '#e2e8f0', textSecondary: '#94a3b8', textTertiary: '#64748b',
49
+ accent: '#f0c040', accent2: '#38bdf8', purple: '#a78bfa',
50
+ profit: '#34d399', loss: '#f87171', warn: '#fbbf24', border: '#1e293b',
51
+ },
52
+ chartColors: ['#00e5ff', '#ffc107', '#00ff88', '#a882ff', '#ff6b9d', '#ff9100', '#448aff', '#64ffda', '#ffd740', '#40c4ff'],
53
+ chart: {
54
+ textColor: '#8892b0', labelColor: '#ccd6f6', borderColor: 'rgba(255,255,255,0.06)',
55
+ bgDark: '#0a1628', tooltipBg: 'rgba(10,22,40,0.95)', tooltipBorder: 'rgba(255,255,255,0.1)',
56
+ tooltipText: '#ccd6f6', labelLine: 'rgba(255,255,255,0.15)',
57
+ },
58
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif",
59
+ };
60
+ const LIGHT_THEME = {
61
+ css: {
62
+ bgPage: '#f8fafc', bgCard: '#ffffff', bgSubtle: '#f1f5f9', bgHover: '#e2e8f0',
63
+ textPrimary: '#1e293b', textSecondary: '#475569', textTertiary: '#94a3b8',
64
+ accent: '#2563eb', accent2: '#0891b2', purple: '#7c3aed',
65
+ profit: '#059669', loss: '#dc2626', warn: '#d97706', border: '#e2e8f0',
66
+ },
67
+ chartColors: ['#2563eb', '#0891b2', '#059669', '#7c3aed', '#dc2626', '#d97706', '#0d9488', '#4f46e5', '#ea580c', '#16a34a'],
68
+ chart: {
69
+ textColor: '#64748b', labelColor: '#334155', borderColor: 'rgba(0,0,0,0.08)',
70
+ bgDark: '#f8fafc', tooltipBg: 'rgba(255,255,255,0.98)', tooltipBorder: 'rgba(0,0,0,0.08)',
71
+ tooltipText: '#1e293b', labelLine: 'rgba(0,0,0,0.15)',
72
+ },
73
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif",
74
+ };
75
+ function loadTheme(themeName) {
76
+ // 优先从项目 themes 目录加载
77
+ const candidates = [
78
+ path.join(__dirname, '..', '..', '..', '..', 'data', 'doc-templates', 'html', 'themes'),
79
+ path.join(process.cwd(), 'data', 'doc-templates', 'html', 'themes'),
80
+ ];
81
+ const name = themeName || 'dark';
82
+ for (const dir of candidates) {
83
+ const p = path.join(dir, name + '.json');
84
+ if (fs.existsSync(p)) {
85
+ try {
86
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
87
+ }
88
+ catch { /* fallback */ }
89
+ }
90
+ }
91
+ return name === 'light' ? LIGHT_THEME : DARK_THEME;
92
+ }
93
+ // ============================================================
94
+ // HTML 转义
95
+ // ============================================================
96
+ function escHtml(s) {
97
+ if (s == null)
98
+ return '';
99
+ return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
100
+ }
101
+ function escJs(s) {
102
+ if (!s)
103
+ return '';
104
+ return String(s).replace(/\\/g, '\\\\').replace(/'/g, "\\'");
105
+ }
106
+ // ============================================================
107
+ // Markdown → HTML 转换(用于 text-block 内容渲染)
108
+ // ============================================================
109
+ let _markedConfigured = false;
110
+ function markdownToHtml(md) {
111
+ if (!md)
112
+ return '';
113
+ if (!_markedConfigured) {
114
+ marked_1.marked.setOptions({ breaks: true, gfm: true });
115
+ _markedConfigured = true;
116
+ }
117
+ try {
118
+ return marked_1.marked.parse(md);
119
+ }
120
+ catch {
121
+ return escHtml(md).replace(/\n/g, '<br>');
122
+ }
123
+ }
124
+ // ============================================================
125
+ // CSS 生成
126
+ // ============================================================
127
+ function generateCSS(theme) {
128
+ const c = theme.css;
129
+ return `:root {
130
+ --color-bg-page: ${c.bgPage};
131
+ --color-bg-card: ${c.bgCard};
132
+ --color-bg-subtle: ${c.bgSubtle};
133
+ --color-bg-hover: ${c.bgHover};
134
+ --color-text-primary: ${c.textPrimary};
135
+ --color-text-secondary: ${c.textSecondary};
136
+ --color-text-tertiary: ${c.textTertiary};
137
+ --color-border-muted: ${c.border};
138
+ --color-accent-primary: ${c.accent};
139
+ --color-accent-soft: ${c.accent2};
140
+ --color-profit: ${c.profit};
141
+ --color-loss: ${c.loss};
142
+ --color-warning: ${c.warn};
143
+ }
144
+ * { margin: 0; padding: 0; box-sizing: border-box; }
145
+ body {
146
+ font-family: ${theme.fontFamily};
147
+ background: var(--color-bg-page);
148
+ color: var(--color-text-primary);
149
+ min-height: 100vh;
150
+ line-height: 1.5;
151
+ }
152
+ .container { max-width: 1280px; margin: 0 auto; padding: 32px 24px; }
153
+ .header {
154
+ text-align: center; padding: 40px 24px 32px;
155
+ position: relative; overflow: hidden;
156
+ }
157
+ .header::before {
158
+ content: ''; position: absolute;
159
+ top: -60px; left: 50%; transform: translateX(-50%);
160
+ width: 400px; height: 200px;
161
+ background: radial-gradient(ellipse, ${c.accent}20 0%, transparent 70%);
162
+ pointer-events: none;
163
+ }
164
+ .header h1 {
165
+ font-size: 28px; font-weight: 600;
166
+ background: linear-gradient(135deg, var(--color-accent-primary), var(--color-accent-soft));
167
+ -webkit-background-clip: text; -webkit-text-fill-color: transparent;
168
+ background-clip: text; margin-bottom: 8px; letter-spacing: 0.02em;
169
+ }
170
+ .header .subtitle {
171
+ font-size: 14px; color: var(--color-text-secondary);
172
+ display: flex; align-items: center; justify-content: center; gap: 16px; flex-wrap: wrap;
173
+ }
174
+ .header .subtitle span { display: inline-flex; align-items: center; gap: 4px; }
175
+ .header .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--color-accent-primary); }
176
+ .metrics {
177
+ display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
178
+ gap: 14px; margin-bottom: 24px;
179
+ }
180
+ .metric-card {
181
+ background: var(--color-bg-card); border: 1px solid var(--color-border-muted);
182
+ border-radius: 12px; padding: 20px 18px; position: relative; overflow: hidden;
183
+ transition: all 0.3s ease;
184
+ }
185
+ .metric-card:hover {
186
+ border-color: var(--color-accent-primary); transform: translateY(-2px);
187
+ box-shadow: 0 8px 25px ${c.accent}14;
188
+ }
189
+ .metric-card::after {
190
+ content: ''; position: absolute; top: 0; right: 0;
191
+ width: 60px; height: 60px;
192
+ background: radial-gradient(circle at top right, ${c.accent}10, transparent 70%);
193
+ pointer-events: none;
194
+ }
195
+ .metric-card .icon { font-size: 22px; margin-bottom: 8px; }
196
+ .metric-card .label {
197
+ font-size: 11px; color: var(--color-text-tertiary);
198
+ text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 6px;
199
+ }
200
+ .metric-card .value {
201
+ font-size: 28px; font-weight: 600;
202
+ background: linear-gradient(135deg, var(--color-text-primary), var(--color-accent-primary));
203
+ -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
204
+ }
205
+ .metric-card .sub { font-size: 12px; color: var(--color-text-secondary); margin-top: 4px; }
206
+ .section {
207
+ background: var(--color-bg-card); border: 1px solid var(--color-border-muted);
208
+ border-radius: 14px; padding: 22px 20px; margin-bottom: 18px;
209
+ }
210
+ .section-title {
211
+ font-size: 16px; font-weight: 600; margin-bottom: 16px;
212
+ display: flex; align-items: center; gap: 10px;
213
+ }
214
+ .section-title::before {
215
+ content: ''; width: 4px; height: 18px;
216
+ background: linear-gradient(180deg, var(--color-accent-primary), var(--color-accent-soft));
217
+ border-radius: 2px;
218
+ }
219
+ .grid-2 {
220
+ display: grid; grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
221
+ gap: 18px; margin-bottom: 18px;
222
+ }
223
+ .chart-box { width: 100%; height: 320px; }
224
+ .data-table { width: 100%; border-collapse: collapse; font-size: 13px; }
225
+ .data-table thead th {
226
+ text-align: left; padding: 10px 12px;
227
+ color: var(--color-text-tertiary); font-weight: 500; font-size: 11px;
228
+ text-transform: uppercase; letter-spacing: 0.05em;
229
+ border-bottom: 1px solid var(--color-border-muted);
230
+ }
231
+ .data-table tbody td {
232
+ padding: 10px 12px; border-bottom: 1px solid ${c.border}99;
233
+ color: var(--color-text-secondary);
234
+ }
235
+ .data-table tbody tr:hover td { background: var(--color-bg-hover); color: var(--color-text-primary); }
236
+ .rank {
237
+ display: inline-flex; align-items: center; justify-content: center;
238
+ width: 24px; height: 24px; border-radius: 6px; font-size: 12px; font-weight: 600;
239
+ }
240
+ .rank-1 { background: ${c.accent}33; color: var(--color-accent-primary); }
241
+ .rank-2 { background: #c0c0c026; color: #c0c0c0; }
242
+ .rank-3 { background: #cd7f3226; color: #cd7f32; }
243
+ .rank-n { color: var(--color-text-tertiary); }
244
+ .tag-cloud { display: flex; flex-wrap: wrap; gap: 10px; }
245
+ .tag {
246
+ padding: 6px 14px; border-radius: 20px; font-size: 13px; font-weight: 500;
247
+ border: 1px solid var(--color-border-muted); background: var(--color-bg-subtle);
248
+ color: var(--color-text-secondary); transition: all 0.2s;
249
+ }
250
+ .tag:hover { border-color: var(--color-accent-primary); color: var(--color-accent-primary); }
251
+ .tag.accent { border-color: ${c.accent}4d; color: var(--color-accent-primary); }
252
+ .tag.accent2 { border-color: ${c.accent2}4d; color: var(--color-accent-soft); }
253
+ .tag.purple { border-color: ${c.purple}4d; color: var(--color-accent-soft); }
254
+ .tag.warning { border-color: ${c.warn}4d; color: var(--color-warning); }
255
+ .tag.success { border-color: ${c.profit}4d; color: var(--color-profit); }
256
+ .tag.info { border-color: ${c.accent2}4d; color: var(--color-accent-soft); }
257
+ .quality-grid {
258
+ display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 10px;
259
+ }
260
+ .quality-item {
261
+ display: flex; align-items: flex-start; gap: 10px;
262
+ padding: 12px 14px; background: var(--color-bg-subtle); border-radius: 8px;
263
+ border: 1px solid ${c.warn}26; font-size: 13px; color: var(--color-text-secondary);
264
+ }
265
+ .quality-item .warn-icon { font-size: 16px; flex-shrink: 0; margin-top: 1px; }
266
+ .text-block { font-size: 14px; color: var(--color-text-secondary); line-height: 1.7; }
267
+ .text-block > *:first-child { margin-top: 0; }
268
+ .text-block > *:last-child { margin-bottom: 0; }
269
+ .text-block h1, .text-block h2, .text-block h3, .text-block h4, .text-block h5, .text-block h6 {
270
+ color: var(--color-text-primary); font-weight: 600; margin: 20px 0 10px; line-height: 1.3;
271
+ }
272
+ .text-block h1 { font-size: 22px; }
273
+ .text-block h2 { font-size: 18px; padding-bottom: 6px; border-bottom: 1px solid var(--color-border-muted); }
274
+ .text-block h3 { font-size: 16px; }
275
+ .text-block h4 { font-size: 14px; }
276
+ .text-block p { margin: 8px 0; }
277
+ .text-block ul, .text-block ol { margin: 8px 0; padding-left: 22px; }
278
+ .text-block li { margin: 4px 0; }
279
+ .text-block ul li::marker { color: var(--color-accent-primary); }
280
+ .text-block ol li::marker { color: var(--color-accent-soft); }
281
+ .text-block strong { color: var(--color-text-primary); font-weight: 600; }
282
+ .text-block em { color: var(--color-accent-soft); font-style: italic; }
283
+ .text-block code {
284
+ background: var(--color-bg-subtle); padding: 2px 6px; border-radius: 4px;
285
+ font-size: 13px; font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
286
+ color: var(--color-accent-primary);
287
+ }
288
+ .text-block pre {
289
+ background: var(--color-bg-subtle); border: 1px solid var(--color-border-muted);
290
+ border-radius: 8px; padding: 12px 14px; margin: 10px 0; overflow-x: auto;
291
+ }
292
+ .text-block pre code { background: none; padding: 0; color: var(--color-text-secondary); }
293
+ .text-block blockquote {
294
+ border-left: 3px solid var(--color-accent-soft); padding: 4px 14px;
295
+ margin: 10px 0; color: var(--color-text-tertiary); background: var(--color-bg-subtle);
296
+ border-radius: 0 6px 6px 0;
297
+ }
298
+ .text-block a { color: var(--color-accent-soft); text-decoration: none; }
299
+ .text-block a:hover { text-decoration: underline; }
300
+ .text-block hr { border: none; border-top: 1px solid var(--color-border-muted); margin: 16px 0; }
301
+ .text-block table {
302
+ width: 100%; border-collapse: collapse; margin: 12px 0;
303
+ font-size: 13px; border: 1px solid var(--color-border-muted); border-radius: 8px; overflow: hidden;
304
+ }
305
+ .text-block table thead th {
306
+ text-align: left; padding: 10px 12px;
307
+ color: var(--color-text-tertiary); font-weight: 500; font-size: 11px;
308
+ text-transform: uppercase; letter-spacing: 0.05em;
309
+ border-bottom: 1px solid var(--color-border-muted); background: var(--color-bg-subtle);
310
+ }
311
+ .text-block table tbody td {
312
+ padding: 10px 12px; border-bottom: 1px solid ${c.border}55;
313
+ color: var(--color-text-secondary);
314
+ }
315
+ .text-block table tbody tr:last-child td { border-bottom: none; }
316
+ .text-block table tbody tr:hover td { background: var(--color-bg-hover); color: var(--color-text-primary); }
317
+ .kv-list { display: flex; flex-direction: column; gap: 8px; }
318
+ .kv-item {
319
+ display: flex; justify-content: space-between; align-items: center;
320
+ padding: 10px 14px; background: var(--color-bg-subtle); border-radius: 8px;
321
+ border: 1px solid ${c.border}66;
322
+ }
323
+ .kv-item .kv-label { font-size: 13px; color: var(--color-text-secondary); }
324
+ .kv-item .kv-value { font-size: 14px; font-weight: 600; color: var(--color-text-primary); }
325
+ .kv-item.highlight { border-color: ${c.accent}4d; }
326
+ .kv-item.highlight .kv-value { color: var(--color-accent-primary); }
327
+ .actions {
328
+ display: flex; flex-wrap: wrap; gap: 10px; margin-top: 24px; justify-content: center;
329
+ }
330
+ .btn {
331
+ padding: 10px 20px; border-radius: 8px; font-size: 13px; font-weight: 500;
332
+ cursor: pointer; border: none; transition: all 0.2s; font-family: inherit;
333
+ }
334
+ .btn-primary {
335
+ background: linear-gradient(135deg, ${c.accent}, ${c.accent}cc); color: ${c.bgPage};
336
+ }
337
+ .btn-primary:hover { box-shadow: 0 4px 16px ${c.accent}4d; transform: translateY(-1px); }
338
+ .btn-outline {
339
+ background: transparent; color: var(--color-text-secondary);
340
+ border: 1px solid var(--color-border-muted);
341
+ }
342
+ .btn-outline:hover { border-color: var(--color-accent-soft); color: var(--color-accent-soft); }
343
+ .footer {
344
+ text-align: center; padding: 24px;
345
+ color: var(--color-text-tertiary); font-size: 12px;
346
+ border-top: 1px solid var(--color-border-muted); margin-top: 8px;
347
+ }
348
+ @media (max-width: 768px) {
349
+ .container { padding: 16px 12px; }
350
+ .header h1 { font-size: 22px; }
351
+ .metrics { grid-template-columns: repeat(2, 1fr); }
352
+ .grid-2 { grid-template-columns: 1fr; }
353
+ .chart-box { height: 260px; }
354
+ }`;
355
+ }
356
+ // ============================================================
357
+ // HTML 块渲染器
358
+ // ============================================================
359
+ function renderHeader(data) {
360
+ const parts = [];
361
+ if (data.date)
362
+ parts.push(`<span><span class="dot"></span> ${escHtml(data.date)}</span>`);
363
+ if (data.subtitle)
364
+ parts.push(`<span>${escHtml(data.subtitle)}</span>`);
365
+ return `<div class="header">
366
+ <h1>${escHtml(data.title || 'Report')}</h1>
367
+ <div class="subtitle">${parts.join('\n ')}</div>
368
+ </div>`;
369
+ }
370
+ function renderMetrics(data) {
371
+ if (!data.metrics?.length)
372
+ return '';
373
+ const cards = data.metrics.map(m => ` <div class="metric-card">
374
+ <div class="icon">${escHtml(m.icon || '📊')}</div>
375
+ <div class="label">${escHtml(m.label)}</div>
376
+ <div class="value">${escHtml(String(m.value))}</div>
377
+ ${m.sub ? '<div class="sub">' + escHtml(m.sub) + '</div>' : ''}
378
+ </div>`).join('\n');
379
+ return `<div class="metrics">\n${cards}\n</div>`;
380
+ }
381
+ function renderChartBlock(block) {
382
+ const id = block.id || ('chart-' + Math.random().toString(36).slice(2, 8));
383
+ const height = block.chart?.height || 320;
384
+ const title = block.title ? `<div class="section-title">${escHtml(block.title)}</div>` : '';
385
+ return `<div class="section">
386
+ ${title}
387
+ <div id="${id}" class="chart-box" style="height:${height}px;"></div>
388
+ </div>`;
389
+ }
390
+ function renderTableBlock(block) {
391
+ const t = block.table;
392
+ if (!t)
393
+ return '';
394
+ const title = block.title ? `<div class="section-title">${escHtml(block.title)}</div>` : '';
395
+ const headerRow = '<tr>' + t.headers.map(h => `<th>${escHtml(h)}</th>`).join('') + '</tr>';
396
+ const bodyRows = t.rows.map((row, ri) => {
397
+ const cells = row.map((cell, ci) => {
398
+ if (ci === 0 && t.ranked) {
399
+ const rc = ri === 0 ? 'rank-1' : ri === 1 ? 'rank-2' : ri === 2 ? 'rank-3' : 'rank-n';
400
+ return `<td><span class="rank ${rc}">${ri + 1}</span> ${escHtml(cell)}</td>`;
401
+ }
402
+ return `<td>${escHtml(cell)}</td>`;
403
+ });
404
+ return '<tr>' + cells.join('') + '</tr>';
405
+ }).join('\n');
406
+ return `<div class="section">
407
+ ${title}
408
+ <table class="data-table">
409
+ <thead>${headerRow}</thead>
410
+ <tbody>${bodyRows}</tbody>
411
+ </table>
412
+ </div>`;
413
+ }
414
+ function renderTagsBlock(block) {
415
+ const title = block.title ? `<div class="section-title">${escHtml(block.title)}</div>` : '';
416
+ const groups = (block.groups || []).map(g => {
417
+ const styleClass = g.style || '';
418
+ const label = g.label ? `<div style="font-size:12px;color:var(--color-text-tertiary);margin-bottom:8px;">${escHtml(g.label)}</div>` : '';
419
+ const tags = g.items.map(item => `<span class="tag ${styleClass}">${escHtml(item)}</span>`).join('');
420
+ return `${label}\n <div class="tag-cloud">${tags}</div>`;
421
+ }).join('\n <div style="margin-top:14px;">\n ');
422
+ return `<div class="section">\n ${title}\n ${groups}\n</div>`;
423
+ }
424
+ function renderTextBlock(block) {
425
+ const title = block.title ? `<div class="section-title">${escHtml(block.title)}</div>` : '';
426
+ return `<div class="section">\n ${title}\n <div class="text-block">${markdownToHtml(block.content || '')}</div>\n</div>`;
427
+ }
428
+ function renderAlertsBlock(block, theme) {
429
+ const title = block.title || '⚠️ 注意事项';
430
+ const items = (block.items || []).map(item => `<div class="quality-item">
431
+ <span class="warn-icon">⚠️</span>
432
+ <span>${escHtml(item)}</span>
433
+ </div>`).join('\n ');
434
+ return `<div class="section" style="border-color:${theme.css.warn}33;">
435
+ <div class="section-title" style="color:var(--color-warning);">${escHtml(title)}</div>
436
+ <div class="quality-grid">\n ${items}\n </div>
437
+ </div>`;
438
+ }
439
+ function renderKvListBlock(block) {
440
+ const title = block.title ? `<div class="section-title">${escHtml(block.title)}</div>` : '';
441
+ const items = (block.kvItems || []).map(kv => {
442
+ const cls = kv.highlight ? 'kv-item highlight' : 'kv-item';
443
+ return `<div class="${cls}">
444
+ <span class="kv-label">${escHtml(kv.label)}</span>
445
+ <span class="kv-value">${escHtml(kv.value)}</span>
446
+ </div>`;
447
+ }).join('\n ');
448
+ return `<div class="section">\n ${title}\n <div class="kv-list">\n ${items}\n </div>\n</div>`;
449
+ }
450
+ function renderBlock(block, theme) {
451
+ switch (block.type) {
452
+ case 'chart': return renderChartBlock(block);
453
+ case 'table': return renderTableBlock(block);
454
+ case 'tags': return renderTagsBlock(block);
455
+ case 'text': return renderTextBlock(block);
456
+ case 'alerts': return renderAlertsBlock(block, theme);
457
+ case 'kv-list': return renderKvListBlock(block);
458
+ default: return '';
459
+ }
460
+ }
461
+ function renderSections(data, theme) {
462
+ if (!data.sections?.length)
463
+ return '';
464
+ return data.sections.map(section => {
465
+ const layout = section.layout || 'full';
466
+ const blocks = section.blocks.map(b => renderBlock(b, theme)).join('\n');
467
+ if (layout === 'grid-2')
468
+ return `<div class="grid-2">\n${blocks}\n</div>`;
469
+ return blocks;
470
+ }).join('\n\n');
471
+ }
472
+ function renderActions(data) {
473
+ if (!data.actions?.length)
474
+ return '';
475
+ const buttons = data.actions.map(a => {
476
+ const cls = a.primary ? 'btn btn-primary' : 'btn btn-outline';
477
+ return ` <button class="${cls}" onclick="sendPrompt('${escJs(a.prompt || a.label)}')">${escHtml(a.label)}</button>`;
478
+ }).join('\n');
479
+ return `<div class="actions">\n${buttons}\n</div>`;
480
+ }
481
+ function renderFooter(data) {
482
+ if (!data.footer)
483
+ return '';
484
+ return `<div class="footer">${escHtml(data.footer)}</div>`;
485
+ }
486
+ function collectCharts(data) {
487
+ const charts = [];
488
+ if (!data.sections)
489
+ return charts;
490
+ for (const section of data.sections) {
491
+ for (const block of section.blocks || []) {
492
+ if (block.type === 'chart' && block.chart) {
493
+ charts.push({ id: block.id || ('chart-' + Math.random().toString(36).slice(2, 8)), chart: block.chart });
494
+ }
495
+ }
496
+ }
497
+ return charts;
498
+ }
499
+ function generateChartJS(item, theme) {
500
+ const { id, chart } = item;
501
+ const ct = theme.chart;
502
+ const tooltipBase = `tooltip: { backgroundColor: cv('--color-bg-card','${ct.tooltipBg}'), borderColor: cv('--color-border-muted','${ct.tooltipBorder}'), textStyle: { color: cv('--color-text-primary','${ct.tooltipText}') }`;
503
+ const type = chart.type || 'bar';
504
+ const chartData = chart.data || [];
505
+ if (type === 'pie' || type === 'doughnut') {
506
+ const radius = type === 'doughnut' ? "['45%','72%']" : "['0%','72%']";
507
+ const pieData = chartData.map((d, i) => `{ value: ${d.value}, name: '${escJs(d.name)}', itemStyle: { color: COLORS[${i % theme.chartColors.length}] } }`).join(',\n ');
508
+ return ` (function() {
509
+ var c = echarts.init(document.getElementById('${id}'));
510
+ c.setOption({
511
+ ${tooltipBase}, trigger: 'item', formatter: '{b}: {c} ({d}%)' },
512
+ series: [{
513
+ type: 'pie', radius: ${radius}, center: ['50%','52%'],
514
+ emphasis: { label: { fontSize: 16, fontWeight: 'bold' } },
515
+ label: { color: cv('--color-text-secondary','${ct.textColor}'), fontSize: 11 },
516
+ labelLine: { lineStyle: { color: cv('--color-border-muted','${ct.labelLine}') } },
517
+ data: [${pieData}]
518
+ }]
519
+ });
520
+ })();`;
521
+ }
522
+ if (type === 'radar') {
523
+ const indicators = chart.indicators || chartData.map(d => ({ name: d.name, max: Math.ceil(d.value * 1.2) }));
524
+ const indStr = indicators.map(ind => {
525
+ const maxVal = (ind.max != null && !Number.isNaN(ind.max) && ind.max > 0) ? ind.max : 100;
526
+ return `{ name: '${escJs(ind.name)}', max: ${maxVal} }`;
527
+ }).join(', ');
528
+ const values = chartData.length > 0
529
+ ? chartData.map(d => d.value).join(', ')
530
+ : indicators.map(() => 0).join(', ');
531
+ return ` (function() {
532
+ var c = echarts.init(document.getElementById('${id}'));
533
+ c.setOption({
534
+ ${tooltipBase},
535
+ radar: {
536
+ indicator: [${indStr}],
537
+ axisName: { color: cv('--color-text-secondary','${ct.textColor}'), fontSize: 11 },
538
+ splitArea: { areaStyle: { color: ['transparent'] } },
539
+ splitLine: { lineStyle: { color: cv('--color-border-muted','${ct.borderColor}') } },
540
+ axisLine: { lineStyle: { color: cv('--color-border-muted','${ct.borderColor}') } }
541
+ },
542
+ series: [{
543
+ type: 'radar',
544
+ data: [{ value: [${values}], areaStyle: { color: COLORS[0] + '30' }, lineStyle: { color: COLORS[0], width: 2 }, itemStyle: { color: COLORS[0] } }]
545
+ }]
546
+ });
547
+ })();`;
548
+ }
549
+ if (type === 'gauge') {
550
+ const val = chartData[0]?.value || 0;
551
+ const maxVal = chart.max || 100;
552
+ return ` (function() {
553
+ var c = echarts.init(document.getElementById('${id}'));
554
+ c.setOption({
555
+ ${tooltipBase},
556
+ series: [{
557
+ type: 'gauge',
558
+ max: ${maxVal},
559
+ progress: { show: true, width: 14, itemStyle: { color: COLORS[0] } },
560
+ axisLine: { lineStyle: { width: 14, color: [[1, cv('--color-border-muted','${ct.borderColor}')]] } },
561
+ axisTick: { show: false },
562
+ splitLine: { length: 8, lineStyle: { width: 2, color: cv('--color-text-tertiary','${ct.textColor}') } },
563
+ axisLabel: { distance: 20, color: cv('--color-text-secondary','${ct.textColor}'), fontSize: 11 },
564
+ detail: { valueAnimation: true, fontSize: 28, fontWeight: 600, color: cv('--color-text-primary','${ct.labelColor}'), offsetCenter: [0, '60%'] },
565
+ data: [{ value: ${val}, name: '${escJs(chartData[0]?.name || '')}' }],
566
+ title: { offsetCenter: [0, '80%'], fontSize: 13, color: cv('--color-text-secondary','${ct.textColor}') }
567
+ }]
568
+ });
569
+ })();`;
570
+ }
571
+ if (type === 'bar' || type === 'bar-horizontal') {
572
+ const labels = chartData.map(d => `'${escJs(d.name || '')}'`).join(',');
573
+ const values = chartData.map(d => d.value);
574
+ const isH = type === 'bar-horizontal';
575
+ if (isH) {
576
+ return ` (function() {
577
+ var c = echarts.init(document.getElementById('${id}'));
578
+ var names = [${labels}];
579
+ var vals = [${values.join(',')}];
580
+ c.setOption({
581
+ ${tooltipBase}, trigger: 'axis', axisPointer: { type: 'shadow' } },
582
+ grid: { left: 140, right: 60, top: 10, bottom: 20 },
583
+ xAxis: { type: 'value', axisLine: { show: false }, axisTick: { show: false }, splitLine: { lineStyle: { color: cv('--color-border-muted','${ct.borderColor}') } }, axisLabel: { color: cv('--color-text-secondary','${ct.textColor}'), fontSize: 11 } },
584
+ yAxis: { type: 'category', data: names, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { color: cv('--color-text-primary','${ct.labelColor}'), fontSize: 12 }, inverse: true },
585
+ series: [{
586
+ type: 'bar', data: vals.map(function(v, i) { return { value: v, itemStyle: { color: COLORS[i % COLORS.length], borderRadius: [0, 4, 4, 0] } }; }),
587
+ barWidth: 18,
588
+ label: { show: true, position: 'right', color: cv('--color-text-secondary','${ct.textColor}'), fontSize: 11 }
589
+ }]
590
+ });
591
+ })();`;
592
+ }
593
+ return ` (function() {
594
+ var c = echarts.init(document.getElementById('${id}'));
595
+ var names = [${labels}];
596
+ var vals = [${values.join(',')}];
597
+ c.setOption({
598
+ ${tooltipBase}, trigger: 'axis', axisPointer: { type: 'shadow' } },
599
+ grid: { left: 50, right: 20, top: 20, bottom: 80 },
600
+ xAxis: { type: 'category', data: names, axisLabel: { color: cv('--color-text-secondary','${ct.textColor}'), fontSize: 10, rotate: ${chart.rotate || 30} }, axisLine: { lineStyle: { color: cv('--color-border-muted','${ct.borderColor}') } }, axisTick: { show: false } },
601
+ yAxis: { type: 'value', ${chart.yName ? "name: '" + escJs(chart.yName) + "', nameTextStyle: { color: cv('--color-text-secondary','" + ct.textColor + "') }, " : ''}axisLabel: { color: cv('--color-text-secondary','${ct.textColor}') }, splitLine: { lineStyle: { color: cv('--color-border-muted','${ct.borderColor}') } } },
602
+ series: [{
603
+ type: 'bar', data: vals.map(function(v, i) { return { value: v, itemStyle: { color: COLORS[i % COLORS.length], borderRadius: [6, 6, 0, 0] } }; }),
604
+ barWidth: 22,
605
+ label: { show: true, position: 'top', color: cv('--color-text-secondary','${ct.textColor}'), fontSize: 10 }
606
+ }]
607
+ });
608
+ })();`;
609
+ }
610
+ if (type === 'line') {
611
+ const labels = chartData.map(d => `'${escJs(d.name || '')}'`).join(',');
612
+ const datasets = chart.datasets || [{ name: '', data: chartData.map(d => d.value) }];
613
+ const seriesCode = datasets.map((ds, di) => `{ name: '${escJs(ds.name)}', type: 'line', data: [${ds.data.join(',')}], smooth: true, lineStyle: { width: 2, color: COLORS[${di}] }, itemStyle: { color: COLORS[${di}] }, areaStyle: { color: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: COLORS[${di}] + '40' }, { offset: 1, color: 'transparent' }] } } }`).join(',\n ');
614
+ return ` (function() {
615
+ var c = echarts.init(document.getElementById('${id}'));
616
+ c.setOption({
617
+ ${tooltipBase}, trigger: 'axis' },
618
+ legend: { data: [${datasets.map(d => "'" + escJs(d.name) + "'").join(',')}], textStyle: { color: cv('--color-text-secondary','${ct.textColor}') } },
619
+ grid: { left: 50, right: 20, top: 40, bottom: 30 },
620
+ xAxis: { type: 'category', data: [${labels}], axisLabel: { color: cv('--color-text-secondary','${ct.textColor}') }, axisLine: { lineStyle: { color: cv('--color-border-muted','${ct.borderColor}') } } },
621
+ yAxis: { type: 'value', axisLabel: { color: cv('--color-text-secondary','${ct.textColor}') }, splitLine: { lineStyle: { color: cv('--color-border-muted','${ct.borderColor}') } } },
622
+ series: [\n ${seriesCode}\n ]
623
+ });
624
+ })();`;
625
+ }
626
+ return '';
627
+ }
628
+ function generateScript(data, theme) {
629
+ const charts = collectCharts(data);
630
+ if (!charts.length)
631
+ return '';
632
+ const chartIds = charts.map(c => `'${c.id}'`).join(',');
633
+ const chartInits = charts.map(c => generateChartJS(c, theme)).join('\n\n');
634
+ return `<script>
635
+ (function() {
636
+ var cs = getComputedStyle(document.documentElement);
637
+ var cv = function(n, fb) { return cs.getPropertyValue(n).trim() || fb; };
638
+ var COLORS = window.__THEME_COLORS__ || [
639
+ cv('--color-accent-primary','#6B5CE7'), cv('--color-accent-soft','#38bdf8'),
640
+ cv('--color-profit','#22c55e'), cv('--color-loss','#ef4444'),
641
+ cv('--color-warning','#f59e0b'), cv('--color-info','#3b82f6'),
642
+ '#a78bfa','#ec4899','#14b8a6','#f97316','#8b5cf6'
643
+ ];
644
+
645
+ ${chartInits}
646
+
647
+ // Responsive resize
648
+ window.addEventListener('resize', function() {
649
+ [${chartIds}].forEach(function(id) {
650
+ var dom = document.getElementById(id);
651
+ if (dom) { var inst = echarts.getInstanceByDom(dom); if (inst) inst.resize(); }
652
+ });
653
+ });
654
+ })();
655
+ </script>`;
656
+ }
657
+ // ============================================================
658
+ // 主渲染函数
659
+ // ============================================================
660
+ /**
661
+ * 将 ReportData 渲染为完整 HTML 字符串
662
+ */
663
+ function renderReport(data) {
664
+ const theme = loadTheme(data.theme);
665
+ const lang = data.lang || 'zh-CN';
666
+ const title = data.title || 'Report';
667
+ return `<!DOCTYPE html>
668
+ <html lang="${lang}">
669
+ <head>
670
+ <meta charset="UTF-8">
671
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
672
+ <title>${escHtml(title)}</title>
673
+ <script src="https://jtcospublic.ciccten.com/public/lib/echarts/6.1.0/dist/echarts.min.js"><\/script>
674
+ <style>
675
+ ${generateCSS(theme)}
676
+ </style>
677
+ </head>
678
+ <body>
679
+ <div class="container">
680
+
681
+ ${renderHeader(data)}
682
+
683
+ ${renderMetrics(data)}
684
+
685
+ ${renderSections(data, theme)}
686
+
687
+ ${renderActions(data)}
688
+
689
+ ${renderFooter(data)}
690
+
691
+ </div>
692
+
693
+ ${generateScript(data, theme)}
694
+
695
+ </body>
696
+ </html>
697
+ `;
698
+ }
699
+ //# sourceMappingURL=report-renderer.js.map