lumencode 1.3.3 → 1.3.4
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/README.md +12 -0
- package/lib/server.js +361 -29
- package/lib/smart-report-store.js +98 -0
- package/lib/smart-report.js +339 -0
- package/package.json +1 -1
- package/public/api.js +25 -0
- package/public/app.js +315 -2
- package/public/config.js +4 -0
- package/public/index.html +86 -3
- package/public/style.css +202 -0
package/public/app.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { COLORS, SCENARIO_COLORS, TEXT, ID, STORAGE } from './config.js';
|
|
2
2
|
import { esc, fmt, fmtShort, destroyChart, destroyAllCharts, getChart, setChart, todayISO, fmtDate, TOOL_DISPLAY_NAMES, groupMcpByServer, aggregateToolsWithDualCounts } from './utils.js';
|
|
3
|
-
import { createLatestRequestGuard, fetchTools, fetchReport, fetchConfig, saveConfig, fetchDetails, fetchSessions, fetchStepStats, fetchHooksStatus, updateHooks } from './api.js';
|
|
3
|
+
import { createLatestRequestGuard, fetchTools, fetchReport, fetchConfig, saveConfig, fetchDetails, fetchSessions, fetchStepStats, fetchHooksStatus, updateHooks, fetchSmartReportTools, fetchSmartReportRecord, generateSmartReport } from './api.js';
|
|
4
4
|
import { renderWorkTypePie, renderModelBars, renderProjectBars, renderTimelineArea, renderCacheStack } from './charts.js';
|
|
5
5
|
import { renderGitInsights, renderLineBlameEvidence } from './git-insights.js';
|
|
6
6
|
import { loadWorkReport, copyWorkReport, downloadMarkdown, getWorkReportState, setWorkReportState } from './work-report.js';
|
|
@@ -36,6 +36,28 @@ function appState() {
|
|
|
36
36
|
reportProjects: [],
|
|
37
37
|
copied: false,
|
|
38
38
|
reportHtml: '',
|
|
39
|
+
reportContentMode: 'source',
|
|
40
|
+
smartReportTools: [],
|
|
41
|
+
smartReportAgent: '',
|
|
42
|
+
smartReportStyle: ['default', 'workhorse'].includes(localStorage.getItem(STORAGE.SMART_REPORT_STYLE)) ? localStorage.getItem(STORAGE.SMART_REPORT_STYLE) : 'default',
|
|
43
|
+
smartReportStyleModalOpen: false,
|
|
44
|
+
smartReportLoading: false,
|
|
45
|
+
smartReportError: '',
|
|
46
|
+
smartReportMarkdown: '',
|
|
47
|
+
smartReportHtml: '',
|
|
48
|
+
smartReportCopied: false,
|
|
49
|
+
smartReportRecord: null,
|
|
50
|
+
smartReportRecordMeta: '',
|
|
51
|
+
smartReportNeedsUpdate: false,
|
|
52
|
+
smartReportUpdateMessage: '',
|
|
53
|
+
smartReportJob: null,
|
|
54
|
+
smartReportStatusMessage: '',
|
|
55
|
+
smartReportPollTimer: null,
|
|
56
|
+
smartReportElapsedTimer: null,
|
|
57
|
+
smartReportCompletionTimer: null,
|
|
58
|
+
smartReportStartedAt: '',
|
|
59
|
+
smartReportNow: Date.now(),
|
|
60
|
+
smartReportProgress: 0,
|
|
39
61
|
|
|
40
62
|
/* constants */
|
|
41
63
|
customStart: '',
|
|
@@ -199,6 +221,7 @@ function appState() {
|
|
|
199
221
|
}
|
|
200
222
|
});
|
|
201
223
|
await this.loadTools();
|
|
224
|
+
await this.loadSmartReportTools();
|
|
202
225
|
await this.loadHooksStatus();
|
|
203
226
|
await this.loadStepStats();
|
|
204
227
|
// 首次加载时先获取全量数据填充侧边栏,再按当前工具加载
|
|
@@ -233,6 +256,22 @@ function appState() {
|
|
|
233
256
|
} catch (e) { console.warn('loadTools failed:', e); this.availableTools = []; }
|
|
234
257
|
},
|
|
235
258
|
|
|
259
|
+
async loadSmartReportTools() {
|
|
260
|
+
try {
|
|
261
|
+
const data = await fetchSmartReportTools();
|
|
262
|
+
this.smartReportTools = data.tools || [];
|
|
263
|
+
const savedAgent = localStorage.getItem(STORAGE.SMART_REPORT_AGENT);
|
|
264
|
+
const firstDetected = this.smartReportTools.find(t => t.detected);
|
|
265
|
+
const savedDetected = this.smartReportTools.find(t => t.detected && t.name === savedAgent);
|
|
266
|
+
this.smartReportAgent = savedDetected?.name || firstDetected?.name || '';
|
|
267
|
+
await this.loadSmartReportRecord();
|
|
268
|
+
} catch (e) {
|
|
269
|
+
console.warn('loadSmartReportTools failed:', e);
|
|
270
|
+
this.smartReportTools = [];
|
|
271
|
+
this.smartReportAgent = '';
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
|
|
236
275
|
async loadStepStats() {
|
|
237
276
|
try {
|
|
238
277
|
const data = await fetchStepStats();
|
|
@@ -312,6 +351,7 @@ function appState() {
|
|
|
312
351
|
|
|
313
352
|
setTool(name) {
|
|
314
353
|
this.activeTool = name;
|
|
354
|
+
this.resetSmartReportDisplay();
|
|
315
355
|
this.loadCurrentView();
|
|
316
356
|
if (this.view === 'report') this.loadReportContent();
|
|
317
357
|
},
|
|
@@ -383,6 +423,7 @@ function appState() {
|
|
|
383
423
|
this.customStart = '';
|
|
384
424
|
this.customEnd = '';
|
|
385
425
|
this.saveStateToHash();
|
|
426
|
+
this.resetSmartReportDisplay();
|
|
386
427
|
this.loadCurrentView();
|
|
387
428
|
if (this.view === 'report') this.loadReportContent();
|
|
388
429
|
}
|
|
@@ -390,6 +431,7 @@ function appState() {
|
|
|
390
431
|
|
|
391
432
|
onCustomStartChange() {
|
|
392
433
|
if (this.customStart && this.customEnd) {
|
|
434
|
+
this.resetSmartReportDisplay();
|
|
393
435
|
this.loadCurrentView();
|
|
394
436
|
if (this.view === 'report') this.loadReportContent();
|
|
395
437
|
}
|
|
@@ -397,6 +439,7 @@ function appState() {
|
|
|
397
439
|
|
|
398
440
|
onCustomEndChange() {
|
|
399
441
|
if (this.customStart && this.customEnd) {
|
|
442
|
+
this.resetSmartReportDisplay();
|
|
400
443
|
this.loadCurrentView();
|
|
401
444
|
if (this.view === 'report') this.loadReportContent();
|
|
402
445
|
}
|
|
@@ -412,6 +455,7 @@ function appState() {
|
|
|
412
455
|
}
|
|
413
456
|
this.currentDate = d.toISOString().slice(0, 10);
|
|
414
457
|
this.saveStateToHash();
|
|
458
|
+
this.resetSmartReportDisplay();
|
|
415
459
|
this.loadCurrentView();
|
|
416
460
|
if (this.view === 'report') this.loadReportContent();
|
|
417
461
|
},
|
|
@@ -419,6 +463,7 @@ function appState() {
|
|
|
419
463
|
onDateChange() {
|
|
420
464
|
if (this.currentDate > this.today) this.currentDate = this.today;
|
|
421
465
|
this.saveStateToHash();
|
|
466
|
+
this.resetSmartReportDisplay();
|
|
422
467
|
this.loadCurrentView();
|
|
423
468
|
if (this.view === 'report') this.loadReportContent();
|
|
424
469
|
},
|
|
@@ -848,6 +893,7 @@ function appState() {
|
|
|
848
893
|
|
|
849
894
|
async loadReportContent() {
|
|
850
895
|
try {
|
|
896
|
+
if (!['detailed', 'brief'].includes(this.reportLevel)) this.reportLevel = 'detailed';
|
|
851
897
|
const params = { tool: this.activeTool, period: this.period, date: this.currentDate, format: 'work', platform: this.reportPlatform, level: this.reportLevel };
|
|
852
898
|
if (this.period === 'custom' && this.customStart && this.customEnd) {
|
|
853
899
|
params.start = this.customStart;
|
|
@@ -862,24 +908,291 @@ function appState() {
|
|
|
862
908
|
const markdown = await res.text();
|
|
863
909
|
setWorkReportState({ markdown, platform: this.reportPlatform, level: this.reportLevel });
|
|
864
910
|
this.reportHtml = this.renderMarkdownToReportHtml(markdown);
|
|
911
|
+
await this.loadSmartReportRecord();
|
|
865
912
|
} catch (e) { console.warn('loadReportContent failed:', e); }
|
|
866
913
|
},
|
|
867
914
|
|
|
868
915
|
setReportLevel(level) {
|
|
869
|
-
this.reportLevel = level;
|
|
916
|
+
this.reportLevel = ['detailed', 'brief'].includes(level) ? level : 'detailed';
|
|
917
|
+
this.resetSmartReportDisplay();
|
|
870
918
|
this.loadReportContent();
|
|
871
919
|
},
|
|
872
920
|
|
|
873
921
|
setReportPlatform(platform) {
|
|
874
922
|
this.reportPlatform = platform;
|
|
923
|
+
this.resetSmartReportDisplay();
|
|
875
924
|
this.loadReportContent();
|
|
876
925
|
},
|
|
877
926
|
|
|
878
927
|
setReportProject(project) {
|
|
879
928
|
this.reportProject = project;
|
|
929
|
+
this.resetSmartReportDisplay();
|
|
880
930
|
this.loadReportContent();
|
|
881
931
|
},
|
|
882
932
|
|
|
933
|
+
setSmartReportAgent(agent) {
|
|
934
|
+
this.smartReportAgent = agent;
|
|
935
|
+
localStorage.setItem(STORAGE.SMART_REPORT_AGENT, agent);
|
|
936
|
+
this.resetSmartReportDisplay();
|
|
937
|
+
this.loadSmartReportRecord();
|
|
938
|
+
},
|
|
939
|
+
|
|
940
|
+
resetSmartReportDisplay() {
|
|
941
|
+
this.stopSmartReportPolling();
|
|
942
|
+
this.stopSmartReportElapsedTimer();
|
|
943
|
+
this.stopSmartReportCompletionTimer();
|
|
944
|
+
this.smartReportError = '';
|
|
945
|
+
this.smartReportMarkdown = '';
|
|
946
|
+
this.smartReportHtml = '';
|
|
947
|
+
this.smartReportRecord = null;
|
|
948
|
+
this.smartReportRecordMeta = '';
|
|
949
|
+
this.smartReportNeedsUpdate = false;
|
|
950
|
+
this.smartReportUpdateMessage = '';
|
|
951
|
+
this.smartReportJob = null;
|
|
952
|
+
this.smartReportLoading = false;
|
|
953
|
+
this.smartReportStatusMessage = '';
|
|
954
|
+
this.smartReportStartedAt = '';
|
|
955
|
+
this.smartReportNow = Date.now();
|
|
956
|
+
this.smartReportProgress = 0;
|
|
957
|
+
this.reportContentMode = 'source';
|
|
958
|
+
},
|
|
959
|
+
|
|
960
|
+
smartReportParams() {
|
|
961
|
+
const params = {
|
|
962
|
+
agent: this.smartReportAgent,
|
|
963
|
+
tool: this.activeTool,
|
|
964
|
+
period: this.period,
|
|
965
|
+
date: this.currentDate,
|
|
966
|
+
level: this.reportLevel,
|
|
967
|
+
style: this.smartReportStyle,
|
|
968
|
+
platform: this.reportPlatform,
|
|
969
|
+
project: this.reportProject,
|
|
970
|
+
};
|
|
971
|
+
if (this.period === 'custom' && this.customStart && this.customEnd) {
|
|
972
|
+
params.start = this.customStart;
|
|
973
|
+
params.end = this.customEnd;
|
|
974
|
+
}
|
|
975
|
+
return params;
|
|
976
|
+
},
|
|
977
|
+
|
|
978
|
+
setReportContentMode(mode) {
|
|
979
|
+
this.reportContentMode = mode === 'smart' ? 'smart' : 'source';
|
|
980
|
+
},
|
|
981
|
+
|
|
982
|
+
openSmartReportStyleModal() {
|
|
983
|
+
if (!this.smartReportAgent || this.smartReportLoading) return;
|
|
984
|
+
this.smartReportStyleModalOpen = true;
|
|
985
|
+
},
|
|
986
|
+
|
|
987
|
+
closeSmartReportStyleModal() {
|
|
988
|
+
this.smartReportStyleModalOpen = false;
|
|
989
|
+
},
|
|
990
|
+
|
|
991
|
+
async confirmSmartReportStyle(style) {
|
|
992
|
+
this.smartReportStyle = style === 'workhorse' ? 'workhorse' : 'default';
|
|
993
|
+
localStorage.setItem(STORAGE.SMART_REPORT_STYLE, this.smartReportStyle);
|
|
994
|
+
this.closeSmartReportStyleModal();
|
|
995
|
+
this.resetSmartReportDisplay();
|
|
996
|
+
await this.generateSmartReportContent();
|
|
997
|
+
},
|
|
998
|
+
|
|
999
|
+
applySmartReportRecord(record, meta = {}) {
|
|
1000
|
+
if (record?.style) this.smartReportStyle = record.style;
|
|
1001
|
+
this.smartReportRecord = record || null;
|
|
1002
|
+
this.smartReportMarkdown = record?.markdown || '';
|
|
1003
|
+
this.smartReportHtml = this.renderMarkdownToReportHtml(this.smartReportMarkdown);
|
|
1004
|
+
this.smartReportRecordMeta = record ? this.formatSmartReportRecordMeta(record) : '';
|
|
1005
|
+
this.smartReportNeedsUpdate = !!record && !!meta.needsUpdate;
|
|
1006
|
+
this.smartReportUpdateMessage = this.smartReportNeedsUpdate ? '当前统计数据或原始报告已变化,建议重新生成智能报告。' : '';
|
|
1007
|
+
if (!record && this.reportContentMode === 'smart') this.reportContentMode = 'source';
|
|
1008
|
+
},
|
|
1009
|
+
|
|
1010
|
+
applySmartReportJob(job) {
|
|
1011
|
+
this.smartReportJob = job || null;
|
|
1012
|
+
if (!job) {
|
|
1013
|
+
this.smartReportLoading = false;
|
|
1014
|
+
this.smartReportStatusMessage = '';
|
|
1015
|
+
this.smartReportStartedAt = '';
|
|
1016
|
+
this.smartReportProgress = 0;
|
|
1017
|
+
this.stopSmartReportPolling();
|
|
1018
|
+
this.stopSmartReportElapsedTimer();
|
|
1019
|
+
this.stopSmartReportCompletionTimer();
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
if (job.status === 'running') {
|
|
1023
|
+
this.stopSmartReportCompletionTimer();
|
|
1024
|
+
this.smartReportLoading = true;
|
|
1025
|
+
this.smartReportError = '';
|
|
1026
|
+
this.smartReportStartedAt = job.startedAt || this.smartReportStartedAt || new Date().toISOString();
|
|
1027
|
+
if (this.smartReportProgress <= 0) this.smartReportProgress = 4;
|
|
1028
|
+
this.updateSmartReportProgress();
|
|
1029
|
+
this.startSmartReportElapsedTimer();
|
|
1030
|
+
this.smartReportStatusMessage = '后台生成中,页面可刷新,回来后会继续显示进度。';
|
|
1031
|
+
this.scheduleSmartReportPolling();
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
if (job.status === 'completed') {
|
|
1035
|
+
this.finishSmartReportProgress();
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
this.smartReportLoading = false;
|
|
1039
|
+
this.smartReportStatusMessage = '';
|
|
1040
|
+
this.smartReportStartedAt = '';
|
|
1041
|
+
this.smartReportProgress = 0;
|
|
1042
|
+
this.stopSmartReportPolling();
|
|
1043
|
+
this.stopSmartReportElapsedTimer();
|
|
1044
|
+
this.stopSmartReportCompletionTimer();
|
|
1045
|
+
if (job.status === 'failed') {
|
|
1046
|
+
this.smartReportError = job.error || '智能报告生成失败';
|
|
1047
|
+
showToast(this.smartReportError);
|
|
1048
|
+
}
|
|
1049
|
+
},
|
|
1050
|
+
|
|
1051
|
+
startSmartReportElapsedTimer() {
|
|
1052
|
+
this.smartReportNow = Date.now();
|
|
1053
|
+
if (this.smartReportElapsedTimer) return;
|
|
1054
|
+
this.smartReportElapsedTimer = setInterval(() => {
|
|
1055
|
+
this.smartReportNow = Date.now();
|
|
1056
|
+
this.updateSmartReportProgress();
|
|
1057
|
+
}, 1000);
|
|
1058
|
+
},
|
|
1059
|
+
|
|
1060
|
+
stopSmartReportElapsedTimer() {
|
|
1061
|
+
if (this.smartReportElapsedTimer) {
|
|
1062
|
+
clearInterval(this.smartReportElapsedTimer);
|
|
1063
|
+
this.smartReportElapsedTimer = null;
|
|
1064
|
+
}
|
|
1065
|
+
},
|
|
1066
|
+
|
|
1067
|
+
stopSmartReportCompletionTimer() {
|
|
1068
|
+
if (this.smartReportCompletionTimer) {
|
|
1069
|
+
clearTimeout(this.smartReportCompletionTimer);
|
|
1070
|
+
this.smartReportCompletionTimer = null;
|
|
1071
|
+
}
|
|
1072
|
+
},
|
|
1073
|
+
|
|
1074
|
+
updateSmartReportProgress() {
|
|
1075
|
+
if (!this.smartReportLoading || this.smartReportProgress >= 100) return;
|
|
1076
|
+
const startedAt = Date.parse(this.smartReportStartedAt || this.smartReportJob?.startedAt || '');
|
|
1077
|
+
if (!Number.isFinite(startedAt)) {
|
|
1078
|
+
this.smartReportProgress = Math.max(this.smartReportProgress, 4);
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
const seconds = Math.max(0, Math.floor((Date.now() - startedAt) / 1000));
|
|
1082
|
+
const eased = 1 - Math.exp(-seconds / 180);
|
|
1083
|
+
const target = Math.min(95, Math.round(6 + eased * 89));
|
|
1084
|
+
this.smartReportProgress = Math.max(this.smartReportProgress, target);
|
|
1085
|
+
},
|
|
1086
|
+
|
|
1087
|
+
finishSmartReportProgress() {
|
|
1088
|
+
this.stopSmartReportPolling();
|
|
1089
|
+
this.stopSmartReportElapsedTimer();
|
|
1090
|
+
this.stopSmartReportCompletionTimer();
|
|
1091
|
+
this.smartReportLoading = true;
|
|
1092
|
+
this.smartReportError = '';
|
|
1093
|
+
this.smartReportProgress = 100;
|
|
1094
|
+
this.smartReportStatusMessage = '生成完成,正在展示结果...';
|
|
1095
|
+
this.smartReportCompletionTimer = setTimeout(() => {
|
|
1096
|
+
this.smartReportCompletionTimer = null;
|
|
1097
|
+
this.smartReportLoading = false;
|
|
1098
|
+
this.smartReportStatusMessage = '';
|
|
1099
|
+
this.smartReportStartedAt = '';
|
|
1100
|
+
}, 1200);
|
|
1101
|
+
},
|
|
1102
|
+
|
|
1103
|
+
get smartReportElapsedLabel() {
|
|
1104
|
+
if (!this.smartReportLoading) return '';
|
|
1105
|
+
if (this.smartReportProgress >= 100) return '100%';
|
|
1106
|
+
const startedAt = Date.parse(this.smartReportStartedAt || this.smartReportJob?.startedAt || '');
|
|
1107
|
+
if (!Number.isFinite(startedAt)) return '正在启动后台任务';
|
|
1108
|
+
const seconds = Math.max(0, Math.floor((this.smartReportNow - startedAt) / 1000));
|
|
1109
|
+
if (seconds < 60) return `已等待 ${seconds} 秒`;
|
|
1110
|
+
const minutes = Math.floor(seconds / 60);
|
|
1111
|
+
const rest = seconds % 60;
|
|
1112
|
+
return `已等待 ${minutes} 分 ${String(rest).padStart(2, '0')} 秒`;
|
|
1113
|
+
},
|
|
1114
|
+
|
|
1115
|
+
scheduleSmartReportPolling() {
|
|
1116
|
+
this.stopSmartReportPolling();
|
|
1117
|
+
this.smartReportPollTimer = setTimeout(() => {
|
|
1118
|
+
this.smartReportPollTimer = null;
|
|
1119
|
+
this.loadSmartReportRecord();
|
|
1120
|
+
}, 2500);
|
|
1121
|
+
},
|
|
1122
|
+
|
|
1123
|
+
stopSmartReportPolling() {
|
|
1124
|
+
if (this.smartReportPollTimer) {
|
|
1125
|
+
clearTimeout(this.smartReportPollTimer);
|
|
1126
|
+
this.smartReportPollTimer = null;
|
|
1127
|
+
}
|
|
1128
|
+
},
|
|
1129
|
+
|
|
1130
|
+
formatSmartReportRecordMeta(record) {
|
|
1131
|
+
const updatedAt = record?.updatedAt ? new Date(record.updatedAt) : null;
|
|
1132
|
+
const time = updatedAt && !Number.isNaN(updatedAt.getTime()) ? updatedAt.toLocaleString('zh-CN', { hour12: false }) : '';
|
|
1133
|
+
const count = record?.generatedCount ? `第 ${record.generatedCount} 次生成` : '已生成';
|
|
1134
|
+
const styleLabel = record?.style === 'workhorse' ? '牛马风格' : '默认风格';
|
|
1135
|
+
return time ? `${styleLabel} · ${count} · ${time}` : `${styleLabel} · ${count}`;
|
|
1136
|
+
},
|
|
1137
|
+
|
|
1138
|
+
async loadSmartReportRecord() {
|
|
1139
|
+
if (!this.smartReportAgent) {
|
|
1140
|
+
this.resetSmartReportDisplay();
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
try {
|
|
1144
|
+
const data = await fetchSmartReportRecord(this.smartReportParams());
|
|
1145
|
+
this.smartReportError = '';
|
|
1146
|
+
this.applySmartReportRecord(data.record || null, { needsUpdate: data.needsUpdate });
|
|
1147
|
+
this.applySmartReportJob(data.job || null);
|
|
1148
|
+
if (data.job?.status === 'completed' && data.record) this.reportContentMode = 'smart';
|
|
1149
|
+
} catch (err) {
|
|
1150
|
+
console.warn('loadSmartReportRecord failed:', err);
|
|
1151
|
+
this.applySmartReportRecord(null);
|
|
1152
|
+
this.applySmartReportJob(null);
|
|
1153
|
+
}
|
|
1154
|
+
},
|
|
1155
|
+
|
|
1156
|
+
async generateSmartReportContent() {
|
|
1157
|
+
if (!this.smartReportAgent || this.smartReportLoading) return;
|
|
1158
|
+
this.smartReportLoading = true;
|
|
1159
|
+
this.smartReportError = '';
|
|
1160
|
+
this.smartReportStartedAt = new Date().toISOString();
|
|
1161
|
+
this.smartReportProgress = 4;
|
|
1162
|
+
this.smartReportStatusMessage = '正在提交后台生成任务...';
|
|
1163
|
+
this.startSmartReportElapsedTimer();
|
|
1164
|
+
try {
|
|
1165
|
+
const payload = this.smartReportParams();
|
|
1166
|
+
const data = await generateSmartReport(payload);
|
|
1167
|
+
this.applySmartReportRecord(data.record || (data.markdown ? { ...payload, markdown: data.markdown, generatedCount: 1, updatedAt: new Date().toISOString() } : null), { needsUpdate: false });
|
|
1168
|
+
this.applySmartReportJob(data.job || null);
|
|
1169
|
+
if (data.record && !data.job) this.reportContentMode = 'smart';
|
|
1170
|
+
} catch (err) {
|
|
1171
|
+
this.smartReportError = err.message || '智能报告生成失败';
|
|
1172
|
+
this.stopSmartReportElapsedTimer();
|
|
1173
|
+
showToast(this.smartReportError);
|
|
1174
|
+
} finally {
|
|
1175
|
+
if (this.smartReportJob?.status !== 'running') this.smartReportLoading = false;
|
|
1176
|
+
}
|
|
1177
|
+
},
|
|
1178
|
+
|
|
1179
|
+
async copySmartReport() {
|
|
1180
|
+
if (!this.smartReportMarkdown) return;
|
|
1181
|
+
await navigator.clipboard.writeText(this.smartReportMarkdown);
|
|
1182
|
+
this.smartReportCopied = true;
|
|
1183
|
+
setTimeout(() => this.smartReportCopied = false, 1400);
|
|
1184
|
+
},
|
|
1185
|
+
|
|
1186
|
+
downloadSmartReport() {
|
|
1187
|
+
if (!this.smartReportMarkdown) return;
|
|
1188
|
+
const blob = new Blob([this.smartReportMarkdown], { type: 'text/markdown;charset=utf-8' });
|
|
1189
|
+
const a = document.createElement('a');
|
|
1190
|
+
a.href = URL.createObjectURL(blob);
|
|
1191
|
+
a.download = `smart-report-${this.period}-${this.currentDate}.md`;
|
|
1192
|
+
a.click();
|
|
1193
|
+
URL.revokeObjectURL(a.href);
|
|
1194
|
+
},
|
|
1195
|
+
|
|
883
1196
|
async copyReport() {
|
|
884
1197
|
await copyWorkReport();
|
|
885
1198
|
this.copied = true;
|
package/public/config.js
CHANGED
|
@@ -7,6 +7,8 @@ export const API = {
|
|
|
7
7
|
SESSIONS: '/api/sessions',
|
|
8
8
|
STEP_STATS: '/api/step-stats',
|
|
9
9
|
HOOKS: '/api/hooks',
|
|
10
|
+
SMART_REPORT_TOOLS: '/api/smart-report/tools',
|
|
11
|
+
SMART_REPORT: '/api/smart-report',
|
|
10
12
|
};
|
|
11
13
|
|
|
12
14
|
// 灰阶色板(按视觉权重从重到轻)
|
|
@@ -141,4 +143,6 @@ export const STORAGE = {
|
|
|
141
143
|
CONFIG: 'ccusage-config',
|
|
142
144
|
THEME: 'lc-theme',
|
|
143
145
|
SIDEBAR_COLLAPSED: 'ccusage-sidebar-collapsed',
|
|
146
|
+
SMART_REPORT_AGENT: 'lc-smart-report-agent',
|
|
147
|
+
SMART_REPORT_STYLE: 'lc-smart-report-style',
|
|
144
148
|
};
|
package/public/index.html
CHANGED
|
@@ -562,7 +562,6 @@
|
|
|
562
562
|
<div style="display:flex;border:1px solid var(--border);border-radius:6px;overflow:hidden;">
|
|
563
563
|
<button class="period-btn" :class="reportLevel === 'detailed' ? 'active' : ''" @click="setReportLevel('detailed')" style="border-left:none">详细 Detail</button>
|
|
564
564
|
<button class="period-btn" :class="reportLevel === 'brief' ? 'active' : ''" @click="setReportLevel('brief')">简略 Brief</button>
|
|
565
|
-
<button class="period-btn" :class="reportLevel === 'boss' ? 'active' : ''" @click="setReportLevel('boss')">汇报 Boss</button>
|
|
566
565
|
</div>
|
|
567
566
|
<button class="btn btn-outline" @click="copyReport()" style="display:inline-flex;align-items:center;gap:6px;">
|
|
568
567
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
|
@@ -630,7 +629,22 @@
|
|
|
630
629
|
|
|
631
630
|
<!-- Report Content -->
|
|
632
631
|
<div style="margin-top:48px;display:grid;grid-template-columns:5fr 2fr;gap:24px;">
|
|
633
|
-
<div class="work-report-content" id="reportContent"
|
|
632
|
+
<div class="work-report-content report-content-frame" id="reportContent">
|
|
633
|
+
<div class="report-content-toggle">
|
|
634
|
+
<button class="report-toggle-btn" :class="reportContentMode === 'source' ? 'active' : ''" @click="setReportContentMode('source')">原报告</button>
|
|
635
|
+
<button class="report-toggle-btn" :class="reportContentMode === 'smart' ? 'active' : ''" @click="setReportContentMode('smart')" :disabled="!smartReportMarkdown">
|
|
636
|
+
<span>智能报告</span>
|
|
637
|
+
<span class="report-toggle-dot" x-show="smartReportNeedsUpdate" x-cloak></span>
|
|
638
|
+
</button>
|
|
639
|
+
</div>
|
|
640
|
+
<div x-show="reportContentMode === 'source'" x-html="reportHtml"></div>
|
|
641
|
+
<div x-show="reportContentMode === 'smart'" x-cloak>
|
|
642
|
+
<div class="smart-report-update-notice" x-show="smartReportNeedsUpdate" x-cloak>
|
|
643
|
+
当前统计数据或原始报告已变化,这份 AI 报告可能不是最新内容。建议点击右侧“重新生成”更新。
|
|
644
|
+
</div>
|
|
645
|
+
<div x-html="smartReportHtml"></div>
|
|
646
|
+
</div>
|
|
647
|
+
</div>
|
|
634
648
|
|
|
635
649
|
<!-- Sticky Meta Panel -->
|
|
636
650
|
<aside>
|
|
@@ -639,6 +653,51 @@
|
|
|
639
653
|
<span class="label">SUMMARY · 摘要</span>
|
|
640
654
|
<p style="font-size:13px;line-height:1.7;opacity:0.85;margin-top:12px;" x-html="reportSummary"></p>
|
|
641
655
|
</div>
|
|
656
|
+
<!-- Smart Report -->
|
|
657
|
+
<div class="card" style="padding:16px 20px;">
|
|
658
|
+
<div style="display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px;">
|
|
659
|
+
<span class="label">SMART REPORT · 智能报告</span>
|
|
660
|
+
<span class="label" x-show="smartReportRecordMeta" x-text="smartReportRecordMeta"></span>
|
|
661
|
+
</div>
|
|
662
|
+
<p style="font-size:12px;line-height:1.6;opacity:0.7;margin-bottom:12px;">
|
|
663
|
+
基于当前统计维度和工作汇报生成。智能体只接收受限统计上下文。
|
|
664
|
+
</p>
|
|
665
|
+
<div class="smart-agent-list">
|
|
666
|
+
<template x-for="agent in smartReportTools" :key="agent.name">
|
|
667
|
+
<button class="smart-agent-btn" :class="{ active: smartReportAgent === agent.name, unavailable: !agent.detected }" @click="setSmartReportAgent(agent.name)" :disabled="!agent.detected" :title="agent.detected ? agent.version : agent.error">
|
|
668
|
+
<span class="smart-agent-dot" :class="agent.detected ? 'available' : 'missing'"></span>
|
|
669
|
+
<span class="smart-agent-name" x-text="agent.displayName"></span>
|
|
670
|
+
<span class="smart-agent-status" x-text="agent.detected ? '可连接' : '未检测'"></span>
|
|
671
|
+
</button>
|
|
672
|
+
</template>
|
|
673
|
+
</div>
|
|
674
|
+
<button class="btn btn-primary" style="width:100%;justify-content:center;" @click="openSmartReportStyleModal()" :disabled="smartReportLoading || !smartReportAgent">
|
|
675
|
+
<span class="smart-report-spinner" x-show="smartReportLoading" x-cloak></span>
|
|
676
|
+
<span x-text="smartReportLoading ? '生成中...' : (smartReportMarkdown ? (smartReportNeedsUpdate ? '更新智能报告' : '重新生成') : '生成智能报告')"></span>
|
|
677
|
+
</button>
|
|
678
|
+
<div x-show="smartReportTools.length > 0 && !smartReportTools.some(agent => agent.detected)" x-cloak style="margin-top:12px;padding:10px 12px;border:1px solid var(--border);border-radius:6px;font-size:12px;line-height:1.6;opacity:0.75;">
|
|
679
|
+
未检测到可用的本地智能体命令。请确认 Claude Code、Codex 或 OpenCode 已安装,并且对应命令在启动 LumenCode 的终端 PATH 中可用。
|
|
680
|
+
</div>
|
|
681
|
+
<div x-show="smartReportError" x-cloak style="margin-top:12px;padding:10px 12px;border:1px solid var(--dest);border-radius:6px;color:var(--dest);font-size:12px;" x-text="smartReportError"></div>
|
|
682
|
+
<div x-show="smartReportNeedsUpdate" x-cloak style="margin-top:12px;padding:10px 12px;border:1px solid var(--dest);border-radius:6px;color:var(--dest);font-size:12px;line-height:1.6;" x-text="smartReportUpdateMessage"></div>
|
|
683
|
+
<div class="smart-report-progress" x-show="smartReportLoading" x-cloak>
|
|
684
|
+
<div class="smart-report-progress-head">
|
|
685
|
+
<span x-text="smartReportStatusMessage || '正在调用本地智能体分析当前报告数据...'"></span>
|
|
686
|
+
<span class="font-mono" x-text="smartReportElapsedLabel"></span>
|
|
687
|
+
</div>
|
|
688
|
+
<div class="smart-report-progress-track" role="progressbar" aria-valuemin="0" aria-valuemax="100" :aria-valuenow="smartReportProgress" :aria-valuetext="smartReportProgress + '%'">
|
|
689
|
+
<span class="smart-report-progress-bar" :style="'width:' + smartReportProgress + '%'"></span>
|
|
690
|
+
</div>
|
|
691
|
+
<div class="smart-report-progress-foot">本地智能体生成时间受报告大小和模型响应影响,可刷新页面后继续查看。</div>
|
|
692
|
+
</div>
|
|
693
|
+
<div x-show="smartReportMarkdown" x-cloak style="margin-top:14px;display:flex;gap:8px;">
|
|
694
|
+
<button class="btn btn-outline" style="flex:1;justify-content:center;" @click="setReportContentMode('smart')">查看</button>
|
|
695
|
+
<button class="btn btn-outline" style="flex:1;justify-content:center;" @click="copySmartReport()">
|
|
696
|
+
<span x-text="smartReportCopied ? '已复制' : '复制'"></span>
|
|
697
|
+
</button>
|
|
698
|
+
<button class="btn btn-outline" @click="downloadSmartReport()">.md</button>
|
|
699
|
+
</div>
|
|
700
|
+
</div>
|
|
642
701
|
<!-- Project Selector -->
|
|
643
702
|
<div class="card" style="padding:16px 20px;" x-show="reportProjects.length > 1" x-cloak>
|
|
644
703
|
<span class="label">PROJECTS · 项目</span>
|
|
@@ -677,6 +736,31 @@
|
|
|
677
736
|
</div>
|
|
678
737
|
</main>
|
|
679
738
|
|
|
739
|
+
<!-- ── Smart Report Style Modal ── -->
|
|
740
|
+
<div id="smart-report-style-modal" class="modal-overlay" x-show="smartReportStyleModalOpen" x-cloak>
|
|
741
|
+
<div class="modal-backdrop" @click="closeSmartReportStyleModal()"></div>
|
|
742
|
+
<div class="modal-panel" style="max-width:460px;">
|
|
743
|
+
<div class="modal-header">
|
|
744
|
+
<h3 style="font-size:15px;font-weight:500;">选择智能报告风格</h3>
|
|
745
|
+
<button class="rail-btn-icon" @click="closeSmartReportStyleModal()" style="color:var(--foreground);opacity:0.65;">
|
|
746
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
|
747
|
+
</button>
|
|
748
|
+
</div>
|
|
749
|
+
<div class="modal-body" style="padding:16px 24px;">
|
|
750
|
+
<div class="smart-report-style-options">
|
|
751
|
+
<button class="smart-report-style-option" :class="smartReportStyle === 'default' ? 'active' : ''" @click="confirmSmartReportStyle('default')">
|
|
752
|
+
<span class="smart-report-style-title">默认风格</span>
|
|
753
|
+
<span class="smart-report-style-desc">专业分析口径,强调数据摘要、工作亮点、关键洞察和风险建议。</span>
|
|
754
|
+
</button>
|
|
755
|
+
<button class="smart-report-style-option" :class="smartReportStyle === 'workhorse' ? 'active' : ''" @click="confirmSmartReportStyle('workhorse')">
|
|
756
|
+
<span class="smart-report-style-title">牛马</span>
|
|
757
|
+
<span class="smart-report-style-desc">面向领导汇报的表达倾向,突出工作投入、产出价值、风险兜底和下一步计划。</span>
|
|
758
|
+
</button>
|
|
759
|
+
</div>
|
|
760
|
+
</div>
|
|
761
|
+
</div>
|
|
762
|
+
</div>
|
|
763
|
+
|
|
680
764
|
<!-- ── Hooks Confirm Modal ── -->
|
|
681
765
|
<div id="hooksConfirmModal" class="modal-overlay" style="display:none;">
|
|
682
766
|
<div class="modal-backdrop" @click="hideHooksConfirmModal()"></div>
|
|
@@ -855,4 +939,3 @@
|
|
|
855
939
|
<script type="module" src="/app.js"></script>
|
|
856
940
|
</body>
|
|
857
941
|
</html>
|
|
858
|
-
|