codebuddy-stats 1.1.2 → 1.1.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.
package/dist/index.js CHANGED
@@ -40,19 +40,6 @@ Options:
40
40
  }
41
41
  return options;
42
42
  }
43
- // 生成热力图数据
44
- function generateHeatmapData(dailySummary) {
45
- const sortedDates = Object.keys(dailySummary).sort();
46
- if (sortedDates.length === 0)
47
- return { dates: [], costs: [], maxCost: 0 };
48
- const costs = sortedDates.map(d => dailySummary[d]?.cost ?? 0);
49
- const maxCost = Math.max(...costs);
50
- return {
51
- dates: sortedDates,
52
- costs,
53
- maxCost,
54
- };
55
- }
56
43
  // 获取热力图字符
57
44
  function getHeatChar(cost, maxCost) {
58
45
  if (cost === 0)
@@ -69,7 +56,6 @@ function getHeatChar(cost, maxCost) {
69
56
  // 渲染 Overview 视图
70
57
  function renderOverview(box, data, width, note) {
71
58
  const { dailySummary, grandTotal, topModel, topProject, cacheHitRate, activeDays } = data;
72
- const heatmap = generateHeatmapData(dailySummary);
73
59
  // 根据宽度计算热力图周数
74
60
  const availableWidth = width - 10;
75
61
  const maxWeeks = Math.min(Math.floor(availableWidth / 2), 26); // 最多 26 周 (半年)
@@ -96,7 +82,43 @@ function renderOverview(box, data, width, note) {
96
82
  }
97
83
  weeks.push(week);
98
84
  }
99
- const maxCost = heatmap.maxCost || 1;
85
+ // 以“当前热力图窗口”的最大值做归一化(避免历史极值导致近期全是浅色)
86
+ const visibleCosts = [];
87
+ for (const week of weeks) {
88
+ for (const date of week) {
89
+ if (!date || date > todayStr)
90
+ continue;
91
+ visibleCosts.push(dailySummary[date]?.cost ?? 0);
92
+ }
93
+ }
94
+ const maxCost = Math.max(...visibleCosts, 0) || 1;
95
+ // 月份标尺(在列上方标注月份变化)
96
+ const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
97
+ const colWidth = 2; // 每周一列:字符 + 空格
98
+ const heatStartCol = 4; // 左侧周几标签宽度
99
+ const headerLen = heatStartCol + weeks.length * colWidth;
100
+ const monthHeader = Array.from({ length: headerLen }, () => ' ');
101
+ let lastMonth = -1;
102
+ let lastPlacedAt = -999;
103
+ for (let i = 0; i < weeks.length; i++) {
104
+ const week = weeks[i];
105
+ const repDate = week.find(d => d && d <= todayStr) ?? week[0];
106
+ if (!repDate)
107
+ continue;
108
+ const m = new Date(repDate).getMonth();
109
+ if (m !== lastMonth) {
110
+ const label = monthNames[m];
111
+ const pos = heatStartCol + i * colWidth;
112
+ // 避免月份标签过于拥挤/相互覆盖
113
+ if (pos - lastPlacedAt >= 4 && pos + label.length <= monthHeader.length) {
114
+ for (let k = 0; k < label.length; k++)
115
+ monthHeader[pos + k] = label[k];
116
+ lastPlacedAt = pos;
117
+ }
118
+ lastMonth = m;
119
+ }
120
+ }
121
+ content += `{gray-fg}${monthHeader.join('').trimEnd()}{/gray-fg}\n`;
100
122
  const dayLabels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
101
123
  for (let dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) {
102
124
  let row = dayLabels[dayOfWeek].padEnd(4);
@@ -114,6 +136,8 @@ function renderOverview(box, data, width, note) {
114
136
  }
115
137
  content += row + '\n';
116
138
  }
139
+ const rangeStart = weeks[0]?.[0] ?? todayStr;
140
+ content += `{gray-fg}Range: ${rangeStart} → ${todayStr}{/gray-fg}\n`;
117
141
  content += ' Less {gray-fg}·░▒▓{/gray-fg}{white-fg}█{/white-fg} More\n\n';
118
142
  // 汇总指标 - 根据宽度决定布局
119
143
  const avgDailyCost = activeDays > 0 ? grandTotal.cost / activeDays : 0;
@@ -444,10 +468,29 @@ async function main() {
444
468
  function updateStatusBar() {
445
469
  const daysInfo = options.days ? `Last ${options.days} days` : 'All time';
446
470
  const sourceInfo = currentSource === 'code' ? 'Code' : 'IDE';
447
- const leftContent = ` ${daysInfo} | Source: ${sourceInfo} | Total: ${formatCost(data.grandTotal.cost)} | q quit, Tab view, s source, r refresh`;
448
- const rightContent = `v${VERSION} `;
471
+ const rightContent = `v${VERSION}`;
449
472
  const width = Number(screen.width) || 80;
450
- const padding = Math.max(0, width - leftContent.length - rightContent.length);
473
+ // 根据剩余宽度决定左侧内容详细程度(预留版本号空间)
474
+ const reservedForRight = rightContent.length + 2; // 版本号 + 两侧空格
475
+ const availableForLeft = width - reservedForRight;
476
+ let leftContent;
477
+ const fullContent = ` ${daysInfo} | Source: ${sourceInfo} | Total: ${formatCost(data.grandTotal.cost)} | q quit, Tab view, s source, r refresh`;
478
+ const mediumContent = ` ${daysInfo} | ${sourceInfo} | ${formatCost(data.grandTotal.cost)} | q/Tab/s/r`;
479
+ const shortContent = ` ${sourceInfo} | ${formatCost(data.grandTotal.cost)} | q/Tab/s/r`;
480
+ const minContent = ` ${formatCost(data.grandTotal.cost)}`;
481
+ if (fullContent.length <= availableForLeft) {
482
+ leftContent = fullContent;
483
+ }
484
+ else if (mediumContent.length <= availableForLeft) {
485
+ leftContent = mediumContent;
486
+ }
487
+ else if (shortContent.length <= availableForLeft) {
488
+ leftContent = shortContent;
489
+ }
490
+ else {
491
+ leftContent = minContent;
492
+ }
493
+ const padding = Math.max(1, width - leftContent.length - rightContent.length);
451
494
  statusBar.setContent(leftContent + ' '.repeat(padding) + rightContent);
452
495
  }
453
496
  // 键盘事件
@@ -517,7 +560,9 @@ async function main() {
517
560
  });
518
561
  // 监听窗口大小变化
519
562
  screen.on('resize', () => {
563
+ updateTabBar();
520
564
  updateContent();
565
+ updateStatusBar();
521
566
  screen.render();
522
567
  });
523
568
  // 初始渲染
package/dist/lib/paths.js CHANGED
@@ -24,7 +24,7 @@ export function getConfigDir() {
24
24
  * 获取 CodeBuddy IDE (CodeBuddyExtension) 数据目录
25
25
  * - macOS: ~/Library/Application Support/CodeBuddyExtension/Data
26
26
  * - Windows: %APPDATA%/CodeBuddyExtension/Data
27
- * - Linux: $XDG_CONFIG_HOME/CodeBuddyExtension/Data 或 ~/.config/CodeBuddyExtension/Data
27
+ * - Linux: $XDG_DATA_HOME/CodeBuddyExtension/Data 或 ~/.local/share/CodeBuddyExtension/Data
28
28
  */
29
29
  export function getIdeDataDir() {
30
30
  if (process.platform === 'darwin') {
@@ -34,8 +34,8 @@ export function getIdeDataDir() {
34
34
  const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
35
35
  return path.join(appData, 'CodeBuddyExtension', 'Data');
36
36
  }
37
- const xdgConfigHome = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
38
- return path.join(xdgConfigHome, 'CodeBuddyExtension', 'Data');
37
+ const xdgDataHome = process.env.XDG_DATA_HOME || path.join(os.homedir(), '.local', 'share');
38
+ return path.join(xdgDataHome, 'CodeBuddyExtension', 'Data');
39
39
  }
40
40
  /**
41
41
  * 获取 CodeBuddy IDE 的 workspaceStorage 目录
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebuddy-stats",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "files": [