wukong-gitlog-cli 1.0.19 → 1.0.22

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/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [1.0.22](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v1.0.21...v1.0.22) (2025-12-08)
6
+
7
+ ### [1.0.21](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v1.0.20...v1.0.21) (2025-12-08)
8
+
9
+
10
+ ### Features
11
+
12
+ * 🎸 weekly rangeMap ([8389e5e](https://github.com/tomatobybike/wukong-gitlog-cli/commit/8389e5e700640273906a4e616333e318a5961d12))
13
+
14
+ ### [1.0.20](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v1.0.19...v1.0.20) (2025-12-05)
15
+
16
+
17
+ ### Features
18
+
19
+ * 🎸 rolling30DurationRiskSummary ([b4a976e](https://github.com/tomatobybike/wukong-gitlog-cli/commit/b4a976e552cfdc1483b0f23f2a4126ecede42af3))
20
+
5
21
  ### [1.0.19](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v1.0.18...v1.0.19) (2025-12-05)
6
22
 
7
23
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wukong-gitlog-cli",
3
- "version": "1.0.19",
3
+ "version": "1.0.22",
4
4
  "description": "Advanced Git commit log exporter with Excel/JSON/TXT output, grouping, stats and CLI.",
5
5
  "keywords": [
6
6
  "git",
@@ -116,5 +116,6 @@
116
116
  "prettier-plugin-packagejson": "2.5.19",
117
117
  "sort-package-json": "3.4.0",
118
118
  "standard-version": "9.5.0"
119
- }
119
+ },
120
+ "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
120
121
  }
package/web/app.js CHANGED
@@ -16,6 +16,34 @@ function getIsoWeekKey(dStr) {
16
16
  return `${year}-W${String(weekNo).padStart(2, '0')}`
17
17
  }
18
18
 
19
+ function formatDateYMD(d) {
20
+ const yyyy = d.getFullYear()
21
+ const mm = String(d.getMonth() + 1).padStart(2, '0')
22
+ const dd = String(d.getDate()).padStart(2, '0')
23
+ return `${yyyy}-${mm}-${dd}`
24
+ }
25
+
26
+ function getISOWeekRange(isoYear, isoWeek) {
27
+ // 找到 ISO 年的第一个周一
28
+ // ISO 年的第 1 周包含 1 月 4 日
29
+ const simple = new Date(isoYear, 0, 4)
30
+ const dayOfWeek = simple.getDay() || 7 // Sunday=7
31
+ const firstMonday = new Date(simple)
32
+ firstMonday.setDate(simple.getDate() - dayOfWeek + 1)
33
+
34
+ // 计算目标周的周一
35
+ const monday = new Date(firstMonday)
36
+ monday.setDate(firstMonday.getDate() + (isoWeek - 1) * 7)
37
+
38
+ const sunday = new Date(monday)
39
+ sunday.setDate(monday.getDate() + 6)
40
+
41
+ return {
42
+ start: formatDateYMD(monday),
43
+ end: formatDateYMD(sunday)
44
+ }
45
+ }
46
+
19
47
  async function loadData() {
20
48
  try {
21
49
  const [
@@ -42,7 +70,15 @@ async function loadData() {
42
70
  const latestByDay = latestByDayModule.default || []
43
71
  const config = configModule.default || {}
44
72
  const authorChanges = authorChangesModule.default || {}
45
- return { commits, stats, weekly, monthly, latestByDay, config, authorChanges }
73
+ return {
74
+ commits,
75
+ stats,
76
+ weekly,
77
+ monthly,
78
+ latestByDay,
79
+ config,
80
+ authorChanges
81
+ }
46
82
  } catch (err) {
47
83
  console.error('Load data failed', err)
48
84
  return { commits: [], stats: {}, weekly: [], monthly: [], latestByDay: [] }
@@ -60,8 +96,6 @@ function renderCommitsTablePage() {
60
96
  const start = (page - 1) * pageSize
61
97
  const end = start + pageSize
62
98
  filtered.slice(start, end).forEach((c) => {
63
- // FIXME: remove debug log before production
64
- console.log('❌', 'c', c);
65
99
  const tr = document.createElement('tr')
66
100
  tr.innerHTML = `<td>${c.hash.slice(0, 8)}</td><td>${c.author}</td><td>${formatDate(c.date)}</td><td>${c.message}</td><td>${c.changed}</td>`
67
101
  tbody.appendChild(tr)
@@ -1244,7 +1278,6 @@ function groupCommitsByHour(commits) {
1244
1278
  return byHour
1245
1279
  }
1246
1280
 
1247
-
1248
1281
  // 基于 latestByDay + cutoff/endHour 统计「最晚加班的一天 / 一周 / 一月」
1249
1282
  function computeAndRenderLatestOvertime(latestByDay) {
1250
1283
  if (!Array.isArray(latestByDay) || latestByDay.length === 0) return
@@ -1337,21 +1370,21 @@ function computeAndRenderLatestOvertime(latestByDay) {
1337
1370
  }
1338
1371
 
1339
1372
  function buildDataset(stats, type) {
1340
- const dataMap = stats[type]; // { author: { period: changed } }
1373
+ const dataMap = stats[type] // { author: { period: changed } }
1341
1374
 
1342
- const authors = Object.keys(dataMap);
1343
- const allPeriods = Array.from(new Set(
1344
- authors.flatMap(a => Object.keys(dataMap[a]))
1345
- )).sort();
1375
+ const authors = Object.keys(dataMap)
1376
+ const allPeriods = Array.from(
1377
+ new Set(authors.flatMap((a) => Object.keys(dataMap[a])))
1378
+ ).sort()
1346
1379
 
1347
- const series = authors.map(a => ({
1380
+ const series = authors.map((a) => ({
1348
1381
  name: a,
1349
1382
  type: 'line',
1350
1383
  smooth: true,
1351
- data: allPeriods.map(p => dataMap[a][p] || 0)
1352
- }));
1384
+ data: allPeriods.map((p) => dataMap[a][p] || 0)
1385
+ }))
1353
1386
 
1354
- return { authors, allPeriods, series };
1387
+ return { authors, allPeriods, series }
1355
1388
  }
1356
1389
 
1357
1390
  const drawChangeTrends = (stats) => {
@@ -1361,6 +1394,7 @@ const drawChangeTrends = (stats) => {
1361
1394
 
1362
1395
  function render(t) {
1363
1396
  const { authors, allPeriods, series } = buildDataset(stats, t)
1397
+
1364
1398
  chart.setOption({
1365
1399
  tooltip: { trigger: 'axis' },
1366
1400
  legend: { data: authors },
@@ -1387,13 +1421,7 @@ const drawChangeTrends = (stats) => {
1387
1421
  }
1388
1422
 
1389
1423
  // ========= 开发者加班趋势(基于 commits 现场计算) =========
1390
- function buildAuthorOvertimeDataset(
1391
- commits,
1392
- type,
1393
- startHour,
1394
- endHour,
1395
- cutoff
1396
- ) {
1424
+ function buildAuthorOvertimeDataset(commits, type, startHour, endHour, cutoff) {
1397
1425
  const byAuthor = new Map()
1398
1426
  const periods = new Set()
1399
1427
 
@@ -1428,7 +1456,7 @@ function buildAuthorOvertimeDataset(
1428
1456
  name: a,
1429
1457
  type: 'line',
1430
1458
  smooth: true,
1431
- data: allPeriods.map((p) => (byAuthor.get(a)[p] || 0))
1459
+ data: allPeriods.map((p) => byAuthor.get(a)[p] || 0)
1432
1460
  }))
1433
1461
  return { authors, allPeriods, series }
1434
1462
  }
@@ -1456,8 +1484,45 @@ function drawAuthorOvertimeTrends(commits, stats) {
1456
1484
  endHour,
1457
1485
  cutoff
1458
1486
  )
1487
+ ds.rangeMap = {}
1488
+
1489
+ for (const period of ds.allPeriods) {
1490
+ if (period.includes('-W')) {
1491
+ const [yy, ww] = period.split('-W')
1492
+ ds.rangeMap[period] = getISOWeekRange(Number(yy), Number(ww))
1493
+ }
1494
+ }
1459
1495
  chart.setOption({
1460
- tooltip: { trigger: 'axis' },
1496
+ tooltip: {
1497
+ trigger: 'axis',
1498
+ formatter(params) {
1499
+ if (!params || !params.length) return ''
1500
+
1501
+ const p = params[0]
1502
+ const label = p.axisValue
1503
+ const isWeekly = type === 'weekly'
1504
+
1505
+ let extra = ''
1506
+ if (isWeekly && ds.rangeMap && ds.rangeMap[label]) {
1507
+ const { start, end } = ds.rangeMap[label]
1508
+ extra = `<div style="margin-top:4px;color:#999;font-size:12px">
1509
+ 周区间:${start} ~ ${end}
1510
+ </div>`
1511
+ }
1512
+
1513
+ const lines = params
1514
+ .map(
1515
+ (item) => `${item.marker}${item.seriesName}: ${item.data} 小时`
1516
+ )
1517
+ .join('<br/>')
1518
+
1519
+ return `
1520
+ <div>${label}</div>
1521
+ ${extra}
1522
+ ${lines}
1523
+ `
1524
+ }
1525
+ },
1461
1526
  legend: { data: ds.authors },
1462
1527
  xAxis: { type: 'category', data: ds.allPeriods },
1463
1528
  yAxis: { type: 'value' },
@@ -1483,6 +1548,7 @@ function drawAuthorOvertimeTrends(commits, stats) {
1483
1548
  renderMonthlyRiskSummary(commits, { startHour, endHour, cutoff })
1484
1549
  renderWeeklyDurationRiskSummary(commits, { startHour, endHour, cutoff })
1485
1550
  renderMonthlyDurationRiskSummary(commits, { startHour, endHour, cutoff })
1551
+ renderRolling30DurationRiskSummary(commits, { startHour, endHour, cutoff })
1486
1552
 
1487
1553
  return chart
1488
1554
  }
@@ -1532,7 +1598,9 @@ function renderWeeklyRiskSummary(
1532
1598
  const curTotal = Array.from(curMap.values()).reduce((a, b) => a + b, 0)
1533
1599
  const prevTotal = Array.from(prevMap.values()).reduce((a, b) => a + b, 0)
1534
1600
  const delta =
1535
- prevTotal > 0 ? Math.round(((curTotal - prevTotal) / prevTotal) * 100) : null
1601
+ prevTotal > 0
1602
+ ? Math.round(((curTotal - prevTotal) / prevTotal) * 100)
1603
+ : null
1536
1604
 
1537
1605
  // 找当前周最“活跃”的人(加班提交最多),并统计他加班的自然日数
1538
1606
  let topAuthor = null
@@ -1595,7 +1663,9 @@ function computeAuthorDailyMaxOvertime(commits, startHour, endHour, cutoff) {
1595
1663
  dayKey = d.toISOString().slice(0, 10)
1596
1664
  } else if (h >= 0 && h < cutoff && h < startHour) {
1597
1665
  overtime = 24 - endHour + h
1598
- const cur = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()))
1666
+ const cur = new Date(
1667
+ Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())
1668
+ )
1599
1669
  cur.setUTCDate(cur.getUTCDate() - 1)
1600
1670
  dayKey = cur.toISOString().slice(0, 10)
1601
1671
  }
@@ -1617,7 +1687,12 @@ function renderWeeklyDurationRiskSummary(
1617
1687
  if (!box) return
1618
1688
  const now = new Date()
1619
1689
  const curWeek = getIsoWeekKey(now.toISOString().slice(0, 10))
1620
- const byAuthorDay = computeAuthorDailyMaxOvertime(commits, startHour, endHour, cutoff)
1690
+ const byAuthorDay = computeAuthorDailyMaxOvertime(
1691
+ commits,
1692
+ startHour,
1693
+ endHour,
1694
+ cutoff
1695
+ )
1621
1696
  const sums = []
1622
1697
  byAuthorDay.forEach((dayMap, author) => {
1623
1698
  let total = 0
@@ -1638,7 +1713,9 @@ function renderWeeklyDurationRiskSummary(
1638
1713
  let level = '轻度'
1639
1714
  if (total >= 12) level = '严重'
1640
1715
  else if (total >= 6) level = '中度'
1641
- lines.push(`${author} 本周累计加班 ${total.toFixed(2)} 小时(${level})。`)
1716
+ lines.push(
1717
+ `${author} 本周累计加班 ${total.toFixed(2)} 小时(${level})。`
1718
+ )
1642
1719
  })
1643
1720
  }
1644
1721
  box.innerHTML = `
@@ -1662,7 +1739,12 @@ function renderMonthlyDurationRiskSummary(
1662
1739
  if (!box) return
1663
1740
  const now = new Date()
1664
1741
  const curMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
1665
- const byAuthorDay = computeAuthorDailyMaxOvertime(commits, startHour, endHour, cutoff)
1742
+ const byAuthorDay = computeAuthorDailyMaxOvertime(
1743
+ commits,
1744
+ startHour,
1745
+ endHour,
1746
+ cutoff
1747
+ )
1666
1748
  const sums = []
1667
1749
  byAuthorDay.forEach((dayMap, author) => {
1668
1750
  let total = 0
@@ -1683,7 +1765,9 @@ function renderMonthlyDurationRiskSummary(
1683
1765
  let level = '轻度'
1684
1766
  if (total >= 20) level = '严重'
1685
1767
  else if (total >= 10) level = '中度'
1686
- lines.push(`${author} 本月累计加班 ${total.toFixed(2)} 小时(${level})。`)
1768
+ lines.push(
1769
+ `${author} 本月累计加班 ${total.toFixed(2)} 小时(${level})。`
1770
+ )
1687
1771
  })
1688
1772
  }
1689
1773
  box.innerHTML = `
@@ -1698,6 +1782,62 @@ function renderMonthlyDurationRiskSummary(
1698
1782
  </div>
1699
1783
  `
1700
1784
  }
1785
+
1786
+ function renderRolling30DurationRiskSummary(
1787
+ commits,
1788
+ { startHour = 9, endHour = 18, cutoff = 6 } = {}
1789
+ ) {
1790
+ const box = document.getElementById('rolling30DurationRiskSummary')
1791
+ if (!box) return
1792
+ const now = new Date()
1793
+ const utcToday = new Date(
1794
+ Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())
1795
+ )
1796
+ utcToday.setUTCDate(utcToday.getUTCDate() - 29)
1797
+ const startKey = utcToday.toISOString().slice(0, 10)
1798
+
1799
+ const byAuthorDay = computeAuthorDailyMaxOvertime(
1800
+ commits,
1801
+ startHour,
1802
+ endHour,
1803
+ cutoff
1804
+ )
1805
+ const sums = []
1806
+ byAuthorDay.forEach((dayMap, author) => {
1807
+ let total = 0
1808
+ dayMap.forEach((v, dayKey) => {
1809
+ if (dayKey >= startKey) total += v
1810
+ })
1811
+ if (total > 0) sums.push({ author, total })
1812
+ })
1813
+ sums.sort((a, b) => b.total - a.total)
1814
+ const top = sums.slice(0, 6)
1815
+ const lines = []
1816
+ lines.push('【最近30天加班时长风险】')
1817
+ if (top.length === 0) {
1818
+ lines.push('最近30天暂无加班时长风险。')
1819
+ } else {
1820
+ top.forEach(({ author, total }) => {
1821
+ let level = '轻度'
1822
+ if (total >= 20) level = '严重'
1823
+ else if (total >= 10) level = '中度'
1824
+ lines.push(
1825
+ `${author} 最近30天累计加班 ${total.toFixed(2)} 小时(${level})。`
1826
+ )
1827
+ })
1828
+ }
1829
+ box.innerHTML = `
1830
+ <div class="risk-summary">
1831
+ <div class="risk-title">【最近30天加班时长风险】</div>
1832
+ <ul>
1833
+ ${lines
1834
+ .slice(1)
1835
+ .map((l) => `<li>${escapeHtml(l)}</li>`)
1836
+ .join('')}
1837
+ </ul>
1838
+ </div>
1839
+ `
1840
+ }
1701
1841
  function renderMonthlyRiskSummary(
1702
1842
  commits,
1703
1843
  { startHour = 9, endHour = 18, cutoff = 6 } = {}
@@ -1718,7 +1858,8 @@ function renderMonthlyRiskSummary(
1718
1858
  const d = new Date(c.date)
1719
1859
  if (Number.isNaN(d.valueOf())) return
1720
1860
  const h = d.getHours()
1721
- const isOT = (h >= endHour && h < 24) || (h >= 0 && h < cutoff && h < startHour)
1861
+ const isOT =
1862
+ (h >= endHour && h < 24) || (h >= 0 && h < cutoff && h < startHour)
1722
1863
  if (!isOT) return
1723
1864
 
1724
1865
  const key = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`
@@ -1737,14 +1878,18 @@ function renderMonthlyRiskSummary(
1737
1878
  const mm = monthMax.get(key)
1738
1879
  const cur = mm.get(author)
1739
1880
  const dateStr = d.toISOString().slice(0, 10)
1740
- if (!cur || overtime > cur.max) mm.set(author, { max: overtime, date: dateStr })
1881
+ if (!cur || overtime > cur.max)
1882
+ mm.set(author, { max: overtime, date: dateStr })
1741
1883
  })
1742
1884
 
1743
1885
  const curMap = monthAuthor.get(curKey) || new Map()
1744
1886
  const prevMap = monthAuthor.get(prevKey) || new Map()
1745
1887
  const curTotal = Array.from(curMap.values()).reduce((a, b) => a + b, 0)
1746
1888
  const prevTotal = Array.from(prevMap.values()).reduce((a, b) => a + b, 0)
1747
- const delta = prevTotal > 0 ? Math.round(((curTotal - prevTotal) / prevTotal) * 100) : null
1889
+ const delta =
1890
+ prevTotal > 0
1891
+ ? Math.round(((curTotal - prevTotal) / prevTotal) * 100)
1892
+ : null
1748
1893
 
1749
1894
  let topAuthor = null
1750
1895
  let top = { max: -1, date: null }
@@ -1782,7 +1927,9 @@ function renderMonthlyRiskSummary(
1782
1927
  else if (top.max < prevMax) trend2 = '较上月提前'
1783
1928
  else trend2 = '与上月持平'
1784
1929
  }
1785
- lines.push(`${topAuthor} 本月最晚超出下班 ${top.max.toFixed(2)} 小时(${top.date}),${trend2}。`)
1930
+ lines.push(
1931
+ `${topAuthor} 本月最晚超出下班 ${top.max.toFixed(2)} 小时(${top.date}),${trend2}。`
1932
+ )
1786
1933
  if (top.max >= 2) lines.push('已超过 2 小时,存在严重加班风险。')
1787
1934
  else if (top.max >= 1) lines.push('已超过 1 小时,存在中度加班风险。')
1788
1935
  }
@@ -1802,13 +1949,7 @@ function renderMonthlyRiskSummary(
1802
1949
  }
1803
1950
 
1804
1951
  // ========= 开发者加班“最晚”趋势(每期取最大超时) =========
1805
- function buildAuthorLatestDataset(
1806
- commits,
1807
- type,
1808
- startHour,
1809
- endHour,
1810
- cutoff
1811
- ) {
1952
+ function buildAuthorLatestDataset(commits, type, startHour, endHour, cutoff) {
1812
1953
  const byAuthor = new Map()
1813
1954
  const periods = new Set()
1814
1955
 
@@ -1819,8 +1960,7 @@ function buildAuthorLatestDataset(
1819
1960
 
1820
1961
  let overtime = null
1821
1962
  if (h >= endHour && h < 24) overtime = h - endHour
1822
- else if (h >= 0 && h < cutoff && h < startHour)
1823
- overtime = 24 - endHour + h
1963
+ else if (h >= 0 && h < cutoff && h < startHour) overtime = 24 - endHour + h
1824
1964
  if (overtime == null) return
1825
1965
 
1826
1966
  let key
@@ -1841,6 +1981,7 @@ function buildAuthorLatestDataset(
1841
1981
  })
1842
1982
 
1843
1983
  const allPeriods = Array.from(periods).sort()
1984
+
1844
1985
  const authors = Array.from(byAuthor.keys()).sort()
1845
1986
  const series = authors.map((a) => ({
1846
1987
  name: a,
@@ -1874,8 +2015,45 @@ function drawAuthorLatestOvertimeTrends(commits, stats) {
1874
2015
  endHour,
1875
2016
  cutoff
1876
2017
  )
2018
+ ds.rangeMap = {}
2019
+
2020
+ for (const period of ds.allPeriods) {
2021
+ if (period.includes('-W')) {
2022
+ const [yy, ww] = period.split('-W')
2023
+ ds.rangeMap[period] = getISOWeekRange(Number(yy), Number(ww))
2024
+ }
2025
+ }
1877
2026
  chart.setOption({
1878
- tooltip: { trigger: 'axis' },
2027
+ tooltip: {
2028
+ trigger: 'axis',
2029
+ formatter(params) {
2030
+ if (!params || !params.length) return ''
2031
+
2032
+ const p = params[0]
2033
+ const label = p.axisValue
2034
+ const isWeekly = type === 'weekly'
2035
+
2036
+ let extra = ''
2037
+ if (isWeekly && ds.rangeMap && ds.rangeMap[label]) {
2038
+ const { start, end } = ds.rangeMap[label]
2039
+ extra = `<div style="margin-top:4px;color:#999;font-size:12px">
2040
+ 周区间:${start} ~ ${end}
2041
+ </div>`
2042
+ }
2043
+
2044
+ const lines = params
2045
+ .map(
2046
+ (item) => `${item.marker}${item.seriesName}: ${item.data} 小时`
2047
+ )
2048
+ .join('<br/>')
2049
+
2050
+ return `
2051
+ <div>${label}</div>
2052
+ ${extra}
2053
+ ${lines}
2054
+ `
2055
+ }
2056
+ },
1879
2057
  legend: { data: ds.authors },
1880
2058
  xAxis: { type: 'category', data: ds.allPeriods },
1881
2059
  yAxis: {
@@ -1926,8 +2104,7 @@ function renderLatestRiskSummary(
1926
2104
  const h = d.getHours()
1927
2105
  let overtime = null
1928
2106
  if (h >= endHour && h < 24) overtime = h - endHour
1929
- else if (h >= 0 && h < cutoff && h < startHour)
1930
- overtime = 24 - endHour + h
2107
+ else if (h >= 0 && h < cutoff && h < startHour) overtime = 24 - endHour + h
1931
2108
  if (overtime == null) return
1932
2109
 
1933
2110
  const wKey = getIsoWeekKey(d.toISOString().slice(0, 10))
@@ -2059,7 +2236,9 @@ function renderLatestMonthlyRiskSummary(
2059
2236
  else if (top.max < prevMax) trend = '较上月提前'
2060
2237
  else trend = '与上月持平'
2061
2238
  }
2062
- lines.push(`${topAuthor} 本月最晚超出下班 ${top.max.toFixed(2)} 小时(${top.date}),${trend}。`)
2239
+ lines.push(
2240
+ `${topAuthor} 本月最晚超出下班 ${top.max.toFixed(2)} 小时(${top.date}),${trend}。`
2241
+ )
2063
2242
  if (top.max >= 2) {
2064
2243
  lines.push('已超过 2 小时,存在严重加班风险,请关注工作节奏。')
2065
2244
  } else if (top.max >= 1) {
@@ -2089,8 +2268,7 @@ async function main() {
2089
2268
  latestByDay,
2090
2269
  config,
2091
2270
  authorChanges
2092
- } =
2093
- await loadData()
2271
+ } = await loadData()
2094
2272
  commitsAll = commits
2095
2273
  filtered = commitsAll.slice()
2096
2274
  window.__overtimeEndHour =
@@ -2137,7 +2315,6 @@ async function main() {
2137
2315
  renderKpi(stats)
2138
2316
  }
2139
2317
 
2140
-
2141
2318
  // 抽屉关闭交互(按钮 + 点击遮罩)
2142
2319
  document.getElementById('sidebarClose').onclick = () => {
2143
2320
  document.getElementById('dayDetailSidebar').classList.remove('show')
package/web/index.html CHANGED
@@ -86,6 +86,7 @@
86
86
  <div id="monthlyRiskSummary" class="risk-summary-box"></div>
87
87
  <div id="weeklyDurationRiskSummary" class="risk-summary-box"></div>
88
88
  <div id="monthlyDurationRiskSummary" class="risk-summary-box"></div>
89
+ <div id="rolling30DurationRiskSummary" class="risk-summary-box"></div>
89
90
  </div>
90
91
 
91
92
  <div class="chart-card">
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- import('../src/cli.mjs')