lumencode 0.4.3

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.
@@ -0,0 +1,118 @@
1
+ import { ID, TEXT } from './config.js';
2
+ import { esc } from './utils.js';
3
+
4
+ // ── Markdown 渲染 ──
5
+ export function renderMarkdown(md) {
6
+ const lines = md.split('\n');
7
+ const out = [];
8
+ let inTable = false;
9
+
10
+ function inline(s) {
11
+ const safe = esc(s);
12
+ return safe.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>').replace(/`([^`]+)`/g, '<code>$1</code>');
13
+ }
14
+
15
+ for (let i = 0; i < lines.length; i++) {
16
+ const line = lines[i];
17
+
18
+ if (line.startsWith('|')) {
19
+ if (!inTable) { inTable = true; out.push('<table class="md-table">'); }
20
+ const cells = line.split('|').slice(1, -1).map(c => c.trim());
21
+ if (cells.every(c => /^[-:]+$/.test(c.replace(/\|/g, '')))) continue;
22
+ const tag = inTable && out[out.length - 1] === '<table class="md-table">' ? 'th' : 'td';
23
+ out.push('<tr>' + cells.map(c => `<${tag}>${inline(c)}</${tag}>`).join('') + '</tr>');
24
+ continue;
25
+ } else if (inTable) {
26
+ inTable = false;
27
+ out.push('</table>');
28
+ }
29
+
30
+ if (line.startsWith('# ')) { out.push(`<h1 class="md-h1">${inline(line.slice(2))}</h1>`); continue; }
31
+ if (line.startsWith('## ')) { out.push(`<h2 class="md-h2">${inline(line.slice(3))}</h2>`); continue; }
32
+ if (line.startsWith('- ') || line.startsWith('• ')) { out.push(`<li class="md-li">${inline(line.slice(2))}</li>`); continue; }
33
+ if (/^[━─]+/.test(line.trim()) && line.trim().length >= 5) { out.push(`<div class="md-divider">${inline(line.trim())}</div>`); continue; }
34
+ if (line.trim() === '') { out.push(''); continue; }
35
+ out.push(`<p class="md-p">${inline(line)}</p>`);
36
+ }
37
+
38
+ if (inTable) out.push('</table>');
39
+ let html = out.join('\n');
40
+ html = html.replace(/(<li[^>]*>[<\s\S]*?<\/li>\n?)+/g, m => '<ul class="md-ul">\n' + m + '</ul>\n');
41
+ return html;
42
+ }
43
+
44
+ // ── 工作汇报状态 ──
45
+ let currentWorkReportMarkdown = '';
46
+ let currentPlatform = 'default';
47
+ let currentLevel = 'detailed';
48
+
49
+ export function getWorkReportState() {
50
+ return { markdown: currentWorkReportMarkdown, platform: currentPlatform, level: currentLevel };
51
+ }
52
+
53
+ export function setWorkReportState(state) {
54
+ if (state.markdown !== undefined) currentWorkReportMarkdown = state.markdown;
55
+ if (state.platform !== undefined) currentPlatform = state.platform;
56
+ if (state.level !== undefined) currentLevel = state.level;
57
+ }
58
+
59
+ // ── 加载并渲染工作汇报 ──
60
+ export async function loadWorkReport(fetchFn, tool, period, date, platform, level) {
61
+ if (platform) currentPlatform = platform;
62
+ if (level) currentLevel = level;
63
+
64
+ const params = { tool, period, date, format: 'work', platform: currentPlatform, level: currentLevel };
65
+ const qs = new URLSearchParams(params).toString();
66
+ const res = await fetchFn(`/api/report?${qs}`);
67
+ if (!res.ok) return;
68
+ const markdown = await res.text();
69
+ currentWorkReportMarkdown = markdown;
70
+
71
+ let html = renderMarkdown(markdown);
72
+ const platformLabels = { default: '标准', feishu: '飞书', dingtalk: '钉钉' };
73
+ const platformClass = currentPlatform === 'feishu' ? 'feishu' : currentPlatform === 'dingtalk' ? 'dingtalk' : 'default';
74
+ const badgeHtml = `<span class="platform-badge ${platformClass}">${platformLabels[currentPlatform] || '标准'}</span>`;
75
+ html = html.replace(/(<h1 class="md-h1">.*?<\/h1>)/, `$1\n${badgeHtml}`);
76
+
77
+ const content = document.getElementById(ID.WORK_REPORT_CONTENT);
78
+ if (content) {
79
+ content.innerHTML = html;
80
+ content.classList.toggle('is-brief', currentLevel === 'brief');
81
+ }
82
+
83
+ document.getElementById(ID.STATS_GRID).style.display = 'none';
84
+ document.getElementById(ID.ANALYTICS_SECTION).style.display = 'none';
85
+ document.getElementById(ID.GIT_SECTION).style.display = 'none';
86
+ document.getElementById(ID.WORK_REPORT_SECTION).style.display = 'block';
87
+ document.getElementById(ID.WORK_REPORT_BTN).style.display = 'none';
88
+ }
89
+
90
+ // ── 复制 ──
91
+ export async function copyWorkReport() {
92
+ const text = currentWorkReportMarkdown;
93
+ const btn = document.getElementById(ID.COPY_WORK_REPORT);
94
+ try {
95
+ await navigator.clipboard.writeText(text);
96
+ if (btn) { btn.textContent = TEXT.COPIED; setTimeout(() => { btn.textContent = TEXT.COPY; }, 1500); }
97
+ } catch {
98
+ const ta = document.createElement('textarea');
99
+ ta.value = text;
100
+ document.body.appendChild(ta);
101
+ ta.select();
102
+ document.execCommand('copy');
103
+ document.body.removeChild(ta);
104
+ if (btn) { btn.textContent = TEXT.COPIED; setTimeout(() => { btn.textContent = TEXT.COPY; }, 1500); }
105
+ }
106
+ }
107
+
108
+ // ── 下载 Markdown ──
109
+ export function downloadMarkdown(period, date) {
110
+ if (!currentWorkReportMarkdown) return;
111
+ const blob = new Blob([currentWorkReportMarkdown], { type: 'text/markdown;charset=utf-8' });
112
+ const url = URL.createObjectURL(blob);
113
+ const a = document.createElement('a');
114
+ a.href = url;
115
+ a.download = `work-report-${period}-${date}.md`;
116
+ a.click();
117
+ URL.revokeObjectURL(url);
118
+ }