git-flex 1.1.0 → 1.1.1

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/svg.js +79 -46
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-flex",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "type": "module",
5
5
  "description": "Show off your coding stats in style. Beautiful terminal cards, shareable SVGs, and fun highlights from your git history.",
6
6
  "main": "src/index.js",
package/src/svg.js CHANGED
@@ -21,31 +21,70 @@ export function generateSVG(stats, streakData, repoName, periodLabel, theme = 'd
21
21
  const rank = getRank(stats, streakData);
22
22
  const level = getLevel(stats.commits);
23
23
  const highlights = getHighlights(stats, streakData);
24
- const W = 480, H = 430;
24
+ const W = 480;
25
+ const F = `font-family="'SF Mono','Fira Code',monospace"`;
25
26
 
26
- // Language bars
27
+ // Dynamic Y layout — each section pushes cursor down
28
+ let y = 0;
29
+
30
+ // Header: 0-54
31
+ y = 54;
32
+
33
+ // User + rank + level: 55-116
34
+ y = 116;
35
+
36
+ // Stats grid row 1: values at y+26, y+44
37
+ const statsY = y + 10; // 126 — labels
38
+ const statsValY = statsY + 18; // 144 — values
39
+
40
+ // Stats grid row 2
41
+ const stats2Y = statsValY + 25; // 169 — labels
42
+ const stats2ValY = stats2Y + 18; // 187 — values
43
+
44
+ // Highlights
45
+ y = stats2ValY + 20; // 207
46
+ const hlY = y;
47
+
48
+ // Language section
49
+ y = hlY + highlights.length * 18 + 14;
50
+ const langTitleY = y;
51
+ const langBarY = y + 14;
52
+ const langLabelY = langBarY + 20;
53
+
54
+ // Sparkline section
55
+ y = langLabelY + 16;
56
+ const sparkTitleY = y;
57
+ const sparkBaseY = sparkTitleY + 50;
58
+
59
+ // Footer
60
+ const footerY = sparkBaseY + 20;
61
+ const H = footerY + 10;
62
+
63
+ // Build language bars
27
64
  let langBars = '';
28
65
  let langLabels = '';
29
66
  let xOffset = 30;
30
67
  const barW = W - 60;
31
- for (const lang of stats.languages.slice(0, 6)) {
68
+ const langs = stats.languages.slice(0, 6);
69
+ const langColors = ['#58a6ff', '#3fb950', '#d29922', '#bc8cff', '#f85149', '#79c0ff'];
70
+ for (let i = 0; i < langs.length; i++) {
71
+ const lang = langs[i];
32
72
  const w = Math.max(2, (lang.pct / 100) * barW);
33
- const colors = ['#58a6ff', '#3fb950', '#d29922', '#bc8cff', '#f85149', '#79c0ff'];
34
- const color = colors[stats.languages.indexOf(lang) % colors.length];
35
- langBars += `<rect x="${xOffset}" y="290" width="${w}" height="8" rx="2" fill="${color}"/>`;
73
+ langBars += `<rect x="${xOffset}" y="${langBarY}" width="${w}" height="8" rx="2" fill="${langColors[i % langColors.length]}"/>`;
36
74
  if (lang.pct >= 8) {
37
- langLabels += `<text x="${xOffset + w / 2}" y="316" fill="${c.textDim}" font-size="10" text-anchor="middle" font-family="'SF Mono','Fira Code',monospace">${lang.name} ${lang.pct}%</text>`;
75
+ langLabels += `<text x="${xOffset + w / 2}" y="${langLabelY}" fill="${c.textDim}" font-size="10" text-anchor="middle" ${F}>${lang.name} ${lang.pct}%</text>`;
38
76
  }
39
77
  xOffset += w + 2;
40
78
  }
41
79
 
42
- // Hour activity chart (mini sparkline)
43
- const maxH = Math.max(...stats.hourCounts, 1);
80
+ // Build sparkline
81
+ const maxHour = Math.max(...stats.hourCounts, 1);
44
82
  let sparkline = '';
83
+ const sparkH = 40;
45
84
  for (let i = 0; i < 24; i++) {
46
- const h = (stats.hourCounts[i] / maxH) * 40;
85
+ const h = (stats.hourCounts[i] / maxHour) * sparkH;
47
86
  const x = 30 + i * ((W - 60) / 24);
48
- sparkline += `<rect x="${x}" y="${340 - h}" width="${(W - 60) / 24 - 1}" height="${h}" rx="1" fill="${c.accent}" opacity="0.6"/>`;
87
+ sparkline += `<rect x="${x}" y="${sparkBaseY - h}" width="${(W - 60) / 24 - 1}" height="${h}" rx="1" fill="${c.accent}" opacity="0.6"/>`;
49
88
  }
50
89
 
51
90
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
@@ -67,72 +106,66 @@ export function generateSVG(stats, streakData, repoName, periodLabel, theme = 'd
67
106
  <!-- Background -->
68
107
  <rect width="${W}" height="${H}" rx="12" fill="url(#bg)" stroke="${c.border}" stroke-width="1"/>
69
108
 
70
- <!-- Header line -->
109
+ <!-- Header -->
71
110
  <rect x="0" y="54" width="${W}" height="1" fill="${c.border}"/>
72
-
73
- <!-- Title -->
74
- <text x="30" y="36" fill="${c.accent}" font-size="16" font-weight="bold" font-family="'SF Mono','Fira Code',monospace" filter="url(#glow)">FLEX</text>
75
- <text x="78" y="36" fill="${c.textDim}" font-size="13" font-family="'SF Mono','Fira Code',monospace">${escXml(repoName)} \u2022 ${escXml(periodLabel)}</text>
111
+ <text x="30" y="36" fill="${c.accent}" font-size="16" font-weight="bold" ${F} filter="url(#glow)">FLEX</text>
112
+ <text x="78" y="36" fill="${c.textDim}" font-size="13" ${F}>${escXml(repoName)} \u2022 ${escXml(periodLabel)}</text>
76
113
 
77
114
  <!-- User + Rank -->
78
- <text x="30" y="82" fill="${c.yellow}" font-size="15" font-weight="bold" font-family="'SF Mono','Fira Code',monospace">${escXml(stats.author)}</text>
79
- <text x="${30 + stats.author.length * 9.5}" y="82" fill="${c.purple}" font-size="13" font-style="italic" font-family="'SF Mono','Fira Code',monospace"> ${rank.icon} ${escXml(rank.title)}</text>
115
+ <text x="30" y="82" fill="${c.yellow}" font-size="15" font-weight="bold" ${F}>${escXml(stats.author)}</text>
116
+ <text x="${30 + stats.author.length * 9.5}" y="82" fill="${c.purple}" font-size="13" font-style="italic" ${F}> ${rank.icon} ${escXml(rank.title)}</text>
80
117
 
81
118
  <!-- Level bar -->
82
- <text x="30" y="105" fill="${c.textDim}" font-size="11" font-family="'SF Mono','Fira Code',monospace">LVL ${level.level} \u2022 ${escXml(level.name)}</text>
119
+ <text x="30" y="105" fill="${c.textDim}" font-size="11" ${F}>LVL ${level.level} \u2022 ${escXml(level.name)}</text>
83
120
  <rect x="160" y="96" width="120" height="8" rx="4" fill="${c.border}"/>
84
121
  <rect x="160" y="96" width="${level.level * 12}" height="8" rx="4" fill="url(#accent)"/>
85
122
 
86
123
  <!-- Separator -->
87
124
  <rect x="30" y="116" width="${W - 60}" height="1" fill="${c.border}"/>
88
125
 
89
- <!-- Stats grid -->
90
- <text x="30" y="142" fill="${c.textDim}" font-size="10" font-family="'SF Mono','Fira Code',monospace">COMMITS</text>
91
- <text x="30" y="160" fill="${c.text}" font-size="18" font-weight="bold" font-family="'SF Mono','Fira Code',monospace">${formatNumber(stats.commits)}</text>
126
+ <!-- Stats row 1 -->
127
+ <text x="30" y="${statsY}" fill="${c.textDim}" font-size="10" ${F}>COMMITS</text>
128
+ <text x="30" y="${statsValY}" fill="${c.text}" font-size="18" font-weight="bold" ${F}>${formatNumber(stats.commits)}</text>
92
129
 
93
- <text x="140" y="142" fill="${c.textDim}" font-size="10" font-family="'SF Mono','Fira Code',monospace">ADDED</text>
94
- <text x="140" y="160" fill="${c.green}" font-size="18" font-weight="bold" font-family="'SF Mono','Fira Code',monospace">+${formatNumber(stats.added)}</text>
130
+ <text x="140" y="${statsY}" fill="${c.textDim}" font-size="10" ${F}>ADDED</text>
131
+ <text x="140" y="${statsValY}" fill="${c.green}" font-size="18" font-weight="bold" ${F}>+${formatNumber(stats.added)}</text>
95
132
 
96
- <text x="250" y="142" fill="${c.textDim}" font-size="10" font-family="'SF Mono','Fira Code',monospace">REMOVED</text>
97
- <text x="250" y="160" fill="${c.red}" font-size="18" font-weight="bold" font-family="'SF Mono','Fira Code',monospace">-${formatNumber(stats.removed)}</text>
133
+ <text x="250" y="${statsY}" fill="${c.textDim}" font-size="10" ${F}>REMOVED</text>
134
+ <text x="250" y="${statsValY}" fill="${c.red}" font-size="18" font-weight="bold" ${F}>-${formatNumber(stats.removed)}</text>
98
135
 
99
- <text x="370" y="142" fill="${c.textDim}" font-size="10" font-family="'SF Mono','Fira Code',monospace">FILES</text>
100
- <text x="370" y="160" fill="${c.text}" font-size="18" font-weight="bold" font-family="'SF Mono','Fira Code',monospace">${formatNumber(stats.filesCount)}</text>
136
+ <text x="370" y="${statsY}" fill="${c.textDim}" font-size="10" ${F}>FILES</text>
137
+ <text x="370" y="${statsValY}" fill="${c.text}" font-size="18" font-weight="bold" ${F}>${formatNumber(stats.filesCount)}</text>
101
138
 
102
- <!-- Row 2 -->
103
- <text x="30" y="195" fill="${c.textDim}" font-size="10" font-family="'SF Mono','Fira Code',monospace">PEAK HOUR</text>
104
- <text x="30" y="213" fill="${c.text}" font-size="14" font-weight="bold" font-family="'SF Mono','Fira Code',monospace">${formatHour(stats.peakHour)}</text>
139
+ <!-- Stats row 2 -->
140
+ <text x="30" y="${stats2Y}" fill="${c.textDim}" font-size="10" ${F}>PEAK HOUR</text>
141
+ <text x="30" y="${stats2ValY}" fill="${c.text}" font-size="14" font-weight="bold" ${F}>${formatHour(stats.peakHour)}</text>
105
142
 
106
- <text x="140" y="195" fill="${c.textDim}" font-size="10" font-family="'SF Mono','Fira Code',monospace">STREAK</text>
107
- <text x="140" y="213" fill="${c.yellow}" font-size="14" font-weight="bold" font-family="'SF Mono','Fira Code',monospace">${streakData.current}d \u{1F525}</text>
143
+ <text x="140" y="${stats2Y}" fill="${c.textDim}" font-size="10" ${F}>STREAK</text>
144
+ <text x="140" y="${stats2ValY}" fill="${c.yellow}" font-size="14" font-weight="bold" ${F}>${streakData.current}d \u{1F525}</text>
108
145
 
109
- <text x="250" y="195" fill="${c.textDim}" font-size="10" font-family="'SF Mono','Fira Code',monospace">BEST STREAK</text>
110
- <text x="250" y="213" fill="${c.text}" font-size="14" font-weight="bold" font-family="'SF Mono','Fira Code',monospace">${streakData.longest}d</text>
146
+ <text x="250" y="${stats2Y}" fill="${c.textDim}" font-size="10" ${F}>TOP STREAK</text>
147
+ <text x="250" y="${stats2ValY}" fill="${c.text}" font-size="14" font-weight="bold" ${F}>${streakData.longest}d</text>
111
148
 
112
- <text x="370" y="195" fill="${c.textDim}" font-size="10" font-family="'SF Mono','Fira Code',monospace">NET LINES</text>
113
- <text x="370" y="213" fill="${stats.net >= 0 ? c.green : c.red}" font-size="14" font-weight="bold" font-family="'SF Mono','Fira Code',monospace">${stats.net >= 0 ? '+' : ''}${formatNumber(stats.net)}</text>
149
+ <text x="370" y="${stats2Y}" fill="${c.textDim}" font-size="10" ${F}>NET LINES</text>
150
+ <text x="370" y="${stats2ValY}" fill="${stats.net >= 0 ? c.green : c.red}" font-size="14" font-weight="bold" ${F}>${stats.net >= 0 ? '+' : ''}${formatNumber(stats.net)}</text>
114
151
 
115
152
  <!-- Highlights -->
116
- ${highlights.map((h, i) => `<text x="30" y="${250 + i * 18}" fill="${c.yellow}" font-size="11" font-style="italic" font-family="'SF Mono','Fira Code',monospace">&gt; ${escXml(h)}</text>`).join('\n ')}
153
+ ${highlights.map((h, i) => `<text x="30" y="${hlY + i * 18}" fill="${c.yellow}" font-size="11" font-style="italic" ${F}>&gt; ${escXml(h)}</text>`).join('\n ')}
117
154
 
118
- <!-- Language bars -->
155
+ <!-- Languages -->
156
+ <text x="30" y="${langTitleY}" fill="${c.textDim}" font-size="10" ${F}>LANGUAGES</text>
119
157
  ${langBars}
120
158
  ${langLabels}
121
159
 
122
160
  <!-- Activity sparkline -->
123
- <text x="30" y="340" fill="${c.textDim}" font-size="10" font-family="'SF Mono','Fira Code',monospace">ACTIVITY BY HOUR</text>
161
+ <text x="30" y="${sparkTitleY}" fill="${c.textDim}" font-size="10" ${F}>ACTIVITY BY HOUR</text>
124
162
  ${sparkline}
125
163
 
126
164
  <!-- Footer -->
127
- <text x="${W - 30}" y="${H - 12}" fill="${c.textDim}" font-size="9" text-anchor="end" font-family="'SF Mono','Fira Code',monospace">generated by flex</text>
165
+ <text x="${W - 30}" y="${footerY}" fill="${c.textDim}" font-size="9" text-anchor="end" ${F}>generated by git-flex</text>
128
166
  </svg>`;
129
167
  }
130
168
 
131
169
  function escXml(s) {
132
170
  return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
133
171
  }
134
-
135
- function truncPath(p, max) {
136
- if (p.length <= max) return p;
137
- return '...' + p.slice(-(max - 3));
138
- }