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,156 @@
1
+ // Navigation and Collapsible Sections
2
+ function initNavigation() {
3
+ // Sidebar navigation - tab switching
4
+ const sidebarNavItems = document.querySelectorAll('.sidebar .nav-item[data-tab]');
5
+ const tabButtons = document.querySelectorAll('.tab-button');
6
+ const tabContents = document.querySelectorAll('.tab-content');
7
+
8
+ // Handle sidebar nav clicks for tab switching
9
+ sidebarNavItems.forEach(item => {
10
+ item.addEventListener('click', (e) => {
11
+ e.preventDefault();
12
+ const targetTab = item.dataset.tab;
13
+
14
+ if (targetTab) {
15
+ // Update sidebar nav active state
16
+ sidebarNavItems.forEach(nav => nav.classList.remove('active'));
17
+ item.classList.add('active');
18
+
19
+ // Update tab button active state
20
+ tabButtons.forEach(btn => {
21
+ btn.classList.toggle('active', btn.dataset.tab === targetTab);
22
+ });
23
+
24
+ // Show target tab content
25
+ tabContents.forEach(content => {
26
+ content.classList.toggle('active', content.id === `${targetTab}-content`);
27
+ });
28
+ }
29
+ });
30
+ });
31
+
32
+ // Keep sidebar nav in sync when tab buttons are clicked
33
+ tabButtons.forEach(btn => {
34
+ btn.addEventListener('click', () => {
35
+ const targetTab = btn.dataset.tab;
36
+ sidebarNavItems.forEach(nav => {
37
+ nav.classList.toggle('active', nav.dataset.tab === targetTab);
38
+ });
39
+ });
40
+ });
41
+
42
+ // Quick jump links - handle tab switching and smooth scroll
43
+ const quickJumpLinks = document.querySelectorAll('.sidebar .nav-item-small[href^="#"]');
44
+ quickJumpLinks.forEach(link => {
45
+ link.addEventListener('click', (e) => {
46
+ e.preventDefault();
47
+ const href = link.getAttribute('href');
48
+ const targetTab = link.dataset.tab;
49
+
50
+ // If link specifies a tab, switch to it first
51
+ if (targetTab) {
52
+ // Update sidebar nav active state
53
+ sidebarNavItems.forEach(nav => nav.classList.remove('active'));
54
+ const tabNavItem = document.querySelector(`.sidebar .nav-item[data-tab="${targetTab}"]`);
55
+ if (tabNavItem) tabNavItem.classList.add('active');
56
+
57
+ // Update tab button active state
58
+ tabButtons.forEach(btn => {
59
+ btn.classList.toggle('active', btn.dataset.tab === targetTab);
60
+ });
61
+
62
+ // Show target tab content
63
+ tabContents.forEach(content => {
64
+ content.classList.toggle('active', content.id === `${targetTab}-content`);
65
+ });
66
+ }
67
+
68
+ // Scroll to section after a short delay to allow tab switch
69
+ setTimeout(() => {
70
+ const targetSection = document.querySelector(href);
71
+ if (targetSection) {
72
+ targetSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
73
+ }
74
+ }, targetTab ? 50 : 0);
75
+ });
76
+ });
77
+ }
78
+
79
+ // Collapsible sections
80
+ function initCollapsibleSections() {
81
+ const sectionHeaders = document.querySelectorAll('.section-header[role="button"]');
82
+
83
+ // Load saved states from localStorage
84
+ const savedStates = JSON.parse(localStorage.getItem('git-wrapped-sections') || '{}');
85
+
86
+ sectionHeaders.forEach(header => {
87
+ const section = header.closest('.dashboard-section');
88
+ const sectionId = section?.id;
89
+ const content = header.nextElementSibling;
90
+ const icon = header.querySelector('.collapse-icon');
91
+
92
+ // Restore saved state
93
+ if (sectionId && savedStates[sectionId] === false) {
94
+ content.classList.add('collapsed');
95
+ header.setAttribute('aria-expanded', 'false');
96
+ if (icon) icon.textContent = '▶';
97
+ }
98
+
99
+ header.addEventListener('click', () => {
100
+ const isExpanded = header.getAttribute('aria-expanded') !== 'false';
101
+ header.setAttribute('aria-expanded', !isExpanded);
102
+ content.classList.toggle('collapsed');
103
+
104
+ if (icon) {
105
+ icon.textContent = isExpanded ? '▶' : '▼';
106
+ }
107
+
108
+ // Save state
109
+ if (sectionId) {
110
+ savedStates[sectionId] = !isExpanded;
111
+ localStorage.setItem('git-wrapped-sections', JSON.stringify(savedStates));
112
+ }
113
+ });
114
+ });
115
+ }
116
+
117
+ // Mobile hamburger menu
118
+ function initMobileNav() {
119
+ const hamburger = document.querySelector('.mobile-nav-toggle');
120
+ const sidebar = document.querySelector('.sidebar');
121
+
122
+ if (hamburger && sidebar) {
123
+ hamburger.addEventListener('click', () => {
124
+ sidebar.classList.toggle('open');
125
+ hamburger.classList.toggle('open');
126
+ });
127
+
128
+ // Close sidebar when clicking outside on mobile
129
+ document.addEventListener('click', (e) => {
130
+ if (window.innerWidth <= 1024) {
131
+ if (!sidebar.contains(e.target) && !hamburger.contains(e.target)) {
132
+ sidebar.classList.remove('open');
133
+ hamburger.classList.remove('open');
134
+ }
135
+ }
136
+ });
137
+
138
+ // Close sidebar when clicking a nav item on mobile
139
+ const navItems = sidebar.querySelectorAll('.nav-item');
140
+ navItems.forEach(item => {
141
+ item.addEventListener('click', () => {
142
+ if (window.innerWidth <= 1024) {
143
+ sidebar.classList.remove('open');
144
+ hamburger.classList.remove('open');
145
+ }
146
+ });
147
+ });
148
+ }
149
+ }
150
+
151
+ // Initialize all navigation features
152
+ document.addEventListener('DOMContentLoaded', () => {
153
+ initNavigation();
154
+ initCollapsibleSections();
155
+ initMobileNav();
156
+ });
@@ -0,0 +1,18 @@
1
+ // Tab switching functionality
2
+ const tabButtons = document.querySelectorAll('.tab-button');
3
+ const tabContents = document.querySelectorAll('.tab-content');
4
+
5
+ tabButtons.forEach(button => {
6
+ button.addEventListener('click', () => {
7
+ const tabName = button.getAttribute('data-tab');
8
+
9
+ // Remove active class from all buttons and contents
10
+ tabButtons.forEach(btn => btn.classList.remove('active'));
11
+ tabContents.forEach(content => content.classList.remove('active'));
12
+
13
+ // Add active class to clicked button and corresponding content
14
+ button.classList.add('active');
15
+ const targetContent = document.getElementById(tabName + '-content');
16
+ if (targetContent) targetContent.classList.add('active');
17
+ });
18
+ });
@@ -0,0 +1,21 @@
1
+ // Tooltip functionality for contribution graph
2
+ const days = document.querySelectorAll('.day');
3
+ const tooltip = document.getElementById('tooltip');
4
+
5
+ days.forEach(day => {
6
+ day.addEventListener('mouseenter', (e) => {
7
+ const count = day.getAttribute('data-count');
8
+ const date = day.getAttribute('data-date');
9
+ tooltip.textContent = `${date}: ${count} contribution${count !== '1' ? 's' : ''}`;
10
+ tooltip.style.display = 'block';
11
+ });
12
+
13
+ day.addEventListener('mousemove', (e) => {
14
+ tooltip.style.left = (e.clientX + 10) + 'px';
15
+ tooltip.style.top = (e.clientY + 10) + 'px';
16
+ });
17
+
18
+ day.addEventListener('mouseleave', () => {
19
+ tooltip.style.display = 'none';
20
+ });
21
+ });
@@ -0,0 +1,387 @@
1
+ /* === Achievements Section === */
2
+ .achievements-section {
3
+ margin-top: var(--spacing-xl);
4
+ padding-top: var(--spacing-lg);
5
+ border-top: 1px solid var(--border-default);
6
+ }
7
+
8
+ .achievements-section h2 {
9
+ font-size: var(--font-size-xl);
10
+ margin-bottom: var(--spacing-md);
11
+ color: var(--text-primary);
12
+ }
13
+
14
+ /* === Growth Narrative === */
15
+ .growth-narrative {
16
+ padding: var(--spacing-md);
17
+ background: var(--bg-primary);
18
+ border: 1px solid var(--border-default);
19
+ border-radius: var(--radius-md);
20
+ margin-bottom: var(--spacing-lg);
21
+ }
22
+
23
+ .growth-narrative h3 {
24
+ font-size: var(--font-size-md);
25
+ font-weight: var(--font-weight-semibold);
26
+ color: var(--text-primary);
27
+ margin-bottom: var(--spacing-sm);
28
+ }
29
+
30
+ .growth-narrative .narrative-text {
31
+ font-size: var(--font-size-sm);
32
+ color: var(--text-secondary);
33
+ line-height: 1.6;
34
+ }
35
+
36
+ .growth-narrative .narrative-text strong {
37
+ color: var(--text-primary);
38
+ font-weight: var(--font-weight-semibold);
39
+ }
40
+
41
+ .achievement-stats {
42
+ display: flex;
43
+ gap: var(--spacing-md);
44
+ margin-bottom: var(--spacing-lg);
45
+ flex-wrap: wrap;
46
+ }
47
+
48
+ .stat-card {
49
+ flex: 1;
50
+ min-width: 200px;
51
+ background: var(--bg-primary);
52
+ border: 1px solid var(--border-default);
53
+ border-radius: var(--radius-md);
54
+ padding: var(--spacing-md);
55
+ text-align: center;
56
+ }
57
+
58
+ .stat-value {
59
+ font-size: 32px;
60
+ font-weight: var(--font-weight-semibold);
61
+ color: var(--accent-gold);
62
+ margin-bottom: var(--spacing-xs);
63
+ }
64
+
65
+ .stat-label {
66
+ font-size: var(--font-size-sm);
67
+ color: var(--text-secondary);
68
+ text-transform: uppercase;
69
+ letter-spacing: 0.5px;
70
+ font-weight: var(--font-weight-medium);
71
+ }
72
+
73
+ /* === Progress Ring (SVG) === */
74
+ .progress-ring {
75
+ display: block;
76
+ }
77
+
78
+ .achievement-ring-wrapper {
79
+ position: absolute;
80
+ top: var(--spacing-sm);
81
+ right: var(--spacing-sm);
82
+ }
83
+
84
+ .achievement-ring-wrapper .progress-ring {
85
+ width: 40px;
86
+ height: 40px;
87
+ }
88
+
89
+ .achievement-progress-text {
90
+ font-size: var(--font-size-xs);
91
+ color: var(--text-muted);
92
+ margin-top: var(--spacing-xs);
93
+ }
94
+
95
+ .achievements-grid {
96
+ display: grid;
97
+ grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
98
+ gap: var(--spacing-md);
99
+ margin-top: var(--spacing-md);
100
+ }
101
+
102
+ .achievement-card {
103
+ background: var(--bg-primary);
104
+ border: 1px solid var(--border-default);
105
+ border-radius: var(--radius-md);
106
+ padding: var(--spacing-md);
107
+ text-align: center;
108
+ transition: border-color var(--transition-fast);
109
+ position: relative;
110
+ }
111
+
112
+ .achievement-card.unlocked {
113
+ border-color: var(--accent-gold);
114
+ }
115
+
116
+ /* Subtle unlock animation */
117
+ @keyframes achievement-unlock {
118
+ 0% {
119
+ opacity: 0;
120
+ transform: scale(0.95);
121
+ }
122
+ 100% {
123
+ opacity: 1;
124
+ transform: scale(1);
125
+ }
126
+ }
127
+
128
+ .achievement-card.recently-unlocked {
129
+ animation: achievement-unlock 0.3s ease-out;
130
+ }
131
+
132
+ .achievement-card.unlocked:hover {
133
+ border-color: var(--accent-gold-muted);
134
+ }
135
+
136
+ .achievement-card.locked {
137
+ opacity: 0.6;
138
+ filter: grayscale(80%);
139
+ }
140
+
141
+ .badge-emoji {
142
+ font-size: 48px;
143
+ margin-bottom: var(--spacing-sm);
144
+ }
145
+
146
+ .badge-name {
147
+ font-size: var(--font-size-md);
148
+ font-weight: var(--font-weight-semibold);
149
+ color: var(--text-primary);
150
+ margin-bottom: var(--spacing-xs);
151
+ }
152
+
153
+ .badge-description {
154
+ font-size: var(--font-size-xs);
155
+ color: var(--text-secondary);
156
+ line-height: 1.4;
157
+ margin-bottom: var(--spacing-sm);
158
+ min-height: 32px;
159
+ }
160
+
161
+ .badge-tier {
162
+ display: inline-block;
163
+ padding: 3px var(--spacing-sm);
164
+ border-radius: var(--radius-md);
165
+ font-size: 10px;
166
+ font-weight: var(--font-weight-semibold);
167
+ text-transform: uppercase;
168
+ margin-top: var(--spacing-xs);
169
+ }
170
+
171
+ /* Muted Tier Colors */
172
+ .badge-tier.bronze { background: var(--tier-bronze); color: white; }
173
+ .badge-tier.silver { background: var(--tier-silver); color: var(--bg-primary); }
174
+ .badge-tier.gold { background: var(--tier-gold); color: var(--bg-primary); }
175
+ .badge-tier.platinum { background: var(--tier-platinum); color: var(--bg-primary); }
176
+ .badge-tier.legendary { background: var(--tier-legendary); color: white; }
177
+
178
+ .achievement-progress {
179
+ margin-top: var(--spacing-sm);
180
+ height: 4px;
181
+ background: var(--border-subtle);
182
+ border-radius: var(--radius-sm);
183
+ overflow: hidden;
184
+ }
185
+
186
+ .achievement-progress-fill {
187
+ height: 100%;
188
+ background: var(--accent-blue);
189
+ transition: width 0.5s;
190
+ }
191
+
192
+ /* === Next Milestones === */
193
+ .next-milestones {
194
+ margin-top: var(--spacing-lg);
195
+ padding: var(--spacing-md);
196
+ background: var(--bg-primary);
197
+ border: 1px solid var(--border-default);
198
+ border-radius: var(--radius-md);
199
+ }
200
+
201
+ .next-milestones h3 {
202
+ font-size: var(--font-size-lg);
203
+ color: var(--accent-gold);
204
+ margin-bottom: var(--spacing-md);
205
+ }
206
+
207
+ .milestone-item {
208
+ padding: var(--spacing-md);
209
+ background: var(--bg-secondary);
210
+ border-radius: var(--radius-md);
211
+ margin-bottom: var(--spacing-sm);
212
+ border-left: 3px solid var(--accent-gold);
213
+ }
214
+
215
+ .milestone-header {
216
+ display: flex;
217
+ align-items: center;
218
+ gap: var(--spacing-sm);
219
+ margin-bottom: var(--spacing-sm);
220
+ }
221
+
222
+ .milestone-emoji {
223
+ font-size: 24px;
224
+ }
225
+
226
+ .milestone-name {
227
+ font-size: var(--font-size-md);
228
+ font-weight: var(--font-weight-semibold);
229
+ color: var(--text-primary);
230
+ }
231
+
232
+ .milestone-description {
233
+ font-size: var(--font-size-sm);
234
+ color: var(--text-secondary);
235
+ margin-bottom: var(--spacing-sm);
236
+ line-height: 1.4;
237
+ }
238
+
239
+ .milestone-progress-bar {
240
+ height: 20px;
241
+ background: var(--border-subtle);
242
+ border-radius: var(--radius-lg);
243
+ overflow: hidden;
244
+ position: relative;
245
+ }
246
+
247
+ .milestone-progress-fill {
248
+ height: 100%;
249
+ background: var(--accent-green);
250
+ transition: width 0.5s;
251
+ }
252
+
253
+ .milestone-progress-text {
254
+ position: absolute;
255
+ top: 50%;
256
+ left: 50%;
257
+ transform: translate(-50%, -50%);
258
+ font-size: var(--font-size-xs);
259
+ font-weight: var(--font-weight-semibold);
260
+ color: white;
261
+ text-shadow: 0 1px 2px rgba(0,0,0,0.5);
262
+ }
263
+
264
+ /* === Achievement Categories === */
265
+ .achievement-categories {
266
+ margin-top: var(--spacing-lg);
267
+ }
268
+
269
+ .category-section {
270
+ margin-bottom: var(--spacing-lg);
271
+ padding: var(--spacing-md);
272
+ background: var(--bg-primary);
273
+ border: 1px solid var(--border-default);
274
+ border-radius: var(--radius-md);
275
+ }
276
+
277
+ .category-section h3 {
278
+ font-size: var(--font-size-lg);
279
+ color: var(--text-primary);
280
+ margin-bottom: var(--spacing-md);
281
+ }
282
+
283
+ .category-progress {
284
+ display: flex;
285
+ align-items: center;
286
+ gap: var(--spacing-sm);
287
+ margin-bottom: var(--spacing-md);
288
+ }
289
+
290
+ .category-bar {
291
+ flex: 1;
292
+ height: 8px;
293
+ background: var(--border-subtle);
294
+ border-radius: var(--radius-md);
295
+ overflow: hidden;
296
+ }
297
+
298
+ .category-fill {
299
+ height: 100%;
300
+ background: var(--accent-gold);
301
+ transition: width 0.5s;
302
+ }
303
+
304
+ .category-count {
305
+ font-size: var(--font-size-sm);
306
+ color: var(--text-secondary);
307
+ font-weight: var(--font-weight-semibold);
308
+ }
309
+
310
+ .achievement-grid {
311
+ display: grid;
312
+ grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
313
+ gap: var(--spacing-sm);
314
+ }
315
+
316
+ .achievement-item {
317
+ background: var(--bg-secondary);
318
+ border: 1px solid var(--border-default);
319
+ border-radius: var(--radius-md);
320
+ padding: var(--spacing-sm);
321
+ text-align: center;
322
+ transition: border-color var(--transition-fast);
323
+ position: relative;
324
+ }
325
+
326
+ .achievement-item.unlocked {
327
+ border-color: var(--accent-gold);
328
+ }
329
+
330
+ .achievement-item.unlocked:hover {
331
+ border-color: var(--accent-gold-muted);
332
+ }
333
+
334
+ .achievement-item.locked {
335
+ opacity: 0.6;
336
+ filter: grayscale(80%);
337
+ }
338
+
339
+ .achievement-emoji {
340
+ font-size: 36px;
341
+ margin-bottom: var(--spacing-sm);
342
+ }
343
+
344
+ .achievement-name {
345
+ font-size: var(--font-size-sm);
346
+ font-weight: var(--font-weight-semibold);
347
+ color: var(--text-primary);
348
+ margin-bottom: var(--spacing-xs);
349
+ }
350
+
351
+ .achievement-description {
352
+ font-size: 10px;
353
+ color: var(--text-secondary);
354
+ line-height: 1.3;
355
+ min-height: 26px;
356
+ }
357
+
358
+ /* === New Badges === */
359
+ .new-badges {
360
+ margin-top: var(--spacing-md);
361
+ padding: var(--spacing-md);
362
+ background: var(--bg-secondary);
363
+ border: 1px solid var(--border-default);
364
+ border-radius: var(--radius-md);
365
+ border-left: 3px solid var(--accent-gold);
366
+ }
367
+
368
+ .new-badges h3 {
369
+ font-size: var(--font-size-md);
370
+ color: var(--accent-gold);
371
+ margin-bottom: var(--spacing-sm);
372
+ }
373
+
374
+ .badge-list {
375
+ display: flex;
376
+ gap: var(--spacing-sm);
377
+ flex-wrap: wrap;
378
+ }
379
+
380
+ .badge-item-mini {
381
+ background: var(--bg-primary);
382
+ border: 1px solid var(--accent-gold);
383
+ border-radius: 20px;
384
+ padding: var(--spacing-xs) var(--spacing-sm);
385
+ font-size: var(--font-size-sm);
386
+ color: var(--text-primary);
387
+ }