repo-wrapped 0.0.7 → 0.0.9
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/.github/agents/complete.agent.md +257 -0
- package/.github/agents/feature-scaffold.agent.md +248 -0
- package/.github/agents/jsdoc.agent.md +243 -0
- package/.github/agents/plan.agent.md +202 -0
- package/.github/agents/spec-writer.agent.md +169 -0
- package/.github/agents/test-writer.agent.md +169 -0
- package/.stylelintrc.json +27 -0
- package/README.md +94 -94
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +446 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +446 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +7039 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/commands/generate.js +56 -56
- package/dist/config/defaults.js +158 -0
- package/dist/config/index.js +10 -0
- package/dist/features/achievements/data/achievements.json +284 -0
- package/dist/features/achievements/engine.js +140 -0
- package/dist/features/achievements/evaluators.js +246 -0
- package/dist/features/achievements/helpers.js +58 -0
- package/dist/features/achievements/index.js +57 -0
- package/dist/features/achievements/loader.js +88 -0
- package/dist/features/achievements/template.js +155 -0
- package/dist/features/achievements/types.js +7 -0
- package/dist/features/commit-quality/analyzer.js +378 -0
- package/dist/features/commit-quality/analyzer.test.js +484 -0
- package/dist/features/commit-quality/index.js +28 -0
- package/dist/features/commit-quality/template.js +114 -0
- package/dist/features/commit-quality/types.js +2 -0
- package/dist/features/comparison/analyzer.js +222 -0
- package/dist/features/comparison/index.js +28 -0
- package/dist/features/comparison/template.js +119 -0
- package/dist/features/comparison/types.js +2 -0
- package/dist/features/contribution-graph/index.js +9 -0
- package/dist/features/contribution-graph/template.js +89 -0
- package/dist/features/events/index.js +31 -0
- package/dist/features/events/parser.js +253 -0
- package/dist/features/events/template.js +113 -0
- package/dist/features/events/types.js +2 -0
- package/dist/features/executive-summary/generator.js +275 -0
- package/dist/features/executive-summary/index.js +27 -0
- package/dist/features/executive-summary/template.js +80 -0
- package/dist/features/executive-summary/types.js +2 -0
- package/dist/features/gaps/analyzer.js +298 -0
- package/dist/features/gaps/analyzer.test.js +517 -0
- package/dist/features/gaps/index.js +27 -0
- package/dist/features/gaps/template.js +190 -0
- package/dist/features/gaps/types.js +2 -0
- package/dist/features/impact/analyzer.js +248 -0
- package/dist/features/impact/index.js +26 -0
- package/dist/features/impact/template.js +118 -0
- package/dist/features/impact/types.js +2 -0
- package/dist/features/index.js +40 -0
- package/dist/features/knowledge/analyzer.js +385 -0
- package/dist/features/knowledge/index.js +26 -0
- package/dist/features/knowledge/template.js +239 -0
- package/dist/features/knowledge/types.js +2 -0
- package/dist/features/streaks/calculator.js +184 -0
- package/dist/features/streaks/calculator.test.js +366 -0
- package/dist/features/streaks/index.js +36 -0
- package/dist/features/streaks/template.js +41 -0
- package/dist/features/streaks/types.js +9 -0
- package/dist/features/team/analyzer.js +316 -0
- package/dist/features/team/index.js +30 -0
- package/dist/features/team/template.js +146 -0
- package/dist/features/team/types.js +2 -0
- package/dist/features/time-patterns/analyzer.js +319 -0
- package/dist/features/time-patterns/analyzer.test.js +278 -0
- package/dist/features/time-patterns/index.js +37 -0
- package/dist/features/time-patterns/template.js +109 -0
- package/dist/features/time-patterns/types.js +9 -0
- package/dist/features/velocity/analyzer.js +257 -0
- package/dist/features/velocity/analyzer.test.js +383 -0
- package/dist/features/velocity/index.js +27 -0
- package/dist/features/velocity/template.js +189 -0
- package/dist/features/velocity/types.js +2 -0
- package/dist/generators/html/scripts/knowledge.js +17 -0
- package/dist/generators/html/styles/base.css +8 -3
- package/dist/generators/html/styles/components.css +121 -1
- package/dist/generators/html/styles/knowledge.css +21 -0
- package/dist/generators/html/styles/leaddev.css +108 -48
- package/dist/generators/html/styles/strategic-insights.css +1337 -0
- package/dist/generators/html/templates/commitQualitySection.js +28 -2
- package/dist/generators/html/templates/executiveSummarySection.js +0 -4
- package/dist/generators/html/templates/impactSection.js +8 -6
- package/dist/generators/html/templates/knowledgeSection.js +16 -2
- package/dist/generators/html/templates/velocitySection.js +2 -2
- package/dist/generators/html/types.js +7 -0
- package/dist/generators/html/utils/analysisRunner.js +93 -0
- package/dist/generators/html/utils/cardBuilder.js +47 -0
- package/dist/generators/html/utils/contextBuilder.js +54 -0
- package/dist/generators/html/utils/htmlDocumentBuilder.js +396 -0
- package/dist/generators/html/utils/kpiBuilder.js +76 -0
- package/dist/generators/html/utils/sectionWrapper.js +71 -0
- package/dist/generators/html/utils/styleLoader.js +2 -2
- package/dist/html/analysisRunner.js +93 -0
- package/dist/html/htmlDocumentBuilder.js +396 -0
- package/dist/html/index.js +29 -0
- package/dist/html/shared/colorUtils.js +61 -0
- package/dist/html/shared/commitMapBuilder.js +23 -0
- package/dist/html/shared/components/cardBuilder.js +47 -0
- package/dist/html/shared/components/index.js +18 -0
- package/dist/html/shared/components/kpiBuilder.js +76 -0
- package/dist/html/shared/components/sectionWrapper.js +71 -0
- package/dist/html/shared/contextBuilder.js +54 -0
- package/dist/html/shared/dateRangeCalculator.js +56 -0
- package/dist/html/shared/developerStatsCalculator.js +28 -0
- package/dist/html/shared/index.js +39 -0
- package/dist/html/shared/scriptLoader.js +15 -0
- package/dist/html/shared/scripts/export.js +125 -0
- package/dist/html/shared/scripts/knowledge.js +137 -0
- package/dist/html/shared/scripts/modal.js +68 -0
- package/dist/html/shared/scripts/navigation.js +156 -0
- package/dist/html/shared/scripts/tabs.js +18 -0
- package/dist/html/shared/scripts/tooltip.js +21 -0
- package/dist/html/shared/styleLoader.js +18 -0
- package/dist/html/shared/styles/achievements.css +387 -0
- package/dist/html/shared/styles/base.css +822 -0
- package/dist/html/shared/styles/components.css +1511 -0
- package/dist/html/shared/styles/knowledge.css +242 -0
- package/dist/html/shared/styles/strategic-insights.css +1337 -0
- package/dist/html/shared/weekGrouper.js +27 -0
- package/dist/html/types.js +7 -0
- package/dist/index.js +39 -39
- package/dist/test/helpers/commitFactory.js +166 -0
- package/dist/test/helpers/dateUtils.js +101 -0
- package/dist/test/helpers/index.js +29 -0
- package/dist/test/setup.js +17 -0
- package/dist/test/smoke.test.js +94 -0
- package/dist/types/achievements.js +7 -0
- package/dist/types/analysis.js +7 -0
- package/dist/types/core.js +7 -0
- package/dist/types/index.js +38 -0
- package/dist/types/options.js +7 -0
- package/dist/types/shared.js +7 -0
- package/dist/types/strategic.js +7 -0
- package/dist/types/summary.js +7 -0
- package/dist/utils/achievementDefinitions.js +22 -22
- package/dist/utils/analyzerContextBuilder.js +124 -0
- package/dist/utils/commitQualityAnalyzer.js +13 -2
- package/dist/utils/emptyResults.js +95 -0
- package/dist/utils/fileHotspotAnalyzer.js +4 -12
- package/dist/utils/gapAnalyzer.js +26 -28
- package/dist/utils/gitParser.test.js +363 -0
- package/dist/utils/htmlGenerator.js +62 -466
- package/dist/utils/impactAnalyzer.js +20 -19
- package/dist/utils/knowledgeDistributionAnalyzer.js +32 -27
- package/dist/utils/matrixGenerator.js +13 -13
- package/dist/utils/rangeComparisonAnalyzer.js +2 -2
- package/dist/utils/streakCalculator.js +77 -27
- package/dist/utils/teamAnalyzer.js +20 -1
- package/dist/utils/timePatternAnalyzer.js +18 -3
- package/dist/utils/velocityAnalyzer.js +23 -18
- package/dist/utils/wrappedGenerator.js +8 -8
- package/package.json +74 -64
- package/vitest.config.ts +46 -0
- package/SPECS.md +0 -490
- package/dist/cli.js +0 -24
- package/dist/commands/index.js +0 -24
- package/test-team.txt +0 -2
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Section wrapper utilities for dashboard sections.
|
|
4
|
+
*
|
|
5
|
+
* @module generators/html/utils/sectionWrapper
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.wrapInSection = wrapInSection;
|
|
9
|
+
exports.wrapInStaticSection = wrapInStaticSection;
|
|
10
|
+
exports.createTabButton = createTabButton;
|
|
11
|
+
exports.createTabContent = createTabContent;
|
|
12
|
+
/**
|
|
13
|
+
* Wrap content in a collapsible dashboard section.
|
|
14
|
+
*
|
|
15
|
+
* @param id - Unique section ID for targeting
|
|
16
|
+
* @param title - Section title displayed in header
|
|
17
|
+
* @param content - HTML content to wrap
|
|
18
|
+
* @param icon - Optional icon emoji to display before title
|
|
19
|
+
* @returns HTML string for the wrapped section
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const html = wrapInSection('velocity', 'Velocity Timeline', velocityContent, '📈');
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
function wrapInSection(id, title, content, icon = '') {
|
|
27
|
+
return `
|
|
28
|
+
<section class="dashboard-section" id="${id}">
|
|
29
|
+
<header class="section-header" role="button" aria-expanded="true">
|
|
30
|
+
<h2>${icon ? icon + ' ' : ''}${title}</h2>
|
|
31
|
+
<span class="collapse-icon">▼</span>
|
|
32
|
+
</header>
|
|
33
|
+
<div class="section-content">
|
|
34
|
+
${content}
|
|
35
|
+
</div>
|
|
36
|
+
</section>
|
|
37
|
+
`;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Wrap content in a non-collapsible section (for embedded content).
|
|
41
|
+
*/
|
|
42
|
+
function wrapInStaticSection(id, title, content, icon = '') {
|
|
43
|
+
return `
|
|
44
|
+
<section class="dashboard-section" id="${id}">
|
|
45
|
+
<header class="section-header">
|
|
46
|
+
<h2>${icon ? icon + ' ' : ''}${title}</h2>
|
|
47
|
+
</header>
|
|
48
|
+
<div class="section-content">
|
|
49
|
+
${content}
|
|
50
|
+
</div>
|
|
51
|
+
</section>
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Create a tab button for the tab navigation.
|
|
56
|
+
*/
|
|
57
|
+
function createTabButton(id, label, icon = '', isActive = false) {
|
|
58
|
+
const activeClass = isActive ? ' active' : '';
|
|
59
|
+
return `<button class="tab-button${activeClass}" data-tab="${id}">${icon ? icon + ' ' : ''}${label}</button>`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Create a tab content container.
|
|
63
|
+
*/
|
|
64
|
+
function createTabContent(id, content, isActive = false) {
|
|
65
|
+
const activeClass = isActive ? ' active' : '';
|
|
66
|
+
return `
|
|
67
|
+
<div class="tab-content${activeClass}" id="${id}-content">
|
|
68
|
+
${content}
|
|
69
|
+
</div>
|
|
70
|
+
`;
|
|
71
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Context builder for HTML generation.
|
|
4
|
+
*
|
|
5
|
+
* @module html/shared/contextBuilder
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.buildHtmlContext = buildHtmlContext;
|
|
9
|
+
const dateRangeCalculator_1 = require("./dateRangeCalculator");
|
|
10
|
+
const developerStatsCalculator_1 = require("./developerStatsCalculator");
|
|
11
|
+
/**
|
|
12
|
+
* Build the HTML generation context from options.
|
|
13
|
+
*
|
|
14
|
+
* @param options - Generation options
|
|
15
|
+
* @returns Fully populated context for HTML generation
|
|
16
|
+
*/
|
|
17
|
+
function buildHtmlContext(options) {
|
|
18
|
+
const { commits, repoPath, repoName, repoUrl, year = new Date().getFullYear(), monthsToShow = 12, allTime = false, } = options;
|
|
19
|
+
// Get current git user
|
|
20
|
+
const { getCurrentGitUser } = require('../../utils/gitParser');
|
|
21
|
+
const currentUser = getCurrentGitUser(repoPath);
|
|
22
|
+
// Calculate date ranges
|
|
23
|
+
const dateRanges = allTime
|
|
24
|
+
? (0, dateRangeCalculator_1.calculateDateRangesFromCommits)(commits)
|
|
25
|
+
: (0, dateRangeCalculator_1.calculateDateRanges)(year, monthsToShow);
|
|
26
|
+
const { startDate, endDate, weekStartDate, weekEndDate } = dateRanges;
|
|
27
|
+
// Filter commits to date range
|
|
28
|
+
const filteredCommits = commits.filter(commit => {
|
|
29
|
+
const commitDate = new Date(commit.date);
|
|
30
|
+
return commitDate >= startDate && commitDate <= endDate;
|
|
31
|
+
});
|
|
32
|
+
// Filter personal commits
|
|
33
|
+
const personalCommits = filteredCommits.filter(c => c.author === currentUser);
|
|
34
|
+
// Calculate developer stats
|
|
35
|
+
const developerStats = (0, developerStatsCalculator_1.calculateDeveloperStats)(filteredCommits, startDate, endDate);
|
|
36
|
+
return {
|
|
37
|
+
options,
|
|
38
|
+
repoInfo: {
|
|
39
|
+
name: repoName,
|
|
40
|
+
url: repoUrl,
|
|
41
|
+
path: repoPath,
|
|
42
|
+
},
|
|
43
|
+
currentUser,
|
|
44
|
+
dateRange: {
|
|
45
|
+
start: startDate,
|
|
46
|
+
end: endDate,
|
|
47
|
+
weekStart: weekStartDate,
|
|
48
|
+
weekEnd: weekEndDate,
|
|
49
|
+
},
|
|
50
|
+
filteredCommits,
|
|
51
|
+
personalCommits,
|
|
52
|
+
developerStats,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.calculateDateRanges = calculateDateRanges;
|
|
4
|
+
exports.calculateDateRangesFromCommits = calculateDateRangesFromCommits;
|
|
5
|
+
const date_fns_1 = require("date-fns");
|
|
6
|
+
/**
|
|
7
|
+
* Calculates date ranges for the visualization
|
|
8
|
+
*/
|
|
9
|
+
function calculateDateRanges(year, monthsToShow) {
|
|
10
|
+
const now = new Date();
|
|
11
|
+
const isCurrentYear = year === now.getFullYear();
|
|
12
|
+
// Calculate the end date (today if current year, or end of December for past years)
|
|
13
|
+
const endDate = isCurrentYear ? now : new Date(year, 11, 31);
|
|
14
|
+
// Calculate start date (N months back from end date)
|
|
15
|
+
const startDate = (0, date_fns_1.startOfMonth)((0, date_fns_1.subMonths)(endDate, monthsToShow - 1));
|
|
16
|
+
// Get the full week range (start on Sunday, end on Saturday)
|
|
17
|
+
const weekStartDate = (0, date_fns_1.startOfWeek)(startDate, { weekStartsOn: 0 });
|
|
18
|
+
const weekEndDate = (0, date_fns_1.endOfWeek)(endDate, { weekStartsOn: 0 });
|
|
19
|
+
return {
|
|
20
|
+
startDate,
|
|
21
|
+
endDate,
|
|
22
|
+
weekStartDate,
|
|
23
|
+
weekEndDate
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Calculates date ranges from the entire commit history (first to last commit)
|
|
28
|
+
*/
|
|
29
|
+
function calculateDateRangesFromCommits(commits) {
|
|
30
|
+
if (commits.length === 0) {
|
|
31
|
+
const now = new Date();
|
|
32
|
+
return {
|
|
33
|
+
startDate: now,
|
|
34
|
+
endDate: now,
|
|
35
|
+
weekStartDate: (0, date_fns_1.startOfWeek)(now, { weekStartsOn: 0 }),
|
|
36
|
+
weekEndDate: (0, date_fns_1.endOfWeek)(now, { weekStartsOn: 0 })
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// Sort commits by date to find first and last
|
|
40
|
+
const sortedCommits = [...commits].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
|
|
41
|
+
const firstCommitDate = new Date(sortedCommits[0].date);
|
|
42
|
+
const lastCommitDate = new Date(sortedCommits[sortedCommits.length - 1].date);
|
|
43
|
+
// Use start of month for first commit and today/last commit date for end
|
|
44
|
+
const now = new Date();
|
|
45
|
+
const startDate = (0, date_fns_1.startOfMonth)(firstCommitDate);
|
|
46
|
+
const endDate = lastCommitDate > now ? lastCommitDate : now;
|
|
47
|
+
// Get the full week range (start on Sunday, end on Saturday)
|
|
48
|
+
const weekStartDate = (0, date_fns_1.startOfWeek)(startDate, { weekStartsOn: 0 });
|
|
49
|
+
const weekEndDate = (0, date_fns_1.endOfWeek)(endDate, { weekStartsOn: 0 });
|
|
50
|
+
return {
|
|
51
|
+
startDate,
|
|
52
|
+
endDate,
|
|
53
|
+
weekStartDate,
|
|
54
|
+
weekEndDate
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.calculateDeveloperStats = calculateDeveloperStats;
|
|
4
|
+
/**
|
|
5
|
+
* Calculates statistics for each developer from commits within a date range
|
|
6
|
+
*/
|
|
7
|
+
function calculateDeveloperStats(commits, startDate, endDate) {
|
|
8
|
+
const developerStats = new Map();
|
|
9
|
+
commits.forEach(commit => {
|
|
10
|
+
const commitDate = new Date(commit.date);
|
|
11
|
+
if (commitDate >= startDate && commitDate <= endDate) {
|
|
12
|
+
if (!developerStats.has(commit.author)) {
|
|
13
|
+
developerStats.set(commit.author, {
|
|
14
|
+
commits: 0,
|
|
15
|
+
firstCommit: commitDate,
|
|
16
|
+
lastCommit: commitDate
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
const stats = developerStats.get(commit.author);
|
|
20
|
+
stats.commits++;
|
|
21
|
+
if (commitDate < stats.firstCommit)
|
|
22
|
+
stats.firstCommit = commitDate;
|
|
23
|
+
if (commitDate > stats.lastCommit)
|
|
24
|
+
stats.lastCommit = commitDate;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return developerStats;
|
|
28
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.buildHtmlContext = exports.calculateDeveloperStats = exports.calculateDateRangesFromCommits = exports.calculateDateRanges = exports.getDayLabels = exports.groupDaysIntoWeeks = exports.createCommitMaps = exports.loadScripts = exports.loadStyles = void 0;
|
|
18
|
+
/**
|
|
19
|
+
* HTML Shared Utilities
|
|
20
|
+
* @module html/shared
|
|
21
|
+
*/
|
|
22
|
+
__exportStar(require("./components"), exports);
|
|
23
|
+
var styleLoader_1 = require("./styleLoader");
|
|
24
|
+
Object.defineProperty(exports, "loadStyles", { enumerable: true, get: function () { return styleLoader_1.loadStyles; } });
|
|
25
|
+
var scriptLoader_1 = require("./scriptLoader");
|
|
26
|
+
Object.defineProperty(exports, "loadScripts", { enumerable: true, get: function () { return scriptLoader_1.loadScripts; } });
|
|
27
|
+
var commitMapBuilder_1 = require("./commitMapBuilder");
|
|
28
|
+
Object.defineProperty(exports, "createCommitMaps", { enumerable: true, get: function () { return commitMapBuilder_1.createCommitMaps; } });
|
|
29
|
+
var weekGrouper_1 = require("./weekGrouper");
|
|
30
|
+
Object.defineProperty(exports, "groupDaysIntoWeeks", { enumerable: true, get: function () { return weekGrouper_1.groupDaysIntoWeeks; } });
|
|
31
|
+
Object.defineProperty(exports, "getDayLabels", { enumerable: true, get: function () { return weekGrouper_1.getDayLabels; } });
|
|
32
|
+
var dateRangeCalculator_1 = require("./dateRangeCalculator");
|
|
33
|
+
Object.defineProperty(exports, "calculateDateRanges", { enumerable: true, get: function () { return dateRangeCalculator_1.calculateDateRanges; } });
|
|
34
|
+
Object.defineProperty(exports, "calculateDateRangesFromCommits", { enumerable: true, get: function () { return dateRangeCalculator_1.calculateDateRangesFromCommits; } });
|
|
35
|
+
var developerStatsCalculator_1 = require("./developerStatsCalculator");
|
|
36
|
+
Object.defineProperty(exports, "calculateDeveloperStats", { enumerable: true, get: function () { return developerStatsCalculator_1.calculateDeveloperStats; } });
|
|
37
|
+
var contextBuilder_1 = require("./contextBuilder");
|
|
38
|
+
Object.defineProperty(exports, "buildHtmlContext", { enumerable: true, get: function () { return contextBuilder_1.buildHtmlContext; } });
|
|
39
|
+
__exportStar(require("./colorUtils"), exports);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadScripts = loadScripts;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
function loadScripts() {
|
|
7
|
+
const scriptsDir = (0, path_1.join)(__dirname, 'scripts');
|
|
8
|
+
const tabs = (0, fs_1.readFileSync)((0, path_1.join)(scriptsDir, 'tabs.js'), 'utf-8');
|
|
9
|
+
const tooltip = (0, fs_1.readFileSync)((0, path_1.join)(scriptsDir, 'tooltip.js'), 'utf-8');
|
|
10
|
+
const modal = (0, fs_1.readFileSync)((0, path_1.join)(scriptsDir, 'modal.js'), 'utf-8');
|
|
11
|
+
const navigation = (0, fs_1.readFileSync)((0, path_1.join)(scriptsDir, 'navigation.js'), 'utf-8');
|
|
12
|
+
const exportScript = (0, fs_1.readFileSync)((0, path_1.join)(scriptsDir, 'export.js'), 'utf-8');
|
|
13
|
+
const knowledge = (0, fs_1.readFileSync)((0, path_1.join)(scriptsDir, 'knowledge.js'), 'utf-8');
|
|
14
|
+
return `${tabs}\n\n${tooltip}\n\n${modal}\n\n${navigation}\n\n${exportScript}\n\n${knowledge}`;
|
|
15
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// Export functionality for Git Wrapped reports
|
|
2
|
+
|
|
3
|
+
function initExport() {
|
|
4
|
+
document.querySelectorAll('[data-export]').forEach(btn => {
|
|
5
|
+
btn.addEventListener('click', (e) => {
|
|
6
|
+
const format = e.currentTarget.dataset.export;
|
|
7
|
+
exportData(format);
|
|
8
|
+
});
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function exportData(format) {
|
|
13
|
+
const data = window.__GITWRAPPED_DATA__;
|
|
14
|
+
|
|
15
|
+
if (!data) {
|
|
16
|
+
console.warn('No export data available');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const repoName = (data.repository || 'report').replace(/[^a-z0-9]/gi, '-').toLowerCase();
|
|
21
|
+
const timestamp = new Date().toISOString().slice(0, 10);
|
|
22
|
+
|
|
23
|
+
if (format === 'json') {
|
|
24
|
+
downloadFile(
|
|
25
|
+
JSON.stringify(data, null, 2),
|
|
26
|
+
`git-wrapped-${repoName}-${timestamp}.json`,
|
|
27
|
+
'application/json'
|
|
28
|
+
);
|
|
29
|
+
} else if (format === 'csv') {
|
|
30
|
+
const csv = convertToCSV(data);
|
|
31
|
+
downloadFile(csv, `git-wrapped-${repoName}-${timestamp}.csv`, 'text/csv');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function convertToCSV(data) {
|
|
36
|
+
const sections = [];
|
|
37
|
+
|
|
38
|
+
// Section 1: Summary Metrics
|
|
39
|
+
sections.push('# Summary Metrics');
|
|
40
|
+
sections.push('Metric,Value');
|
|
41
|
+
sections.push(`Repository,${escapeCSV(data.repository || '')}`);
|
|
42
|
+
sections.push(`Period Start,${data.period?.start || ''}`);
|
|
43
|
+
sections.push(`Period End,${data.period?.end || ''}`);
|
|
44
|
+
sections.push(`Author Filter,${escapeCSV(data.author || 'All')}`);
|
|
45
|
+
sections.push(`Total Commits,${data.summary?.totalCommits || 0}`);
|
|
46
|
+
sections.push(`Total Contributors,${data.summary?.totalContributors || 0}`);
|
|
47
|
+
sections.push(`Active Days,${data.summary?.activeDays || 0}`);
|
|
48
|
+
|
|
49
|
+
// Section 2: Streak Data
|
|
50
|
+
if (data.streaks) {
|
|
51
|
+
sections.push('');
|
|
52
|
+
sections.push('# Streak Analysis');
|
|
53
|
+
sections.push('Metric,Value');
|
|
54
|
+
sections.push(`Current Streak (days),${data.streaks.currentStreak || 0}`);
|
|
55
|
+
sections.push(`Longest Streak (days),${data.streaks.longestStreak || 0}`);
|
|
56
|
+
sections.push(`Total Active Days,${data.streaks.totalActiveDays || 0}`);
|
|
57
|
+
sections.push(`Active Day Percentage,${(data.streaks.activeDayPercentage || 0).toFixed(1)}%`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Section 3: Quality Metrics
|
|
61
|
+
if (data.quality) {
|
|
62
|
+
sections.push('');
|
|
63
|
+
sections.push('# Commit Quality');
|
|
64
|
+
sections.push('Metric,Value');
|
|
65
|
+
sections.push(`Overall Score,${(data.quality.overallScore || 0).toFixed(1)}`);
|
|
66
|
+
sections.push(`Conventional Commits %,${(data.quality.conventionalAdherence || 0).toFixed(1)}%`);
|
|
67
|
+
sections.push(`Avg Subject Length,${(data.quality.avgSubjectLength || 0).toFixed(0)} chars`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Section 4: Time Patterns
|
|
71
|
+
if (data.timePattern) {
|
|
72
|
+
sections.push('');
|
|
73
|
+
sections.push('# Time Patterns');
|
|
74
|
+
sections.push('Metric,Value');
|
|
75
|
+
sections.push(`Chronotype,${data.timePattern.chronotype || 'Unknown'}`);
|
|
76
|
+
sections.push(`Peak Hour,${data.timePattern.peakHour || 0}:00`);
|
|
77
|
+
sections.push(`Consistency Score,${(data.timePattern.consistency || 0).toFixed(1)}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Section 5: Developer Stats
|
|
81
|
+
if (data.developerStats && data.developerStats.length > 0) {
|
|
82
|
+
sections.push('');
|
|
83
|
+
sections.push('# Developer Statistics');
|
|
84
|
+
sections.push('Developer,Commits,First Commit,Last Commit');
|
|
85
|
+
data.developerStats.forEach(dev => {
|
|
86
|
+
sections.push(`${escapeCSV(dev.name)},${dev.commits},${dev.firstCommit || ''},${dev.lastCommit || ''}`);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Section 6: Recent Commits (last 100)
|
|
91
|
+
if (data.commits && data.commits.length > 0) {
|
|
92
|
+
sections.push('');
|
|
93
|
+
sections.push('# Commits (last 100)');
|
|
94
|
+
sections.push('Date,Author,Message,Hash');
|
|
95
|
+
data.commits.slice(0, 100).forEach(c => {
|
|
96
|
+
sections.push(`${c.date},${escapeCSV(c.author)},${escapeCSV(c.message)},${c.hash || ''}`);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return sections.join('\n');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function escapeCSV(str) {
|
|
104
|
+
if (str == null) return '';
|
|
105
|
+
const s = String(str);
|
|
106
|
+
if (s.includes(',') || s.includes('"') || s.includes('\n') || s.includes('\r')) {
|
|
107
|
+
return '"' + s.replace(/"/g, '""') + '"';
|
|
108
|
+
}
|
|
109
|
+
return s;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function downloadFile(content, filename, mimeType) {
|
|
113
|
+
const blob = new Blob([content], { type: mimeType });
|
|
114
|
+
const url = URL.createObjectURL(blob);
|
|
115
|
+
const a = document.createElement('a');
|
|
116
|
+
a.href = url;
|
|
117
|
+
a.download = filename;
|
|
118
|
+
document.body.appendChild(a);
|
|
119
|
+
a.click();
|
|
120
|
+
document.body.removeChild(a);
|
|
121
|
+
URL.revokeObjectURL(url);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Initialize when DOM is ready
|
|
125
|
+
document.addEventListener('DOMContentLoaded', initExport);
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Knowledge Distribution - Expandable Row Interactivity
|
|
3
|
+
* Handles accordion-style expand/collapse for directory deep-dive analysis
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
(function() {
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Initialize expandable rows in the knowledge table
|
|
11
|
+
*/
|
|
12
|
+
function initExpandableRows() {
|
|
13
|
+
const expandableRows = document.querySelectorAll('.knowledge-table .expandable-row');
|
|
14
|
+
|
|
15
|
+
expandableRows.forEach(row => {
|
|
16
|
+
row.addEventListener('click', handleRowExpand);
|
|
17
|
+
row.addEventListener('keydown', handleRowKeydown);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Handle click on expandable row
|
|
23
|
+
*/
|
|
24
|
+
function handleRowExpand(event) {
|
|
25
|
+
const row = event.currentTarget;
|
|
26
|
+
const rowId = row.getAttribute('data-row-id');
|
|
27
|
+
if (!rowId) return;
|
|
28
|
+
|
|
29
|
+
const contentRow = document.getElementById(`${rowId}-content`);
|
|
30
|
+
if (!contentRow) return;
|
|
31
|
+
|
|
32
|
+
const isExpanded = row.getAttribute('aria-expanded') === 'true';
|
|
33
|
+
|
|
34
|
+
if (isExpanded) {
|
|
35
|
+
collapseRow(row, contentRow);
|
|
36
|
+
} else {
|
|
37
|
+
expandRow(row, contentRow);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Handle keyboard navigation on expandable rows
|
|
43
|
+
*/
|
|
44
|
+
function handleRowKeydown(event) {
|
|
45
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
handleRowExpand(event);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Expand a directory row to show details
|
|
53
|
+
*/
|
|
54
|
+
function expandRow(row, contentRow) {
|
|
55
|
+
row.setAttribute('aria-expanded', 'true');
|
|
56
|
+
row.classList.add('expanded');
|
|
57
|
+
contentRow.classList.remove('hidden');
|
|
58
|
+
|
|
59
|
+
// Animate expand icon
|
|
60
|
+
const icon = row.querySelector('.expand-icon');
|
|
61
|
+
if (icon) {
|
|
62
|
+
icon.textContent = '▼';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Animate the content panel
|
|
66
|
+
const panel = contentRow.querySelector('.expansion-panel');
|
|
67
|
+
if (panel) {
|
|
68
|
+
panel.style.maxHeight = panel.scrollHeight + 'px';
|
|
69
|
+
panel.style.opacity = '1';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Collapse a directory row to hide details
|
|
75
|
+
*/
|
|
76
|
+
function collapseRow(row, contentRow) {
|
|
77
|
+
row.setAttribute('aria-expanded', 'false');
|
|
78
|
+
row.classList.remove('expanded');
|
|
79
|
+
contentRow.classList.add('hidden');
|
|
80
|
+
|
|
81
|
+
// Animate expand icon
|
|
82
|
+
const icon = row.querySelector('.expand-icon');
|
|
83
|
+
if (icon) {
|
|
84
|
+
icon.textContent = '▶';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Animate the content panel
|
|
88
|
+
const panel = contentRow.querySelector('.expansion-panel');
|
|
89
|
+
if (panel) {
|
|
90
|
+
panel.style.maxHeight = '0';
|
|
91
|
+
panel.style.opacity = '0';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Collapse all expanded rows
|
|
97
|
+
*/
|
|
98
|
+
function collapseAllRows() {
|
|
99
|
+
const expandedRows = document.querySelectorAll('.knowledge-table .expandable-row.expanded');
|
|
100
|
+
expandedRows.forEach(row => {
|
|
101
|
+
const rowId = row.getAttribute('data-row-id');
|
|
102
|
+
const contentRow = document.getElementById(`${rowId}-content`);
|
|
103
|
+
if (contentRow) {
|
|
104
|
+
collapseRow(row, contentRow);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Initialize when DOM is ready
|
|
110
|
+
if (document.readyState === 'loading') {
|
|
111
|
+
document.addEventListener('DOMContentLoaded', initExpandableRows);
|
|
112
|
+
} else {
|
|
113
|
+
initExpandableRows();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Expose function for external use (e.g., collapse all when switching tabs)
|
|
117
|
+
window.knowledgeModule = {
|
|
118
|
+
collapseAllRows: collapseAllRows
|
|
119
|
+
};
|
|
120
|
+
})();
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Toggle visibility of additional files in the high-risk files list
|
|
124
|
+
*/
|
|
125
|
+
function toggleMoreFiles(button) {
|
|
126
|
+
const targetGroup = button.getAttribute('data-target');
|
|
127
|
+
const count = button.getAttribute('data-count');
|
|
128
|
+
const hiddenRows = document.querySelectorAll(`tr.hidden-file[data-group="${targetGroup}"]`);
|
|
129
|
+
const isExpanded = button.getAttribute('data-expanded') === 'true';
|
|
130
|
+
|
|
131
|
+
hiddenRows.forEach(row => {
|
|
132
|
+
row.style.display = isExpanded ? 'none' : 'table-row';
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
button.setAttribute('data-expanded', !isExpanded);
|
|
136
|
+
button.textContent = isExpanded ? `+ ${count} more files` : 'Show less';
|
|
137
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Modal functionality for commit details
|
|
2
|
+
const modal = document.getElementById('modal');
|
|
3
|
+
const modalTitle = document.getElementById('modal-title');
|
|
4
|
+
const modalClose = document.getElementById('modal-close');
|
|
5
|
+
const commitList = document.getElementById('commit-list');
|
|
6
|
+
|
|
7
|
+
// Helper function to escape HTML
|
|
8
|
+
function escapeHtml(text) {
|
|
9
|
+
const div = document.createElement('div');
|
|
10
|
+
div.textContent = text;
|
|
11
|
+
return div.innerHTML;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Modal click handlers
|
|
15
|
+
const clickableDays = document.querySelectorAll('.day.clickable');
|
|
16
|
+
clickableDays.forEach(day => {
|
|
17
|
+
day.addEventListener('click', () => {
|
|
18
|
+
const details = day.getAttribute('data-details');
|
|
19
|
+
const dateStr = day.getAttribute('data-date');
|
|
20
|
+
|
|
21
|
+
if (details && dateStr) {
|
|
22
|
+
try {
|
|
23
|
+
const commits = JSON.parse(details);
|
|
24
|
+
const date = new Date(dateStr);
|
|
25
|
+
const formattedDate = date.toLocaleDateString('en-US', {
|
|
26
|
+
weekday: 'long',
|
|
27
|
+
year: 'numeric',
|
|
28
|
+
month: 'long',
|
|
29
|
+
day: 'numeric'
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
modalTitle.textContent = `${commits.length} contribution${commits.length !== 1 ? 's' : ''} on ${formattedDate}`;
|
|
33
|
+
|
|
34
|
+
// Build commit list
|
|
35
|
+
commitList.innerHTML = commits.map(commit => `
|
|
36
|
+
<div class="commit-item">
|
|
37
|
+
<div class="commit-message">${escapeHtml(commit.message)}</div>
|
|
38
|
+
<div class="commit-meta">
|
|
39
|
+
<span>by <strong>${escapeHtml(commit.author)}</strong></span>
|
|
40
|
+
<span class="commit-sha">${commit.hash.substring(0, 7)}</span>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
`).join('');
|
|
44
|
+
|
|
45
|
+
modal.classList.add('active');
|
|
46
|
+
} catch (e) {
|
|
47
|
+
console.error('Failed to parse commit details:', e);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Close modal handlers
|
|
54
|
+
modalClose.addEventListener('click', () => {
|
|
55
|
+
modal.classList.remove('active');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
modal.addEventListener('click', (e) => {
|
|
59
|
+
if (e.target === modal) {
|
|
60
|
+
modal.classList.remove('active');
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
document.addEventListener('keydown', (e) => {
|
|
65
|
+
if (e.key === 'Escape' && modal.classList.contains('active')) {
|
|
66
|
+
modal.classList.remove('active');
|
|
67
|
+
}
|
|
68
|
+
});
|