@cicctencent/agent-server 0.2.41 → 0.2.43

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