repo-wrapped 0.0.2

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 (51) hide show
  1. package/README.md +94 -0
  2. package/dist/cli.js +24 -0
  3. package/dist/commands/generate.js +95 -0
  4. package/dist/commands/index.js +24 -0
  5. package/dist/constants/chronotypes.js +23 -0
  6. package/dist/constants/colors.js +18 -0
  7. package/dist/constants/index.js +18 -0
  8. package/dist/formatters/index.js +17 -0
  9. package/dist/formatters/timeFormatter.js +29 -0
  10. package/dist/generators/html/scripts/export.js +125 -0
  11. package/dist/generators/html/scripts/knowledge.js +120 -0
  12. package/dist/generators/html/scripts/modal.js +68 -0
  13. package/dist/generators/html/scripts/navigation.js +156 -0
  14. package/dist/generators/html/scripts/tabs.js +18 -0
  15. package/dist/generators/html/scripts/tooltip.js +21 -0
  16. package/dist/generators/html/styles/achievements.css +387 -0
  17. package/dist/generators/html/styles/base.css +818 -0
  18. package/dist/generators/html/styles/components.css +1391 -0
  19. package/dist/generators/html/styles/knowledge.css +221 -0
  20. package/dist/generators/html/templates/achievementsSection.js +156 -0
  21. package/dist/generators/html/templates/commitQualitySection.js +89 -0
  22. package/dist/generators/html/templates/contributionGraph.js +73 -0
  23. package/dist/generators/html/templates/impactSection.js +117 -0
  24. package/dist/generators/html/templates/knowledgeSection.js +226 -0
  25. package/dist/generators/html/templates/streakSection.js +42 -0
  26. package/dist/generators/html/templates/timePatternsSection.js +110 -0
  27. package/dist/generators/html/utils/colorUtils.js +21 -0
  28. package/dist/generators/html/utils/commitMapBuilder.js +24 -0
  29. package/dist/generators/html/utils/dateRangeCalculator.js +57 -0
  30. package/dist/generators/html/utils/developerStatsCalculator.js +29 -0
  31. package/dist/generators/html/utils/scriptLoader.js +16 -0
  32. package/dist/generators/html/utils/styleLoader.js +18 -0
  33. package/dist/generators/html/utils/weekGrouper.js +28 -0
  34. package/dist/index.js +77 -0
  35. package/dist/types/index.js +2 -0
  36. package/dist/utils/achievementDefinitions.js +433 -0
  37. package/dist/utils/achievementEngine.js +170 -0
  38. package/dist/utils/commitQualityAnalyzer.js +368 -0
  39. package/dist/utils/fileHotspotAnalyzer.js +270 -0
  40. package/dist/utils/gitParser.js +125 -0
  41. package/dist/utils/htmlGenerator.js +449 -0
  42. package/dist/utils/impactAnalyzer.js +248 -0
  43. package/dist/utils/knowledgeDistributionAnalyzer.js +374 -0
  44. package/dist/utils/matrixGenerator.js +350 -0
  45. package/dist/utils/slideGenerator.js +313 -0
  46. package/dist/utils/streakCalculator.js +135 -0
  47. package/dist/utils/timePatternAnalyzer.js +305 -0
  48. package/dist/utils/wrappedDisplay.js +115 -0
  49. package/dist/utils/wrappedGenerator.js +377 -0
  50. package/dist/utils/wrappedHtmlGenerator.js +552 -0
  51. package/package.json +55 -0
@@ -0,0 +1,221 @@
1
+ /* === Knowledge Distribution Section === */
2
+
3
+ /* Deep Analysis Badge */
4
+ .deep-analysis-badge {
5
+ display: inline-block;
6
+ background: var(--accent-blue);
7
+ color: var(--text-primary);
8
+ font-size: var(--font-size-xs);
9
+ font-weight: var(--font-weight-medium);
10
+ padding: var(--spacing-xs) var(--spacing-sm);
11
+ border-radius: var(--radius-full);
12
+ margin-left: var(--spacing-sm);
13
+ vertical-align: middle;
14
+ }
15
+
16
+ /* Knowledge Table */
17
+ .knowledge-table {
18
+ width: 100%;
19
+ border-collapse: collapse;
20
+ }
21
+
22
+ .knowledge-table th.expandable-header {
23
+ padding-left: calc(var(--spacing-md) + 16px);
24
+ }
25
+
26
+ /* Expandable Rows */
27
+ .expandable-row {
28
+ cursor: pointer;
29
+ transition: background-color var(--transition-fast);
30
+ }
31
+
32
+ .expandable-row:hover {
33
+ background-color: var(--bg-secondary);
34
+ }
35
+
36
+ .expandable-row:focus {
37
+ outline: 2px solid var(--accent-blue);
38
+ outline-offset: -2px;
39
+ }
40
+
41
+ .expandable-row .dir-path {
42
+ display: flex;
43
+ align-items: center;
44
+ gap: var(--spacing-xs);
45
+ }
46
+
47
+ .expand-icon {
48
+ display: inline-block;
49
+ width: 16px;
50
+ font-size: 10px;
51
+ color: var(--text-secondary);
52
+ transition: transform var(--transition-fast);
53
+ }
54
+
55
+ .expandable-row.expanded .expand-icon {
56
+ transform: rotate(90deg);
57
+ }
58
+
59
+ /* Last Activity Column */
60
+ .last-activity {
61
+ font-family: var(--font-mono);
62
+ font-size: var(--font-size-sm);
63
+ color: var(--text-secondary);
64
+ }
65
+
66
+ /* Expanded Content Row */
67
+ .expanded-content {
68
+ background: var(--bg-secondary);
69
+ }
70
+
71
+ .expanded-content.hidden {
72
+ display: none;
73
+ }
74
+
75
+ .expansion-panel {
76
+ padding: var(--spacing-md) var(--spacing-lg);
77
+ overflow: hidden;
78
+ transition: max-height 0.3s ease-out, opacity 0.2s ease-out;
79
+ }
80
+
81
+ /* Contributor Breakdown */
82
+ .contributor-breakdown {
83
+ margin-bottom: var(--spacing-lg);
84
+ }
85
+
86
+ .contributor-breakdown h4,
87
+ .file-breakdown h4 {
88
+ font-size: var(--font-size-sm);
89
+ font-weight: var(--font-weight-semibold);
90
+ color: var(--text-secondary);
91
+ margin-bottom: var(--spacing-sm);
92
+ text-transform: uppercase;
93
+ letter-spacing: 0.05em;
94
+ }
95
+
96
+ .contributors-list {
97
+ display: grid;
98
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
99
+ gap: var(--spacing-md);
100
+ }
101
+
102
+ .contributor-item {
103
+ background: var(--bg-tertiary);
104
+ border-radius: var(--radius-md);
105
+ padding: var(--spacing-sm) var(--spacing-md);
106
+ }
107
+
108
+ .contributor-name {
109
+ font-weight: var(--font-weight-medium);
110
+ color: var(--text-primary);
111
+ display: block;
112
+ margin-bottom: var(--spacing-xs);
113
+ }
114
+
115
+ .contributor-stats {
116
+ display: flex;
117
+ gap: var(--spacing-md);
118
+ font-size: var(--font-size-xs);
119
+ color: var(--text-secondary);
120
+ margin-bottom: var(--spacing-xs);
121
+ }
122
+
123
+ .contributor-commits {
124
+ color: var(--text-secondary);
125
+ }
126
+
127
+ .contributor-percent {
128
+ font-weight: var(--font-weight-medium);
129
+ color: var(--accent-blue);
130
+ }
131
+
132
+ .contributor-age {
133
+ font-family: var(--font-mono);
134
+ }
135
+
136
+ .contributor-bar {
137
+ height: 4px;
138
+ background: var(--bg-primary);
139
+ border-radius: var(--radius-full);
140
+ overflow: hidden;
141
+ }
142
+
143
+ .contributor-fill {
144
+ height: 100%;
145
+ background: linear-gradient(90deg, var(--accent-blue), var(--accent-purple));
146
+ border-radius: var(--radius-full);
147
+ transition: width 0.3s ease-out;
148
+ }
149
+
150
+ /* File Breakdown Table */
151
+ .file-breakdown {
152
+ margin-top: var(--spacing-md);
153
+ }
154
+
155
+ .file-table {
156
+ width: 100%;
157
+ border-collapse: collapse;
158
+ font-size: var(--font-size-sm);
159
+ }
160
+
161
+ .file-table th {
162
+ text-align: left;
163
+ font-weight: var(--font-weight-medium);
164
+ color: var(--text-secondary);
165
+ padding: var(--spacing-xs) var(--spacing-sm);
166
+ border-bottom: 1px solid var(--border-subtle);
167
+ font-size: var(--font-size-xs);
168
+ text-transform: uppercase;
169
+ letter-spacing: 0.05em;
170
+ }
171
+
172
+ .file-table td {
173
+ padding: var(--spacing-xs) var(--spacing-sm);
174
+ border-bottom: 1px solid var(--border-subtle);
175
+ }
176
+
177
+ .file-table tr:last-child td {
178
+ border-bottom: none;
179
+ }
180
+
181
+ .file-table tr.high-risk-file {
182
+ background-color: rgba(255, 87, 87, 0.08);
183
+ }
184
+
185
+ .file-path {
186
+ font-family: var(--font-mono);
187
+ font-size: var(--font-size-xs);
188
+ color: var(--text-primary);
189
+ max-width: 200px;
190
+ overflow: hidden;
191
+ text-overflow: ellipsis;
192
+ white-space: nowrap;
193
+ }
194
+
195
+ .file-owner {
196
+ color: var(--text-secondary);
197
+ }
198
+
199
+ .file-ownership {
200
+ font-family: var(--font-mono);
201
+ color: var(--text-primary);
202
+ }
203
+
204
+ .file-age {
205
+ font-family: var(--font-mono);
206
+ color: var(--text-secondary);
207
+ }
208
+
209
+ .more-files {
210
+ font-size: var(--font-size-xs);
211
+ color: var(--text-secondary);
212
+ font-style: italic;
213
+ margin-top: var(--spacing-sm);
214
+ text-align: center;
215
+ }
216
+
217
+ /* Risk Badge Enhancement for Files */
218
+ .file-table .risk-badge {
219
+ font-size: var(--font-size-xs);
220
+ padding: 2px 6px;
221
+ }
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildAchievementsSection = void 0;
4
+ // Helper: Build SVG progress ring
5
+ function buildProgressRing(progress, size = 50) {
6
+ const radius = (size / 2) - 4;
7
+ const circumference = 2 * Math.PI * radius;
8
+ const offset = circumference * (1 - progress / 100);
9
+ return `
10
+ <svg class="progress-ring" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">
11
+ <circle class="progress-ring-bg" cx="${size / 2}" cy="${size / 2}" r="${radius}"
12
+ fill="none" stroke="var(--border-subtle)" stroke-width="3" />
13
+ <circle class="progress-ring-fill" cx="${size / 2}" cy="${size / 2}" r="${radius}"
14
+ fill="none" stroke="var(--accent-blue)" stroke-width="3"
15
+ stroke-dasharray="${circumference}"
16
+ stroke-dashoffset="${offset}"
17
+ stroke-linecap="round"
18
+ transform="rotate(-90 ${size / 2} ${size / 2})" />
19
+ </svg>
20
+ `;
21
+ }
22
+ // Helper: Build individual achievement card
23
+ function buildAchievementCard(achievement) {
24
+ const cardClass = achievement.isUnlocked ? 'unlocked' : 'locked';
25
+ const showProgress = !achievement.isUnlocked && achievement.progress > 0;
26
+ return `
27
+ <div class="achievement-card ${cardClass}">
28
+ ${showProgress ? `
29
+ <div class="achievement-ring-wrapper">
30
+ ${buildProgressRing(achievement.progress, 60)}
31
+ </div>
32
+ ` : ''}
33
+ <div class="achievement-emoji">${achievement.emoji}</div>
34
+ <div class="achievement-name">${achievement.name}</div>
35
+ ${!achievement.isSecret || achievement.isUnlocked ? `
36
+ <div class="achievement-description">${achievement.description}</div>
37
+ ` : `
38
+ <div class="achievement-description">???</div>
39
+ `}
40
+ <div class="badge-tier ${achievement.tier}">${achievement.tier}</div>
41
+ ${showProgress ? `
42
+ <div class="achievement-progress-text">${achievement.progress.toFixed(0)}%</div>
43
+ ` : ''}
44
+ </div>
45
+ `;
46
+ }
47
+ // Helper: Generate growth narrative
48
+ function buildGrowthNarrative(progress) {
49
+ const { unlockedCount, totalCount, completionPercentage, categories, nextMilestones } = progress;
50
+ // Find strongest category
51
+ let strongestCategory = '';
52
+ let strongestCount = 0;
53
+ if (categories && categories.length > 0) {
54
+ for (const cat of categories) {
55
+ if (cat.unlocked > strongestCount) {
56
+ strongestCount = cat.unlocked;
57
+ strongestCategory = cat.name;
58
+ }
59
+ }
60
+ }
61
+ // Get next milestone info
62
+ let nextMilestoneText = '';
63
+ if (nextMilestones && nextMilestones.length > 0) {
64
+ const next = nextMilestones[0];
65
+ const remaining = 100 - next.progress;
66
+ nextMilestoneText = `Next milestone: <strong>${next.name}</strong> — ${remaining.toFixed(0)}% to go.`;
67
+ }
68
+ return `
69
+ <div class="growth-narrative">
70
+ <h3>Your Developer Journey</h3>
71
+ <p class="narrative-text">
72
+ You've unlocked <strong>${unlockedCount} of ${totalCount}</strong> achievements (${completionPercentage.toFixed(0)}%).
73
+ ${strongestCategory ? `Your strongest area is <strong>${strongestCategory}</strong> with ${strongestCount} badges earned.` : ''}
74
+ ${nextMilestoneText}
75
+ </p>
76
+ </div>
77
+ `;
78
+ }
79
+ function buildAchievementsSection(achievementProgress) {
80
+ return `
81
+ <div class="achievements-section">
82
+ <h2>Achievements</h2>
83
+
84
+ ${buildGrowthNarrative(achievementProgress)}
85
+
86
+ <div class="achievement-stats">
87
+ <div class="stat-card">
88
+ <div class="stat-value">${achievementProgress.unlockedCount}</div>
89
+ <div class="stat-label">Badges Unlocked</div>
90
+ </div>
91
+ <div class="stat-card">
92
+ <div class="stat-value">${achievementProgress.completionPercentage.toFixed(0)}%</div>
93
+ <div class="stat-label">Completion</div>
94
+ </div>
95
+ <div class="stat-card">
96
+ <div class="stat-value">${achievementProgress.totalCount - achievementProgress.unlockedCount}</div>
97
+ <div class="stat-label">Remaining</div>
98
+ </div>
99
+ </div>
100
+
101
+ ${achievementProgress.nextMilestones && achievementProgress.nextMilestones.length > 0 ? `
102
+ <div class="next-milestones">
103
+ <h3>Next Milestones</h3>
104
+ ${achievementProgress.nextMilestones.slice(0, 3).map(milestone => `
105
+ <div class="milestone-item">
106
+ <div class="milestone-header">
107
+ <span class="milestone-emoji">${milestone.emoji}</span>
108
+ <span class="milestone-name">${milestone.name}</span>
109
+ </div>
110
+ <div class="milestone-description">${milestone.description}</div>
111
+ <div class="milestone-progress-bar">
112
+ <div class="milestone-progress-fill" style="width: ${milestone.progress}%"></div>
113
+ <div class="milestone-progress-text">${milestone.progress.toFixed(0)}%</div>
114
+ </div>
115
+ </div>
116
+ `).join('')}
117
+ </div>
118
+ ` : ''}
119
+
120
+ ${achievementProgress.newBadges && achievementProgress.newBadges.length > 0 ? `
121
+ <div class="new-badges">
122
+ <h3>Recently Unlocked</h3>
123
+ <div class="badge-list">
124
+ ${achievementProgress.newBadges.map(badge => `
125
+ <div class="badge-item-mini">${badge.emoji} ${badge.name}</div>
126
+ `).join('')}
127
+ </div>
128
+ </div>
129
+ ` : ''}
130
+
131
+ ${achievementProgress.categories ? `
132
+ <div class="achievement-categories">
133
+ ${achievementProgress.categories.map(category => `
134
+ <div class="category-section">
135
+ <h3>${category.emoji} ${category.name}</h3>
136
+ <div class="category-progress">
137
+ <div class="category-bar">
138
+ <div class="category-fill" style="width: ${category.progress}%"></div>
139
+ </div>
140
+ <span class="category-count">${category.unlocked}/${category.total}</span>
141
+ </div>
142
+ <div class="achievement-grid">
143
+ ${category.achievements.map(achievement => buildAchievementCard(achievement)).join('')}
144
+ </div>
145
+ </div>
146
+ `).join('')}
147
+ </div>
148
+ ` : achievementProgress.achievements && achievementProgress.achievements.length > 0 ? `
149
+ <div class="achievements-grid">
150
+ ${achievementProgress.achievements.map(achievement => buildAchievementCard(achievement)).join('')}
151
+ </div>
152
+ ` : ''}
153
+ </div>
154
+ `;
155
+ }
156
+ exports.buildAchievementsSection = buildAchievementsSection;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildCommitQualitySection = void 0;
4
+ function getQualityLabel(score) {
5
+ if (score >= 9)
6
+ return 'Exceptional';
7
+ if (score >= 8)
8
+ return 'Excellent';
9
+ if (score >= 7)
10
+ return 'Good';
11
+ if (score >= 6)
12
+ return 'Fair';
13
+ return 'Needs Attention';
14
+ }
15
+ function getQualityIndicator(value, threshold) {
16
+ if (value >= threshold * 0.8)
17
+ return '✓';
18
+ if (value >= threshold * 0.5)
19
+ return '○';
20
+ return '–';
21
+ }
22
+ function buildCommitQualitySection(commitQuality, totalCommits, skipBodyCheck = false) {
23
+ const qualityLabel = getQualityLabel(commitQuality.overallScore);
24
+ return `
25
+ <div class="commit-quality-section">
26
+ <h2>Commit Quality</h2>
27
+
28
+ <div class="quality-summary">
29
+ <div class="quality-score-display">
30
+ <div class="score-value">${commitQuality.overallScore.toFixed(1)}</div>
31
+ <div class="score-max">/ 10</div>
32
+ <div class="score-label">${qualityLabel}</div>
33
+ </div>
34
+
35
+ <div class="quality-breakdown">
36
+ <div class="quality-metric">
37
+ <div class="metric-label">${getQualityIndicator(commitQuality.conventionalCommits.adherence, 80)} Convention Adherence</div>
38
+ <div class="metric-bar-container">
39
+ <div class="metric-bar" style="width: ${commitQuality.conventionalCommits.adherence}%"></div>
40
+ </div>
41
+ <div class="metric-value">${commitQuality.conventionalCommits.adherence.toFixed(1)}%</div>
42
+ </div>
43
+
44
+ <div class="quality-metric">
45
+ <div class="metric-label">${getQualityIndicator(commitQuality.subjectQuality.score, 7)} Subject Quality</div>
46
+ <div class="metric-bar-container">
47
+ <div class="metric-bar" style="width: ${(commitQuality.subjectQuality.avgLength / 72) * 100}%"></div>
48
+ </div>
49
+ <div class="metric-value">${commitQuality.subjectQuality.score.toFixed(1)}/10 (avg ${commitQuality.subjectQuality.avgLength} chars)</div>
50
+ </div>
51
+
52
+ ${!skipBodyCheck ? `
53
+ <div class="quality-metric">
54
+ <div class="metric-label">${getQualityIndicator(commitQuality.bodyQuality.score, 5)} Body Quality</div>
55
+ <div class="metric-bar-container">
56
+ <div class="metric-bar" style="width: ${(commitQuality.bodyQuality.withBody / totalCommits) * 100}%"></div>
57
+ </div>
58
+ <div class="metric-value">${commitQuality.bodyQuality.score.toFixed(1)}/10 (${commitQuality.bodyQuality.withBody}/${totalCommits} with body)</div>
59
+ </div>
60
+ ` : ''}
61
+ </div>
62
+ </div>
63
+
64
+ ${Object.keys(commitQuality.conventionalCommits.types).length > 0 ? `
65
+ <div class="commit-types-chart">
66
+ <h3>Commit Type Distribution</h3>
67
+ <div class="commit-types-list">
68
+ ${Object.entries(commitQuality.conventionalCommits.types).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([type, count]) => `
69
+ <div class="commit-type-row">
70
+ <span class="commit-type">${type}:</span>
71
+ <span class="commit-type-count">${count}</span>
72
+ </div>
73
+ `).join('')}
74
+ </div>
75
+ </div>
76
+ ` : ''}
77
+
78
+ ${commitQuality.improvements.length > 0 ? `
79
+ <div class="improvement-tips">
80
+ <h3>Recommendations</h3>
81
+ <ul>
82
+ ${commitQuality.improvements.map((tip) => `<li>${tip}</li>`).join('')}
83
+ </ul>
84
+ </div>
85
+ ` : ''}
86
+ </div>
87
+ `;
88
+ }
89
+ exports.buildCommitQualitySection = buildCommitQualitySection;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildContributionGraph = void 0;
4
+ const date_fns_1 = require("date-fns");
5
+ function buildContributionGraph(options) {
6
+ const { commitMap, commitDetailsMap, weeks, dayLabels, dataStartDate, dataEndDate, totalCommits, title, getColorFn } = options;
7
+ // Build week columns
8
+ let weekColumns = '';
9
+ weeks.forEach((week) => {
10
+ let weekColumn = `<div class="graph-column">`;
11
+ week.forEach((day) => {
12
+ const dateKey = (0, date_fns_1.format)(day, 'yyyy-MM-dd');
13
+ const count = commitMap.get(dateKey) || 0;
14
+ const isInRange = day >= dataStartDate && day <= dataEndDate;
15
+ const color = isInRange ? getColorFn(count) : 'transparent';
16
+ const dateStr = (0, date_fns_1.format)(day, 'MMM d, yyyy');
17
+ const emptyClass = count === 0 ? ' empty' : '';
18
+ const clickable = count > 0 ? ' clickable' : '';
19
+ const detailsData = count > 0 ? `data-details='${JSON.stringify(commitDetailsMap.get(dateKey) || [])}'` : '';
20
+ weekColumn += `<div class="day${emptyClass}${clickable}" style="background-color: ${color};" data-count="${count}" data-date="${dateStr}" ${detailsData}></div>`;
21
+ });
22
+ weekColumn += '</div>';
23
+ weekColumns += weekColumn;
24
+ });
25
+ // Build day labels column
26
+ let dayLabelsHtml = '<div class="day-labels">';
27
+ dayLabels.forEach((label, index) => {
28
+ const displayLabel = [1, 3, 5].includes(index) ? label : '';
29
+ dayLabelsHtml += `<div class="day-label">${displayLabel}</div>`;
30
+ });
31
+ dayLabelsHtml += '</div>';
32
+ // Build month labels header
33
+ let monthLabelsHtml = '';
34
+ let currentMonth = '';
35
+ weeks.forEach((week) => {
36
+ const firstDayOfWeek = week[0];
37
+ const monthName = (0, date_fns_1.format)(firstDayOfWeek, 'MMM');
38
+ if (monthName !== currentMonth) {
39
+ monthLabelsHtml += `<div class="month-label">${monthName}</div>`;
40
+ currentMonth = monthName;
41
+ }
42
+ else {
43
+ monthLabelsHtml += `<div class="month-label"></div>`;
44
+ }
45
+ });
46
+ return `
47
+ <div class="stats">
48
+ ${title}: <strong>${totalCommits}</strong> commits
49
+ </div>
50
+
51
+ <div class="graph-container">
52
+ <div class="months">
53
+ ${monthLabelsHtml}
54
+ </div>
55
+
56
+ <div class="graph">
57
+ ${dayLabelsHtml}
58
+ ${weekColumns}
59
+ </div>
60
+
61
+ <div class="legend">
62
+ <span>Less</span>
63
+ <span class="legend-box" style="background-color: rgba(235, 237, 240, 0.1); border: 1px solid #21262d;"></span>
64
+ <span class="legend-box" style="background-color: #9be9a8;"></span>
65
+ <span class="legend-box" style="background-color: #40c463;"></span>
66
+ <span class="legend-box" style="background-color: #30a14e;"></span>
67
+ <span class="legend-box" style="background-color: #216e39;"></span>
68
+ <span>More</span>
69
+ </div>
70
+ </div>
71
+ `;
72
+ }
73
+ exports.buildContributionGraph = buildContributionGraph;
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildImpactSection = void 0;
4
+ /**
5
+ * Build the Impact Analysis section for the dashboard
6
+ */
7
+ function buildImpactSection(impact) {
8
+ const trendIcon = impact.impactTrend === 'increasing' ? '↑' : impact.impactTrend === 'decreasing' ? '↓' : '→';
9
+ const trendClass = impact.impactTrend === 'increasing' ? 'trend-up' : impact.impactTrend === 'decreasing' ? 'trend-down' : 'trend-stable';
10
+ return `
11
+ <div class="impact-section">
12
+ <h2>Impact Analysis</h2>
13
+
14
+ <div class="impact-overview">
15
+ <div class="impact-score-display">
16
+ <div class="score-circle">
17
+ <span class="score-value">${impact.overallScore}</span>
18
+ <span class="score-max">/100</span>
19
+ </div>
20
+ <div class="score-meta">
21
+ <span class="score-label">Impact Score</span>
22
+ <span class="score-trend ${trendClass}">${trendIcon} ${impact.impactTrend}</span>
23
+ </div>
24
+ </div>
25
+
26
+ <div class="impact-breakdown">
27
+ <h3>Contribution Breakdown</h3>
28
+ <div class="breakdown-items">
29
+ <div class="breakdown-item">
30
+ <div class="breakdown-header">
31
+ <span class="breakdown-label">Core Contributions</span>
32
+ <span class="breakdown-value">${impact.scoreBreakdown.coreContributions}%</span>
33
+ </div>
34
+ <div class="breakdown-bar">
35
+ <div class="breakdown-fill core" style="width: ${impact.scoreBreakdown.coreContributions}%"></div>
36
+ </div>
37
+ </div>
38
+
39
+ <div class="breakdown-item">
40
+ <div class="breakdown-header">
41
+ <span class="breakdown-label">Feature Work</span>
42
+ <span class="breakdown-value">${impact.scoreBreakdown.featureWork}%</span>
43
+ </div>
44
+ <div class="breakdown-bar">
45
+ <div class="breakdown-fill feature" style="width: ${impact.scoreBreakdown.featureWork}%"></div>
46
+ </div>
47
+ </div>
48
+
49
+ <div class="breakdown-item">
50
+ <div class="breakdown-header">
51
+ <span class="breakdown-label">Maintenance</span>
52
+ <span class="breakdown-value">${impact.scoreBreakdown.maintenanceWork}%</span>
53
+ </div>
54
+ <div class="breakdown-bar">
55
+ <div class="breakdown-fill maintenance" style="width: ${impact.scoreBreakdown.maintenanceWork}%"></div>
56
+ </div>
57
+ </div>
58
+
59
+ <div class="breakdown-item">
60
+ <div class="breakdown-header">
61
+ <span class="breakdown-label">Documentation</span>
62
+ <span class="breakdown-value">${impact.scoreBreakdown.documentationWork}%</span>
63
+ </div>
64
+ <div class="breakdown-bar">
65
+ <div class="breakdown-fill docs" style="width: ${impact.scoreBreakdown.documentationWork}%"></div>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </div>
71
+
72
+ ${impact.topImpactFiles.length > 0 ? `
73
+ <div class="impact-files">
74
+ <h3>Highest Impact Changes</h3>
75
+ <table class="data-table">
76
+ <thead>
77
+ <tr>
78
+ <th>File</th>
79
+ <th>Category</th>
80
+ <th>Changes</th>
81
+ <th>Impact</th>
82
+ </tr>
83
+ </thead>
84
+ <tbody>
85
+ ${impact.topImpactFiles.map(file => `
86
+ <tr>
87
+ <td class="file-path">${truncatePath(file.path)}</td>
88
+ <td><span class="category-badge ${file.category}">${file.category}</span></td>
89
+ <td>${file.changeCount}</td>
90
+ <td><span class="impact-badge">${file.impactScore.toFixed(1)}</span></td>
91
+ </tr>
92
+ `).join('')}
93
+ </tbody>
94
+ </table>
95
+ </div>
96
+ ` : ''}
97
+
98
+ ${impact.insights.length > 0 ? `
99
+ <div class="insights-list">
100
+ <h3>Insights</h3>
101
+ <ul>
102
+ ${impact.insights.map(insight => `<li>${insight}</li>`).join('')}
103
+ </ul>
104
+ </div>
105
+ ` : ''}
106
+ </div>
107
+ `;
108
+ }
109
+ exports.buildImpactSection = buildImpactSection;
110
+ /**
111
+ * Truncate file path for display
112
+ */
113
+ function truncatePath(path, maxLength = 40) {
114
+ if (path.length <= maxLength)
115
+ return path;
116
+ return '...' + path.slice(-maxLength + 3);
117
+ }